<commit_before>/*
 * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-lwm2m-lib
 *
 * iotagent-lwm2m-lib is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-lwm2m-lib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-lwm2m-lib.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[contacto@tid.es]
 */

'use strict';

var async = require('async'),
    apply = async.apply,
    coapUtils = require('./coapUtils'),
    errors = require('../../errors'),
    registry,
    config,
    logger = require('logops'),
    context = {
        op: 'LWM2MLib.DeviceManagement'
    };

/**
 * Execute a read operation for the selected resource, identified following the LWTM2M conventions by its: deviceId,
 * objectType, objectId and resourceId.
 */
function read(deviceId, objectType, objectId, resourceId, callback) {
    function createReadRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'GET',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId + '/' + resourceId
        };

        callback(null, request);
    }

    logger.debug(context, 'Reading value from resource /%s/%s/%s in device [%d]',
        objectType, objectId, resourceId, deviceId);

    async.waterfall([
        apply(registry.get, deviceId),
        createReadRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.05')
    ], callback);
}

/**
 * Makes a modification over the selected resource, identified following the LWTM2M conventions by its: deviceId,
 * objectType, objectId and resourceId, changing its value to the value passed as a parameter. The method will
 * determine whether its an Execute or a Write operation.
 *
 * @param {String} method           COAP Method to execute.
 * @param {String} deviceId         ID of the device that will be called.
 * @param {Number} objectType       Object type ID.
 * @param {Number} objectId         Object instance ID.
 * @param {Number} resourceId       Resource ID.
 * @param {String} value            Value to write.
 */
function writeResource(method, deviceId, objectType, objectId, resourceId, value, callback) {
    function createUpdateRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: method,
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId + '/' + resourceId,
            payload: value,
            options: {
                'Content-Format': config.writeFormat
            }
        };

        callback(null, request);
    }

    function processResponse(res, callback) {
        if (res.code === '2.04') {
            callback(null, res.payload.toString('utf8'));
        } else if (res.code === '4.04') {
            callback(new errors.ObjectNotFound('/' + objectType + '/' + objectId));
        } else {
            callback(new errors.ClientError(res.code));
        }
    }

    logger.debug(context, 'Writting a new value [%s] on resource /%s/%s/%s in device [%d]',
        value, objectType, objectId, resourceId, deviceId);

    async.waterfall([
        apply(registry.get, deviceId),
        createUpdateRequest,
        coapUtils.sendRequest,
        processResponse
    ], callback);
}

/**
 * Makes a Write operation over the designed resource ID of the selected device.
 */
function write(deviceId, objectType, objectId, resourceId, value, callback) {
    writeResource('PUT', deviceId, objectType, objectId, resourceId, value, callback);
}

/**
 * Makes an Execute operation over the designed resource ID of the selected device.
 */
function execute(deviceId, objectType, objectId, resourceId, value, callback) {
    writeResource('POST', deviceId, objectType, objectId, resourceId, value, callback);
}

/**
 * Write the attributes given as a parameter in the remote resource identified by the Object type and id in the
 * selected device.
 *
 * @param {String} deviceId             ID of the device that holds the resource.
 * @param {String} objectType           ID of the object type of the instance.
 * @param {String} objectId             ID of the instance whose resource will be modified.
 * @param {String} resourceId           ID of the resource to modify.
 * @param {Object} parameters           Object with the parameters to write: each parameter is stored as an attribute.
 */
function writeAttributes(deviceId, objectType, objectId, resourceId, attributes, callback) {
    function createQueryParams(innerCb) {
        var validAttributes = ['pmin', 'pmax', 'gt', 'lt', 'st', 'cancel'],
            result = '?',
            errorList = [];

        for (var i in attributes) {
            if (attributes.hasOwnProperty(i)) {
                if (validAttributes.indexOf(i) >= 0) {
                    result += i + '=' + attributes[i] + '&';
                } else {
                    errorList.push(i);
                }
            }
        }

        if (errorList.length !== 0) {
            innerCb(new errors.UnsupportedAttributes(errorList));
        } else {
            innerCb(null, result);
        }
    }

    function createWriteAttributesRequest(data, innerCb) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+data[0].address+']' : data[0].address) +
              ':' + data[0].port,
            method: 'PUT'
        };
        if (objectType && objectId && resourceId) {
            request.pathname = '/' + objectType + '/' + objectId + '/' + resourceId + data[1];
        } else if (objectType && objectId) {
            request.pathname = '/' + objectType + '/' + objectId + data[1];
        } else {
            request.pathname = '/' + objectType + data[1];
        }

        innerCb(null, request);
    }

    logger.debug(context, 'Writting new discover attributes on resource /%s/%s/%s in device [%d]',
        objectType, objectId, resourceId, deviceId);
    logger.debug(context, 'The new attributes are:\n%j', attributes);

    async.waterfall([
        apply(async.parallel, [
            apply(registry.get, deviceId),
            createQueryParams
        ]),
        createWriteAttributesRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.04')
    ], callback);
}

/**
 * Execute a discover operation for the selected resource, identified following the LWTM2M conventions by its:
 * deviceId, objectType, objectId and resourceId.
 */
function discover(deviceId, objectType, objectId, resourceId, fullCallback) {
    var pathname,
        trueCallback;

    if (objectId && resourceId && fullCallback) {
        logger.debug(context, 'Executing a resource discover operation on resource /%s/%s/%s in device [%d]',
            objectType, objectId, resourceId, deviceId);

        pathname= '/' + objectType + '/' + objectId + '/' + resourceId;
        trueCallback = fullCallback;
    } else if (objectId && resourceId) {
        logger.debug(context, 'Executing a instance discover operation on resource /%s/%s in device [%d]',
            objectType, objectId, deviceId);

        pathname= '/' + objectType + '/' + objectId;
        trueCallback = resourceId;
    } else {
        logger.debug(context, 'Executing a type discover operation on resource /%s in device [%d]',
            objectType, deviceId);

        pathname= '/' + objectType;
        trueCallback = objectId;
    }

    function createReadRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'GET',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: pathname,
            options: {
                'Accept': 'application/link-format'
            }
        };

        callback(null, request);
    }

    if (!objectType && !objectId && !resourceId) {
        logger.error(context, 'Method called with wrong number of parameters. Couldn\'t identify callback');
    } else {
        async.waterfall([
            apply(registry.get, deviceId),
            createReadRequest,
            coapUtils.sendRequest,
            coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.05')
        ], trueCallback);
    }
}

function create(deviceId, objectType, objectId, callback) {
    function createUpdateRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'POST',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId
        };

        callback(null, request);
    }

    logger.debug(context, 'Creating a new instance of object type [%s] in the device [%d] with instance id [%s]',
        objectType, deviceId, objectId);

    async.waterfall([
        apply(registry.get, deviceId),
        createUpdateRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, null, '2.01')
    ], callback);
}

function remove(deviceId, objectType, objectId, callback) {
    callback(null);
}

function init(deviceRegistry, newConfig) {
    registry = deviceRegistry;
    config = newConfig;
}

exports.read = read;
exports.write = write;
exports.execute = execute;
exports.writeAttributes = writeAttributes;
exports.discover = discover;
exports.create = create;
exports.remove = remove;
exports.init = init;
<commit_msg>Removing trailing whitespace.<commit_after>/*
 * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-lwm2m-lib
 *
 * iotagent-lwm2m-lib is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-lwm2m-lib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-lwm2m-lib.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::[contacto@tid.es]
 */

'use strict';

var async = require('async'),
    apply = async.apply,
    coapUtils = require('./coapUtils'),
    errors = require('../../errors'),
    registry,
    config,
    logger = require('logops'),
    context = {
        op: 'LWM2MLib.DeviceManagement'
    };

/**
 * Execute a read operation for the selected resource, identified following the LWTM2M conventions by its: deviceId,
 * objectType, objectId and resourceId.
 */
function read(deviceId, objectType, objectId, resourceId, callback) {
    function createReadRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'GET',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId + '/' + resourceId
        };

        callback(null, request);
    }

    logger.debug(context, 'Reading value from resource /%s/%s/%s in device [%d]',
        objectType, objectId, resourceId, deviceId);

    async.waterfall([
        apply(registry.get, deviceId),
        createReadRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.05')
    ], callback);
}

/**
 * Makes a modification over the selected resource, identified following the LWTM2M conventions by its: deviceId,
 * objectType, objectId and resourceId, changing its value to the value passed as a parameter. The method will
 * determine whether its an Execute or a Write operation.
 *
 * @param {String} method           COAP Method to execute.
 * @param {String} deviceId         ID of the device that will be called.
 * @param {Number} objectType       Object type ID.
 * @param {Number} objectId         Object instance ID.
 * @param {Number} resourceId       Resource ID.
 * @param {String} value            Value to write.
 */
function writeResource(method, deviceId, objectType, objectId, resourceId, value, callback) {
    function createUpdateRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: method,
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId + '/' + resourceId,
            payload: value,
            options: {
                'Content-Format': config.writeFormat
            }
        };

        callback(null, request);
    }

    function processResponse(res, callback) {
        if (res.code === '2.04') {
            callback(null, res.payload.toString('utf8'));
        } else if (res.code === '4.04') {
            callback(new errors.ObjectNotFound('/' + objectType + '/' + objectId));
        } else {
            callback(new errors.ClientError(res.code));
        }
    }

    logger.debug(context, 'Writting a new value [%s] on resource /%s/%s/%s in device [%d]',
        value, objectType, objectId, resourceId, deviceId);

    async.waterfall([
        apply(registry.get, deviceId),
        createUpdateRequest,
        coapUtils.sendRequest,
        processResponse
    ], callback);
}

/**
 * Makes a Write operation over the designed resource ID of the selected device.
 */
function write(deviceId, objectType, objectId, resourceId, value, callback) {
    writeResource('PUT', deviceId, objectType, objectId, resourceId, value, callback);
}

/**
 * Makes an Execute operation over the designed resource ID of the selected device.
 */
function execute(deviceId, objectType, objectId, resourceId, value, callback) {
    writeResource('POST', deviceId, objectType, objectId, resourceId, value, callback);
}

/**
 * Write the attributes given as a parameter in the remote resource identified by the Object type and id in the
 * selected device.
 *
 * @param {String} deviceId             ID of the device that holds the resource.
 * @param {String} objectType           ID of the object type of the instance.
 * @param {String} objectId             ID of the instance whose resource will be modified.
 * @param {String} resourceId           ID of the resource to modify.
 * @param {Object} parameters           Object with the parameters to write: each parameter is stored as an attribute.
 */
