<commit_before>// Requires
const xml2js = require('xml2js').parseString
const _ = require('lodash')
const q = require('q')
const fs = require('fs')
const async = require('async')
const unirest = require('unirest')
const mathjs = require('mathjs')
const mongoose = require('mongoose')
const utils = require('./utils.js')
const wolframAPI = process.env.app_id || null
const mongoAddress = 'mongodb://localhost'
mongoose.connect(mongoAddress)

/**
 * Checks if the expression is an equation or a simple math
 * expression. Checks if there is an = sign and/or variables
 * to decide.
 * @type {Function}
 * @param {String} expressions Math expressions
 * @return {Promise} promise
 */
var getExpType = (expression) => {
  var dfd = q.defer()

  var paramError = {message: 'Please provide a parameter'}
  var typeError = {message: 'Parameter must be a string'}

  var funcArray = [
    (callback) => {
      if (!expression) return callback(paramError)
      if (typeof expression !== 'string') return callback(typeError)

      var isEquation = expression.indexOf('=') > -1 // Needs to be improved

      if (isEquation) {
        var equation = {}
        var expressions = expression.split('=')
        equation.leftHandSide = mathjs.parse(expressions[0]).toString()
        equation.leftHandSide = mathjs.simplify(equation.leftHandSide).toString()
        equation.rightHandSide = mathjs.parse(expressions[1]).toString()
        equation.rightHandSide = mathjs.simplify(equation.rightHandSide).toString()
        callback(null, equation, isEquation)
      } else {
        expression = mathjs.parse(expression).toString()
        callback(null, expression, isEquation)
      }
    },
    (en, isEq, callback) => {
      var err = null
      var result = {
        isEq: isEq
      }
      if (isEq) {
        result.expression = en.leftHandSide + ' = ' + en.rightHandSide
      } else {
        result.expression = en
      }
      callback(err, result)
    }
  ]

  async.waterfall(
    funcArray,
    (err, result) => err ? dfd.reject(err) : dfd.resolve(result)
  )

  return dfd.promise
}

/**
 * Solves mathematical expression
 * @type {Function}
 * @param {Array} expressions Math expressions
 * @return {Promise} promise
 */
var mathJsSolve = (expressions) => {
  var dfd = q.defer()

  var paramErrorMessage = 'Please provide a parameter'
  var arrayErrorMessage = 'Expression array is empty!'
  var typeErrorMessage = 'Parameter must be an array'
  var expressionErrorMessage = 'Expression must be a string'

  var funcArray = [
    (callback) => {
      var hasExpressionError = false
      var paramError = {
        status: false,
        message: null
      }

      if ( !expressions ) {
        paramError.status = true
        paramError.message = paramErrorMessage
      }

      if ( !_.isArray(expressions) && !!expressions ) {
        paramError.status = true
        paramError.message = typeErrorMessage
      }

      if ( _.isArray(expressions) && _.isEmpty(expressions) ) {
        paramError.status = true
        paramError.message = arrayErrorMessage
      }

      var hasError = paramError.status

      if (!hasError) {
        solvedExpressions = _.map(expressions, (expression) => {
          var simplified, result, error
          try {
            if (typeof expression !== 'string') throw new Error(expressionErrorMessage)
            simplified = mathjs.simplify(expression).toString()
            result = {
              simplified: simplified,
              expression: expression,
              error: null,
              solution: mathjs.eval(simplified)
            }
          } catch (error) {
            hasExpressionError = true
            result = {
              expression: expression,
              error: error
            }
          }
          return result
        })
        if(hasExpressionError) solvedExpressions.hasExpressionError = hasExpressionError
        callback(null, solvedExpressions)
      } else {
        callback(paramError)
      }
    }
  ]

  async.waterfall(
    funcArray,
    (err, result) => err ? dfd.reject(err) : dfd.resolve(result)
  )

  return dfd.promise
}

/**
 * Wraps promise over Wolfram Module.
 * It needs input object to be validated
 * before hand. This also saves the Wolfram
 * result to the database.
 * @type {Function}
 * @param {Object} input Input information
 * @return {Promise} promise
 */
var wolframCall = (input) => {
  var dfd = q.defer()
  utils
    .buildUrl({
      input: input,
      url: 'https://api.wolframalpha.com/v2/query'
    })
    .then(bUrl => {
      unirest
        .get(bUrl.url)
        .end(res => {
          if (res.statusCode == 200) { // A OK
            xml2js(res.body, (err, result) => {
              if ( err ) return dfd.reject(err)
              if ( !result ) return dfd.reject(new Error('Result was not parsed!'))
              if ( result.queryresult.$.error === "true") return dfd.reject(result.queryresult)
              dfd.resolve(result.queryresult)
            })
          } else {
            dfd.reject(res)
          }
        })
    })
    .catch(err => dfd.reject(err))
  return dfd.promise
}

/**
 * Evaluate expression function.
 * 1 - Get Expression type
 * 2 - Send to solve
 * 3 - build and return result object
 * @type {Function} evaluate
 * @param {Object} input input data
 * @return {Promise} promise
 */
var evaluate = (input) => {
  var dfd = q.defer()

  var funcArray = [
    ( callback ) => {
      getExpType(input.query)
        .then(result => {
          var endRes = {
            user: input.user,
            query: result.expression
          }
          if (result.isEq) { // WolframSolve
            wolframCall(input.query)
              .then(result => {
                // console.log(result)
                endRes.solveType = 'wolfram'
                endRes.solution = result
                callback(null, endRes)
              })
              .catch(err => callback(err))
          } else { // MathJS Solve
            mathJsSolve([input.query])
              .then(result => {
                endRes.solveType = 'mathjs'
                endRes.solution = result[0].solution
                endRes.simplified = result[0].simplified
                endRes.error = result[0].error
                endRes.expression = result[0].expression

                callback(null, endRes)
              })
              .catch(err => callback(err))
          }
        })
        .catch(err => callback(err))
    }
  ]

  async.waterfall(
    funcArray,
    (err, result) => err ? dfd.reject(err) : dfd.resolve(result)
  )

  return dfd.promise
}

/**
 * Returns pod by id
 * @type {Function} getPodById
 * @param {Array} pods Wolfram pods
 * @return {Promise} promise
 */
var getPodById = (pods, id) => {
  var dfd = q.defer()

  var funcArray = [
    ( callback ) => { // Sweep pods looking for result id
      var count = 0
      if(!_.isArray(pods)) return callback({message: 'Parameter is not an Array!'})
      _.forEach(pods, (value) => {
        if(((value || {}).$ || {}).id === id) return callback(null, value)
        count++
        if (count == pods.length) callback({message: 'No '+id+' found!'})
      })
    }
  ]

  async.waterfall(
    funcArray,
    (err, result) => err ? dfd.reject(err) : dfd.resolve(result)
  )

  return dfd.promise
}

/**
 * Main Solve function, check if exists in the database,
 * if not, evaluates the input and saves the result at the end
 * @type {Function} solve
 * @param {Object} input input data
 * @return {Promise} promise
 */
var solve = (input) => {
  var dfd = q.defer()

  var funcArray = [
    ( callback ) => { // 1
      //VALIDATE INPUT HERE
      History
        .findOne({query: input.query})
        .where('active', true)
        .exec((err, doc) => {
          callback(err, doc, input)
        })
    },
    (doc, input, callback) => { // 2
      if (doc) return callback(null, null, doc)
      evaluate(input)
        .then(result => callback(null, result, null))
        .catch(err=> callback(err))
    },
    (result, doc, callback) => { // 3
      if (result && !doc) {
        var model = new History({
          user: result.user,
          solveType: result.solveType,
          query: result.query,
          result: result.res
        })
        model.save(err => err? callback(err) : callback(null, model))
      } else {
        callback(null, doc)
      }
    }
  ]

  async.waterfall(
    funcArray,
    (err, result) => err ? dfd.reject(err) : dfd.resolve(result)
  )

  return dfd.promise
}

/**
 * Exports
 */
module.exports = {
  db: mongoose,
  wolframCall: wolframCall,
  getPodById: getPodById,
  evaluate: evaluate,
  getExpType: getExpType,
  mathJsSolve: mathJsSolve
}
<commit_msg>Fixed some promises order<commit_after>// Requires
const xml2js = require('xml2js').parseString
const _ = require('lodash')
const q = require('q')
const fs = require('fs')
const async = require('async')
const unirest = require('unirest')
const mathjs = require('mathjs')
const mongoose = require('mongoose')
const utils = require('./utils.js')
const wolframAPI = process.env.app_id || null
const mongoAddress = 'mongodb://localhost'
mongoose.connect(mongoAddress)

/**
 * Checks if the expression is an equation or a simple math
 * expression. Checks if there is an = sign and/or variables
 * to decide.
 * @type {Function}
 * @param {String} expressions Math expressions
 * @return {Promise} promise
 */
var getExpType = (expression) => {
  var dfd = q.defer()

  var paramError = {message: 'Please provide a parameter'}
  var typeError = {message: 'Parameter must be a string'}

  var funcArray = [
    (callback) => {
      if (!expression) return callback(paramError)
      if (typeof expression !== 'string') return callback(typeError)

      var isEquation = expression.indexOf('=') > -1 // Needs to be improved

      if (isEquation) {
        var equation = {}
        var expressions = expression.split('=')
        equation.leftHandSide = mathjs.parse(expressions[0]).toString()
        equation.leftHandSide = mathjs.simplify(equation.leftHandSide).toString()
        equation.rightHandSide = mathjs.parse(expressions[1]).toString()
        equation.rightHandSide = mathjs.simplify(equation.rightHandSide).toString()
        callback(null, equation, isEquation)
      } else {
        expression = mathjs.parse(expression).toString()
        callback(null, expression, isEquation)
      }
    },
    (en, isEq, callback) => {
      var err = null
      var result = {
        isEq: isEq
      }
      if (isEq) {
        result.expression = en.leftHandSide + ' = ' + en.rightHandSide
      } else {
        result.expression = en
      }
      callback(err, result)
    }
  ]

  async.waterfall(
    funcArray,
    (err, result) => err ? dfd.reject(err) : dfd.resolve(result)
  )

  return dfd.promise
}

/**
 * Solves mathematical expression
 * @type {Function}
 * @param {Array} expressions Math expressions
 * @return {Promise} promise
 */
var mathJsSolve = (expressions) => {
  var dfd = q.defer()

  var paramErrorMessage = 'Please provide a parameter'
  var arrayErrorMessage = 'Expression array is empty!'
  var typeErrorMessage = 'Parameter must be an array'
  var expressionErrorMessage = 'Expression must be a string'

  var funcArray = [
    (callback) => {
      var hasExpressionError = false
      var paramError = {
        status: false,
        message: null
      }

      if ( !expressions ) {
        paramError.status = true
        paramError.message = paramErrorMessage
      }

      if ( !_.isArray(expressions) && !!expressions ) {
        paramError.status = true
        paramError.message = typeErrorMessage
      }

      if ( _.isArray(expressions) && _.isEmpty(expressions) ) {
        paramError.status = true
        paramError.message = arrayErrorMessage
      }

      var hasError = paramError.status

      if (!hasError) {
        solvedExpressions = _.map(expressions, (expression) => {
          var simplified, result, error
          try {
            if (typeof expression !== 'string') throw new Error(expressionErrorMessage)
            simplified = mathjs.simplify(expression).toString()
            result = {
              simplified: simplified,
              expression: expression,
              error: null,
              solution: mathjs.eval(simplified)
            }
          } catch (error) {
            hasExpressionError = true
            result = {
              expression: expression,
              error: error
            }
          }
          return result
        })
        if(hasExpressionError) solvedExpressions.hasExpressionError = hasExpressionError
        callback(null, solvedExpressions)
      } else {
        callback(paramError)
      }
    }
  ]

  async.waterfall(
    funcArray,
    (err, result) => err ? dfd.reject(err) : dfd.resolve(result)
  )

  return dfd.promise
}

