<commit_before>var Lab = require('lab');
var Code = require('code');

var server = require('../').hapi;

var lab = exports.lab = Lab.script();

var credentials = {
  id: 'john.doe',
  name: 'John Doe',
  roles: [{
    id: 'development-team',
    isTeamLeader: false
  }],
};

var eventA = {
  name: 'Super Festinha de anos do Chico',
};

var eventAId;

var changesToA = {
  name: 'Super Grande Festarrona de Anos do Chico'
};

lab.experiment('Event', function() {

  lab.test('Create', function(done) {
    var options = {
      method: 'POST',
      url: '/events',
      credentials: credentials,
      payload: eventA
    };

    server.inject(options, function(response) {
      var result = response.result;
      eventAid = result.id.toString();
      Code.expect(response.statusCode).to.equal(201);
      Code.expect(result).to.be.instanceof(Object);
      Code.expect(result.name).to.equal(eventA.name);

      done();
    });
  });

  lab.test('List all', function(done) {
    var options = {
      method: 'GET',
      url: '/events',
      credentials: credentials,
    };

    server.inject(options, function(response) {
      var result = response.result;

      Code.expect(response.statusCode).to.equal(200);
      Code.expect(result).to.be.instanceof(Array);
      Code.expect(result[0].name).to.be.name;
      done();
    });
  });

  lab.test('Get one', function(done) {
    var options = {
      method: 'GET',
      url: '/events/'+eventAid,
      credentials: credentials,
    };

    server.inject(options, function(response) {
      var result = response.result;

      Code.expect(response.statusCode).to.equal(200);
      Code.expect(result).to.be.instanceof(Object);
      Code.expect(result.name).to.equal(eventA.name);

      done();
    });
  });

  lab.test('Update', function(done) {
    var options = {
      method: 'PUT',
      url: '/events/'+eventAid,
      credentials: credentials,
      payload: changesToA
    };

    server.inject(options, function(response) {
      var result = response.result;

      Code.expect(response.statusCode).to.equal(200);
      Code.expect(result).to.be.instanceof(Object);
      Code.expect(result.name).to.equal(changesToA.name);

      done();
    });
  });

  lab.test('Delete', function(done) {
    var options = {
      method: 'DELETE',
      url: '/events/'+eventAid,
      credentials: credentials,
    };

    server.inject(options, function(response) {
      var result = response.result;

      Code.expect(response.statusCode).to.equal(200);
      Code.expect(result).to.be.instanceof(Object);
      Code.expect(result.name).to.equal(changesToA.name);
      done();
    });
  });

});<commit_msg>Fixed leak on eventAid<commit_after>var Lab = require('lab');
var Code = require('code');

var server = require('../').hapi;

var lab = exports.lab = Lab.script();

var credentials = {
  id: 'john.doe',
  name: 'John Doe',
  roles: [{
    id: 'development-team',
    isTeamLeader: false
  }],
};

var eventA = {
  name: 'Super Festinha de anos do Chico',
};

var eventAId;

var changesToA = {
  name: 'Super Grande Festarrona de Anos do Chico'
};

lab.experiment('Event', function() {

  lab.test('Create', function(done) {
    var options = {
      method: 'POST',
      url: '/events',
      credentials: credentials,
      payload: eventA
    };

    server.inject(options, function(response) {
      var result = response.result;
      eventAId = result.id.toString();
      Code.expect(response.statusCode).to.equal(201);
      Code.expect(result).to.be.instanceof(Object);
      Code.expect(result.name).to.equal(eventA.name);

      done();
    });
  });

  lab.test('List all', function(done) {
    var options = {
      method: 'GET',
      url: '/events',
      credentials: credentials,
    };

    server.inject(options, function(response) {
      var result = response.result;

      Code.expect(response.statusCode).to.equal(200);
      Code.expect(result).to.be.instanceof(Array);
      Code.expect(result[0].name).to.be.name;
      done();
    });
  });

  lab.test('Get one', function(done) {
    var options = {
      method: 'GET',
      url: '/events/'+eventAId,
      credentials: credentials,
    };

    server.inject(options, function(response) {
      var result = response.result;

      Code.expect(response.statusCode).to.equal(200);
      Code.expect(result).to.be.instanceof(Object);
      Code.expect(result.name).to.equal(eventA.name);

      done();
    });
  });

  lab.test('Update', function(done) {
    var options = {
      method: 'PUT',
      url: '/events/'+eventAId,
      credentials: credentials,
      payload: changesToA
    };

    server.inject(options, function(response) {
      var result = response.result;

      Code.expect(response.statusCode).to.equal(200);
      Code.expect(result).to.be.instanceof(Object);
      Code.expect(result.name).to.equal(changesToA.name);

      done();
    });
  });

  lab.test('Delete', function(done) {
    var options = {
      method: 'DELETE',
      url: '/events/'+eventAId,
      credentials: credentials,
    };

    server.inject(options, function(response) {
      var result = response.result;

      Code.expect(response.statusCode).to.equal(200);
      Code.expect(result).to.be.instanceof(Object);
      Code.expect(result.name).to.equal(changesToA.name);
      done();
    });
  });

});<|endoftext|>


<commit_before>'use strict';

const rethinkDB = require('rethinkdb');
const labyrinthDB = require('./labyrinthdb');

// https://github.com/websockets/ws/
const WebSocketServer = require('ws').Server;

/**
 *
 * @param message
 */
function log(message) {
  console.log(`${Date.now() / 1000}: ${message}`);
}

class UserDB extends labyrinthDB.LabyrinthDB {
  runDB () {
    this.locationMap = [];
    const _this = this;

    rethinkDB
      .table('startLocation', {readMode: 'outdated'})
      .run(this.conn, function(err, cursor) {
        if (err) throw err;

        cursor.toArray(function (err, res) {
          if (err) throw err;

          let i =0;
          for (; i < res.length; i++) {
            let e = res[i];

            if (_this.locationMap[e.y] === undefined) _this.locationMap[e.y] = [];
            _this.locationMap[e.y][e.x] = e.type;
          }

          log(`Map buffer is ready, ${i} elements.`);

          _this.startWebSocketServer();
          _this.readChanges();
        });
      });
  }

  readChanges () {
    const _this = this;

    rethinkDB
      .table('startLocation', {readMode: 'outdated'})
      .changes()
      .run(this.conn, function(err, cursor) {
        if (err) throw err;

        cursor.each(function(err, res) {
          if (err) throw err;

          if (res.old_val.type != res.new_val.type) {
            _this.locationMap[res.new_val.y][res.new_val.x] = res.new_val.type;
            log(`Inserted element ${JSON.stringify(res.new_val)}`);
          }
        });
      });
  }

  startWebSocketServer () {
    this.webAPI = new WebAPI(this);
  }
}

class WebAPI {
  constructor (cdb) {
    this.cdb = cdb;
    this.connPool = {};
    this.clientId = 0;

    // dictionary for variant of offset
    this.offsets = {
      'up': {'x': 0, 'y': -1},
      'down': {'x': 0, 'y': 1},
      'left': {'x': -1, 'y': 0},
      'right': {'x': 1, 'y': 0}
    };

    this.wss = new WebSocketServer({ port: 8081 });

    const _this = this;

    this.wss.broadcast = function broadcast(data) {
      if (_this.wss.clients.length) {
        data = JSON.stringify(data);
        log(`Send broadcast: ${data}`);
      }

      _this.wss.clients.forEach(function each(client) {
        client.send(data);
      });
    };

    this.wss.on('connection', function(ws) {
      // increment id counter
      const thisId = ++_this.clientId;
      // set up structure for this connection
      _this.connPool[thisId] = {};

      // we accept message from user!
      ws.on('message', function(rawMessage) {
        log(`Received: ${rawMessage}`);

        const message = JSON.parse(rawMessage);
        _this.connPool[thisId]['login'] = message.login;

        if (message.direction && 'position' in _this.connPool[thisId]) {
          // user would like changes his position and he has old position
          const new_position = _this.getNewPosition(_this.connPool[thisId]['position'], message.direction);

          if (new_position) {
            _this.connPool[thisId]['position'] = new_position;

            let resp = {'changePosition': new_position};
            resp.changePosition.login = message.login;

            _this.wss.broadcast(resp);
          }
        } else {
          /**
          * user would like changes his position and he has not old position
          * because is connecting at server just now
          */
          log(`New user!! ${message.login} ${thisId}`);

          const position = _this.searchStartPosition();
          _this.connPool[thisId]['position'] = position;

          let resp = {
            allMap: _this.cdb.locationMap,
            changePosition: {
              x: position.x,
              y: position.y,
              login: message.login
            }
          };

          resp = JSON.stringify(resp);

          log(`Send: ${resp}`);
          ws.send(resp);
        }
      });

      ws.on('close', function() {
        log(`Client disconnected: ${_this.connPool[thisId]['login']}`);
        delete _this.connPool[thisId];
      });

      ws.on('error', function(e) {
        log(`Client ${_this.connPool[thisId]['login']} error: ${e.message}`);
      });
    });

    log('Web API started');
  }

  /**
  * search start position for new user
  * @returns {{y: number, x: number}}
  */
  searchStartPosition () {
    for (let i = 0; i < this.cdb.locationMap.length; i++) {
      for (let ii = 0; ii < this.cdb.locationMap[i].length; ii++) {
        if (this.cdb.locationMap[i][ii] === 0) {
          return {'y': i, 'x': ii};
        }
      }
    }
  }

  /**
  *
  * @param curPosition {y, x} current user position
  * @param direction 'up', or 'down', or 'left', or 'right'
  * @returns {{y: *, x: *, direction: *}} new position and direction
  *          or undefined if block ahead
  * @constructor
  */
  getNewPosition(curPosition, direction) {
    const offset = this.offsets[direction];

    const newY = curPosition.y + offset.y;
    const newX = curPosition.x + offset.x;

    if (newX >= 0 && newX < this.cdb.locationMap.length && newY >= 0 && newY < this.cdb.locationMap.length) {
      if (this.cdb.locationMap[newY][newX] === 0) {
        return {
        'y': newY,
        'x': newX,
        'direction': direction};
      }
    }
  }
}

if (require.main === module) {
  rethinkDB.connect( {host: 'localhost', port: 28015}, function(err, conn) {
    if (err) throw err;

    const cdb = new UserDB(conn, 'labyrinth', ['userPosition']);
    cdb.initDB();
  });
}<commit_msg>Change insert logs<commit_after>'use strict';

const rethinkDB = require('rethinkdb');
const labyrinthDB = require('./labyrinthdb');

// https://github.com/websockets/ws/
const WebSocketServer = require('ws').Server;

/**
 *
 * @param message
 */
function log(message) {
  console.log(`${Date.now() / 1000}: ${message}`);
}

