<commit_before>/*** MQTT Z-Way odule *******************************************
 Version: 1.0.0
 -----------------------------------------------------------------------------
 Author: David Uebelacker <david@uebelacker.ch>
 ******************************************************************************/

function ZWayMqttBridge(id, controller) {
    ZWayMqttBridge.super_.call(this, id, controller);
}

inherits(ZWayMqttBridge, AutomationModule);

_module = ZWayMqttBridge;

ZWayMqttBridge.prototype.init = function (config) {
    ZWayMqttBridge.super_.prototype.init.call(this, config);

    var self = this;

    this.reconnect = function () {
        self.connected = false;
        self.mqttBridge = null;
        console.log('Will try to reconnect to Mqtt Bridge in 10 seconds ...');
        setTimeout(function() {
            console.log('Will try to reconnect to Mqtt Bridge ...');
            self.connect();
        }, 10000);
    }

    this.connect = function () {
        console.log('Connecting to Mqtt Bridge ....');
        this.mqttBridge = new sockets.websocket('ws://192.168.0.62:8080');

        setTimeout(function () {
            if (self.mqttBridge && !self.connected) {
                console.log('Could not connect to Mqtt Bridge after 5 seconds!');
                self.reconnect();
            }
        }, 5000);

        this.mqttBridge.onopen = function () {
            console.log('Mqtt Bridge websocket connected!');
            self.connected = true;
        };

        this.mqttBridge.onmessage = function (ev) {
            console.log('got data:' + ev.data);
        };

        this.mqttBridge.onclose = function () {
            console.log('Mqtt Bridge websocket was closed!');
            self.reconnect();
        };

        this.mqttBridge.onerror = function (ev) {
            console.log('Mqtt Bridge websocket error: ' + ev.data);
            self.reconnect();
        };
    }

    this.deviceUpdate = function (device) {
        if (self.mqttBridge) {
            self.mqttBridge.send(JSON.stringify(device, null, 4));
        }
    };

    this.connect();
    this.controller.devices.on('change:metrics:level', self.deviceUpdate);
};

<commit_msg>devel<commit_after>/*** MQTT Z-Way odule *******************************************
 Version: 1.0.0
 -----------------------------------------------------------------------------
 Author: David Uebelacker <david@uebelacker.ch>
 ******************************************************************************/

function ZWayMqttBridge(id, controller) {
    ZWayMqttBridge.super_.call(this, id, controller);
}

inherits(ZWayMqttBridge, AutomationModule);

_module = ZWayMqttBridge;

ZWayMqttBridge.prototype.init = function (config) {
    ZWayMqttBridge.super_.prototype.init.call(this, config);

    var self = this;

    this.reconnect = function () {
        self.connected = false;
        if (self.mqttBridge) {
            self.mqttBridge.close()
            self.mqttBridge = null;
        }
        console.log('Will try to reconnect to Mqtt Bridge in 10 seconds ...');
        setTimeout(function () {
            console.log('Will try to reconnect to Mqtt Bridge ...');
            self.connect();
        }, 10000);
    }

    this.connect = function () {
        console.log('Connecting to Mqtt Bridge ....');
        this.mqttBridge = new sockets.websocket('ws://192.168.0.62:8080');

        setTimeout(function () {
            if (self.mqttBridge && !self.connected) {
                console.log('Could not connect to Mqtt Bridge after 5 seconds!');
                self.reconnect();
            }
        }, 5000);

        this.mqttBridge.onopen = function () {
            console.log('Mqtt Bridge websocket connected!');
            self.connected = true;
        };

        this.mqttBridge.onmessage = function (ev) {
            console.log('got data:' + ev.data);
        };

        this.mqttBridge.onclose = function () {
            console.log('Mqtt Bridge websocket was closed!');
            self.reconnect();
        };

        this.mqttBridge.onerror = function (ev) {
            console.log('Mqtt Bridge websocket error: ' + ev.data);
            self.reconnect();
        };
    }

    this.deviceUpdate = function (device) {
        if (self.mqttBridge) {
            self.mqttBridge.send(JSON.stringify(device, null, 4));
        }
    };

    this.connect();
    this.controller.devices.on('change:metrics:level', self.deviceUpdate);
};

<|endoftext|>


<commit_before>define([

  'Backbone',

  //Templates
  'text!templates/project-page/closeGameTemplate.html'
], function(

  Backbone,

  //Template
  closeGameTemplate
){
  var GameController = Backbone.View.extend({

    template: _.template(closeGameTemplate),

    initialize: function(){ },

    close: function(event){
      event.preventDefault();
      var task = this.selectedTask.get('id');
      socket.emit('close game', task);
    },

    render: function(){
      var estimations = this.selectedTask.get('estimated') || [];
      var time = this.selectedTask.get('time');

      if(!estimations[0]){
        estimations.push({
          player: 'No one estimated',
          card: '?'
        });
      }

      this.$el.html(this.template({estimations: estimations, time: time}));
    }

  });

  return GameController;
});
<commit_msg>Add result to game<commit_after>define([

  'Backbone',

  //Templates
  'text!templates/project-page/closeGameTemplate.html'
], function(

  Backbone,

  //Template
  closeGameTemplate
){
  var GameController = Backbone.View.extend({

    template: _.template(closeGameTemplate),

    close: function(event){
      event.preventDefault();
      var task = this.selectedTask.get('id');
      socket.emit('close game', task);
    },

    render: function(){
      var estimations = this.selectedTask.get('estimated') || [];
      var time        = this.selectedTask.get('time');
      var total       = _.countBy(estimations, function(estimation){
        return estimation.card;
      });

      var max    = _.max(total, function(point){return point;});

      var result = _.compact(_.map(total, function(count, point){
        return count === max ? point : false;
      })).pop();

      if(!estimations[0]){
        estimations.push({
          player: 'No one estimated',
          card: '?'
        });
      }

      this.$el.html(this.template({
        estimations: estimations,
        time: time,
        result: result
      }));

    }

  });

  return GameController;
});
<|endoftext|>


<commit_before>var page=$("#page_name").attr('data-page');

twttr.anywhere(function (T) {
	if(T.isConnected()){
		show_dropdown(T);
		$("#upload_file_btn").live("click", function(){
		var user_name=T.currentUser.data('screen_name');
		var d=new Date();
		d=d.toISOString();
		var tags=$("#upload_message").val().split(" ");
		var act_tags="", k=0;
		for(var j=0;j<tags.length;j++){
			if(tags[j].indexOf('#') == 0){
				act_tags=act_tags+" "+tags[j];
				act_tags=act_tags.replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '');
				k++;
			}
		}
		act_tags=act_tags.substring(1,act_tags.length);
		var uid=$.couch.newUUID();
		uid="mytweet_"+uid;
		var doc={
			"_id": uid,
			"uploaded_by": user_name,
			"gravatar_url": "http://api.twitter.com/1/users/profile_image/"+user_name,
			"uploader_msg": $("#upload_message").val(),
			"created_at": d,
			"tags": act_tags.split(" ")
		};
		$.couch.db($db).saveDoc(doc,{
			success: function(data){
				var id=data.id;
				var stat=data.ok;
				if(stat){
					$("#uploadModal").modal('hide');
				}
			},
			error: function(data){
				console.log(data);
			}
		});
		return false;
	});
	}
	else{
		$("#uploadModal").on("shown",function(){
			$("#upload_file_btn").live("click", function(){
				if(T.isConnected()){

				}else{
					$("#uploadModal").modal('hide');
				$("#messages").html("
×You need to be logged in!
").hide().fadeIn(500);
				}
			});
		});
		$("#peepin").html('Login »').show();
	}
  $("#login").live("click",function(){
  	if(T.isConnected()){
  		show_dropdown(T);
  	}
  	else{
  		T.signIn();
  	}
  });
  $("#logout").live("click",function(){
  	twttr.anywhere.signOut();
  	window.location="index.html";
  });

});