function writeAttributes(deviceId, objectType, objectId, resourceId, attributes, callback) {
    function createQueryParams(innerCb) {
        var validAttributes = ['pmin', 'pmax', 'gt', 'lt', 'st', 'cancel'],
            result = '?',
            errorList = [];

        for (var i in attributes) {
            if (attributes.hasOwnProperty(i)) {
                if (validAttributes.indexOf(i) >= 0) {
                    result += i + '=' + attributes[i] + '&';
                } else {
                    errorList.push(i);
                }
            }
        }

        if (errorList.length !== 0) {
            innerCb(new errors.UnsupportedAttributes(errorList));
        } else {
            innerCb(null, result);
        }
    }

    function createWriteAttributesRequest(data, innerCb) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+data[0].address+']' : data[0].address) +
              ':' + data[0].port,
            method: 'PUT'
        };
        if (objectType && objectId && resourceId) {
            request.pathname = '/' + objectType + '/' + objectId + '/' + resourceId + data[1];
        } else if (objectType && objectId) {
            request.pathname = '/' + objectType + '/' + objectId + data[1];
        } else {
            request.pathname = '/' + objectType + data[1];
        }

        innerCb(null, request);
    }

    logger.debug(context, 'Writting new discover attributes on resource /%s/%s/%s in device [%d]',
        objectType, objectId, resourceId, deviceId);
    logger.debug(context, 'The new attributes are:\n%j', attributes);

    async.waterfall([
        apply(async.parallel, [
            apply(registry.get, deviceId),
            createQueryParams
        ]),
        createWriteAttributesRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.04')
    ], callback);
}

/**
 * Execute a discover operation for the selected resource, identified following the LWTM2M conventions by its:
 * deviceId, objectType, objectId and resourceId.
 */
function discover(deviceId, objectType, objectId, resourceId, fullCallback) {
    var pathname,
        trueCallback;

    if (objectId && resourceId && fullCallback) {
        logger.debug(context, 'Executing a resource discover operation on resource /%s/%s/%s in device [%d]',
            objectType, objectId, resourceId, deviceId);

        pathname= '/' + objectType + '/' + objectId + '/' + resourceId;
        trueCallback = fullCallback;
    } else if (objectId && resourceId) {
        logger.debug(context, 'Executing a instance discover operation on resource /%s/%s in device [%d]',
            objectType, objectId, deviceId);

        pathname= '/' + objectType + '/' + objectId;
        trueCallback = resourceId;
    } else {
        logger.debug(context, 'Executing a type discover operation on resource /%s in device [%d]',
            objectType, deviceId);

        pathname= '/' + objectType;
        trueCallback = objectId;
    }

    function createReadRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'GET',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: pathname,
            options: {
                'Accept': 'application/link-format'
            }
        };

        callback(null, request);
    }

    if (!objectType && !objectId && !resourceId) {
        logger.error(context, 'Method called with wrong number of parameters. Couldn\'t identify callback');
    } else {
        async.waterfall([
            apply(registry.get, deviceId),
            createReadRequest,
            coapUtils.sendRequest,
            coapUtils.generateProcessResponse(objectType, objectId, resourceId, '2.05')
        ], trueCallback);
    }
}

function create(deviceId, objectType, objectId, callback) {
    function createUpdateRequest(obj, callback) {
        var request = {
            host: (config.ipProtocol === 'udp6')?'::1':'127.0.0.1',
            port: config.port,
            method: 'POST',
            proxyUri: 'coap://' + (config.ipProtocol === 'udp6' ? '['+obj.address+']' : obj.address) + ':' + obj.port,
            pathname: '/' + objectType + '/' + objectId
        };

        callback(null, request);
    }

    logger.debug(context, 'Creating a new instance of object type [%s] in the device [%d] with instance id [%s]',
        objectType, deviceId, objectId);

    async.waterfall([
        apply(registry.get, deviceId),
        createUpdateRequest,
        coapUtils.sendRequest,
        coapUtils.generateProcessResponse(objectType, objectId, null, '2.01')
    ], callback);
}

function remove(deviceId, objectType, objectId, callback) {
    callback(null);
}

function init(deviceRegistry, newConfig) {
    registry = deviceRegistry;
    config = newConfig;
}

exports.read = read;
exports.write = write;
exports.execute = execute;
exports.writeAttributes = writeAttributes;
exports.discover = discover;
exports.create = create;
exports.remove = remove;
exports.init = init;
<|endoftext|>


<commit_before>/**
 * Created by kelvinzhang on 5/29/17.
 */

var canvas;
var bgCanvas;

var robotX;
var robotY;

var senseRadius = 150;

var nLasers = 36;

var robotDir = 0;
var dirOffset = 0;

var sensorNoise = 0.1;

//Results of laser scan
var z = new Array(nLasers);

var sensorModel;

function update()
{
    //Only scan if location changed
    scan(robotX, robotY, robotDir, senseRadius, map, z);
}

function drawLaserLines(ctx)
{
    //Angle between each laser, in radians
    var rad = Math.PI*2/nLasers;

    //index of corresponding entry in z
    var i = (dirOffset - nLasers/4);
    i = (i+nLasers+nLasers);

    for(var index = 0; index <= nLasers/2; index ++, i++)
    {
        //dirOffset is the direction the user's mouse
        //is point at, -nLasers/4 to offset it by 90 degrees,
        //so that laser scan is centered at where the user
        //points to

        //get i stay in bound
        i %= nLasers;

        var dir = i * rad;
        var laserLen = senseRadius;

        //Grey color for a miss
        if(z[i] >= senseRadius)
        {
            ctx.strokeStyle = 'grey';
        }else
        {
            //distance reported by the laser sensor
            var dist = z[i] + gaussian()*sensorNoise;
            laserLen = min(laserLen, dist);

            //red for a hit
            ctx.strokeStyle = 'red';
            ctx.fillStyle = 'red';
            ctx.beginPath();
            ctx.circle(robotX + cos(dir)*dist, robotY + sin(dir)*dist, 5);
            ctx.fill();
        }
        ctx.strokeLine(robotX, robotY, robotX + cos(dir)*laserLen, robotY + sin(dir)*laserLen)
    }
}

function drawRobot(ctx)
{
    ctx.strokeStyle = 'black';
    ctx.drawRobot(robotX, robotY, robotDir, 20);
}

function parameterChanged(event)
{
    var target = event.target || event.srcElement;

    if(target.id === 'sensorRadius')
    {
        senseRadius = Number(target.value) / 0.02;
    }
    else if(target.id === 'sensorNoise')
    {
        sensorNoise = Number(target.value);
        sensorModel.a1 = sensorNoise;
    }
    else if(target.id === 'nLasers')
    {
        nLasers = Number(target.value) * 2;
        z = new Array(nLasers);
    }
}

function trackRobotDir(event)
{
    var coor = getClickLoc(event);
    var x = coor.x;
    var y = coor.y;

    robotDir = atan2(y-robotY, x-robotX);
    dirOffset = round(robotDir/Math.PI/2 * nLasers);
    clearCanvas(canvas);
    canvas.getContext('2d').drawRobot(robotX, robotY, robotDir, 20);
}

function mouseDown(event)
{
	var coor = getClickLoc(event);
	var x = coor.x;
	var y = coor.y;

	//Do nothing if it is not a left button event
	if(event.altKey)
    {
    	clearCanvas(canvas);
		var ctx = canvas.getContext('2d');
		drawRobot(ctx);
		drawLaserLines(ctx);
		ctx.strokeStyle = 'rgba(0, 0, 255, 0.5)';
		ctx.drawRobot(x, y, robotDir, 5);
		var probability = sensorModel.probability(z, new RobotState(x, y, robotDir));
		document.getElementById('probability').innerHTML = "Probability: " + probability;
		return;
	}

    robotX = x;
    robotY = y;
    clearCanvas(canvas);
    drawRobot(canvas.getContext('2d'));
    bgCanvas.onmousemove = trackRobotDir;
    bgCanvas.onmouseout = mouseUp;
	bgCanvas.onmouseup = mouseUp;
}

function mouseUp()
{
    bgCanvas.onmousemove = undefined;
    bgCanvas.onmouseout = undefined;
    bgCanvas.onmouseup = undefined;
    update();
    drawLaserLines(canvas.getContext('2d'));
}

function init()
{
    canvas = document.getElementById('canvas');
    bgCanvas = document.getElementById('background');

    //Draw the map
    var map = getMapForCanvas(canvas);
    bgCanvas.getContext('2d').drawMap(map);
    robotX = floor(random()*canvas.width);
    robotY = floor(random()*canvas.height);
    robotDir = random()*TWO_PI;
    dirOffset = round(robotDir/Math.PI/2 * nLasers);

    update();
    drawRobot(canvas.getContext('2d'));
    drawLaserLines(canvas.getContext('2d'));

	sensorModel = new BeamModel(getSensorNoise(), getSensorRadius(), map,
		canvas.width, canvas.height);

    Particle.size = 5;

    //Listen to mouse click events
    bgCanvas.onmousedown = mouseDown;
    senseRadius = getSensorRadius();
    sensorNoise = getSensorNoise();
}

function getSensorNoise()
{
	return getValue('sensorNoise');
}

function getSensorRadius()
{
	return getValue('sensorRadius')/0.02;
}

function toggleColoring(event)
{
	console.log(event);
	var target = event.target || event.srcElement;
	if(target.checked)
	{
		colorMap();
	}else
	{
		clearCanvas(bgCanvas);
		bgCanvas.getContext('2d').drawMap(map);
	}
}

function colorMap()
{
	var ctx = bgCanvas.getContext('2d');
	var resolution = getValue('colorRes');
	var probs = new Array(Math.ceil(bgCanvas.height/resolution));
	var sum = 0;
	for(var i = 0; i < probs.length; i ++)
	{
		probs[i] = new Array(Math.ceil(bgCanvas.width/resolution));
		for(var j = 0; j < probs[i].length; j ++)
		{
			var p = sensorModel.probability(z, new RobotState(j*resolution + resolution/2, i*resolution + resolution/2, robotDir));
			console.log(p);
			console.assert(p >= 0 && p < 1);
			sum += p;
			probs[i][j] = p;
		}
	}

	for(var i = 0; i < probs.length; i ++)
	{
		for(var j = 0; j < probs[i].length; j ++)
		{
			var p = probs[i][j] / sum;
			// console.log(p*255);
			ctx.fillStyle = 'rgba(' + round(p*255) + ', 0, ' + (255-round(p*255)) + ', 0.5)';
			ctx.fillRect(j*resolution, i*resolution, resolution, resolution);
		}
	}
}<commit_msg>disable coloring on location change<commit_after>/**
 * Created by kelvinzhang on 5/29/17.
 */

var canvas;
var bgCanvas;

var robotX;
var robotY;

var senseRadius = 150;

var nLasers = 36;

var robotDir = 0;
var dirOffset = 0;

var sensorNoise = 0.1;

//Results of laser scan
var z = new Array(nLasers);

var sensorModel;

function update()
{
    //Only scan if location changed
    scan(robotX, robotY, robotDir, senseRadius, map, z);
    var checkbox = document.getElementById('shouldColor');
    checkbox.checked = false;
	clearColor();
}

function drawLaserLines(ctx)
{
    //Angle between each laser, in radians
    var rad = Math.PI*2/nLasers;

    //index of corresponding entry in z
    var i = (dirOffset - nLasers/4);
    i = (i+nLasers+nLasers);

    for(var index = 0; index <= nLasers/2; index ++, i++)
    {
        //dirOffset is the direction the user's mouse
        //is point at, -nLasers/4 to offset it by 90 degrees,
        //so that laser scan is centered at where the user
        //points to

        //get i stay in bound
        i %= nLasers;

        var dir = i * rad;
        var laserLen = senseRadius;

        //Grey color for a miss
        if(z[i] >= senseRadius)
        {
            ctx.strokeStyle = 'grey';
        }else
        {
            //distance reported by the laser sensor
            var dist = z[i] + gaussian()*sensorNoise;
            laserLen = min(laserLen, dist);

            //red for a hit
            ctx.strokeStyle = 'red';
            ctx.fillStyle = 'red';
            ctx.beginPath();
            ctx.circle(robotX + cos(dir)*dist, robotY + sin(dir)*dist, 5);
            ctx.fill();
        }
        ctx.strokeLine(robotX, robotY, robotX + cos(dir)*laserLen, robotY + sin(dir)*laserLen)
    }
}