/**
 * Wraps promise over Wolfram Module.
 * It needs input object to be validated
 * before hand. This also saves the Wolfram
 * result to the database.
 * @type {Function}
 * @param {Object} input Input information
 * @return {Promise} promise
 */
var wolframCall = (input) => {
  var dfd = q.defer()
  utils
    .buildUrl({
      input: input,
      url: 'https://api.wolframalpha.com/v2/query'
    })
    .then(bUrl => {
      unirest
        .get(bUrl.url)
        .end(res => {
          if (res.statusCode == 200) { // A OK
            xml2js(res.body, (err, result) => {
              if ( err ) return dfd.reject(err)
              if ( !result ) return dfd.reject(new Error('Result was not parsed!'))
              if ( result.queryresult.$.error === "true") return dfd.reject(result.queryresult)
              dfd.resolve(result.queryresult)
            })
          } else {
            dfd.reject(res)
          }
        })
    })
    .catch(err => dfd.reject(err))
  return dfd.promise
}

/**
 * Evaluate expression function.
 * 1 - Get Expression type
 * 2 - Send to solve
 * 3 - build and return result object
 * @type {Function} evaluate
 * @param {Object} input input data
 * @return {Promise} promise
 */
var evaluate = (input) => {
  var dfd = q.defer()

  var funcArray = [
    ( callback ) => {
      var endRes = {
        user: input.user,
        query: {},
        solution: {}
      }
      var cache = {}
      getExpType(input.query)
        .then(result => {
          if (result.isEq) { // WolframSolve
            endRes.query = result.expression
            endRes.solveType = 'wolfram'
            wolframCall(input.query)
              .then(result => {
                cache = result
                endRes.solution.$ = result.$
                return getPodById(cache.pod, 'Input')
              })
              .then(inputPod => {
                endRes.solution.inputPod = inputPod
                return getPodById(cache.pod, 'Result')
              })
              .then(resultPod => {

                endRes.solution.resultPod = resultPod
                callback(null, endRes)
              })
              .catch(err => callback(err))
          } else { // MathJS Solve
            endRes.solveType = 'mathjs'
            mathJsSolve([input.query])
              .then(result => {
                endRes.solution = result[0].solution
                endRes.simplified = result[0].simplified
                endRes.error = result[0].error
                endRes.expression = result[0].expression

                callback(null, endRes)
              })
              .catch(err => callback(err))
          }
        })
        .catch(err => callback(err))
    }
  ]

  async.waterfall(
    funcArray,
    (err, result) => err ? dfd.reject(err) : dfd.resolve(result)
  )

  return dfd.promise
}

/**
 * Returns pod by id
 * @type {Function} getPodById
 * @param {Array} pods Wolfram pods
 * @return {Promise} promise
 */
var getPodById = (pods, id) => {
  var dfd = q.defer()
  var id2 = id === 'Result'? 'Solution' : null
  var funcArray = [
    ( callback ) => { // Sweep pods looking for result id
      var count = 0
      if(!_.isArray(pods)) return callback({message: 'Parameter is not an Array!'})
      _.forEach(pods, (value) => {
        var podId = ((value || {}).$ || {}).id
        if( podId === id || podId === id2) return callback(null, value)
        count++
        if (count == pods.length) callback({message: 'No '+id +' found!'})
      })
    }
  ]

  async.waterfall(
    funcArray,
    (err, result) => err ? dfd.reject(err) : dfd.resolve(result)
  )

  return dfd.promise
}

/**
 * Main Solve function, check if exists in the database,
 * if not, evaluates the input and saves the result at the end
 * @type {Function} solve
 * @param {Object} input input data
 * @return {Promise} promise
 */
var solve = (input) => {
  var dfd = q.defer()

  var funcArray = [
    ( callback ) => { // 1
      //VALIDATE INPUT HERE
      History
        .findOne({query: input.query})
        .where('active', true)
        .exec((err, doc) => {
          callback(err, doc, input)
        })
    },
    (doc, input, callback) => { // 2
      if (doc) return callback(null, null, doc)
      evaluate(input)
        .then(result => callback(null, result, null))
        .catch(err=> callback(err))
    },
    (result, doc, callback) => { // 3
      if (result && !doc) {
        var model = new History({
          user: result.user,
          solveType: result.solveType,
          query: result.query,
          result: result.res
        })
        model.save(err => err? callback(err) : callback(null, model))
      } else {
        callback(null, doc)
      }
    }
  ]

  async.waterfall(
    funcArray,
    (err, result) => err ? dfd.reject(err) : dfd.resolve(result)
  )

  return dfd.promise
}

/**
 * Exports
 */
module.exports = {
  db: mongoose,
  wolframCall: wolframCall,
  getPodById: getPodById,
  evaluate: evaluate,
  getExpType: getExpType,
  mathJsSolve: mathJsSolve
}
<|endoftext|>


<commit_before>var config = require('./config');
var manageUsers = require('./manageUsers.js');
var request = require('request');
//var mongo = require('./mongoFunctions.js');
var adm1 = config.adm1;
var adm2 = config.adm2;

function createMessage(type, id, content, callback){
	var message = {
		recipient: {
		  id: id
		}
	};
	console.log(content);
	if(type == 'text'){
		message['message']={text: content};
	}else{
		message['message']={
			attachment:{
				type: 'template',
				payload:{
					template_type: 'button',
					text: content['text'],
					buttons: content['buttons']
				}
			}
		}
	}
	setImmediate(function(){
		callback(message);
	});
}

function createButtons(tab, callback){
	var buttons=[];
	for(var i = 0; i < tab.length; i++){
		var btn = tab[i];
		var singleBTN={};
		singleBTN['type']=btn[0];
		singleBTN['title']=btn[2];
		if(btn[0]=='web_url'){
			singleBTN['url']=btn[1];
		} else if(btn[0]=='postback'){
			singleBTN['payload']=btn[1];
		}
		console.log(singleBTN);
		console.log(JSON.stringify(singleBTN));
		buttons.push(singleBTN);
	}
	setImmediate(function(){
		console.log(buttons);
		callback(buttons);
	});
}

function sendSubstitutions(senderID, message){
	var admMessage1 = {
		recipient: {
		  id: adm1
		},
		message: {
		  attachment:{
			  type: 'template',
			  payload:{
					template_type: 'button',
					text: 'nowa wiadomość:\n' + message,
					buttons:[
						{
							type: 'web_url',
							url: 'https://www.facebook.com/Zastępstwa-dla-szkół-573446562859405/messages',
							title: 'Odpowiedz'
						}
					]
			  }
		  }
		}
	};
	var admMessage2 = {
		recipient: {
		  id: adm2
		},
		message: {
		  attachment:{
			  type: 'template',
			  payload:{
					template_type: 'button',
					text: 'nowa wiadomość:\n' + message,
					buttons:[
						{
							type: 'web_url',
							url: 'https://www.facebook.com/Zastępstwa-dla-szkół-573446562859405/messages',
							title: 'Odpowiedz'
						}
					]
			  }
		  }
		}
	};
	var body = {
		'mode': 'classList',
		'param': 'today'
	};
	message = message.toLowerCase();
	var opt = message[0];
	var help = 'Dostępne polecenia to:\n"0 <klasa>" - zastępstwa dla klasy na dzisiaj\n"1 <klasa>" - zastępstwa dla klasy na jutro\n"2 <pytanie>" - pomoc';
	var messageData = {
		recipient: {
		  id: senderID
		},
		message: {
		  text: help
		}
	};
	var reqClass = message[2] + message[3];
	if(reqClass[1]=='g'){
		reqClass += message[4];
	}
	switch(opt){
		case '0':
			break;
		case '1':
			body['param']='tomorrow'; //masz else, więc jak dam dobrze to przejdzie, chociaż ty piszesz źle
			break;
		case '2':
			body['mode']='NO';
			createMessage('text', senderID, 'Skontaktujemy się aby odpowiedzieć na pytanie.', function(message){
				callSendAPI(message);
			});
			callSendAPI(admMessage1);
			callSendAPI(admMessage2);
			break;
		/*case '3':
			body['mode']='NO';
			createMessage('text', senderID, 'Jakiś tekst - test', function(message){
				callSendAPI(message);
			});
			createButtons([['web_url', 'https://google.com', 'TEST LINK'],['postback', 'payload', 'POSTBACK - TEST']], function(buttons){
				var content={
					'text': 'TEST GENERIC',
					'buttons': buttons
				}
				createMessage('generic', senderID, content, function(message){
					callSendAPI(message);
				});
			});
			break;*/
		default:
			body['mode']='NO';
			createMessage('text', senderID, help, function(message){
				callSendAPI(message);
			});
			break;
	};
	if(body['mode'] != 'NO'){
		manageUsers.postCall('0000', body, function(classes){
			var is = 0;
			for(var i = 0; i < classes.length; i++){
				if(classes[i] == reqClass){
					is++;
				}
			}
			if(is > 0){
				var messageData = {
					recipient: {
					  id: senderID
					},
					message: {
					  attachment:{
						  type: 'template',
						  payload:{
								template_type: 'button',
								text: 'zastępstwa',
								buttons:[
									{
										type: 'web_url',
										url: 'https://domek.emadar.eu',
										title: 'Sprawdź na stronie'
									},
									{
										type: 'postback',
										title: 'Send on chat',
										payload: message
									}
								]
						  }
					  }
					}
				};
			} else {
				var messageData = {
					recipient: {
					  id: senderID
					},
					message: {
					  text: 'brak zastępstw'
					}
				};
			}
			callSendAPI(messageData);
		});
	}
}

function sendList(senderID, message){
	var body = {
		'mode': 'getChanges',
		'param': 'today'
	};
	if(message[0]=='1'){
		body['param']='tomorrow';
	}
	var reqClass = message[2] + message[3];
	if(reqClass[1]=='g'){
		reqClass += message[4];
	}
	manageUsers.postCall('0000', body, function(obj){
		var subs = obj['substitution'];
		var msg = "";
		for(var i = 0; i < subs.length; i++){
			var oneSub = subs[i];
			if(oneSub.classes == reqClass){
				var changes = oneSub['changes'];
				if(oneSub.cancelled[0]){
					msg+='anulowanie';
				} else {
					msg+='Typ: ' + oneSub.substitution_types;
				}
				msg+='\nLekcja: ' + oneSub.periods;
				msg+='\nNauczyciel: ' + oneSub.teachers;
				if(changes){
					if(changes.teachers){
						msg+=' => ' + changes.teachers;
					}
				}
				msg+='\nPrzedmiot: ' + oneSub.subjects;
				if(changes){
					if(changes.subjects){
						msg+= ' => ' + changes.subjects;
					}
				}
				msg+='\nSala: ' + oneSub.classrooms;
				if(changes){
					if(changes.classrooms){
						msg+=' => ' + changes.classrooms;
					}
				}
				if(oneSub.groupnames){
					if(oneSub.groupnames != ""){
						msg+='\nGrupa: ' + oneSub.groupnames;
					}
				}
				if(oneSub.note){
					if(oneSub.note != ""){
						msg+='\nKomentarz: '  + oneSub.note;
					}
				}
				var messageData = {
					recipient: {
						id: senderID
					},
					message: {
						text: msg
					}
				};
				callSendAPI(messageData);
				msg='';
			}
		}
	});
}

