<commit_before>$(document).ready(function(){
	if($('#content .container').hasClass('home'))
		twitterIni();
	$.cookie.defaults.path = '/';
	$('.jscrollpane').jScrollPane();
	$('.custom-select').customSelect();
	$('.calificacion-form').validate();
	$('.reporte-form').validate();
	$('.petition-form').validate();
	$('.contacto-form').validate();
	$('form.newsletter').validate();

	$('#ver-en-mapa').click(function(e){
		e.preventDefault();
		$('#general-search').attr('action',$(this).attr('href'));
		$('#general-search').submit();
	});

	$('.compara-tabs a').click(function(e){
		e.preventDefault();
		var index = $(this).index();
		$('.compara-tabs a.on').removeClass('on');
		$(this).addClass('on');
		$('.compara-tab-container .tab.on').removeClass('on');
		$('.compara-tab-container .tab').eq(index).addClass('on');
	})

	$('.compara-escuela').on('click',function(e){
		e.preventDefault();
		var tr = $(this).parent().parent().toggleClass('on');
		var cct = $(this).attr('href');
		toggle_escuela(cct);
	});

	$('#content .perfil .tabs li a').click(function(e){
		e.preventDefault();
		var index = 4 - $(this).parent().index();
		$('#content .perfil .tabs li.on').removeClass('on');
		$(this).parent().addClass('on');
		$('#content .perfil .tab-container .tab.on').removeClass('on');
		$('#content .perfil .tab-container .tab').eq(index).addClass('on').jScrollPane();
		if($(this).html() == 'Resultados Educativos' ) drawCharts();
	})
	/*$('#rank-bar').mousemove(function(e){
		set_rank_bar(e.pageX - $(this).offset().left);
	});*/
	/*$('#rank-bar').click(function(e){
		var rank = set_rank_bar(e.pageX - $(this).offset().left);
		$('#rank-value').val(rank);
	});*/
	/*$('#rank-bar').mouseout(function(e){
		var max = $('.container').hasClass('B')?173:336;
		var x = e.pageX - $(this).offset().left;
		var y = e.pageY - $(this).offset().top;
		if((y >= 10 || y < 0) || (x < 0 || x > max)){
			var val = $('#rank-value').val();
			if(val != ''){
				set_rank_bar(Math.round(val/100*max));
			}else{
				$('#rank-bar .bar').hide();
				$('#rank-label').hide();
			}
		}
	});*/
	if($("#name-input") .length)
	$( "#name-input" ).autocomplete({
  		source: function(request,response){
  			$.post("/main/get_escuelas/",{
  				term : request.term,
  				entidad : $("#state-input").val(),
  				municipio : $("#municipio-input").val(),
  				localidad : $("#localidad-input").val(),
  				nivel : $("#nivel-input").val(),
  				json : true
  			},response,'json');
  		},
  		minLength: 3,
		select: function( event, ui ) {
			window.location = "/escuelas/index/"+ui.item.cct;
			return false;
		}
	}).data( "ui-autocomplete" )._renderItem = function( ul, item ){
      return $("
").append(""+item.label+""+item.address+"").appendTo(ul);
    };

	$('#map-button').click(function(e){
		e.preventDefault();
		$('#general-search').attr('action','/mapa/');
		$('#general-search').submit();

	});
	$('#state-input').change(function(e){
		$("#localidad-input").prop('disabled', true).next().addClass('customSelectDisabled');
		load_location_options(
			$("#municipio-input"),
			'load_municipios',
			{entidad:$(this).val(),json:true},
			"Municipio"
		);
	});

	$('#municipio-input').change(function(){
		if($(this).val() != "" || ($("#state-input option:selected").val() != "" && $("#state-input option:selected").val() != 7 && $("#state-input option:selected").val() != 20 && $("#state-input option:selected").val() != 30)){
			load_location_options(
				$("#localidad-input"),
				'load_localidades',
				{municipio:$(this).val(),entidad:$("#state-input option:selected").val(),json:true},
				"Localidad"
			);
		}else{
			$("#localidad-input").prop('disabled', true).next().addClass('customSelectDisabled');
			$("#localidad-input").html('<option value="">Localidad</option>');
		}
	});

	$('#content .home .circle a.line').hover(function(){
		var x=[27,23,19];
		var i=$(this).index();
		var xP=$(this).position().left;
		$(this).parent().find('.line1').css({'left':xP+x[i-1] + 'px','display':'block'});
		$(this).parent().find('.line2').css({'left':89+xP+x[i-1]+'px','display':'block'});
	},function(){
			$(this).parent().find('.line1').css('display','none');
			$(this).parent().find('.line2').css('display','none');
	});

	$('#content .share-bt a.button-frame').click(function(e){
		e.preventDefault();
		$('#content .share-bt .social').toggleClass('on');
	});

	$('.wrap_cal span').click(function(){
		var span =  $(this).parent().find('span');
		if($(this).hasClass('on') && $(this).index()==0){
			span.removeClass('on');
		}else{
			span.removeClass('on');
			for(var i=0;i<=$(this).index();i++){
				$(span[i]).addClass('on');
			}
		}
		var promedio = $('.wrap_cal span.on').size() / $('.wrap_cal').size();
		promedio = promedio.toString().length>3?promedio.toFixed(1):promedio;
		$('.promedio span').html(promedio);
		$('#rank-value').val(promedio);
	});

	$('.menu a.logo + a + a').click(function(e){
		e.preventDefault();
		var cookie = $.cookie('escuelas'),
		    url = '/compara/escuelas/'+ (cookie != undefined ? cookie:'');
		location.href = url;
	});

	$('.search-estado select.custom-select').change(function(){
		var id;
		if((id = $(this).val())!=''){
			location.href = '/resultados-nacionales/entidad/'+id;
		}
	});

	$('.peticion h1').click(function(){
		$('.wrap_peticion, .social.on').removeClass('on');
		$(this).next().addClass('on');
	});

	if($('#content .container').hasClass('perfil')){
		$('.menu a.logo + a + a + a').click(function(e){
			e.preventDefault();
			location.href = $('.califica a.title').attr('href');
		});

		$('.tabs a.result').click(function(){
		var texts = $('text[text-anchor="start"]'),
		    text;
		for(var i=0;i<texts.length;i++){
			text = $(texts[i]);
			text.text(text.text()+' grado');
		}
		});

	}

	if($('.container').hasClass('perfil')){
		var cct = $('span.CCT').html();
		var escuelas = $.cookie('escuelas') && $.cookie('escuelas').split('-') || [];
		if(escuelas.indexOf(cct)==-1){
			escuelas.push(cct);
			escuelas.sort();
			$.cookie('escuelas',escuelas.join('-'));
			$('a[href="'+cct+'"]').parent().parent().addClass('on');
		}
		/*if(!($.cookie('escuelas')) || $.cookie('escuelas').split('-').indexOf(cct)==-1){
			$('a[href="'+cct+'"]').trigger('click');
			if(!$('a[href="'+cct+'"]').size()){
				toggle_escuela(cct);
			}
		}*/

	}

	if($('.container').hasClass('comparar')){
		$('#general-search').submit(function(e){
			e.preventDefault();
			var url='';
			$(this).find('[name]').each(function(i,val){
				url +='&'+$(val).attr('name')+'='+$(val).val();
			});
			window.location = $(this).attr('action')+url;
		});
	}

	if($('.container').hasClass('resultados')){
		$(window).unload(add_escuelas_cookie);
	}

	$('#compara-main-button').click(function(e){
		e.preventDefault();
		add_escuelas_cookie();
		$(window).off();
		location.href = '/compara/escuelas/' + $.cookie('escuelas');
	});

	$('#content .comparar.resultados table tr.on .compara-escuela').click(function(e){
		e.preventDefault();
		var cct = $(this).attr('href'),
		url=document.URL.replace(new RegExp('-*'+cct),''),
		escuelas = $.cookie('escuelas') && $.cookie('escuelas').split('-') || [];
		escuelas.splice(escuelas.indexOf(cct),1);
		$.cookie('escuelas',escuelas.join('-'));
		if(document.URL != url)
			location.href = url;

	});

});

function load_location_options(input,directive,options,name){
	input.prop('disabled', true);
	input.next().addClass('customSelectDisabled');
	$.post('/main/'+directive,options,function(data){
		input.html('<option value="">'+name+'</option>');
		for(x in data){
			var item = data[x];
			input.append('<option value="'+item.id+'">'+item.nombre+'</option>');
		}
		input.prop('disabled', false);
		input.next().removeClass('customSelectDisabled');
		input.trigger('change');
	},'json');
}
/*function set_rank_bar(x){
	var max = $('.container').hasClass('B')?173:336;
	var rank = Math.round(x/max * 100);
	var offset = x - 12;
	$('#rank-bar .bar').css('width',x+'px').show();
	$('#rank-label').html(rank+'%').css('left',offset+'px').show();
	return rank;
}*/
function toggle_escuela(cct){
	var escuelas = $.cookie('escuelas') && $.cookie('escuelas').split('-') || [],
	index;
	if((index = escuelas.indexOf(cct)) != -1){
		escuelas.splice(index,1);
		escuelas.sort();
		$.cookie('escuelas',escuelas.join('-'));
	}
	//$('#compara-main-button').attr('href','/compara/escuelas/'+escuelas.join('-') || '');
	/*
	if(typeof($.cookie('escuelas')) == 'undefined'){
		$.cookie('escuelas',[cct]);
	}else{
		var escuelas = $.cookie('escuelas').split('-');
		var index = escuelas.indexOf(cct);
		if(index != -1){
			escuelas.pop(index);
		}else{
			escuelas.push(cct);
		}
		//escuelas.sort();
		$.cookie('escuelas',escuelas.join('-'));
		if( $('.container.resultados').hasClass('comparar')){
			var url=document.URL.replace(new RegExp('-*'+cct),'');
			if(document.URL != url)
				location.href = url;
		}
	}
	*/
}
function twitterIni(){
    var username =  "mejoratuescuela",
	page_proxy = '/home/twitter';
	$("#tweets .tweet p").html("cargando tweets...");
	$.getJSON(page_proxy, function(data){
		$("#tweets .tweet").css('display','none');
		$("#tweets").append('

');
		for(d in data){
			var x = data[d];
			$("#tweets ul").append('

@'+username+' '+x.text+'

');
	    	}
	    })
}

function add_escuelas_cookie(){
	var selector_table = $('.resultados.container table'),
	on = selector_table.find('tr.on'),
	cookie = $.cookie('escuelas'),
	escuelas = cookie && cookie.split('-') || [];
	for(var i=0;i<on.length;i++){
		var val = $(on[i]),
		cct = val.find('.compara-escuela').attr('href');
		if(escuelas.indexOf(cct)==-1)
			escuelas.push(cct);
	}
	escuelas.sort();
	if(escuelas.length){
		$.cookie('escuelas',escuelas.join('-'));
	}
}
<commit_msg>una correccion mas<commit_after>$(document).ready(function(){
	if($('#content .container').hasClass('home'))
		twitterIni();
	$.cookie.defaults.path = '/';
	$('.jscrollpane').jScrollPane();
	$('.custom-select').customSelect();
	$('.calificacion-form').validate();
	$('.reporte-form').validate();
	$('.petition-form').validate();
	$('.contacto-form').validate();
	$('form.newsletter').validate();

	$('#ver-en-mapa').click(function(e){
		e.preventDefault();
		$('#general-search').attr('action',$(this).attr('href'));
		$('#general-search').submit();
	});

	$('.compara-tabs a').click(function(e){
		e.preventDefault();
		var index = $(this).index();
		$('.compara-tabs a.on').removeClass('on');
		$(this).addClass('on');
		$('.compara-tab-container .tab.on').removeClass('on');
		$('.compara-tab-container .tab').eq(index).addClass('on');
	})

	$('.compara-escuela').on('click',function(e){
		e.preventDefault();
		var tr = $(this).parent().parent().toggleClass('on');
		var cct = $(this).attr('href');
		toggle_escuela(cct);
	});

	$('#content .perfil .tabs li a').click(function(e){
		e.preventDefault();
		var index = 4 - $(this).parent().index();
		$('#content .perfil .tabs li.on').removeClass('on');
		$(this).parent().addClass('on');
		$('#content .perfil .tab-container .tab.on').removeClass('on');
		$('#content .perfil .tab-container .tab').eq(index).addClass('on').jScrollPane();
		if($(this).html() == 'Resultados Educativos' ) drawCharts();
	})
	/*$('#rank-bar').mousemove(function(e){
		set_rank_bar(e.pageX - $(this).offset().left);
	});*/
	/*$('#rank-bar').click(function(e){
		var rank = set_rank_bar(e.pageX - $(this).offset().left);
		$('#rank-value').val(rank);
	});*/
	/*$('#rank-bar').mouseout(function(e){
		var max = $('.container').hasClass('B')?173:336;
		var x = e.pageX - $(this).offset().left;
		var y = e.pageY - $(this).offset().top;
		if((y >= 10 || y < 0) || (x < 0 || x > max)){
			var val = $('#rank-value').val();
			if(val != ''){
				set_rank_bar(Math.round(val/100*max));
			}else{
				$('#rank-bar .bar').hide();
				$('#rank-label').hide();
			}
		}
	});*/
	if($("#name-input") .length)
	$( "#name-input" ).autocomplete({
  		source: function(request,response){
  			$.post("/main/get_escuelas/",{
  				term : request.term,
  				entidad : $("#state-input").val(),
  				municipio : $("#municipio-input").val(),
  				localidad : $("#localidad-input").val(),
  				nivel : $("#nivel-input").val(),
  				json : true
  			},response,'json');
  		},
  		minLength: 3,
		select: function( event, ui ) {
			window.location = "/escuelas/index/"+ui.item.cct;
			return false;
		}
	}).data( "ui-autocomplete" )._renderItem = function( ul, item ){
      return $("
").append(""+item.label+""+item.address+"").appendTo(ul);
    };

	$('#map-button').click(function(e){
		e.preventDefault();
		$('#general-search').attr('action','/mapa/');
		$('#general-search').submit();

	});
	$('#state-input').change(function(e){
		$("#localidad-input").prop('disabled', true).next().addClass('customSelectDisabled');
		load_location_options(
			$("#municipio-input"),
			'load_municipios',
			{entidad:$(this).val(),json:true},
			"Municipio"
		);
	});

	$('#municipio-input').change(function(){
		if($(this).val() != "" || ($("#state-input option:selected").val() != "" && $("#state-input option:selected").val() != 7 && $("#state-input option:selected").val() != 20 && $("#state-input option:selected").val() != 30)){
			load_location_options(
				$("#localidad-input"),
				'load_localidades',
				{municipio:$(this).val(),entidad:$("#state-input option:selected").val(),json:true},
				"Localidad"
			);
		}else{
			$("#localidad-input").prop('disabled', true).next().addClass('customSelectDisabled');
			$("#localidad-input").html('<option value="">Localidad</option>');
		}
	});

	$('#content .home .circle a.line').hover(function(){
		var x=[27,23,19];
		var i=$(this).index();
		var xP=$(this).position().left;
		$(this).parent().find('.line1').css({'left':xP+x[i-1] + 'px','display':'block'});
		$(this).parent().find('.line2').css({'left':89+xP+x[i-1]+'px','display':'block'});
	},function(){
			$(this).parent().find('.line1').css('display','none');
			$(this).parent().find('.line2').css('display','none');
	});

	$('#content .share-bt a.button-frame').click(function(e){
		e.preventDefault();
		$('#content .share-bt .social').toggleClass('on');
	});

	$('.wrap_cal span').click(function(){
		var span =  $(this).parent().find('span');
		if($(this).hasClass('on') && $(this).index()==0){
			span.removeClass('on');
		}else{
			span.removeClass('on');
			for(var i=0;i<=$(this).index();i++){
				$(span[i]).addClass('on');
			}
		}
		var promedio = $('.wrap_cal span.on').size() / $('.wrap_cal').size();
		promedio = promedio.toString().length>3?promedio.toFixed(1):promedio;
		$('.promedio span').html(promedio);
		$('#rank-value').val(promedio);
	});

	$('.menu a.logo + a + a').click(function(e){
		e.preventDefault();
		var cookie = $.cookie('escuelas'),
		    url = '/compara/escuelas/'+ (cookie != undefined ? cookie:'');
		location.href = url;
	});

	$('.search-estado select.custom-select').change(function(){
		var id;
		if((id = $(this).val())!=''){
			location.href = '/resultados-nacionales/entidad/'+id;
		}
	});

	$('.peticion h1').click(function(){
		$('.wrap_peticion, .social.on').removeClass('on');
		$(this).next().addClass('on');
	});

	if($('#content .container').hasClass('perfil')){
		$('.menu a.logo + a + a + a').click(function(e){
			e.preventDefault();
			location.href = $('.califica a.title').attr('href');
		});

		$('.tabs a.result').click(function(){
		var texts = $('text[text-anchor="start"]'),
		    text;
		for(var i=0;i<texts.length;i++){
			text = $(texts[i]);
			text.text(text.text()+' grado');
		}
		});

	}

	if($('.container').hasClass('perfil')){
		var cct = $('span.CCT').html();
		var escuelas = $.cookie('escuelas') && $.cookie('escuelas').split('-') || [];
		if(escuelas.indexOf(cct)==-1){
			escuelas.push(cct);
			escuelas.sort();
			$.cookie('escuelas',escuelas.join('-'));
			$('a[href="'+cct+'"]').parent().parent().addClass('on');
		}
		/*if(!($.cookie('escuelas')) || $.cookie('escuelas').split('-').indexOf(cct)==-1){
			$('a[href="'+cct+'"]').trigger('click');
			if(!$('a[href="'+cct+'"]').size()){
				toggle_escuela(cct);
			}
		}*/

	}

	if($('.container').hasClass('comparar')){
		$('#general-search').submit(function(e){
			e.preventDefault();
			var url='';
			$(this).find('[name]').each(function(i,val){
				url +='&'+$(val).attr('name')+'='+$(val).val();
			});
			window.location = $(this).attr('action')+url;
		});
	}

	if($('.container').hasClass('resultados')){
		$(window).unload(add_escuelas_cookie);
	}

	$('#compara-main-button').click(function(e){
		e.preventDefault();
		add_escuelas_cookie();
		$(window).off();
		location.href = '/compara/escuelas/' + $.cookie('escuelas');
	});

	$('#content .comparar.resultados table tr.on .compara-escuela').click(function(e){
		e.preventDefault();
		var cct = $(this).attr('href'),
		url=document.URL.replace(new RegExp('-*'+cct),''),
		escuelas = $.cookie('escuelas') && $.cookie('escuelas').split('-') || [];
		escuelas.splice(escuelas.indexOf(cct),1);
		$.cookie('escuelas',escuelas.join('-'));
		if(document.URL != url)
			location.href = url;

	});

});

function load_location_options(input,directive,options,name){
	input.prop('disabled', true);
	input.next().addClass('customSelectDisabled');
	$.post('/main/'+directive,options,function(data){
		input.html('<option value="">'+name+'</option>');
		for(x in data){
			var item = data[x];
			item.nombre = item.nombre[0].toLocaleUpperCase() + item.nombre.substr(1);
			input.append('<option value="'+item.id+'">'+item.nombre+'</option>');
		}
		input.prop('disabled', false);
		input.next().removeClass('customSelectDisabled');
		input.trigger('change');
	},'json');
}
/*function set_rank_bar(x){
	var max = $('.container').hasClass('B')?173:336;
	var rank = Math.round(x/max * 100);
	var offset = x - 12;
	$('#rank-bar .bar').css('width',x+'px').show();
	$('#rank-label').html(rank+'%').css('left',offset+'px').show();
	return rank;
}*/
function toggle_escuela(cct){
	var escuelas = $.cookie('escuelas') && $.cookie('escuelas').split('-') || [],
	index;
	if((index = escuelas.indexOf(cct)) != -1){
		escuelas.splice(index,1);
		escuelas.sort();
		$.cookie('escuelas',escuelas.join('-'));
	}
	//$('#compara-main-button').attr('href','/compara/escuelas/'+escuelas.join('-') || '');
	/*
	if(typeof($.cookie('escuelas')) == 'undefined'){
		$.cookie('escuelas',[cct]);
	}else{
		var escuelas = $.cookie('escuelas').split('-');
		var index = escuelas.indexOf(cct);
		if(index != -1){
			escuelas.pop(index);
		}else{
			escuelas.push(cct);
		}
		//escuelas.sort();
		$.cookie('escuelas',escuelas.join('-'));
		if( $('.container.resultados').hasClass('comparar')){
			var url=document.URL.replace(new RegExp('-*'+cct),'');
			if(document.URL != url)
				location.href = url;
		}
	}
	*/
}
function twitterIni(){
    var username =  "mejoratuescuela",
	page_proxy = '/home/twitter';
	$("#tweets .tweet p").html("cargando tweets...");
	$.getJSON(page_proxy, function(data){
		$("#tweets .tweet").css('display','none');
		$("#tweets").append('

');
		for(d in data){
			var x = data[d];
			$("#tweets ul").append('

@'+username+' '+x.text+'

');
	    	}
	    })
}

function add_escuelas_cookie(){
	var selector_table = $('.resultados.container table'),
	on = selector_table.find('tr.on'),
	cookie = $.cookie('escuelas'),
	escuelas = cookie && cookie.split('-') || [];
	for(var i=0;i<on.length;i++){
		var val = $(on[i]),
		cct = val.find('.compara-escuela').attr('href');
		if(escuelas.indexOf(cct)==-1)
			escuelas.push(cct);
	}
	escuelas.sort();
	if(escuelas.length){
		$.cookie('escuelas',escuelas.join('-'));
	}
}
<|endoftext|>


<commit_before>(function () {
    'use strict';

    const GATEWAYS = {
        [WavesApp.defaultAssets.VOSTOK]: { waves: 'WVOSTOK', gateway: 'VOSTOK' }
    };

    const PATH = 'https://gateways-dev.wvservices.com/api/v1';

    /**
     * @returns {VostokService}
     */

    const factory = function () {

        class VostokService {

            /**
             * @param {Asset} asset
             * @return {IGatewaySupportMap}
             */
            getSupportMap(asset) {
                if (GATEWAYS[asset.id]) {
                    return {
                        deposit: true,
                        withdraw: true
                    };
                }
            }

            /**
             * From Vostok to Waves
             * @param {Asset} asset
             * @param {string} wavesAddress
             * @return {Promise}
             */
            getDepositDetails(asset, wavesAddress) {
                VostokService._assertAsset(asset.id);

                const body = JSON.stringify({
                    userAddress: wavesAddress,
                    assetId: asset.id
                });

                return ds.fetch(`${PATH}/external/deposit`, { method: 'POST', body })
                    .then(details => {
                        const [minAmount, maxAmount, fee] = [details.minAmount, details.maxAmount, details.fee]
                            .map(value => this._normalaizeValue(value, -asset.precision));
                        return ({
                            address: details.address,
                            minimumAmount: new BigNumber(minAmount),
                            maximumAmount: new BigNumber(maxAmount),
                            gatewayFee: new BigNumber(fee)
                        });
                    });
            }

            /**
             * From Waves to Vostok
             * @param {Asset} asset
             * @param {string} targetAddress
             * @param {string} [paymentId]
             * @return {Promise}
             */
            getWithdrawDetails(asset, targetAddress) {
                VostokService._assertAsset(asset.id);

                const body = JSON.stringify({
                    userAddress: targetAddress,
                    assetId: asset.id
                });

                return ds.fetch(`${PATH}/external/withdraw`, { method: 'POST', body })
                    .then(details => {
                        const [minAmount, maxAmount, fee] = [details.minAmount, details.maxAmount, details.fee]
                            .map(value => this._normalaizeValue(value, -asset.precision));
                        return ({
                            address: details.recipientAddress,
                            minimumAmount: new BigNumber(minAmount),
                            maximumAmount: new BigNumber(maxAmount),
                            gatewayFee: new BigNumber(fee),
                            attachment: details.processId
                        });
                    });
            }

            /**
             * @param {Asset} asset
             * @return {string}
             */
            getAssetKeyName(asset) {
                return `${GATEWAYS[asset.id].gateway}`;
            }

            /**
             *
             * @param value {number}
             * @param pow {number}
             * @returns {number}
             * @private
             */
            _normalaizeValue(value, pow) {
                return value * Math.pow(10, pow);
            }

            static _assertAsset(assetId) {
                if (!GATEWAYS[assetId]) {
                    throw new Error('Asset is not supported by Vostok');
                }
            }

        }

        return new VostokService();
    };

    angular.module('app.utils').factory('vostokService', factory);
})();
<commit_msg>CLIENT-1836: normalize response values do better<commit_after>(function () {
    'use strict';

    const GATEWAYS = {
        [WavesApp.defaultAssets.VOSTOK]: { waves: 'WVOSTOK', gateway: 'VOSTOK' }
    };

    const PATH = 'https://gateways-dev.wvservices.com/api/v1';

    /**
     * @returns {VostokService}
     */

    const factory = function () {

        class VostokService {

            /**
             * @param {Asset} asset
             * @return {IGatewaySupportMap}
             */
            getSupportMap(asset) {
                if (GATEWAYS[asset.id]) {
                    return {
                        deposit: true,
                        withdraw: true
                    };
                }
            }

            /**
             * From Vostok to Waves
             * @param {Asset} asset
             * @param {string} wavesAddress
             * @return {Promise}
             */
            getDepositDetails(asset, wavesAddress) {
                VostokService._assertAsset(asset.id);

                const body = JSON.stringify({
                    userAddress: wavesAddress,
                    assetId: asset.id
                });

                return ds.fetch(`${PATH}/external/deposit`, { method: 'POST', body })
                    .then(details => {
                        const [minAmount, maxAmount, fee] = [details.minAmount, details.maxAmount, details.fee]
                            .map(value => this._normalaizeValue(value, -asset.precision));
                        return ({
                            address: details.address,
                            minimumAmount: new BigNumber(minAmount),
                            maximumAmount: new BigNumber(maxAmount),
                            gatewayFee: new BigNumber(fee)
                        });
                    });
            }

            /**
             * From Waves to Vostok
             * @param {Asset} asset
             * @param {string} targetAddress
             * @param {string} [paymentId]
             * @return {Promise}
             */
            getWithdrawDetails(asset, targetAddress) {
                VostokService._assertAsset(asset.id);

                const body = JSON.stringify({
                    userAddress: targetAddress,
                    assetId: asset.id
                });

                return ds.fetch(`${PATH}/external/withdraw`, { method: 'POST', body })
                    .then(details => {
                        const [minAmount, maxAmount, fee] = [details.minAmount, details.maxAmount, details.fee]
                            .map(value => this._normalaizeValue(value, -asset.precision));
                        return ({
                            address: details.recipientAddress,
                            minimumAmount: new BigNumber(minAmount),
                            maximumAmount: new BigNumber(maxAmount),
                            gatewayFee: new BigNumber(fee),
                            attachment: details.processId
                        });
                    });
            }

            /**
             * @param {Asset} asset
             * @return {string}
             */
            getAssetKeyName(asset) {
                return `${GATEWAYS[asset.id].gateway}`;
            }

            /**
             *
             * @param {number} value
             * @param {number} pow
             * @returns {number}
             * @private
             */
            _normalaizeValue(value, pow) {
                return value * Math.pow(10, pow);
            }

            static _assertAsset(assetId) {
                if (!GATEWAYS[assetId]) {
                    throw new Error('Asset is not supported by Vostok');
                }
            }

        }

        return new VostokService();
    };

    angular.module('app.utils').factory('vostokService', factory);
})();
<|endoftext|>


<commit_before>/*
 * Copyright 2014 MarkLogic
 *
 * Licensed 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.
 */
'use strict';

var moment = require('/lib/moment.2.8.3.min.sjs');
var m = require('/lib/moment-range.1.0.5.min.sjs');

function valueToDate(value) {
  if(value instanceof Value) {
    return new Date(value.toObject());
  }
  return value;
}

/**
 * Month-long buckets fully containing the start and end dates. Optionally set where to
 * start counting in the month (1-31).
 */
function monthBuckets(start, end, dateStart /* 1-31 */) {
  // xdmp.log([start, end].join(' – '));
  // xdmp.log(start instanceof Date && end instanceof Date);
  // xdmp.log(Object.prototype.toString.call(start));
  // xdmp.log(start instanceof Value);
  // xdmp.log(start instanceof Value && end instanceof Value);
  // xdmp.log(Object.prototype.toString.call(start));
  // xdmp.log(start.toObject().toString());
  // xdmp.log(start.toObject() instanceof Date);
  // xdmp.log(Object.prototype.toString.call(start.toObject()));

  // FIXME: This is an ugly way to get from a Value to a Date (to a moment)
  // if('function' === start.toObject) { start = start.toObject(); }
  // if('function' === end.toObject) { end = end.toObject(); }
  // https://bugtrack.marklogic.com/31445
  try { start = start.toObject(); } catch(err) { xdmp.warn('Yikes!'); }
  try { end = end.toObject(); } catch(err) { xdmp.warn('Yikes!'); }

  // xdmp.log([start, end].join(' – '));
  // xdmp.log(start instanceof Date && end instanceof Date);
  // xdmp.log(Object.prototype.toString.call(start));
  // xdmp.log(start instanceof Date);
  // xdmp.log(start instanceof Value && end instanceof Value);
  // xdmp.log(start.toObject());

  start = moment(start);
  end = moment(end);
  // xdmp.log([start.format(), end.format()].join(' – '));
  // xdmp.log(moment('1986-02-11').format());
  dateStart = dateStart || start.date();

  if(dateStart > start.date()) {
    start.subtract(1, "months").date(dateStart);
  } else {
    start.date(dateStart);
  }

  if(dateStart < end.date()) {
    end.add(1, 'months').date(dateStart);
  } else {
    end.date(dateStart);
  }

  var range = moment().range(start, end);
  var out = [];
  range.by("months", function(moment) {
    out.push(moment.toDate());
  });
  return out;
}

/*
 * Week-long buckets fully containing the start and end dates. Optionally set where to
 * start counting in the week (0: Sun ... 6: Sat).
 */
function weekBuckets(start, end, dayStart /* 0-6 */) {
  start = moment(start);
  end = moment(end);
  // TODO: Default to 0 and allow passing a Date in for dayStart
  dayStart = (dayStart > 6 || dayStart < 0) ? Math.abs(dayStart % 7) : dayStart; // Keep bounds 0-6
  dayStart = dayStart || start.day();
  dayStart = (start.day() < dayStart) ? dayStart - 7 : dayStart; // Make sure to expand to the left

  var range = moment().range(
    start.day(dayStart),
    end.day(dayStart).add(2, "weeks")
  );
  var out = [];
  range.by("weeks", function(moment) {
    out.push(moment.toDate());
  });
  return out;
}

/** @module Buckets */
module.exports = {
  byMonth: monthBuckets,
  byWeek: weekBuckets
}

// var start = new Date(2001, 3-1, 12);
// var end = new Date(2002, 9-1, 28);
//
// var buckets =
// monthBuckets(start, end, 26)
// //weekBuckets(start, end, 0)
//   .map(function(dt){
//   return moment(dt).format("dddd, MMMM Do YYYY");
// });
//
// [
//   buckets[0],
//   buckets[1],
//   "…" + (buckets.length - 4) + " more…",
//   buckets[buckets.length - 2],
//   buckets[buckets.length - 1]
// ]
<commit_msg>Work-around for 31445<commit_after>/*
 * Copyright 2014 MarkLogic
 *
 * Licensed 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.
 */
'use strict';

var moment = require('/lib/moment.2.8.3.min.sjs');
var m = require('/lib/moment-range.1.0.5.min.sjs');

function valueToDate(value) {
  // https://bugtrack.marklogic.com/31445
  var out = value;
  try {
    out = value.toObject();
  } catch(err) { /* Swallow */ }
  return out;
  //if(value instanceof Value) {
  //  return new Date(value.toObject());
  //}
  //return value;
}

/**
 * Month-long buckets fully containing the start and end dates. Optionally set where to
 * start counting in the month (1-31).
 */
function monthBuckets(start, end, dateStart /* 1-31 */) {
  start = valueToDate(start);
  end = valueToDate(end);

  start = moment(start);
  end = moment(end);

  dateStart = dateStart || start.date();

  if(dateStart > start.date()) {
    start.subtract(1, 'months').date(dateStart);
  } else {
    start.date(dateStart);
  }

  if(dateStart < end.date()) {
    end.add(1, 'months').date(dateStart);
  } else {
    end.date(dateStart);
  }

  var range = moment().range(start, end);
  var out = [];
  range.by('months', function(moment) {
    out.push(moment.toDate());
  });
  return out;
}

/*
 * Week-long buckets fully containing the start and end dates. Optionally set where to
 * start counting in the week (0: Sun ... 6: Sat).
 */
function weekBuckets(start, end, dayStart /* 0-6 */) {
  start = moment(start);
  end = moment(end);
  // TODO: Default to 0 and allow passing a Date in for dayStart
  dayStart = (dayStart > 6 || dayStart < 0) ? Math.abs(dayStart % 7) : dayStart; // Keep bounds 0-6
  dayStart = dayStart || start.day();
  dayStart = (start.day() < dayStart) ? dayStart - 7 : dayStart; // Make sure to expand to the left

  var range = moment().range(
    start.day(dayStart),
    end.day(dayStart).add(2, "weeks")
  );
  var out = [];
  range.by("weeks", function(moment) {
    out.push(moment.toDate());
  });
  return out;
}

/** @module Buckets */
module.exports = {
  byMonth: monthBuckets,
  byWeek: weekBuckets
}

// var start = new Date(2001, 3-1, 12);
// var end = new Date(2002, 9-1, 28);
//
// var buckets =
// monthBuckets(start, end, 26)
// //weekBuckets(start, end, 0)
//   .map(function(dt){
//   return moment(dt).format("dddd, MMMM Do YYYY");
// });
//
// [
//   buckets[0],
//   buckets[1],
//   "…" + (buckets.length - 4) + " more…",
//   buckets[buckets.length - 2],
//   buckets[buckets.length - 1]
// ]
<|endoftext|>


<commit_before>var SHIM_ATTRIBUTE = 'rel';
var SHIMMED_ATTRIBUTE = 'shim-shadowdom-css';
var NO_SHIM_ATTRIBUTE = 'no-shim';

module.exports = function(){
  if (window.HTMLImports && !HTMLImports.useNative) {
    var SHIM_SHEET_SELECTOR = 'link[rel=stylesheet]';
    HTMLImports.importer.documentPreloadSelectors += ',' + SHIM_SHEET_SELECTOR;
    HTMLImports.importer.importsPreloadSelectors += ',' + SHIM_SHEET_SELECTOR;

    HTMLImports.parser.documentSelectors = [
      HTMLImports.parser.documentSelectors,
      SHIM_SHEET_SELECTOR,
    ].join(',');

    var originalParseGeneric = HTMLImports.parser.parseGeneric;

    HTMLImports.parser.parseGeneric = function(elt) {
      if (elt[SHIMMED_ATTRIBUTE]) { return; }

      var style = elt.__importElement || elt;

      if(!(style.nodeName=='STYLE' || style.nodeName=='LINK')){
        originalParseGeneric.call(this, elt);
        return;
      }

      if (elt.__resource) {
        style = elt.ownerDocument.createElement('style');
        style.textContent = elt.__resource;
      }

      // relay on HTMLImports for path fixup
      HTMLImports.path.resolveUrlsInStyle(style, elt.href);

      style.setAttribute(SHIMMED_ATTRIBUTE, '');
      style[SHIMMED_ATTRIBUTE] = true;

      elt.ownerDocument.body.appendChild(style)

      this.markParsingComplete(elt);
      this.parseNext();
    }

    var hasResource = HTMLImports.parser.hasResource;
    HTMLImports.parser.hasResource = function(node) {
      if (node.localName === 'link' && node.rel === 'stylesheet' && node.hasAttribute(SHIM_ATTRIBUTE)) {
        return (node.__resource);
      } else {
        return hasResource.call(this, node);
      }
    }

  }
  true
}
<commit_msg>update shim to not append a second style tag.<commit_after>var SHIM_ATTRIBUTE = 'rel';
var SHIMMED_ATTRIBUTE = 'shim-shadowdom-css';
var NO_SHIM_ATTRIBUTE = 'no-shim';

module.exports = function(){
  if (window.HTMLImports && !HTMLImports.useNative) {
    var SHIM_SHEET_SELECTOR = 'link[rel=stylesheet]';
    HTMLImports.importer.documentPreloadSelectors += ',' + SHIM_SHEET_SELECTOR;
    HTMLImports.importer.importsPreloadSelectors += ',' + SHIM_SHEET_SELECTOR;

    HTMLImports.parser.documentSelectors = [
      HTMLImports.parser.documentSelectors,
      SHIM_SHEET_SELECTOR,
    ].join(',');

    var originalParseGeneric = HTMLImports.parser.parseGeneric;

    HTMLImports.parser.parseGeneric = function(elt) {
      if (elt[SHIMMED_ATTRIBUTE]) { return; }

      // var style = elt.__importElement || elt;
      // if(!(style.nodeName=='STYLE' || style.nodeName=='LINK')){
      //   originalParseGeneric.call(this, elt);
      //   return;
      // }

      // if (elt.__resource) {
      //   style = elt.ownerDocument.createElement('style');
      //   style.textContent = elt.__resource;
      // }

      // // relay on HTMLImports for path fixup
      // HTMLImports.path.resolveUrlsInStyle(style, elt.href);

      // style.setAttribute(SHIMMED_ATTRIBUTE, '');
      // style[SHIMMED_ATTRIBUTE] = true;

      // elt.ownerDocument.body.appendChild(style)

      this.markParsingComplete(elt);
      this.parseNext();
    }

    var hasResource = HTMLImports.parser.hasResource;
    HTMLImports.parser.hasResource = function(node) {
      if (node.localName === 'link' && node.rel === 'stylesheet' && node.hasAttribute(SHIM_ATTRIBUTE)) {
        return (node.__resource);
      } else {
        return hasResource.call(this, node);
      }
    }

  }
  true
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 The Regents of the University of Michigan.
// All Rights Reserved. Licensed according to the terms of the Revised
// BSD License. See LICENSE.txt for details.

const expect = require("chai").expect;

const randomString = require("../../lib/random-string");

describe("the randomString() function", () => {
  it("returns a six-character string by default", () => {
    expect(randomString()).to.have.lengthOf(6);
  });

  it("returns different strings", () => {
    let results = new Set();

    for (let i = 0; i < 100; i += 1)
      results.add(randomString());

    expect(results.size).to.be.above(1);
  });

  it("returns an eight-character string when given XXXXXXXX", () => {
    expect(randomString("XXXXXXXX")).to.have.lengthOf(8);
  });

  it("returns a four-character string when given XXXX", () => {
    expect(randomString("XXXX")).to.have.lengthOf(4);
  });

  it("leaves the prefix alone when given hey-XXXX", () => {
    expect(randomString("hey-XXXX")).to.match(/^hey-....$/);
  });

  it("returns the input when given no Xs", () => {
    expect(randomString("sup-friend")).to.equal("sup-friend");
  });

  it("returns the ingut when given XXX", () => {
    expect(randomString("XXX")).to.equal("XXX");
  });

  it("leaves suffixes alone", () => {
    expect(randomString("XXXX-what")).to.match(/^....-what$/);
  });

  it("can fill in multiple blank areas", () => {
    expect(randomString("XXXX-XXXX")).to.match(/^....-....$/);
    expect(randomString("XXXX-XXXX").slice(0, 4)).not.to.equal("XXXX");
    expect(randomString("XXXX-XXXX").slice(5)).not.to.equal("XXXX");
  });

  it("replaces YYYY with the year", () => {
    expect(randomString("yyyYYYYyyy")).to.equal(
      "yyy" + (new Date()).getFullYear() + "yyy");
  });
});
<commit_msg>:white_check_mark: randomString() ignores YYYY given XXXX<commit_after>// Copyright (c) 2017 The Regents of the University of Michigan.
// All Rights Reserved. Licensed according to the terms of the Revised
// BSD License. See LICENSE.txt for details.

const expect = require("chai").expect;

const randomString = require("../../lib/random-string");

describe("the randomString() function", () => {
  it("returns a six-character string by default", () => {
    expect(randomString()).to.have.lengthOf(6);
  });

  it("returns different strings", () => {
    let results = new Set();

    for (let i = 0; i < 100; i += 1)
      results.add(randomString());

    expect(results.size).to.be.above(1);
  });

  it("returns an eight-character string when given XXXXXXXX", () => {
    expect(randomString("XXXXXXXX")).to.have.lengthOf(8);
  });

  it("returns a four-character string when given XXXX", () => {
    expect(randomString("XXXX")).to.have.lengthOf(4);
  });

  it("leaves the prefix alone when given hey-XXXX", () => {
    expect(randomString("hey-XXXX")).to.match(/^hey-....$/);
  });

  it("returns the input when given no Xs", () => {
    expect(randomString("sup-friend")).to.equal("sup-friend");
  });

  it("returns the ingut when given XXX", () => {
    expect(randomString("XXX")).to.equal("XXX");
  });

  it("leaves suffixes alone", () => {
    expect(randomString("XXXX-what")).to.match(/^....-what$/);
  });

  it("can fill in multiple blank areas", () => {
    expect(randomString("XXXX-XXXX")).to.match(/^....-....$/);
    expect(randomString("XXXX-XXXX").slice(0, 4)).not.to.equal("XXXX");
    expect(randomString("XXXX-XXXX").slice(5)).not.to.equal("XXXX");
  });

  it("replaces YYYY with the year", () => {
    expect(randomString("yyyYYYYyyy")).to.equal(
      "yyy" + (new Date()).getFullYear() + "yyy");
  });

  it("ignores YYYY in the presence of XXXX", () => {
    expect(randomString("YYYYXXXX")).to.match(/^YYYY/);
  });
});
<|endoftext|>


<commit_before>'use strict'

const distanceTransform = require('thirdparty_distancetransform')

/**
 * Plan room structures
 */

const LAYOUT_CORE_BUFFER = 4 // CEIL(radius)
// Keep spawn center for first room.
const LAYOUT_CORE = [
  [STRUCTURE_TOWER, STRUCTURE_TOWER, STRUCTURE_TOWER],
  [
    STRUCTURE_TOWER,
    STRUCTURE_LOADER,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD
  ],
  [
    STRUCTURE_TOWER,
    STRUCTURE_TOWER,
    STRUCTURE_LINK,
    STRUCTURE_TERMINAL,
    STRUCTURE_STORAGE,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD
  ],
  [
    STRUCTURE_ROAD,
    STRUCTURE_OBSERVER,
    STRUCTURE_NUKER,
    STRUCTURE_CRANE,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_SPAWN,
    STRUCTURE_ROAD
  ],
  [
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_POWER_SPAWN,
    STRUCTURE_ROAD,
    STRUCTURE_SPAWN,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD
  ],
  [
    null,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD
  ],
  [
    null,
    STRUCTURE_ROAD,
    STRUCTURE_SPAWN,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_LAB,
    STRUCTURE_ROAD,
    STRUCTURE_LAB,
    STRUCTURE_ROAD
  ],
  [
    null,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_LAB,
    STRUCTURE_ROAD,
    STRUCTURE_LAB,
    STRUCTURE_ROAD,
    STRUCTURE_LAB
  ],
  [
    null,
    null,
    null,
    null,
    STRUCTURE_LAB,
    STRUCTURE_LAB,
    STRUCTURE_LAB,
    STRUCTURE_LAB,
    STRUCTURE_LAB
  ]
]

const LAYOUT_FLOWER_BUFFER = 3 // CEIL(radius)
const LAYOUT_FLOWER = [
  [
    null,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    null,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    null
  ],
  [
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION
  ],
  [
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_LINK,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION
  ],
  [
    null,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_CONTAINER,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD
  ],
  [
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION
  ],
  [
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION
  ],
  [
    null,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    null,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    null
  ]
]

class CityLayout extends kernel.process {
  constructor (...args) {
    super(...args)
    this.priority = PRIORITIES_CONSTRUCTION
  }

  getDescriptor () {
    return this.data.room
  }

  main () {
    if (!Game.rooms[this.data.room]) {
      return this.suicide()
    }

    this.room = Game.rooms[this.data.room]
    const layout = this.room.getLayout()
    if (layout.isPlanned()) {
      Logger.log(`Room ${this.data.room} already planned`, LOG_ERROR)
      layout.visualize()
      return this.suicide()
    }

    // Check for existing spawns in case this is the first room.
    const spawns = this.room.find(FIND_MY_SPAWNS)
    this.corePos = spawns.length > 0 ? spawns[0].pos : false

    // Iterate through plans, trying each one multiple times before moving on.
    const plans = [
      'nearController',
      'randomCore',
      'randomAll'
    ]
    if (!this.data.plan) {
      this.data.plan = 0
    }
    if (!this.data.attempts) {
      this.data.attempts = 0
    }
    if (this.data.attempts > 15) {
      this.data.plan++
      this.data.attempts = 0
    }
    if (this.data.plan >= plans.length) {
      // Room probably can't support things
      this.suicide()
    }

    // Actually run layout planning attempt.
    this.data.attempts++
    this[plans[this.data.plan]]()
  }

  /**
   * Try to place the core structures as close to the controller as possible, with the flower structures as close to
   * the core structures as can be managed.
   */
  nearController () {
    let baseMatrix = this.getBaseMatrix()
    let dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get core structures */
    const corePosition = this.corePos ? this.corePos : this.getPositionFor(dt, LAYOUT_CORE_BUFFER, function (a, b) {
      return a.getRangeTo(Game.rooms[a.roomName].controller) - b.getRangeTo(Game.rooms[b.roomName].controller)
    })
    if (!corePosition) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, corePosition, LAYOUT_CORE_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower1 structures */
    const flower1Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER, function (a, b) {
      return a.getRangeTo(corePosition) - b.getRangeTo(corePosition)
    })
    if (!flower1Position) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, flower1Position, LAYOUT_FLOWER_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower2 structures */
    const flower2Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER, function (a, b) {
      return a.getRangeTo(corePosition) - b.getRangeTo(corePosition)
    })
    if (!flower2Position) {
      return false
    }

    return this.planLayout(corePosition, flower1Position, flower2Position)
  }

  /**
   * Place the core structures anywhere they'll fit, with the flower structures as close to the core structures as can
   * be managed.
   */
  randomCore () {
    let baseMatrix = this.getBaseMatrix()
    let dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get core structures */
    const corePosition = this.corePos ? this.corePos : this.getPositionFor(dt, LAYOUT_CORE_BUFFER)
    if (!corePosition) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, corePosition, LAYOUT_CORE_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower1 structures */
    const flower1Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER, function (a, b) {
      return a.getRangeTo(corePosition) - b.getRangeTo(corePosition)
    })
    if (!flower1Position) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, flower1Position, LAYOUT_FLOWER_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower2 structures */
    const flower2Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER, function (a, b) {
      return a.getRangeTo(corePosition) - b.getRangeTo(corePosition)
    })
    if (!flower2Position) {
      return false
    }

    return this.planLayout(corePosition, flower1Position, flower2Position)
  }

  /**
   * Place core structures and flower structures anywhere they will fit.
   */
  randomAll () {
    let baseMatrix = this.getBaseMatrix()
    let dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get core structures */
    const corePosition = this.corePos ? this.corePos : this.getPositionFor(dt, LAYOUT_CORE_BUFFER)
    if (!corePosition) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, corePosition, LAYOUT_CORE_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower1 structures */
    const flower1Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER)
    if (!flower1Position) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, flower1Position, LAYOUT_FLOWER_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower2 structures */
    const flower2Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER)
    if (!flower2Position) {
      return false
    }

    return this.planLayout(corePosition, flower1Position, flower2Position)
  }

  /**
   * Convert the positions and templates into an actual RoomLayout and save it.
   */
  planLayout (corePos, flower1Pos, flower2Pos) {
    const layout = Room.getLayout(this.data.room)

    const coreAdjusted = new RoomPosition(corePos.x - LAYOUT_CORE_BUFFER, corePos.y - LAYOUT_CORE_BUFFER, this.data.room)
    this.planStructureMatrix(layout, coreAdjusted, LAYOUT_CORE, (2 * LAYOUT_CORE_BUFFER) + 1)

    const flower1Adjusted = new RoomPosition(flower1Pos.x - LAYOUT_FLOWER_BUFFER, flower1Pos.y - LAYOUT_FLOWER_BUFFER, this.data.room)
    this.planStructureMatrix(layout, flower1Adjusted, LAYOUT_FLOWER, (2 * LAYOUT_FLOWER_BUFFER) + 1)

    const flower2Adjusted = new RoomPosition(flower2Pos.x - LAYOUT_FLOWER_BUFFER, flower2Pos.y - LAYOUT_FLOWER_BUFFER, this.data.room)
    this.planStructureMatrix(layout, flower2Adjusted, LAYOUT_FLOWER, (2 * LAYOUT_FLOWER_BUFFER) + 1)

    layout.save()
    Logger.log(`Room planning for room ${this.data.room} has successfully completed`)
    return this.suicide()
  }

  /**
   * Adds a predefined template of structures to a layout starting from the specific position.
   */
  planStructureMatrix (layout, leftCorner, matrix, size = false) {
    if (!size) {
      size = matrix.length
    }
    let row,
      column
    for (row = 0; row < size; row++) {
      if (!matrix[row]) {
        continue
      }
      for (column = 0; column < size; column++) {
        if (!matrix[row][column]) {
          continue
        }
        const structure = matrix[row][column]
        layout.planStructureAt(structure, leftCorner.x + column, leftCorner.y + row)
        if (structure !== STRUCTURE_ROAD && !OBSTACLE_OBJECT_TYPES[structure]) {
          layout.planStructureAt(STRUCTURE_ROAD, leftCorner.x + column, leftCorner.y + row)
        }
      }
    }
  }

  /**
   * Return positions with the required amount of room around them. A random result from all possible matches is
   * returned, or if a sort function is provided it will be run *after* a shuffle is applied (so positions with the same
   * sort value will be in different places each run, allowing the same planning pattern to run multiple times with
   * differing results).
   */
  getPositionFor (dt, buffer, sort = false) {
    let positions = []
    let x,
      y
    for (x = 0; x < 50; x++) {
      for (y = 0; y < 50; y++) {
        // gt, not gte, because the DT has a minimum of 1 instead of 0
        if (dt.get(x, y) > buffer) {
          const pos = this.room.getPositionAt(x, y)
          positions.push(pos)
        }
      }
    }

    positions = _.shuffle(positions)
    if (positions.length < 1) {
      return false
    }
    if (sort) {
      positions.sort(sort)
    }
    return positions[0]
  }

  addToMatrix (matrix, center, radius) {
    let xLeft = center.x - radius
    let yTop = center.y - radius

    if (xLeft < 0) {
      xLeft = 0
    }
    if (yTop < 0) {
      yTop = 0
    }

    let xRight = center.x + radius
    let yBottom = center.y + radius
    if (xRight > 49) {
      xRight = 49
    }
    if (yBottom > 49) {
      yBottom = 49
    }

    let x,
      y
    for (x = xLeft; x <= xRight; x++) {
      for (y = yTop; y <= yBottom; y++) {
        matrix.set(x, y, 0)
      }
    }
    return matrix
  }

  getBaseMatrix () {
    const costMatrix = new PathFinder.CostMatrix()
    let x,
      y
    for (x = 1; x < 49; ++x) {
      for (y = 1; y < 49; ++y) {
        const pos = new RoomPosition(x, y, this.data.room)
        if (pos.inFrontOfExit()) {
          continue
        }
        if (pos.getTerrainAt() !== 'wall') {
          costMatrix.set(x, y, 1)
        }
      }
    }
    if (this.room.controller) {
      const poses = this.room.controller.pos.getAdjacent()
      let pos
      for (pos of poses) {
        costMatrix.set(pos.x, pos.y, 0)
      }
    }
    const sources = this.room.find(FIND_SOURCES)
    let source,
      pos
    for (source of sources) {
      const poses = source.pos.getAdjacent()
      for (pos of poses) {
        costMatrix.set(pos.x, pos.y, 0)
      }
    }
    const minerals = this.room.find(FIND_MINERALS)
    let mineral
    for (mineral of minerals) {
      const poses = mineral.pos.getAdjacent()
      for (pos of poses) {
        costMatrix.set(pos.x, pos.y, 0)
      }
    }

    return costMatrix
  }

  displayMatrix (matrix) {
    const visual = new RoomVisual(this.data.room)
    let x,
      y
    for (x = 0; x < 50; ++x) {
      for (y = 0; y < 50; ++y) {
        const value = matrix.get(x, y)
        if (value > 0) {
          let fontsize,
            yOffset
          // vis.circle(x, y, {radius:costMatrix.get(x, y)/max/2, fill:color})
          if (value >= 100) {
            fontsize = '0.5'
            yOffset = 0.19
          } else if (value >= 10) {
            fontsize = '0.7'
            yOffset = 0.22
          } else {
            fontsize = '0.8'
            yOffset = 0.25
          }
          visual.text(value, x - 0.05, +y + yOffset, {
            color: '#000000',
            stroke: '#FFFFFF',
            strokeWidth: 0.05,
            font: fontsize
          })
        }
      }
    }
  }
}

module.exports = CityLayout
<commit_msg>extend range around controller where no structures are planned<commit_after>'use strict'

const distanceTransform = require('thirdparty_distancetransform')

/**
 * Plan room structures
 */

const LAYOUT_CORE_BUFFER = 4 // CEIL(radius)
// Keep spawn center for first room.
const LAYOUT_CORE = [
  [STRUCTURE_TOWER, STRUCTURE_TOWER, STRUCTURE_TOWER],
  [
    STRUCTURE_TOWER,
    STRUCTURE_LOADER,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD
  ],
  [
    STRUCTURE_TOWER,
    STRUCTURE_TOWER,
    STRUCTURE_LINK,
    STRUCTURE_TERMINAL,
    STRUCTURE_STORAGE,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD
  ],
  [
    STRUCTURE_ROAD,
    STRUCTURE_OBSERVER,
    STRUCTURE_NUKER,
    STRUCTURE_CRANE,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_SPAWN,
    STRUCTURE_ROAD
  ],
  [
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_POWER_SPAWN,
    STRUCTURE_ROAD,
    STRUCTURE_SPAWN,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD
  ],
  [
    null,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD
  ],
  [
    null,
    STRUCTURE_ROAD,
    STRUCTURE_SPAWN,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_LAB,
    STRUCTURE_ROAD,
    STRUCTURE_LAB,
    STRUCTURE_ROAD
  ],
  [
    null,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD,
    STRUCTURE_LAB,
    STRUCTURE_ROAD,
    STRUCTURE_LAB,
    STRUCTURE_ROAD,
    STRUCTURE_LAB
  ],
  [
    null,
    null,
    null,
    null,
    STRUCTURE_LAB,
    STRUCTURE_LAB,
    STRUCTURE_LAB,
    STRUCTURE_LAB,
    STRUCTURE_LAB
  ]
]

const LAYOUT_FLOWER_BUFFER = 3 // CEIL(radius)
const LAYOUT_FLOWER = [
  [
    null,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    null,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    null
  ],
  [
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION
  ],
  [
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_LINK,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION
  ],
  [
    null,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_CONTAINER,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_ROAD
  ],
  [
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION
  ],
  [
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    STRUCTURE_ROAD,
    STRUCTURE_EXTENSION
  ],
  [
    null,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    null,
    STRUCTURE_EXTENSION,
    STRUCTURE_EXTENSION,
    null
  ]
]

class CityLayout extends kernel.process {
  constructor (...args) {
    super(...args)
    this.priority = PRIORITIES_CONSTRUCTION
  }

  getDescriptor () {
    return this.data.room
  }

  main () {
    if (!Game.rooms[this.data.room]) {
      return this.suicide()
    }

    this.room = Game.rooms[this.data.room]
    const layout = this.room.getLayout()
    if (layout.isPlanned()) {
      Logger.log(`Room ${this.data.room} already planned`, LOG_ERROR)
      layout.visualize()
      return this.suicide()
    }

    // Check for existing spawns in case this is the first room.
    const spawns = this.room.find(FIND_MY_SPAWNS)
    this.corePos = spawns.length > 0 ? spawns[0].pos : false

    // Iterate through plans, trying each one multiple times before moving on.
    const plans = [
      'nearController',
      'randomCore',
      'randomAll'
    ]
    if (!this.data.plan) {
      this.data.plan = 0
    }
    if (!this.data.attempts) {
      this.data.attempts = 0
    }
    if (this.data.attempts > 15) {
      this.data.plan++
      this.data.attempts = 0
    }
    if (this.data.plan >= plans.length) {
      // Room probably can't support things
      this.suicide()
    }

    // Actually run layout planning attempt.
    this.data.attempts++
    this[plans[this.data.plan]]()
  }

  /**
   * Try to place the core structures as close to the controller as possible, with the flower structures as close to
   * the core structures as can be managed.
   */
  nearController () {
    let baseMatrix = this.getBaseMatrix()
    let dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get core structures */
    const corePosition = this.corePos ? this.corePos : this.getPositionFor(dt, LAYOUT_CORE_BUFFER, function (a, b) {
      return a.getRangeTo(Game.rooms[a.roomName].controller) - b.getRangeTo(Game.rooms[b.roomName].controller)
    })
    if (!corePosition) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, corePosition, LAYOUT_CORE_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower1 structures */
    const flower1Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER, function (a, b) {
      return a.getRangeTo(corePosition) - b.getRangeTo(corePosition)
    })
    if (!flower1Position) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, flower1Position, LAYOUT_FLOWER_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower2 structures */
    const flower2Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER, function (a, b) {
      return a.getRangeTo(corePosition) - b.getRangeTo(corePosition)
    })
    if (!flower2Position) {
      return false
    }

    return this.planLayout(corePosition, flower1Position, flower2Position)
  }

  /**
   * Place the core structures anywhere they'll fit, with the flower structures as close to the core structures as can
   * be managed.
   */
  randomCore () {
    let baseMatrix = this.getBaseMatrix()
    let dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get core structures */
    const corePosition = this.corePos ? this.corePos : this.getPositionFor(dt, LAYOUT_CORE_BUFFER)
    if (!corePosition) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, corePosition, LAYOUT_CORE_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower1 structures */
    const flower1Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER, function (a, b) {
      return a.getRangeTo(corePosition) - b.getRangeTo(corePosition)
    })
    if (!flower1Position) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, flower1Position, LAYOUT_FLOWER_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower2 structures */
    const flower2Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER, function (a, b) {
      return a.getRangeTo(corePosition) - b.getRangeTo(corePosition)
    })
    if (!flower2Position) {
      return false
    }

    return this.planLayout(corePosition, flower1Position, flower2Position)
  }

  /**
   * Place core structures and flower structures anywhere they will fit.
   */
  randomAll () {
    let baseMatrix = this.getBaseMatrix()
    let dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get core structures */
    const corePosition = this.corePos ? this.corePos : this.getPositionFor(dt, LAYOUT_CORE_BUFFER)
    if (!corePosition) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, corePosition, LAYOUT_CORE_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower1 structures */
    const flower1Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER)
    if (!flower1Position) {
      return false
    }
    baseMatrix = this.addToMatrix(baseMatrix, flower1Position, LAYOUT_FLOWER_BUFFER)
    dt = distanceTransform.distanceTransform(baseMatrix)

    /* Get flower2 structures */
    const flower2Position = this.getPositionFor(dt, LAYOUT_FLOWER_BUFFER)
    if (!flower2Position) {
      return false
    }

    return this.planLayout(corePosition, flower1Position, flower2Position)
  }

  /**
   * Convert the positions and templates into an actual RoomLayout and save it.
   */
  planLayout (corePos, flower1Pos, flower2Pos) {
    const layout = Room.getLayout(this.data.room)

    const coreAdjusted = new RoomPosition(corePos.x - LAYOUT_CORE_BUFFER, corePos.y - LAYOUT_CORE_BUFFER, this.data.room)
    this.planStructureMatrix(layout, coreAdjusted, LAYOUT_CORE, (2 * LAYOUT_CORE_BUFFER) + 1)

    const flower1Adjusted = new RoomPosition(flower1Pos.x - LAYOUT_FLOWER_BUFFER, flower1Pos.y - LAYOUT_FLOWER_BUFFER, this.data.room)
    this.planStructureMatrix(layout, flower1Adjusted, LAYOUT_FLOWER, (2 * LAYOUT_FLOWER_BUFFER) + 1)

    const flower2Adjusted = new RoomPosition(flower2Pos.x - LAYOUT_FLOWER_BUFFER, flower2Pos.y - LAYOUT_FLOWER_BUFFER, this.data.room)
    this.planStructureMatrix(layout, flower2Adjusted, LAYOUT_FLOWER, (2 * LAYOUT_FLOWER_BUFFER) + 1)

    layout.save()
    Logger.log(`Room planning for room ${this.data.room} has successfully completed`)
    return this.suicide()
  }

  /**
   * Adds a predefined template of structures to a layout starting from the specific position.
   */
  planStructureMatrix (layout, leftCorner, matrix, size = false) {
    if (!size) {
      size = matrix.length
    }
    let row,
      column
    for (row = 0; row < size; row++) {
      if (!matrix[row]) {
        continue
      }
      for (column = 0; column < size; column++) {
        if (!matrix[row][column]) {
          continue
        }
        const structure = matrix[row][column]
        layout.planStructureAt(structure, leftCorner.x + column, leftCorner.y + row)
        if (structure !== STRUCTURE_ROAD && !OBSTACLE_OBJECT_TYPES[structure]) {
          layout.planStructureAt(STRUCTURE_ROAD, leftCorner.x + column, leftCorner.y + row)
        }
      }
    }
  }

  /**
   * Return positions with the required amount of room around them. A random result from all possible matches is
   * returned, or if a sort function is provided it will be run *after* a shuffle is applied (so positions with the same
   * sort value will be in different places each run, allowing the same planning pattern to run multiple times with
   * differing results).
   */
  getPositionFor (dt, buffer, sort = false) {
    let positions = []
    let x,
      y
    for (x = 0; x < 50; x++) {
      for (y = 0; y < 50; y++) {
        // gt, not gte, because the DT has a minimum of 1 instead of 0
        if (dt.get(x, y) > buffer) {
          const pos = this.room.getPositionAt(x, y)
          positions.push(pos)
        }
      }
    }

    positions = _.shuffle(positions)
    if (positions.length < 1) {
      return false
    }
    if (sort) {
      positions.sort(sort)
    }
    return positions[0]
  }

  addToMatrix (matrix, center, radius) {
    let xLeft = center.x - radius
    let yTop = center.y - radius

    if (xLeft < 0) {
      xLeft = 0
    }
    if (yTop < 0) {
      yTop = 0
    }

    let xRight = center.x + radius
    let yBottom = center.y + radius
    if (xRight > 49) {
      xRight = 49
    }
    if (yBottom > 49) {
      yBottom = 49
    }

    let x,
      y
    for (x = xLeft; x <= xRight; x++) {
      for (y = yTop; y <= yBottom; y++) {
        matrix.set(x, y, 0)
      }
    }
    return matrix
  }

  getBaseMatrix () {
    const costMatrix = new PathFinder.CostMatrix()
    let x,
      y
    for (x = 1; x < 49; ++x) {
      for (y = 1; y < 49; ++y) {
        const pos = new RoomPosition(x, y, this.data.room)
        if (pos.inFrontOfExit()) {
          continue
        }
        if (pos.getTerrainAt() !== 'wall') {
          costMatrix.set(x, y, 1)
        }
      }
    }
    if (this.room.controller) {
      const poses = this.room.controller.pos.getAdjacentInRange(3)
      let pos
      for (pos of poses) {
        costMatrix.set(pos.x, pos.y, 0)
      }
    }
    const sources = this.room.find(FIND_SOURCES)
    let source,
      pos
    for (source of sources) {
      const poses = source.pos.getAdjacent()
      for (pos of poses) {
        costMatrix.set(pos.x, pos.y, 0)
      }
    }
    const minerals = this.room.find(FIND_MINERALS)
    let mineral
    for (mineral of minerals) {
      const poses = mineral.pos.getAdjacent()
      for (pos of poses) {
        costMatrix.set(pos.x, pos.y, 0)
      }
    }

    return costMatrix
  }

  displayMatrix (matrix) {
    const visual = new RoomVisual(this.data.room)
    let x,
      y
    for (x = 0; x < 50; ++x) {
      for (y = 0; y < 50; ++y) {
        const value = matrix.get(x, y)
        if (value > 0) {
          let fontsize,
            yOffset
          // vis.circle(x, y, {radius:costMatrix.get(x, y)/max/2, fill:color})
          if (value >= 100) {
            fontsize = '0.5'
            yOffset = 0.19
          } else if (value >= 10) {
            fontsize = '0.7'
            yOffset = 0.22
          } else {
            fontsize = '0.8'
            yOffset = 0.25
          }
          visual.text(value, x - 0.05, +y + yOffset, {
            color: '#000000',
            stroke: '#FFFFFF',
            strokeWidth: 0.05,
            font: fontsize
          })
        }
      }
    }
  }
}