function show_dropdown(T){
		var user=T.currentUser;
		var p=user.data('profile_image_url');
		var a=p.split('_');
		p=a[0]+"_"+a[1]+"_"+"mini.jpeg";
		$("#peepin").html('
 '+user.data("screen_name")+'

Home

Discussions

Files

Log out!

').hide().fadeIn('fast');
		if(page==="index.html"){
			$("#main-menu").hide();
			$("#tweet_div").html('Tweet »').hide().fadeIn(500);
		}
		if(page==="discussion.html"){
			$("#main-menu").hide();
			$("#discuss_btn_div").html('Discuss »').hide().fadeIn(500);
		}
}
<commit_msg>function that creates a new discussion on click of a discuss button<commit_after>var page=$("#page_name").attr('data-page');

twttr.anywhere(function (T) {
	if(T.isConnected()){
		show_dropdown(T);
		var user_name=T.currentUser.data('screen_name');
		$("#upload_file_btn").live("click", function(){
		var d=new Date();
		d=d.toISOString();
		var tags=$("#upload_message").val().split(" ");
		var act_tags="", k=0;
		for(var j=0;j<tags.length;j++){
			if(tags[j].indexOf('#') == 0){
				act_tags=act_tags+" "+tags[j];
				act_tags=act_tags.replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '');
				k++;
			}
		}
		act_tags=act_tags.substring(1,act_tags.length);
		var uid=$.couch.newUUID();
		uid="mytweet_"+uid;
		var doc={
			"_id": uid,
			"uploaded_by": user_name,
			"gravatar_url": "http://api.twitter.com/1/users/profile_image/"+user_name,
			"uploader_msg": $("#upload_message").val(),
			"created_at": d,
			"tags": act_tags.split(" ")
		};
		$.couch.db($db).saveDoc(doc,{
			success: function(data){
				var id=data.id;
				var stat=data.ok;
				if(stat){
					$("#uploadModal").modal('hide');
				}
			},
			error: function(data){
				console.log(data);
			}
		});
		return false;
	});

	$("#discuss_btn").live("click",function(){
		console.log(":)");
		var dt=$.trim($("#discuss_title").val());
		var dm=$.trim($("#discuss_message").val());
		if(dt=='' || dm==''){
			if(dt==''){
				$("#discuss_title").focus();
			}
			else if(dm==''){
				$("#discuss_message").focus();
			}
			else{

			}
		}
		else{
			var d=new Date();
			d=d.toISOString();
			var uid=$.couch.newUUID();
			uid="discuss_"+uid;
			var doc={
				"_id": uid,
				"title": dt,
				"author": user_name,
				"content": dm ,
				"date": d,
				"author_pic": "http://api.twitter.com/1/users/profile_image/"+user_name,
				"type": "discussion"
			};
			$.couch.db($db).saveDoc(doc,{
			success: function(data){
				var id=data.id;
				var stat=data.ok;
				if(stat){
					$("#discussModal").modal('hide');
				}
			},
			error: function(data){
				console.log(data);
			}
		});
		}
		return false;
	});

	}
	else{
		$("#uploadModal").on("shown",function(){
			$("#upload_file_btn").live("click", function(){
				if(T.isConnected()){

				}else{
					$("#uploadModal").modal('hide');
				$("#messages").html("
×You need to be logged in!
").hide().fadeIn(500);
				}
			});
		});
		$("#peepin").html('Login »').show();
	}
  $("#login").live("click",function(){
  	if(T.isConnected()){
  		show_dropdown(T);
  	}
  	else{
  		T.signIn();
  	}
  });
  $("#logout").live("click",function(){
  	twttr.anywhere.signOut();
  	window.location="index.html";
  });

});

function show_dropdown(T){
		var user=T.currentUser;
		var p=user.data('profile_image_url');
		var a=p.split('_');
		p=a[0]+"_"+a[1]+"_"+"mini.jpeg";
		$("#peepin").html('
 '+user.data("screen_name")+'

Home

Discussions

Files

Log out!

').hide().fadeIn('fast');
		if(page==="index.html"){
			$("#main-menu").hide();
			$("#tweet_div").html('Tweet »').hide().fadeIn(500);
		}
		if(page==="discussion.html"){
			$("#main-menu").hide();
			$("#discuss_btn_div").html('Discuss »').hide().fadeIn(500);
		}
}
<|endoftext|>


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

    const DEFAULT_LINK = '#';

    const FIAT_CODES = {
        [WavesApp.defaultAssets.USD]: 'USD',
        [WavesApp.defaultAssets.EUR]: 'EURO'
    };

    // TODO : move this to the related gateway service
    // TODO : keep only IDs here
    const FIAT_LIST = [
        {
            name: 'USD',
            assetId: WavesApp.defaultAssets.USD,
            fiatCode: FIAT_CODES[WavesApp.defaultAssets.USD],
            min: '30',
            max: '50'
        },
        {
            name: 'EUR',
            assetId: WavesApp.defaultAssets.EUR,
            fiatCode: FIAT_CODES[WavesApp.defaultAssets.EUR],
            min: '30',
            max: '50'
        }
    ];

    /**
     * @param Base
     * @param {$rootScope.Scope} $scope
     * @param {GatewayService} gatewayService
     * @param {User} user
     * @param {app.utils} utils
     * @param {Waves} waves
     * @return {ReceiveCtrl}
     */
    const controller = function (Base, $scope, gatewayService, user, utils, waves) {

        class ReceiveCtrl extends Base {

            constructor({ address, asset }) {
                super($scope);

                this.activeTab = 'cryptocurrency';
                /**
                 * @type {string}
                 */
                this.address = address;

                /**
                 * @type {Asset}
                 */
                this.asset = asset;

                /**
                 * @type {string}
                 */
                this.chosenAssetId = '';

                /**
                 * @type {number}
                 */
                this.step = 0;

                /**
                 * @type {string}
                 */
                this.assetKeyName = null;

                /**
                 * @type {string}
                 */
                this.gatewayAddress = null;

                /**
                 * @type {string}
                 */
                this.listOfEligibleCountries = DEFAULT_LINK;
                /**
                 * @type {string}
                 */
                this.idNowSiteUrl = DEFAULT_LINK;

                /**
                 * @type {string}
                 */
                this.idNowUserLink = DEFAULT_LINK;

                /**
                 * @type {string}
                 */
                this.digiLiraUserLink = 'https://www.digilira.com/';

                /**
                 * @type {string}
                 */
                this.indacoinLink = DEFAULT_LINK;

                /**
                 * @type {boolean}
                 */
                this.singleAsset = true;

                /**
                 * @type {boolean}
                 */
                this.showCryptocurrencyTab = false;

                /**
                 * @type {Array}
                 */
                this.cryptocurrencies = null;

                /**
                 * @type {boolean}
                 */
                this.showInvoiceTab = false;

                /**
                 * @type {Array}
                 */
                this.invoicables = null;

                /**
                 * @type {Array}
                 */
                this.addressAndAliases = null;

                /**
                 * @type {string}
                 */
                this.chosenAlias = '';

                /**
                 * @type {Object}
                 */
                this.invoiceAmount = null;

                /**
                 * @type {string}
                 */
                this.sendLink = '';

                /**
                 * @type {boolean}
                 */
                this.showCardTab = false;

                /**
                 * @type {Array}
                 */
                this.purchasablesByCards = null;

                /**
                 * @type {boolean}
                 */
                this.showBankTab = false;

                /**
                 * @type {Array}
                 */
                this.fiats = null;

                /**
                 * @type {boolean}
                 */
                this.showIdNowRedirectionDescription = false;

                /**
                 * @type {boolean}
                 */
                this.showIndacoinRedirectionDescription = false;

                /**
                 * @type {boolean}
                 */
                this.showDigiliraRedirectionDescription = false;

                /**
                 * @type {number}
                 */
                this.chosenCurrencyIndex = 0;

                /**
                 * {{[p: string]: string}}
                 */
                this.cardFiatList = FIAT_LIST;

                /**
                 * @type {Object}
                 */
                this.cardPayment = null;

                /**
                 * @type {number}
                 */
                this.approximateAmount = 0;

                if (this.asset) {
                    this.initForSingleAsset();
                } else {
                    this.singleAsset = false;
                    this.initForAllAssets();
                }
            }

            initCryptocurrencyTab() {
                this.gatewayServerError = false;
                const depositDetails = gatewayService.getDepositDetails(this.asset, this.address);
                if (depositDetails) {
                    depositDetails.then((details) => {
                        this.gatewayAddress = details.address;
                        $scope.$digest();
                    }, () => {
                        this.gatewayAddress = null;
                        this.gatewayServerError = true;
                        $scope.$digest();
                    });

                    this.assetKeyName = gatewayService.getAssetKeyName(this.asset, 'deposit');
                    this.activateCryptocurrencyTab();
                }
            }

            initInvoiceTab() {
                this.addressAndAliases = [
                    this.address,
                    ...waves.node.aliases.getAliasList()
                ];

                this.activateInvoiceTab();

                if (this.showInvoiceTab) {
                    this.setInvoiceObserver();
                }
            }

            updateGatewayAddress() {
                this.initCryptocurrencyTab();
            }

            setInvoiceObserver() {
                this.observe(['chosenAlias', 'invoiceAmount'], () => {
                    this.updateSendLink();
                });
            }

            updateSendLink() {
                const assetId = this.asset && this.asset.id;

                if (!(assetId && this.chosenAlias)) {
                    this.sendLink = '';
                    return;
                }

                const invoiceAmount = (this.invoiceAmount && this.invoiceAmount.toTokens()) || '0';

                const WAVES_URL = 'https://client.wavesplatform.com';

                this.sendLink = `${WAVES_URL}/#send/${assetId}?recipient=${this.chosenAlias}&amount=${invoiceAmount}`;
            }

            initCardTab() {
                if (gatewayService.hasSupportOf(this.asset, 'card')) {
                    this.showCardTab = true;
                    this.updateCardDetails().then(() => {
                        gatewayService.getCardFiatWithLimits(this.asset, user.address, FIAT_LIST).then((fiatList) => {
                            this.cardFiatList = fiatList;
                            this.updateCardTab();
                            this.setCardObserver();
                        });
                    });
                }
            }

            updateCardDetails() {
                return ds.moneyFromTokens(this.cardFiatList[0].min, this.cardFiatList[0].assetId).then((sum) => {
                    this.cardPayment = sum;
                });
            }

            updateCardTab() {
                this.updateApproximateAmount();
                this.indacoinLink = gatewayService.getCardBuyLink(
                    this.asset,
                    this.cardFiatList[this.chosenCurrencyIndex].fiatCode,
                    user.address,
                    this._tokenizeCardPayment()
                );
            }

            setCardObserver() {
                this.observe(['chosenCurrencyIndex', 'cardPayment', 'asset'], () => {
                    if (!Number(this._tokenizeCardPayment())) {
                        this.approximateAmount = new ds.wavesDataEntities.Money(0, this.asset);
                    }

                    this.updateCardTab();
                });
            }

            updateApproximateAmount() {
                this.approximateAmount = new ds.wavesDataEntities.Money(0, this.asset);

                gatewayService.getCardApproximateCryptoAmount(
                    this.asset,
                    this.cardFiatList[this.chosenCurrencyIndex].fiatCode,
                    user.address,
                    this._tokenizeCardPayment()
                )
                    .then((approximateAmount) => {
                        const coins = new BigNumber(approximateAmount).times(Math.pow(10, this.asset.precision));
                        this.approximateAmount = new ds.wavesDataEntities.Money(coins.dp(0), this.asset);
                        $scope.$digest();
                    });
            }

            initBankTab() {
                const sepaDetails = gatewayService.getSepaDetails(this.asset, this.address);
                if (sepaDetails) {
                    this.activateBankTab();
                }

            }

            initForSingleAsset() {
                this.initCryptocurrencyTab();
                this.initInvoiceTab();
                this.initCardTab();
                this.initBankTab();
            }

            initForAllAssets() {
                const cryptocurrenciesRequests = this.getExtendedAssets(gatewayService.getCryptocurrencies());
                const cryptocurrenciesRequest = Promise.all(cryptocurrenciesRequests).then((results) => {
                    this.cryptocurrencies = results;
                });

                const invoicesRequest = waves.node.assets.userBalances().then((results) => {
                    this.invoicables = results.filter(ReceiveCtrl._isNotScam).map((balance) => balance.asset);
                });

                const cardsRequests = this.getExtendedAssets(gatewayService.getPurchasableWithCards());
                const cardsRequest = Promise.all(cardsRequests).then((results) => {
                    this.purchasablesByCards = results;
                });

                const fiatsRequests = this.getExtendedAssets(gatewayService.getFiats());
                const fiatsRequest = Promise.all(fiatsRequests).then((results) => {
                    this.fiats = results;
                });

                Promise.all([cryptocurrenciesRequest, invoicesRequest, cardsRequest, fiatsRequest]).then(() => {
                    this.updateAssetAndDataBy(this.cryptocurrencies[0].id);

                    this.activateCryptocurrencyTab();
                    this.activateInvoiceTab();
                    this.activateCardTab();
                    this.activateBankTab();

                    this.initForSingleAsset();
                });

                this.observe('chosenAssetId', ({ value: id }) => this.updateAssetAndDataBy(id));
            }

            getExtendedAssets(assetsIds) {
                return (
                    Object
                        .keys(assetsIds)
                        .map(waves.node.assets.getAsset)
                );
            }

            updateAssetAndDataBy(id) {
                this.updateAssetBy(id);

                this.initCryptocurrencyTab();
                this.updateSendLink();
                if (this.isPurchasableByCard(this.asset)) {
                    if (this.showCardTab === true) {
                        this.initCardTab();
                    }
                    this.updateCardTab();
                }
            }

            updateAssetBy(id) {
                this.asset = (
                    this.cryptocurrencies.find((cryptocurrency) => cryptocurrency.id === id) ||
                    this.invoicables.find((invoicable) => invoicable.id === id) ||
                    this.purchasablesByCards.find((purchasableByCards) => purchasableByCards.id === id) ||
                    this.fiats.find((fiat) => fiat.id === id)
                );
            }

            isPurchasableByCard(asset) {
                return this.purchasablesByCards.find((purchasable) => purchasable.id === asset.id);
            }

            activateCryptocurrencyTab() {
                this.showCryptocurrencyTab = true;
            }

            activateInvoiceTab() {
                this.showInvoiceTab = true;
            }

            activateCardTab() {
                this.showCardTab = true;
            }

            activateBankTab() {
                this.showBankTab = true;
            }

            confirmIdNow() {
                this.showIdNowRedirectionDescription = true;
                this.increaseStep();
            }

            confirmIndacoin() {
                this.showIndacoinRedirectionDescription = true;
                this.increaseStep();
            }

            confirmDigilira() {
                this.showDigiliraRedirectionDescription = true;
                this.increaseStep();
            }

            increaseStep() {
                this.step++;
            }

            isLira() {
                return this.asset.id === WavesApp.defaultAssets.TRY;
            }

            _tokenizeCardPayment() {
                return (this.cardPayment && this.cardPayment.toTokens()) || 0;
            }

            /**
             * @return {boolean}
             * @private
             */
            static _isNotScam(item) {
                return !WavesApp.scam[item.asset.id];
            }

        }

        return new ReceiveCtrl(this.locals);
    };

    controller.$inject = ['Base', '$scope', 'gatewayService', 'user', 'utils', 'waves'];

    angular.module('app.utils').controller('ReceiveCtrl', controller);
})();
<commit_msg>CLIENT-1484: invoice link fixed<commit_after>(function () {
    'use strict';

    const DEFAULT_LINK = '#';

    const FIAT_CODES = {
        [WavesApp.defaultAssets.USD]: 'USD',
        [WavesApp.defaultAssets.EUR]: 'EURO'
    };

    // TODO : move this to the related gateway service
    // TODO : keep only IDs here
    const FIAT_LIST = [
        {
            name: 'USD',
            assetId: WavesApp.defaultAssets.USD,
            fiatCode: FIAT_CODES[WavesApp.defaultAssets.USD],
            min: '30',
            max: '50'
        },
        {
            name: 'EUR',
            assetId: WavesApp.defaultAssets.EUR,
            fiatCode: FIAT_CODES[WavesApp.defaultAssets.EUR],
            min: '30',
            max: '50'
        }
    ];

    /**
     * @param Base
     * @param {$rootScope.Scope} $scope
     * @param {GatewayService} gatewayService
     * @param {User} user
     * @param {app.utils} utils
     * @param {Waves} waves
     * @return {ReceiveCtrl}
     */
    const controller = function (Base, $scope, gatewayService, user, utils, waves) {

        class ReceiveCtrl extends Base {

            constructor({ address, asset }) {
                super($scope);

                this.activeTab = 'cryptocurrency';
                /**
                 * @type {string}
                 */
                this.address = address;

                /**
                 * @type {Asset}
                 */
                this.asset = asset;

                /**
                 * @type {string}
                 */
                this.chosenAssetId = '';

                /**
                 * @type {number}
                 */
                this.step = 0;

                /**
                 * @type {string}
                 */
                this.assetKeyName = null;

                /**
                 * @type {string}
                 */
                this.gatewayAddress = null;

                /**
                 * @type {string}
                 */
                this.listOfEligibleCountries = DEFAULT_LINK;
                /**
                 * @type {string}
                 */
                this.idNowSiteUrl = DEFAULT_LINK;

                /**
                 * @type {string}
                 */
                this.idNowUserLink = DEFAULT_LINK;

                /**
                 * @type {string}
                 */
                this.digiLiraUserLink = 'https://www.digilira.com/';

                /**
                 * @type {string}
                 */
                this.indacoinLink = DEFAULT_LINK;

                /**
                 * @type {boolean}
                 */
                this.singleAsset = true;

                /**
                 * @type {boolean}
                 */
                this.showCryptocurrencyTab = false;

                /**
                 * @type {Array}
                 */
                this.cryptocurrencies = null;

                /**
                 * @type {boolean}
                 */
                this.showInvoiceTab = false;

                /**
                 * @type {Array}
                 */
                this.invoicables = null;

                /**
                 * @type {Array}
                 */
                this.addressAndAliases = null;

                /**
                 * @type {string}
                 */
                this.chosenAlias = '';

                /**
                 * @type {Object}
                 */
                this.invoiceAmount = null;

                /**
                 * @type {string}
                 */
                this.sendLink = '';

                /**
                 * @type {boolean}
                 */
                this.showCardTab = false;

                /**
                 * @type {Array}
                 */
                this.purchasablesByCards = null;

                /**
                 * @type {boolean}
                 */
                this.showBankTab = false;

                /**
                 * @type {Array}
                 */
                this.fiats = null;

                /**
                 * @type {boolean}
                 */
                this.showIdNowRedirectionDescription = false;

                /**
                 * @type {boolean}
                 */
                this.showIndacoinRedirectionDescription = false;

                /**
                 * @type {boolean}
                 */
                this.showDigiliraRedirectionDescription = false;

                /**
                 * @type {number}
                 */
                this.chosenCurrencyIndex = 0;

                /**
                 * {{[p: string]: string}}
                 */
                this.cardFiatList = FIAT_LIST;

                /**
                 * @type {Object}
                 */
                this.cardPayment = null;

                /**
                 * @type {number}
                 */
                this.approximateAmount = 0;

                if (this.asset) {
                    this.initForSingleAsset();
                } else {
                    this.singleAsset = false;
                    this.initForAllAssets();
                }
            }

            initCryptocurrencyTab() {
                this.gatewayServerError = false;
                const depositDetails = gatewayService.getDepositDetails(this.asset, this.address);
                if (depositDetails) {
                    depositDetails.then((details) => {
                        this.gatewayAddress = details.address;
                        $scope.$digest();
                    }, () => {
                        this.gatewayAddress = null;
                        this.gatewayServerError = true;
                        $scope.$digest();
                    });

                    this.assetKeyName = gatewayService.getAssetKeyName(this.asset, 'deposit');
                    this.activateCryptocurrencyTab();
                }
            }

            initInvoiceTab() {
                this.addressAndAliases = [
                    this.address,
                    ...waves.node.aliases.getAliasList()
                ];

                this.activateInvoiceTab();

                if (this.showInvoiceTab) {
                    this.setInvoiceObserver();
                }
            }

            updateGatewayAddress() {
                this.initCryptocurrencyTab();
            }

            setInvoiceObserver() {
                this.observe(['chosenAlias', 'invoiceAmount'], () => {
                    this.updateSendLink();
                });
            }

            updateSendLink() {
                const assetId = this.asset && this.asset.id;

                if (!(assetId && this.chosenAlias)) {
                    this.sendLink = '';
                    return;
                }

                const invoiceAmount = (this.invoiceAmount && this.invoiceAmount.toTokens()) || '0';

                const WAVES_URL = location.origin;

                this.sendLink = `${WAVES_URL}/#send/${assetId}?recipient=${this.chosenAlias}&amount=${invoiceAmount}`;
            }

            initCardTab() {
                if (gatewayService.hasSupportOf(this.asset, 'card')) {
                    this.showCardTab = true;
                    this.updateCardDetails().then(() => {
                        gatewayService.getCardFiatWithLimits(this.asset, user.address, FIAT_LIST).then((fiatList) => {
                            this.cardFiatList = fiatList;
                            this.updateCardTab();
                            this.setCardObserver();
                        });
                    });
                }
            }

            updateCardDetails() {
                return ds.moneyFromTokens(this.cardFiatList[0].min, this.cardFiatList[0].assetId).then((sum) => {
                    this.cardPayment = sum;
                });
            }

            updateCardTab() {
                this.updateApproximateAmount();
                this.indacoinLink = gatewayService.getCardBuyLink(
                    this.asset,
                    this.cardFiatList[this.chosenCurrencyIndex].fiatCode,
                    user.address,
                    this._tokenizeCardPayment()
                );
            }

            setCardObserver() {
                this.observe(['chosenCurrencyIndex', 'cardPayment', 'asset'], () => {
                    if (!Number(this._tokenizeCardPayment())) {
                        this.approximateAmount = new ds.wavesDataEntities.Money(0, this.asset);
                    }

                    this.updateCardTab();
                });
            }

            updateApproximateAmount() {
                this.approximateAmount = new ds.wavesDataEntities.Money(0, this.asset);

                gatewayService.getCardApproximateCryptoAmount(
                    this.asset,
                    this.cardFiatList[this.chosenCurrencyIndex].fiatCode,
                    user.address,
                    this._tokenizeCardPayment()
                )
                    .then((approximateAmount) => {
                        const coins = new BigNumber(approximateAmount).times(Math.pow(10, this.asset.precision));
                        this.approximateAmount = new ds.wavesDataEntities.Money(coins.dp(0), this.asset);
                        $scope.$digest();
                    });
            }

            initBankTab() {
                const sepaDetails = gatewayService.getSepaDetails(this.asset, this.address);
                if (sepaDetails) {
                    this.activateBankTab();
                }

            }

            initForSingleAsset() {
                this.initCryptocurrencyTab();
                this.initInvoiceTab();
                this.initCardTab();
                this.initBankTab();
            }

            initForAllAssets() {
                const cryptocurrenciesRequests = this.getExtendedAssets(gatewayService.getCryptocurrencies());
                const cryptocurrenciesRequest = Promise.all(cryptocurrenciesRequests).then((results) => {
                    this.cryptocurrencies = results;
                });

                const invoicesRequest = waves.node.assets.userBalances().then((results) => {
                    this.invoicables = results.filter(ReceiveCtrl._isNotScam).map((balance) => balance.asset);
                });

                const cardsRequests = this.getExtendedAssets(gatewayService.getPurchasableWithCards());
                const cardsRequest = Promise.all(cardsRequests).then((results) => {
                    this.purchasablesByCards = results;
                });

                const fiatsRequests = this.getExtendedAssets(gatewayService.getFiats());
                const fiatsRequest = Promise.all(fiatsRequests).then((results) => {
                    this.fiats = results;
                });

                Promise.all([cryptocurrenciesRequest, invoicesRequest, cardsRequest, fiatsRequest]).then(() => {
                    this.updateAssetAndDataBy(this.cryptocurrencies[0].id);

                    this.activateCryptocurrencyTab();
                    this.activateInvoiceTab();
                    this.activateCardTab();
                    this.activateBankTab();

                    this.initForSingleAsset();
                });

                this.observe('chosenAssetId', ({ value: id }) => this.updateAssetAndDataBy(id));
            }

            getExtendedAssets(assetsIds) {
                return (
                    Object
                        .keys(assetsIds)
                        .map(waves.node.assets.getAsset)
                );
            }

            updateAssetAndDataBy(id) {
                this.updateAssetBy(id);

                this.initCryptocurrencyTab();
                this.updateSendLink();
                if (this.isPurchasableByCard(this.asset)) {
                    if (this.showCardTab === true) {
                        this.initCardTab();
                    }
                    this.updateCardTab();
                }
            }

            updateAssetBy(id) {
                this.asset = (
                    this.cryptocurrencies.find((cryptocurrency) => cryptocurrency.id === id) ||
                    this.invoicables.find((invoicable) => invoicable.id === id) ||
                    this.purchasablesByCards.find((purchasableByCards) => purchasableByCards.id === id) ||
                    this.fiats.find((fiat) => fiat.id === id)
                );
            }

            isPurchasableByCard(asset) {
                return this.purchasablesByCards.find((purchasable) => purchasable.id === asset.id);
            }

            activateCryptocurrencyTab() {
                this.showCryptocurrencyTab = true;
            }

            activateInvoiceTab() {
                this.showInvoiceTab = true;
            }

            activateCardTab() {
                this.showCardTab = true;
            }

            activateBankTab() {
                this.showBankTab = true;
            }

            confirmIdNow() {
                this.showIdNowRedirectionDescription = true;
                this.increaseStep();
            }

            confirmIndacoin() {
                this.showIndacoinRedirectionDescription = true;
                this.increaseStep();
            }

            confirmDigilira() {
                this.showDigiliraRedirectionDescription = true;
                this.increaseStep();
            }

            increaseStep() {
                this.step++;
            }

            isLira() {
                return this.asset.id === WavesApp.defaultAssets.TRY;
            }

            _tokenizeCardPayment() {
                return (this.cardPayment && this.cardPayment.toTokens()) || 0;
            }

            /**
             * @return {boolean}
             * @private
             */
            static _isNotScam(item) {
                return !WavesApp.scam[item.asset.id];
            }

        }

        return new ReceiveCtrl(this.locals);
    };

    controller.$inject = ['Base', '$scope', 'gatewayService', 'user', 'utils', 'waves'];

    angular.module('app.utils').controller('ReceiveCtrl', controller);
})();
<|endoftext|>