function receivedPostback(event) {
	var senderID = event.sender.id;
	var recipientID = event.recipient.id;
	var timeOfPostback = event.timestamp;

	// The 'payload' param is a developer-defined field which is set in a postback
	// button for Structured Messages.
	var payload = event.postback.payload;

	console.log("Received postback for user %d and page %d with payload '%s' " +
	"at %d", senderID, recipientID, payload, timeOfPostback);

	sendList(senderID, payload);
}

function receivedMessage(event) {
	var senderID = event.sender.id;
	var recipientID = event.recipient.id;
	var timeOfMessage = event.timestamp;
	var message = event.message;

	console.log("Received message for user %d and page %d at %d with message:",
	senderID, recipientID, timeOfMessage);
	console.log(JSON.stringify(message));

	var messageId = message.mid;

	var messageText = message.text;
	var messageAttachments = message.attachments;
	var help = 'NIE PRZYJMUJEMY ZAŁĄCZNIKÓW\nDostępne polecenia to:\n"0 <klasa>" - zastępstwa dla klasy na dzisiaj\n"1 <klasa>" - zastępstwa dla klasy na jutro\n"2 <pytanie>" - pomoc';

	if (messageText) {
		sendSubstitutions(senderID, messageText);
	} else {
		var messageData = {
			recipient: {
			  id: senderID
			},
			message: {
			  text: help
			}
		};
		callSendAPI(messageData);
	}
}

function callSendAPI(messageData) {
  request({
    uri: 'https://graph.facebook.com/v2.7/me/messages',
    qs: { access_token: config.pageToken },
    method: 'POST',
    json: messageData

  }, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      var recipientId = body.recipient_id;
      var messageId = body.message_id;

      console.log("Successfully sent generic message with id %s to recipient %s",
        messageId, recipientId);
    } else {
      console.error("Unable to send message.");
      console.error(response);
      console.error(error);
    }
  });
}

/*function sendTextMessage(recipientId, messageText) {
  var messageData = {
    recipient: {
      id: recipientId
    },
    message: {
      text: messageText
    }
  };

  callSendAPI(messageData);
}*/

/*function sendGenericMessage(recipientId) {
  var messageData = {
    recipient: {
      id: recipientId
    },
    message: {
      attachment: {
        type: "template",
        payload: {
          template_type: "generic",
          elements: [{
            title: "rift",
            subtitle: "Next-generation virtual reality",
            item_url: "https://www.oculus.com/en-us/rift/",
            image_url: "http://messengerdemo.parseapp.com/img/rift.png",
            buttons: [{
              type: "web_url",
              url: "https://www.oculus.com/en-us/rift/",
              title: "Open Web URL"
            }, {
              type: "postback",
              title: "Call Postback",
              payload: "Payload for first bubble",
            }],
          }, {
            title: "touch",
            subtitle: "Your Hands, Now in VR",
            item_url: "https://www.oculus.com/en-us/touch/",
            image_url: "http://messengerdemo.parseapp.com/img/touch.png",
            buttons: [{
              type: "web_url",
              url: "https://www.oculus.com/en-us/touch/",
              title: "Open Web URL"
            }, {
              type: "postback",
              title: "Call Postback",
              payload: "Payload for second bubble",
            }]
          }]
        }
      }
    }
  };

  callSendAPI(messageData);
}*/

exports.receivedPostback=receivedPostback;
exports.receivedMessage=receivedMessage;
<commit_msg>Use func to create messages, change some var names<commit_after>var config = require('./config');
var manageUsers = require('./manageUsers.js');
var request = require('request');
//var mongo = require('./mongoFunctions.js');
var adm1 = config.adm1;
var adm2 = config.adm2;

function createMessage(type, id, content, callback){
	var message = {
		recipient: {
		  id: id
		}
	};
	console.log(content);
	if(type == 'text'){
		message['message']={text: content};
	}else{
		message['message']={
			attachment:{
				type: 'template',
				payload:{
					template_type: 'button',
					text: content['text'],
					buttons: content['buttons']
				}
			}
		}
	}
	setImmediate(function(){
		callback(message);
	});
}

function createButtons(tab, callback){
	var buttons=[];
	for(var i = 0; i < tab.length; i++){
		var btn = tab[i];
		var singleBTN={};
		singleBTN['type']=btn[0];
		singleBTN['title']=btn[2];
		if(btn[0]=='web_url'){
			singleBTN['url']=btn[1];
		} else if(btn[0]=='postback'){
			singleBTN['payload']=btn[1];
		}
		console.log(singleBTN);
		console.log(JSON.stringify(singleBTN));
		buttons.push(singleBTN);
	}
	setImmediate(function(){
		console.log(buttons);
		callback(buttons);
	});
}

function sendSubstitutions(senderID, message){
	var admMessage1 = {
		recipient: {
		  id: adm1
		},
		message: {
		  attachment:{
			  type: 'template',
			  payload:{
					template_type: 'button',
					text: 'nowa wiadomość:\n' + message,
					buttons:[
						{
							type: 'web_url',
							url: 'https://www.facebook.com/Zastępstwa-dla-szkół-573446562859405/messages',
							title: 'Odpowiedz'
						}
					]
			  }
		  }
		}
	};
	var admMessage2 = {
		recipient: {
		  id: adm2
		},
		message: {
		  attachment:{
			  type: 'template',
			  payload:{
					template_type: 'button',
					text: 'nowa wiadomość:\n' + message,
					buttons:[
						{
							type: 'web_url',
							url: 'https://www.facebook.com/Zastępstwa-dla-szkół-573446562859405/messages',
							title: 'Odpowiedz'
						}
					]
			  }
		  }
		}
	};
	var body = {
		'mode': 'classList',
		'param': 'today'
	};
	message = message.toLowerCase();
	var opt = message[0];
	var help = 'Dostępne polecenia to:\n"0 <klasa>" - zastępstwa dla klasy na dzisiaj\n"1 <klasa>" - zastępstwa dla klasy na jutro\n"2 <pytanie>" - pomoc';
	var reqClass = message[2] + message[3];
	if(reqClass[1]=='g'){
		reqClass += message[4];
	}
	switch(opt){
		case '0':
			break;
		case '1':
			body['param']='tomorrow'; //masz else, więc jak dam dobrze to przejdzie, chociaż ty piszesz źle
			break;
		case '2':
			body['mode']='NO';
			createMessage('text', senderID, 'Skontaktujemy się aby odpowiedzieć na pytanie.', function(messageTS){
				callSendAPI(messageTS);
			});
			callSendAPI(admMessage1);
			callSendAPI(admMessage2);
			break;
		/*case '3':
			body['mode']='NO';
			createMessage('text', senderID, 'Jakiś tekst - test', function(message){
				callSendAPI(message);
			});
			createButtons([['web_url', 'https://google.com', 'TEST LINK'],['postback', 'payload', 'POSTBACK - TEST']], function(buttons){
				var content={
					'text': 'TEST GENERIC',
					'buttons': buttons
				}
				createMessage('generic', senderID, content, function(message){
					callSendAPI(message);
				});
			});
			break;*/
		default:
			body['mode']='NO';
			createMessage('text', senderID, help, function(messageTS){
				callSendAPI(messageTS);
			});
			break;
	};
	if(body['mode'] != 'NO'){
		manageUsers.postCall('0000', body, function(classes){
			var is = 0;
			for(var i = 0; i < classes.length; i++){
				if(classes[i] == reqClass){
					is++;
				}
			}
			if(opt == 0){
				var dayToMSG = 'Jutro';
			} else {
				var dayToMSG = 'Dzisiaj';
			}
			if(is > 0){
				createButtons([['web_url', 'https://domek.emadar.eu', 'Sprawdź na stronie'],['postback', message, 'Wyślij na czacie']], function(buttons){
					dayToMSG += ' są zastępstwa dla klasy ' + reqClass;
					var content={
						text: dayToMSG,
						buttons: buttons
					}
					createMessage('generic', senderID, content, function(messageTS){
						callSendAPI(messageTS);
					});
				});
			} else {
				dayToMSG += ' brak zastępstw dla klasy ' + reqClass;
				createMessage('text', senderID, dayToMSG, function(messageTS){
					callSendAPI(messageTS);
				});
			}
		});
	}
}

function sendList(senderID, message){
	var body = {
		'mode': 'getChanges',
		'param': 'today'
	};
	if(message[0]=='1'){
		body['param']='tomorrow';
	}
	var reqClass = message[2] + message[3];
	if(reqClass[1]=='g'){
		reqClass += message[4];
	}
	manageUsers.postCall('0000', body, function(obj){
		var subs = obj['substitution'];
		var msg = "";
		for(var i = 0; i < subs.length; i++){
			var oneSub = subs[i];
			if(oneSub.classes == reqClass){
				var changes = oneSub['changes'];
				if(oneSub.cancelled[0]){
					msg+='anulowanie';
				} else {
					msg+='Typ: ' + oneSub.substitution_types;
				}
				msg+='\nLekcja: ' + oneSub.periods;
				msg+='\nNauczyciel: ' + oneSub.teachers;
				if(changes){
					if(changes.teachers){
						msg+=' => ' + changes.teachers;
					}
				}
				msg+='\nPrzedmiot: ' + oneSub.subjects;
				if(changes){
					if(changes.subjects){
						msg+= ' => ' + changes.subjects;
					}
				}
				msg+='\nSala: ' + oneSub.classrooms;
				if(changes){
					if(changes.classrooms){
						msg+=' => ' + changes.classrooms;
					}
				}
				if(oneSub.groupnames){
					if(oneSub.groupnames != ""){
						msg+='\nGrupa: ' + oneSub.groupnames;
					}
				}
				if(oneSub.note){
					if(oneSub.note != ""){
						msg+='\nKomentarz: '  + oneSub.note;
					}
				}
				createMessage('text', senderID, msg, function(messageTS){
					callSendAPI(messageTS);
				});
				msg='';
			}
		}
	});
}

function receivedPostback(event) {
	var senderID = event.sender.id;
	var recipientID = event.recipient.id;
	var timeOfPostback = event.timestamp;

	// The 'payload' param is a developer-defined field which is set in a postback
	// button for Structured Messages.
	var payload = event.postback.payload;

	console.log("Received postback for user %d and page %d with payload '%s' " +
	"at %d", senderID, recipientID, payload, timeOfPostback);

	sendList(senderID, payload);
}

function receivedMessage(event) {
	var senderID = event.sender.id;
	var recipientID = event.recipient.id;
	var timeOfMessage = event.timestamp;
	var message = event.message;

	console.log("Received message for user %d and page %d at %d with message:",
	senderID, recipientID, timeOfMessage);
	console.log(JSON.stringify(message));

	var messageId = message.mid;

	var messageText = message.text;
	var messageAttachments = message.attachments;
	var help = 'NIE PRZYJMUJEMY ZAŁĄCZNIKÓW\nDostępne polecenia to:\n"0 <klasa>" - zastępstwa dla klasy na dzisiaj\n"1 <klasa>" - zastępstwa dla klasy na jutro\n"2 <pytanie>" - pomoc';

	if (messageText) {
		sendSubstitutions(senderID, messageText);
	} else {
		createMessage('text', senderID, help, function(messageTS){
			callSendAPI(messageTS);
		});
	}
}

function callSendAPI(messageData) {
  request({
    uri: 'https://graph.facebook.com/v2.7/me/messages',
    qs: { access_token: config.pageToken },
    method: 'POST',
    json: messageData

  }, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      var recipientId = body.recipient_id;
      var messageId = body.message_id;

      console.log("Successfully sent generic message with id %s to recipient %s",
        messageId, recipientId);
    } else {
      console.error("Unable to send message.");
      console.error(response);
      console.error(error);
    }
  });
}