module.exports = CityLayout
<|endoftext|>


<commit_before>describe("Transaction", function() {
  var transactions;

  beforeEach(function() {
    transactions = CoinPocketApp.Models.transactions;
    transactions.length = 0;
    transactions.unbind("transactions.updated");
  });

  describe("transaction.confirmations", function() {

    describe("without a block height", function() {
      var transaction;

      beforeEach(function() {
        transaction = new CoinPocketApp.Models.Transaction('address', {
          tx_index: 0,
          time: 1389124087,
          out: [],
          inputs: []
        });
      });

      it("returns 0 confirmations at any block height", function() {
        expect(transaction.confirmations(280000)).toEqual(0);
        expect(transaction.confirmations(100)).toEqual(0);
        expect(transaction.confirmations(99999999)).toEqual(0);
      });

    });

    describe("at a block height of 280000", function() {
      var transaction;

      beforeEach(function() {
        transaction = new CoinPocketApp.Models.Transaction('address', {
          tx_index: 0,
          time: 1389124087,
          block_height: 280000,
          out: [],
          inputs: []
        });
      });

      it("returns 0 at 279998 blocks", function() {
        expect(transaction.confirmations(279998)).toEqual(0);
      });

      it("returns 0 at 279999 blocks", function() {
        expect(transaction.confirmations(279999)).toEqual(0);
      });

      it("returns 1 at 280000 blocks", function() {
        expect(transaction.confirmations(280000)).toEqual(1);
      });

      it("returns 2 at 280001 blocks", function() {
        expect(transaction.confirmations(280001)).toEqual(2);
      });
    });

  });

  describe("transactions#fetchRecent", function() {

    describe("with no transactions", function() {
      var result;

      beforeEach(function(done) {
        result = null;
        transactions.fetchRecent('1M3p9Gfhn9vrPgjYLZEcFSnxSM6WuCVm2Y-empty', function(txs) {
          result = txs;
          done()
        });
      });

      it("is empty", function() {
        expect(result).toEqual([]);
      });

    });

    describe("with 2 credits and 1 debit", function() {
      var result;

      beforeEach(function(done) {
        result = null;
        transactions.fetchRecent('1KCVyR5Ucq3ExNhVFwbTWkeviU1ZpWpSoH-2credit1debit', function(txs) {
          result = txs;
          done()
        });
      });

      it("has 3 transactions", function() {
        expect(result.length).toEqual(3);
      });

      it("is ordered by most recent first", function() {
        expect(result[0].time).toEqual(1389124087000);
        expect(result[1].time).toEqual(1389067925000);
        expect(result[2].time).toEqual(1388545895000);
      });

      it("reports each transactions amount delta", function() {
        expect(result[0].amountDelta).toEqual(1000000000);
        expect(result[1].amountDelta).toEqual(-1000000);
        expect(result[2].amountDelta).toEqual(1000000);
      });

      it("reports each transactions block height", function() {
        expect(result[0].blockHeight).toEqual(279165);
        expect(result[1].blockHeight).toEqual(279054);
        expect(result[2].blockHeight).toEqual(278021);
      });

    });

  });

  describe("transactions#any", function() {

    it("has no transacitons", function() {
      expect(transactions.any()).toBe(false);
    });

    it("has transactions", function() {
      transactions.push({});
      expect(transactions.any()).toBe(true);
    });

  });

  describe("transactions.updated event", function() {
    var transactions = CoinPocketApp.Models.transactions;

    it("triggers when new transactions are fetched", function(done) {
      transactions.bind("transactions.updated", function(newTransactions) {
        done();
      });
      transactions.fetchRecent('1KCVyR5Ucq3ExNhVFwbTWkeviU1ZpWpSoH-2credit1debit');
    });

    it("does not trigger if there are transactions already", function(done) {
      transactions.push({});
      var self = this;
      transactions.bind("transactions.updated", function(newTransactions) {
        throw new Error('should not trigger transactions.updated event');
      });
      transactions.fetchRecent('1KCVyR5Ucq3ExNhVFwbTWkeviU1ZpWpSoH-2credit1debit', function() {
        done();
      });
    });

  });

});
<commit_msg>Remove spec that relies on localStorage<commit_after>describe("Transaction", function() {
  var transactions;

  beforeEach(function() {
    transactions = CoinPocketApp.Models.transactions;
    transactions.length = 0;
    transactions.unbind("transactions.updated");
  });

  describe("transaction.confirmations", function() {

    describe("without a block height", function() {
      var transaction;

      beforeEach(function() {
        transaction = new CoinPocketApp.Models.Transaction('address', {
          tx_index: 0,
          time: 1389124087,
          out: [],
          inputs: []
        });
      });

      it("returns 0 confirmations at any block height", function() {
        expect(transaction.confirmations(280000)).toEqual(0);
        expect(transaction.confirmations(100)).toEqual(0);
        expect(transaction.confirmations(99999999)).toEqual(0);
      });

    });

    describe("at a block height of 280000", function() {
      var transaction;

      beforeEach(function() {
        transaction = new CoinPocketApp.Models.Transaction('address', {
          tx_index: 0,
          time: 1389124087,
          block_height: 280000,
          out: [],
          inputs: []
        });
      });

      it("returns 0 at 279998 blocks", function() {
        expect(transaction.confirmations(279998)).toEqual(0);
      });

      it("returns 0 at 279999 blocks", function() {
        expect(transaction.confirmations(279999)).toEqual(0);
      });

      it("returns 1 at 280000 blocks", function() {
        expect(transaction.confirmations(280000)).toEqual(1);
      });

      it("returns 2 at 280001 blocks", function() {
        expect(transaction.confirmations(280001)).toEqual(2);
      });
    });

  });

  describe("transactions#fetchRecent", function() {

    describe("with no transactions", function() {
      var result;

      beforeEach(function(done) {
        result = null;
        transactions.fetchRecent('1M3p9Gfhn9vrPgjYLZEcFSnxSM6WuCVm2Y-empty', function(txs) {
          result = txs;
          done()
        });
      });

      it("is empty", function() {
        expect(result).toEqual([]);
      });

    });

    describe("with 2 credits and 1 debit", function() {
      var result;

      beforeEach(function(done) {
        result = null;
        transactions.fetchRecent('1KCVyR5Ucq3ExNhVFwbTWkeviU1ZpWpSoH-2credit1debit', function(txs) {
          result = txs;
          done()
        });
      });

      it("has 3 transactions", function() {
        expect(result.length).toEqual(3);
      });

      it("is ordered by most recent first", function() {
        expect(result[0].time).toEqual(1389124087000);
        expect(result[1].time).toEqual(1389067925000);
        expect(result[2].time).toEqual(1388545895000);
      });

      it("reports each transactions amount delta", function() {
        expect(result[0].amountDelta).toEqual(1000000000);
        expect(result[1].amountDelta).toEqual(-1000000);
        expect(result[2].amountDelta).toEqual(1000000);
      });

      it("reports each transactions block height", function() {
        expect(result[0].blockHeight).toEqual(279165);
        expect(result[1].blockHeight).toEqual(279054);
        expect(result[2].blockHeight).toEqual(278021);
      });

    });

  });

  describe("transactions.updated event", function() {
    var transactions = CoinPocketApp.Models.transactions;

    it("triggers when new transactions are fetched", function(done) {
      transactions.bind("transactions.updated", function(newTransactions) {
        done();
      });
      transactions.fetchRecent('1KCVyR5Ucq3ExNhVFwbTWkeviU1ZpWpSoH-2credit1debit');
    });

    it("does not trigger if there are transactions already", function(done) {
      transactions.push({});
      var self = this;
      transactions.bind("transactions.updated", function(newTransactions) {
        throw new Error('should not trigger transactions.updated event');
      });
      transactions.fetchRecent('1KCVyR5Ucq3ExNhVFwbTWkeviU1ZpWpSoH-2credit1debit', function() {
        done();
      });
    });

  });

});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import {Button, Icon, Flag} from 'semantic-ui-react';
import {navigateAction} from 'fluxible-router';
import qs from 'querystring';
import {getLanguageName} from '../../../common';
import {flagForLocale} from '../../../configs/locales';