<commit_before>// FIXME offset panning

goog.provide('ol.renderer.canvas.Map');

goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.style');
goog.require('goog.vec.Mat4');
goog.require('ol');
goog.require('ol.RendererType');
goog.require('ol.css');
goog.require('ol.dom');
goog.require('ol.extent');
goog.require('ol.layer.Image');
goog.require('ol.layer.Layer');
goog.require('ol.layer.Tile');
goog.require('ol.layer.Vector');
goog.require('ol.render.Event');
goog.require('ol.render.EventType');
goog.require('ol.render.canvas.Immediate');
goog.require('ol.render.canvas.ReplayGroup');
goog.require('ol.renderer.Map');
goog.require('ol.renderer.canvas.ImageLayer');
goog.require('ol.renderer.canvas.Layer');
goog.require('ol.renderer.canvas.TileLayer');
goog.require('ol.renderer.canvas.VectorLayer');
goog.require('ol.renderer.vector');
goog.require('ol.source.State');
goog.require('ol.vec.Mat4');

/**
 * @constructor
 * @extends {ol.renderer.Map}
 * @param {Element} container Container.
 * @param {ol.Map} map Map.
 */
ol.renderer.canvas.Map = function(container, map) {

  goog.base(this, container, map);

  /**
   * @private
   * @type {CanvasRenderingContext2D}
   */
  this.context_ = ol.dom.createCanvasContext2D();

  /**
   * @private
   * @type {HTMLCanvasElement}
   */
  this.canvas_ = this.context_.canvas;

  this.canvas_.style.width = '100%';
  this.canvas_.style.height = '100%';
  this.canvas_.className = ol.css.CLASS_UNSELECTABLE;
  goog.dom.insertChildAt(container, this.canvas_, 0);

  /**
   * @private
   * @type {boolean}
   */
  this.renderedVisible_ = true;

  /**
   * @private
   * @type {!goog.vec.Mat4.Number}
   */
  this.transform_ = goog.vec.Mat4.createNumber();

};
goog.inherits(ol.renderer.canvas.Map, ol.renderer.Map);