class UserDB extends labyrinthDB.LabyrinthDB {
  runDB () {
    this.locationMap = [];
    const _this = this;

    rethinkDB
      .table('startLocation', {readMode: 'outdated'})
      .run(this.conn, function(err, cursor) {
        if (err) throw err;

        cursor.toArray(function (err, res) {
          if (err) throw err;

          let i =0;
          for (; i < res.length; i++) {
            let e = res[i];

            if (_this.locationMap[e.y] === undefined) _this.locationMap[e.y] = [];
            _this.locationMap[e.y][e.x] = e.type;
          }

          log(`Map buffer is ready, ${i} elements.`);

          _this.startWebSocketServer();
          _this.readChanges();
        });
      });
  }

  readChanges () {
    const _this = this;

    rethinkDB
      .table('startLocation', {readMode: 'outdated'})
      .changes()
      .run(this.conn, function(err, cursor) {
        if (err) throw err;

        cursor.each(function(err, res) {
          if (err) throw err;

          if (res.old_val.type != res.new_val.type) {
            _this.locationMap[res.new_val.y][res.new_val.x] = res.new_val.type;
            log(`Inserted element ${JSON.stringify(res.new_val)}`);
          }
        });
      });
  }

  startWebSocketServer () {
    this.webAPI = new WebAPI(this);
  }
}

class WebAPI {
  constructor (cdb) {
    this.cdb = cdb;
    this.connPool = {};
    this.clientId = 0;

    // dictionary for variant of offset
    this.offsets = {
      'up': {'x': 0, 'y': -1},
      'down': {'x': 0, 'y': 1},
      'left': {'x': -1, 'y': 0},
      'right': {'x': 1, 'y': 0}
    };

    this.wss = new WebSocketServer({ port: 8081 });

    const _this = this;

    this.wss.broadcast = function broadcast(data) {
      if (_this.wss.clients.length) {
        data = JSON.stringify(data);
        log(`Send broadcast: ${data}`);
      }

      _this.wss.clients.forEach(function each(client) {
        client.send(data);
      });
    };

    this.wss.on('connection', function(ws) {
      // increment id counter
      const thisId = ++_this.clientId;
      // set up structure for this connection
      _this.connPool[thisId] = {};

      // we accept message from user!
      ws.on('message', function(rawMessage) {
        log(`Received: ${rawMessage}`);

        const message = JSON.parse(rawMessage);
        _this.connPool[thisId]['login'] = message.login;

        if (message.direction && 'position' in _this.connPool[thisId]) {
          // user would like changes his position and he has old position
          const new_position = _this.getNewPosition(_this.connPool[thisId]['position'], message.direction);

          if (new_position) {
            _this.connPool[thisId]['position'] = new_position;

            let resp = {'changePosition': new_position};
            resp.changePosition.login = message.login;

            _this.wss.broadcast(resp);
          }
        } else {
          /**
          * user would like changes his position and he has not old position
          * because is connecting at server just now
          */
          log(`New user!! ${message.login} ${thisId}`);

          const position = _this.searchStartPosition();
          _this.connPool[thisId]['position'] = position;

          let resp = {
            allMap: _this.cdb.locationMap,
            changePosition: {
              x: position.x,
              y: position.y,
              login: message.login
            }
          };

          resp = JSON.stringify(resp);

          log(`Send: ${resp}`);
          ws.send(resp);
        }
      });

      ws.on('close', this.close(thisId));

      ws.on('error', function(e) {
        log(`Client ${_this.connPool[thisId]['login']} error: ${e.message}`);
      });
    });

    log('Web API started');
  }

  close (userId) {
    log(`Client disconnected: ${this.connPool[userId]['login']}`);
    delete this.connPool[thisId];
  }

  /**
  * search start position for new user
  * @returns {{y: number, x: number}}
  */
  searchStartPosition () {
    for (let i = 0; i < this.cdb.locationMap.length; i++) {
      for (let ii = 0; ii < this.cdb.locationMap[i].length; ii++) {
        if (this.cdb.locationMap[i][ii] === 0) {
          return {'y': i, 'x': ii};
        }
      }
    }
  }

  /**
  *
  * @param curPosition {y, x} current user position
  * @param direction 'up', or 'down', or 'left', or 'right'
  * @returns {{y: *, x: *, direction: *}} new position and direction
  *          or undefined if block ahead
  * @constructor
  */
  getNewPosition(curPosition, direction) {
    const offset = this.offsets[direction];

    const newY = curPosition.y + offset.y;
    const newX = curPosition.x + offset.x;

    if (newX >= 0 && newX < this.cdb.locationMap.length && newY >= 0 && newY < this.cdb.locationMap.length) {
      if (this.cdb.locationMap[newY][newX] === 0) {
        return {
        'y': newY,
        'x': newX,
        'direction': direction};
      }
    }
  }
}

if (require.main === module) {
  rethinkDB.connect( {host: 'localhost', port: 28015}, function(err, conn) {
    if (err) throw err;

    const cdb = new UserDB(conn, 'labyrinth', ['userPosition']);
    cdb.initDB();
  });
}<|endoftext|>


<commit_before>/*
 Websocket handler module
 */

const _ = require('underscore'),
	caps = require('./caps'),
    common = require('../common/index'),
    events = require('events'),
	hooks = require('../util/hooks'),
    Muggle = require('../util/etc').Muggle,
    state = require('./state'),
    util = require('util'),
    winston = require('winston')

export const dispatcher = {}

/**
 * Websocket message parser and connection handler
 */
export class Client extends events.EventEmitter {
	/**
	 * Create websocket client
	 * @param {net.Socket} socket
	 * @param {string} ip
	 */
	constructor(socket, ip) {
		super()
		this.socket = socket
		this.ident = caps.lookup_ident(ip)
		state.clients.add(this)
		state.countIPs()
	}

	/**
	 * Strigify and send message through websocket
	 * @param {Array} msg
	 */
	send(msg) {
		this.socket.write(JSON.stringify([msg]))
	}

	//TODO: Logic for redirecting from dead threads

	/**
	 * Parse incomming websocket message and dispatch to appropriate handler
	 * @param {string} data
	 */
	onMessage(data) {
		if (this.ident.ban)
			return
		let msg
		try {
			msg = JSON.parse(msg)
		}
		catch (e) {
			winston.warn('Unparsable websocket message:', data)
		}
		let type
		if (msg) {
			// Less overhead for sending post text body fragmets.
			if (this.post && typeof msg == 'string')
				type = common.UPDATE_POST
			else if (msg instanceof Array)
				type = msg.shift()
		}
		if (!this.synced && type !== common.SYNCHRONIZE)
			type = null
		const func = dispatcher[type]
		if (!func || !func(msg, this)) {
			this.disconnect(Muggle('Bad protocol:',
				new Error('Invalid message: ' + JSON.stringify(data))))
		}
	}

	/**
	 * Close all client listers on socket close
	 */
	onClose() {
		const {ip} = this.ident
		state.clients.delete(this)
		this.synced = false
		if (this.db)
			this.db.disconnect()
		this.emit('close')
	}

	/**
	 * Forcefully disconnect client due to error
	 * @param {(Muggle|Error)} error
	 */
	disconnect(error) {
		let msg = 'Server error'
		if (error instanceof Muggle) {
			msg = error.most_precise_error_message()
			error = error.deepest_reason()
		}
		winston.error(`Error by ${JSON.stringify(this.ident)}: ${error || msg}`)
		this.send([common.INVALID, msg])
		this.synced = false
	}
}

/**
 * Rescan connected client permissions and disconnect, if needed
 */
function scan_client_caps() {
	for (let client of state.clients) {
		if (!caps.lookup_ident(client.ident.ip).ban)
			continue
		client.ident.ban = true
		client.disconnect(Muggle('Banned'))
	}
}
exports.scan_client_caps = scan_client_caps

/**
 * Push a message to all connected websocket clients
 * @param {*} msg
 */
export function push(msg){
	for (let client of _.values(state.clients)) {
		try {
			client.send(msg)
		}
		catch(e){
			// Client died, but we don't care
		}
	}
}

/**
 * Returns online count message
 * @param {int} count
 * @returns {*[]}
 */
function countMessage(count) {
	return [0, common.ONLINE_COUNT, count]
}

/**
 * Push online count on client connection
 */
hooks.hook('clientSynced', (info, cb) => {
	info.client.send(countMessage(state.IPCount))
	cb(null)
})

/**
 * Push online count update to all clients on change
 */
state.emitter.on('change:clientCount', count => push(countMessage(count)))
<commit_msg>Fix: Broken refference<commit_after>/*
 Websocket handler module
 */

const _ = require('underscore'),
	caps = require('./caps'),
    common = require('../common/index'),
    events = require('events'),
	hooks = require('../util/hooks'),
    Muggle = require('../util/etc').Muggle,
    state = require('./state'),
    util = require('util'),
    winston = require('winston')

export const dispatcher = {}

/**
 * Websocket message parser and connection handler
 */
export class Client extends events.EventEmitter {
	/**
	 * Create websocket client
	 * @param {net.Socket} socket
	 * @param {string} ip
	 */
	constructor(socket, ip) {
		super()
		this.socket = socket
		this.ident = caps.lookUpIdent(ip)
		state.clients.add(this)
		state.countIPs()
	}

	/**
	 * Strigify and send message through websocket
	 * @param {Array} msg
	 */
	send(msg) {
		this.socket.write(JSON.stringify([msg]))
	}

	//TODO: Logic for redirecting from dead threads

	/**
	 * Parse incomming websocket message and dispatch to appropriate handler
	 * @param {string} data
	 */
	onMessage(data) {
		if (this.ident.ban)
			return
		let msg
		try {
			msg = JSON.parse(msg)
		}
		catch (e) {
			winston.warn('Unparsable websocket message:', data)
		}
		let type
		if (msg) {
			// Less overhead for sending post text body fragmets.
			if (this.post && typeof msg == 'string')
				type = common.UPDATE_POST
			else if (msg instanceof Array)
				type = msg.shift()
		}
		if (!this.synced && type !== common.SYNCHRONIZE)
			type = null
		const func = dispatcher[type]
		if (!func || !func(msg, this)) {
			this.disconnect(Muggle('Bad protocol:',
				new Error('Invalid message: ' + JSON.stringify(data))))
		}
	}

	/**
	 * Close all client listers on socket close
	 */
	onClose() {
		const {ip} = this.ident
		state.clients.delete(this)
		this.synced = false
		if (this.db)
			this.db.disconnect()
		this.emit('close')
	}

	/**
	 * Forcefully disconnect client due to error
	 * @param {(Muggle|Error)} error
	 */
	disconnect(error) {
		let msg = 'Server error'
		if (error instanceof Muggle) {
			msg = error.most_precise_error_message()
			error = error.deepest_reason()
		}
		winston.error(`Error by ${JSON.stringify(this.ident)}: ${error || msg}`)
		this.send([common.INVALID, msg])
		this.synced = false
	}
}

/**
 * Rescan connected client permissions and disconnect, if needed
 */
function scan_client_caps() {
	for (let client of state.clients) {
		if (!caps.lookUpIdent(client.ident.ip).ban)
			continue
		client.ident.ban = true
		client.disconnect(Muggle('Banned'))
	}
}
exports.scan_client_caps = scan_client_caps