class TranslationItem extends React.Component {
    directToTranslation(href, e) {
        e.preventDefault();
        this.context.executeAction(navigateAction, { url: href });
    }

    render() {
        let href = '';
        if (this.props.clickable) {
            // build the href!
            let routeName = 'deck';
            let navParams = Object.assign({ slug: this.props.slug || '_' }, this.props.selector);
            let queryParams = { language: this.props.language };

            href = ['', routeName, navParams.id, navParams.slug, navParams.stype, navParams.sid, navParams.spath].join('/');
            href += '?' + qs.stringify(queryParams);
        }

        let flagName = flagForLocale(this.props.language);
        let iconName = flagName ? flagName + ' flag': 'flag icon';
        let text = getLanguageName(this.props.language) + (this.props.primary ? ' (primary)' : '');

        let node = (this.props.clickable === true) ?

          <Button role="button" data-tooltip={text}
            onClick={this.directToTranslation.bind(this, href)}
            aria-label={text} aria-required
            tabIndex="0" attached basic >
              {iconName === 'flag icon' ? <Icon name='flag' /> : <Flag name={flagName} />}{text}
          </Button>
          :

                  {iconName === 'flag icon' ? <Icon name='flag' /> : <Flag name={flagName} />}{text}

        return (

              {node}

        );
    }
}