/*function sendTextMessage(recipientId, messageText) {
  var messageData = {
    recipient: {
      id: recipientId
    },
    message: {
      text: messageText
    }
  };

  callSendAPI(messageData);
}*/

/*function sendGenericMessage(recipientId) {
  var messageData = {
    recipient: {
      id: recipientId
    },
    message: {
      attachment: {
        type: "template",
        payload: {
          template_type: "generic",
          elements: [{
            title: "rift",
            subtitle: "Next-generation virtual reality",
            item_url: "https://www.oculus.com/en-us/rift/",
            image_url: "http://messengerdemo.parseapp.com/img/rift.png",
            buttons: [{
              type: "web_url",
              url: "https://www.oculus.com/en-us/rift/",
              title: "Open Web URL"
            }, {
              type: "postback",
              title: "Call Postback",
              payload: "Payload for first bubble",
            }],
          }, {
            title: "touch",
            subtitle: "Your Hands, Now in VR",
            item_url: "https://www.oculus.com/en-us/touch/",
            image_url: "http://messengerdemo.parseapp.com/img/touch.png",
            buttons: [{
              type: "web_url",
              url: "https://www.oculus.com/en-us/touch/",
              title: "Open Web URL"
            }, {
              type: "postback",
              title: "Call Postback",
              payload: "Payload for second bubble",
            }]
          }]
        }
      }
    }
  };

  callSendAPI(messageData);
}*/

exports.receivedPostback=receivedPostback;
exports.receivedMessage=receivedMessage;
<|endoftext|>


<commit_before>var mbaasApi = require('fh-mbaas-api');
var q = require('q');
var request = require('request');
var memecanvas = require('memecanvas');
var dateFormat = require('dateformat');

var Meme = function () {
    this.create = create;

    memecanvas.init('static', '-meme');

    function create(req, res) {
        memecanvas.generate(req.file.path, req.body.topMessage, req.body.bottomMessage, function (error, memefilename) {
            if (error) {
                res.statusCode = 500;
                res.end(error);
            } else {
                var today = dateFormat(new Date(), "mmm dS, yyyy h:MM:ss TT");;
                var meme = {
                    ownerId : req.get('X-FH-sessionToken'),
                    createdOn : today,
                    updatedOn : today,
                    sharedWith : [],
                    commits : [{
                        ownerId : req.get('X-FH-sessionToken'),
                        createdOn : today,
                        updatedOn : today,
                        photoUrl : req.protocol + '://' + req.get('host') + '/static/' + memefilename,
                        comments : [{
                            ownerId : req.get('X-FH-sessionToken'),
                            createdOn : today,
                            updatedOn : today,
                            comment : "Posted " + today
                        }]
                    }]
                };

                console.log(memefilename);
                console.log(req.file);
                console.log(req.body);
                mbaasApi.db({
                    "act": "create",
                    "type": 'photos',
                    "fields" : meme
                },  function(err, data) {
                    if (err) {
                        res.statusCode = 500;
                        res.end(err);
                    } else {
                        res.statusCode = 200;
                        res.json({});
                    }
                });

            }
        });

    }

};

module.exports = new Meme();
<commit_msg>update file /lib/meme.js<commit_after>var mbaasApi = require('fh-mbaas-api');
var q = require('q');
var request = require('request');
var memecanvas = require('memecanvas');
var dateFormat = require('dateformat');

var Meme = function () {
    this.create = create;

    memecanvas.init('static', '-meme');

    function create(req, res) {
        memecanvas.generate(req.file.path, req.body.topMessage, req.body.bottomMessage, function (error, memefilename) {
            if (error) {
                res.statusCode = 500;
                res.end(error);
            } else {
                var today = dateFormat(new Date(), "mmm dS, yyyy h:MM:ss TT");;
                var meme = {
                    ownerId : req.get('X-FH-sessionToken'),
                    createdOn : today,
                    updatedOn : today,
                    sharedWith : [],
                    commits : [{
                        ownerId : req.get('X-FH-sessionToken'),
                        createdOn : today,
                        updatedOn : today,
                        photoUrl : req.protocol + '://' + req.get('host') + '/static/' + memefilename,
                        comments : [{
                            ownerId : req.get('X-FH-sessionToken'),
                            createdOn : today,
                            updatedOn : today,
                            comment : "Posted " + today
                        }]
                    }]
                };

                console.log(memefilename);
                console.log(req.file);
                console.log(req.body);
                mbaasApi.db({
                    "act": "create",
                    "type": 'photos',
                    "fields" : meme
                }, function(err, data) {
                    if (err) {
                        res.statusCode = 500;
                        res.end(err);
                    } else {
                        res.statusCode = 200;
                        res.json({});
                    }
                });

            }
        });

    }

};

module.exports = new Meme();
<|endoftext|>


<commit_before>const request = require('request');
const path = require('path');
const Promise = require('bluebird');
const fs = require('fs');

const elasticsearch = require('../src/server/plugins/gp-lookup/elasticsearch');

const GPMedicalPracticesSourceURL = process.env.GPMedicalPracticesHost || 'https://raw.githubusercontent.com/nhsuk/general-medical-practices/master/output/general-medical-practices.json';

const GPMedicalPractitionersSourceURL = process.env.GPMedicalPracticesHost || 'https://raw.githubusercontent.com/nhsuk/general-medical-practitioners/master/output/general-medical-practitioners.json';

const appRootPath = path.dirname(__dirname);
const practiceDataPath = path.join(appRootPath, 'data/practiceData.json');

const requestAsync = Promise.promisify(request);

Promise.all([requestAsync(GPMedicalPracticesSourceURL), requestAsync(GPMedicalPractitionersSourceURL)])
    .then(function(allData) {
      const practicesData = JSON.parse(allData[0].body);
      const practitionersData = JSON.parse(allData[1].body);
      transformPracticeData(practicesData, practitionersData);
    });

function transformPracticeData(practicesData, practitionersData) {
  let practiceList = [];
  for(let i = 0; i < practicesData.length; i++){
    let practice = practicesData[i];

    let gpPractitioners = [];
    for(let c = 0; c < practitionersData.length; c++){
      let practitioner = practitionersData[c];
      let practiceCode = `general-medical-practice:${practice.organisation_code}`;

      if (practitioner.practice === practiceCode){
        gpPractitioners.push(practitioner);
      }
    }
    practice['practitioners'] = gpPractitioners;
    practiceList.push(practice);
  }
  savePracticeData(practiceList);
}

function savePracticeData(practiceList) {
  fs.writeFile(practiceDataPath, JSON.stringify(practiceList, null, 2), function (err) {
    if (err) {
      throw err;
    }
    let data = loadDataFromFile();
    updatePracticesSearchIndex(data);
  });
}

function loadDataFromFile() {
  return fs.readFileSync(practiceDataPath,'utf8');
}

function updatePracticesSearchIndex(data) {
  let items = [];
  elasticsearch.deleteIndex(elasticsearch.GPlookupIndexName, function (err, res) {
    elasticsearch.createGPLookupSearchIndex(function (err, res) {
      if (err) {
        throw err;
      }

      const practitionersData = JSON.parse(data);
      for(let i = 0; i < practitionersData.length; i++){
        let practice = practitionersData[i];
        items.push({index: {_index: elasticsearch.GPlookupIndexName,  _type: 'practice', _id: practice.organisation_code}});
        items.push(practice);
      }

      elasticsearch.bulkUpdate(items, function (err, res) {
        if (err) {
          throw err;
        }
        console.log('All practice data are indexed.');
      });
    });
  });
}<commit_msg>clean update-gp-lookup-search-index.js<commit_after>const request = require('request');
const path = require('path');
const Promise = require('bluebird');
const fs = require('fs');

const elasticsearch = require('../src/server/plugins/gp-lookup/elasticsearch');

const GPMedicalPracticesSourceURL = process.env.GPMedicalPracticesSourceURL;

const GPMedicalPractitionersSourceURL = process.env.GPMedicalPractitionersSourceURL;

const appRootPath = path.dirname(__dirname);
const practiceDataPath = path.join(appRootPath, 'data/practiceData.json');

const requestAsync = Promise.promisify(request);

Promise.all([requestAsync(GPMedicalPracticesSourceURL), requestAsync(GPMedicalPractitionersSourceURL)])
    .then(function(allData) {
      console.log('Getting practices data from sources');
      const practicesData = JSON.parse(allData[0].body);
      const practitionersData = JSON.parse(allData[1].body);
      transformPracticeData(practicesData, practitionersData);
    });

function transformPracticeData(practicesData, practitionersData) {
  let practiceList = [];
  for(let i = 0; i < practicesData.length; i++){
    let practice = practicesData[i];

    let gpPractitioners = [];
    for(let c = 0; c < practitionersData.length; c++){
      let practitioner = practitionersData[c];
      let practiceCode = `general-medical-practice:${practice.organisation_code}`;

      if (practitioner.practice === practiceCode){
        gpPractitioners.push(practitioner);
      }
    }
    practice['practitioners'] = gpPractitioners;
    practiceList.push(practice);
  }
  savePracticeData(practiceList);
}

function savePracticeData(practiceList) {
  fs.writeFile(practiceDataPath, JSON.stringify(practiceList, null, 2), function (err) {
    if (err) {
      throw err;
    }
    let data = loadDataFromFile();
    updatePracticeSearchIndex(data);
  });
}

function loadDataFromFile() {
  return fs.readFileSync(practiceDataPath,'utf8');
}

function updatePracticeSearchIndex(data) {
  elasticsearch.indexIsExists(elasticsearch.GPlookupIndexName, function (err, exists) {
    if (exists){
      elasticsearch.deleteIndex(elasticsearch.GPlookupIndexName, function (err, res) {
        elasticsearch.createGPLookupSearchIndex(function (err, res) {
          if (err) {
            throw err;
          }
          updateSearchIndexitems(data);
        });
      });
    }else{
      elasticsearch.createGPLookupSearchIndex(function (err, res) {
        if (err) {
          throw err;
        }
        updateSearchIndexitems(data);
      });
    }
  });
}

function updateSearchIndexitems(data) {
  let items = [];
  const practitionersData = JSON.parse(data);
  for(let i = 0; i < practitionersData.length; i++){
    let practice = practitionersData[i];
    items.push({index: {_index: elasticsearch.GPlookupIndexName,  _type: 'practice', _id: practice.organisation_code}});
    items.push(practice);
  }

  elasticsearch.bulkUpdate(items, function (err, res) {
    if (err) {
      throw err;
    }
    console.log('All practice data are indexed.');
  });
}<|endoftext|>


<commit_before>// @flow

import {
  createMaterialTopTabNavigator,
  type MaterialTopTabNavigationProp,
} from '@react-navigation/material-top-tabs';
import {
  createNavigatorFactory,
  useNavigationBuilder,
  type StackNavigationState,
  type StackOptions,
  type StackNavigationEventMap,
  type StackNavigatorProps,
  type ExtraStackNavigatorProps,
  type StackHeaderProps as CoreStackHeaderProps,
} from '@react-navigation/native';
import { StackView, type StackHeaderProps } from '@react-navigation/stack';
import invariant from 'invariant';
import * as React from 'react';
import { Platform, View } from 'react-native';

import { threadIsPending } from 'lib/shared/thread-utils';
import { firstLine } from 'lib/utils/string-utils';