function drawRobot(ctx)
{
    ctx.strokeStyle = 'black';
    ctx.drawRobot(robotX, robotY, robotDir, 20);
}

function parameterChanged(event)
{
    var target = event.target || event.srcElement;

    if(target.id === 'sensorRadius')
    {
        senseRadius = Number(target.value) / 0.02;
    }
    else if(target.id === 'sensorNoise')
    {
        sensorNoise = Number(target.value);
        sensorModel.a1 = sensorNoise;
    }
    else if(target.id === 'nLasers')
    {
        nLasers = Number(target.value) * 2;
        z = new Array(nLasers);
    }
}

function trackRobotDir(event)
{
    var coor = getClickLoc(event);
    var x = coor.x;
    var y = coor.y;

    robotDir = atan2(y-robotY, x-robotX);
    dirOffset = round(robotDir/Math.PI/2 * nLasers);
    clearCanvas(canvas);
    canvas.getContext('2d').drawRobot(robotX, robotY, robotDir, 20);
}

function mouseDown(event)
{
	var coor = getClickLoc(event);
	var x = coor.x;
	var y = coor.y;

	//Do nothing if it is not a left button event
	if(event.altKey)
    {
    	clearCanvas(canvas);
		var ctx = canvas.getContext('2d');
		drawRobot(ctx);
		drawLaserLines(ctx);
		ctx.strokeStyle = 'rgba(0, 0, 255, 0.5)';
		ctx.drawRobot(x, y, robotDir, 5);
		var probability = sensorModel.probability(z, new RobotState(x, y, robotDir));
		document.getElementById('probability').innerHTML = "Probability: " + probability;
		return;
	}

    robotX = x;
    robotY = y;
    clearCanvas(canvas);
    drawRobot(canvas.getContext('2d'));
    bgCanvas.onmousemove = trackRobotDir;
    bgCanvas.onmouseout = mouseUp;
	bgCanvas.onmouseup = mouseUp;
}

function mouseUp()
{
    bgCanvas.onmousemove = undefined;
    bgCanvas.onmouseout = undefined;
    bgCanvas.onmouseup = undefined;
    update();
    drawLaserLines(canvas.getContext('2d'));
}

function init()
{
    canvas = document.getElementById('canvas');
    bgCanvas = document.getElementById('background');

    //Draw the map
    var map = getMapForCanvas(canvas);
    bgCanvas.getContext('2d').drawMap(map);
    robotX = floor(random()*canvas.width);
    robotY = floor(random()*canvas.height);
    robotDir = random()*TWO_PI;
    dirOffset = round(robotDir/Math.PI/2 * nLasers);

    update();
    drawRobot(canvas.getContext('2d'));
    drawLaserLines(canvas.getContext('2d'));

	sensorModel = new BeamModel(getSensorNoise(), getSensorRadius(), map,
		canvas.width, canvas.height);

    Particle.size = 5;

    //Listen to mouse click events
    bgCanvas.onmousedown = mouseDown;
    senseRadius = getSensorRadius();
    sensorNoise = getSensorNoise();
}

function getSensorNoise()
{
	return getValue('sensorNoise');
}

function getSensorRadius()
{
	return getValue('sensorRadius')/0.02;
}

function toggleColoring(event)
{
	console.log(event);
	var target = event.target || event.srcElement;
	if(target.checked)
	{
		colorMap();
	}else
	{
		clearColor();
	}
}

function colorMap()
{
	var ctx = bgCanvas.getContext('2d');
	var resolution = getValue('colorRes');
	var probs = new Array(Math.ceil(bgCanvas.height/resolution));
	var sum = 0;
	for(var i = 0; i < probs.length; i ++)
	{
		probs[i] = new Array(Math.ceil(bgCanvas.width/resolution));
		for(var j = 0; j < probs[i].length; j ++)
		{
			var p = sensorModel.probability(z, new RobotState(j*resolution + resolution/2, i*resolution + resolution/2, robotDir));
			console.assert(p >= 0 && p < 1);
			sum += p;
			probs[i][j] = p;
		}
	}

	for(var i = 0; i < probs.length; i ++)
	{
		for(var j = 0; j < probs[i].length; j ++)
		{
			var p = probs[i][j] / sum;
			ctx.fillStyle = 'rgba(' + round(p*255) + ', 0, ' + (255-round(p*255)) + ', 0.5)';
			ctx.fillRect(j*resolution, i*resolution, resolution, resolution);
		}
	}
}

function clearColor()
{
	clearCanvas(bgCanvas);
	bgCanvas.getContext('2d').drawMap(map);
}<|endoftext|>


<commit_before>// Universal Subtitles, universalsubtitles.org
//
// Copyright (C) 2010 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('mirosubs.widget.SubtitleDialogOpener');

/**
 * @constructor
 * @param {string} videoID
 * @param {string} videoURL This is used for creating the embed code
 *     that appears in the widget.
 * @param {mirosubs.video.VideoSource} videoSource
 * @param {function(boolean)=} opt_loadingFn
 * @param {function()=} opt_subOpenFn
 */
mirosubs.widget.SubtitleDialogOpener = function(
    videoID, videoURL, videoSource, opt_loadingFn, opt_subOpenFn)
{
    goog.events.EventTarget.call(this);
    this.videoID_ = videoID;
    this.videoURL_ = videoURL;
    this.videoSource_ = videoSource;
    this.loadingFn_ = opt_loadingFn;
    this.subOpenFn_ = opt_subOpenFn;
};
goog.inherits(mirosubs.widget.SubtitleDialogOpener,
              goog.events.EventTarget);

mirosubs.widget.SubtitleDialogOpener.prototype.showLoading_ =
    function(loading)
{
    if (this.loadingFn_)
        this.loadingFn_(loading);
};

mirosubs.widget.SubtitleDialogOpener.prototype.openDialog = function(
    baseVersionNo, subLanguageCode, originalLanguageCode, fork)
{
    this.showLoading_(true);
    mirosubs.Rpc.call(
        'start_editing',
        {'video_id': this.videoID_,
         'language_code': subLanguageCode,
         'original_language_code': originalLanguageCode,
         'base_version_no': baseVersionNo,
         'fork': fork},
        goog.bind(this.startEditingResponseHandler_, this));
};

/**
 * @param {number} draftPK The draft saved with this primary key should
 *     already be in forked state on the server.
 * @param {mirosubs.widget.SubtitleState} subtitles The subtitles should
 *     include current timing information.
 */
mirosubs.widget.SubtitleDialogOpener.prototype.openForkedTranslationDialog =
    function(draftPK, subtitles)
{
    var serverModel = new mirosubs.subtitle.MSServerModel(
        draftPK, this.videoID_, this.videoURL_);
    this.openSubtitlingDialog_(serverModel, subtitles);
};

mirosubs.widget.SubtitleDialogOpener.prototype.startEditingResponseHandler_ =
    function(result)
{
    this.showLoading_(false);
    if (result['can_edit']) {
        var draftPK = result['draft_pk'];
        subtitles = mirosubs.widget.SubtitleState.fromJSON(
            result['subtitles']);
        originalSubtitles = mirosubs.widget.SubtitleState.fromJSON(
            result['original_subtitles']);
        var serverModel = new mirosubs.subtitle.MSServerModel(
            draftPK, this.videoID_, this.videoURL_);
        if (subtitles.IS_ORIGINAL || subtitles.FORKED)
            this.openSubtitlingDialog_(serverModel, subtitles);
        else
            this.openDependentTranslationDialog_(
                serverModel, subtitles, originalSubtitles);
    }
    else {
        var username =
            (result['locked_by'] ==
             'anonymous' ? 'Someone else' : ('The user ' + result['locked_by']));
        alert(username + ' is currently editing these subtitles. Please wait and try again later.');
    }
};

mirosubs.widget.SubtitleDialogOpener.prototype.openSubtitlingDialog_ =
    function(serverModel, subtitleState)
{
    if (this.subOpenFn_)
        this.subOpenFn_();
    var subDialog = new mirosubs.subtitle.Dialog(
        this.videoSource_,
        serverModel, subtitleState.SUBTITLES,
        this);
    subDialog.setParentEventTarget(this);
    subDialog.setVisible(true);
};

mirosubs.widget.SubtitleDialogOpener.prototype.openDependentTranslationDialog_ =
    function(serverModel, subtitleState, originalSubtitleState)
{
    if (this.subOpenFn_)
        this.subOpenFn_();
    var transDialog = new mirosubs.translate.Dialog(
        this,
        serverModel,
        this.videoSource_,
        subtitleState, originalSubtitleState);
    transDialog.setParentEventTarget(this);
    transDialog.setVisible(true);
};
<commit_msg>Return to returnURL when onsite_widget finds that someone else is editing subs. Fixes https://www.pivotaltracker.com/story/show/10359339<commit_after>// Universal Subtitles, universalsubtitles.org
//
// Copyright (C) 2010 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('mirosubs.widget.SubtitleDialogOpener');

/**
 * @constructor
 * @param {string} videoID
 * @param {string} videoURL This is used for creating the embed code
 *     that appears in the widget.
 * @param {mirosubs.video.VideoSource} videoSource
 * @param {function(boolean)=} opt_loadingFn
 * @param {function()=} opt_subOpenFn
 */
mirosubs.widget.SubtitleDialogOpener = function(
    videoID, videoURL, videoSource, opt_loadingFn, opt_subOpenFn)
{
    goog.events.EventTarget.call(this);
    this.videoID_ = videoID;
    this.videoURL_ = videoURL;
    this.videoSource_ = videoSource;
    this.loadingFn_ = opt_loadingFn;
    this.subOpenFn_ = opt_subOpenFn;
};
goog.inherits(mirosubs.widget.SubtitleDialogOpener,
              goog.events.EventTarget);

mirosubs.widget.SubtitleDialogOpener.prototype.showLoading_ =
    function(loading)
{
    if (this.loadingFn_)
        this.loadingFn_(loading);
};

mirosubs.widget.SubtitleDialogOpener.prototype.openDialog = function(
    baseVersionNo, subLanguageCode, originalLanguageCode, fork)
{
    this.showLoading_(true);
    mirosubs.Rpc.call(
        'start_editing',
        {'video_id': this.videoID_,
         'language_code': subLanguageCode,
         'original_language_code': originalLanguageCode,
         'base_version_no': baseVersionNo,
         'fork': fork},
        goog.bind(this.startEditingResponseHandler_, this));
};

/**
 * @param {number} draftPK The draft saved with this primary key should
 *     already be in forked state on the server.
 * @param {mirosubs.widget.SubtitleState} subtitles The subtitles should
 *     include current timing information.
 */
mirosubs.widget.SubtitleDialogOpener.prototype.openForkedTranslationDialog =
    function(draftPK, subtitles)
{
    var serverModel = new mirosubs.subtitle.MSServerModel(
        draftPK, this.videoID_, this.videoURL_);
    this.openSubtitlingDialog_(serverModel, subtitles);
};