TranslationItem.contextTypes = {
    executeAction: PropTypes.func.isRequired
};

export default TranslationItem;
<commit_msg>[SWIK-2363_improvements_translationsUI] Added missing semicolon<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import {Button, Icon, Flag} from 'semantic-ui-react';
import {navigateAction} from 'fluxible-router';
import qs from 'querystring';
import {getLanguageName} from '../../../common';
import {flagForLocale} from '../../../configs/locales';

class TranslationItem extends React.Component {
    directToTranslation(href, e) {
        e.preventDefault();
        this.context.executeAction(navigateAction, { url: href });
    }

    render() {
        let href = '';
        if (this.props.clickable) {
            // build the href!
            let routeName = 'deck';
            let navParams = Object.assign({ slug: this.props.slug || '_' }, this.props.selector);
            let queryParams = { language: this.props.language };

            href = ['', routeName, navParams.id, navParams.slug, navParams.stype, navParams.sid, navParams.spath].join('/');
            href += '?' + qs.stringify(queryParams);
        }

        let flagName = flagForLocale(this.props.language);
        let iconName = flagName ? flagName + ' flag': 'flag icon';
        let text = getLanguageName(this.props.language) + (this.props.primary ? ' (primary)' : '');

        let node = (this.props.clickable === true) ?

          <Button role="button" data-tooltip={text}
            onClick={this.directToTranslation.bind(this, href)}
            aria-label={text} aria-required
            tabIndex="0" attached basic >
              {iconName === 'flag icon' ? <Icon name='flag' /> : <Flag name={flagName} />}{text}
          </Button>
          :

                  {iconName === 'flag icon' ? <Icon name='flag' /> : <Flag name={flagName} />}{text}

;

        return (

              {node}

        );
    }
}

