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

var stitcher = require('../../bin/TsStitcher.node');
require('../adIntegration/KalturaAdIntegrationHandler');

var kaltura = module.exports = require('../KalturaManager');

/**
 * @service media
 */
var KalturaMediaManager = function(){
};
util.inherits(KalturaMediaManager, kaltura.KalturaManager);

KalturaMediaManager.TS_PACKET_LENGTH = 188;
KalturaMediaManager.FILE_CHUNK_SIZE = 2500 * KalturaMediaManager.TS_PACKET_LENGTH;

KalturaMediaManager.PBA_CALL_AGAIN = 0;
KalturaMediaManager.PBA_GET_NEXT_CHUNK = 1;
KalturaMediaManager.PBA_CLONE_CURRENT_CHUNK = 2;

KalturaMediaManager.ALIGN_LEFT = 0;
KalturaMediaManager.ALIGN_MIDDLE =	1;
KalturaMediaManager.ALIGN_RIGHT = 	2;

KalturaMediaManager.CHUNK_TYPE_INVALID  =	-1;
KalturaMediaManager.CHUNK_TYPE_TS_HEADER = 0;
KalturaMediaManager.CHUNK_TYPE_PRE_AD = 	 1;
KalturaMediaManager.CHUNK_TYPE_POST_AD = 	 2;

KalturaMediaManager.CHUNK_TYPE_AD = 		 5;
KalturaMediaManager.CHUNK_TYPE_BLACK = 	 4;

KalturaMediaManager.prototype.outputStitchedSegment = function(outputLayout, outputState, curChunk, preAdKey, adKeys, blackKey, postAdKey, response) {
	if (!curChunk) {
		// not much to do about this since we already returned the response headers
		response.log('failed to get chunk from memcache');
		response.end();
		return;
	}

	do {
		var processResult = stitcher.processChunk(
			outputLayout,
			curChunk,
			outputState);

		if (processResult.chunkOutputEnd > 0) {
			response.log('Writing ' + processResult.chunkOutputStart + '..' + processResult.chunkOutputEnd);
			var curSlice = curChunk.slice(processResult.chunkOutputStart, processResult.chunkOutputEnd);
			response.write(curSlice);
		}

		if (processResult.outputBuffer) {
			response.log('Writing extra buffer of size ' + processResult.outputBuffer.length);
			response.write(processResult.outputBuffer);
		}

		if (processResult.action == KalturaMediaManager.PBA_CLONE_CURRENT_CHUNK)
		{
			response.log('Cloning chunk buffer');
			chunkClone = new Buffer(curChunk.length);
			curChunk.copy(chunkClone);
			curChunk = chunkClone;
		}
	} while (processResult.action != KalturaMediaManager.PBA_GET_NEXT_CHUNK);

	curChunk = null;		// no longer need the chunk

	var chunkIndex = Math.floor(outputState.chunkStartOffset / KalturaMediaManager.FILE_CHUNK_SIZE);
	var videoKey = null;

	switch (outputState.chunkType) {
	case KalturaMediaManager.CHUNK_TYPE_PRE_AD:
		videoKey = preAdKey + '-' + chunkIndex;
		break;
	case KalturaMediaManager.CHUNK_TYPE_BLACK:
		videoKey = blackKey + '-' + chunkIndex;
		break;
	case KalturaMediaManager.CHUNK_TYPE_POST_AD:
		videoKey = postAdKey + '-' + chunkIndex;
		break;
	case KalturaMediaManager.CHUNK_TYPE_TS_HEADER:
		videoKey = preAdKey + '-header';
		break;
	default:
		for(var i=0; i<adKeys.length; i++){
			if(KalturaMediaManager.CHUNK_TYPE_AD + i == outputState.chunkType){
				videoKey = adKeys[i] + '-' + chunkIndex;
				break;
			}
		}
		if(!videoKey){
			response.log('Request completed');
			response.end();
			return;
		}
	}

	response.log('Getting ' + videoKey);
	var This = this;
	KalturaCache.getBinary(videoKey, function (curChunk) {
		outputState.chunkStartOffset = chunkIndex * KalturaMediaManager.FILE_CHUNK_SIZE;
		This.outputStitchedSegment(outputLayout, outputState, curChunk, preAdKey, adKeys, blackKey, postAdKey, response);
	});
};

KalturaMediaManager.prototype.get = function(request, response, params){
	// TODO verify the call is from the CDN
	if (!params.e) {
		response.dir(params);
		response.error('Missing arguments [e]');
		this.errorMissingParameter(response);
		return;
	}

	params = this.decrypt(params);
	response.dir(params);
	this.serve(request, response, params.serverAdId, params.cuePointId, params.renditionId, params.encodingId, parseInt(params.segmentIndex), parseInt(params.outputStart), parseInt(params.outputEnd), params.originalUrl);
};

KalturaMediaManager.prototype.serve = function(request, response, serverAdId, cuePointId, renditionId, encodingId, segmentIndex, outputStart, outputEnd, originalUrl){
	var This = this;

	var preSegmentId = KalturaCache.getPreSegmentId(cuePointId, renditionId);
	var postSegmentId = KalturaCache.getPostSegmentId(cuePointId, renditionId);
	var preAdMetadataKey = KalturaCache.getSegmentMediaMetadata(preSegmentId);
	var blackMetadataKey = KalturaCache.getBlackMediaMetadata(encodingId);
	var postAdMetadataKey = KalturaCache.getSegmentMediaMetadata(postSegmentId);
	var adMetadataKeys = [];
	serverAdId = JSON.parse(serverAdId);
	var adKeys = [];
	for(var i=0; i<serverAdId.length; i++){
		adMetadataKeys.push(KalturaCache.getAdMediaMetadata(serverAdId[i].id));
		adKeys.push(KalturaCache.getAdMedia(serverAdId[i].id));
	}

	var cacheKeys = adMetadataKeys.concat([preAdMetadataKey, blackMetadataKey, postAdMetadataKey]);
	KalturaCache.getMultiBinary(cacheKeys, function(data){
		preAdMetadata = data[preAdMetadataKey];
		blackMetadata = data[blackMetadataKey];
		postAdMetadata = data[postAdMetadataKey];

		adsMetadata = [];
		for(var i=0; i<adMetadataKeys.length; i++){
			if(data[adMetadataKeys[i]]){
				var adMetadata =
				{
					adChunkType: KalturaMediaManager.CHUNK_TYPE_AD + i,
					ad: data[adMetadataKeys[i]],
					fillerChunkType: KalturaMediaManager.CHUNK_TYPE_BLACK,
					filler: blackMetadata,
					startPos: serverAdId[i].startPos,
					endPos: serverAdId[i].endPos,
					alignment: KalturaMediaManager.ALIGN_LEFT
				};
				adsMetadata.push(adMetadata);
				response.log('Added ad metadata: adChunkType [' + adMetadata.adChunkType + '], startPos [' + serverAdId[i].startPos + '] endPos [' + serverAdId[i].endPos + ']');
			}
			else{
				delete adKeys[i];
			}
		}

		if (!preAdMetadata){
			response.log('Pre-Ad metadata is null, redirecting to original ts');
			response.writeHead(302, {'Location' : originalUrl});
			response.end();
		}
		else{
			response.log('Pre-Ad metadata length ' + preAdMetadata.length);

			if (!adsMetadata.length){
				response.log('Ad metadata is null');
				//set black ad as a dummy ad metadata
				var adMetadata =
				{
					adChunkType: KalturaMediaManager.CHUNK_TYPE_BLACK,
					ad: blackMetadata,
					fillerChunkType: KalturaMediaManager.CHUNK_TYPE_BLACK,
					filler: blackMetadata,
					startPos: 0,
					endPos: 0,
					alignment: KalturaMediaManager.ALIGN_LEFT
				};
				adsMetadata.push(adMetadata);
			}
			else{
				response.log('Ad metadata length ' + adsMetadata.length);
				// build the layout of the output TS
				var outputLayout = stitcher.buildLayout(
						preAdMetadata,
						postAdMetadata,
						adsMetadata,
						segmentIndex,
						outputStart,
						outputEnd);

				// free the metadata buffers, we don't need them anymore
				preAdMetadata = null;
				adsMetadata = null;
				blackMetadata = null;
				postAdMetadata = null;

				var preAdKey = KalturaCache.getSegmentMedia(preSegmentId);
				var blackKey = KalturaCache.getBlackMedia(encodingId);
				var postAdKey = KalturaCache.getSegmentMedia(postSegmentId);

				// output the TS
				response.writeHead(200, {
					'Content-Type': 'video/MP2T',
					'Cache-Control': KalturaConfig.config.media.cdnCacheScope + ', max-age=' + KalturaConfig.config.media.cdnMaxAge + ', max-stale=0'
					});

				This.outputStitchedSegment(outputLayout, {}, new Buffer(0), preAdKey, adKeys, blackKey, postAdKey, response);
			}
		}
	});
};

/**
 * Returns the segment media from cache
 *
 * @action media.segment
 */
KalturaMediaManager.prototype.segment = function(request, response, params){
	var This = this;
	if (!params.e) {
		response.dir(params);
		response.error('Missing arguments [e]');
		this.errorMissingParameter(response);
		return;
	}

	params = this.decrypt(params);
	response.dir(params);

	var requiredParams = [
		'cuePointId',
		'renditionId',
		'encodingId',
		'segmentIndex',
		'outputStart',
		'outputEnd',
		'sessionId',
		'adStart',
		'originalUrl'
	];

	var missingParams = this.getMissingParams(params, requiredParams);
	if(missingParams.length){
		response.error('Missing arguments [' + missingParams.join(', ') + ']');
		this.errorMissingParameter(response);
		return;
	}

	var serverAdIdKey = KalturaCache.getServerAdId(params.cuePointId, params.encodingId, params.sessionId);

	KalturaCache.get(serverAdIdKey, function(serverAdIds){
		if(!serverAdIds){
			response.log('No ad found, redirecting to original ts');
			response.writeHead(302, {'Location' : params.originalUrl});
			response.end();
		}
		else{
			var outputStart = parseInt(params.outputStart);
			var outputEnd = parseInt(params.outputEnd);
			var adStart = parseInt(params.adStart);
			var serverAdId = [];
			var adsSequence = [];
			var currentAdsIdx = [];
			var sequenceDuration = adStart;
			var startSequenceIndex = 0;
			var startPos = adStart;
			var endPos = adStart;
			for(var i = 0; i<= serverAdIds.length; i++){
				if(!serverAdIds[i]){
					continue;
				}
				if(sequenceDuration + serverAdIds[i].duration <= outputStart){
					sequenceDuration += serverAdIds[i].duration;
					startSequenceIndex = i;
				}
				startPos = endPos;
				endPos += serverAdIds[i].duration;
				adsSequence.push({id:serverAdIds[i].id, startPos:startPos, endPos:endPos, sequence:i});
			}

			for(var j = startSequenceIndex; adsSequence[j] && (adsSequence[j].startPos <= outputEnd || !outputEnd); j++){
				currentAdsIdx.push(adsSequence[j].sequence);
				serverAdId.push({id:adsSequence[j].id, startPos:adsSequence[j].startPos, endPos:adsSequence[j].endPos});
			}

			if(serverAdId.length == 0){
				response.log('No ad match the ad sequence, redirecting to original ts');
				response.writeHead(302, {'Location' : params.originalUrl});
				response.end();
			}
			else{
				response.log('Handling server ad Ids: ' + JSON.stringify(serverAdId));
				params.serverAdId = JSON.stringify(serverAdId);

				var trackingKey = KalturaCache.getAdTrackingId(params.cuePointId, params.sessionId);
				KalturaAdIntegrationHandler.sendBeacon(trackingKey, currentAdsIdx, sequenceDuration, outputStart, outputEnd, adStart);

				delete params.sessionId;
				response.dir(params);

				var redirectUrl = This.getPlayServerUrl('media', 'get', params.partnerId, null, params);
				response.writeHead(302, {'Location' : redirectUrl});
				response.end();
			}
		}

	}, function(err){
		response.log('No ad found, redirecting to original ts');
		response.writeHead(302, {'Location' : params.originalUrl});
		response.end();
	});
};