mirosubs.widget.SubtitleDialogOpener.prototype.startEditingResponseHandler_ =
    function(result)
{
    this.showLoading_(false);
    if (result['can_edit']) {
        var draftPK = result['draft_pk'];
        subtitles = mirosubs.widget.SubtitleState.fromJSON(
            result['subtitles']);
        originalSubtitles = mirosubs.widget.SubtitleState.fromJSON(
            result['original_subtitles']);
        var serverModel = new mirosubs.subtitle.MSServerModel(
            draftPK, this.videoID_, this.videoURL_);
        if (subtitles.IS_ORIGINAL || subtitles.FORKED)
            this.openSubtitlingDialog_(serverModel, subtitles);
        else
            this.openDependentTranslationDialog_(
                serverModel, subtitles, originalSubtitles);
    }
    else {
        var username =
            (result['locked_by'] ==
             'anonymous' ? 'Someone else' : ('The user ' + result['locked_by']));
        alert(username + ' is currently editing these subtitles. Please wait and try again later.');
        if (mirosubs.returnURL != null)
            window.location.replace(mirosubs.returnURL);
    }
};

mirosubs.widget.SubtitleDialogOpener.prototype.openSubtitlingDialog_ =
    function(serverModel, subtitleState)
{
    if (this.subOpenFn_)
        this.subOpenFn_();
    var subDialog = new mirosubs.subtitle.Dialog(
        this.videoSource_,
        serverModel, subtitleState.SUBTITLES,
        this);
    subDialog.setParentEventTarget(this);
    subDialog.setVisible(true);
};

mirosubs.widget.SubtitleDialogOpener.prototype.openDependentTranslationDialog_ =
    function(serverModel, subtitleState, originalSubtitleState)
{
    if (this.subOpenFn_)
        this.subOpenFn_();
    var transDialog = new mirosubs.translate.Dialog(
        this,
        serverModel,
        this.videoSource_,
        subtitleState, originalSubtitleState);
    transDialog.setParentEventTarget(this);
    transDialog.setVisible(true);
};
<|endoftext|>


<commit_before>// Amara, universalsubtitles.org
//
// Copyright (C) 2012 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('unisubs.timeline.TimelineSub');
/**
* @constructor
* @extends goog.ui.Component
*/
unisubs.timeline.TimelineSub = function(
    subtitle, pixelsPerSecond, opt_pixelOffset, readOnly)
{
    goog.ui.Component.call(this);
    this.subtitle_ = subtitle;
    this.pixelsPerMillisecond_ = pixelsPerSecond  / 1000;
    this.pixelOffset_ = opt_pixelOffset ? opt_pixelOffset : 0;
    this.editing_ = false;
    this.documentEventHandler_ = new goog.events.EventHandler(this);
    this.readOnly_ = readOnly;
};
goog.inherits(unisubs.timeline.TimelineSub, goog.ui.Component);
/**
 * Whether one of the timeline subs is currently being edited.
 */
unisubs.timeline.TimelineSub.currentlyEditing_ = false;
unisubs.timeline.TimelineSub.isCurrentlyEditing = function() {
    return unisubs.timeline.TimelineSub.currentlyEditing_;
};
unisubs.timeline.TimelineSub.EventType = {
    START_EDITING : 'startediting',
    FINISH_EDITING : 'finishediting'
};
unisubs.timeline.TimelineSub.prototype.createDom = function() {
    unisubs.timeline.TimelineSub.superClass_.createDom.call(this);
    this.getElement().className = 'unisubs-timeline-sub';
    var $d = goog.bind(this.getDomHelper().createDom, this.getDomHelper());
    var el = this.getElement();
    el.appendChild(this.textElem_ = $d('div', 'unisubs-subtext'));
    el.appendChild(
        this.leftGrabber_ =
            $d('span', 'unisubs-grabber unisubs-leftGrabber',
               $d('strong')));
    el.appendChild(
        this.rightGrabber_ =
            $d('span', 'unisubs-grabber unisubs-rightGrabber',
               $d('strong')));
    unisubs.style.setProperty(el, 'cursor', 'pointer');
    this.updateValues_();
};
unisubs.timeline.TimelineSub.prototype.enterDocument = function() {
    unisubs.timeline.TimelineSub.superClass_.enterDocument.call(this);
    this.getHandler().listen(
        this.getElement(), 'mouseover', this.onMouseOver_).
        listen(this.getElement(), 'mouseout', this.onMouseOut_).
        listen(this.leftGrabber_, 'mousedown', this.onGrabberMousedown_).
        listen(this.rightGrabber_, 'mousedown', this.onGrabberMousedown_).
        listen(this.subtitle_, unisubs.timeline.Subtitle.CHANGE,
               this.updateValues_);
};
unisubs.timeline.TimelineSub.prototype.onMouseOver_ = function(event) {
    if (this.readOnly_) {
        return;
    }
    if (!unisubs.timeline.TimelineSub.currentlyEditing_)
        this.setGrabberVisibility_(true);
    this.mouseOver_ = true;
};
unisubs.timeline.TimelineSub.prototype.onMouseOut_ = function(event) {
    if (this.readOnly_) {
        return;
    }
    if (event.relatedTarget &&
        !goog.dom.contains(this.getElement(), event.relatedTarget)) {
        if (!this.editing_)
            this.setGrabberVisibility_(false);
        this.mouseOver_ = false;
    }
};
unisubs.timeline.TimelineSub.prototype.onDocMouseMoveLeft_ = function(event) {
    // moving left grabber
    var time = this.grabberMousedownTime_ +
            (event.clientX - this.grabberMousedownClientX_) /
            this.pixelsPerMillisecond_;

    this.subtitle_.getEditableCaption().setStartTime(parseInt(time));
};
unisubs.timeline.TimelineSub.prototype.onDocMouseMoveRight_ = function(event) {
    // moving right grabber
    var time =         this.grabberMousedownTime_ +
            (event.clientX - this.grabberMousedownClientX_) /
            this.pixelsPerMillisecond_;
    this.subtitle_.getEditableCaption().setEndTime(parseInt(time));
};
unisubs.timeline.TimelineSub.prototype.onDocMouseUp_ = function(event) {
    this.editing_ = false;
    unisubs.timeline.TimelineSub.currentlyEditing_ = false;
    this.documentEventHandler_.removeAll();
    if (!this.mouseOver_)
        this.setGrabberVisibility_(false);
    this.dispatchEvent(
        unisubs.timeline.TimelineSub.EventType.FINISH_EDITING);
};
unisubs.timeline.TimelineSub.prototype.getSubtitle = function() {
    return this.subtitle_;
};
unisubs.timeline.TimelineSub.prototype.onGrabberMousedown_ =
    function(event)
{
    var left = goog.dom.contains(this.leftGrabber_, event.target);
    this.editing_ = true;
    this.dispatchEvent(
        unisubs.timeline.TimelineSub.EventType.START_EDITING);
    unisubs.timeline.TimelineSub.currentlyEditing_ = true;
    this.grabberMousedownClientX_ = event.clientX;
    this.grabberMousedownTime_ = left ?
        parseInt(this.subtitle_.getStartTime()) : parseInt(this.subtitle_.getEndTime());
    this.documentEventHandler_.listen(
        document, 'mousemove',
        left ? this.onDocMouseMoveLeft_ : this.onDocMouseMoveRight_);
    this.documentEventHandler_.listen(
        document, 'mouseup', this.onDocMouseUp_);
    event.preventDefault(); // necessary to prevent image dragging in FF3
};
unisubs.timeline.TimelineSub.prototype.setGrabberVisibility_ =
    function(visible)
{
    var c = goog.dom.classes;
    var overClass = 'unisubs-grabber-over';
    if (visible) {
        c.add(this.leftGrabber_, overClass);
        c.add(this.rightGrabber_, overClass);
    }
    else {
        c.remove(this.leftGrabber_, overClass);
        c.remove(this.rightGrabber_, overClass);
    }
};
unisubs.timeline.TimelineSub.prototype.updateValues_ = function() {
    var nextToBeSynced = this.subtitle_.isNextToBeSynced();
    if (this.subtitle_.getEditableCaption().getText() !=
        this.existingSubText_)
    {
        goog.dom.setTextContent(
            this.textElem_, this.subtitle_.getEditableCaption().getText());
        this.existingSubText_ = this.subtitle_.getEditableCaption().getText();
    }
    if (this.subtitle_.getEndTime() != this.existingSubEnd_ ||
        this.subtitle_.getStartTime() != this.existingSubStart_) {
        var width =
            (this.subtitle_.getEndTime() - this.subtitle_.getStartTime()) *
                this.pixelsPerMillisecond_;
        width = Math.max(width,unisubs.timeline.TimelineSub.MIN_LENGTH * this.pixelsPerMillisecond_ )

        unisubs.style.setWidth( this.getElement(), width);
        this.existingSubEnd_ = this.subtitle_.getEndTime();
    }
    if (this.subtitle_.getStartTime() != this.existingSubStart_) {
        var left  = this.subtitle_.getStartTime() * this.pixelsPerMillisecond_ -
            this.pixelOffset_;
        unisubs.style.setPosition(
            this.getElement(),
            left, null);
        this.existingSubStart_ = this.subtitle_.getStartTime();
    }
    if ( nextToBeSynced != this.existingSubNextToSync_) {
        var c = goog.dom.classes;
        var unsyncedclass = 'unisubs-timeline-sub-unsynced';
        if (nextToBeSynced) {
            c.add(this.getElement(), unsyncedclass);
            unisubs.style.showElement(this.rightGrabber_, false);
            console.log(this.getElement())
        }
        else {
            c.remove(this.getElement(), unsyncedclass);
            unisubs.style.showElement(this.rightGrabber_, true);
        }
        this.existingSubNextToSync_ = nextToBeSynced;
    }
};
unisubs.timeline.TimelineSub.prototype.disposeInternal = function() {
    unisubs.timeline.TimelineSub.superClass_.disposeInternal.call(this);
    this.documentEventHandler_.dispose();
};
<commit_msg>Fixed width calculation on timeline sub dragging (for unsyched subs this is done elsewhere)<commit_after>// Amara, universalsubtitles.org
//
// Copyright (C) 2012 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('unisubs.timeline.TimelineSub');
/**
* @constructor
* @extends goog.ui.Component
*/
unisubs.timeline.TimelineSub = function(
    subtitle, pixelsPerSecond, opt_pixelOffset, readOnly)
{
    goog.ui.Component.call(this);
    this.subtitle_ = subtitle;
    this.pixelsPerMillisecond_ = pixelsPerSecond  / 1000;
    this.pixelOffset_ = opt_pixelOffset ? opt_pixelOffset : 0;
    this.editing_ = false;
    this.documentEventHandler_ = new goog.events.EventHandler(this);
    this.readOnly_ = readOnly;
};
goog.inherits(unisubs.timeline.TimelineSub, goog.ui.Component);
/**
 * Whether one of the timeline subs is currently being edited.
 */