/**
 * @inheritDoc
 */
ol.renderer.canvas.Map.prototype.createLayerRenderer = function(layer) {
  if (ol.ENABLE_IMAGE && layer instanceof ol.layer.Image) {
    return new ol.renderer.canvas.ImageLayer(layer);
  } else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) {
    return new ol.renderer.canvas.TileLayer(layer);
  } else if (ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) {
    return new ol.renderer.canvas.VectorLayer(layer);
  } else {
    goog.asserts.fail('unexpected layer configuration');
    return null;
  }
};

/**
 * @param {ol.render.EventType} type Event type.
 * @param {olx.FrameState} frameState Frame state.
 * @param {boolean} wrapX Wrap the x-axis.
 * @private
 */
ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ =
    function(type, frameState, wrapX) {
  var map = this.getMap();
  var context = this.context_;
  if (map.hasListener(type)) {
    var extent = frameState.extent;
    var pixelRatio = frameState.pixelRatio;
    var viewState = frameState.viewState;
    var projection = viewState.projection;
    var projectionExtent = projection.getExtent();
    var resolution = viewState.resolution;
    var rotation = viewState.rotation;
    var repeatReplay = (wrapX && projection.canWrapX() &&
        !ol.extent.containsExtent(projectionExtent, extent));
    var skippedFeaturesHash = {};

    var transform = this.getTransform(frameState, 0);

    var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio);
    var replayGroup = new ol.render.canvas.ReplayGroup(tolerance,
        repeatReplay ?
            [projectionExtent[0], extent[1], projectionExtent[2], extent[3]] :
            extent,
        resolution);

    var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio,
        extent, transform, rotation);
    var composeEvent = new ol.render.Event(type, map, vectorContext,
        replayGroup, frameState, context, null);
    map.dispatchEvent(composeEvent);

    replayGroup.finish();
    if (!replayGroup.isEmpty()) {
      replayGroup.replay(context, pixelRatio, transform, rotation,
          skippedFeaturesHash);

      if (repeatReplay) {
        var startX = extent[0];
        var worldWidth = ol.extent.getWidth(projectionExtent);
        var world = 0;
        while (startX < projectionExtent[0]) {
          --world;
          transform = this.getTransform(frameState, worldWidth * world);
          replayGroup.replay(context, pixelRatio, transform, rotation,
              skippedFeaturesHash);
          startX += worldWidth;
        }
        world = 0;
        startX = extent[2];
        while (startX > projectionExtent[2]) {
          ++world;
          transform = this.getTransform(frameState, worldWidth * ++world);
          replayGroup.replay(context, pixelRatio, transform, rotation,
              skippedFeaturesHash);
          startX -= worldWidth;
        }
      }
    }
    vectorContext.flush();
    this.replayGroup = replayGroup;
  }
};