TranslationItem.contextTypes = {
    executeAction: PropTypes.func.isRequired
};

export default TranslationItem;
<|endoftext|>


<commit_before>import React from 'react';
import { Microservices } from '../../../configs/microservices';
import { connectToStores } from 'fluxible-addons-react';
import {NavLink, navigateAction} from 'fluxible-router';
import UserProfileStore from '../../../stores/UserProfileStore';
import { timeSince } from '../../../common';
import updateUsergroup from '../../../actions/user/userprofile/updateUsergroup';
import saveUsergroup from '../../../actions/user/userprofile/saveUsergroup';

class UserGroupEdit extends React.Component {
    constructor(props){
        super(props);

        this.styles = {'backgroundColor': '#2185D0', 'color': 'white'};
    }

    componentWillReceiveProps(nextProps) {
        console.log('UserGroupEdit componentWillReceiveProps:', nextProps.UserProfileStore.saveUsergroupError, this.props.UserProfileStore.saveUsergroupError);
        if (nextProps.UserProfileStore.saveUsergroupError !== '' && this.props.UserProfileStore.saveUsergroupError === '') {
            swal({
                title: 'Error',
                text: 'Unknown error while saving.',
                type: 'error',
                confirmButtonText: 'Close',
                confirmButtonClass: 'negative ui button',
                allowEscapeKey: true,
                allowOutsideClick: true,
                buttonsStyling: false
            })
            .then(() => {
                return true;
            })
            .catch();
            return;
        }
    }