unisubs.timeline.TimelineSub.currentlyEditing_ = false;
unisubs.timeline.TimelineSub.isCurrentlyEditing = function() {
    return unisubs.timeline.TimelineSub.currentlyEditing_;
};
unisubs.timeline.TimelineSub.EventType = {
    START_EDITING : 'startediting',
    FINISH_EDITING : 'finishediting'
};
unisubs.timeline.TimelineSub.prototype.createDom = function() {
    unisubs.timeline.TimelineSub.superClass_.createDom.call(this);
    this.getElement().className = 'unisubs-timeline-sub';
    var $d = goog.bind(this.getDomHelper().createDom, this.getDomHelper());
    var el = this.getElement();
    el.appendChild(this.textElem_ = $d('div', 'unisubs-subtext'));
    el.appendChild(
        this.leftGrabber_ =
            $d('span', 'unisubs-grabber unisubs-leftGrabber',
               $d('strong')));
    el.appendChild(
        this.rightGrabber_ =
            $d('span', 'unisubs-grabber unisubs-rightGrabber',
               $d('strong')));
    unisubs.style.setProperty(el, 'cursor', 'pointer');
    this.updateValues_();
};
unisubs.timeline.TimelineSub.prototype.enterDocument = function() {
    unisubs.timeline.TimelineSub.superClass_.enterDocument.call(this);
    this.getHandler().listen(
        this.getElement(), 'mouseover', this.onMouseOver_).
        listen(this.getElement(), 'mouseout', this.onMouseOut_).
        listen(this.leftGrabber_, 'mousedown', this.onGrabberMousedown_).
        listen(this.rightGrabber_, 'mousedown', this.onGrabberMousedown_).
        listen(this.subtitle_, unisubs.timeline.Subtitle.CHANGE,
               this.updateValues_);
};
unisubs.timeline.TimelineSub.prototype.onMouseOver_ = function(event) {
    if (this.readOnly_) {
        return;
    }
    if (!unisubs.timeline.TimelineSub.currentlyEditing_)
        this.setGrabberVisibility_(true);
    this.mouseOver_ = true;
};
unisubs.timeline.TimelineSub.prototype.onMouseOut_ = function(event) {
    if (this.readOnly_) {
        return;
    }
    if (event.relatedTarget &&
        !goog.dom.contains(this.getElement(), event.relatedTarget)) {
        if (!this.editing_)
            this.setGrabberVisibility_(false);
        this.mouseOver_ = false;
    }
};
unisubs.timeline.TimelineSub.prototype.onDocMouseMoveLeft_ = function(event) {
    // moving left grabber
    var time = this.grabberMousedownTime_ +
            (event.clientX - this.grabberMousedownClientX_) /
            this.pixelsPerMillisecond_;

    this.subtitle_.getEditableCaption().setStartTime(parseInt(time));
};
unisubs.timeline.TimelineSub.prototype.onDocMouseMoveRight_ = function(event) {
    // moving right grabber
    var time =         this.grabberMousedownTime_ +
            (event.clientX - this.grabberMousedownClientX_) /
            this.pixelsPerMillisecond_;
    this.subtitle_.getEditableCaption().setEndTime(parseInt(time));
};
unisubs.timeline.TimelineSub.prototype.onDocMouseUp_ = function(event) {
    this.editing_ = false;
    unisubs.timeline.TimelineSub.currentlyEditing_ = false;
    this.documentEventHandler_.removeAll();
    if (!this.mouseOver_)
        this.setGrabberVisibility_(false);
    this.dispatchEvent(
        unisubs.timeline.TimelineSub.EventType.FINISH_EDITING);
};
unisubs.timeline.TimelineSub.prototype.getSubtitle = function() {
    return this.subtitle_;
};
unisubs.timeline.TimelineSub.prototype.onGrabberMousedown_ =
    function(event)
{
    var left = goog.dom.contains(this.leftGrabber_, event.target);
    this.editing_ = true;
    this.dispatchEvent(
        unisubs.timeline.TimelineSub.EventType.START_EDITING);
    unisubs.timeline.TimelineSub.currentlyEditing_ = true;
    this.grabberMousedownClientX_ = event.clientX;
    this.grabberMousedownTime_ = left ?
        parseInt(this.subtitle_.getStartTime()) : parseInt(this.subtitle_.getEndTime());
    this.documentEventHandler_.listen(
        document, 'mousemove',
        left ? this.onDocMouseMoveLeft_ : this.onDocMouseMoveRight_);
    this.documentEventHandler_.listen(
        document, 'mouseup', this.onDocMouseUp_);
    event.preventDefault(); // necessary to prevent image dragging in FF3
};
unisubs.timeline.TimelineSub.prototype.setGrabberVisibility_ =
    function(visible)
{
    var c = goog.dom.classes;
    var overClass = 'unisubs-grabber-over';
    if (visible) {
        c.add(this.leftGrabber_, overClass);
        c.add(this.rightGrabber_, overClass);
    }
    else {
        c.remove(this.leftGrabber_, overClass);
        c.remove(this.rightGrabber_, overClass);
    }
};
unisubs.timeline.TimelineSub.prototype.updateValues_ = function() {
    var nextToBeSynced = this.subtitle_.isNextToBeSynced();
    if (this.subtitle_.getEditableCaption().getText() !=
        this.existingSubText_)
    {
        goog.dom.setTextContent(
            this.textElem_, this.subtitle_.getEditableCaption().getText());
        this.existingSubText_ = this.subtitle_.getEditableCaption().getText();
    }
    if (this.subtitle_.getEndTime() != this.existingSubEnd_ ||
        this.subtitle_.getStartTime() != this.existingSubStart_) {
        var width =
            (this.subtitle_.getEndTime() - this.subtitle_.getStartTime()) *
                this.pixelsPerMillisecond_;

        unisubs.style.setWidth( this.getElement(), width);
        this.existingSubEnd_ = this.subtitle_.getEndTime();
    }
    if (this.subtitle_.getStartTime() != this.existingSubStart_) {
        var left  = this.subtitle_.getStartTime() * this.pixelsPerMillisecond_ -
            this.pixelOffset_;
        unisubs.style.setPosition(
            this.getElement(),
            left, null);
        this.existingSubStart_ = this.subtitle_.getStartTime();
    }
    if ( nextToBeSynced != this.existingSubNextToSync_) {
        var c = goog.dom.classes;
        var unsyncedclass = 'unisubs-timeline-sub-unsynced';
        if (nextToBeSynced) {
            c.add(this.getElement(), unsyncedclass);
            unisubs.style.showElement(this.rightGrabber_, false);
            console.log(this.getElement())
        }
        else {
            c.remove(this.getElement(), unsyncedclass);
            unisubs.style.showElement(this.rightGrabber_, true);
        }
        this.existingSubNextToSync_ = nextToBeSynced;
    }
};
unisubs.timeline.TimelineSub.prototype.disposeInternal = function() {
    unisubs.timeline.TimelineSub.superClass_.disposeInternal.call(this);
    this.documentEventHandler_.dispose();
};
<|endoftext|>


<commit_before>var Mode = require('./Mode');

function Tournament() {
    Mode.apply(this, Array.prototype.slice.call(arguments));

    this.ID = 10;
    this.name = "Tournament";
    this.packetLB = 48;

    // Config (1 tick = 1000 ms)
    this.prepTime = 5; // Amount of ticks after the server fills up to wait until starting the game
    this.endTime = 15; // Amount of ticks after someone wins to restart the game
    this.autoFill = false;
    this.autoFillPlayers = 1;
    this.dcTime = 0;

    // Gamemode Specific Variables
    this.gamePhase = 0; // 0 = Waiting for players, 1 = Prepare to start, 2 = Game in progress, 3 = End
    this.contenders = [];
    this.maxContenders = 12;

    this.winner;
    this.timer;
    this.timeLimit = 3600; // in seconds
}

module.exports = Tournament;
Tournament.prototype = new Mode();

// Gamemode Specific Functions

Tournament.prototype.startGamePrep = function (gameServer) {
    this.gamePhase = 1;
    this.timer = this.prepTime; // 10 seconds
};

Tournament.prototype.startGame = function (gameServer) {
    gameServer.run = true;
    this.gamePhase = 2;
    this.getSpectate(); // Gets a random person to spectate
    gameServer.config.playerDisconnectTime = this.dcTime; // Reset config
};

Tournament.prototype.endGame = function (gameServer) {
    this.winner = this.contenders[0];
    this.gamePhase = 3;
    this.timer = this.endTime; // 30 Seconds
};

Tournament.prototype.endGameTimeout = function (gameServer) {
    gameServer.run = false;
    this.gamePhase = 4;
    this.timer = this.endTime; // 30 Seconds
};

Tournament.prototype.fillBots = function (gameServer) {
    // Fills the server with bots if there arent enough players
    var fill = this.maxContenders - this.contenders.length;
    for (var i = 0; i < fill; i++) {
        gameServer.bots.addBot();
    }
};

Tournament.prototype.getSpectate = function () {
    // Finds a random person to spectate
    var index = Math.floor(Math.random() * this.contenders.length);
    this.rankOne = this.contenders[index];
};

Tournament.prototype.prepare = function (gameServer) {
    // Remove all cells
    var len = gameServer.nodes.length;
    for (var i = 0; i < len; i++) {
        var node = gameServer.nodes[0];

        if (!node) {
            continue;
        }

        gameServer.removeNode(node);
    }

    gameServer.bots.loadNames();

    // Pauses the server
    gameServer.run = false;
    this.gamePhase = 0;

    // Get config values
    if (gameServer.config.tourneyAutoFill > 0) {
        this.timer = gameServer.config.tourneyAutoFill;
        this.autoFill = true;
        this.autoFillPlayers = gameServer.config.tourneyAutoFillPlayers;
    }
    // Handles disconnections
    this.dcTime = gameServer.config.playerDisconnectTime;
    gameServer.config.playerDisconnectTime = 0;

    this.prepTime = gameServer.config.tourneyPrepTime;
    this.endTime = gameServer.config.tourneyEndTime;
    this.maxContenders = gameServer.config.tourneyMaxPlayers;

    // Time limit
    this.timeLimit = gameServer.config.tourneyTimeLimit * 60; // in seconds
};

Tournament.prototype.onPlayerDeath = function (gameServer) {
    // Nothing
};

Tournament.prototype.formatTime = function (time) {
    if (time < 0) {
        return "0:00";
    }
    // Format
    var min = Math.floor(this.timeLimit / 60);
    var sec = this.timeLimit % 60;
    sec = (sec > 9) ? sec : "0" + sec.toString();
    return min + ":" + sec;
};

// Override

Tournament.prototype.onServerInit = function (gameServer) {
    this.prepare(gameServer);
};

Tournament.prototype.onPlayerSpawn = function (gameServer, player) {
    // Only spawn players if the game hasnt started yet
    if ((this.gamePhase == 0) && (this.contenders.length < this.maxContenders)) {
        player.setColor(gameServer.getRandomColor()); // Random color
        this.contenders.push(player); // Add to contenders list
        gameServer.spawnPlayer(player);

        if (this.contenders.length == this.maxContenders) {
            // Start the game once there is enough players
            this.startGamePrep(gameServer);
        }
    }
};