/**
 * @param {olx.FrameState} frameState Frame state.
 * @param {number} offsetX Offset on the x-axis in view coordinates.
 * @protected
 * @return {!goog.vec.Mat4.Number} Transform.
 */
ol.renderer.canvas.Map.prototype.getTransform = function(frameState, offsetX) {
  var pixelRatio = frameState.pixelRatio;
  var viewState = frameState.viewState;
  var resolution = viewState.resolution;
  return ol.vec.Mat4.makeTransform2D(this.transform_,
      this.canvas_.width / 2, this.canvas_.height / 2,
      pixelRatio / resolution, -pixelRatio / resolution,
      -viewState.rotation,
      -viewState.center[0] + offsetX,
      -viewState.center[1]);
};

/**
 * @inheritDoc
 */
ol.renderer.canvas.Map.prototype.getType = function() {
  return ol.RendererType.CANVAS;
};

/**
 * @inheritDoc
 */
ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {

  if (goog.isNull(frameState)) {
    if (this.renderedVisible_) {
      goog.style.setElementShown(this.canvas_, false);
      this.renderedVisible_ = false;
    }
    return;
  }

  var context = this.context_;
  var width = frameState.size[0] * frameState.pixelRatio;
  var height = frameState.size[1] * frameState.pixelRatio;
  if (this.canvas_.width != width || this.canvas_.height != height) {
    this.canvas_.width = width;
    this.canvas_.height = height;
  } else {
    context.clearRect(0, 0, this.canvas_.width, this.canvas_.height);
  }

  this.calculateMatrices2D(frameState);

  this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState, false);

  var layerStatesArray = frameState.layerStatesArray;
  var viewResolution = frameState.viewState.resolution;
  var wrapX = false;
  var i, ii, layer, layerRenderer, layerState;
  for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
    layerState = layerStatesArray[i];
    layer = layerState.layer;
    layerRenderer = this.getLayerRenderer(layer);
    goog.asserts.assertInstanceof(layerRenderer, ol.renderer.canvas.Layer,
        'layerRenderer is an instance of ol.renderer.canvas.Layer');
    if (!ol.layer.Layer.visibleAtResolution(layerState, viewResolution) ||
        layerState.sourceState != ol.source.State.READY) {
      continue;
    }
    if (layerRenderer.prepareFrame(frameState, layerState)) {
      if (layer instanceof ol.layer.Vector && layer.getSource().getWrapX()) {
        wrapX = true;
      }
      layerRenderer.composeFrame(frameState, layerState, context);
    }
  }

  this.dispatchComposeEvent_(
      ol.render.EventType.POSTCOMPOSE, frameState, wrapX);

  if (!this.renderedVisible_) {
    goog.style.setElementShown(this.canvas_, true);
    this.renderedVisible_ = true;
  }

  this.scheduleRemoveUnusedLayerRenderers(frameState);
  this.scheduleExpireIconCache(frameState);
};
<commit_msg>Clarify when we make feature overlays wrap the x-axis<commit_after>// FIXME offset panning

goog.provide('ol.renderer.canvas.Map');

goog.require('goog.asserts');
goog.require('goog.dom');
goog.require('goog.style');
goog.require('goog.vec.Mat4');
goog.require('ol');
goog.require('ol.RendererType');
goog.require('ol.css');
goog.require('ol.dom');
goog.require('ol.extent');
goog.require('ol.layer.Image');
goog.require('ol.layer.Layer');
goog.require('ol.layer.Tile');
goog.require('ol.layer.Vector');
goog.require('ol.render.Event');
goog.require('ol.render.EventType');
goog.require('ol.render.canvas.Immediate');
goog.require('ol.render.canvas.ReplayGroup');
goog.require('ol.renderer.Map');
goog.require('ol.renderer.canvas.ImageLayer');
goog.require('ol.renderer.canvas.Layer');
goog.require('ol.renderer.canvas.TileLayer');
goog.require('ol.renderer.canvas.VectorLayer');
goog.require('ol.renderer.vector');
goog.require('ol.source.State');
goog.require('ol.vec.Mat4');

/**
 * @constructor
 * @extends {ol.renderer.Map}
 * @param {Element} container Container.
 * @param {ol.Map} map Map.
 */
ol.renderer.canvas.Map = function(container, map) {

  goog.base(this, container, map);

  /**
   * @private
   * @type {CanvasRenderingContext2D}
   */
  this.context_ = ol.dom.createCanvasContext2D();

  /**
   * @private
   * @type {HTMLCanvasElement}
   */
  this.canvas_ = this.context_.canvas;

  this.canvas_.style.width = '100%';
  this.canvas_.style.height = '100%';
  this.canvas_.className = ol.css.CLASS_UNSELECTABLE;
  goog.dom.insertChildAt(container, this.canvas_, 0);

  /**
   * @private
   * @type {boolean}
   */
  this.renderedVisible_ = true;

  /**
   * @private
   * @type {!goog.vec.Mat4.Number}
   */
  this.transform_ = goog.vec.Mat4.createNumber();

};
goog.inherits(ol.renderer.canvas.Map, ol.renderer.Map);

/**
 * @inheritDoc
 */
ol.renderer.canvas.Map.prototype.createLayerRenderer = function(layer) {
  if (ol.ENABLE_IMAGE && layer instanceof ol.layer.Image) {
    return new ol.renderer.canvas.ImageLayer(layer);
  } else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) {
    return new ol.renderer.canvas.TileLayer(layer);
  } else if (ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) {
    return new ol.renderer.canvas.VectorLayer(layer);
  } else {
    goog.asserts.fail('unexpected layer configuration');
    return null;
  }
};

/**
 * @param {ol.render.EventType} type Event type.
 * @param {olx.FrameState} frameState Frame state.
 * @param {boolean} wrapX Wrap the x-axis.
 * @private
 */
ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ =
    function(type, frameState, wrapX) {
  var map = this.getMap();
  var context = this.context_;
  if (map.hasListener(type)) {
    var extent = frameState.extent;
    var pixelRatio = frameState.pixelRatio;
    var viewState = frameState.viewState;
    var projection = viewState.projection;
    var projectionExtent = projection.getExtent();
    var resolution = viewState.resolution;
    var rotation = viewState.rotation;
    var repeatReplay = (wrapX && projection.canWrapX() &&
        !ol.extent.containsExtent(projectionExtent, extent));
    var skippedFeaturesHash = {};

    var transform = this.getTransform(frameState, 0);

    var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio);
    var replayGroup = new ol.render.canvas.ReplayGroup(tolerance,
        repeatReplay ?
            [projectionExtent[0], extent[1], projectionExtent[2], extent[3]] :
            extent,
        resolution);

    var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio,
        extent, transform, rotation);
    var composeEvent = new ol.render.Event(type, map, vectorContext,
        replayGroup, frameState, context, null);
    map.dispatchEvent(composeEvent);

    replayGroup.finish();
    if (!replayGroup.isEmpty()) {
      replayGroup.replay(context, pixelRatio, transform, rotation,
          skippedFeaturesHash);

      if (repeatReplay) {
        var startX = extent[0];
        var worldWidth = ol.extent.getWidth(projectionExtent);
        var world = 0;
        while (startX < projectionExtent[0]) {
          --world;
          transform = this.getTransform(frameState, worldWidth * world);
          replayGroup.replay(context, pixelRatio, transform, rotation,
              skippedFeaturesHash);
          startX += worldWidth;
        }
        world = 0;
        startX = extent[2];
        while (startX > projectionExtent[2]) {
          ++world;
          transform = this.getTransform(frameState, worldWidth * ++world);
          replayGroup.replay(context, pixelRatio, transform, rotation,
              skippedFeaturesHash);
          startX -= worldWidth;
        }
      }
    }
    vectorContext.flush();
    this.replayGroup = replayGroup;
  }
};

/**
 * @param {olx.FrameState} frameState Frame state.
 * @param {number} offsetX Offset on the x-axis in view coordinates.
 * @protected
 * @return {!goog.vec.Mat4.Number} Transform.
 */