import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react';
import { InputStateContext } from '../input/input-state';
import HeaderBackButton from '../navigation/header-back-button.react';
import {
  ComposeThreadRouteName,
  DeleteThreadRouteName,
  ThreadSettingsRouteName,
  MessageListRouteName,
  ChatThreadListRouteName,
  HomeChatThreadListRouteName,
  BackgroundChatThreadListRouteName,
  type ScreenParamList,
  type ChatParamList,
  type ChatTopTabsParamList,
} from '../navigation/route-names';
import { useStyles } from '../themes/colors';
import BackgroundChatThreadList from './background-chat-thread-list.react';
import ChatHeader from './chat-header.react';
import ChatRouter, { type ChatRouterNavigationProp } from './chat-router';
import ComposeThreadButton from './compose-thread-button.react';
import ComposeThread from './compose-thread.react';
import HomeChatThreadList from './home-chat-thread-list.react';
import MessageListContainer from './message-list-container.react';
import MessageListHeaderTitle from './message-list-header-title.react';
import MessageStorePruner from './message-store-pruner.react';
import DeleteThread from './settings/delete-thread.react';
import ThreadSettings from './settings/thread-settings.react';
import ThreadDraftUpdater from './thread-draft-updater.react';
import ThreadScreenPruner from './thread-screen-pruner.react';
import ThreadSettingsButton from './thread-settings-button.react';

const unboundStyles = {
  keyboardAvoidingView: {
    flex: 1,
  },
  view: {
    flex: 1,
    backgroundColor: 'listBackground',
  },
  threadListHeaderStyle: {
    elevation: 0,
    shadowOffset: { width: 0, height: 0 },
    borderBottomWidth: 0,
  },
};

export type ChatTopTabsNavigationProp<
  RouteName: $Keys<ChatTopTabsParamList> = $Keys<ChatTopTabsParamList>,
> = MaterialTopTabNavigationProp<ScreenParamList, RouteName>;

const homeChatThreadListOptions = {
  title: 'Home',
};
const backgroundChatThreadListOptions = {
  title: 'Background',
};

const ChatThreadsTopTab = createMaterialTopTabNavigator();
const ChatThreadsComponent = () => {
  return (
    <ChatThreadsTopTab.Navigator>
      <ChatThreadsTopTab.Screen
        name={HomeChatThreadListRouteName}
        component={HomeChatThreadList}
        options={homeChatThreadListOptions}
      />
      <ChatThreadsTopTab.Screen
        name={BackgroundChatThreadListRouteName}
        component={BackgroundChatThreadList}
        options={backgroundChatThreadListOptions}
      />
    </ChatThreadsTopTab.Navigator>
  );
};

type ChatNavigatorProps = StackNavigatorProps<ChatRouterNavigationProp<>>;
function ChatNavigator({
  initialRouteName,
  children,
  screenOptions,
  ...rest
}: ChatNavigatorProps) {
  const { state, descriptors, navigation } = useNavigationBuilder(ChatRouter, {
    initialRouteName,
    children,
    screenOptions,
  });

  // Clear ComposeThread screens after each message is sent. If a user goes to
  // ComposeThread to create a new thread, but finds an existing one and uses it
  // instead, we can assume the intent behind opening ComposeThread is resolved
  const inputState = React.useContext(InputStateContext);
  invariant(inputState, 'InputState should be set in ChatNavigator');
  const clearComposeScreensAfterMessageSend = React.useCallback(() => {
    navigation.clearScreens([ComposeThreadRouteName]);
  }, [navigation]);
  React.useEffect(() => {
    inputState.registerSendCallback(clearComposeScreensAfterMessageSend);
    return () => {
      inputState.unregisterSendCallback(clearComposeScreensAfterMessageSend);
    };
  }, [inputState, clearComposeScreensAfterMessageSend]);

  return (
    <StackView
      {...rest}
      state={state}
      descriptors={descriptors}
      navigation={navigation}
      detachInactiveScreens={Platform.OS !== 'ios'}
    />
  );
}
const createChatNavigator = createNavigatorFactory<
  StackNavigationState,
  StackOptions,
  StackNavigationEventMap,
  ChatRouterNavigationProp<>,
  ExtraStackNavigatorProps,
>(ChatNavigator);

const header = (props: CoreStackHeaderProps) => {
  // Flow has trouble reconciling identical types between different libdefs,
  // and flow-typed has no way for one libdef to depend on another
  const castProps: StackHeaderProps = (props: any);
  return <ChatHeader {...castProps} />;
};
const headerBackButton = (props) => <HeaderBackButton {...props} />;
const screenOptions = {
  header,
  headerLeft: headerBackButton,
  gestureEnabled: Platform.OS === 'ios',
  animationEnabled:
    Platform.OS !== 'web' &&
    Platform.OS !== 'windows' &&
    Platform.OS !== 'macos',
};

const chatThreadListOptions = ({ navigation }) => ({
  headerTitle: 'Threads',
  headerRight:
    Platform.OS === 'ios'
      ? () => <ComposeThreadButton navigate={navigation.navigate} />
      : undefined,
  headerBackTitle: 'Back',
  headerStyle: unboundStyles.threadListHeaderStyle,
});
const messageListOptions = ({ navigation, route }) => ({
  // This is a render prop, not a component
  // eslint-disable-next-line react/display-name
  headerTitle: () => (
    <MessageListHeaderTitle
      threadInfo={route.params.threadInfo}
      searching={route.params.searching}
      navigate={navigation.navigate}
    />
  ),
  headerTitleContainerStyle: {
    marginHorizontal: Platform.select({ ios: 80, default: 0 }),
    flex: 1,
  },
  headerRight:
    Platform.OS === 'android' && !threadIsPending(route.params.threadInfo.id)
      ? // This is a render prop, not a component
        // eslint-disable-next-line react/display-name
        () => (
          <ThreadSettingsButton
            threadInfo={route.params.threadInfo}
            navigate={navigation.navigate}
          />
        )
      : undefined,
  headerBackTitle: 'Back',
});
const composeThreadOptions = {
  headerTitle: 'Compose thread',
  headerBackTitle: 'Back',
};
const threadSettingsOptions = ({ route }) => ({
  headerTitle: firstLine(route.params.threadInfo.uiName),
  headerBackTitle: 'Back',
});
const deleteThreadOptions = {
  headerTitle: 'Delete thread',
  headerBackTitle: 'Back',
};

export type ChatNavigationProp<
  RouteName: $Keys<ChatParamList> = $Keys<ChatParamList>,
> = ChatRouterNavigationProp<ScreenParamList, RouteName>;

const Chat = createChatNavigator<
  ScreenParamList,
  ChatParamList,
  ChatNavigationProp<>,
>();
export default function ChatComponent() {
  const styles = useStyles(unboundStyles);
  const behavior = Platform.select({
    android: 'height',
    default: 'padding',
  });
  return (
    <View style={styles.view}>
      <KeyboardAvoidingView
        behavior={behavior}
        style={styles.keyboardAvoidingView}
      >
        <Chat.Navigator screenOptions={screenOptions}>
          <Chat.Screen
            name={ChatThreadListRouteName}
            component={ChatThreadsComponent}
            options={chatThreadListOptions}
          />
          <Chat.Screen
            name={MessageListRouteName}
            component={MessageListContainer}
            options={messageListOptions}
          />
          <Chat.Screen
            name={ComposeThreadRouteName}
            component={ComposeThread}
            options={composeThreadOptions}
          />
          <Chat.Screen
            name={ThreadSettingsRouteName}
            component={ThreadSettings}
            options={threadSettingsOptions}
          />
          <Chat.Screen
            name={DeleteThreadRouteName}
            component={DeleteThread}
            options={deleteThreadOptions}
          />
        </Chat.Navigator>
        <MessageStorePruner />
        <ThreadScreenPruner />
        <ThreadDraftUpdater />
      </KeyboardAvoidingView>
    </View>
  );
}
<commit_msg>[native] Render ThreadDraftUpdater only when user is logged in<commit_after>// @flow

import {
  createMaterialTopTabNavigator,
  type MaterialTopTabNavigationProp,
} from '@react-navigation/material-top-tabs';
import {
  createNavigatorFactory,
  useNavigationBuilder,
  type StackNavigationState,
  type StackOptions,
  type StackNavigationEventMap,
  type StackNavigatorProps,
  type ExtraStackNavigatorProps,
  type StackHeaderProps as CoreStackHeaderProps,
} from '@react-navigation/native';
import { StackView, type StackHeaderProps } from '@react-navigation/stack';
import invariant from 'invariant';
import * as React from 'react';
import { Platform, View } from 'react-native';
import { useSelector } from 'react-redux';

import { isLoggedIn } from 'lib/selectors/user-selectors';
import { threadIsPending } from 'lib/shared/thread-utils';
import { firstLine } from 'lib/utils/string-utils';

import KeyboardAvoidingView from '../components/keyboard-avoiding-view.react';
import { InputStateContext } from '../input/input-state';
import HeaderBackButton from '../navigation/header-back-button.react';
import {
  ComposeThreadRouteName,
  DeleteThreadRouteName,
  ThreadSettingsRouteName,
  MessageListRouteName,
  ChatThreadListRouteName,
  HomeChatThreadListRouteName,
  BackgroundChatThreadListRouteName,
  type ScreenParamList,
  type ChatParamList,
  type ChatTopTabsParamList,
} from '../navigation/route-names';
import { useStyles } from '../themes/colors';
import BackgroundChatThreadList from './background-chat-thread-list.react';
import ChatHeader from './chat-header.react';
import ChatRouter, { type ChatRouterNavigationProp } from './chat-router';
import ComposeThreadButton from './compose-thread-button.react';
import ComposeThread from './compose-thread.react';
import HomeChatThreadList from './home-chat-thread-list.react';
import MessageListContainer from './message-list-container.react';
import MessageListHeaderTitle from './message-list-header-title.react';
import MessageStorePruner from './message-store-pruner.react';
import DeleteThread from './settings/delete-thread.react';
import ThreadSettings from './settings/thread-settings.react';
import ThreadDraftUpdater from './thread-draft-updater.react';
import ThreadScreenPruner from './thread-screen-pruner.react';
import ThreadSettingsButton from './thread-settings-button.react';

const unboundStyles = {
  keyboardAvoidingView: {
    flex: 1,
  },
  view: {
    flex: 1,
    backgroundColor: 'listBackground',
  },
  threadListHeaderStyle: {
    elevation: 0,
    shadowOffset: { width: 0, height: 0 },
    borderBottomWidth: 0,
  },
};

export type ChatTopTabsNavigationProp<
  RouteName: $Keys<ChatTopTabsParamList> = $Keys<ChatTopTabsParamList>,
> = MaterialTopTabNavigationProp<ScreenParamList, RouteName>;

const homeChatThreadListOptions = {
  title: 'Home',
};
const backgroundChatThreadListOptions = {
  title: 'Background',
};

const ChatThreadsTopTab = createMaterialTopTabNavigator();
const ChatThreadsComponent = () => {
  return (
    <ChatThreadsTopTab.Navigator>
      <ChatThreadsTopTab.Screen
        name={HomeChatThreadListRouteName}
        component={HomeChatThreadList}
        options={homeChatThreadListOptions}
      />
      <ChatThreadsTopTab.Screen
        name={BackgroundChatThreadListRouteName}
        component={BackgroundChatThreadList}
        options={backgroundChatThreadListOptions}
      />
    </ChatThreadsTopTab.Navigator>
  );
};