module.exports.KalturaMediaManager = KalturaMediaManager;
<commit_msg>bug fix<commit_after>
var url = require('url');
var util = require('util');

var stitcher = require('../../bin/TsStitcher.node');
require('../adIntegration/KalturaAdIntegrationHandler');

var kaltura = module.exports = require('../KalturaManager');

/**
 * @service media
 */
var KalturaMediaManager = function(){
};
util.inherits(KalturaMediaManager, kaltura.KalturaManager);

KalturaMediaManager.TS_PACKET_LENGTH = 188;
KalturaMediaManager.FILE_CHUNK_SIZE = 2500 * KalturaMediaManager.TS_PACKET_LENGTH;

KalturaMediaManager.PBA_CALL_AGAIN = 0;
KalturaMediaManager.PBA_GET_NEXT_CHUNK = 1;
KalturaMediaManager.PBA_CLONE_CURRENT_CHUNK = 2;

KalturaMediaManager.ALIGN_LEFT = 0;
KalturaMediaManager.ALIGN_MIDDLE =	1;
KalturaMediaManager.ALIGN_RIGHT = 	2;

KalturaMediaManager.CHUNK_TYPE_INVALID  =	-1;
KalturaMediaManager.CHUNK_TYPE_TS_HEADER = 0;
KalturaMediaManager.CHUNK_TYPE_PRE_AD = 	 1;
KalturaMediaManager.CHUNK_TYPE_POST_AD = 	 2;

KalturaMediaManager.CHUNK_TYPE_AD = 		 5;
KalturaMediaManager.CHUNK_TYPE_BLACK = 	 4;

KalturaMediaManager.prototype.outputStitchedSegment = function(outputLayout, outputState, curChunk, preAdKey, adKeys, blackKey, postAdKey, response) {
	if (!curChunk) {
		// not much to do about this since we already returned the response headers
		response.log('failed to get chunk from memcache');
		response.end();
		return;
	}

	do {
		var processResult = stitcher.processChunk(
			outputLayout,
			curChunk,
			outputState);

		if (processResult.chunkOutputEnd > 0) {
			response.log('Writing ' + processResult.chunkOutputStart + '..' + processResult.chunkOutputEnd);
			var curSlice = curChunk.slice(processResult.chunkOutputStart, processResult.chunkOutputEnd);
			response.write(curSlice);
		}

		if (processResult.outputBuffer) {
			response.log('Writing extra buffer of size ' + processResult.outputBuffer.length);
			response.write(processResult.outputBuffer);
		}

		if (processResult.action == KalturaMediaManager.PBA_CLONE_CURRENT_CHUNK)
		{
			response.log('Cloning chunk buffer');
			chunkClone = new Buffer(curChunk.length);
			curChunk.copy(chunkClone);
			curChunk = chunkClone;
		}
	} while (processResult.action != KalturaMediaManager.PBA_GET_NEXT_CHUNK);

	curChunk = null;		// no longer need the chunk

	var chunkIndex = Math.floor(outputState.chunkStartOffset / KalturaMediaManager.FILE_CHUNK_SIZE);
	var videoKey = null;

	switch (outputState.chunkType) {
	case KalturaMediaManager.CHUNK_TYPE_PRE_AD:
		videoKey = preAdKey + '-' + chunkIndex;
		break;
	case KalturaMediaManager.CHUNK_TYPE_BLACK:
		videoKey = blackKey + '-' + chunkIndex;
		break;
	case KalturaMediaManager.CHUNK_TYPE_POST_AD:
		videoKey = postAdKey + '-' + chunkIndex;
		break;
	case KalturaMediaManager.CHUNK_TYPE_TS_HEADER:
		videoKey = preAdKey + '-header';
		break;
	default:
		for(var i=0; i<adKeys.length; i++){
			if(KalturaMediaManager.CHUNK_TYPE_AD + i == outputState.chunkType){
				videoKey = adKeys[i] + '-' + chunkIndex;
				break;
			}
		}
		if(!videoKey){
			response.log('Request completed');
			response.end();
			return;
		}
	}

	response.log('Getting ' + videoKey);
	var This = this;
	KalturaCache.getBinary(videoKey, function (curChunk) {
		outputState.chunkStartOffset = chunkIndex * KalturaMediaManager.FILE_CHUNK_SIZE;
		This.outputStitchedSegment(outputLayout, outputState, curChunk, preAdKey, adKeys, blackKey, postAdKey, response);
	});
};

KalturaMediaManager.prototype.get = function(request, response, params){
	// TODO verify the call is from the CDN
	if (!params.e) {
		response.dir(params);
		response.error('Missing arguments [e]');
		this.errorMissingParameter(response);
		return;
	}

	params = this.decrypt(params);
	response.dir(params);
	this.serve(request, response, params.serverAdId, params.cuePointId, params.renditionId, params.encodingId, parseInt(params.segmentIndex), parseInt(params.outputStart), parseInt(params.outputEnd), params.originalUrl);
};

KalturaMediaManager.prototype.serve = function(request, response, serverAdId, cuePointId, renditionId, encodingId, segmentIndex, outputStart, outputEnd, originalUrl){
	var This = this;

	var preSegmentId = KalturaCache.getPreSegmentId(cuePointId, renditionId);
	var postSegmentId = KalturaCache.getPostSegmentId(cuePointId, renditionId);
	var preAdMetadataKey = KalturaCache.getSegmentMediaMetadata(preSegmentId);
	var blackMetadataKey = KalturaCache.getBlackMediaMetadata(encodingId);
	var postAdMetadataKey = KalturaCache.getSegmentMediaMetadata(postSegmentId);
	var adMetadataKeys = [];
	serverAdId = JSON.parse(serverAdId);
	var adKeys = [];
	for(var i=0; i<serverAdId.length; i++){
		adMetadataKeys.push(KalturaCache.getAdMediaMetadata(serverAdId[i].id));
		adKeys.push(KalturaCache.getAdMedia(serverAdId[i].id));
	}

	var cacheKeys = adMetadataKeys.concat([preAdMetadataKey, blackMetadataKey, postAdMetadataKey]);
	KalturaCache.getMultiBinary(cacheKeys, function(data){
		preAdMetadata = data[preAdMetadataKey];
		blackMetadata = data[blackMetadataKey];
		postAdMetadata = data[postAdMetadataKey];

		adsMetadata = [];
		for(var i=0; i<adMetadataKeys.length; i++){
			if(data[adMetadataKeys[i]]){
				var adMetadata =
				{
					adChunkType: KalturaMediaManager.CHUNK_TYPE_AD + i,
					ad: data[adMetadataKeys[i]],
					fillerChunkType: KalturaMediaManager.CHUNK_TYPE_BLACK,
					filler: blackMetadata,
					startPos: serverAdId[i].startPos,
					endPos: serverAdId[i].endPos,
					alignment: KalturaMediaManager.ALIGN_LEFT
				};
				adsMetadata.push(adMetadata);
				response.log('Added ad metadata: adChunkType [' + adMetadata.adChunkType + '], startPos [' + serverAdId[i].startPos + '] endPos [' + serverAdId[i].endPos + ']');
			}
			else{
				delete adKeys[i];
			}
		}

		if (!preAdMetadata){
			response.log('Pre-Ad metadata is null, redirecting to original ts');
			response.writeHead(302, {'Location' : originalUrl});
			response.end();
		}
		else{
			response.log('Pre-Ad metadata length ' + preAdMetadata.length);

			if (!adsMetadata.length){
				response.log('Ad metadata is null');
				//set black ad as a dummy ad metadata
				var adMetadata =
				{
					adChunkType: KalturaMediaManager.CHUNK_TYPE_BLACK,
					ad: blackMetadata,
					fillerChunkType: KalturaMediaManager.CHUNK_TYPE_BLACK,
					filler: blackMetadata,
					startPos: 0,
					endPos: 0,
					alignment: KalturaMediaManager.ALIGN_LEFT
				};
				adsMetadata.push(adMetadata);
			}
			else{
				response.log('Ad metadata length ' + adsMetadata.length);
			}

			// build the layout of the output TS
			var outputLayout = stitcher.buildLayout(
					preAdMetadata,
					postAdMetadata,
					adsMetadata,
					segmentIndex,
					outputStart,
					outputEnd);

			// free the metadata buffers, we don't need them anymore
			preAdMetadata = null;
			adsMetadata = null;
			blackMetadata = null;
			postAdMetadata = null;

			var preAdKey = KalturaCache.getSegmentMedia(preSegmentId);
			var blackKey = KalturaCache.getBlackMedia(encodingId);
			var postAdKey = KalturaCache.getSegmentMedia(postSegmentId);

			// output the TS
			response.writeHead(200, {
				'Content-Type': 'video/MP2T',
				'Cache-Control': KalturaConfig.config.media.cdnCacheScope + ', max-age=' + KalturaConfig.config.media.cdnMaxAge + ', max-stale=0'
				});

			This.outputStitchedSegment(outputLayout, {}, new Buffer(0), preAdKey, adKeys, blackKey, postAdKey, response);
		}
	});
};

/**
 * Returns the segment media from cache
 *
 * @action media.segment
 */