Tournament.prototype.onCellRemove = function (cell) {
    var owner = cell.owner,
        human_just_died = false;

    if (owner.cells.length <= 0) {
        // Remove from contenders list
        var index = this.contenders.indexOf(owner);
        if (index != -1) {
            if ('_socket' in this.contenders[index].socket) {
                human_just_died = true;
            }
            this.contenders.splice(index, 1);
        }

        // Victory conditions
        var humans = 0;
        for (var i = 0; i < this.contenders.length; i++) {
            if ('_socket' in this.contenders[i].socket) {
                humans++;
            }
        }

        // the game is over if:
        // 1) there is only 1 player left, OR
        // 2) all the humans are dead, OR
        // 3) the last-but-one human just died
        if ((this.contenders.length == 1 || humans == 0 || (humans == 1 && human_just_died)) && this.gamePhase == 2) {
            this.endGame(cell.owner.gameServer);
        } else {
            // Do stuff
            this.onPlayerDeath(cell.owner.gameServer);
        }
    }
};

Tournament.prototype.updateLB = function (gameServer) {
    gameServer.leaderboardType = this.packetLB;
    var lb = gameServer.leaderboard;

    switch (this.gamePhase) {
        case 0:
            lb[0] = "Waiting for";
            lb[1] = "players: ";
            lb[2] = this.contenders.length + "/" + this.maxContenders;
            if (this.autoFill) {
                if (this.timer <= 0) {
                    this.fillBots(gameServer);
                } else if (this.contenders.length >= this.autoFillPlayers) {
                    this.timer--;
                }
            }
            break;
        case 1:
            lb[0] = "Game starting in";
            lb[1] = this.timer.toString();
            lb[2] = "Good luck!";
            if (this.timer <= 0) {
                // Reset the game
                this.startGame(gameServer);
            } else {
                this.timer--;
            }
            break;
        case 2:
            lb[0] = "Players Remaining";
            lb[1] = this.contenders.length + "/" + this.maxContenders;
            lb[2] = "Time Limit:";
            lb[3] = this.formatTime(this.timeLimit);
            if (this.timeLimit < 0) {
                // Timed out
                this.endGameTimeout(gameServer);
            } else {
                this.timeLimit--;
            }
            break;
        case 3:
            lb[0] = "Congratulations";
            lb[1] = this.winner._name;
            lb[2] = "for winning!";
            if (this.timer <= 0) {
                // Reset the game
                this.onServerInit(gameServer);
                // Respawn starting food
                gameServer.startingFood();
            } else {
                lb[3] = "Game restarting in";
                lb[4] = this.timer.toString();
                this.timer--;
            }
            break;
        case 4:
            lb[0] = "Time Limit";
            lb[1] = "Reached!";
            if (this.timer <= 0) {
                // Reset the game
                this.onServerInit(gameServer);
                // Respawn starting food
                gameServer.startingFood();
            } else {
                lb[2] = "Game restarting in";
                lb[3] = this.timer.toString();
                this.timer--;
            }
        default:
            break;
    }
};
<commit_msg>Delete Tournament.js<commit_after><|endoftext|>


<commit_before>import MagicString, { Bundle } from 'magic-string';
import { walk } from 'estree-walker';
import isReference from '../utils/isReference.js';
import counter from './shared/utils/counter.js';
import flattenReference from '../utils/flattenReference.js';
import globalWhitelist from '../utils/globalWhitelist.js';
import getIntro from './shared/utils/getIntro.js';
import getOutro from './shared/utils/getOutro.js';
import annotateWithScopes from './annotateWithScopes.js';

export default class Generator {
	constructor ( parsed, source, name, names, visitors ) {
		this.parsed = parsed;
		this.source = source;
		this.name = name;
		this.names = names;
		this.visitors = visitors;

		this.imports = [];
		this.helpers = {};
		this.components = {};
		this.events = {};

		// track which properties are needed, so we can provide useful info
		// in dev mode
		this.expectedProperties = {};

		this.elementDepth = 0;

		this.code = new MagicString( source );
		this.getUniqueName = counter( names );
		this.cssId = parsed.css ? `svelte-${parsed.hash}` : '';
		this.usesRefs = false;

		this._callbacks = {};
	}

	addSourcemapLocations ( node ) {
		walk( node, {
			enter: node => {
				this.code.addSourcemapLocation( node.start );
				this.code.addSourcemapLocation( node.end );
			}
		});
	}

	contextualise ( expression, isEventHandler ) {
		const usedContexts = [];
		const dependencies = [];

		const { code, helpers } = this;
		const { contextDependencies, contexts, indexes } = this.current;

		let scope = annotateWithScopes( expression );

		walk( expression, {
			enter ( node, parent, key ) {
				if ( node._scope ) {
					scope = node._scope;
					return;
				}

				if ( isReference( node, parent ) ) {
					const { name } = flattenReference( node );
					if ( scope.has( name ) ) return;

					if ( parent && parent.type === 'CallExpression' && node === parent.callee && helpers[ name ] ) {
						code.prependRight( node.start, `template.helpers.` );
					}

					else if ( name === 'event' && isEventHandler ) {
						// noop
					}

					else if ( name in contexts ) {
						const context = contexts[ name ];
						if ( context !== name ) {
							// this is true for 'reserved' names like `root` and `component`
							code.overwrite( node.start, node.start + name.length, context, true );
						}

						dependencies.push( ...contextDependencies[ name ] );
						if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name );
					}

					else if ( indexes[ name ] ) {
						const context = indexes[ name ];
						if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context );
					}

					else {
						// handle shorthand properties
						if ( parent && parent.type === 'Property' && parent.shorthand ) {
							if ( key === 'key' ) {
								code.appendLeft( node.start, `${name}: ` );
								return;
							}
						}

						if ( globalWhitelist[ name ] ) {
							code.prependRight( node.start, `( '${name}' in root ? root.` );
							code.appendLeft( node.object.end, ` : ${name} )` );
						} else {
							code.prependRight( node.start, `root.` );
						}

						dependencies.push( name );
						if ( !~usedContexts.indexOf( 'root' ) ) usedContexts.push( 'root' );
					}

					this.skip();
				}
			},

			leave ( node ) {
				if ( node._scope ) scope = scope.parent;
			}
		});

		dependencies.forEach( name => {
			this.expectedProperties[ name ] = true;
		});

		return {
			dependencies,
			contexts: usedContexts,
			snippet: `[✂${expression.start}-${expression.end}✂]`,
			string: this.code.slice( expression.start, expression.end )
		};
	}

	fire ( eventName, data ) {
		const handlers = eventName in this._callbacks && this._callbacks[ eventName ].slice();
		if ( !handlers ) return;

		for ( let i = 0; i < handlers.length; i += 1 ) {
			handlers[i].call( this, data );
		}
	}

	generate ( result, options, { name, format } ) {
		if ( this.imports.length ) {
			const statements = [];

			this.imports.forEach( ( declaration, i ) => {
				if ( format === 'es' ) {
					statements.push( this.source.slice( declaration.start, declaration.end ) );
					return;
				}

				const defaultImport = declaration.specifiers.find( x => x.type === 'ImportDefaultSpecifier' || x.type === 'ImportSpecifier' && x.imported.name === 'default' );
				const namespaceImport = declaration.specifiers.find( x => x.type === 'ImportNamespaceSpecifier' );
				const namedImports = declaration.specifiers.filter( x => x.type === 'ImportSpecifier' && x.imported.name !== 'default' );

				const name = ( defaultImport || namespaceImport ) ? ( defaultImport || namespaceImport ).local.name : `__import${i}`;
				declaration.name = name; // hacky but makes life a bit easier later

				namedImports.forEach( specifier => {
					statements.push( `var ${specifier.local.name} = ${name}.${specifier.imported.name}` );
				});

				if ( defaultImport ) {
					statements.push( `${name} = ( ${name} && ${name}.__esModule ) ? ${name}['default'] : ${name};` );
				}
			});

			result = `${statements.join( '\n' )}\n\n${result}`;
		}

		const pattern = /\[✂(\d+)-(\d+)$/;

		const parts = result.split( '✂]' );
		const finalChunk = parts.pop();

		const compiled = new Bundle({ separator: '' });

		function addString ( str ) {
			compiled.addSource({
				content: new MagicString( str )
			});
		}

		const intro = getIntro( format, options, this.imports );
		if ( intro ) addString( intro );

		const { filename } = options;

		// special case — the source file doesn't actually get used anywhere. we need
		// to add an empty file to populate map.sources and map.sourcesContent
		if ( !parts.length ) {
			compiled.addSource({
				filename,
				content: new MagicString( this.source ).remove( 0, this.source.length )
			});
		}

		parts.forEach( str => {
			const chunk = str.replace( pattern, '' );
			if ( chunk ) addString( chunk );

			const match = pattern.exec( str );

			const snippet = this.code.snip( +match[1], +match[2] );

			compiled.addSource({
				filename,
				content: snippet
			});
		});

		addString( finalChunk );
		addString( '\n\n' + getOutro( format, name, options, this.imports ) );

		return {
			code: compiled.toString(),
			map: compiled.generateMap({ includeContent: true, file: options.outputFilename })
		};
	}

	getUniqueNameMaker () {
		return counter( this.names );
	}

	parseJs () {
		const { source } = this;
		const { js } = this.parsed;

		const imports = this.imports;
		const computations = [];
		const templateProperties = {};

		if ( js ) {
			this.addSourcemapLocations( js.content );

			// imports need to be hoisted out of the IIFE
			for ( let i = 0; i < js.content.body.length; i += 1 ) {
				const node = js.content.body[i];
				if ( node.type === 'ImportDeclaration' ) {
					let a = node.start;
					let b = node.end;
					while ( /[ \t]/.test( source[ a - 1 ] ) ) a -= 1;
					while ( source[b] === '\n' ) b += 1;

					imports.push( node );
					this.code.remove( a, b );
				}
			}

			const defaultExport = js.content.body.find( node => node.type === 'ExportDefaultDeclaration' );

			if ( defaultExport ) {
				const finalNode = js.content.body[ js.content.body.length - 1 ];
				if ( defaultExport === finalNode ) {
					// export is last property, we can just return it
					this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `return ` );
				} else {
					// TODO ensure `template` isn't already declared
					this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `var template = ` );

					let i = defaultExport.start;
					while ( /\s/.test( source[ i - 1 ] ) ) i--;

					const indentation = source.slice( i, defaultExport.start );
					this.code.appendLeft( finalNode.end, `\n\n${indentation}return template;` );
				}

				defaultExport.declaration.properties.forEach( prop => {
					templateProperties[ prop.key.name ] = prop;
				});

				this.code.prependRight( js.content.start, 'var template = (function () {' );
			} else {
				this.code.prependRight( js.content.start, '(function () {' );
			}

			this.code.appendLeft( js.content.end, '}());' );

			[ 'helpers', 'events', 'components' ].forEach( key => {
				if ( templateProperties[ key ] ) {
					templateProperties[ key ].value.properties.forEach( prop => {
						this[ key ][ prop.key.name ] = prop.value;
					});
				}
			});

			if ( templateProperties.computed ) {
				const dependencies = new Map();

				templateProperties.computed.value.properties.forEach( prop => {
					const key = prop.key.name;
					const value = prop.value;

					const deps = value.params.map( param => param.type === 'AssignmentPattern' ? param.left.name : param.name );
					dependencies.set( key, deps );
				});

				const visited = new Set();

				function visit ( key ) {
					if ( !dependencies.has( key ) ) return; // not a computation

					if ( visited.has( key ) ) return;
					visited.add( key );

					const deps = dependencies.get( key );
					deps.forEach( visit );

					computations.push({ key, deps });
				}

				templateProperties.computed.value.properties.forEach( prop => visit( prop.key.name ) );
			}
		}

		return {
			computations,
			templateProperties
		};
	}

	on ( eventName, handler ) {
		const handlers = this._callbacks[ eventName ] || ( this._callbacks[ eventName ] = [] );
		handlers.push( handler );
	}

	pop () {
		const tail = this.current;
		this.current = tail.parent;

		return tail;
	}

	push ( fragment ) {
		const newFragment = Object.assign( {}, this.current, fragment, {
			parent: this.current
		});

		this.current = newFragment;
	}

	visit ( node ) {
		const visitor = this.visitors[ node.type ];
		if ( !visitor ) throw new Error( `Not implemented: ${node.type}` );

		if ( visitor.enter ) visitor.enter( this, node );

		if ( visitor.type === 'Element' ) {
			this.elementDepth += 1;
		}

		if ( node.children ) {
			node.children.forEach( child => {
				this.visit( child );
			});
		}

		if ( visitor.type === 'Element' ) {
			this.elementDepth -= 1;
		}

		if ( visitor.leave ) visitor.leave( this, node );
	}
}