ol.renderer.canvas.Map.prototype.getTransform = function(frameState, offsetX) {
  var pixelRatio = frameState.pixelRatio;
  var viewState = frameState.viewState;
  var resolution = viewState.resolution;
  return ol.vec.Mat4.makeTransform2D(this.transform_,
      this.canvas_.width / 2, this.canvas_.height / 2,
      pixelRatio / resolution, -pixelRatio / resolution,
      -viewState.rotation,
      -viewState.center[0] + offsetX,
      -viewState.center[1]);
};

/**
 * @inheritDoc
 */
ol.renderer.canvas.Map.prototype.getType = function() {
  return ol.RendererType.CANVAS;
};

/**
 * @inheritDoc
 */
ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) {

  if (goog.isNull(frameState)) {
    if (this.renderedVisible_) {
      goog.style.setElementShown(this.canvas_, false);
      this.renderedVisible_ = false;
    }
    return;
  }

  var context = this.context_;
  var width = frameState.size[0] * frameState.pixelRatio;
  var height = frameState.size[1] * frameState.pixelRatio;
  if (this.canvas_.width != width || this.canvas_.height != height) {
    this.canvas_.width = width;
    this.canvas_.height = height;
  } else {
    context.clearRect(0, 0, this.canvas_.width, this.canvas_.height);
  }

  this.calculateMatrices2D(frameState);

  this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState, false);

  var layerStatesArray = frameState.layerStatesArray;
  var viewResolution = frameState.viewState.resolution;
  var wrapX = false;
  var i, ii, layer, layerRenderer, layerState;
  for (i = 0, ii = layerStatesArray.length; i < ii; ++i) {
    layerState = layerStatesArray[i];
    layer = layerState.layer;
    layerRenderer = this.getLayerRenderer(layer);
    goog.asserts.assertInstanceof(layerRenderer, ol.renderer.canvas.Layer,
        'layerRenderer is an instance of ol.renderer.canvas.Layer');
    if (!ol.layer.Layer.visibleAtResolution(layerState, viewResolution) ||
        layerState.sourceState != ol.source.State.READY) {
      continue;
    }
    if (layerRenderer.prepareFrame(frameState, layerState)) {
      // As soon as a vector layer on the map has wrapX set to true, we make
      // feature overlays wrap the x-axis too.
      if (layer instanceof ol.layer.Vector && layer.getSource().getWrapX()) {
        wrapX = true;
      }
      layerRenderer.composeFrame(frameState, layerState, context);
    }
  }

  this.dispatchComposeEvent_(
      ol.render.EventType.POSTCOMPOSE, frameState, wrapX);

  if (!this.renderedVisible_) {
    goog.style.setElementShown(this.canvas_, true);
    this.renderedVisible_ = true;
  }

  this.scheduleRemoveUnusedLayerRenderers(frameState);
  this.scheduleExpireIconCache(frameState);
};
<|endoftext|>


<commit_before>module.exports = {
  name: 'clone',
  command: clone,
  help: [
    'Clone a remote Dat archive',
    '',
    'Usage: dat clone <link> [download-folder]'
  ].join('\n'),
  options: [
    {
      name: 'empty',
      boolean: false,
      default: false,
      help: 'Do not download files by default. Files must be synced manually.'
    },
    {
      name: 'upload',
      boolean: true,
      default: true,
      help: 'announce your address on link (improves connection capability) and upload data to other downloaders.'
    },
    {
      name: 'show-key',
      boolean: true,
      default: false,
      abbr: 'k',
      help: 'print out the dat key'
    }
  ]
}

function clone (opts) {
  var fs = require('fs')
  var path = require('path')
  var rimraf = require('rimraf')
  var Dat = require('dat-node')
  var linkResolve = require('dat-link-resolve')
  var neatLog = require('neat-log')
  var archiveUI = require('../ui/archive')
  var trackArchive = require('../lib/archive')
  var discoveryExit = require('../lib/discovery-exit')
  var onExit = require('../lib/exit')
  var parseArgs = require('../parse-args')
  var debug = require('debug')('dat')

  var parsed = parseArgs(opts)
  opts.key = parsed.key || opts._[0] // pass other links to resolver
  opts.dir = parsed.dir
  opts.showKey = opts['show-key'] // using abbr in option makes printed help confusing
  opts.sparse = opts.empty

  debug('clone()')

  // cmd: dat /path/to/dat.json (opts.key is path to dat.json)
  if (fs.existsSync(opts.key)) {
    try {
      opts.key = getDatJsonKey()
    } catch (e) {
      debug('error reading dat.json key', e)
    }
  }

  debug(Object.assign({}, opts, {key: '<private>', _: null})) // don't show key

  var neat = neatLog(archiveUI, { logspeed: opts.logspeed, quiet: opts.quiet, debug: opts.debug })
  neat.use(trackArchive)
  neat.use(discoveryExit)
  neat.use(onExit)
  neat.use(function (state, bus) {
    if (!opts.key) return bus.emit('exit:warn', 'key required to clone')

    state.opts = opts
    var createdDirectory = null // so we can delete directory if we get error

    // Force these options for clone command
    opts.exit = (opts.exit !== false)
    // opts.errorIfExists = true // TODO: do we want to force this?

    linkResolve(opts.key, function (err, key) {
      if (err && err.message.indexOf('Invalid key') === -1) return bus.emit('exit:error', 'Could not resolve link')
      else if (err) return bus.emit('exit:warn', 'Link is not a valid Dat link.')

      opts.key = key
      createDir(opts.key, function () {
        bus.emit('key', key)
        runDat()
      })
    })

    function createDir (key, cb) {
      debug('Checking directory for clone')
      // Create the directory if it doesn't exist
      // If no dir is specified, we put dat in a dir with name = key
      if (!opts.dir) opts.dir = key
      fs.access(opts.dir, fs.F_OK, function (err) {
        if (!err) {
          createdDirectory = false
          return cb()
        }
        debug('No existing directory, creating it.')
        createdDirectory = true
        fs.mkdir(opts.dir, cb)
      })
    }

    function runDat () {
      Dat(opts.dir, opts, function (err, dat) {
        if (err && err.name === 'ExistsError') return bus.emit('exit:warn', 'Existing archive in this directory. Use pull or sync to update.')
        if (err) {
          if (createdDirectory) rimraf.sync(dat.path)
          return bus.emit('exit:error', err)
        }
        if (dat.writable) return bus.emit('exit:warn', 'Archive is writable. Cannot clone your own archive =).')

        state.dat = dat
        state.title = 'Cloning'
        bus.emit('dat')
        bus.emit('render')
      })
    }
  })

  function getDatJsonKey () {
    var datPath = opts.key
    var stat = fs.lstatSync(datPath)

    if (stat.isDirectory()) datPath = path.join(datPath, 'dat.json')

    if (!fs.existsSync(datPath) || path.basename(datPath) !== 'dat.json') {
      if (stat.isFile()) throw new Error('must specify existing dat.json file to read key')
      throw new Error('directory must contain a dat.json')
    }

    debug('reading key from dat.json:', datPath)
    return JSON.parse(fs.readFileSync(datPath, 'utf8')).url
  }
}
<commit_msg>show messsage if invalid dir name on createDir (#889)<commit_after>module.exports = {
  name: 'clone',
  command: clone,
  help: [
    'Clone a remote Dat archive',
    '',
    'Usage: dat clone <link> [download-folder]'
  ].join('\n'),
  options: [
    {
      name: 'empty',
      boolean: false,
      default: false,
      help: 'Do not download files by default. Files must be synced manually.'
    },
    {
      name: 'upload',
      boolean: true,
      default: true,
      help: 'announce your address on link (improves connection capability) and upload data to other downloaders.'
    },
    {
      name: 'show-key',
      boolean: true,
      default: false,
      abbr: 'k',
      help: 'print out the dat key'
    }
  ]
}