KalturaMediaManager.prototype.segment = function(request, response, params){
	var This = this;
	if (!params.e) {
		response.dir(params);
		response.error('Missing arguments [e]');
		this.errorMissingParameter(response);
		return;
	}

	params = this.decrypt(params);
	response.dir(params);

	var requiredParams = [
		'cuePointId',
		'renditionId',
		'encodingId',
		'segmentIndex',
		'outputStart',
		'outputEnd',
		'sessionId',
		'adStart',
		'originalUrl'
	];

	var missingParams = this.getMissingParams(params, requiredParams);
	if(missingParams.length){
		response.error('Missing arguments [' + missingParams.join(', ') + ']');
		this.errorMissingParameter(response);
		return;
	}

	var serverAdIdKey = KalturaCache.getServerAdId(params.cuePointId, params.encodingId, params.sessionId);

	KalturaCache.get(serverAdIdKey, function(serverAdIds){
		if(!serverAdIds){
			response.log('No ad found, redirecting to original ts');
			response.writeHead(302, {'Location' : params.originalUrl});
			response.end();
		}
		else{
			var outputStart = parseInt(params.outputStart);
			var outputEnd = parseInt(params.outputEnd);
			var adStart = parseInt(params.adStart);
			var serverAdId = [];
			var adsSequence = [];
			var currentAdsIdx = [];
			var sequenceDuration = adStart;
			var startSequenceIndex = 0;
			var startPos = adStart;
			var endPos = adStart;
			for(var i = 0; i<= serverAdIds.length; i++){
				if(!serverAdIds[i]){
					continue;
				}
				if(sequenceDuration + serverAdIds[i].duration <= outputStart){
					sequenceDuration += serverAdIds[i].duration;
					startSequenceIndex = i;
				}
				startPos = endPos;
				endPos += serverAdIds[i].duration;
				adsSequence.push({id:serverAdIds[i].id, startPos:startPos, endPos:endPos, sequence:i});
			}

			for(var j = startSequenceIndex; adsSequence[j] && (adsSequence[j].startPos <= outputEnd || !outputEnd); j++){
				currentAdsIdx.push(adsSequence[j].sequence);
				serverAdId.push({id:adsSequence[j].id, startPos:adsSequence[j].startPos, endPos:adsSequence[j].endPos});
			}

			if(serverAdId.length == 0){
				response.log('No ad match the ad sequence, redirecting to original ts');
				response.writeHead(302, {'Location' : params.originalUrl});
				response.end();
			}
			else{
				response.log('Handling server ad Ids: ' + JSON.stringify(serverAdId));
				params.serverAdId = JSON.stringify(serverAdId);

				var trackingKey = KalturaCache.getAdTrackingId(params.cuePointId, params.sessionId);
				KalturaAdIntegrationHandler.sendBeacon(trackingKey, currentAdsIdx, sequenceDuration, outputStart, outputEnd, adStart);

				delete params.sessionId;
				response.dir(params);

				var redirectUrl = This.getPlayServerUrl('media', 'get', params.partnerId, null, params);
				response.writeHead(302, {'Location' : redirectUrl});
				response.end();
			}
		}

	}, function(err){
		response.log('No ad found, redirecting to original ts');
		response.writeHead(302, {'Location' : params.originalUrl});
		response.end();
	});
};

module.exports.KalturaMediaManager = KalturaMediaManager;
<|endoftext|>


<commit_before>'use strict';

var Node = require('../node');

var TYPE = 'georeference-city';
var PARAMS = {
    source: Node.PARAM.NODE(Node.GEOMETRY.ANY),
    city: Node.PARAM.STRING(),
    admin_region: Node.PARAM.STRING(),
    country: Node.PARAM.STRING()
};

var GeoreferenceCity = Node.create(TYPE, PARAMS, { cache: true });

module.exports = GeoreferenceCity;
module.exports.TYPE = TYPE;
module.exports.PARAMS = PARAMS;

GeoreferenceCity.prototype.sql = function() {
    return queryTemplate({
        source: this.source.getQuery(),
        columns: this.source.getColumns(true).join(', '),
        city: this.city,
        admin_region: this.admin_region,
        country: this.country
    });
};

var queryTemplate = Node.template([
    'SELECT',
    '  {{=it.columns}},',
    '  cdb_dataservices_client.cdb_geocode_namedplace_point(' +
    '    {{=it.city}},',
    '    {{=it.admin_region}},',
    '    {{=it.country}}',
    '  ) AS the_geom',
    'FROM ({{=it.source}}) AS _camshaft_georeference_city_analysis'
 ].join('\n'));
<commit_msg>Now optional params are nullables in georeference city analysis<commit_after>'use strict';

var Node = require('../node');

var TYPE = 'georeference-city';
var PARAMS = {
    source: Node.PARAM.NODE(Node.GEOMETRY.ANY),
    city: Node.PARAM.STRING(),
    admin_region: Node.PARAM.NULLABLE(Node.PARAM.STRING()),
    country: Node.PARAM.NULLABLE(Node.PARAM.STRING())
};

var GeoreferenceCity = Node.create(TYPE, PARAMS, { cache: true });

module.exports = GeoreferenceCity;
module.exports.TYPE = TYPE;
module.exports.PARAMS = PARAMS;

GeoreferenceCity.prototype.sql = function() {
    var geocoderParams = [this.city];

    if (this.admin_region) {
        geocoderParams.push(this.admin_region);
    }

    if (this.country) {
        geocoderParams.push(this.country);
    }

    return queryTemplate({
        source: this.source.getQuery(),
        columns: this.source.getColumns(true).join(', '),
        geocoder_params: geocoderParams.map(function (param) {
            return '\'' + param + '\'';
        }).join(', ')
    });
};

var queryTemplate = Node.template([
    'SELECT',
    '  {{=it.columns}},',
    '  cdb_dataservices_client.cdb_geocode_namedplace_point(' +
    '    {{=it.geocoder_params}}',
    '  ) AS the_geom',
    'FROM ({{=it.source}}) AS _camshaft_georeference_city_analysis'
 ].join('\n'));
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var Int64 = require('node-int64');

var Int64Util = module.exports = {};

var POW2_24 = Math.pow(2, 24);
var POW2_31 = Math.pow(2, 31);
var POW2_32 = Math.pow(2, 32);
var POW10_11 = Math.pow(10, 11);

Int64Util.toDecimalString = function(i64) {
  var b = i64.buffer;
  var o = i64.offset;
  if ((!b[o] && !(b[o + 1] & 0xe0)) ||
      (!~b[o] && !~(b[o + 1] & 0xe0))) {
    // The magnitude is small enough.
    return i64.toString();
  } else {
    var negative = b[o] & 0x80;
    if (negative) {
      // 2's complement
      var incremented = false;
      var buffer = new Buffer(8);
      for (var i = 7; i >= 0; --i) {
        buffer[i] = ~b[o + i] + (incremented ? 0 : 1);
        incremented |= b[o + i];
      }
      b = buffer;
    }
    var high2 = b[o + 1] + (b[o] << 8);
    // Lesser 11 digits with exceeding values but is under 53 bits capacity.
    var low = b[o + 7] + (b[o + 6] << 8) + (b[o + 5] << 16)
        + b[o + 4] * POW2_24  // Bit shift renders 32th bit as sign, so use multiplication
        + (b[o + 3] + (b[o + 2] << 8)) * POW2_32 + high2 * 74976710656;  // The literal is 2^48 % 10^11
    // 12th digit and greater.
    var high = Math.floor(low / POW10_11) + high2 * 2814;  // The literal is 2^48 / 10^11
    // Make it exactly 11 with leading zeros.
    low = ('00000000000' + String(low % POW10_11)).slice(-11);
    return (negative ? '-' : '') + String(high) + low;
  }
};

Int64Util.fromDecimalString = function(text) {
  var negative = text.charAt(0) === '-';
  if (text.length < (negative ? 17 : 16)) {
    // The magnitude is smaller than 2^53.
    return new Int64(+text);
  } else if (text.length > (negative ? 20 : 19)) {
    throw new RangeError('Too many digits for Int64: ' + text);
  } else {
    // Most significant (up to 5) digits
    var high5 = +text.slice(negative ? 1 : 0, -15);
    var low = +text.slice(-15) + high5 * 2764472320;  // The literal is 10^15 % 2^32
    var high = Math.floor(low / POW2_32) + high5 * 232830;  // The literal is 10^15 / 2^&32
    low = low % POW2_32;
    if (high >= POW2_31 &&
        !(negative && high == POW2_31 && low == 0)  // Allow minimum Int64
       ) {
      throw new RangeError('The magnitude is too large for Int64.');
    }
    if (negative) {
      // 2's complement
      high = ~high;
      if (low === 0) {
        high = (high + 1) & 0xffffffff;
      } else {
        low = ~low + 1;
      }
      high = 0x80000000 | high;
    }
    return new Int64(high, low);
  }
};
<commit_msg>THRIFT-3763 Fix serialization of i64 larger than 2^53 for browserify<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var Int64 = require('node-int64');

var Int64Util = module.exports = {};

var POW2_24 = Math.pow(2, 24);
var POW2_31 = Math.pow(2, 31);
var POW2_32 = Math.pow(2, 32);
var POW10_11 = Math.pow(10, 11);

Int64Util.toDecimalString = function(i64) {
  var b = i64.buffer;
  var o = i64.offset;
  if ((!b[o] && !(b[o + 1] & 0xe0)) ||
      (!~b[o] && !~(b[o + 1] & 0xe0))) {
    // The magnitude is small enough.
    return i64.toString();
  } else {
    var negative = b[o] & 0x80;
    if (negative) {
      // 2's complement
      var incremented = false;
      var buffer = new Buffer(8);
      for (var i = 7; i >= 0; --i) {
        buffer[i] = (~b[o + i] + (incremented ? 0 : 1)) & 0xff;
        incremented |= b[o + i];
      }
      b = buffer;
    }
    var high2 = b[o + 1] + (b[o] << 8);
    // Lesser 11 digits with exceeding values but is under 53 bits capacity.
    var low = b[o + 7] + (b[o + 6] << 8) + (b[o + 5] << 16)
        + b[o + 4] * POW2_24  // Bit shift renders 32th bit as sign, so use multiplication
        + (b[o + 3] + (b[o + 2] << 8)) * POW2_32 + high2 * 74976710656;  // The literal is 2^48 % 10^11
    // 12th digit and greater.
    var high = Math.floor(low / POW10_11) + high2 * 2814;  // The literal is 2^48 / 10^11
    // Make it exactly 11 with leading zeros.
    low = ('00000000000' + String(low % POW10_11)).slice(-11);
    return (negative ? '-' : '') + String(high) + low;
  }
};

Int64Util.fromDecimalString = function(text) {
  var negative = text.charAt(0) === '-';
  if (text.length < (negative ? 17 : 16)) {
    // The magnitude is smaller than 2^53.
    return new Int64(+text);
  } else if (text.length > (negative ? 20 : 19)) {
    throw new RangeError('Too many digits for Int64: ' + text);
  } else {
    // Most significant (up to 5) digits
    var high5 = +text.slice(negative ? 1 : 0, -15);
    var low = +text.slice(-15) + high5 * 2764472320;  // The literal is 10^15 % 2^32
    var high = Math.floor(low / POW2_32) + high5 * 232830;  // The literal is 10^15 / 2^&32
    low = low % POW2_32;
    if (high >= POW2_31 &&
        !(negative && high == POW2_31 && low == 0)  // Allow minimum Int64
       ) {
      throw new RangeError('The magnitude is too large for Int64.');
    }
    if (negative) {
      // 2's complement
      high = ~high;
      if (low === 0) {
        high = (high + 1) & 0xffffffff;
      } else {
        low = ~low + 1;
      }
      high = 0x80000000 | high;
    }
    return new Int64(high, low);
  }
};
<|endoftext|>