    componentDidMount() {
        $('#usergoup_edit_dropdown_usernames_remote')
            .dropdown({
                apiSettings: {
                    url: Microservices.user.uri + '/information/username/search/{query}'
                },
                saveRemoteData: false,
                action: (name, value, source) => {
                    console.log('dropdown select', name, value, source);

                    $('#usergoup_edit_dropdown_usernames_remote').dropdown('clear');
                    $('#usergoup_edit_dropdown_usernames_remote').dropdown('hide');

                    let group = this.getGroup(this.props.UserProfileStore.currentUsergroup.members);
                    if (group.members === undefined || group.members === null)
                        group.members = [];

                    // console.log('trying to add', name, 'to', group.members);
                    if (group.members.findIndex((member) => {
                        return member.username === name && member.userid === parseInt(value);
                    }) === -1 && name !== this.props.UserProfileStore.username) {
                        group.members.push({
                            username: name,
                            userid: parseInt(value),
                            joined: (new Date()).toISOString()
                        });
                    }

                    this.context.executeAction(updateUsergroup, group);

                    return true;
                }
            });
    }

    getGroup(members = undefined) {
        let group = {
            _id: this.props.UserProfileStore.currentUsergroup._id,
            name: this.refs.GroupName.value,
            description: this.refs.GroupDescription.value,
            members: members,
            timestamp: this.props.UserProfileStore.currentUsergroup.timestamp || (new Date()).toISOString(),
            creator: this.props.UserProfileStore.currentUsergroup.creator || this.props.UserProfileStore.userid
        };

        if (this.props.UserProfileStore.currentUsergroup._id)
            group.id = group._id;

        //TODO get members from list

        return group;
    }