function clone (opts) {
  var fs = require('fs')
  var path = require('path')
  var rimraf = require('rimraf')
  var Dat = require('dat-node')
  var linkResolve = require('dat-link-resolve')
  var neatLog = require('neat-log')
  var archiveUI = require('../ui/archive')
  var trackArchive = require('../lib/archive')
  var discoveryExit = require('../lib/discovery-exit')
  var onExit = require('../lib/exit')
  var parseArgs = require('../parse-args')
  var debug = require('debug')('dat')

  var parsed = parseArgs(opts)
  opts.key = parsed.key || opts._[0] // pass other links to resolver
  opts.dir = parsed.dir
  opts.showKey = opts['show-key'] // using abbr in option makes printed help confusing
  opts.sparse = opts.empty

  debug('clone()')

  // cmd: dat /path/to/dat.json (opts.key is path to dat.json)
  if (fs.existsSync(opts.key)) {
    try {
      opts.key = getDatJsonKey()
    } catch (e) {
      debug('error reading dat.json key', e)
    }
  }

  debug(Object.assign({}, opts, {key: '<private>', _: null})) // don't show key

  var neat = neatLog(archiveUI, { logspeed: opts.logspeed, quiet: opts.quiet, debug: opts.debug })
  neat.use(trackArchive)
  neat.use(discoveryExit)
  neat.use(onExit)
  neat.use(function (state, bus) {
    if (!opts.key) return bus.emit('exit:warn', 'key required to clone')

    state.opts = opts
    var createdDirectory = null // so we can delete directory if we get error

    // Force these options for clone command
    opts.exit = (opts.exit !== false)
    // opts.errorIfExists = true // TODO: do we want to force this?

    linkResolve(opts.key, function (err, key) {
      if (err && err.message.indexOf('Invalid key') === -1) return bus.emit('exit:error', 'Could not resolve link')
      else if (err) return bus.emit('exit:warn', 'Link is not a valid Dat link.')

      opts.key = key
      createDir(opts.key, function () {
        bus.emit('key', key)
        runDat()
      })
    })

    function createDir (key, cb) {
      debug('Checking directory for clone')
      // Create the directory if it doesn't exist
      // If no dir is specified, we put dat in a dir with name = key
      if (!opts.dir) opts.dir = key
      if (!Buffer.isBuffer(opts.dir) && typeof opts.dir !== 'string') {
        return bus.emit('exit:error', 'Directory path must be a string or Buffer')
      }
      fs.access(opts.dir, fs.F_OK, function (err) {
        if (!err) {
          createdDirectory = false
          return cb()
        }
        debug('No existing directory, creating it.')
        createdDirectory = true
        fs.mkdir(opts.dir, cb)
      })
    }

    function runDat () {
      Dat(opts.dir, opts, function (err, dat) {
        if (err && err.name === 'ExistsError') return bus.emit('exit:warn', 'Existing archive in this directory. Use pull or sync to update.')
        if (err) {
          if (createdDirectory) rimraf.sync(dat.path)
          return bus.emit('exit:error', err)
        }
        if (dat.writable) return bus.emit('exit:warn', 'Archive is writable. Cannot clone your own archive =).')

        state.dat = dat
        state.title = 'Cloning'
        bus.emit('dat')
        bus.emit('render')
      })
    }
  })

  function getDatJsonKey () {
    var datPath = opts.key
    var stat = fs.lstatSync(datPath)

    if (stat.isDirectory()) datPath = path.join(datPath, 'dat.json')

    if (!fs.existsSync(datPath) || path.basename(datPath) !== 'dat.json') {
      if (stat.isFile()) throw new Error('must specify existing dat.json file to read key')
      throw new Error('directory must contain a dat.json')
    }

    debug('reading key from dat.json:', datPath)
    return JSON.parse(fs.readFileSync(datPath, 'utf8')).url
  }
}
<|endoftext|>


<commit_before>'use strict';

var isChannelReady = false;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;

var pcConfig = {
  'iceServers': [{
    'urls': 'stun:stun.l.google.com:19302'
  }]
};

// Set up audio and video regardless of what devices are present.
var sdpConstraints = {
  offerToReceiveAudio: true,
  offerToReceiveVideo: true
};

/////////////////////////////////////////////

var room = 'foo';
// Could prompt for room name:
// room = prompt('Enter room name:');

var socket = io.connect();

if (room !== '') {
  socket.emit('create or join', room);
  console.log('Attempted to create or  join room', room);
}

socket.on('created', function(room) {
  console.log('Created room ' + room);
  isInitiator = true;
});

socket.on('full', function(room) {
  console.log('Room ' + room + ' is full');
});

socket.on('join', function (room){
  console.log('Another peer made a request to join room ' + room);
  console.log('This peer is the initiator of room ' + room + '!');
  isChannelReady = true;
});

socket.on('joined', function(room) {
  console.log('joined: ' + room);
  isChannelReady = true;
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

////////////////////////////////////////////////

function sendMessage(message) {
  console.log('Client sending message: ', message);
  socket.emit('message', message);
}

// This client receives a message
socket.on('message', function(message) {
  console.log('Client received message:', message);
  if (message === 'got user media') {
    maybeStart();
  } else if (message.type === 'offer') {
    if (!isInitiator && !isStarted) {
      maybeStart();
    }
    pc.setRemoteDescription(new RTCSessionDescription(message));
    doAnswer();
  } else if (message.type === 'answer' && isStarted) {
    pc.setRemoteDescription(new RTCSessionDescription(message));
  } else if (message.type === 'candidate' && isStarted) {
    var candidate = new RTCIceCandidate({
      sdpMLineIndex: message.label,
      candidate: message.candidate
    });
    pc.addIceCandidate(candidate);
  } else if (message === 'bye' && isStarted) {
    handleRemoteHangup();
  }
});

////////////////////////////////////////////////////

var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');

navigator.mediaDevices.getUserMedia({
  audio: false,
  video: true
})
.then(gotStream)
.catch(function(e) {
  alert('getUserMedia() error: ' + e.name);
});

function gotStream(stream) {
  console.log('Adding local stream.');
  localVideo.srcObject = stream;
  localStream = stream;
  sendMessage('got user media');
  if (isInitiator) {
    maybeStart();
  }
}

var constraints = {
  video: true
};

console.log('Getting user media with constraints', constraints);

if (location.hostname !== 'localhost') {
  requestTurn(
    'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
  );
}

function maybeStart() {
  console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
  if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
    console.log('>>>>>> creating peer connection');
    createPeerConnection();
    pc.addStream(localStream);
    isStarted = true;
    console.log('isInitiator', isInitiator);
    if (isInitiator) {
      doCall();
    }
  }
}

window.onbeforeunload = function() {
  sendMessage('bye');
};

/////////////////////////////////////////////////////////

function createPeerConnection() {
  try {
    pc = new RTCPeerConnection(null);
    pc.onicecandidate = handleIceCandidate;
    pc.onaddstream = handleRemoteStreamAdded;
    pc.onremovestream = handleRemoteStreamRemoved;
    console.log('Created RTCPeerConnnection');
  } catch (e) {
    console.log('Failed to create PeerConnection, exception: ' + e.message);
    alert('Cannot create RTCPeerConnection object.');
    return;
  }
}

function handleIceCandidate(event) {
  console.log('icecandidate event: ', event);
  if (event.candidate) {
    sendMessage({
      type: 'candidate',
      label: event.candidate.sdpMLineIndex,
      id: event.candidate.sdpMid,
      candidate: event.candidate.candidate
    });
  } else {
    console.log('End of candidates.');
  }
}

function handleRemoteStreamAdded(event) {
  console.log('Remote stream added.');
  remoteStream = event.stream;
  remoteVideo.srcObject = remoteStream;
}

function handleCreateOfferError(event) {
  console.log('createOffer() error: ', event);
}

function doCall() {
  console.log('Sending offer to peer');
  pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}

function doAnswer() {
  console.log('Sending answer to peer.');
  pc.createAnswer().then(
    setLocalAndSendMessage,
    onCreateSessionDescriptionError
  );
}

function setLocalAndSendMessage(sessionDescription) {
  // Set Opus as the preferred codec in SDP if Opus is present.
  //  sessionDescription.sdp = preferOpus(sessionDescription.sdp);
  pc.setLocalDescription(sessionDescription);
  console.log('setLocalAndSendMessage sending message', sessionDescription);
  sendMessage(sessionDescription);
}

function onCreateSessionDescriptionError(error) {
  trace('Failed to create session description: ' + error.toString());
}

function requestTurn(turnURL) {
  var turnExists = false;
  for (var i in pcConfig.iceServers) {
    if (pcConfig.iceServers[i].url.substr(0, 5) === 'turn:') {
      turnExists = true;
      turnReady = true;
      break;
    }
  }
  if (!turnExists) {
    console.log('Getting TURN server from ', turnURL);
    // No TURN server. Get one from computeengineondemand.appspot.com:
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        var turnServer = JSON.parse(xhr.responseText);
        console.log('Got TURN server: ', turnServer);
        pcConfig.iceServers.push({
          'url': 'turn:' + turnServer.username + '@' + turnServer.turn,
          'credential': turnServer.password
        });
        turnReady = true;
      }
    };
    xhr.open('GET', turnURL, true);
    xhr.send();
  }
}

function handleRemoteStreamAdded(event) {
  console.log('Remote stream added.');
  remoteStream = event.stream;
  remoteVideo.srcObject = remoteStream;
}

function handleRemoteStreamRemoved(event) {
  console.log('Remote stream removed. Event: ', event);
}

function hangup() {
  console.log('Hanging up.');
  stop();
  sendMessage('bye');
}

function handleRemoteHangup() {
  console.log('Session terminated.');
  stop();
  isInitiator = false;
}

function stop() {
  isStarted = false;
  // isAudioMuted = false;
  // isVideoMuted = false;
  pc.close();
  pc = null;
}

///////////////////////////////////////////

// Set Opus as the default audio codec if it's present.
function preferOpus(sdp) {
  var sdpLines = sdp.split('\r\n');
  var mLineIndex;
  // Search for m line.
  for (var i = 0; i < sdpLines.length; i++) {
    if (sdpLines[i].search('m=audio') !== -1) {
      mLineIndex = i;
      break;
    }
  }
  if (mLineIndex === null) {
    return sdp;
  }

  // If Opus is available, set it as the default in m line.
  for (i = 0; i < sdpLines.length; i++) {
    if (sdpLines[i].search('opus/48000') !== -1) {
      var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
      if (opusPayload) {
        sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex],
          opusPayload);
      }
      break;
    }
  }

  // Remove CN in m line and sdp.
  sdpLines = removeCN(sdpLines, mLineIndex);

  sdp = sdpLines.join('\r\n');
  return sdp;
}

function extractSdp(sdpLine, pattern) {
  var result = sdpLine.match(pattern);
  return result && result.length === 2 ? result[1] : null;
}

// Set the selected codec to the first in m line.
function setDefaultCodec(mLine, payload) {
  var elements = mLine.split(' ');
  var newLine = [];
  var index = 0;
  for (var i = 0; i < elements.length; i++) {
    if (index === 3) { // Format of media starts from the fourth.
      newLine[index++] = payload; // Put target payload to the first.
    }
    if (elements[i] !== payload) {
      newLine[index++] = elements[i];
    }
  }
  return newLine.join(' ');
}