<commit_before>'use strict';

var utils = require('../common/utils'),
    url = require('url'),
    fs = require('fs'),
    path = require('path'),
    request = require('request'),
    archiver = require('archiver'),
    cloudappx = require('gulp-appx');

var serviceEndpoint = 'http://cloudappx.azurewebsites.net';

function replaceManifestValues(w3cManifest, content) {
  var replacedContent = content;
  var guid = utils.newGuid();

  var applicationId = utils.sanitizeName(w3cManifest.short_name);

  // Update general properties
  replacedContent = replacedContent.replace(/{IdentityName}/g, guid)
                                    .replace(/{PhoneProductId}/g, guid)
                                    .replace(/{DisplayName}/g, w3cManifest.short_name)
                                    .replace(/{ApplicationId}/g, applicationId)
                                    .replace(/{StartPage}/g, w3cManifest.start_url)
                                    .replace(/{DisplayName}/g, w3cManifest.short_name)
                                    .replace(/{Description}/g, w3cManifest.name || w3cManifest.short_name)
                                    .replace(/{RotationPreference}/g, w3cManifest.orientation || 'portrait');

  // Update access rules
  var indentationChars = '\r\n\t\t\t\t';

  // Set the base access rule using the start_url's base url
  var baseUrlPattern = url.resolve(w3cManifest.start_url, '/');
  if (w3cManifest.scope && w3cManifest.scope.length) {
    // If the scope is defined, the base access rule is defined by the scope
    var parsedScopeUrl = url.parse(w3cManifest.scope);

    if (parsedScopeUrl.host && parsedScopeUrl.protocol) {
      baseUrlPattern = w3cManifest.scope;
    } else {
      baseUrlPattern = url.resolve(baseUrlPattern, w3cManifest.scope);
    }
  }

  // If the base access rule ends with '/*', remove the '*'.
  if (baseUrlPattern.indexOf('/*', baseUrlPattern.length - 2) !== -1) {
    baseUrlPattern = baseUrlPattern.substring(0, baseUrlPattern.length - 1);
  }

  var applicationContentUriRules = '<uap:Rule Type="include" Match="' + baseUrlPattern + '" />';

  // Add additional access rules
  if (w3cManifest.mjs_access_whitelist && w3cManifest.mjs_access_whitelist instanceof Array) {
    for (var j = 0; j < w3cManifest.mjs_access_whitelist.length; j++) {
      var accessUrl = w3cManifest.mjs_access_whitelist[j].url;
      // Ignore the '*' rule
      if (accessUrl !== '*') {
        // If the access url ends with '/*', remove the '*'.
        if (accessUrl.indexOf('/*', accessUrl.length - 2) !== -1) {
          accessUrl = accessUrl.substring(0, accessUrl.length - 1);
        }

        if (accessUrl.indexOf(baseUrlPattern) !== 0) { // To avoid duplicates, add the rule only if it does not have the base URL as a prefix
          applicationContentUriRules += indentationChars + '<uap:Rule Type="include" Match="' + accessUrl + '" />';
        }
      }
    }
  }

  replacedContent = replacedContent.replace(/{ApplicationContentUriRules}/g, applicationContentUriRules);

  return replacedContent;
}

// Start CloudAppX
var makeAppx = function (appName, appFolder, callback) {
  var options = { "dir": path.join(appFolder, 'manifest'), "name": appName, "out": appFolder };
  cloudappx.makeappx(options).then(
    function (output) {
      callback && callback();
    },
    function (err) {
      console.log('Unable to create the package locally. Invoking the CloudAppX service instead...');
      invokeCloudAppX(appName, appFolder, function (err) {
        callback && callback(err);
      });
    }
  );
}

function invokeCloudAppX(appName, appFolder, callback) {
  var zipFile = path.join(appFolder, appName + '.zip');
  var output = fs.createWriteStream(zipFile);
  var archive = archiver('zip');
  output.on('close', function () {
    var uploadUrl = url.resolve(serviceEndpoint, '/v1/upload');
    var req = request.post(uploadUrl, function (err, resp, body) {
      if (err) {
        return callback && callback(err);
      }

      fs.unlink(zipFile, function (err) {
        if (err) {
          return callback && callback(err);
        }

        var packageUrl = url.resolve(serviceEndpoint, body);
        var req = request.get(packageUrl)
                    .on('response', function (res) {
                      var filename = path.join(appFolder, appName + '.appx');
                      res.pipe(fs.createWriteStream(filename));
                      callback && callback();
                    });
      });
    });

    var form = req.form();
    form.append('xml', fs.createReadStream(zipFile));
  });

  archive.on('error', function (err) {
    callback && callback(err);
  });

  archive.pipe(output);
  var appContents = path.join(appFolder, 'manifest');
  archive.directory(appContents, appName);
  archive.finalize();
}

// End CloudAppX

module.exports = {
  replaceManifestValues: replaceManifestValues,
  makeAppx: makeAppx
};
<commit_msg>Report errors when generating Windows Store AppX package<commit_after>'use strict';

var utils = require('../common/utils'),
    url = require('url'),
    fs = require('fs'),
    path = require('path'),
    request = require('request'),
    archiver = require('archiver'),
    cloudappx = require('gulp-appx'),
    log = require('loglevel');

var serviceEndpoint = 'http://cloudappx.azurewebsites.net';

function replaceManifestValues(w3cManifest, content) {
  var replacedContent = content;
  var guid = utils.newGuid();

  var applicationId = utils.sanitizeName(w3cManifest.short_name);

  // Update general properties
  replacedContent = replacedContent.replace(/{IdentityName}/g, guid)
                                    .replace(/{PhoneProductId}/g, guid)
                                    .replace(/{DisplayName}/g, w3cManifest.short_name)
                                    .replace(/{ApplicationId}/g, applicationId)
                                    .replace(/{StartPage}/g, w3cManifest.start_url)
                                    .replace(/{DisplayName}/g, w3cManifest.short_name)
                                    .replace(/{Description}/g, w3cManifest.name || w3cManifest.short_name)
                                    .replace(/{RotationPreference}/g, w3cManifest.orientation || 'portrait');

  // Update access rules
  var indentationChars = '\r\n\t\t\t\t';

  // Set the base access rule using the start_url's base url
  var baseUrlPattern = url.resolve(w3cManifest.start_url, '/');
  if (w3cManifest.scope && w3cManifest.scope.length) {
    // If the scope is defined, the base access rule is defined by the scope
    var parsedScopeUrl = url.parse(w3cManifest.scope);

    if (parsedScopeUrl.host && parsedScopeUrl.protocol) {
      baseUrlPattern = w3cManifest.scope;
    } else {
      baseUrlPattern = url.resolve(baseUrlPattern, w3cManifest.scope);
    }
  }

  // If the base access rule ends with '/*', remove the '*'.
  if (baseUrlPattern.indexOf('/*', baseUrlPattern.length - 2) !== -1) {
    baseUrlPattern = baseUrlPattern.substring(0, baseUrlPattern.length - 1);
  }

  var applicationContentUriRules = '<uap:Rule Type="include" Match="' + baseUrlPattern + '" />';

  // Add additional access rules
  if (w3cManifest.mjs_access_whitelist && w3cManifest.mjs_access_whitelist instanceof Array) {
    for (var j = 0; j < w3cManifest.mjs_access_whitelist.length; j++) {
      var accessUrl = w3cManifest.mjs_access_whitelist[j].url;
      // Ignore the '*' rule
      if (accessUrl !== '*') {
        // If the access url ends with '/*', remove the '*'.
        if (accessUrl.indexOf('/*', accessUrl.length - 2) !== -1) {
          accessUrl = accessUrl.substring(0, accessUrl.length - 1);
        }

        if (accessUrl.indexOf(baseUrlPattern) !== 0) { // To avoid duplicates, add the rule only if it does not have the base URL as a prefix
          applicationContentUriRules += indentationChars + '<uap:Rule Type="include" Match="' + accessUrl + '" />';
        }
      }
    }
  }

  replacedContent = replacedContent.replace(/{ApplicationContentUriRules}/g, applicationContentUriRules);

  return replacedContent;
}

// Start CloudAppX
var makeAppx = function (appName, appFolder, callback) {
  var options = { "dir": path.join(appFolder, 'manifest'), "name": appName, "out": appFolder };
  cloudappx.makeappx(options).then(
    function (output) {
      callback && callback();
    },
    function (err) {
      log.debug('Unable to create the package locally. Invoking the CloudAppX service instead...');
      invokeCloudAppX(appName, appFolder, function (err) {
        callback && callback(err);
      });
    }
  );
}

function invokeCloudAppX(appName, appFolder, callback) {
  var zipFile = path.join(appFolder, appName + '.zip');
  var output = fs.createWriteStream(zipFile);
  var archive = archiver('zip');
  output.on('close', function () {
    var uploadUrl = url.resolve(serviceEndpoint, '/v1/upload');
    var req = request.post(uploadUrl, function (err, resp, body) {
      if (err) {
        return callback && callback(err);
      }

      fs.unlink(zipFile, function (err) {
        if (err) {
          return callback && callback(err);
        }

        if (resp.statusCode != 200) {
          return callback && callback(new Error('Failed to create the package. The CloudAppX service returned an error: \'' + body + '\''));
        }

        var packageUrl = url.resolve(serviceEndpoint, body);
        var req = request.get(packageUrl)
                    .on('response', function (res) {
                      var err;
                      if (res.statusCode == 200) {
                        var filename = path.join(appFolder, appName + '.appx');
                        res.pipe(fs.createWriteStream(filename));
                      }
                      else {
                        err = new Error('Failed to download the generated package. The CloudAppX service returned an error: \'' + res.statusMessage + '\'');
                      }

                      callback && callback(err);
                    })
                    .on('error', function (err) {
                      callback && callback(err);
                    });
      });
    });

    var form = req.form();
    form.append('xml', fs.createReadStream(zipFile));
  });

  archive.on('error', function (err) {
    callback && callback(err);
  });

  archive.pipe(output);
  var appContents = path.join(appFolder, 'manifest');
  archive.directory(appContents, appName);
  archive.finalize();
}

// End CloudAppX

module.exports = {
  replaceManifestValues: replaceManifestValues,
  makeAppx: makeAppx
};
<|endoftext|>


<commit_before>'use strict';

const merge = require('lodash.merge');
const forEach = require('lodash.foreach');
const path = require('path');
const browsertime = require('browsertime');
const set = require('lodash.set');
const get = require('lodash.get');
const coach = require('coach-core');
const log = require('intel').getLogger('plugin.browsertime');

const browserScripts = browsertime.browserScripts;