    handleClickOnEditGroup(e) {
        e.preventDefault();
    }

    handleSave(e) {
        e.preventDefault();

        let group = this.getGroup(this.props.UserProfileStore.currentUsergroup.members);

        console.log('handleSave:', group);

        if (group.name === '') {
            swal({
                title: 'Error',
                text: 'At least you have to give the group a name.',
                type: 'error',
                confirmButtonText: 'Close',
                confirmButtonClass: 'negative ui button',
                allowEscapeKey: true,
                allowOutsideClick: true,
                buttonsStyling: false
            })
            .then(() => {
                return true;
            })
            .catch();
            return;
        }

        this.context.executeAction(saveUsergroup, group);
    }

    handleClickRemoveMember(member) {
        console.log('handleClickRemoveMember', member);

        let group = this.getGroup(this.props.UserProfileStore.currentUsergroup.members);

        group.members.pop(member);

        this.context.executeAction(updateUsergroup, group);
    }

    render() {
        const signUpLabelStyle = {width: '150px'};

        let userlist = [];
        let prefs = {
            name: this.props.UserProfileStore.currentUsergroup.name,
            desc: this.props.UserProfileStore.currentUsergroup.description
        };
        //change header and data depending on group should be created or edited
        let header = 'Create Group';
        if (this.props.UserProfileStore.currentUsergroup._id !== undefined) {
            header = 'Edit Group';
        }

        if (this.props.UserProfileStore.currentUsergroup.members !== undefined && this.props.UserProfileStore.currentUsergroup.members.length > 0) {
            this.props.UserProfileStore.currentUsergroup.members.forEach((member) => {
                let fct = () => {
                    this.handleClickRemoveMember(member);
                };
                userlist.push(
                  (

                              {member.username} ({member.userid})

Joined {timeSince((new Date(member.joined)))}  ago

                  )
                );
            });
        }

        return (

{header}

                        <form className="ui form">

                                    Group Name

                                <input type="text" placeholder="Name" id="usergroupedit_input_GroupName" name="GroupName" ref="GroupName" aria-labelledby="GroupName" aria-required="true" defaultValue={prefs.name}  />

                                Description
                                <textarea rows="4" aria-labelledby="GroupDescription" id="usergroupedit_input_GroupDescription" name="GroupDescription" ref="GroupDescription" defaultValue={prefs.desc} ></textarea>

                                Add user
                                <select className="ui search dropdown" aria-labelledby="AddUserGr" id="usergroupedit_input_AddUserGr" name="AddUserGr" ref="AddUserGr" id="usergoup_edit_dropdown_usernames_remote">
                                </select>

                        </form>

                            <button className="ui blue labeled submit icon button" onClick={this.handleSave.bind(this)} >
                                Save group
                            </button>

                        {(this.props.UserProfileStore.saveUsergroupIsLoading === true) ?

Loading

 : ''}

Members

                            {userlist}

        );
    }
}