// Strip CN from sdp before CN constraints is ready.
function removeCN(sdpLines, mLineIndex) {
  var mLineElements = sdpLines[mLineIndex].split(' ');
  // Scan from end for the convenience of removing an item.
  for (var i = sdpLines.length - 1; i >= 0; i--) {
    var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
    if (payload) {
      var cnPos = mLineElements.indexOf(payload);
      if (cnPos !== -1) {
        // Remove CN payload from m line.
        mLineElements.splice(cnPos, 1);
      }
      // Remove CN line in sdp
      sdpLines.splice(i, 1);
    }
  }

  sdpLines[mLineIndex] = mLineElements.join(' ');
  return sdpLines;
}
<commit_msg>Sync step5 to master<commit_after>'use strict';

var isChannelReady = false;
var isInitiator = false;
var isStarted = false;
var localStream;
var pc;
var remoteStream;
var turnReady;

var pcConfig = {
  'iceServers': [{
    'urls': 'stun:stun.l.google.com:19302'
  }]
};

// Set up audio and video regardless of what devices are present.
var sdpConstraints = {
  offerToReceiveAudio: true,
  offerToReceiveVideo: true
};

/////////////////////////////////////////////

var room = 'foo';
// Could prompt for room name:
// room = prompt('Enter room name:');

var socket = io.connect();

if (room !== '') {
  socket.emit('create or join', room);
  console.log('Attempted to create or  join room', room);
}

socket.on('created', function(room) {
  console.log('Created room ' + room);
  isInitiator = true;
});

socket.on('full', function(room) {
  console.log('Room ' + room + ' is full');
});

socket.on('join', function (room){
  console.log('Another peer made a request to join room ' + room);
  console.log('This peer is the initiator of room ' + room + '!');
  isChannelReady = true;
});

socket.on('joined', function(room) {
  console.log('joined: ' + room);
  isChannelReady = true;
});

socket.on('log', function(array) {
  console.log.apply(console, array);
});

////////////////////////////////////////////////

function sendMessage(message) {
  console.log('Client sending message: ', message);
  socket.emit('message', message);
}

// This client receives a message
socket.on('message', function(message) {
  console.log('Client received message:', message);
  if (message === 'got user media') {
    maybeStart();
  } else if (message.type === 'offer') {
    if (!isInitiator && !isStarted) {
      maybeStart();
    }
    pc.setRemoteDescription(new RTCSessionDescription(message));
    doAnswer();
  } else if (message.type === 'answer' && isStarted) {
    pc.setRemoteDescription(new RTCSessionDescription(message));
  } else if (message.type === 'candidate' && isStarted) {
    var candidate = new RTCIceCandidate({
      sdpMLineIndex: message.label,
      candidate: message.candidate
    });
    pc.addIceCandidate(candidate);
  } else if (message === 'bye' && isStarted) {
    handleRemoteHangup();
  }
});

////////////////////////////////////////////////////

var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');

navigator.mediaDevices.getUserMedia({
  audio: false,
  video: true
})
.then(gotStream)
.catch(function(e) {
  alert('getUserMedia() error: ' + e.name);
});

function gotStream(stream) {
  console.log('Adding local stream.');
  localStream = stream;
  localVideo.srcObject = stream;
  sendMessage('got user media');
  if (isInitiator) {
    maybeStart();
  }
}

var constraints = {
  video: true
};

console.log('Getting user media with constraints', constraints);

if (location.hostname !== 'localhost') {
  requestTurn(
    'https://computeengineondemand.appspot.com/turn?username=41784574&key=4080218913'
  );
}

function maybeStart() {
  console.log('>>>>>>> maybeStart() ', isStarted, localStream, isChannelReady);
  if (!isStarted && typeof localStream !== 'undefined' && isChannelReady) {
    console.log('>>>>>> creating peer connection');
    createPeerConnection();
    pc.addStream(localStream);
    isStarted = true;
    console.log('isInitiator', isInitiator);
    if (isInitiator) {
      doCall();
    }
  }
}

window.onbeforeunload = function() {
  sendMessage('bye');
};

/////////////////////////////////////////////////////////

function createPeerConnection() {
  try {
    pc = new RTCPeerConnection(null);
    pc.onicecandidate = handleIceCandidate;
    pc.onaddstream = handleRemoteStreamAdded;
    pc.onremovestream = handleRemoteStreamRemoved;
    console.log('Created RTCPeerConnnection');
  } catch (e) {
    console.log('Failed to create PeerConnection, exception: ' + e.message);
    alert('Cannot create RTCPeerConnection object.');
    return;
  }
}

function handleIceCandidate(event) {
  console.log('icecandidate event: ', event);
  if (event.candidate) {
    sendMessage({
      type: 'candidate',
      label: event.candidate.sdpMLineIndex,
      id: event.candidate.sdpMid,
      candidate: event.candidate.candidate
    });
  } else {
    console.log('End of candidates.');
  }
}

function handleCreateOfferError(event) {
  console.log('createOffer() error: ', event);
}

function doCall() {
  console.log('Sending offer to peer');
  pc.createOffer(setLocalAndSendMessage, handleCreateOfferError);
}

function doAnswer() {
  console.log('Sending answer to peer.');
  pc.createAnswer().then(
    setLocalAndSendMessage,
    onCreateSessionDescriptionError
  );
}

function setLocalAndSendMessage(sessionDescription) {
  // Set Opus as the preferred codec in SDP if Opus is present.
  //  sessionDescription.sdp = preferOpus(sessionDescription.sdp);
  pc.setLocalDescription(sessionDescription);
  console.log('setLocalAndSendMessage sending message', sessionDescription);
  sendMessage(sessionDescription);
}

function onCreateSessionDescriptionError(error) {
  trace('Failed to create session description: ' + error.toString());
}

function requestTurn(turnURL) {
  var turnExists = false;
  for (var i in pcConfig.iceServers) {
    if (pcConfig.iceServers[i].urls.substr(0, 5) === 'turn:') {
      turnExists = true;
      turnReady = true;
      break;
    }
  }
  if (!turnExists) {
    console.log('Getting TURN server from ', turnURL);
    // No TURN server. Get one from computeengineondemand.appspot.com:
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
      if (xhr.readyState === 4 && xhr.status === 200) {
        var turnServer = JSON.parse(xhr.responseText);
        console.log('Got TURN server: ', turnServer);
        pcConfig.iceServers.push({
          'urls': 'turn:' + turnServer.username + '@' + turnServer.turn,
          'credential': turnServer.password
        });
        turnReady = true;
      }
    };
    xhr.open('GET', turnURL, true);
    xhr.send();
  }
}

function handleRemoteStreamAdded(event) {
  console.log('Remote stream added.');
  remoteStream = event.stream;
  remoteVideo.srcObject = remoteStream;
}

function handleRemoteStreamRemoved(event) {
  console.log('Remote stream removed. Event: ', event);
}

function hangup() {
  console.log('Hanging up.');
  stop();
  sendMessage('bye');
}

function handleRemoteHangup() {
  console.log('Session terminated.');
  stop();
  isInitiator = false;
}

function stop() {
  isStarted = false;
  pc.close();
  pc = null;
}

///////////////////////////////////////////

// Set Opus as the default audio codec if it's present.
function preferOpus(sdp) {
  var sdpLines = sdp.split('\r\n');
  var mLineIndex;
  // Search for m line.
  for (var i = 0; i < sdpLines.length; i++) {
    if (sdpLines[i].search('m=audio') !== -1) {
      mLineIndex = i;
      break;
    }
  }
  if (mLineIndex === null) {
    return sdp;
  }

  // If Opus is available, set it as the default in m line.
  for (i = 0; i < sdpLines.length; i++) {
    if (sdpLines[i].search('opus/48000') !== -1) {
      var opusPayload = extractSdp(sdpLines[i], /:(\d+) opus\/48000/i);
      if (opusPayload) {
        sdpLines[mLineIndex] = setDefaultCodec(sdpLines[mLineIndex],
          opusPayload);
      }
      break;
    }
  }

  // Remove CN in m line and sdp.
  sdpLines = removeCN(sdpLines, mLineIndex);

  sdp = sdpLines.join('\r\n');
  return sdp;
}

function extractSdp(sdpLine, pattern) {
  var result = sdpLine.match(pattern);
  return result && result.length === 2 ? result[1] : null;
}

// Set the selected codec to the first in m line.
function setDefaultCodec(mLine, payload) {
  var elements = mLine.split(' ');
  var newLine = [];
  var index = 0;
  for (var i = 0; i < elements.length; i++) {
    if (index === 3) { // Format of media starts from the fourth.
      newLine[index++] = payload; // Put target payload to the first.
    }
    if (elements[i] !== payload) {
      newLine[index++] = elements[i];
    }
  }
  return newLine.join(' ');
}

// Strip CN from sdp before CN constraints is ready.
function removeCN(sdpLines, mLineIndex) {
  var mLineElements = sdpLines[mLineIndex].split(' ');
  // Scan from end for the convenience of removing an item.
  for (var i = sdpLines.length - 1; i >= 0; i--) {
    var payload = extractSdp(sdpLines[i], /a=rtpmap:(\d+) CN\/\d+/i);
    if (payload) {
      var cnPos = mLineElements.indexOf(payload);
      if (cnPos !== -1) {
        // Remove CN payload from m line.
        mLineElements.splice(cnPos, 1);
      }
      // Remove CN line in sdp
      sdpLines.splice(i, 1);
    }
  }

  sdpLines[mLineIndex] = mLineElements.join(' ');
  return sdpLines;
}
<|endoftext|>