<commit_msg>allow using whitelisted globals themselves in tags<commit_after>import MagicString, { Bundle } from 'magic-string';
import { walk } from 'estree-walker';
import isReference from '../utils/isReference.js';
import counter from './shared/utils/counter.js';
import flattenReference from '../utils/flattenReference.js';
import globalWhitelist from '../utils/globalWhitelist.js';
import getIntro from './shared/utils/getIntro.js';
import getOutro from './shared/utils/getOutro.js';
import annotateWithScopes from './annotateWithScopes.js';

export default class Generator {
	constructor ( parsed, source, name, names, visitors ) {
		this.parsed = parsed;
		this.source = source;
		this.name = name;
		this.names = names;
		this.visitors = visitors;

		this.imports = [];
		this.helpers = {};
		this.components = {};
		this.events = {};

		// track which properties are needed, so we can provide useful info
		// in dev mode
		this.expectedProperties = {};

		this.elementDepth = 0;

		this.code = new MagicString( source );
		this.getUniqueName = counter( names );
		this.cssId = parsed.css ? `svelte-${parsed.hash}` : '';
		this.usesRefs = false;

		this._callbacks = {};
	}

	addSourcemapLocations ( node ) {
		walk( node, {
			enter: node => {
				this.code.addSourcemapLocation( node.start );
				this.code.addSourcemapLocation( node.end );
			}
		});
	}

	contextualise ( expression, isEventHandler ) {
		const usedContexts = [];
		const dependencies = [];

		const { code, helpers } = this;
		const { contextDependencies, contexts, indexes } = this.current;

		let scope = annotateWithScopes( expression );

		walk( expression, {
			enter ( node, parent, key ) {
				if ( node._scope ) {
					scope = node._scope;
					return;
				}

				if ( isReference( node, parent ) ) {
					const { name } = flattenReference( node );
					if ( scope.has( name ) ) return;

					if ( parent && parent.type === 'CallExpression' && node === parent.callee && helpers[ name ] ) {
						code.prependRight( node.start, `template.helpers.` );
					}

					else if ( name === 'event' && isEventHandler ) {
						// noop
					}

					else if ( name in contexts ) {
						const context = contexts[ name ];
						if ( context !== name ) {
							// this is true for 'reserved' names like `root` and `component`
							code.overwrite( node.start, node.start + name.length, context, true );
						}

						dependencies.push( ...contextDependencies[ name ] );
						if ( !~usedContexts.indexOf( name ) ) usedContexts.push( name );
					}

					else if ( indexes[ name ] ) {
						const context = indexes[ name ];
						if ( !~usedContexts.indexOf( context ) ) usedContexts.push( context );
					}

					else {
						// handle shorthand properties
						if ( parent && parent.type === 'Property' && parent.shorthand ) {
							if ( key === 'key' ) {
								code.appendLeft( node.start, `${name}: ` );
								return;
							}
						}

						if ( globalWhitelist[ name ] ) {
							code.prependRight( node.start, `( '${name}' in root ? root.` );
							code.appendLeft( node.object ? node.object.end : node.end, ` : ${name} )` );
						} else {
							code.prependRight( node.start, `root.` );
						}

						dependencies.push( name );
						if ( !~usedContexts.indexOf( 'root' ) ) usedContexts.push( 'root' );
					}

					this.skip();
				}
			},

			leave ( node ) {
				if ( node._scope ) scope = scope.parent;
			}
		});

		dependencies.forEach( name => {
			this.expectedProperties[ name ] = true;
		});

		return {
			dependencies,
			contexts: usedContexts,
			snippet: `[✂${expression.start}-${expression.end}✂]`,
			string: this.code.slice( expression.start, expression.end )
		};
	}

	fire ( eventName, data ) {
		const handlers = eventName in this._callbacks && this._callbacks[ eventName ].slice();
		if ( !handlers ) return;

		for ( let i = 0; i < handlers.length; i += 1 ) {
			handlers[i].call( this, data );
		}
	}

	generate ( result, options, { name, format } ) {
		if ( this.imports.length ) {
			const statements = [];

			this.imports.forEach( ( declaration, i ) => {
				if ( format === 'es' ) {
					statements.push( this.source.slice( declaration.start, declaration.end ) );
					return;
				}

				const defaultImport = declaration.specifiers.find( x => x.type === 'ImportDefaultSpecifier' || x.type === 'ImportSpecifier' && x.imported.name === 'default' );
				const namespaceImport = declaration.specifiers.find( x => x.type === 'ImportNamespaceSpecifier' );
				const namedImports = declaration.specifiers.filter( x => x.type === 'ImportSpecifier' && x.imported.name !== 'default' );

				const name = ( defaultImport || namespaceImport ) ? ( defaultImport || namespaceImport ).local.name : `__import${i}`;
				declaration.name = name; // hacky but makes life a bit easier later

				namedImports.forEach( specifier => {
					statements.push( `var ${specifier.local.name} = ${name}.${specifier.imported.name}` );
				});

				if ( defaultImport ) {
					statements.push( `${name} = ( ${name} && ${name}.__esModule ) ? ${name}['default'] : ${name};` );
				}
			});

			result = `${statements.join( '\n' )}\n\n${result}`;
		}

		const pattern = /\[✂(\d+)-(\d+)$/;

		const parts = result.split( '✂]' );
		const finalChunk = parts.pop();

		const compiled = new Bundle({ separator: '' });

		function addString ( str ) {
			compiled.addSource({
				content: new MagicString( str )
			});
		}

		const intro = getIntro( format, options, this.imports );
		if ( intro ) addString( intro );

		const { filename } = options;

		// special case — the source file doesn't actually get used anywhere. we need
		// to add an empty file to populate map.sources and map.sourcesContent
		if ( !parts.length ) {
			compiled.addSource({
				filename,
				content: new MagicString( this.source ).remove( 0, this.source.length )
			});
		}

		parts.forEach( str => {
			const chunk = str.replace( pattern, '' );
			if ( chunk ) addString( chunk );

			const match = pattern.exec( str );

			const snippet = this.code.snip( +match[1], +match[2] );

			compiled.addSource({
				filename,
				content: snippet
			});
		});

		addString( finalChunk );
		addString( '\n\n' + getOutro( format, name, options, this.imports ) );

		return {
			code: compiled.toString(),
			map: compiled.generateMap({ includeContent: true, file: options.outputFilename })
		};
	}

	getUniqueNameMaker () {
		return counter( this.names );
	}

	parseJs () {
		const { source } = this;
		const { js } = this.parsed;

		const imports = this.imports;
		const computations = [];
		const templateProperties = {};

		if ( js ) {
			this.addSourcemapLocations( js.content );

			// imports need to be hoisted out of the IIFE
			for ( let i = 0; i < js.content.body.length; i += 1 ) {
				const node = js.content.body[i];
				if ( node.type === 'ImportDeclaration' ) {
					let a = node.start;
					let b = node.end;
					while ( /[ \t]/.test( source[ a - 1 ] ) ) a -= 1;
					while ( source[b] === '\n' ) b += 1;

					imports.push( node );
					this.code.remove( a, b );
				}
			}

			const defaultExport = js.content.body.find( node => node.type === 'ExportDefaultDeclaration' );

			if ( defaultExport ) {
				const finalNode = js.content.body[ js.content.body.length - 1 ];
				if ( defaultExport === finalNode ) {
					// export is last property, we can just return it
					this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `return ` );
				} else {
					// TODO ensure `template` isn't already declared
					this.code.overwrite( defaultExport.start, defaultExport.declaration.start, `var template = ` );

					let i = defaultExport.start;
					while ( /\s/.test( source[ i - 1 ] ) ) i--;

					const indentation = source.slice( i, defaultExport.start );
					this.code.appendLeft( finalNode.end, `\n\n${indentation}return template;` );
				}

				defaultExport.declaration.properties.forEach( prop => {
					templateProperties[ prop.key.name ] = prop;
				});

				this.code.prependRight( js.content.start, 'var template = (function () {' );
			} else {
				this.code.prependRight( js.content.start, '(function () {' );
			}

			this.code.appendLeft( js.content.end, '}());' );

			[ 'helpers', 'events', 'components' ].forEach( key => {
				if ( templateProperties[ key ] ) {
					templateProperties[ key ].value.properties.forEach( prop => {
						this[ key ][ prop.key.name ] = prop.value;
					});
				}
			});

			if ( templateProperties.computed ) {
				const dependencies = new Map();

				templateProperties.computed.value.properties.forEach( prop => {
					const key = prop.key.name;
					const value = prop.value;

					const deps = value.params.map( param => param.type === 'AssignmentPattern' ? param.left.name : param.name );
					dependencies.set( key, deps );
				});

				const visited = new Set();

				function visit ( key ) {
					if ( !dependencies.has( key ) ) return; // not a computation

					if ( visited.has( key ) ) return;
					visited.add( key );

					const deps = dependencies.get( key );
					deps.forEach( visit );

					computations.push({ key, deps });
				}

				templateProperties.computed.value.properties.forEach( prop => visit( prop.key.name ) );
			}
		}

		return {
			computations,
			templateProperties
		};
	}

	on ( eventName, handler ) {
		const handlers = this._callbacks[ eventName ] || ( this._callbacks[ eventName ] = [] );
		handlers.push( handler );
	}

	pop () {
		const tail = this.current;
		this.current = tail.parent;

		return tail;
	}

	push ( fragment ) {
		const newFragment = Object.assign( {}, this.current, fragment, {
			parent: this.current
		});

		this.current = newFragment;
	}

	visit ( node ) {
		const visitor = this.visitors[ node.type ];
		if ( !visitor ) throw new Error( `Not implemented: ${node.type}` );

		if ( visitor.enter ) visitor.enter( this, node );

		if ( visitor.type === 'Element' ) {
			this.elementDepth += 1;
		}

		if ( node.children ) {
			node.children.forEach( child => {
				this.visit( child );
			});
		}

		if ( visitor.type === 'Element' ) {
			this.elementDepth -= 1;
		}

		if ( visitor.leave ) visitor.leave( this, node );
	}
}
<|endoftext|>