/**
 * Push a message to all connected websocket clients
 * @param {*} msg
 */
export function push(msg){
	for (let client of _.values(state.clients)) {
		try {
			client.send(msg)
		}
		catch(e){
			// Client died, but we don't care
		}
	}
}

/**
 * Returns online count message
 * @param {int} count
 * @returns {*[]}
 */
function countMessage(count) {
	return [0, common.ONLINE_COUNT, count]
}

/**
 * Push online count on client connection
 */
hooks.hook('clientSynced', (info, cb) => {
	info.client.send(countMessage(state.IPCount))
	cb(null)
})

/**
 * Push online count update to all clients on change
 */
state.emitter.on('change:clientCount', count => push(countMessage(count)))
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var orderSchema = new Schema(
  {
    userId:
    {
      type: mongoose.Schema.Types.ObjectId,
      ref : 'user',
      required : true
    },
    productId:
    {
      type: mongoose.Schema.Types.ObjectId,
      ref : 'boot',
      required : true
    },
    deliveryDate:
    {
      type : Date,
      required : true
    },
    status :
    {
      type : Boolean,
      default : false,
      required : true
    }
  },
  {
    timestamps : true
  }
);
orderSchema.plugin(passportLocalMongoose);
var Pro = mongoose.model('order',orderSchema);
module.exports=Pro;
<commit_msg>Updated order schema.<commit_after>var mongoose = require('mongoose');
var Schema = mongoose.Schema;
require('mongoose-currency').loadType(mongoose);
var passportLocalMongoose = require('passport-local-mongoose');
var Currency = mongoose.Types.Currency;
var productSchema = new Schema(
    {
        productId:
            {
                type: mongoose.Schema.Types.ObjectId,
                ref : 'boot',
                required : true
            },
        quantity: Number,
        salecost : Currency,
        profit : Currency
    }
);
var orderSchema = new Schema(
  {
    userId:
    {
      type: mongoose.Schema.Types.ObjectId,
      ref : 'user',
      required : true
    },
    product : [productSchema],
    deliveryDate:
    {
      type : Date,
      required : true
    },
    status :
    {
      type : Boolean,
      default : false,
      required : true
    }
  },
  {
    timestamps : true
  }
);
orderSchema.plugin(passportLocalMongoose);
var Pro = mongoose.model('order',orderSchema);
module.exports=Pro;
<|endoftext|>


<commit_before>
'use strict';