type ChatNavigatorProps = StackNavigatorProps<ChatRouterNavigationProp<>>;
function ChatNavigator({
  initialRouteName,
  children,
  screenOptions,
  ...rest
}: ChatNavigatorProps) {
  const { state, descriptors, navigation } = useNavigationBuilder(ChatRouter, {
    initialRouteName,
    children,
    screenOptions,
  });

  // Clear ComposeThread screens after each message is sent. If a user goes to
  // ComposeThread to create a new thread, but finds an existing one and uses it
  // instead, we can assume the intent behind opening ComposeThread is resolved
  const inputState = React.useContext(InputStateContext);
  invariant(inputState, 'InputState should be set in ChatNavigator');
  const clearComposeScreensAfterMessageSend = React.useCallback(() => {
    navigation.clearScreens([ComposeThreadRouteName]);
  }, [navigation]);
  React.useEffect(() => {
    inputState.registerSendCallback(clearComposeScreensAfterMessageSend);
    return () => {
      inputState.unregisterSendCallback(clearComposeScreensAfterMessageSend);
    };
  }, [inputState, clearComposeScreensAfterMessageSend]);

  return (
    <StackView
      {...rest}
      state={state}
      descriptors={descriptors}
      navigation={navigation}
      detachInactiveScreens={Platform.OS !== 'ios'}
    />
  );
}
const createChatNavigator = createNavigatorFactory<
  StackNavigationState,
  StackOptions,
  StackNavigationEventMap,
  ChatRouterNavigationProp<>,
  ExtraStackNavigatorProps,
>(ChatNavigator);

const header = (props: CoreStackHeaderProps) => {
  // Flow has trouble reconciling identical types between different libdefs,
  // and flow-typed has no way for one libdef to depend on another
  const castProps: StackHeaderProps = (props: any);
  return <ChatHeader {...castProps} />;
};
const headerBackButton = (props) => <HeaderBackButton {...props} />;
const screenOptions = {
  header,
  headerLeft: headerBackButton,
  gestureEnabled: Platform.OS === 'ios',
  animationEnabled:
    Platform.OS !== 'web' &&
    Platform.OS !== 'windows' &&
    Platform.OS !== 'macos',
};

const chatThreadListOptions = ({ navigation }) => ({
  headerTitle: 'Threads',
  headerRight:
    Platform.OS === 'ios'
      ? () => <ComposeThreadButton navigate={navigation.navigate} />
      : undefined,
  headerBackTitle: 'Back',
  headerStyle: unboundStyles.threadListHeaderStyle,
});
const messageListOptions = ({ navigation, route }) => ({
  // This is a render prop, not a component
  // eslint-disable-next-line react/display-name
  headerTitle: () => (
    <MessageListHeaderTitle
      threadInfo={route.params.threadInfo}
      searching={route.params.searching}
      navigate={navigation.navigate}
    />
  ),
  headerTitleContainerStyle: {
    marginHorizontal: Platform.select({ ios: 80, default: 0 }),
    flex: 1,
  },
  headerRight:
    Platform.OS === 'android' && !threadIsPending(route.params.threadInfo.id)
      ? // This is a render prop, not a component
        // eslint-disable-next-line react/display-name
        () => (
          <ThreadSettingsButton
            threadInfo={route.params.threadInfo}
            navigate={navigation.navigate}
          />
        )
      : undefined,
  headerBackTitle: 'Back',
});
const composeThreadOptions = {
  headerTitle: 'Compose thread',
  headerBackTitle: 'Back',
};
const threadSettingsOptions = ({ route }) => ({
  headerTitle: firstLine(route.params.threadInfo.uiName),
  headerBackTitle: 'Back',
});
const deleteThreadOptions = {
  headerTitle: 'Delete thread',
  headerBackTitle: 'Back',
};

export type ChatNavigationProp<
  RouteName: $Keys<ChatParamList> = $Keys<ChatParamList>,
> = ChatRouterNavigationProp<ScreenParamList, RouteName>;

const Chat = createChatNavigator<
  ScreenParamList,
  ChatParamList,
  ChatNavigationProp<>,
>();
export default function ChatComponent() {
  const styles = useStyles(unboundStyles);
  const behavior = Platform.select({
    android: 'height',
    default: 'padding',
  });
  const loggedIn = useSelector(isLoggedIn);
  let draftUpdater = null;
  if (loggedIn) {
    draftUpdater = <ThreadDraftUpdater />;
  }
  return (
    <View style={styles.view}>
      <KeyboardAvoidingView
        behavior={behavior}
        style={styles.keyboardAvoidingView}
      >
        <Chat.Navigator screenOptions={screenOptions}>
          <Chat.Screen
            name={ChatThreadListRouteName}
            component={ChatThreadsComponent}
            options={chatThreadListOptions}
          />
          <Chat.Screen
            name={MessageListRouteName}
            component={MessageListContainer}
            options={messageListOptions}
          />
          <Chat.Screen
            name={ComposeThreadRouteName}
            component={ComposeThread}
            options={composeThreadOptions}
          />
          <Chat.Screen
            name={ThreadSettingsRouteName}
            component={ThreadSettings}
            options={threadSettingsOptions}
          />
          <Chat.Screen
            name={DeleteThreadRouteName}
            component={DeleteThread}
            options={deleteThreadOptions}
          />
        </Chat.Navigator>
        <MessageStorePruner />
        <ThreadScreenPruner />
        {draftUpdater}
      </KeyboardAvoidingView>
    </View>
  );
}
<|endoftext|>


<commit_before>var http = require('http');
var app= http.createServer();
var io = require('socket.io')(app);
var cookie_reader = require('cookie');
var querystring = require('querystring');

app.listen(4000);

var HOST = 'geekattitude.org';
var HOST_PORT = 80;

io.use(function(socket, next) {
    var handshakeData = socket.request;
    if(handshakeData.headers.cookie){
        next();
    }
    next(new Error('not authorized'));
});

var connected_users = {};
io.on('connection', function(socket){
    var user = cookie_reader.parse(socket.request.headers.cookie).sessionid
    if (user in connected_users) {
        connected_users[user] += 1;
    } else {
        connected_users[user] = 1;
        console.log(user + ' connected');
        // Tell django the user has come online
        var data = querystring.stringify({
            sessionid: user,
            status: 'connected'
        });
        var options = {
            host: HOST,
            port: HOST_PORT,
            path: '/user/node_api/',
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': data.length
            }
        };
        var req = http.request(options, function(res){
            res.setEncoding('utf8');
            res.on('data', function(chunk){
                console.log("body: " + chunk)
            })
        });
        req.write(data);
        req.end();
    };
    socket.on('disconnect', function(){
        setTimeout(function() {
            if (connected_users[user] === 1) {
                delete connected_users[user];
                console.log(user + ' disconnected');
                // Tell django the user is now offline
                var data = querystring.stringify({
                    sessionid: user,
                    status: 'disconnected'
                })
                var options = {
                    host: HOST,
                    port: HOST_PORT,
                    path: '/user/node_api/',
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                        'Content-Length': data.length
                    }
                }
                var req = http.request(options, function(res){
                    res.setEncoding('utf8');
                    res.on('data', function(chunk){
                        console.log("body: " + chunk)
                    })
                })
                req.write(data);
                req.end();
            } else {
                connected_users[user] -= 1;
            };
        }, 2 * 1000);
    });
})
<commit_msg>Moved host and port in a separate module.<commit_after>var http = require('http');
var app= http.createServer();
var io = require('socket.io')(app);
var cookie_reader = require('cookie');
var querystring = require('querystring');
var settings = require('./settings');

var HOST = settings.host;
var HOST_PORT = settings.port;

app.listen(4000);

io.use(function(socket, next) {
    var handshakeData = socket.request;
    if(handshakeData.headers.cookie){
        next();
    }
    next(new Error('not authorized'));
});

var connected_users = {};
io.on('connection', function(socket){
    var user = cookie_reader.parse(socket.request.headers.cookie).sessionid
    if (user in connected_users) {
        connected_users[user] += 1;
    } else {
        connected_users[user] = 1;
        console.log(user + ' connected');
        // Tell django the user has come online
        var data = querystring.stringify({
            sessionid: user,
            status: 'connected'
        });
        var options = {
            host: HOST,
            port: HOST_PORT,
            path: '/user/node_api/',
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': data.length
            }
        };
        var req = http.request(options, function(res){
            res.setEncoding('utf8');
            res.on('data', function(chunk){
                console.log("body: " + chunk)
            })
        });
        req.write(data);
        req.end();
    };
    socket.on('disconnect', function(){
        setTimeout(function() {
            if (connected_users[user] === 1) {
                delete connected_users[user];
                console.log(user + ' disconnected');
                // Tell django the user is now offline
                var data = querystring.stringify({
                    sessionid: user,
                    status: 'disconnected'
                })
                var options = {
                    host: HOST,
                    port: HOST_PORT,
                    path: '/user/node_api/',
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded',
                        'Content-Length': data.length
                    }
                }
                var req = http.request(options, function(res){
                    res.setEncoding('utf8');
                    res.on('data', function(chunk){
                        console.log("body: " + chunk)
                    })
                })
                req.write(data);
                req.end();
            } else {
                connected_users[user] -= 1;
            };
        }, 2 * 1000);
    });
})
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011 Adam Rudd.
 * See LICENSE for more information
 */

var net = require('net')
  , MqttServer = require('./server').MqttServer
  , MqttSecureServer = require('./server').MqttSecureServer
  , MqttClient = require('./client')
  , MqttConnection = require('./connection')
  , tls = require("tls")
  , fs = require("fs");

var defaultPort = 1883
  , defaultHost = 'localhost';

/**
 * createClient - create an MQTT client
 *
 * @param {Number} [port] - broker port
 * @param {String} [host] - broker host
 * @param {Object} [opts] - see MqttClient#constructor
 * @api public
 */
module.exports.createClient = function(port, host, opts) {
  var net_client, mqtt_client;

  if ('undefined' === typeof port) {
    // createClient()
    port = defaultPort;
    host = defaultHost;
    opts = {};
  } else if ('object' === typeof port) {
    // createClient({})
    opts = port;
    port = defaultPort;
    host = defaultHost;
  } else if ('object' === typeof host) {
    // createClient(1883, {})
    opts = host;
    host = defaultHost;
  }

  net_client = net.createConnection(port, host);
  mqtt_client = new MqttClient(net_client, opts);

  return mqtt_client;
};

/**
 * createSecureClient - create a tls secured MQTT client
 *
 * @param {Number} [port]
 * @param {String} [host]
 * @param {Object} opts
 * @api public
 */
module.exports.createSecureClient = function(port, host, opts) {
  var tls_client, mqtt_client, tls_opts = {};

  if ('object' === typeof port) {
    opts = port;
    port = defaultPort;
    host = defaultHost;
  } else if ('object' === typeof host) {
    opts = host;
    host = defaultHost;
  } else if ('object' !== typeof opts) {
    opts = {};
  }

  if (opts.keyPath && opts.certPath) {
    tls_opts.key = fs.readFileSync(opts.keyPath);
    tls_opts.cert = fs.readFileSync(opts.certPath);
  }

  tls_client = tls.connect(port, host, tls_opts);

  tls_client.on('secureConnect', function() {
    if (!tls_client.authorized) {
      throw new Error('TLS not authorized');
    }
  });

  mqtt_client = new MqttClient(tls_client, opts);
  return mqtt_client;
};

/**
 * createServer - create an MQTT server
 *
 * @param {Function} listener - called on new client connections
 */

module.exports.createServer = function(listener) {
  return new MqttServer(listener);
};

/**
 * createSecureServer - create a tls secured MQTT server
 *
 * @param {String} keyPath - path to private key
 * @param {String} certPath - path to public cert
 * @param {Function} listener - called on new client conns
 */

module.exports.createSecureServer =
function(keyPath, certPath, listener) {
  return new MqttSecureServer(keyPath, certPath, listener);
};

/**
 * createConnection - create a bare MQTT connection
 *
 * @param {Number} [port]
 * @param {String} [host]
 * @param {Function} [callback]
 */