const defaultBrowsertimeOptions = {
  statistics: true
};

const delay = ms => new Promise(res => setTimeout(res, ms));

const iphone6UserAgent =
  'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_3 like Mac OS X) AppleWebKit/536.26 ' +
  '(KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25';

async function preWarmServer(urls, options, scriptOrMultiple) {
  const engine = new browsertime.Engine({
    browser: options.browser,
    iterations: 1,
    xvfb: options.xvfb,
    android: options.android
  });
  await engine.start();
  log.info('Start pre-testing/warming' + urls);
  if (scriptOrMultiple) {
    await engine.runMultiple(urls, {});
  } else {
    await engine.run(urls, {});
  }
  await engine.stop();
  log.info('Pre-testing done, closed the browser.');
  return delay(options.preWarmServerWaitTime || 5000);
}

async function parseUserScripts(scripts) {
  if (!Array.isArray(scripts)) scripts = [scripts];
  const allUserScripts = {};
  for (let script of scripts) {
    let myScript = await browserScripts.findAndParseScripts(
      path.resolve(script),
      'custom'
    );
    if (!myScript['custom']) {
      myScript = { custom: Object.values(myScript)[0] };
    }
    merge(allUserScripts, myScript);
  }
  return allUserScripts;
}

async function addCoachScripts(scripts) {
  const coachAdvice = await coach.getDomAdvice();
  scripts.coach = {
    coachAdvice: coachAdvice
  };
  return scripts;
}

function addExtraScripts(scriptsByCategory, pluginScripts) {
  // For all different script in the array
  for (let scripts of pluginScripts) {
    // and then for all scripts in that category
    forEach(scripts.scripts, function (script, name) {
      set(scriptsByCategory, scripts.category + '.' + name, script);
    });
  }
  return scriptsByCategory;
}

function setupAsynScripts(asyncScripts) {
  const allAsyncScripts = {};
  // For all different script in the array
  for (let scripts of asyncScripts) {
    // and then for all scripts in that category
    forEach(scripts.scripts, function (script, name) {
      set(allAsyncScripts, scripts.category + '.' + name, script);
    });
  }
  return allAsyncScripts;
}