var ExpeditionEditController = function($scope, $controller, $routeParams, Expedition, formula,
  formulaAutoCompleteService, npdcAppConfig, chronopicService, fileFunnelService, NpolarLang,
  npolarApiConfig, NpolarApiSecurity, NpolarMessage, npolarCountryService) {
  'ngInject';

  // EditController -> NpolarEditController
  $controller('NpolarEditController', {
    $scope: $scope
  });

  // Inventory -> npolarApiResource -> ngResource
  $scope.resource = Expedition;

  let templates = [{
      match: "people_item",
      template: '<npdc:formula-person></npdc:formula-person>'
    }, {
      match: "placenames_item",
      template: '<npdc:formula-placename></npdc:formula-placename>'
    },
    {
      match: "coverage_item",
      template: "<inventory:coverage></inventory:coverage>"
    }
  ];

  let i18n = [{
      map: require('./en.json'),
      code: 'en'
    },
    {
      map: require('./no.json'),
      code: 'nb_NO',
    }];

    $scope.formula = formula.getInstance({
       schema: '//api.npolar.no/schema/expedition-1',
       form: 'edit/formula.json',
       language: NpolarLang.getLang(),
       templates:  npdcAppConfig.formula.templates.concat(templates),
       languages: npdcAppConfig.formula.languages.concat(i18n)
    });

  formulaAutoCompleteService.autocompleteFacets(['people.first_name',
    'people.last_name', 'links.type', 'tags'], Expedition, $scope.formula);

  chronopicService.defineOptions({ match: 'released', format: '{date}'});
  chronopicService.defineOptions({ match(field) {
    return field.path.match(/^#\/activity\/\d+\/.+/);
  }, format: '{date}'});

  /* let dataLinkSuccess = function (file) {
    return {
      rel: 'DATA',
      href: file.url,
      title: file.filename,
      length: file.file_size,
      hash: [file.md5sum],
      type: file.content_type
    };
  }; */

 /*  let fileToValueMapper = function (file) {
      return {
        rel: 'data',
        href: file.url,
        title: file.filename,
        length: file.file_size,
        hash: [file.md5sum],
        type: file.content_type
      };
    };

    let valueToFileMapper = function (value) {
      if (value.rel !== 'data') {
        return null;
      }
      return {
        filename: value.title,
        file_size: value.length,
        url: value.href
      };
    };

  fileFunnelService.fileUploader({
    match(field) {
      return field.id === "links" && field.instance === "data";
    },
    multiple: true,
    server: 'https://apptest.data.npolar.no:3000/inventory/:id/_file/',
    fileToValueMapper, valueToFileMapper, fields: ['rel']}, $scope.formula);

  fileFunnelService.fileUploader({
    match(field) {
       return field.id === "links" && field.instance === "data";
    },
    server: 'https://apptest.data.npolar.no:3000/inventory/:id/_file/',
    successCallback: dataLinkSuccess,
    filterValues: function (value) {
      return value.rel.toUpperCase() === 'DATA';
    },
    restricted: false
  }, $scope.formula); */

  function initFileUpload(formula) {

    let server = `${NpolarApiSecurity.canonicalUri($scope.resource.path)}/:id/_file`;
    fileFunnelService.fileUploader({
      match(field) {
        return field.id === "files";
      },
      server,
      multiple: true,
      progress: false,
      restricted: function () {
        return !formula.getModel().license;
      },
      fileToValueMapper: Expedition.fileObject,
      valueToFileMapper: Expedition.hashiObject,
      fields: [] // 'type', 'hash'
    }, formula);
  }

  try {
    initFileUpload($scope.formula);
    // edit (or new) action
    $scope.edit();
  } catch (e) {
    NpolarMessage.error(e);
  }
};

module.exports = ExpeditionEditController;<commit_msg>norw text ok edit<commit_after>
'use strict';

var ExpeditionEditController = function($scope, $controller, $routeParams, Expedition, formula,
  formulaAutoCompleteService, npdcAppConfig, chronopicService, fileFunnelService, NpolarLang,
  npolarApiConfig, NpolarApiSecurity, NpolarMessage, npolarCountryService) {
  'ngInject';

  // EditController -> NpolarEditController
  $controller('NpolarEditController', {
    $scope: $scope
  });

  // Inventory -> npolarApiResource -> ngResource
  $scope.resource = Expedition;

  let templates = [{
      match: "people_item",
      template: '<npdc:formula-person></npdc:formula-person>'
    }, {
      match: "placenames_item",
      template: '<npdc:formula-placename></npdc:formula-placename>'
    },
    {
      match: "coverage_item",
      template: "<inventory:coverage></inventory:coverage>"
    }
  ];

  $scope.$on("npolar-lang", function(o, n) {

    console.log("LANG", o, n);
  });

  let i18n = [{
      map: require('./en.json'),
      code: 'en'
    },
    {
      map: require('./no.json'),
      code: 'nb_NO',
    }];

    $scope.formula = formula.getInstance({
       schema: '//api.npolar.no/schema/expedition-1',
       form: 'edit/formula.json',
       language: NpolarLang.getLang(),
       templates:  npdcAppConfig.formula.templates.concat(templates),
       languages: npdcAppConfig.formula.languages.concat(i18n)
    });

  formulaAutoCompleteService.autocompleteFacets(['people.first_name',
    'people.last_name', 'links.type', 'tags'], Expedition, $scope.formula);

  chronopicService.defineOptions({ match: 'released', format: '{date}'});
  chronopicService.defineOptions({ match(field) {
    return field.path.match(/^#\/activity\/\d+\/.+/);
  }, format: '{date}'});

  /* let dataLinkSuccess = function (file) {
    return {
      rel: 'DATA',
      href: file.url,
      title: file.filename,
      length: file.file_size,
      hash: [file.md5sum],
      type: file.content_type
    };
  }; */

 /*  let fileToValueMapper = function (file) {
      return {
        rel: 'data',
        href: file.url,
        title: file.filename,
        length: file.file_size,
        hash: [file.md5sum],
        type: file.content_type
      };
    };

    let valueToFileMapper = function (value) {
      if (value.rel !== 'data') {
        return null;
      }
      return {
        filename: value.title,
        file_size: value.length,
        url: value.href
      };
    };

  fileFunnelService.fileUploader({
    match(field) {
      return field.id === "links" && field.instance === "data";
    },
    multiple: true,
    server: 'https://apptest.data.npolar.no:3000/inventory/:id/_file/',
    fileToValueMapper, valueToFileMapper, fields: ['rel']}, $scope.formula);

  fileFunnelService.fileUploader({
    match(field) {
       return field.id === "links" && field.instance === "data";
    },
    server: 'https://apptest.data.npolar.no:3000/inventory/:id/_file/',
    successCallback: dataLinkSuccess,
    filterValues: function (value) {
      return value.rel.toUpperCase() === 'DATA';
    },
    restricted: false
  }, $scope.formula); */

  function initFileUpload(formula) {

    let server = `${NpolarApiSecurity.canonicalUri($scope.resource.path)}/:id/_file`;
    fileFunnelService.fileUploader({
      match(field) {
        return field.id === "files";
      },
      server,
      multiple: true,
      progress: false,
      restricted: function () {
        return !formula.getModel().license;
      },
      fileToValueMapper: Expedition.fileObject,
      valueToFileMapper: Expedition.hashiObject,
      fields: [] // 'type', 'hash'
    }, formula);
  }

  try {
    initFileUpload($scope.formula);
    // edit (or new) action
    $scope.edit();
  } catch (e) {
    NpolarMessage.error(e);
  }
};

module.exports = ExpeditionEditController;<|endoftext|>


<commit_before>//`new Function(...){`
if(local.log_dir){
    api_text(ret, api, local, req, res, next)
} else {// directoty is not ready
    setTimeout(api_text, 1024, ret, api, local, req, res, next)
}
return true// async anyway
//`}`

/*
 * Event(s):
 *   broadcast: 'chatmsg@um'
 *
 * Developed, tested, debugged using reload `view.Window` tool button
 **/

function api_text(ret, api, local, req, res, next){
var d, f

    if(!local.log_dir){
        throw new Error('Chat: no `log_dir` available')// handled by `connect`
    }

    if('GET' == req.method && req.url.query.file){
    //'http://localhost:3007/um/lib/chat/text?file=2014-07'
        /* lame direct eating memory way :
         *fs.readFile(local.log_dir + '/' + req.url.query.file + '.txt',
         *   function (err, data){
         *       if(err) throw new Error(err)
         *       res.txt(data)
         *   }
        )*/
        api.connect.sendFile(
            local.log_dir + '/' + req.url.query.file + '.txt', true
        )(req, res)// call middleware
        return
    }
    // POST `req.txt`: <olecom>{\t}a simple chat message. (with some html around)
    //'http://localhost:3007/um/lib/chat/text'

    d = new Date()
    f = '/' + d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '.txt'
    if(f != local.log_file_name){// new date, new file
        local.log_file_name = f
        if(local.log_file){// current log file is opened
            local.log_file.end(null,
                function on_close_log_file(err){
                    if(err) next(err)
                    return open_log()
                }
            )
        } else {
            return open_log()
        }
    }
    append_log()

    return

    function open_log(){
        local.log_file = local.require.fs.createWriteStream(
            local.log_dir + local.log_file_name,
            { flags: 'a+' }// read && append
        )
        local.log_file.on('open', append_log)
        local.log_file.on('error',
            function on_error_log_file(err){
                local.log_file = null
                next(err)
                local.log_file.end()
            }
        )
    }

    function append_log(){// limit is '4mb' in `app_main\lib\middleware\postTextPlain.js`
    var msg = JSON.stringify(req.json) + '\n'

        api.wes.broadcast('chatmsg@um', msg)
        local.log_file.write('{"d":"' + d.toISOString() + '",' + msg.slice(1))

        ret.success = true
        return res.json(ret)
    }

    function pad(n){
        return n < 10 ? '0' + n : n
    }
}
<commit_msg>um/lib/chat_text: more style for returns<commit_after>//`new Function(...){`
if(local.log_dir){
    api_text(ret, api, local, req, res, next)
} else {// directoty is not ready
    setTimeout(api_text, 1024, ret, api, local, req, res, next)
}
return true// async anyway
//`}`

/*
 * Event(s):
 *   broadcast: 'chatmsg@um'
 *
 * Developed, tested, debugged using reload `view.Window` tool button
 **/

function api_text(ret, api, local, req, res, next){
var d, f

    if(!local.log_dir){
        throw new Error('Chat: no `log_dir` available')// handled by `connect`
    }

    if('GET' == req.method && req.url.query.file){
    //'http://localhost:3007/um/lib/chat/text?file=2014-07'
        /* lame direct eating memory way :
         *fs.readFile(local.log_dir + '/' + req.url.query.file + '.txt',
         *   function (err, data){
         *       if(err) throw new Error(err)
         *       res.txt(data)
         *   }
        )*/
        return api.connect.sendFile(
            local.log_dir + '/' + req.url.query.file + '.txt', true
        )(req, res)// call middleware
    }
    // POST `req.txt`: <olecom>{\t}a simple chat message. (with some html around)
    //'http://localhost:3007/um/lib/chat/text'

    d = new Date()
    f = '/' + d.getUTCFullYear() + '-' + pad(d.getUTCMonth() + 1) + '.txt'
    if(f != local.log_file_name){// new date, new file
        local.log_file_name = f
        if(local.log_file){// current log file is opened
            local.log_file.end(null,
                function on_close_log_file(err){
                    if(err) next(err)
                    return open_log()
                }
            )
        } else {
            return open_log()
        }
    }
    return append_log()

    function open_log(){
        local.log_file = local.require.fs.createWriteStream(
            local.log_dir + local.log_file_name,
            { flags: 'a+' }// read && append
        )
        local.log_file.on('open', append_log)
        local.log_file.on('error',
            function on_error_log_file(err){
                local.log_file = null
                next(err)
                local.log_file.end()
            }
        )
    }

    function append_log(){// limit is '4mb' in `app_main\lib\middleware\postTextPlain.js`
    var msg = JSON.stringify(req.json) + '\n'

        api.wes.broadcast('chatmsg@um', msg)
        local.log_file.write('{"d":"' + d.toISOString() + '",' + msg.slice(1))

        ret.success = true
        return res.json(ret)
    }

    function pad(n){
        return n < 10 ? '0' + n : n
    }
}
<|endoftext|>


<commit_before>(function (Promise) {
  "use strict";
  if (!("Org" in self && "WebRtc" in Org))
    return;

  function copy(target, source) {
    for (const key in source)
      target[key] = source[key];
    return target;
  }

  // initialize api
  Org.WebRtc.WinJSHooks.initialize();

  const media = Org.WebRtc.Media.createMedia();
  media.setAudioOutputDevice("default");

  MediaStream = Org.WebRtc.MediaStream;

  navigator.mediaDevices.enumerateDevices = function () {
    return Promise.resolve(media.enumerateDevices());
  };

  function createMediaConstraints(constraints) {
    const result = new Org.WebRtc.MediaTrackConstraints;
    if ("optional" in constraints || "mandatory" in constraints) {
      if ("mandatory" in constraints)
        for (const key in constraints.mandatory)
          result.addMandatory(key, constraints.mandatory[key]);
      if ("optional" in constraints)
        for (const constraint of constraints.optional)
          for (const key in constraint)
            result.addOptional(key, constraint[key]);
    } else
      copy(result, constraints);
    return result;
  }

  const { StreamingCaptureMode } = Windows.Media.Capture;

  function CaptureMode(audio, video) {
    if (audio)
      if (video)
        return StreamingCaptureMode.audioAndVideo;
      else
        return StreamingCaptureMode.audio;
    else
      return StreamingCaptureMode.video;
  }

  class PermissionDeniedError extends Error {
    constructor(message) {
      super(message);
      this.name = "PermissionDeniedError";
    }
  }

  const capturePromises = [null, null, null];
  let lastCapturePromise = null;
  navigator.mediaDevices.getUserMedia = function (constraints) {
    return new Promise((resolve, reject) => {
      var constraintsOverride = new Org.WebRtc.RTCMediaStreamConstraints();
      constraintsOverride.audioEnabled = constraints.audio;
      constraintsOverride.videoEnabled = constraints.video;
      if (typeof constraints.audio === "object")
        constraintsOverride.audio = createMediaConstraints(constraints.audio);
      if (typeof constraints.video === "object")
        constraintsOverride.video = createMediaConstraints(constraints.video);

      // ask for permission to access microphone/camera
      const audio = constraintsOverride.audioEnabled && !capturePromises[StreamingCaptureMode.audio];
      const video = constraintsOverride.videoEnabled && !capturePromises[StreamingCaptureMode.video];
      if (audio || video) {
        const requestPermission = () => {
          const captureInitSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
          captureInitSettings.streamingCaptureMode = CaptureMode(audio, video);
          const mediaCapture = new Windows.Media.Capture.MediaCapture();
          return mediaCapture.initializeAsync(captureInitSettings).then(() => {
            mediaCapture.close();
          }, e => {
            console.error("Failed to obtain media access permission:", e.message);
            throw new PermissionDeniedError;
          });
        };
        lastCapturePromise = lastCapturePromise ? lastCapturePromise.then(requestPermission, requestPermission) : requestPermission();
        if (audio)
          capturePromises[StreamingCaptureMode.audio] = lastCapturePromise;
        if (video)
          capturePromises[StreamingCaptureMode.video] = lastCapturePromise;
        if (capturePromises[StreamingCaptureMode.audio] && capturePromises[StreamingCaptureMode.video])
          capturePromises[StreamingCaptureMode.audioAndVideo] = Promise.all(capturePromises);
      }
      resolve(capturePromises[CaptureMode(constraintsOverride.audioEnabled, constraintsOverride.videoEnabled)].then(() => media.getUserMedia(constraintsOverride)));
    });
  };
  navigator.getUserMedia = function (constraints, successCallback, errorCallback) {
    navigator.mediaDevices.getUserMedia(constraints).then(successCallback, errorCallback);
  };

  self.RTCIceCandidate = function (candidate) {
    if (candidate !== undefined) {
      this.candidate = candidate.candidate;
      this.sdpMid = candidate.sdpMid;
      this.sdpMLineIndex = candidate.sdpMLineIndex;
    }
  };

  self.RTCSessionDescription = function (message) {
    var sdpType;
    if (message.type == 'offer') {
      sdpType = Org.WebRtc.RTCSdpType.offer;
    }
    else if (message.type == 'answer') {
      sdpType = Org.WebRtc.RTCSdpType.answer;
    }
    else {
      sdpType = Org.WebRtc.RTCSdpType.pranswer;
    }
    return new Org.WebRtc.RTCSessionDescription(sdpType, message.sdp);
  };

  function attachMediaStream(element, stream) {
    if (stream.getVideoTracks().length > 0) {
      const videoTrack = stream.getVideoTracks().first().current;
      var streamSource = media.createMediaSource(videoTrack, stream.id);
      var mediaSource = Windows.Media.Core.MediaSource.createFromIMediaSource(streamSource);
      var mediaPlaybackItem = new Windows.Media.Playback.MediaPlaybackItem(mediaSource);
      var playlist = new Windows.Media.Playback.MediaPlaybackList();
      playlist.items.append(mediaPlaybackItem);
      element.msRealTime = true;
      element.src = URL.createObjectURL(playlist, { oneTimeOnly: true });
    }
  }

  function convertToRTCOfferAnswerOptions(options) {
    const result = copy(new Org.WebRtc.RTCOfferAnswerOptions, options);
    const optionKeys = {
      OfferToReceiveAudio: "offerToReceiveAudio",
      OfferToReceiveVideo: "offerToReceiveVideo",
      VoiceActivityDetection: "voiceActivityDetection",
      IceRestart: "iceRestart"
    };
    for (const key in options.mandatory)
      result[optionKeys[key]] = options.mandatory[key];
    if ("optional" in options)
      for (const constraint of options.optional)
        for (const key in constraint)
          result[optionKeys[key]] = constraint[key];
    return result;
  }

  class RTCPeerConnection {
    constructor(pcConfig, pcConstraints) {
      //Todo: do we need to implement pcConstraints in C++/CX API?
      var winrtConfig = new Org.WebRtc.RTCConfiguration();
      if (pcConfig.iceServers && pcConfig.iceServers.length > 0) {
        var iceServer = new Org.WebRtc.RTCIceServer();
        if (pcConfig.iceServers[0].urls != null) {
          iceServer.url = pcConfig.iceServers[0].urls[0];
        } else {
          iceServer.url = pcConfig.iceServers[0].url;
        }
        iceServer.credential = pcConfig.iceServers[0].credential;
        iceServer.username = pcConfig.iceServers[0].username;
        winrtConfig.iceServers = [];
        winrtConfig.iceServers.push(iceServer);

      }

      this._nativePC = new Org.WebRtc.RTCPeerConnection(winrtConfig);
    }
    createOffer(...args) {
      let options = null;
      if (args.length > 0 && typeof args[0] === "object")
        options = args[0];
      else if (args.length > 2)
        options = args[2];
      const p = new Promise((resolve, reject) => {
        var winrtOptions = options ? convertToRTCOfferAnswerOptions(options) : null;
        resolve(this._nativePC.createOffer(winrtOptions).then(function (offerSDP) {
          var newOfferSDP = {};
          // HACK: This is a hack to force VP8 while we're waiting for VP9 to be
          // fully implemented.
          newOfferSDP.sdp = offerSDP.sdp.replace(' 101 100', ' 100 101');
          switch (offerSDP.type) {
            case 0: newOfferSDP.type = 'offer'; break;
            case 1: newOfferSDP.type = 'pranswer'; break;
            case 2: newOfferSDP.type = 'answer'; break;
            default: throw 'invalid offer type';
          }
          return newOfferSDP;
        }));
      });
      if (args.length > 0 && typeof args[0] === "function") {
        p.then(args[0], args[1]);
        return Promise.resolve(undefined);
      } else
        return p;
    }
    createAnswer(...args) {
      let options = null;
      if (args.length > 0 && typeof args[0] === "object")
        options = args[0];
      else if (args.length > 2)
        options = args[2];
      const p = new Promise((resolve, reject) => {
        var winrtOptions = options ? convertToRTCOfferAnswerOptions(options) : null;
        resolve(this._nativePC.createAnswer(winrtOptions).then(function (answerSDP) {
          var newAnswerSDP = {};
          // HACK: This is a hack to force VP8 while we're waiting for VP9 to be
          // fully implemented.
          newAnswerSDP.sdp = answerSDP.sdp.replace(' 101 100', ' 100 101');
          switch (answerSDP.type) {
            case 0: newAnswerSDP.type = 'offer'; break;
            case 1: newAnswerSDP.type = 'pranswer'; break;
            case 2: newAnswerSDP.type = 'answer'; break;
            default: throw 'invalid offer type';
          }
          return newAnswerSDP;
        }));
      });
      if (args.length > 0 && typeof args[0] === "function") {
        p.then(args[0], args[1]);
        return Promise.resolve(undefined);
      } else
        return p;
    }
    setLocalDescription(desc) {
      return new Promise((resolve, reject) => {
        var winrtSDP = new RTCSessionDescription(desc);
        resolve(this._nativePC.setLocalDescription(winrtSDP));
      });
    }
    get localDescription() {
      return this._nativePC.localDescription;
    }
    setRemoteDescription(desc) {
      return new Promise((resolve, reject) => {
        // HACK: This is a hack to force VP8 while we're waiting for VP9 to be
        // fully implemented.
        var winrtSDP = desc;
        winrtSDP.sdp = desc.sdp.replace(' 101 100', ' 100 101');
        resolve(this._nativePC.setRemoteDescription(winrtSDP));
      });
    }
    get remoteDescription() {
      return this._nativePC.remoteDescription;
    }
    getConfiguration() {
      return this._nativePC.getConfiguration();
    }
    getLocalStreams() {
      return this._nativePC.getLocalStreams();
    }
    getRemoteStreams() {
      return this._nativePC.getRemoteStreams();
    }
    getStreamById(id) {
      return this._nativePC.getStreamById(id);
    }
    addStream(stream) {
      return this._nativePC.addStream(stream);
    }
    removeStream(stream) {
      return this._nativePC.removeStream(stream);
    }
    createDataChannel(label, init) {
      return this._nativePC.createDataChannel(label, init);
    }
    addIceCandidate(candidate) {
      return new Promise((resolve, reject) => {
        var nativeCandidate = new Org.WebRtc.RTCIceCandidate(candidate.candidate,
          (typeof candidate.sdpMid !== 'undefined') ? candidate.sdpMid : '', candidate.sdpMLineIndex);
        resolve(this._nativePC.addIceCandidate(nativeCandidate));
      });
    }
    get signalingState() {
      return [
        "stable",
        "have-local-offer",
        "have-local-pranswer",
        "have-remote-offer",
        "have-remote-pranswer",
        "closed"
      ][this._nativePC.signalingState];
    }
    get iceGatheringState() {
      return [
        "new",
        "gathering",
        "complete"
      ][this._nativePC.iceGatheringState];
    }
    get iceConnectionState() {
      return [
        "new",
        "checking",
        "connected",
        "completed",
        "failed",
        "disconnected",
        "closed"
      ][this._nativePC.iceConnectionState];
    }
    close() {
      return this._nativePC.close();
    }
    getStats() {
      return new Promise((resolve, reject) => {
        // this._nativePC.addEventListener("rtcstatsreportsready", function onrtcstatsreportsready(event) {
        //   this.removeEventListener("rtcstatsreportsready", onrtcstatsreportsready);
        //   this._nativePC.rtcStatsEnabled = false;
        //   resolve(event.rtcStatsReports);
        // });
        // this._nativePC.rtcStatsEnabled = true;
      });
    }
  }
  for (const type of [
    "negotiationneeded",
    "icecandidate",
    "signalingstatechange",
    "iceconnectionstatechange",
    "icegatheringstatechange",
    "datachannel",
    "addstream",
    "removestream"
  ]) {
    const prop = "on" + type;
    Object.defineProperty(RTCPeerConnection.prototype, prop, {
      configurable: true,
      enumerable: true,
      get() {
        return this._nativePC[prop];
      },
      set(value) {
        if (value)
          this._nativePC[prop] = event => value.call(this, event);
        else
          this._nativePC[prop] = null;
      }
    });
  }
  self.RTCPeerConnection = RTCPeerConnection;

  const origCreateObjectURL = URL.createObjectURL;
  URL.createObjectURL = function (object) {
    if (object instanceof MediaStream)
      return "webrtc:";
    return origCreateObjectURL.apply(this, arguments);
  };
  const origRevokeObjectURL = URL.revokeObjectURL;
  URL.revokeObjectURL = function (url) {
    if (url === "webrtc:")
      return;
    return origRevokeObjectURL.apply(this, arguments);
  };
  HTMLMediaElement.prototype.setSinkId = function (sinkId) {
    return Promise.resolve(media.setAudioOutputDevice(sinkId));
  };
  AudioContext.prototype.createMediaStreamSource = function (mediaStream) {
    const osc = this.createOscillator();
    osc.start();
    return osc;
  };
  const origSrcObject = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, "srcObject").set;
  Object.defineProperty(HTMLMediaElement.prototype, "srcObject", {
    set(value) {
      if (value instanceof Org.WebRtc.MediaStream) {
        attachMediaStream(this, value);
        return;
      } else if (this.msRealTime) {
        this.removeAttribute("src");
        this.msRealTime = false;
      }
      if (value === undefined)
        value = null;
      origSrcObject.call(this, value);
    }
  });
})(Promise);
<commit_msg>Set UA string to Chrome 54<commit_after>(function (Promise) {
  "use strict";
  if (!("Org" in self && "WebRtc" in Org))
    return;

  function copy(target, source) {
    for (const key in source)
      target[key] = source[key];
    return target;
  }

  // initialize api
  Org.WebRtc.WinJSHooks.initialize();

  const media = Org.WebRtc.Media.createMedia();
  media.setAudioOutputDevice("default");
  Object.defineProperty(navigator, "userAgent", { value: "AppleWebKit/537.36 Chrome/54.0.2840.59 Safari/537.36" });

  MediaStream = Org.WebRtc.MediaStream;

  navigator.mediaDevices.enumerateDevices = function () {
    return Promise.resolve(media.enumerateDevices());
  };

  function createMediaConstraints(constraints) {
    const result = new Org.WebRtc.MediaTrackConstraints;
    if ("optional" in constraints || "mandatory" in constraints) {
      if ("mandatory" in constraints)
        for (const key in constraints.mandatory)
          result.addMandatory(key, constraints.mandatory[key]);
      if ("optional" in constraints)
        for (const constraint of constraints.optional)
          for (const key in constraint)
            result.addOptional(key, constraint[key]);
    } else
      copy(result, constraints);
    return result;
  }

  const { StreamingCaptureMode } = Windows.Media.Capture;

  function CaptureMode(audio, video) {
    if (audio)
      if (video)
        return StreamingCaptureMode.audioAndVideo;
      else
        return StreamingCaptureMode.audio;
    else
      return StreamingCaptureMode.video;
  }

  class PermissionDeniedError extends Error {
    constructor(message) {
      super(message);
      this.name = "PermissionDeniedError";
    }
  }

  const capturePromises = [null, null, null];
  let lastCapturePromise = null;
  navigator.mediaDevices.getUserMedia = function (constraints) {
    return new Promise((resolve, reject) => {
      var constraintsOverride = new Org.WebRtc.RTCMediaStreamConstraints();
      constraintsOverride.audioEnabled = constraints.audio;
      constraintsOverride.videoEnabled = constraints.video;
      if (typeof constraints.audio === "object")
        constraintsOverride.audio = createMediaConstraints(constraints.audio);
      if (typeof constraints.video === "object")
        constraintsOverride.video = createMediaConstraints(constraints.video);

      // ask for permission to access microphone/camera
      const audio = constraintsOverride.audioEnabled && !capturePromises[StreamingCaptureMode.audio];
      const video = constraintsOverride.videoEnabled && !capturePromises[StreamingCaptureMode.video];
      if (audio || video) {
        const requestPermission = () => {
          const captureInitSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
          captureInitSettings.streamingCaptureMode = CaptureMode(audio, video);
          const mediaCapture = new Windows.Media.Capture.MediaCapture();
          return mediaCapture.initializeAsync(captureInitSettings).then(() => {
            mediaCapture.close();
          }, e => {
            console.error("Failed to obtain media access permission:", e.message);
            throw new PermissionDeniedError;
          });
        };
        lastCapturePromise = lastCapturePromise ? lastCapturePromise.then(requestPermission, requestPermission) : requestPermission();
        if (audio)
          capturePromises[StreamingCaptureMode.audio] = lastCapturePromise;
        if (video)
          capturePromises[StreamingCaptureMode.video] = lastCapturePromise;
        if (capturePromises[StreamingCaptureMode.audio] && capturePromises[StreamingCaptureMode.video])
          capturePromises[StreamingCaptureMode.audioAndVideo] = Promise.all(capturePromises);
      }
      resolve(capturePromises[CaptureMode(constraintsOverride.audioEnabled, constraintsOverride.videoEnabled)].then(() => media.getUserMedia(constraintsOverride)));
    });
  };
  navigator.getUserMedia = function (constraints, successCallback, errorCallback) {
    navigator.mediaDevices.getUserMedia(constraints).then(successCallback, errorCallback);
  };

  self.RTCIceCandidate = function (candidate) {
    if (candidate !== undefined) {
      this.candidate = candidate.candidate;
      this.sdpMid = candidate.sdpMid;
      this.sdpMLineIndex = candidate.sdpMLineIndex;
    }
  };

  self.RTCSessionDescription = function (message) {
    var sdpType;
    if (message.type == 'offer') {
      sdpType = Org.WebRtc.RTCSdpType.offer;
    }
    else if (message.type == 'answer') {
      sdpType = Org.WebRtc.RTCSdpType.answer;
    }
    else {
      sdpType = Org.WebRtc.RTCSdpType.pranswer;
    }
    return new Org.WebRtc.RTCSessionDescription(sdpType, message.sdp);
  };

  function attachMediaStream(element, stream) {
    if (stream.getVideoTracks().length > 0) {
      const videoTrack = stream.getVideoTracks().first().current;
      var streamSource = media.createMediaSource(videoTrack, stream.id);
      var mediaSource = Windows.Media.Core.MediaSource.createFromIMediaSource(streamSource);
      var mediaPlaybackItem = new Windows.Media.Playback.MediaPlaybackItem(mediaSource);
      var playlist = new Windows.Media.Playback.MediaPlaybackList();
      playlist.items.append(mediaPlaybackItem);
      element.msRealTime = true;
      element.src = URL.createObjectURL(playlist, { oneTimeOnly: true });
    }
  }

  function convertToRTCOfferAnswerOptions(options) {
    const result = copy(new Org.WebRtc.RTCOfferAnswerOptions, options);
    const optionKeys = {
      OfferToReceiveAudio: "offerToReceiveAudio",
      OfferToReceiveVideo: "offerToReceiveVideo",
      VoiceActivityDetection: "voiceActivityDetection",
      IceRestart: "iceRestart"
    };
    for (const key in options.mandatory)
      result[optionKeys[key]] = options.mandatory[key];
    if ("optional" in options)
      for (const constraint of options.optional)
        for (const key in constraint)
          result[optionKeys[key]] = constraint[key];
    return result;
  }

  class RTCPeerConnection {
    constructor(pcConfig, pcConstraints) {
      //Todo: do we need to implement pcConstraints in C++/CX API?
      var winrtConfig = new Org.WebRtc.RTCConfiguration();
      if (pcConfig.iceServers && pcConfig.iceServers.length > 0) {
        var iceServer = new Org.WebRtc.RTCIceServer();
        if (pcConfig.iceServers[0].urls != null) {
          iceServer.url = pcConfig.iceServers[0].urls[0];
        } else {
          iceServer.url = pcConfig.iceServers[0].url;
        }
        iceServer.credential = pcConfig.iceServers[0].credential;
        iceServer.username = pcConfig.iceServers[0].username;
        winrtConfig.iceServers = [];
        winrtConfig.iceServers.push(iceServer);

      }

      this._nativePC = new Org.WebRtc.RTCPeerConnection(winrtConfig);
    }
    createOffer(...args) {
      let options = null;
      if (args.length > 0 && typeof args[0] === "object")
        options = args[0];
      else if (args.length > 2)
        options = args[2];
      const p = new Promise((resolve, reject) => {
        var winrtOptions = options ? convertToRTCOfferAnswerOptions(options) : null;
        resolve(this._nativePC.createOffer(winrtOptions).then(function (offerSDP) {
          var newOfferSDP = {};
          // HACK: This is a hack to force VP8 while we're waiting for VP9 to be
          // fully implemented.
          newOfferSDP.sdp = offerSDP.sdp.replace(' 101 100', ' 100 101');
          switch (offerSDP.type) {
            case 0: newOfferSDP.type = 'offer'; break;
            case 1: newOfferSDP.type = 'pranswer'; break;
            case 2: newOfferSDP.type = 'answer'; break;
            default: throw 'invalid offer type';
          }
          return newOfferSDP;
        }));
      });
      if (args.length > 0 && typeof args[0] === "function") {
        p.then(args[0], args[1]);
        return Promise.resolve(undefined);
      } else
        return p;
    }
    createAnswer(...args) {
      let options = null;
      if (args.length > 0 && typeof args[0] === "object")
        options = args[0];
      else if (args.length > 2)
        options = args[2];
      const p = new Promise((resolve, reject) => {
        var winrtOptions = options ? convertToRTCOfferAnswerOptions(options) : null;
        resolve(this._nativePC.createAnswer(winrtOptions).then(function (answerSDP) {
          var newAnswerSDP = {};
          // HACK: This is a hack to force VP8 while we're waiting for VP9 to be
          // fully implemented.
          newAnswerSDP.sdp = answerSDP.sdp.replace(' 101 100', ' 100 101');
          switch (answerSDP.type) {
            case 0: newAnswerSDP.type = 'offer'; break;
            case 1: newAnswerSDP.type = 'pranswer'; break;
            case 2: newAnswerSDP.type = 'answer'; break;
            default: throw 'invalid offer type';
          }
          return newAnswerSDP;
        }));
      });
      if (args.length > 0 && typeof args[0] === "function") {
        p.then(args[0], args[1]);
        return Promise.resolve(undefined);
      } else
        return p;
    }
    setLocalDescription(desc) {
      return new Promise((resolve, reject) => {
        var winrtSDP = new RTCSessionDescription(desc);
        resolve(this._nativePC.setLocalDescription(winrtSDP));
      });
    }
    get localDescription() {
      return this._nativePC.localDescription;
    }
    setRemoteDescription(desc) {
      return new Promise((resolve, reject) => {
        // HACK: This is a hack to force VP8 while we're waiting for VP9 to be
        // fully implemented.
        var winrtSDP = desc;
        winrtSDP.sdp = desc.sdp.replace(' 101 100', ' 100 101');
        resolve(this._nativePC.setRemoteDescription(winrtSDP));
      });
    }
    get remoteDescription() {
      return this._nativePC.remoteDescription;
    }
    getConfiguration() {
      return this._nativePC.getConfiguration();
    }
    getLocalStreams() {
      return this._nativePC.getLocalStreams();
    }
    getRemoteStreams() {
      return this._nativePC.getRemoteStreams();
    }
    getStreamById(id) {
      return this._nativePC.getStreamById(id);
    }
    addStream(stream) {
      return this._nativePC.addStream(stream);
    }
    removeStream(stream) {
      return this._nativePC.removeStream(stream);
    }
    createDataChannel(label, init) {
      return this._nativePC.createDataChannel(label, init);
    }
    addIceCandidate(candidate) {
      return new Promise((resolve, reject) => {
        var nativeCandidate = new Org.WebRtc.RTCIceCandidate(candidate.candidate,
          (typeof candidate.sdpMid !== 'undefined') ? candidate.sdpMid : '', candidate.sdpMLineIndex);
        resolve(this._nativePC.addIceCandidate(nativeCandidate));
      });
    }
    get signalingState() {
      return [
        "stable",
        "have-local-offer",
        "have-local-pranswer",
        "have-remote-offer",
        "have-remote-pranswer",
        "closed"
      ][this._nativePC.signalingState];
    }
    get iceGatheringState() {
      return [
        "new",
        "gathering",
        "complete"
      ][this._nativePC.iceGatheringState];
    }
    get iceConnectionState() {
      return [
        "new",
        "checking",
        "connected",
        "completed",
        "failed",
        "disconnected",
        "closed"
      ][this._nativePC.iceConnectionState];
    }
    close() {
      return this._nativePC.close();
    }
    getStats() {
      return new Promise((resolve, reject) => {
        // this._nativePC.addEventListener("rtcstatsreportsready", function onrtcstatsreportsready(event) {
        //   this.removeEventListener("rtcstatsreportsready", onrtcstatsreportsready);
        //   this._nativePC.rtcStatsEnabled = false;
        //   resolve(event.rtcStatsReports);
        // });
        // this._nativePC.rtcStatsEnabled = true;
      });
    }
  }
  for (const type of [
    "negotiationneeded",
    "icecandidate",
    "signalingstatechange",
    "iceconnectionstatechange",
    "icegatheringstatechange",
    "datachannel",
    "addstream",
    "removestream"
  ]) {
    const prop = "on" + type;
    Object.defineProperty(RTCPeerConnection.prototype, prop, {
      configurable: true,
      enumerable: true,
      get() {
        return this._nativePC[prop];
      },
      set(value) {
        if (value)
          this._nativePC[prop] = event => value.call(this, event);
        else
          this._nativePC[prop] = null;
      }
    });
  }
  self.RTCPeerConnection = RTCPeerConnection;

  const origCreateObjectURL = URL.createObjectURL;
  URL.createObjectURL = function (object) {
    if (object instanceof MediaStream)
      return "webrtc:";
    return origCreateObjectURL.apply(this, arguments);
  };
  const origRevokeObjectURL = URL.revokeObjectURL;
  URL.revokeObjectURL = function (url) {
    if (url === "webrtc:")
      return;
    return origRevokeObjectURL.apply(this, arguments);
  };
  HTMLMediaElement.prototype.setSinkId = function (sinkId) {
    return Promise.resolve(media.setAudioOutputDevice(sinkId));
  };
  AudioContext.prototype.createMediaStreamSource = function (mediaStream) {
    const osc = this.createOscillator();
    osc.start();
    return osc;
  };
  const origSrcObject = Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, "srcObject").set;
  Object.defineProperty(HTMLMediaElement.prototype, "srcObject", {
    set(value) {
      if (value instanceof Org.WebRtc.MediaStream) {
        attachMediaStream(this, value);
        return;
      } else if (this.msRealTime) {
        this.removeAttribute("src");
        this.msRealTime = false;
      }
      if (value === undefined)
        value = null;
      origSrcObject.call(this, value);
    }
  });
})(Promise);
<|endoftext|>