module.exports.createConnection = function(port, host, callback) {
  var net_client, mqtt_conn;
  if ('undefined' === typeof port) {
    // createConnection();
    port = defaultPort;
    host = defaultHost;
    callback = function(){};
  } else if ('function' === typeof port) {
    // createConnection(function(){});
    callback = port;
    port = defaultPort;
    host = defaultHost;
  } else if ('function' === typeof host) {
    // createConnection(1883, function(){});
    callback = host;
    host = defaultHost;
  } else if ('function' !== typeof callback) {
    // createConnection(1883, 'localhost');
    callback = function(){};
  }

  net_client = net.createConnection(port, host);
  mqtt_conn = new MqttConnection(net_client);

  // Echo net errors
  mqtt_conn.stream.on('error', mqtt_conn.emit.bind(mqtt_conn, 'error'));
  mqtt_conn.stream.on('close', mqtt_conn.emit.bind(mqtt_conn, 'close'));

  net_client.on('connect', function() {
    mqtt_conn.emit('connected');
  });

  mqtt_conn.once('connected', function() {
    callback(null, mqtt_conn);
  });

  mqtt_conn.once('error', function(err) {
    callback(err);
  });

  return mqtt_conn;
};

// Expose MqttClient
module.exports.MqttClient = MqttClient;

// Expose servers
module.exports.MqttServer = MqttServer;
module.exports.MqttSecureServer = MqttSecureServer;

// Expose Connection
module.exports.MqttConnection = MqttConnection;
<commit_msg>Add simple reconnect to MqttClient<commit_after>/*
 * Copyright (c) 2011 Adam Rudd.
 * See LICENSE for more information
 */

var net = require('net')
  , MqttServer = require('./server').MqttServer
  , MqttSecureServer = require('./server').MqttSecureServer
  , MqttClient = require('./client')
  , MqttConnection = require('./connection')
  , tls = require("tls")
  , fs = require("fs");

var defaultPort = 1883
  , defaultHost = 'localhost';

/**
 * createClient - create an MQTT client
 *
 * @param {Number} [port] - broker port
 * @param {String} [host] - broker host
 * @param {Object} [opts] - see MqttClient#constructor
 * @api public
 */
module.exports.createClient = function(port, host, opts) {
  var net_client, mqtt_client;

  if ('undefined' === typeof port) {
    // createClient()
    port = defaultPort;
    host = defaultHost;
    opts = {};
  } else if ('object' === typeof port) {
    // createClient({})
    opts = port;
    port = defaultPort;
    host = defaultHost;
  } else if ('object' === typeof host) {
    // createClient(1883, {})
    opts = host;
    host = defaultHost;
  }

  net_client = net.createConnection(port, host);
  mqtt_client = new MqttClient(net_client, opts);

  var reconnect = null;
  mqtt_client.on('close', function() {
    // If we didn't tell it to disconnect, try to reconnect
    if (!this.disconnecting) {
      reconnect = setInterval(function () {
        net_client.connect(port, host);
      }, 1000);
    }
  });

  net_client.on('connect', function() {
    if (reconnect) {
      clearInterval(reconnect);
      reconnect = null;
    }
  });

  return mqtt_client;
};

/**
 * createSecureClient - create a tls secured MQTT client
 *
 * @param {Number} [port]
 * @param {String} [host]
 * @param {Object} opts
 * @api public
 */
module.exports.createSecureClient = function(port, host, opts) {
  var tls_client, mqtt_client, tls_opts = {};

  if ('object' === typeof port) {
    opts = port;
    port = defaultPort;
    host = defaultHost;
  } else if ('object' === typeof host) {
    opts = host;
    host = defaultHost;
  } else if ('object' !== typeof opts) {
    opts = {};
  }

  if (opts.keyPath && opts.certPath) {
    tls_opts.key = fs.readFileSync(opts.keyPath);
    tls_opts.cert = fs.readFileSync(opts.certPath);
  }

  tls_client = tls.connect(port, host, tls_opts);

  tls_client.on('secureConnect', function() {
    if (!tls_client.authorized) {
      throw new Error('TLS not authorized');
    }
  });

  mqtt_client = new MqttClient(tls_client, opts);
  return mqtt_client;
};

/**
 * createServer - create an MQTT server
 *
 * @param {Function} listener - called on new client connections
 */

module.exports.createServer = function(listener) {
  return new MqttServer(listener);
};

/**
 * createSecureServer - create a tls secured MQTT server
 *
 * @param {String} keyPath - path to private key
 * @param {String} certPath - path to public cert
 * @param {Function} listener - called on new client conns
 */

module.exports.createSecureServer =
function(keyPath, certPath, listener) {
  return new MqttSecureServer(keyPath, certPath, listener);
};

/**
 * createConnection - create a bare MQTT connection
 *
 * @param {Number} [port]
 * @param {String} [host]
 * @param {Function} [callback]
 */
module.exports.createConnection = function(port, host, callback) {
  var net_client, mqtt_conn;
  if ('undefined' === typeof port) {
    // createConnection();
    port = defaultPort;
    host = defaultHost;
    callback = function(){};
  } else if ('function' === typeof port) {
    // createConnection(function(){});
    callback = port;
    port = defaultPort;
    host = defaultHost;
  } else if ('function' === typeof host) {
    // createConnection(1883, function(){});
    callback = host;
    host = defaultHost;
  } else if ('function' !== typeof callback) {
    // createConnection(1883, 'localhost');
    callback = function(){};
  }

  net_client = net.createConnection(port, host);
  mqtt_conn = new MqttConnection(net_client);

  // Echo net errors
  mqtt_conn.stream.on('error', mqtt_conn.emit.bind(mqtt_conn, 'error'));
  mqtt_conn.stream.on('close', mqtt_conn.emit.bind(mqtt_conn, 'close'));

  net_client.on('connect', function() {
    mqtt_conn.emit('connected');
  });

  mqtt_conn.once('connected', function() {
    callback(null, mqtt_conn);
  });

  mqtt_conn.once('error', function(err) {
    callback(err);
  });

  return mqtt_conn;
};

// Expose MqttClient
module.exports.MqttClient = MqttClient;

// Expose servers
module.exports.MqttServer = MqttServer;
module.exports.MqttSecureServer = MqttSecureServer;

// Expose Connection
module.exports.MqttConnection = MqttConnection;
<|endoftext|>


<commit_before>/**
* Exports the {@link module:moonstone/Scrollable~Scrollable} mixin
* @module moonstone/Scrollable
*/
var
	kind = require('enyo/kind'),
	utils = require('enyo/utils'),
	ScrollMath = require('enyo/ScrollMath');

var
	Spotlight = require('spotlight');

/**
* The Scrollable mixin
* @mixin
*/
var Scrollable = {
	// We make ourselves a Spotlight container so that 5-way
	// navigation stays within our bounds by default...
	spotlight: 'container',
	// But when focus enters us, we should spot the nearest
	// child, not whichever one was previously focused
	spotlightRememberFocus: false,

	suppressMouseEvents: true,

	scrollIntoViewOptions: {
		block: 'farthest',
		behavior: 'smooth'
	},

	// TODO: At least in the case of onSpotlightFocus, we
	// probably need to do something to ensure that we don't
	// have a conflict between the handler declared in the mixin
	// and any handler that might be declared in the kind that
	// uses the mixin. Possibilities include a) a hack like the
	// one currently employed in MarqueeSupport; b) a generalized
	// mechanism based on the same approach; or c) support in
	// general for multiple handlers for the same event, so that
	// base kinds, subkinds and instances can all register for
	// handlers independently.
	handlers: {
		onRequestScrollIntoView: 'handleRequestScrollIntoView',
		onSpotlightFocus: 'filterFocus'
	},

	scrollMath: {kind: ScrollMath, kFrictionDamping: 0.93},

	/**
	* @private
	*/
	create: kind.inherit(function (sup) {
		return function () {
			sup.apply(this, arguments);
			// Save default options so they can be restored after runtime changes
			this._scrollIntoViewOptions = utils.clone(this.scrollIntoViewOptions);
		};
	}),

	/**
	* Responds to child components' requests to be scrolled into view.
	*
	* @private
	*/
	handleRequestScrollIntoView: function (sender, event) {
		var bubble = false,
			def = this.scrollIntoViewOptions,
			opts;
		// Only scroll in 5-way mode or when explicitly requested to scroll in pointer mode
		if (!Spotlight.getPointerMode() || event.scrollInPointerMode === true) {
			if (this.canScrollX || this.canScrollY) {
				opts = {
					// If the event explicitly requests a full-page scroll, then we do so...
					block: event.scrollFullPage ? 'farthest' : (
						// ...otherwise, we honor the option set on the scroller for one-off
						// requests, but force smaller scroll increments when Spotlight
						// accelerating, since this produces smoother continuous scrolling
						// in 5-way mode.
						Spotlight.Accelerator.isAccelerating() ? 'nearest' : def.block
					),
					behavior: def.behavior
				};
				this.scrollToControl(event.originator, opts);
			} else {
				// If we don't need to scroll, bubble onRequestScrollIntoView so that
				// any scrollers above us in the control hierarchy can scroll as needed
				bubble = true;
			}
		}
		return !bubble;
	},

	/**
	* @private
	*/
	filterFocus: function (sender, event) {
		var prev,
			defBlock;

		switch (event.focusType) {
			case 'point':
				break;
			case '5-way':
				// If 5-way focus is coming from outside the scroller or from a scroll control,
				// we want to scroll the smallest distance possible to avoid a jarring experience;
				// otherwise, we respect the provided options
				if ((prev = event.previous)) {
					defBlock = this._scrollIntoViewOptions.block;
					this.scrollIntoViewOptions.block = (this.isScrollingChild(prev)) ? defBlock : 'nearest';
				}
				break;
			case 'default':
				// When focusType is 'default', Spotlight is trying to focus
				// a Control in response to something other than a point or a
				// 5-way move. For example, this can happen during
				// initialization, when the pointer is hidden, or when the app
				// regains focus.
				//
				// In the case where a scroller child is being focused by
				// default, we short-circuit the action because the child is
				// potentially not in view, and focusing would cause it to
				// scroll it into view for no reason apparent to the user.
				// Instead, we should focus a child we know to be visible.
				if (this.eventIsFromScrollingChild(event)) {
					return this.spotFirstVisibleChild();
				}
				break;
			default:
				return false;
		}
	},

	/**
	* This check is factored out of `filterFocus()` so that the logic can be
	* overridden by the kind that includes the `Scrollable` mixin. For example,
	* NewDataList can perform this check more efficiently than we can do it here
	* in the general case.
	*
	* @private
	*/
	eventIsFromScrollingChild: kind.inherit(function (sup) {
		return function (event) {
			if (sup === utils.nop) {
				return this.isScrollingChild(event.originator);
			}
			else {
				return sup.apply(this, arguments);
			}
		};
	}),

	/**
	* This behavior is factored out of `filterFocus()` so that the logic can be
	* overridden by the kind that includes the `Scrollable` mixin. For example,
	* NewDataList can identify the first visible child more efficiently than we
	* can do it here in the general case.
	*
	* @private
	*/
	spotFirstVisibleChild: kind.inherit(function (sup) {
		return function () {
			if (sup === utils.nop) {
				// TODO: Implement. For now, only handling the more specific
				// case in moonstone/NewDataList.
				return false;
			}
			else {
				return sup.apply(this, arguments);
			}
		};
	}),

	_suppressMouseEvents: kind.inherit(function (sup) {
		return function () {
			var c;

			sup.apply(this, arguments);

			if (
				// If we're in pointer mode...
				Spotlight.getPointerMode() &&
				// ...and something is currently spotted...
				(c = Spotlight.getCurrent()) &&
				// ...and that thing is a scrolling child of ours...
				this.isScrollingChild(c)
			) {
				// ...then we unspot it, since the fact that we're suppressing
				// mouse events will prevent it from unspotting on its own
				Spotlight.unspot();
			}
		};
	})
};