<commit_before>var _ = require('underscore');
var Seekbar = require('./Seekbar');
var Event = require('../../core/Event');

var definer = require('../../helpers/definer');
var WindowEvent = definer.defineEvent({
    SCROLL: 'uonejs.Window.SCROLL',
    CONTENTRESIZE: 'uonejs.Window.CONTENTRESIZE'
});

var ResizeNotifyConfig = {
    originWidth: undefined,
    originHeight: undefined,
    update: function() {
        var A = this, I = this.one, S = I.state;
        if((S.width != A.originWidth) || (S.height != A.originHeight)) {
            I.fire(new WindowEvent({
                type: WindowEvent.CONTENTRESIZE,
                width: S.width,
                height: S.height
            }));
            A.originWidth = S.width;
            A.originHeight = S.height;
        }
    }
}

var SupportWindowScrollConfig = {
    afterMount: function() {
        var A = this, I = A.one, S = I.state, GS = I.getGroupState();
        I.on('mousedrag', function(e) {
            if(S.horizontal) {
                GS.offsetX += (e.data.paternalDistance.x / S.occupancy);
                GS.offsetX = Math.max(GS.offsetX, 0);
                GS.offsetX = Math.min(GS.offsetX, GS.realWidth - GS.visualWidth);
            }
            else {
                GS.offsetY += (e.data.paternalDistance.y / S.occupancy);
                GS.offsetY = Math.max(GS.offsetY, 0);
                GS.offsetY = Math.min(GS.offsetY, GS.realHeight - GS.visualHeight);
            }
        });
    }
};

/*
 * A window which can contain scalable content.
 *
 * @tempalate Window
 *
 * @option width
 * @option height
 * @option seekbarBreadth
 *
 * @state offsetX
 * @state offsetY
 * @state realWidth
 * @state realHeight
 * @state visualWidth
 * @state visualHeight
 *
 * @action all actions will be added to leader.
 *
 * @children all children will be added into a container.
 * */
module.exports = definer.defineTemplate(function(config) {
    config.options = config.options || {};
    var options = _.defaults(config.options, {
        group: 'Window',
        offsetX: 0,
        offsetY: 0
    });
    /*
     * A new concept about one's width and height will turn.
     * And the logic above might be remnant.
     */
    var firstChild = config.children[0];
    if(firstChild) {
        firstChild.actions = firstChild.actions || [];
        firstChild.actions.push({
            originWidth: undefined,
            originHeight: undefined,
            update: function() {
                var A = this, I = this.one, S = I.state;
                if((S.width != A.originWidth) || (S.height != A.originHeight)) {
                    I.fire(new WindowEvent({
                        type: WindowEvent.CONTENTRESIZE,
                        width: S.width,
                        height: S.height
                    }));
                    A.originWidth = S.width;
                    A.originHeight = S.height;
                }
            }
        });
    }
    var children = [
        {
            template: Seekbar,
            options: {
                name: 'horizontalSeekbar',
                horizontal: true,
                referencePoint: "leftTop"
            },
            actions: [" \
                if (GS.visualHeight == GS.height) { \
                    I.stopDraw(); \
                    I.stopUpdate(); \
                    return; \
                } \
                I.state.x = 0;\
                I.state.y = GS.visualHeight;\
                S.occupancy = GS.visualWidth/GS.realWidth; \
                S.deviation = GS.offsetX/GS.realWidth; \
                S.length = GS.visualWidth;\
                S.breadth = GS.seekbarBreadth;\
            ",
            {
                afterMount: " \
                    I.on('mousedrag', function(e) { \
                        GS.offsetX += (e.data.paternalDistance.x / S.occupancy); \
                        GS.offsetX = Math.max(GS.offsetX, 0); \
                        GS.offsetX = Math.min(GS.offsetX, GS.realWidth - GS.visualWidth); \
                    });"
            }]

        },
        {
            template: Seekbar,
            options: {
                name: 'verticalSeekbar',
                horizontal: false,
                referencePoint: "leftTop"
            },
            actions: [" \
                if (GS.visualWidth == GS.width) { \
                    I.stopDraw(); \
                    I.stopUpdate(); \
                    return; \
                } \
                I.state.x = GS.visualWidth;\
                I.state.y = 0;\
                S.occupancy = GS.visualHeight/GS.realHeight; \
                S.deviation = GS.offsetY/GS.realHeight; \
                S.length = GS.visualHeight;\
                S.breadth = GS.seekbarBreadth;\
            ",
            {
                afterMount: " \
                    I.on('mousedrag', function(e) { \
                        GS.offsetY += (e.data.paternalDistance.y / S.occupancy); \
                        GS.offsetY = Math.max(GS.offsetY, 0); \
                        GS.offsetY = Math.min(GS.offsetY, GS.realHeight - GS.visualHeight); \
                    });"
            }]
        }, {
            alias: 'Cliper',
            options: {
                name: 'container'
            },
            actions: [" \
                S.startX = 0; \
                S.startY = 0; \
                S.width = GS.visualWidth; \
                S.height = GS.visualHeight; \
            "],
            children: [{
                actions: [" \
                    S.x = -GS.offsetX; \
                    S.y = -GS.offsetY; \
                "],
                children: config.children
            }]
        }
    ];
    var actions = [" \
        if (S.visualHeight == undefined) S.visualHeight = S.height; \
        if (S.visualWidth == undefined) S.visualWidth = S.width; \
        if (S.visualWidth < S.realWidth) \
            S.visualHeight = S.height - S.seekbarBreadth; \
        else \
            S.visualHeight = S.height; \
        if (S.visualHeight < S.realHeight) \
            S.visualWidth = S.width - S.seekbarBreadth; \
        else \
            S.visualWidth = S.width; \
    ",
    {
        afterMount: function() {
            var I = this.one, S = I.state;
            I.on(WindowEvent.CONTENTRESIZE, function(e) {
                if(e.data.width != undefined)
                    S.realWidth = e.data.width;
                if(e.data.height != undefined)
                    S.realHeight = e.data.height;
                e.stopPropagation();
            });
        }
    }];
    if(config.actions)
        actions = actions.concat(config.actions);
    return {
        options: options,
        children: children,
        actions: actions
    };
}, 'Window');

module.exports.ResizeNotifyConfig = ResizeNotifyConfig;
<commit_msg>nothing<commit_after>var _ = require('underscore');
var Seekbar = require('./Seekbar');
var Event = require('../../core/Event');

var definer = require('../../helpers/definer');
var WindowEvent = definer.defineEvent({
    SCROLL: 'uonejs.Window.SCROLL',
    CONTENTRESIZE: 'uonejs.Window.CONTENTRESIZE'
});

/*
 * A window which can contain scalable content.
 *
 * @tempalate Window
 *
 * @option width
 * @option height
 * @option seekbarBreadth
 *
 * @state offsetX
 * @state offsetY
 * @state realWidth
 * @state realHeight
 * @state visualWidth
 * @state visualHeight
 *
 * @action all actions will be added to leader.
 *
 * @children all children will be added into a container.
 * */
module.exports = definer.defineTemplate(function(config) {
    config.options = config.options || {};
    var options = _.defaults(config.options, {
        group: 'Window',
        offsetX: 0,
        offsetY: 0
    });
    /*
     * A new concept about one's width and height will turn.
     * And the logic above might be remnant.
     */
    var firstChild = config.children[0];
    if(firstChild) {
        firstChild.actions = firstChild.actions || [];
        firstChild.actions.push({
            originWidth: undefined,
            originHeight: undefined,
            update: function() {
                var A = this, I = this.one, S = I.state;
                if((S.width != A.originWidth) || (S.height != A.originHeight)) {
                    I.fire(new WindowEvent({
                        type: WindowEvent.CONTENTRESIZE,
                        width: S.width,
                        height: S.height
                    }));
                    A.originWidth = S.width;
                    A.originHeight = S.height;
                }
            }
        });
    }
    var children = [
        {
            template: Seekbar,
            options: {
                name: 'horizontalSeekbar',
                horizontal: true,
                referencePoint: "leftTop"
            },
            actions: [" \
                if (GS.visualHeight == GS.height) { \
                    I.stopDraw(); \
                    I.stopUpdate(); \
                    return; \
                } \
                I.state.x = 0;\
                I.state.y = GS.visualHeight;\
                S.occupancy = GS.visualWidth/GS.realWidth; \
                S.deviation = GS.offsetX/GS.realWidth; \
                S.length = GS.visualWidth;\
                S.breadth = GS.seekbarBreadth;\
            ",
            {
                afterMount: " \
                    I.on('mousedrag', function(e) { \
                        GS.offsetX += (e.data.paternalDistance.x / S.occupancy); \
                        GS.offsetX = Math.max(GS.offsetX, 0); \
                        GS.offsetX = Math.min(GS.offsetX, GS.realWidth - GS.visualWidth); \
                    });"
            }]

        },
        {
            template: Seekbar,
            options: {
                name: 'verticalSeekbar',
                horizontal: false,
                referencePoint: "leftTop"
            },
            actions: [" \
                if (GS.visualWidth == GS.width) { \
                    I.stopDraw(); \
                    I.stopUpdate(); \
                    return; \
                } \
                I.state.x = GS.visualWidth;\
                I.state.y = 0;\
                S.occupancy = GS.visualHeight/GS.realHeight; \
                S.deviation = GS.offsetY/GS.realHeight; \
                S.length = GS.visualHeight;\
                S.breadth = GS.seekbarBreadth;\
            ",
            {
                afterMount: " \
                    I.on('mousedrag', function(e) { \
                        GS.offsetY += (e.data.paternalDistance.y / S.occupancy); \
                        GS.offsetY = Math.max(GS.offsetY, 0); \
                        GS.offsetY = Math.min(GS.offsetY, GS.realHeight - GS.visualHeight); \
                    });"
            }]
        }, {
            alias: 'Cliper',
            options: {
                name: 'container'
            },
            actions: [" \
                S.startX = 0; \
                S.startY = 0; \
                S.width = GS.visualWidth; \
                S.height = GS.visualHeight; \
            "],
            children: [{
                actions: [" \
                    S.x = -GS.offsetX; \
                    S.y = -GS.offsetY; \
                "],
                children: config.children
            }]
        }
    ];
    var actions = [" \
        if (S.visualHeight == undefined) S.visualHeight = S.height; \
        if (S.visualWidth == undefined) S.visualWidth = S.width; \
        if (S.visualWidth < S.realWidth) \
            S.visualHeight = S.height - S.seekbarBreadth; \
        else \
            S.visualHeight = S.height; \
        if (S.visualHeight < S.realHeight) \
            S.visualWidth = S.width - S.seekbarBreadth; \
        else \
            S.visualWidth = S.width; \
    ",
    {
        afterMount: function() {
            var I = this.one, S = I.state;
            I.on(WindowEvent.CONTENTRESIZE, function(e) {
                if(e.data.width != undefined)
                    S.realWidth = e.data.width;
                if(e.data.height != undefined)
                    S.realHeight = e.data.height;
                e.stopPropagation();
            });
        }
    }];
    if(config.actions)
        actions = actions.concat(config.actions);
    return {
        options: options,
        children: children,
        actions: actions
    };
}, 'Window');
<|endoftext|>