<commit_before>// Copyright 2018 The LUCI Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

let webpackConfig = require('./webpack.config.js')
// Webpack 3+ configs can be either objects or functions that produce the
// config object. Karma currently doesn't handle the latter, so do it
// ourselves here.
if (typeof webpackConfig === 'function') {
  webpackConfig = webpackConfig({}, {mode: 'development'});
}
webpackConfig.entry = null;
webpackConfig.mode = 'development';

// Allows tests to import modules locally
webpackConfig.resolve = {
    modules: ['./node_modules', './'],
}

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
        'node_modules/@webcomponents/custom-elements/custom-elements.min.js',
        '_all_tests.js',
    ],

    // list of files / patterns to exclude
    exclude: [
    ],

    plugins: [
        'karma-concat-preprocessor',
        'karma-webpack',
        'karma-jasmine',
        'karma-firefox-launcher',
        'karma-chrome-launcher',
        'karma-spec-reporter'
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
        'modules/**/*.js': ['concat'],
        '_all_tests.js': [ 'webpack' ]
    },

    concat: {
        // By default, concat puts everything in a function(){}, but
        // that doesn't work with imports.
        header: '',
        footer: '',
        outputs: [
            {
                file: '_all_tests.js',
                inputs: [
                    'modules/**/*_test.js',
                ],
            },
        ],
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['spec'],

    // web server port
    port: parseInt(process.env.KARMA_PORT || '9876'),

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome', 'Firefox'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity,

    webpack: webpackConfig,
  })
}
<commit_msg>[swarming ui] test only with Chrome(headless)<commit_after>// Copyright 2018 The LUCI Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.