UserGroupEdit.contextTypes = {
    executeAction: React.PropTypes.func.isRequired
};

UserGroupEdit = connectToStores(UserGroupEdit, [UserProfileStore], (context, props) => {
    return {
        UserProfileStore: context.getStore(UserProfileStore).getState()
    };
});

export default UserGroupEdit;
<commit_msg>[SWIK-741_deck_editing_rights] Adopted parsing of return of user-service when searching for a user in UserGroups component.<commit_after>import React from 'react';
import { Microservices } from '../../../configs/microservices';
import { connectToStores } from 'fluxible-addons-react';
import {NavLink, navigateAction} from 'fluxible-router';
import UserProfileStore from '../../../stores/UserProfileStore';
import { timeSince } from '../../../common';
import updateUsergroup from '../../../actions/user/userprofile/updateUsergroup';
import saveUsergroup from '../../../actions/user/userprofile/saveUsergroup';

class UserGroupEdit extends React.Component {
    constructor(props){
        super(props);

        this.styles = {'backgroundColor': '#2185D0', 'color': 'white'};
    }

    componentWillReceiveProps(nextProps) {
        console.log('UserGroupEdit componentWillReceiveProps:', nextProps.UserProfileStore.saveUsergroupError, this.props.UserProfileStore.saveUsergroupError);
        if (nextProps.UserProfileStore.saveUsergroupError !== '' && this.props.UserProfileStore.saveUsergroupError === '') {
            swal({
                title: 'Error',
                text: 'Unknown error while saving.',
                type: 'error',
                confirmButtonText: 'Close',
                confirmButtonClass: 'negative ui button',
                allowEscapeKey: true,
                allowOutsideClick: true,
                buttonsStyling: false
            })
            .then(() => {
                return true;
            })
            .catch();
            return;
        }
    }

    componentDidMount() {
        $('#usergoup_edit_dropdown_usernames_remote')
            .dropdown({
                apiSettings: {
                    url: Microservices.user.uri + '/information/username/search/{query}'
                },
                saveRemoteData: false,
                action: (name, value, source) => {
                    console.log('dropdown select', name, value, source);

                    $('#usergoup_edit_dropdown_usernames_remote').dropdown('clear');
                    $('#usergoup_edit_dropdown_usernames_remote').dropdown('hide');

                    let group = this.getGroup(this.props.UserProfileStore.currentUsergroup.members);
                    if (group.members === undefined || group.members === null)
                        group.members = [];

                    // console.log('trying to add', name, 'to', group.members);
                    if (group.members.findIndex((member) => {
                        return member.username === name && member.userid === parseInt(value);
                    }) === -1 && name !== this.props.UserProfileStore.username) {
                        let data = JSON.parse(decodeURIComponent(value));
                        group.members.push({
                            username: name,
                            userid: parseInt(data.userid),
                            joined: (new Date()).toISOString(),
                            picture: data.picture
                        });
                    }

                    this.context.executeAction(updateUsergroup, group);

                    return true;
                }
            });
    }

    getGroup(members = undefined) {
        let group = {
            _id: this.props.UserProfileStore.currentUsergroup._id,
            name: this.refs.GroupName.value,
            description: this.refs.GroupDescription.value,
            members: members,
            timestamp: this.props.UserProfileStore.currentUsergroup.timestamp || (new Date()).toISOString(),
            creator: this.props.UserProfileStore.currentUsergroup.creator || this.props.UserProfileStore.userid
        };

        if (this.props.UserProfileStore.currentUsergroup._id)
            group.id = group._id;

        //TODO get members from list

        return group;
    }

    handleClickOnEditGroup(e) {
        e.preventDefault();
    }

    handleSave(e) {
        e.preventDefault();

        let group = this.getGroup(this.props.UserProfileStore.currentUsergroup.members);

        console.log('handleSave:', group);

        if (group.name === '') {
            swal({
                title: 'Error',
                text: 'At least you have to give the group a name.',
                type: 'error',
                confirmButtonText: 'Close',
                confirmButtonClass: 'negative ui button',
                allowEscapeKey: true,
                allowOutsideClick: true,
                buttonsStyling: false
            })
            .then(() => {
                return true;
            })
            .catch();
            return;
        }

        this.context.executeAction(saveUsergroup, group);
    }

    handleClickRemoveMember(member) {
        console.log('handleClickRemoveMember', member);

        let group = this.getGroup(this.props.UserProfileStore.currentUsergroup.members);

        group.members.pop(member);

        this.context.executeAction(updateUsergroup, group);
    }

    render() {
        const signUpLabelStyle = {width: '150px'};

        let userlist = [];
        let prefs = {
            name: this.props.UserProfileStore.currentUsergroup.name,
            desc: this.props.UserProfileStore.currentUsergroup.description
        };
        //change header and data depending on group should be created or edited
        let header = 'Create Group';
        if (this.props.UserProfileStore.currentUsergroup._id !== undefined) {
            header = 'Edit Group';
        }

        if (this.props.UserProfileStore.currentUsergroup.members !== undefined && this.props.UserProfileStore.currentUsergroup.members.length > 0) {
            this.props.UserProfileStore.currentUsergroup.members.forEach((member) => {
                let fct = () => {
                    this.handleClickRemoveMember(member);
                };
                userlist.push(
                  (

                              {member.username} ({member.userid})

Joined {timeSince((new Date(member.joined)))}  ago

                  )
                );
            });
        }

        return (

{header}

                        <form className="ui form">

                                    Group Name

                                <input type="text" placeholder="Name" id="usergroupedit_input_GroupName" name="GroupName" ref="GroupName" aria-labelledby="GroupName" aria-required="true" defaultValue={prefs.name}  />

                                Description
                                <textarea rows="4" aria-labelledby="GroupDescription" id="usergroupedit_input_GroupDescription" name="GroupDescription" ref="GroupDescription" defaultValue={prefs.desc} ></textarea>

                                Add user
                                <select className="ui search dropdown" aria-labelledby="AddUserGr" id="usergroupedit_input_AddUserGr" name="AddUserGr" ref="AddUserGr" id="usergoup_edit_dropdown_usernames_remote">
                                </select>

                        </form>

                            <button className="ui blue labeled submit icon button" onClick={this.handleSave.bind(this)} >
                                Save group
                            </button>

                        {(this.props.UserProfileStore.saveUsergroupIsLoading === true) ?

Loading

 : ''}

Members

                            {userlist}

        );
    }
}

UserGroupEdit.contextTypes = {
    executeAction: React.PropTypes.func.isRequired
};

UserGroupEdit = connectToStores(UserGroupEdit, [UserProfileStore], (context, props) => {
    return {
        UserProfileStore: context.getStore(UserProfileStore).getState()
    };
});

export default UserGroupEdit;
<|endoftext|>