module.exports = Scrollable;
<commit_msg>Add inline comments / docs for Scrollable<commit_after>/**
* Exports the {@link module:moonstone/Scrollable~Scrollable} mixin
* @module moonstone/Scrollable
*/
var
	kind = require('enyo/kind'),
	utils = require('enyo/utils'),
	ScrollMath = require('enyo/ScrollMath');

var
	Spotlight = require('spotlight');

/**
* The Scrollable mixin
* @mixin
*/
var Scrollable = {
	// We make ourselves a Spotlight container so that 5-way
	// navigation stays within our bounds by default...
	spotlight: 'container',
	// But when focus enters us, we should spot the nearest
	// child, not whichever one was previously focused
	spotlightRememberFocus: false,

	suppressMouseEvents: true,

	scrollIntoViewOptions: {
		block: 'farthest',
		behavior: 'smooth'
	},

	// TODO: At least in the case of onSpotlightFocus, we
	// probably need to do something to ensure that we don't
	// have a conflict between the handler declared in the mixin
	// and any handler that might be declared in the kind that
	// uses the mixin. Possibilities include a) a hack like the
	// one currently employed in MarqueeSupport; b) a generalized
	// mechanism based on the same approach; or c) support in
	// general for multiple handlers for the same event, so that
	// base kinds, subkinds and instances can all register for
	// handlers independently.
	handlers: {
		onRequestScrollIntoView: 'handleRequestScrollIntoView',
		onSpotlightFocus: 'filterFocus'
	},

	// Override ScrollMath params
	scrollMath: {kind: ScrollMath, kFrictionDamping: 0.93},

	/**
	* @private
	*/
	create: kind.inherit(function (sup) {
		return function () {
			sup.apply(this, arguments);
			// Save default options so they can be restored after runtime changes
			this._scrollIntoViewOptions = utils.clone(this.scrollIntoViewOptions);
		};
	}),

	/**
	* Responds to child components' requests to be scrolled into view.
	*
	* @private
	*/
	handleRequestScrollIntoView: function (sender, event) {
		var bubble = false,
			def = this.scrollIntoViewOptions,
			opts;
		// Only scroll in 5-way mode or when explicitly requested to scroll in pointer mode
		if (!Spotlight.getPointerMode() || event.scrollInPointerMode === true) {
			if (this.canScrollX || this.canScrollY) {
				opts = {
					// If the event explicitly requests a full-page scroll, then we do so...
					block: event.scrollFullPage ? 'farthest' : (
						// ...otherwise, we honor the option set on the scroller for one-off
						// requests, but force smaller scroll increments when Spotlight
						// accelerating, since this produces smoother continuous scrolling
						// in 5-way mode.
						Spotlight.Accelerator.isAccelerating() ? 'nearest' : def.block
					),
					behavior: def.behavior
				};
				this.scrollToControl(event.originator, opts);
			} else {
				// If we don't need to scroll, bubble onRequestScrollIntoView so that
				// any scrollers above us in the control hierarchy can scroll as needed
				bubble = true;
			}
		}
		return !bubble;
	},

	/**
	* @private
	*/
	filterFocus: function (sender, event) {
		var prev,
			defBlock;

		switch (event.focusType) {
			case 'point':
				break;
			case '5-way':
				// If 5-way focus is coming from outside the scroller or from a scroll control,
				// we want to scroll the smallest distance possible to avoid a jarring experience;
				// otherwise, we respect the provided options
				if ((prev = event.previous)) {
					defBlock = this._scrollIntoViewOptions.block;
					this.scrollIntoViewOptions.block = (this.isScrollingChild(prev)) ? defBlock : 'nearest';
				}
				break;
			case 'default':
				// When focusType is 'default', Spotlight is trying to focus
				// a Control in response to something other than a point or a
				// 5-way move. For example, this can happen during
				// initialization, when the pointer is hidden, or when the app
				// regains focus.
				//
				// In the case where a scroller child is being focused by
				// default, we short-circuit the action because the child is
				// potentially not in view, and focusing would cause it to
				// scroll it into view for no reason apparent to the user.
				// Instead, we should focus a child we know to be visible.
				if (this.eventIsFromScrollingChild(event)) {
					return this.spotFirstVisibleChild();
				}
				break;
			default:
				return false;
		}
	},

	/**
	* This check is factored out of `filterFocus()` so that the logic can be
	* overridden by the kind that includes the `Scrollable` mixin. For example,
	* NewDataList can perform this check more efficiently than we can do it here
	* in the general case.
	*
	* @private
	*/
	eventIsFromScrollingChild: kind.inherit(function (sup) {
		return function (event) {
			if (sup === utils.nop) {
				return this.isScrollingChild(event.originator);
			}
			else {
				return sup.apply(this, arguments);
			}
		};
	}),

	/**
	* This behavior is factored out of `filterFocus()` so that the logic can be
	* overridden by the kind that includes the `Scrollable` mixin. For example,
	* NewDataList can identify the first visible child more efficiently than we
	* can do it here in the general case.
	*
	* @private
	*/
	spotFirstVisibleChild: kind.inherit(function (sup) {
		return function () {
			if (sup === utils.nop) {
				// TODO: Implement. For now, only handling the more specific
				// case in moonstone/NewDataList.
				return false;
			}
			else {
				return sup.apply(this, arguments);
			}
		};
	}),

	/**
	* Extends the base implementation provided by the enyo Scrollable mixin
	* with Moonstone-specific functionality.
	*
	* @private
	*/
	_suppressMouseEvents: kind.inherit(function (sup) {
		return function () {
			var c;

			sup.apply(this, arguments);

			if (
				// If we're in pointer mode...
				Spotlight.getPointerMode() &&
				// ...and something is currently spotted...
				(c = Spotlight.getCurrent()) &&
				// ...and that thing is a scrolling child of ours...
				this.isScrollingChild(c)
			) {
				// ...then we unspot it, since the fact that we're suppressing
				// mouse events will prevent it from unspotting on its own
				Spotlight.unspot();
			}
		};
	})
};

module.exports = Scrollable;
<|endoftext|>


<commit_before>var constants = require('./constants.js');
var query = require('./query.js');

var getContent = function(root, noRecurse) {
	var s = window.getComputedStyle(root, ':before').getPropertyValue('content');
	var node = root.firstChild;
	while (node) {
		if (node.nodeType === node.TEXT_NODE) {
			s += node.textContent;
		} else if (node.nodeType === node.ELEMENT_NODE) {
			s += getName(node, noRecurse);
		}
		node = node.nextSibling;
	}
	s += window.getComputedStyle(root, ':after').getPropertyValue('content');
	return s;
};

var allowNameFromContent = function(el) {
	var role = query.getRole(el);
	return !role || constants.nameFromContents.indexOf(role) != -1;
};

// http://www.ssbbartgroup.com/blog/how-the-w3c-text-alternative-computation-works/
// https://www.w3.org/TR/accname-aam-1.1/#h-mapping_additional_nd_te
var getName = function(el, noRecurse) {
	var ret;

	if (!noRecurse && query.matches(el, ':hidden')) {
		return '';
	}
	if (query.matches(el, 'presentation')) {
		return getContent(el, noRecurse);
	}
	if (!noRecurse && el.matches('[aria-labelledby]')) {
		var ids = el.getAttribute('aria-labelledby').split(/\s+/);
		var strings = ids.map(function(id) {
			var label = document.getElementById(id);
			return getName(label, true);
		});
		ret = strings.join(' ');
	}
	if (!ret && el.matches('[aria-label]')) {
		ret = el.getAttribute('aria-label');
	}
	if (!ret && el.labels && el.labels.length > 0) {
		ret = getName(el.labels[0], noRecurse);
	}
	if (!ret) {
		ret = el.getAttribute('placeholder');
	}
	// figcaption
	if (!ret && !query.matches(el, 'presentation')) {
		ret = el.getAttribute('alt');
	}
	// caption
	// table
	if (!ret && (noRecurse || allowNameFromContent(el))) {
		ret = getContent(el, noRecurse);
	}
	if (!ret) {
		ret = el.getAttribute('title');
	}
	if (!ret) {
		ret = el.value;
	}

	return (ret || '').trim().replace(/\s+/g, ' ');
};

var getDescription = function(el) {
	var ret = '';

	if (el.matches('[aria-describedby]')) {
		var ids = el.getAttribute('aria-describedby').split(/\s+/);
		var strings = ids.map(function(id) {
			var label = document.getElementById(id);
			return getName(label, true);
		});
		ret = strings.join(' ');
	} else if (el.title) {
		ret = el.title;
	} else if (el.placeholder) {
		ret = el.placeholder;
	}

	return ret;
};

module.exports = {
	getName: getName,
	getDescription: getDescription,
};
<commit_msg>name fix: only ignore hidden on direct reference<commit_after>var constants = require('./constants.js');
var query = require('./query.js');

var getContent = function(root, noRecurse) {
	var s = window.getComputedStyle(root, ':before').getPropertyValue('content');
	var node = root.firstChild;
	while (node) {
		if (node.nodeType === node.TEXT_NODE) {
			s += node.textContent;
		} else if (node.nodeType === node.ELEMENT_NODE) {
			s += getName(node, noRecurse);
		}
		node = node.nextSibling;
	}
	s += window.getComputedStyle(root, ':after').getPropertyValue('content');
	return s;
};

var allowNameFromContent = function(el) {
	var role = query.getRole(el);
	return !role || constants.nameFromContents.indexOf(role) != -1;
};

// http://www.ssbbartgroup.com/blog/how-the-w3c-text-alternative-computation-works/
// https://www.w3.org/TR/accname-aam-1.1/#h-mapping_additional_nd_te
var getName = function(el, noRecurse, directReference) {
	var ret;

	if (!directReference && query.matches(el, ':hidden')) {
		return '';
	}
	if (query.matches(el, 'presentation')) {
		return getContent(el, noRecurse);
	}
	if (!noRecurse && el.matches('[aria-labelledby]')) {
		var ids = el.getAttribute('aria-labelledby').split(/\s+/);
		var strings = ids.map(function(id) {
			var label = document.getElementById(id);
			return getName(label, true, true);
		});
		ret = strings.join(' ');
	}
	if (!ret && el.matches('[aria-label]')) {
		ret = el.getAttribute('aria-label');
	}
	if (!ret && el.labels && el.labels.length > 0) {
		ret = getName(el.labels[0], noRecurse);
	}
	if (!ret) {
		ret = el.getAttribute('placeholder');
	}
	// figcaption
	if (!ret && !query.matches(el, 'presentation')) {
		ret = el.getAttribute('alt');
	}
	// caption
	// table
	if (!ret && (noRecurse || allowNameFromContent(el))) {
		ret = getContent(el, noRecurse);
	}
	if (!ret) {
		ret = el.getAttribute('title');
	}
	if (!ret) {
		ret = el.value;
	}

	return (ret || '').trim().replace(/\s+/g, ' ');
};

var getDescription = function(el) {
	var ret = '';

	if (el.matches('[aria-describedby]')) {
		var ids = el.getAttribute('aria-describedby').split(/\s+/);
		var strings = ids.map(function(id) {
			var label = document.getElementById(id);
			return getName(label, true, true);
		});
		ret = strings.join(' ');
	} else if (el.title) {
		ret = el.title;
	} else if (el.placeholder) {
		ret = el.placeholder;
	}

	return ret;
};

module.exports = {
	getName: getName,
	getDescription: getDescription,
};
<|endoftext|>