let webpackConfig = require('./webpack.config.js')
// Webpack 3+ configs can be either objects or functions that produce the
// config object. Karma currently doesn't handle the latter, so do it
// ourselves here.
if (typeof webpackConfig === 'function') {
  webpackConfig = webpackConfig({}, {mode: 'development'});
}
webpackConfig.entry = null;
webpackConfig.mode = 'development';

// Allows tests to import modules locally
webpackConfig.resolve = {
    modules: ['./node_modules', './'],
}

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
        'node_modules/@webcomponents/custom-elements/custom-elements.min.js',
        '_all_tests.js',
    ],

    // list of files / patterns to exclude
    exclude: [
    ],

    plugins: [
        'karma-concat-preprocessor',
        'karma-webpack',
        'karma-jasmine',
        'karma-firefox-launcher',
        'karma-chrome-launcher',
        'karma-spec-reporter'
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
        'modules/**/*.js': ['concat'],
        '_all_tests.js': [ 'webpack' ]
    },

    concat: {
        // By default, concat puts everything in a function(){}, but
        // that doesn't work with imports.
        header: '',
        footer: '',
        outputs: [
            {
                file: '_all_tests.js',
                inputs: [
                    'modules/**/*_test.js',
                ],
            },
        ],
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['spec'],

    // web server port
    port: parseInt(process.env.KARMA_PORT || '9876'),

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['ChromeHeadless'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true,

    // Concurrency level
    // how many browser should be started simultaneous
    concurrency: Infinity,

    webpack: webpackConfig,
  })
}
<|endoftext|>