module.exports = {
  async analyzeUrl(
    url,
    scriptOrMultiple,
    pluginScripts,
    pluginAsyncScripts,
    options
  ) {
    const btOptions = merge({}, defaultBrowsertimeOptions, options);

    // set mobile options
    if (options.mobile) {
      btOptions.viewPort = '360x640';
      if (btOptions.browser === 'chrome' || btOptions.browser === 'edge') {
        const emulation = get(
          btOptions,
          'chrome.mobileEmulation.deviceName',
          'Moto G4'
        );
        btOptions.chrome.mobileEmulation = {
          deviceName: emulation
        };
      } else {
        btOptions.userAgent = iphone6UserAgent;
      }
    }
    const scriptCategories = await browserScripts.allScriptCategories;
    let scriptsByCategory = await browserScripts.getScriptsForCategories(
      scriptCategories
    );

    if (btOptions.script) {
      const userScripts = await parseUserScripts(btOptions.script);
      scriptsByCategory = merge(scriptsByCategory, userScripts);
    }

    if (btOptions.coach) {
      scriptsByCategory = addCoachScripts(scriptsByCategory);
    }
    scriptsByCategory = await addExtraScripts(scriptsByCategory, pluginScripts);

    if (btOptions.preWarmServer) {
      await preWarmServer(url, btOptions, scriptOrMultiple);
    }

    const engine = new browsertime.Engine(btOptions);

    const asyncScript =
      pluginAsyncScripts.length > 0
        ? await setupAsynScripts(pluginAsyncScripts)
        : undefined;

    try {
      await engine.start();
      if (scriptOrMultiple) {
        const res = await engine.runMultiple(
          url,
          scriptsByCategory,
          asyncScript
        );
        return res;
      } else {
        const res = await engine.run(url, scriptsByCategory, asyncScript);
        return res;
      }
    } finally {
      await engine.stop();
    }
  }
};
<commit_msg>Fix preWarmServer. (#3683)<commit_after>'use strict';

const merge = require('lodash.merge');
const forEach = require('lodash.foreach');
const path = require('path');
const browsertime = require('browsertime');
const set = require('lodash.set');
const get = require('lodash.get');
const coach = require('coach-core');
const log = require('intel').getLogger('plugin.browsertime');

const browserScripts = browsertime.browserScripts;

const defaultBrowsertimeOptions = {
  statistics: true
};

const delay = ms => new Promise(res => setTimeout(res, ms));

const iphone6UserAgent =
  'Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_3 like Mac OS X) AppleWebKit/536.26 ' +
  '(KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25';

async function preWarmServer(urls, options, scriptOrMultiple) {
  const preWarmOptions = {
    browser: options.browser,
    iterations: 1,
    xvfb: options.xvfb,
    android: options.android,
    docker: options.docker,
    headless: options.headless
  };
  const chromeDevice = get(options, 'chrome.android.deviceSerial');
  const firefoxDevice = get(options, 'firefox.android.deviceSerial');
  const safariIos = get(options, 'safari.ios');
  const safariDeviceName = get(options, 'safari.deviceName');
  const safariDeviceUDID = get(options, 'safari.deviceUDID ');

  if (chromeDevice) {
    set(options, 'chrome.android.deviceSerial', chromeDevice);
  } else if (firefoxDevice) {
    set(options, 'firefox.android.deviceSerial', firefoxDevice);
  }

  if (safariIos) {
    set(options, 'safari.ios', true);
    if (safariDeviceName) {
      set(options, 'safari.deviceName', safariDeviceName);
    }
    if (safariDeviceUDID) {
      set(options, 'safari.deviceUDID', safariDeviceUDID);
    }
  }

  const engine = new browsertime.Engine(preWarmOptions);

  await engine.start();
  log.info('Start pre-testing/warming' + urls);
  if (scriptOrMultiple) {
    await engine.runMultiple(urls, {});
  } else {
    await engine.run(urls, {});
  }
  await engine.stop();
  log.info('Pre-testing done, closed the browser.');
  return delay(options.preWarmServerWaitTime || 5000);
}

async function parseUserScripts(scripts) {
  if (!Array.isArray(scripts)) scripts = [scripts];
  const allUserScripts = {};
  for (let script of scripts) {
    let myScript = await browserScripts.findAndParseScripts(
      path.resolve(script),
      'custom'
    );
    if (!myScript['custom']) {
      myScript = { custom: Object.values(myScript)[0] };
    }
    merge(allUserScripts, myScript);
  }
  return allUserScripts;
}

async function addCoachScripts(scripts) {
  const coachAdvice = await coach.getDomAdvice();
  scripts.coach = {
    coachAdvice: coachAdvice
  };
  return scripts;
}

function addExtraScripts(scriptsByCategory, pluginScripts) {
  // For all different script in the array
  for (let scripts of pluginScripts) {
    // and then for all scripts in that category
    forEach(scripts.scripts, function (script, name) {
      set(scriptsByCategory, scripts.category + '.' + name, script);
    });
  }
  return scriptsByCategory;
}

function setupAsynScripts(asyncScripts) {
  const allAsyncScripts = {};
  // For all different script in the array
  for (let scripts of asyncScripts) {
    // and then for all scripts in that category
    forEach(scripts.scripts, function (script, name) {
      set(allAsyncScripts, scripts.category + '.' + name, script);
    });
  }
  return allAsyncScripts;
}

module.exports = {
  async analyzeUrl(
    url,
    scriptOrMultiple,
    pluginScripts,
    pluginAsyncScripts,
    options
  ) {
    const btOptions = merge({}, defaultBrowsertimeOptions, options);

    // set mobile options
    if (options.mobile) {
      btOptions.viewPort = '360x640';
      if (btOptions.browser === 'chrome' || btOptions.browser === 'edge') {
        const emulation = get(
          btOptions,
          'chrome.mobileEmulation.deviceName',
          'Moto G4'
        );
        btOptions.chrome.mobileEmulation = {
          deviceName: emulation
        };
      } else {
        btOptions.userAgent = iphone6UserAgent;
      }
    }
    const scriptCategories = await browserScripts.allScriptCategories;
    let scriptsByCategory = await browserScripts.getScriptsForCategories(
      scriptCategories
    );

    if (btOptions.script) {
      const userScripts = await parseUserScripts(btOptions.script);
      scriptsByCategory = merge(scriptsByCategory, userScripts);
    }

    if (btOptions.coach) {
      scriptsByCategory = addCoachScripts(scriptsByCategory);
    }
    scriptsByCategory = await addExtraScripts(scriptsByCategory, pluginScripts);

    if (btOptions.preWarmServer) {
      await preWarmServer(url, btOptions, scriptOrMultiple);
    }

    const engine = new browsertime.Engine(btOptions);

    const asyncScript =
      pluginAsyncScripts.length > 0
        ? await setupAsynScripts(pluginAsyncScripts)
        : undefined;

    try {
      await engine.start();
      if (scriptOrMultiple) {
        const res = await engine.runMultiple(
          url,
          scriptsByCategory,
          asyncScript
        );
        return res;
      } else {
        const res = await engine.run(url, scriptsByCategory, asyncScript);
        return res;
      }
    } finally {
      await engine.stop();
    }
  }
};
<|endoftext|>


<commit_before>'use babel';

import {CompositeDisposable, Emitter} from 'atom';
import breakpointManager              from '../common/breakpoint-manager';
import callStackFromString            from '../common/call-stack-factory';
import levelsWorkspaceManager         from '../common/levels-workspace-manager';
import Position                       from '../common/position';
import {fromPoint, toPoint}           from '../common/position-utils';
import * as statusUpdateEventFactory  from '../common/status-update-event-factory';
import variableTableManager           from '../common/variable-table-manager';
import executor                       from '../debugger/executor';
import {DELIMITER}                    from '../messaging/message-utils';
import * as outgoingMessageFactory    from '../messaging/outgoing-message-factory';

export default class DebuggerPresenter {
  constructor(incomingMessageDispatcher, socketChannel) {
    this.incomingMessageDispatcher = incomingMessageDispatcher;
    this.socketChannel = socketChannel;
    this.emitter = new Emitter();

    this.callStack = [];
    this.variableTable = [];
    this.positionMarker = null;
    this.isReplay = false;
    this.isExecutableInDebuggingMode = false;
    this.isAutoSteppingEnabled = true;
    this.areAllControlsDisabled = false;

    this.currentStatusEvent = statusUpdateEventFactory.createStopped(false);
    this.lastEventBeforeDisabling = this.currentStatusEvent;
    this.lastEventBeforeReplay = null;

    this.execSubscriptions = null;

    this.subscriptions = new CompositeDisposable();
    this.subscriptions.add(executor.onReady(() => this.startExecutableAndConnect()));
    this.subscriptions.add(executor.onStop(() => this.handleStop()));
    this.subscriptions.add(levelsWorkspaceManager.onWorkspaceAttached(workspace => this.setLevelsWorkspace(workspace)));
    this.subscriptions.add(this.socketChannel.onError(() => this.handleChannelError()));
    this.subscriptions.add(this.incomingMessageDispatcher.onReady(() => this.handleReady()));
    this.subscriptions.add(this.incomingMessageDispatcher.onPositionUpdated(string => this.emitPositionUpdated(string)));
    this.subscriptions.add(this.incomingMessageDispatcher.onCallStackUpdated(string => this.callStackFromString(string)));
    this.subscriptions.add(this.incomingMessageDispatcher.onVariableTableUpdated(string => this.variableTableFromString(string)));
    this.subscriptions.add(this.incomingMessageDispatcher.onEndOfReplayTape(() => this.handleEndOfReplayTape()));
    this.subscriptions.add(this.incomingMessageDispatcher.onAutoSteppingEnabled(() => this.emitAutoSteppingEnabled()));
    this.subscriptions.add(this.incomingMessageDispatcher.onAutoSteppingDisabled(() => this.emitAutoSteppingDisabled()));
  }

  destroy() {
    this.disconnectAndCleanup();
    this.subscriptions.dispose();
    if (this.execSubscriptions) {
      this.execSubscriptions.dispose();
    }
  }

  initDebuggerView() {
    const isAutoSteppingEnabled = this.isAutoSteppingEnabled;

    this.emitEnableDisableAllBreakpoints();
    this.emitStatusUpdated(this.currentStatusEvent);
    this.emitEnableDisableAllControls(!this.areAllControlsDisabled);

    if (this.isExecutableInDebuggingMode) {
      this.emitRunning();
      this.emitVariableTableUpdated();
      this.emitCallStackUpdated();

      if (isAutoSteppingEnabled) {
        this.emitAutoSteppingEnabled();
      } else {
        this.emitAutoSteppingDisabled();
      }

      if (this.isReplay) {
        this.emitReplayStarted();
      }
    }
  }

  disconnectAndCleanup() {
    this.socketChannel.disconnect();
    this.isExecutableInDebuggingMode = false;
    executor.stopDebugger();
    this.stopExecutable();
  }

  setLevelsWorkspace(workspace) {
    this.subscriptions.add(workspace.onDidEnterWorkspace(() => this.handleWorkspaceEntered()));
    this.subscriptions.add(workspace.onDidExitWorkspace(() => this.handleWorkspaceExited()));
    this.subscriptions.add(workspace.onDidChangeActiveLevel(() => this.handleLevelChanged()));
    this.subscriptions.add(workspace.onDidChangeActiveLevelCodeEditor(() => this.handleLevelCodeEditorChanged()));

    if (!levelsWorkspaceManager.isActive()) {
      this.handleLevelChanged();
    }
  }

  startDebugging() {
    if (!this.areAllControlsDisabled && !this.isExecutableInDebuggingMode && this.saveDocument()) {
      executor.startDebugger();
    }
  }

  startExecutableAndConnect() {
    this.socketChannel.connect();
    this.isExecutableInDebuggingMode = true;
    this.debuggingEditorId = levelsWorkspaceManager.getActiveLevelCodeEditor().getId();
    this.startExecutable();
  }

  stopDebugging() {
    if (!this.areAllControlsDisabled && this.isExecutableInDebuggingMode) {
      this.disconnectAndCleanup();
    }
  }

  step() {
    if (this.areSteppingCommandsEnabled()) {
      this.emitStatusUpdated(statusUpdateEventFactory.createRunning(this.isReplay));
      this.socketChannel.sendMessage(outgoingMessageFactory.createStepMessage());
    }
  }

  stepOver() {
    if (this.areSteppingCommandsEnabled()) {
      this.emitStatusUpdated(statusUpdateEventFactory.createRunning(this.isReplay));
      this.socketChannel.sendMessage(outgoingMessageFactory.createStepOverMessage());
    }
  }

  toggleBreakpoint() {
    if (!this.areAllControlsDisabled) {
      for (const point of levelsWorkspaceManager.getActiveTextEditorCursorPositions()) {
        if (breakpointManager.toggle(point)) {
          this.socketChannel.sendMessage(outgoingMessageFactory.createAddBreakpointMessage(fromPoint(point)));
        } else {
          this.socketChannel.sendMessage(outgoingMessageFactory.createRemoveBreakpointMessage(fromPoint(point)));
        }
      }
    }
  }

  removeAllBreakpoints() {
    if (!this.areAllControlsDisabled) {
      breakpointManager.removeAll();
      this.socketChannel.sendMessage(outgoingMessageFactory.createRemoveAllBreakpointsMessage());
    }
  }

  enableDisableAllBreakpoints() {
    if (!this.areAllControlsDisabled) {
      breakpointManager.flip();
      this.sendEnableDisableAllBreakpoints();
      this.emitEnableDisableAllBreakpoints();
    }
  }

  runToNextBreakpoint() {
    if (this.areSteppingCommandsEnabled()) {
      this.emitStatusUpdated(statusUpdateEventFactory.createRunning(this.isReplay));
      this.socketChannel.sendMessage(outgoingMessageFactory.createRunToNextBreakpointMessage());
    }
  }

  runToEndOfMethod() {
    if (this.areSteppingCommandsEnabled()) {
      this.emitStatusUpdated(statusUpdateEventFactory.createRunning(this.isReplay));
      this.socketChannel.sendMessage(outgoingMessageFactory.createRunToEndOfMethodMessage());
    }
  }

  startReplay(element) {
    if (!this.areAllControlsDisabled && this.isExecutableInDebuggingMode && (this.currentStatusEvent.getStatus() !== statusUpdateEventFactory.RUNNING_STATUS)) {
      this.socketChannel.sendMessage(outgoingMessageFactory.createStartReplayMessage(element.dataset.callId));

      if (!this.isReplay && (this.currentStatusEvent.getStatus() !== statusUpdateEventFactory.END_OF_TAPE_STATUS)) {
        this.lastEventBeforeReplay = this.currentStatusEvent;
      }
      this.isReplay = true;
      this.emitReplayStarted();
    }
  }

  stopReplay() {
    if (!this.areAllControlsDisabled && this.isReplay) {
      this.socketChannel.sendMessage(outgoingMessageFactory.createStopReplayMessage());
      this.isReplay = false;
      this.emitReplayStopped();
      this.emitStatusUpdated(this.lastEventBeforeReplay);
    }
  }

  areSteppingCommandsEnabled() {
    return !this.areAllControlsDisabled && this.isExecutableInDebuggingMode && !this.isAutoSteppingEnabled;
  }

  saveDocument() {
    const textEditor = levelsWorkspaceManager.getActiveTextEditor();

    if (textEditor) {
      const path = textEditor.getPath();
      const saveHere = path ? path : atom.showSaveDialogSync();

      if (saveHere) {
        textEditor.saveAs(saveHere);
        const terminal = levelsWorkspaceManager.getActiveTerminal();
        terminal.show();
        terminal.focus();

        return true;
      }
    }

    return false;
  }

  variableTableFromString(string) {
    this.variableTable = variableTableManager.fromString(string, this.variableTable);
    this.emitVariableTableUpdated();
  }

  getVariableTable() {
    return this.variableTable;
  }

  callStackFromString(string) {
    this.callStack = callStackFromString(string);
    this.emitCallStackUpdated();
  }

  getCallStack() {
    return this.callStack;
  }

  startExecutable() {
    levelsWorkspaceManager.getActiveLevelCodeEditor().startExecution({runExecArgs: ['-d']});
  }

  stopExecutable() {
    levelsWorkspaceManager.getActiveLevelCodeEditor().stopExecution();
  }

  sendEnableDisableAllBreakpoints() {
    if (breakpointManager.getAreBreakpointsEnabled()) {
      this.socketChannel.sendMessage(outgoingMessageFactory.createEnableAllBreakpointsMessage());
    } else {
      this.socketChannel.sendMessage(outgoingMessageFactory.createDisableAllBreakpointsMessage());
    }
  }

  flipAndSortVariableTable() {
    if (!this.areAllControlsDisabled && this.isExecutableInDebuggingMode) {
      variableTableManager.flipSortMode();
      variableTableManager.sort(this.variableTable);
      this.emitVariableTableUpdated();
    }
  }

  onRunning(callback) {
    return this.emitter.on('running', callback);
  }

  onStopped(callback) {
    return this.emitter.on('stopped', callback);
  }

  onPositionUpdated(callback) {
    return this.emitter.on('position-updated', callback);
  }

  onCallStackUpdated(callback) {
    return this.emitter.on('call-stack-updated', callback);
  }

  onVariableTableUpdated(callback) {
    return this.emitter.on('variable-table-updated', callback);
  }

  onStatusUpdated(callback) {
    return this.emitter.on('status-updated', callback);
  }

  onAutoSteppingEnabled(callback) {
    return this.emitter.on('auto-stepping-enabled', callback);
  }

  onAutoSteppingDisabled(callback) {
    return this.emitter.on('auto-stepping-disabled', callback);
  }

  onEnableDisableAllBreakpoints(callback) {
    return this.emitter.on('enable-disable-all-breakpoints', callback);
  }

  onEnableDisableAllControls(callback) {
    return this.emitter.on('enable-disable-all-controls', callback);
  }

  onReplayStarted(callback) {
    return this.emitter.on('replay-started', callback);
  }

  onReplayStopped(callback) {
    return this.emitter.on('replay-stopped', callback);
  }

  emitRunning() {
    this.emitter.emit('running');
  }

  emitStopped() {
    this.emitter.emit('stopped');
  }

  emitPositionUpdated(string) {
    const splitted = string.split(DELIMITER);
    const currentPosition = new Position(+splitted[0], +splitted[1]);
    const point = toPoint(currentPosition);

    breakpointManager.restoreHiddenBreakpoint();
    breakpointManager.hideBreakpoint(currentPosition);

    if (this.positionMarker) {
      this.positionMarker.destroy();
    }
    this.positionMarker = levelsWorkspaceManager.addPositionMarker(point);

    this.emitter.emit('position-updated', currentPosition);
    this.emitStatusUpdated(statusUpdateEventFactory.createWaiting(this.isReplay));

    const textEditor = levelsWorkspaceManager.getActiveTextEditor();
    if (textEditor) {
      textEditor.scrollToBufferPosition(point);
    }
  }

  emitCallStackUpdated() {
    this.emitter.emit('call-stack-updated');
  }

  emitVariableTableUpdated() {
    this.emitter.emit('variable-table-updated');
  }

  emitStatusUpdated(event) {
    this.emitter.emit('status-updated', event);
    this.currentStatusEvent = event;
    if (event.isBlockingStatus()) {
      this.emitAutoSteppingEnabled();
    } else {
      this.emitAutoSteppingDisabled();
    }
  }

  emitAutoSteppingEnabled() {
    this.isAutoSteppingEnabled = true;
    this.emitter.emit('auto-stepping-enabled');
  }

  emitAutoSteppingDisabled() {
    this.isAutoSteppingEnabled = false;
    this.emitter.emit('auto-stepping-disabled');
  }

  emitEnableDisableAllBreakpoints() {
    this.emitter.emit('enable-disable-all-breakpoints', breakpointManager.getAreBreakpointsEnabled());
  }

  emitEnableDisableAllControls(enabled) {
    this.areAllControlsDisabled = !enabled;
    this.emitter.emit('enable-disable-all-controls', enabled);
  }

  emitReplayStarted() {
    this.emitter.emit('replay-started');
  }

  emitReplayStopped() {
    this.emitter.emit('replay-stopped');
  }

  handleChannelError() {
    this.disconnectAndCleanup();
  }

  handleExecutableStarted() {
    if (!this.isExecutableInDebuggingMode) {
      this.emitEnableDisableAllControls(false);
    }
  }

  handleExecutableStopped() {
    if (this.isExecutableInDebuggingMode) {
      this.disconnectAndCleanup();
    }
    this.emitEnableDisableAllControls(levelsWorkspaceManager.isActiveLevelDebuggable());
  }

  handleReady() {
    this.emitRunning();
    this.emitAutoSteppingDisabled();
    for (const bp of breakpointManager.getBreakpoints()) {
      this.socketChannel.sendMessage(outgoingMessageFactory.createAddBreakpointMessage(bp.getPosition()));
    }
    this.sendEnableDisableAllBreakpoints();
  }

  handleStop() {
    if (this.isExecutableInDebuggingMode) {
      this.disconnectAndCleanup();
    }

    this.isReplay = false;
    this.isAutoSteppingEnabled = true;
    this.variableTable = [];
    this.callStack = [];
    variableTableManager.resetSortMode();
    this.emitStopped();
    this.emitAutoSteppingEnabled();

    if (this.positionMarker) {
      this.positionMarker.destroy();
    }
    breakpointManager.restoreHiddenBreakpoint();

    this.emitStatusUpdated(statusUpdateEventFactory.createStopped(this.isReplay));
  }

  handleEndOfReplayTape() {
    this.emitStatusUpdated(statusUpdateEventFactory.createEndOfTape(false));
  }

  handleWorkspaceEntered() {
    const editor = levelsWorkspaceManager.getActiveLevelCodeEditor();

    if (this.isExecutableInDebuggingMode) {
      const enabled = this.debuggingEditorId === editor.getId();
      this.emitEnableDisableAllControls(enabled);
    } else {
      this.handleLevelChanged();

      this.execSubscriptions = new CompositeDisposable();
      this.execSubscriptions.add(editor.onDidStartExecution(() => this.handleExecutableStarted()));
      this.execSubscriptions.add(editor.onDidStopExecution(() => this.handleExecutableStopped()));

      if (editor.isExecuting()) {
        this.emitEnableDisableAllControls(false);
      }
    }
  }

  handleWorkspaceExited() {
    if (this.isExecutableInDebuggingMode) {
      this.emitEnableDisableAllControls(false);
    } else {
      this.handleLevelChanged();
      this.execSubscriptions.dispose();
    }
  }

  handleLevelChanged() {
    if (!this.isExecutableInDebuggingMode) {
      if (levelsWorkspaceManager.isActiveLevelDebuggable()) {
        this.emitStatusUpdated(this.lastEventBeforeDisabling);
        this.emitEnableDisableAllControls(true);
      } else {
        if (this.currentStatusEvent.getStatus() !== statusUpdateEventFactory.DISABLED_STATUS) {
          this.lastEventBeforeDisabling = this.currentStatusEvent;
        }
        this.emitStatusUpdated(statusUpdateEventFactory.createDisabled(this.isReplay));
        this.emitEnableDisableAllControls(false);
      }
    }
  }

  handleLevelCodeEditorChanged() {
    const editor = levelsWorkspaceManager.getActiveLevelCodeEditor();

    if (this.isExecutableInDebuggingMode) {
      const enabled = this.debuggingEditorId === editor.getId();
      this.emitEnableDisableAllControls(enabled);
    } else {
      this.execSubscriptions.dispose();
      this.execSubscriptions = new CompositeDisposable();
      this.execSubscriptions.add(editor.onDidStartExecution(() => this.handleExecutableStarted()));
      this.execSubscriptions.add(editor.onDidStopExecution(() => this.handleExecutableStopped()));

      if (editor.isExecuting()) {
        this.emitEnableDisableAllControls(false);
      } else {
        this.emitEnableDisableAllControls(levelsWorkspaceManager.isActiveLevelDebuggable());
      }
    }
  }
}<commit_msg>Avoid use of the soon to be deprecated Atom function showSaveDialogSync<commit_after>'use babel';

import {CompositeDisposable, Emitter} from 'atom';
import breakpointManager              from '../common/breakpoint-manager';
import callStackFromString            from '../common/call-stack-factory';
import levelsWorkspaceManager         from '../common/levels-workspace-manager';
import Position                       from '../common/position';
import {fromPoint, toPoint}           from '../common/position-utils';
import * as statusUpdateEventFactory  from '../common/status-update-event-factory';
import variableTableManager           from '../common/variable-table-manager';
import executor                       from '../debugger/executor';
import {DELIMITER}                    from '../messaging/message-utils';
import * as outgoingMessageFactory    from '../messaging/outgoing-message-factory';

export default class DebuggerPresenter {
  constructor(incomingMessageDispatcher, socketChannel) {
    this.incomingMessageDispatcher = incomingMessageDispatcher;
    this.socketChannel = socketChannel;
    this.emitter = new Emitter();

    this.callStack = [];
    this.variableTable = [];
    this.positionMarker = null;
    this.isReplay = false;
    this.isExecutableInDebuggingMode = false;
    this.isAutoSteppingEnabled = true;
    this.areAllControlsDisabled = false;

    this.currentStatusEvent = statusUpdateEventFactory.createStopped(false);
    this.lastEventBeforeDisabling = this.currentStatusEvent;
    this.lastEventBeforeReplay = null;

    this.execSubscriptions = null;

    this.subscriptions = new CompositeDisposable();
    this.subscriptions.add(executor.onReady(() => this.startExecutableAndConnect()));
    this.subscriptions.add(executor.onStop(() => this.handleStop()));
    this.subscriptions.add(levelsWorkspaceManager.onWorkspaceAttached(workspace => this.setLevelsWorkspace(workspace)));
    this.subscriptions.add(this.socketChannel.onError(() => this.handleChannelError()));
    this.subscriptions.add(this.incomingMessageDispatcher.onReady(() => this.handleReady()));
    this.subscriptions.add(this.incomingMessageDispatcher.onPositionUpdated(string => this.emitPositionUpdated(string)));
    this.subscriptions.add(this.incomingMessageDispatcher.onCallStackUpdated(string => this.callStackFromString(string)));
    this.subscriptions.add(this.incomingMessageDispatcher.onVariableTableUpdated(string => this.variableTableFromString(string)));
    this.subscriptions.add(this.incomingMessageDispatcher.onEndOfReplayTape(() => this.handleEndOfReplayTape()));
    this.subscriptions.add(this.incomingMessageDispatcher.onAutoSteppingEnabled(() => this.emitAutoSteppingEnabled()));
    this.subscriptions.add(this.incomingMessageDispatcher.onAutoSteppingDisabled(() => this.emitAutoSteppingDisabled()));
  }

  destroy() {
    this.disconnectAndCleanup();
    this.subscriptions.dispose();
    if (this.execSubscriptions) {
      this.execSubscriptions.dispose();
    }
  }

  initDebuggerView() {
    const isAutoSteppingEnabled = this.isAutoSteppingEnabled;

    this.emitEnableDisableAllBreakpoints();
    this.emitStatusUpdated(this.currentStatusEvent);
    this.emitEnableDisableAllControls(!this.areAllControlsDisabled);

    if (this.isExecutableInDebuggingMode) {
      this.emitRunning();
      this.emitVariableTableUpdated();
      this.emitCallStackUpdated();

      if (isAutoSteppingEnabled) {
        this.emitAutoSteppingEnabled();
      } else {
        this.emitAutoSteppingDisabled();
      }

      if (this.isReplay) {
        this.emitReplayStarted();
      }
    }
  }

  disconnectAndCleanup() {
    this.socketChannel.disconnect();
    this.isExecutableInDebuggingMode = false;
    executor.stopDebugger();
    this.stopExecutable();
  }

  setLevelsWorkspace(workspace) {
    this.subscriptions.add(workspace.onDidEnterWorkspace(() => this.handleWorkspaceEntered()));
    this.subscriptions.add(workspace.onDidExitWorkspace(() => this.handleWorkspaceExited()));
    this.subscriptions.add(workspace.onDidChangeActiveLevel(() => this.handleLevelChanged()));
    this.subscriptions.add(workspace.onDidChangeActiveLevelCodeEditor(() => this.handleLevelCodeEditorChanged()));

    if (!levelsWorkspaceManager.isActive()) {
      this.handleLevelChanged();
    }
  }

  startDebugging() {
    if (!this.areAllControlsDisabled && !this.isExecutableInDebuggingMode) {
      const textEditor = levelsWorkspaceManager.getActiveTextEditor();

      if (textEditor) {
        const path = textEditor.getPath();
        const startDebuggerAction = () => {
          const terminal = levelsWorkspaceManager.getActiveTerminal();
          terminal.show();
          terminal.focus();

          executor.startDebugger();
        };

        if (path) {
          textEditor.saveAs(path).then(startDebuggerAction);
        } else {
          const textEditorPaneContainer = atom.workspace.paneContainerForItem(textEditor);
          textEditorPaneContainer.getActivePane().saveItemAs(textEditor, error => {
            if (!error) {
              startDebuggerAction();
            }
          });
        }
      }
    }
  }

  startExecutableAndConnect() {
    this.socketChannel.connect();
    this.isExecutableInDebuggingMode = true;
    this.debuggingEditorId = levelsWorkspaceManager.getActiveLevelCodeEditor().getId();
    this.startExecutable();
  }

  stopDebugging() {
    if (!this.areAllControlsDisabled && this.isExecutableInDebuggingMode) {
      this.disconnectAndCleanup();
    }
  }

  step() {
    if (this.areSteppingCommandsEnabled()) {
      this.emitStatusUpdated(statusUpdateEventFactory.createRunning(this.isReplay));
      this.socketChannel.sendMessage(outgoingMessageFactory.createStepMessage());
    }
  }

  stepOver() {
    if (this.areSteppingCommandsEnabled()) {
      this.emitStatusUpdated(statusUpdateEventFactory.createRunning(this.isReplay));
      this.socketChannel.sendMessage(outgoingMessageFactory.createStepOverMessage());
    }
  }

  toggleBreakpoint() {
    if (!this.areAllControlsDisabled) {
      for (const point of levelsWorkspaceManager.getActiveTextEditorCursorPositions()) {
        if (breakpointManager.toggle(point)) {
          this.socketChannel.sendMessage(outgoingMessageFactory.createAddBreakpointMessage(fromPoint(point)));
        } else {
          this.socketChannel.sendMessage(outgoingMessageFactory.createRemoveBreakpointMessage(fromPoint(point)));
        }
      }
    }
  }

  removeAllBreakpoints() {
    if (!this.areAllControlsDisabled) {
      breakpointManager.removeAll();
      this.socketChannel.sendMessage(outgoingMessageFactory.createRemoveAllBreakpointsMessage());
    }
  }

  enableDisableAllBreakpoints() {
    if (!this.areAllControlsDisabled) {
      breakpointManager.flip();
      this.sendEnableDisableAllBreakpoints();
      this.emitEnableDisableAllBreakpoints();
    }
  }

  runToNextBreakpoint() {
    if (this.areSteppingCommandsEnabled()) {
      this.emitStatusUpdated(statusUpdateEventFactory.createRunning(this.isReplay));
      this.socketChannel.sendMessage(outgoingMessageFactory.createRunToNextBreakpointMessage());
    }
  }

  runToEndOfMethod() {
    if (this.areSteppingCommandsEnabled()) {
      this.emitStatusUpdated(statusUpdateEventFactory.createRunning(this.isReplay));
      this.socketChannel.sendMessage(outgoingMessageFactory.createRunToEndOfMethodMessage());
    }
  }

  startReplay(element) {
    if (!this.areAllControlsDisabled && this.isExecutableInDebuggingMode && (this.currentStatusEvent.getStatus() !== statusUpdateEventFactory.RUNNING_STATUS)) {
      this.socketChannel.sendMessage(outgoingMessageFactory.createStartReplayMessage(element.dataset.callId));

      if (!this.isReplay && (this.currentStatusEvent.getStatus() !== statusUpdateEventFactory.END_OF_TAPE_STATUS)) {
        this.lastEventBeforeReplay = this.currentStatusEvent;
      }
      this.isReplay = true;
      this.emitReplayStarted();
    }
  }

  stopReplay() {
    if (!this.areAllControlsDisabled && this.isReplay) {
      this.socketChannel.sendMessage(outgoingMessageFactory.createStopReplayMessage());
      this.isReplay = false;
      this.emitReplayStopped();
      this.emitStatusUpdated(this.lastEventBeforeReplay);
    }
  }

  areSteppingCommandsEnabled() {
    return !this.areAllControlsDisabled && this.isExecutableInDebuggingMode && !this.isAutoSteppingEnabled;
  }

  variableTableFromString(string) {
    this.variableTable = variableTableManager.fromString(string, this.variableTable);
    this.emitVariableTableUpdated();
  }

  getVariableTable() {
    return this.variableTable;
  }

  callStackFromString(string) {
    this.callStack = callStackFromString(string);
    this.emitCallStackUpdated();
  }

  getCallStack() {
    return this.callStack;
  }

  startExecutable() {
    levelsWorkspaceManager.getActiveLevelCodeEditor().startExecution({runExecArgs: ['-d']});
  }

  stopExecutable() {
    levelsWorkspaceManager.getActiveLevelCodeEditor().stopExecution();
  }

  sendEnableDisableAllBreakpoints() {
    if (breakpointManager.getAreBreakpointsEnabled()) {
      this.socketChannel.sendMessage(outgoingMessageFactory.createEnableAllBreakpointsMessage());
    } else {
      this.socketChannel.sendMessage(outgoingMessageFactory.createDisableAllBreakpointsMessage());
    }
  }

  flipAndSortVariableTable() {
    if (!this.areAllControlsDisabled && this.isExecutableInDebuggingMode) {
      variableTableManager.flipSortMode();
      variableTableManager.sort(this.variableTable);
      this.emitVariableTableUpdated();
    }
  }

  onRunning(callback) {
    return this.emitter.on('running', callback);
  }

  onStopped(callback) {
    return this.emitter.on('stopped', callback);
  }

  onPositionUpdated(callback) {
    return this.emitter.on('position-updated', callback);
  }

  onCallStackUpdated(callback) {
    return this.emitter.on('call-stack-updated', callback);
  }

  onVariableTableUpdated(callback) {
    return this.emitter.on('variable-table-updated', callback);
  }

  onStatusUpdated(callback) {
    return this.emitter.on('status-updated', callback);
  }

  onAutoSteppingEnabled(callback) {
    return this.emitter.on('auto-stepping-enabled', callback);
  }

  onAutoSteppingDisabled(callback) {
    return this.emitter.on('auto-stepping-disabled', callback);
  }

  onEnableDisableAllBreakpoints(callback) {
    return this.emitter.on('enable-disable-all-breakpoints', callback);
  }

  onEnableDisableAllControls(callback) {
    return this.emitter.on('enable-disable-all-controls', callback);
  }

  onReplayStarted(callback) {
    return this.emitter.on('replay-started', callback);
  }

  onReplayStopped(callback) {
    return this.emitter.on('replay-stopped', callback);
  }

  emitRunning() {
    this.emitter.emit('running');
  }

  emitStopped() {
    this.emitter.emit('stopped');
  }

  emitPositionUpdated(string) {
    const splitted = string.split(DELIMITER);
    const currentPosition = new Position(+splitted[0], +splitted[1]);
    const point = toPoint(currentPosition);

    breakpointManager.restoreHiddenBreakpoint();
    breakpointManager.hideBreakpoint(currentPosition);

    if (this.positionMarker) {
      this.positionMarker.destroy();
    }
    this.positionMarker = levelsWorkspaceManager.addPositionMarker(point);

    this.emitter.emit('position-updated', currentPosition);
    this.emitStatusUpdated(statusUpdateEventFactory.createWaiting(this.isReplay));

    const textEditor = levelsWorkspaceManager.getActiveTextEditor();
    if (textEditor) {
      textEditor.scrollToBufferPosition(point);
    }
  }

  emitCallStackUpdated() {
    this.emitter.emit('call-stack-updated');
  }

  emitVariableTableUpdated() {
    this.emitter.emit('variable-table-updated');
  }

  emitStatusUpdated(event) {
    this.emitter.emit('status-updated', event);
    this.currentStatusEvent = event;
    if (event.isBlockingStatus()) {
      this.emitAutoSteppingEnabled();
    } else {
      this.emitAutoSteppingDisabled();
    }
  }

  emitAutoSteppingEnabled() {
    this.isAutoSteppingEnabled = true;
    this.emitter.emit('auto-stepping-enabled');
  }

  emitAutoSteppingDisabled() {
    this.isAutoSteppingEnabled = false;
    this.emitter.emit('auto-stepping-disabled');
  }

  emitEnableDisableAllBreakpoints() {
    this.emitter.emit('enable-disable-all-breakpoints', breakpointManager.getAreBreakpointsEnabled());
  }

  emitEnableDisableAllControls(enabled) {
    this.areAllControlsDisabled = !enabled;
    this.emitter.emit('enable-disable-all-controls', enabled);
  }

  emitReplayStarted() {
    this.emitter.emit('replay-started');
  }

  emitReplayStopped() {
    this.emitter.emit('replay-stopped');
  }

  handleChannelError() {
    this.disconnectAndCleanup();
  }

  handleExecutableStarted() {
    if (!this.isExecutableInDebuggingMode) {
      this.emitEnableDisableAllControls(false);
    }
  }

  handleExecutableStopped() {
    if (this.isExecutableInDebuggingMode) {
      this.disconnectAndCleanup();
    }
    this.emitEnableDisableAllControls(levelsWorkspaceManager.isActiveLevelDebuggable());
  }

  handleReady() {
    this.emitRunning();
    this.emitAutoSteppingDisabled();
    for (const bp of breakpointManager.getBreakpoints()) {
      this.socketChannel.sendMessage(outgoingMessageFactory.createAddBreakpointMessage(bp.getPosition()));
    }
    this.sendEnableDisableAllBreakpoints();
  }

  handleStop() {
    if (this.isExecutableInDebuggingMode) {
      this.disconnectAndCleanup();
    }

    this.isReplay = false;
    this.isAutoSteppingEnabled = true;
    this.variableTable = [];
    this.callStack = [];
    variableTableManager.resetSortMode();
    this.emitStopped();
    this.emitAutoSteppingEnabled();

    if (this.positionMarker) {
      this.positionMarker.destroy();
    }
    breakpointManager.restoreHiddenBreakpoint();

    this.emitStatusUpdated(statusUpdateEventFactory.createStopped(this.isReplay));
  }

  handleEndOfReplayTape() {
    this.emitStatusUpdated(statusUpdateEventFactory.createEndOfTape(false));
  }

  handleWorkspaceEntered() {
    const editor = levelsWorkspaceManager.getActiveLevelCodeEditor();

    if (this.isExecutableInDebuggingMode) {
      const enabled = this.debuggingEditorId === editor.getId();
      this.emitEnableDisableAllControls(enabled);
    } else {
      this.handleLevelChanged();

      this.execSubscriptions = new CompositeDisposable();
      this.execSubscriptions.add(editor.onDidStartExecution(() => this.handleExecutableStarted()));
      this.execSubscriptions.add(editor.onDidStopExecution(() => this.handleExecutableStopped()));

      if (editor.isExecuting()) {
        this.emitEnableDisableAllControls(false);
      }
    }
  }

  handleWorkspaceExited() {
    if (this.isExecutableInDebuggingMode) {
      this.emitEnableDisableAllControls(false);
    } else {
      this.handleLevelChanged();
      this.execSubscriptions.dispose();
    }
  }

  handleLevelChanged() {
    if (!this.isExecutableInDebuggingMode) {
      if (levelsWorkspaceManager.isActiveLevelDebuggable()) {
        this.emitStatusUpdated(this.lastEventBeforeDisabling);
        this.emitEnableDisableAllControls(true);
      } else {
        if (this.currentStatusEvent.getStatus() !== statusUpdateEventFactory.DISABLED_STATUS) {
          this.lastEventBeforeDisabling = this.currentStatusEvent;
        }
        this.emitStatusUpdated(statusUpdateEventFactory.createDisabled(this.isReplay));
        this.emitEnableDisableAllControls(false);
      }
    }
  }

  handleLevelCodeEditorChanged() {
    const editor = levelsWorkspaceManager.getActiveLevelCodeEditor();

    if (this.isExecutableInDebuggingMode) {
      const enabled = this.debuggingEditorId === editor.getId();
      this.emitEnableDisableAllControls(enabled);
    } else {
      this.execSubscriptions.dispose();
      this.execSubscriptions = new CompositeDisposable();
      this.execSubscriptions.add(editor.onDidStartExecution(() => this.handleExecutableStarted()));
      this.execSubscriptions.add(editor.onDidStopExecution(() => this.handleExecutableStopped()));

      if (editor.isExecuting()) {
        this.emitEnableDisableAllControls(false);
      } else {
        this.emitEnableDisableAllControls(levelsWorkspaceManager.isActiveLevelDebuggable());
      }
    }
  }
}<|endoftext|>