<commit_before>const module_name = __filename.slice(__dirname.length + 1, -3);

// Decode various BMBT button presses
function decode_button(data) {
	data.command = 'bro';
	data.value   = 'BM button ';

	let action = 'depress';
	let button;

	// Determine action
	let mask = bitmask.check(data.msg[1]).mask;
	switch (mask.b6) {
		case true : {
			switch (mask.b7) {
				case true  : break;
				case false : {
					// Remove hold bit from button value
					data.msg[1] = bitmask.unset(data.msg[1], bitmask.b[6]);
					action      = 'hold';
				}
			}
			break;
		}

		case false : {
			switch (mask.b7) {
				case false : break;
				case true  : {
					// Remove release bit from button value
					data.msg[1] = bitmask.unset(data.msg[1], bitmask.b[7]);
					action      = 'release';
				}
			}
		}
	}

	// Determine button
	switch (data.msg[1]) {
		case 0x00 : button = 'right';    break;
		case 0x01 : button = '2';        break;
		case 0x02 : button = '4';        break;
		case 0x03 : button = '6';        break;
		case 0x04 : button = 'tone';     break;
		case 0x05 : button = 'knob';     break;
		case 0x06 : button = 'power';    break;
		case 0x07 : button = 'clock';    break;
		case 0x08 : button = 'phone';    break;
		case 0x10 : button = 'left';     break;
		case 0x11 : button = '1';        break;
		case 0x12 : button = '3';        break;
		case 0x13 : button = '5';        break;
		case 0x14 : button = '<>';       break;
		case 0x20 : button = 'select';   break;
		case 0x21 : button = 'am';       break;
		case 0x22 : button = 'rds';      break;
		case 0x23 : button = 'mode';     break;
		case 0x24 : button = 'eject';    break;
		case 0x30 : button = 'rad menu'; break;
		case 0x31 : button = 'fm';       break;
		case 0x32 : button = 'pty/tp';   break;
		case 0x33 : button = 'dolby';    break;
		case 0x34 : button = 'gt menu';  break;
		case 0x38 : button = 'info';     break;
		default   : button = 'Unknown';
	}

	data.value += action + ' ' + button;

	// If media control is disabled, return here
	if (config.bmbt.media === false) return data;

	switch (action) {
		case 'hold' : {
			switch (config.bmbt.media) {
				case 'bluetooth' : // Bluetooth version
					switch (button) {
						case 'left'  : break;
						case 'right' : break;
						case 'eject' : BT.command('play'); break; // Think about it
					}
					break;

				case 'kodi' : // Kodi version
					switch (button) {
						case 'left'  : kodi.command('seek-rewind');  break;
						case 'right' : kodi.command('seek-forward'); break;
						case 'eject' : break;
					}
			}
			break;
		}

		case 'release' : {
			switch (config.bmbt.media) {
				case 'bluetooth' : // Bluetooth version
					switch (status.bmbt.last.action + status.bmbt.last.button) {
						case 'depressleft'  : BT.command('previous'); break;
						case 'depressright' : BT.command('next');     break;
						case 'depresseject' : BT.command('pause');    break; // Think about it

						case 'depress5' : LCM.police(true);  break;
						case 'depress6' : LCM.police(false); break;

						case 'holdleft'  : break;
						case 'holdright' : break;
						case 'holdeject' : break;
					}
					break;

				case 'kodi' : // Kodi version
					switch (status.bmbt.last.action + status.bmbt.last.button) {
						case 'depressleft'  : kodi.command('previous'); break;
						case 'depressright' : kodi.command('next');     break;
						case 'depresseject' : kodi.command('toggle');   break;

						case 'holdleft'  : kodi.command('toggle'); break;
						case 'holdright' : kodi.command('toggle'); break;
						case 'holdeject' : break;
					}
			}
			break;
		}
	}

	// case 'depress' :

	// Update status object with the new data
	update.status('bmbt.last.action', action);
	update.status('bmbt.last.button', button);

	return data;
}

// Decode BMBT knob turns
function decode_knob(data) {
	data.command = 'bro';
	data.value   = 'BM knob ';

	let direction = 'left';
	let steps;

	// Determine rotation direction
	// Bit7 : Right
	let mask = bitmask.check(data.msg[1]).mask;
	if (mask.b7) {
		data.msg[1] = bitmask.unset(data.msg[1], bitmask.b[7]);
		direction   = 'right';
	}

	steps = data.msg[1];

	data.value += direction + ' ' + steps + ' steps';

	return data;
}

// Set or unset the status timeout
function status_loop(action) {
	if (config.emulate.bmbt !== true) return;

	if (status.vehicle.ignition_level < 1) action = false;

	if (BMBT.status_status_loop === action) return;

	log.module({ msg : 'status_loop(' + action + ')' });

	switch (action) {
		case false :
			update.status('rad.source_name', 'off');

			update.status('dsp.reset',  true);
			update.status('dsp.ready',  false);
			update.status('dspc.reset', true);
			update.status('dspc.ready', false);
			update.status('rad.reset',  true);
			update.status('rad.ready',  false);

			// Set status variable
			BMBT.status_status_loop = false;

			if (BMBT.timeouts.status_loop !== null) {
				log.module({ msg : 'Unset status refresh timeout' });
				clearTimeout(BMBT.timeouts.status_loop);
				BMBT.timeouts.status_loop = null;
			}
			break;

		case true :
			// Send a couple through to prime the pumps
			refresh_status();

			// Set status variable
			BMBT.status_status_loop = true;

			log.module({ msg : 'Set status refresh timeout' });
	}
}

// Send BMBT status, and request status from RAD
function refresh_status() {
	if (status.vehicle.ignition_level > 0) {
		log.module({ msg : 'Refreshing status' });

		bus.cmds.send_device_status('CDC');
		bus.cmds.send_device_status('BMBT');
		bus.cmds.send_device_status('DSPC');

		bus.cmds.request_device_status(module_name, 'RAD');
		bus.cmds.request_device_status('RAD',  'DSP');

		BMBT.timeouts.status_loop = setTimeout(refresh_status, 8000);

		return;
	}

	status_loop(false);
}

// Send the power on button command if needed/ready
function toggle_power_if_ready() {
	if (config.emulate.bmbt !== true) return;

	// Only setTimeout if we don't already have one waiting
	if (BMBT.timeouts.power_on !== null) return;

	BMBT.timeouts.power_on = setTimeout(() => {
		// Debug logging
		// log.module({ msg : 'dsp.ready: '+status.dsp.ready });
		// log.module({ msg : 'rad.source_name: '+status.rad.source_name });

		BMBT.timeouts.power_on = null;

		if (status.rad.source_name !== 'off' || status.vehicle.ignition_level === 0) {
			return;
		}

		kodi.notify(module_name,         'power [BMBT]');
		IKE.text_override(module_name + ' power [BMBT]');

		log.module({ msg : 'Sending power!' });

		send_button('power');
		// DSP.request('memory'); // Get the DSP memory

		status_loop(true);

		// Increase volume after power on
		setTimeout(() => {
			RAD.volume_control(5);
			RAD.volume_control(5);
			RAD.volume_control(5);
		}, 500);
		setTimeout(() => {
			RAD.volume_control(5);
			RAD.volume_control(5);
			RAD.volume_control(5);
		}, 750);
		setTimeout(() => {
			RAD.volume_control(5);
			RAD.volume_control(5);
			RAD.volume_control(5);
		}, 1000);
	}, 1000);
}

// Parse data sent to BMBT module
function parse_in(data) {
	switch (data.msg[0]) {
		case 0x4A: // Cassette control
			send_cassette_status();
			toggle_power_if_ready();
			break;
	}
}

// Parse data sent from BMBT module
function parse_out(data) {
	switch (data.msg[0]) {
		case 0x4B: // Cassette status
			data.command = 'sta';
			data.value   = 'cassette: ';

			switch (data.msg[1]) {
				case 0x00 : data.value += 'off';     break;
				case 0x05 : data.value += 'no tape'; break;
				case 0x06 : data.value += 'tape in'; break;
				case 0xFF : data.value += 'on';      break;
				default   : data.value += 'unknown 0x' + data.msg[1].toString(16);
			}
			break;

		case 0x47: // Broadcast: BM status
			data = decode_button(data);
			break;

		case 0x48: // Broadcast: BM button
			data = decode_button(data);
			break;

		case 0x49: // Broadcast: BM knob
			data = decode_knob(data);
			break;

		default:
			data.command = 'unk';
			data.value   = Buffer.from(data.msg);
			break;
	}

	log.bus(data);
}

// Say we have no tape in the player
function send_cassette_status(value = 0x05) {
	bus.data.send({
		src : module_name,
		dst : 'RAD',
		msg : [ 0x4B, value ],
	});
}

// Emulate button presses
function send_button(button) {
	let button_down = 0x00;
	// let button_hold;
	let button_up;

	// Switch statement to determine button, then encode bitmask
	switch (button) {
		case 'power' :
			// Get down value of button
			button_down = bitmask.set(button_down, bitmask.bit[1]);
			button_down = bitmask.set(button_down, bitmask.bit[2]);

			// Generate hold and up values
			// button_hold = bitmask.set(button_down, bitmask.bit[6]);
			button_up = bitmask.set(button_down, bitmask.bit[7]);
			break;
	}

	log.module({ msg : 'Button down ' + button });

	// Init variables
	let command     = 0x48; // Button action
	let packet_down = [ command, button_down ];
	let packet_up   = [ command, button_up ];

	bus.data.send({
		src : module_name,
		dst : 'RAD',
		msg : packet_down,
	});

	// Prepare and send the up message after 150ms
	setTimeout(() => {
		log.module({ msg : 'Button up ' + button });

		bus.data.send({
			src : module_name,
			dst : 'RAD',
			msg : packet_up,
		});
	}, 150);
}

function init_listeners() {
	// Enable keepalive on IKE ignition event
	IKE.on('ignition-powerup', () => {
		status_loop(true);
	});

	// Enable keepalive on IKE ignition event
	IKE.on('ignition-poweroff', () => {
		status_loop(false);
	});
}

module.exports = {
	status_status_loop : false,

	timeouts : {
		power_on    : null,
		status_loop : null,
	},

	init_listeners        : init_listeners,
	parse_in              : parse_in,
	parse_out             : parse_out,
	send_button           : send_button,
	send_cassette_status  : send_cassette_status,
	status_loop           : status_loop,
	toggle_power_if_ready : toggle_power_if_ready,
};
<commit_msg>Update<commit_after>const module_name = __filename.slice(__dirname.length + 1, -3);

// Decode various BMBT button presses
function decode_button(data) {
	data.command = 'bro';
	data.value   = 'BM button ';

	let action = 'depress';
	let button;

	// Determine action
	let mask = bitmask.check(data.msg[1]).mask;
	switch (mask.b6) {
		case true : {
			switch (mask.b7) {
				case true  : break;
				case false : {
					// Remove hold bit from button value
					data.msg[1] = bitmask.unset(data.msg[1], bitmask.b[6]);
					action      = 'hold';
				}
			}
			break;
		}

		case false : {
			switch (mask.b7) {
				case false : break;
				case true  : {
					// Remove release bit from button value
					data.msg[1] = bitmask.unset(data.msg[1], bitmask.b[7]);
					action      = 'release';
				}
			}
		}
	}

	// Determine button
	switch (data.msg[1]) {
		case 0x00 : button = 'right';    break;
		case 0x01 : button = '2';        break;
		case 0x02 : button = '4';        break;
		case 0x03 : button = '6';        break;
		case 0x04 : button = 'tone';     break;
		case 0x05 : button = 'knob';     break;
		case 0x06 : button = 'power';    break;
		case 0x07 : button = 'clock';    break;
		case 0x08 : button = 'phone';    break;
		case 0x10 : button = 'left';     break;
		case 0x11 : button = '1';        break;
		case 0x12 : button = '3';        break;
		case 0x13 : button = '5';        break;
		case 0x14 : button = '<>';       break;
		case 0x20 : button = 'select';   break;
		case 0x21 : button = 'am';       break;
		case 0x22 : button = 'rds';      break;
		case 0x23 : button = 'mode';     break;
		case 0x24 : button = 'eject';    break;
		case 0x30 : button = 'rad menu'; break;
		case 0x31 : button = 'fm';       break;
		case 0x32 : button = 'pty/tp';   break;
		case 0x33 : button = 'dolby';    break;
		case 0x34 : button = 'gt menu';  break;
		case 0x38 : button = 'info';     break;
		default   : button = 'Unknown';
	}

	data.value += action + ' ' + button;

	// If media control is disabled, return here
	if (config.bmbt.media === false) return data;

	switch (action) {
		case 'hold' : {
			switch (config.bmbt.media) {
				case 'bluetooth' : // Bluetooth version
					switch (button) {
						case 'left'  : break;
						case 'right' : break;
						case 'eject' : BT.command('play'); break; // Think about it
					}
					break;

				case 'kodi' : // Kodi version
					switch (button) {
						case 'left'  : kodi.command('seek-rewind');  break;
						case 'right' : kodi.command('seek-forward'); break;
						case 'eject' : break;
					}
			}
			break;
		}

		case 'release' : {
			switch (config.bmbt.media) {
				case 'bluetooth' : // Bluetooth version
					switch (status.bmbt.last.action + status.bmbt.last.button) {
						case 'depressleft'  : BT.command('previous'); break;
						case 'depressright' : BT.command('next');     break;
						case 'depresseject' : BT.command('pause');    break; // Think about it

						case 'depress2' : {
							LCM.police(true);
							setTimeout(() => {
								LCM.police(false);
							}, 500);
							break;
						}

						case 'depress3' : LCM.police(true);  break;
						case 'depress6' : LCM.police(false); break;

						case 'holdleft'  : break;
						case 'holdright' : break;
						case 'holdeject' : break;
					}
					break;

				case 'kodi' : // Kodi version
					switch (status.bmbt.last.action + status.bmbt.last.button) {
						case 'depressleft'  : kodi.command('previous'); break;
						case 'depressright' : kodi.command('next');     break;
						case 'depresseject' : kodi.command('toggle');   break;

						case 'holdleft'  : kodi.command('toggle'); break;
						case 'holdright' : kodi.command('toggle'); break;
						case 'holdeject' : break;
					}
			}
			break;
		}

		// case 'depress' :
	}

	// Update status object with the new data
	update.status('bmbt.last.action', action);
	update.status('bmbt.last.button', button);

	return data;
}

// Decode BMBT knob turns
function decode_knob(data) {
	data.command = 'bro';
	data.value   = 'BM knob ';

	let direction = 'left';
	let steps;

	// Determine rotation direction
	// Bit7 : Right
	let mask = bitmask.check(data.msg[1]).mask;
	if (mask.b7) {
		data.msg[1] = bitmask.unset(data.msg[1], bitmask.b[7]);
		direction   = 'right';
	}

	steps = data.msg[1];

	data.value += direction + ' ' + steps + ' steps';

	return data;
}

// Set or unset the status timeout
function status_loop(action) {
	if (config.emulate.bmbt !== true) return;

	if (status.vehicle.ignition_level < 1) action = false;

	if (BMBT.status_status_loop === action) return;

	log.module({ msg : 'status_loop(' + action + ')' });

	switch (action) {
		case false :
			update.status('rad.source_name', 'off');

			update.status('dsp.reset',  true);
			update.status('dsp.ready',  false);
			update.status('dspc.reset', true);
			update.status('dspc.ready', false);
			update.status('rad.reset',  true);
			update.status('rad.ready',  false);

			// Set status variable
			BMBT.status_status_loop = false;

			if (BMBT.timeouts.status_loop !== null) {
				log.module({ msg : 'Unset status refresh timeout' });
				clearTimeout(BMBT.timeouts.status_loop);
				BMBT.timeouts.status_loop = null;
			}
			break;

		case true :
			// Send a couple through to prime the pumps
			refresh_status();

			// Set status variable
			BMBT.status_status_loop = true;

			log.module({ msg : 'Set status refresh timeout' });
	}
}

// Send BMBT status, and request status from RAD
function refresh_status() {
	if (status.vehicle.ignition_level > 0) {
		log.module({ msg : 'Refreshing status' });

		bus.cmds.send_device_status('CDC');
		bus.cmds.send_device_status('BMBT');
		bus.cmds.send_device_status('DSPC');

		bus.cmds.request_device_status(module_name, 'RAD');
		bus.cmds.request_device_status('RAD',  'DSP');

		BMBT.timeouts.status_loop = setTimeout(refresh_status, 8000);

		return;
	}

	status_loop(false);
}

// Send the power on button command if needed/ready
function toggle_power_if_ready() {
	if (config.emulate.bmbt !== true) return;

	// Only setTimeout if we don't already have one waiting
	if (BMBT.timeouts.power_on !== null) return;

	BMBT.timeouts.power_on = setTimeout(() => {
		// Debug logging
		// log.module({ msg : 'dsp.ready: '+status.dsp.ready });
		// log.module({ msg : 'rad.source_name: '+status.rad.source_name });

		BMBT.timeouts.power_on = null;

		if (status.rad.source_name !== 'off' || status.vehicle.ignition_level === 0) {
			return;
		}

		kodi.notify(module_name,         'power [BMBT]');
		IKE.text_override(module_name + ' power [BMBT]');

		log.module({ msg : 'Sending power!' });

		send_button('power');
		// DSP.request('memory'); // Get the DSP memory

		status_loop(true);

		// Increase volume after power on
		setTimeout(() => {
			RAD.volume_control(5);
			RAD.volume_control(5);
			RAD.volume_control(5);
		}, 500);
		setTimeout(() => {
			RAD.volume_control(5);
			RAD.volume_control(5);
			RAD.volume_control(5);
		}, 750);
		setTimeout(() => {
			RAD.volume_control(5);
			RAD.volume_control(5);
			RAD.volume_control(5);
		}, 1000);
	}, 1000);
}

// Parse data sent to BMBT module
function parse_in(data) {
	switch (data.msg[0]) {
		case 0x4A: // Cassette control
			send_cassette_status();
			toggle_power_if_ready();
			break;
	}
}

// Parse data sent from BMBT module
function parse_out(data) {
	switch (data.msg[0]) {
		case 0x4B: // Cassette status
			data.command = 'sta';
			data.value   = 'cassette: ';

			switch (data.msg[1]) {
				case 0x00 : data.value += 'off';     break;
				case 0x05 : data.value += 'no tape'; break;
				case 0x06 : data.value += 'tape in'; break;
				case 0xFF : data.value += 'on';      break;
				default   : data.value += 'unknown 0x' + data.msg[1].toString(16);
			}
			break;

		case 0x47: // Broadcast: BM status
			data = decode_button(data);
			break;

		case 0x48: // Broadcast: BM button
			data = decode_button(data);
			break;

		case 0x49: // Broadcast: BM knob
			data = decode_knob(data);
			break;

		default:
			data.command = 'unk';
			data.value   = Buffer.from(data.msg);
			break;
	}

	log.bus(data);
}

// Say we have no tape in the player
function send_cassette_status(value = 0x05) {
	bus.data.send({
		src : module_name,
		dst : 'RAD',
		msg : [ 0x4B, value ],
	});
}

// Emulate button presses
function send_button(button) {
	let button_down = 0x00;
	// let button_hold;
	let button_up;

	// Switch statement to determine button, then encode bitmask
	switch (button) {
		case 'power' :
			// Get down value of button
			button_down = bitmask.set(button_down, bitmask.bit[1]);
			button_down = bitmask.set(button_down, bitmask.bit[2]);

			// Generate hold and up values
			// button_hold = bitmask.set(button_down, bitmask.bit[6]);
			button_up = bitmask.set(button_down, bitmask.bit[7]);
			break;
	}

	log.module({ msg : 'Button down ' + button });

	// Init variables
	let command     = 0x48; // Button action
	let packet_down = [ command, button_down ];
	let packet_up   = [ command, button_up ];

	bus.data.send({
		src : module_name,
		dst : 'RAD',
		msg : packet_down,
	});

	// Prepare and send the up message after 150ms
	setTimeout(() => {
		log.module({ msg : 'Button up ' + button });

		bus.data.send({
			src : module_name,
			dst : 'RAD',
			msg : packet_up,
		});
	}, 150);
}

function init_listeners() {
	// Enable keepalive on IKE ignition event
	IKE.on('ignition-powerup', () => {
		status_loop(true);
	});

	// Enable keepalive on IKE ignition event
	IKE.on('ignition-poweroff', () => {
		status_loop(false);
	});
}

module.exports = {
	status_status_loop : false,

	timeouts : {
		power_on    : null,
		status_loop : null,
	},

	init_listeners        : init_listeners,
	parse_in              : parse_in,
	parse_out             : parse_out,
	send_button           : send_button,
	send_cassette_status  : send_cassette_status,
	status_loop           : status_loop,
	toggle_power_if_ready : toggle_power_if_ready,
};
<|endoftext|>