<commit_before>//Function takes an array of Google Places
//request functions and executes each one
//in order with a time delay

//When each request returns
//callback called with (results, pagination)

function executePlacesReqArr(reqArr, callback){

	(function makeReq(){

		reqArr.shift()(callback);

		if(reqArr.length>0){
			setTimeout(function(){
				makeReq();
			},200);
		};
	})();
};<commit_msg>simplified placesReq function<commit_after>//Function takes an array of Google Places
//request functions and executes each one
//in order with a time delay

//When each request returns
//callback called with (results, pagination)

function executePlacesReqArr(reqArr, callback){

	function makeReq(){

		reqArr.shift()(callback);

		if(reqArr.length>0){
			setTimeout(function(){
				makeReq();
			},200);
		};
	};

	makeReq();
};<|endoftext|>


<commit_before>var role = "nothing";
module.exports = function(app){

var mysql = require('mysql');
var dbconfig = require('../config/database');
var connection;
function handleDisconnect() {
  connection = mysql.createConnection(dbconfig.connection); // Recreate the connection, since the old one cannot be reused.

  connection.connect(function(err) {              // The server is either down
    if(err) {                                     // or restarting (takes a while sometimes).
      console.log('error when connecting to db:', err);
      setTimeout(handleDisconnect, 10000); // We introduce a delay before attempting to reconnect,
    }                                     // to avoid a hot loop, and to allow our node script to
  });                                     // process asynchronous requests in the meantime.
                                          // If you're also serving http, display a 503 error.
  connection.on('error', function(err) {
    console.log('db error', err);
    if(err.code === 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
      handleDisconnect();                         // lost due to either server restart, or a
    } else {                                      // connnection idle timeout (the wait_timeout
      throw err;                                  // server variable configures this)
    }
  });
  connection.query('USE ' + dbconfig.database);
}
// ROUTES FOR OUR API
// =============================================================================
	//route to delete a User
	app.get('/api/deleteUser',function(req,res){
		var scase_token=req.param('scase_token');
		if(scase_token){
			handleDisconnect();
			var selectUsersQuery = "SELECT * FROM " + dbconfig.users_table + " WHERE scase_token = '" + scase_token + "'";
				connection.query(selectUsersQuery, function(err, rows){
				   	if (err)
                        return done(err);
                    if (rows.length > 0) {
                    	var user_id = rows[0].id;
                    	//delete any project I am the only owner (the only large SQL statement)
			        	var DeleteProjectsOnlyOwnerQuery = "DELETE FROM " + dbconfig.projects_table +
			        		" WHERE " + dbconfig.projects_table + ".project_id IN (SELECT project_id "+
				        	"FROM (SELECT * FROM " + dbconfig.owners_table +
				        	" WHERE " +dbconfig.owners_table +".project_id IN (SELECT project_id FROM " +
				        	dbconfig.owners_table + " WHERE "+ dbconfig.owners_table + ".user_id=" + user_id +
				        	")) AS projectsofuser GROUP BY project_id HAVING COUNT(project_id)=1)";
						console.log(DeleteProjectsOnlyOwnerQuery);
			            handleDisconnect();
			            connection.query(DeleteProjectsOnlyOwnerQuery, function(err, rows) {
			                //delete from owners
							var DeleteFromOwnerQuery = "DELETE FROM " + dbconfig.owners_table +
				                                " WHERE `user_id` = " + user_id;
			                console.log(DeleteFromOwnerQuery);
			                handleDisconnect();
			                connection.query(DeleteFromOwnerQuery, function(err, rows) {
			            		var DeleteFromCollabQuery = "DELETE FROM " + dbconfig.collaborators_table +
			                                " WHERE `user_id` = " + user_id;
			                    console.log(DeleteFromCollabQuery);
			                    handleDisconnect();
			                    connection.query(DeleteFromCollabQuery, function(err, rows) {
			                    	var DeleteFromUsersQuery = "DELETE FROM " + dbconfig.users_table +
			                			" WHERE `id` = " + user_id;
			            			console.log(DeleteFromUsersQuery);
			    				  	connection.query(DeleteFromUsersQuery, function(err, rows) {
			                            	res.setHeader('Content-Type', 'application/json');
											var obj = '{'
													+ '"userDeleted" : "true"'
													+ '}';
											var Jobj=JSON.parse(obj);
											res.send(Jobj);
			                        });
			                    });
			                });
			        	});
                    }
			});
		}
	});
	//route to refresh the S-CASE token
	//we create a new scase token
	var crypto = require('crypto');
	var base64url = require('base64url');
	//function to crete an S-CASE token
	function scasetokenCreate(size){
	    return base64url(crypto.randomBytes(size));
	}
	app.get('/api/refreshSCASEtoken',function(req,res){
		var scase_token=req.param('scase_token');
		if(scase_token){
			handleDisconnect();
			var selectUsersQuery = "SELECT * FROM " + dbconfig.users_table + " WHERE scase_token = '" + scase_token + "'";
				connection.query(selectUsersQuery, function(err, rows){
				   	if (err)
                        return done(err);
                    if (rows.length > 0) {
                    	var user = rows[0];
                    	handleDisconnect();
						//we select the user from the user's table (just to check if the user exists)
						connection.query("SELECT * FROM " + dbconfig.users_table + " WHERE id = '" + user.id + "'", function(err, rows){
				            if (rows.length > 0) {
				                user = rows[0];
				                var scasetoken = scasetokenCreate(35)//we create a new scase token
				                user.scase_token=scasetoken;
				                //we update the token
				                var updateQuery = "UPDATE " + dbconfig.users_table + " SET " +
				                        "`scase_token` = '" + user.scase_token + "' " +
				                        "WHERE `id` = '" + user.id + "' LIMIT 1";
				                handleDisconnect();
				                connection.query(updateQuery, function(err, rows) {
				                  	res.setHeader('Content-Type', 'application/json');
											var obj = '{'
													+ '"newSCASEtoken" : "'+scasetoken+'"}';
											var Jobj=JSON.parse(obj);
											res.send(Jobj);
				                });
				            }
				        });
                    }
			});
		}
	});
};<commit_msg>initial attempt for display project names I own<commit_after>var role = "nothing";
module.exports = function(app){

var mysql = require('mysql');
var dbconfig = require('../config/database');
var connection;
function handleDisconnect() {
  connection = mysql.createConnection(dbconfig.connection); // Recreate the connection, since the old one cannot be reused.

  connection.connect(function(err) {              // The server is either down
    if(err) {                                     // or restarting (takes a while sometimes).
      console.log('error when connecting to db:', err);
      setTimeout(handleDisconnect, 10000); // We introduce a delay before attempting to reconnect,
    }                                     // to avoid a hot loop, and to allow our node script to
  });                                     // process asynchronous requests in the meantime.
                                          // If you're also serving http, display a 503 error.
  connection.on('error', function(err) {
    console.log('db error', err);
    if(err.code === 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
      handleDisconnect();                         // lost due to either server restart, or a
    } else {                                      // connnection idle timeout (the wait_timeout
      throw err;                                  // server variable configures this)
    }
  });
  connection.query('USE ' + dbconfig.database);
}
// ROUTES FOR OUR API
// =============================================================================
	// =============================================================================
	// Delete account API =============================================================
	// =============================================================================
	app.get('/api/deleteUser',function(req,res){
		var scase_token=req.param('scase_token');
		if(scase_token){
			handleDisconnect();
			//we select the user with the scase_token provided
			var selectUsersQuery = "SELECT * FROM " + dbconfig.users_table + " WHERE scase_token = '" + scase_token + "'";
				connection.query(selectUsersQuery, function(err, rows){
				   	if (err)
                        return done(err);
                    if (rows.length > 0) {
                    	var user_id = rows[0].id;
                    	//delete any project I am the only owner (the only large SQL statement)
			        	var DeleteProjectsOnlyOwnerQuery = "DELETE FROM " + dbconfig.projects_table +
			        		" WHERE " + dbconfig.projects_table + ".project_id IN (SELECT project_id "+
				        	"FROM (SELECT * FROM " + dbconfig.owners_table +
				        	" WHERE " +dbconfig.owners_table +".project_id IN (SELECT project_id FROM " +
				        	dbconfig.owners_table + " WHERE "+ dbconfig.owners_table + ".user_id=" + user_id +
				        	")) AS projectsofuser GROUP BY project_id HAVING COUNT(project_id)=1)";
						console.log(DeleteProjectsOnlyOwnerQuery);
			            handleDisconnect();
			            connection.query(DeleteProjectsOnlyOwnerQuery, function(err, rows) {
			                //delete from owners
							var DeleteFromOwnerQuery = "DELETE FROM " + dbconfig.owners_table +
				                                " WHERE `user_id` = " + user_id;
			                console.log(DeleteFromOwnerQuery);
			                handleDisconnect();
			                connection.query(DeleteFromOwnerQuery, function(err, rows) {
			            		var DeleteFromCollabQuery = "DELETE FROM " + dbconfig.collaborators_table +
			                                " WHERE `user_id` = " + user_id;
			                    console.log(DeleteFromCollabQuery);
			                    handleDisconnect();
			                    connection.query(DeleteFromCollabQuery, function(err, rows) {
			                    	var DeleteFromUsersQuery = "DELETE FROM " + dbconfig.users_table +
			                			" WHERE `id` = " + user_id;
			            			console.log(DeleteFromUsersQuery);
			    				  	connection.query(DeleteFromUsersQuery, function(err, rows) {
			                            	res.setHeader('Content-Type', 'application/json');
											var obj = '{'
													+ '"userDeleted" : "true"'
													+ '}';
											var Jobj=JSON.parse(obj);
											res.send(Jobj);
			                        });
			                    });
			                });
			        	});
                    }
			});
		}
	});
	// =============================================================================
	// Refresh S-CASE token ACCOUNTS API===============================================
	// =============================================================================
	//we create a new scase token
	var crypto = require('crypto');
	var base64url = require('base64url');
	//function to crete an S-CASE token
	function scasetokenCreate(size){
	    return base64url(crypto.randomBytes(size));
	}
	app.get('/api/refreshSCASEtoken',function(req,res){
		var scase_token=req.param('scase_token');
		if(scase_token){
			handleDisconnect();
			//we select the user with the scase_token provided
			var selectUsersQuery = "SELECT * FROM " + dbconfig.users_table + " WHERE scase_token = '" + scase_token + "'";
				connection.query(selectUsersQuery, function(err, rows){
				   	if (err)
                        return done(err);
                    if (rows.length > 0) {
                    	var user = rows[0];
                    	handleDisconnect();
						//we select the user from the user's table (just to check if the user exists)
						connection.query("SELECT * FROM " + dbconfig.users_table + " WHERE id = '" + user.id + "'", function(err, rows){
				            if (rows.length > 0) {
				                user = rows[0];
				                var scasetoken = scasetokenCreate(35)//we create a new scase token
				                user.scase_token=scasetoken;
				                //we update the token
				                var updateQuery = "UPDATE " + dbconfig.users_table + " SET " +
				                        "`scase_token` = '" + user.scase_token + "' " +
				                        "WHERE `id` = '" + user.id + "' LIMIT 1";
				                handleDisconnect();
				                connection.query(updateQuery, function(err, rows) {
				                  	res.setHeader('Content-Type', 'application/json');
											var obj = '{'
													+ '"newSCASEtoken" : "'+scasetoken+'"}';
											var Jobj=JSON.parse(obj);
											res.send(Jobj);
				                });
				            }
				        });
                    }
			});
		}
	});
	// =============================================================================
	// Display Projects I own API===============================================
	// =============================================================================
	//we create a new scase token
	var crypto = require('crypto');
	var base64url = require('base64url');
	//function to crete an S-CASE token
	function scasetokenCreate(size){
	    return base64url(crypto.randomBytes(size));
	}
	app.get('/api/displayProjectsOwn',function(req,res){
		var scase_token=req.param('scase_token');
		if(scase_token){
			handleDisconnect();
			//we select the user with the scase_token provided
			var selectUsersQuery = "SELECT * FROM " + dbconfig.users_table + " WHERE scase_token = '" + scase_token + "'";
				connection.query(selectUsersQuery, function(err, rows){
				   	if (err)
                        return done(err);
                    if (rows.length > 0) {
                    	var user = rows[0];
                    	handleDisconnect();
						//we select the user from the user's table (just to check if the user exists)
						connection.query("SELECT * FROM " + dbconfig.users_table + " WHERE id = '" + user.id + "'", function(err, rows){
				            if (rows.length > 0) {
				                user = rows[0];
				                //query for the projects I own
								var projectsOwnedQuery = "SELECT `project_name` FROM " + dbconfig.projects_table +" JOIN " + dbconfig.owners_table + " ON "+ dbconfig.projects_table+
									".`project_id` = "+ dbconfig.owners_table + ".`project_id` "+" WHERE "+ dbconfig.owners_table+".`user_id` = '" + user.id + "'";
								connection.query(projectsOwnedQuery, function(err, rows){
						            if (rows.length > 0) {
						            	res.setHeader('Content-Type', 'application/json');
											var obj = '{'
													+ '"projectnames" : "'+rows+'"}';
											var Jobj=JSON.parse(obj);
											res.send(Jobj);
									}
					            });
					        }
					    });
		            }

			    });
            }

		});
};<|endoftext|>


<commit_before>function update(treestr, id, pname) {
  var newick = Newick.parse(treestr)

  var newickNodes = []
  function buildNewickNodes(node, callback) {
    newickNodes.push(node)
    if (node.branchset) {
      for (var i=0; i < node.branchset.length; i++) {
        buildNewickNodes(node.branchset[i])
      }
    }
  }
  buildNewickNodes(newick)

  if (pname == "all") {
    var width = 600
    var height = 1000
  } else {
    var width = 400
    var height = 300
  }

  var dict = d3.phylogram.build("#phylogram_"+id, newick, {
    width: width,
    height: height
  });

  dict.vis.selectAll("circle")
   .attr('stroke',  'steelblue')
   .attr('fill', 'steelblue');

}
<commit_msg>Manual tree building, more control<commit_after>function cross(a, b) { return a[0] * b[1] - a[1] * b[0]; }
function dot(a, b) { return a[0] * b[0] + a[1] * b[1]; }

function project(d) {
  var r = d.y, a = (d.x - 90) / 180 * Math.PI;
  return [r * Math.cos(a), r * Math.sin(a)];
}

function maxDepth(n, offset, maxOffset) {
  var tmp;
  if (n.length != null) offset += n.length;
  if (offset > maxOffset) maxOffset = offset;
  if (n.children) {
    for(i=0; i < n.children.length; i++) {
      tmp = maxDepth(n.children[i], offset, maxOffset);
      if (tmp > maxOffset) maxOffset = tmp;
    }
  }
  return maxOffset;
}

function phylo(n, offset, treeScale) {
  if (n.length != null) offset += treeScale * n.length;
  n.y = offset;
  if (n.children)
    n.children.forEach(function(n) {
      phylo(n, offset, treeScale);
    });
}

function step(d) {
  var s = project(d.source),
      m = project({x: d.target.x, y: d.source.y}),
      t = project(d.target),
      r = d.source.y,
      sweep = d.target.x > d.source.x ? 1 : 0;
  return (
    "M" + s[0] + "," + s[1] +
    "A" + r + "," + r + " 0 0," + sweep + " " + m[0] + "," + m[1] +
    "L" + t[0] + "," + t[1]);
}

function stepAnno(d, r) {
  var s = project({x: d.x, y: d.y + 5}),
      t = project({x: d.x, y: r});
  return (
    "M" + s[0] + "," + s[1] +
    "L" + t[0] + "," + t[1]);
}

function update(text, id, pname) {

 var maxAngle = 360;
 var div_width = +($("#phylogram_"+id).width()),
     svg_width = 0.9 * div_width,
     margins = {"top": 5, "bottom": 5, "left": 5, "right": 5},
     width = svg_width - margins.left - margins.right,
     height = svg_width - margins.top - margins.bottom,
     r = width / 2,
     rInternal = r - 170,
     rLeaves = rInternal - 100;

 var chart_ext = d3.select("#phylogram_"+id).append("svg")
    .attr("width", width + margins.left + margins.right)
    .attr("height", height + margins.top + margins.bottom);

 var vis = chart_ext.append("g")
    .attr("transform", "translate(" + (margins.top + r) + "," + (margins.left + r) + ")");

 var cluster = d3.layout.cluster()
    .size([maxAngle, 1])
    .sort(null)
    .value(function(d) { return d.length; })
    .children(function(d) { return d.branchset; })
    .separation(function(a, b) { return 1; });

 var tree = Newick.parse(text);
 var nodes = cluster.nodes(tree);
 var depth = maxDepth(nodes[0], 0, 0),
     treeScale = 100 / depth;
 phylo(nodes[0], 0, treeScale);

 var link = vis.selectAll("path.link")
      .data(cluster.links(nodes))
      .enter()
      .append("path")
      .attr("class", "link")
      .attr("d", step)
      .attr("fill", "none")
      .attr("stroke", "black")
      .attr("stroke-width", 2);

 /* label nodes with circles
 var node = vis.selectAll("g.node")
      .data(nodes.filter(function(d) { return d.x !== undefined && !d.children; }))
    .enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })

  node.append("circle")
      .attr("r", 5)
      .style("stroke", "steelblue")
      .style("stroke-width", 2)
      .style("fill", "none");
  */

 var annoLine = vis.selectAll("path.anno")
      .data(nodes.filter(function(d) { return d.x !== undefined && !d.children; }))
      .enter()
      .append("path")
      .attr("class", "anno")
      .attr("d", function(d) { return stepAnno(d, rInternal); })
      .attr("fill", "none")
      .attr("stroke", "lightgrey")
      .style("stroke-dasharray", ("3, 3"))
      .attr("stroke-width", 2);

 var label = vis.selectAll("text")
      .data(nodes.filter(function(d) { return d.x !== undefined && !d.children; }))
    .enter().append("text")
      .attr("dy", ".31em")
      .attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
      .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + (r - 170 + 8) + ")rotate(" + (d.x < 180 ? 0 : 180) + ")"; })
      .text(function(d) { return d.name.replace(/_/g, ' '); });
}
<|endoftext|>


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

    angular.module('monitool.app.configs')
        .run([
            '$rootElement', 'Config', '$rootScope', '$location', 'cfpLoadingBar', 'AuthProvider',
            function ($rootElement, Config, $rootScope, $location, cfpLoadingBar, AuthProvider) {
                var config = {};
                _.each($rootElement[0].attributes, function(attr) {
                    Config[attr.name.replace('data-', '')] = attr.value;
                });

                $rootScope.$on('$routeChangeStart', function (event, next) {
                    var authorised;
                    cfpLoadingBar.start();
                    if (next.access !== undefined) {
                        authorised = AuthProvider.isLoggedIn();
                        var route = next.$$route.originalPath.replace('/','');
                        if (authorised) {
                            if( route === 'login' || route === 'register' ) {
                                $location.path('/dashboard').replace();
                            }
                        } else {
                            if( route === 'register' ) {
                                $location.path('/register').replace();
                            } else {
                                $location.path('/login').replace();
                            }
                        }
                    }
                });
            }
        ]);
})();<commit_msg>fix loading bar on login/register page. Continued development...<commit_after>(function() {
    'use strict';

    angular.module('monitool.app.configs')
        .run([
            '$rootElement', 'Config', '$rootScope', '$location', 'cfpLoadingBar', 'AuthProvider',
            function ($rootElement, Config, $rootScope, $location, cfpLoadingBar, AuthProvider) {
                var config = {};
                _.each($rootElement[0].attributes, function(attr) {
                    Config[attr.name.replace('data-', '')] = attr.value;
                });

                $rootScope.$on('$routeChangeStart', function (event, next) {
                    var authorised;
                    cfpLoadingBar.start();
                    if (next.access !== undefined) {
                        authorised = AuthProvider.isLoggedIn();
                        var route = next.$$route.originalPath.replace('/','');
                        if (authorised) {
                            if( route === 'login' || route === 'register' ) {
                                $location.path('/dashboard').replace();
                            }
                        } else {
                            if( route === 'register' ) {
                                $location.path('/register').replace();
                            } else {
                                $location.path('/login').replace();
                            }
                        }
                    }
                    cfpLoadingBar.complete();
                });
            }
        ]);
})();<|endoftext|>


<commit_before>class InfoView {
  constructor(options) {
    var texture = new THREE.VideoTexture(options.videoSource);
    texture.minFilter = THREE.LinearFilter;
    texture.magFilter = THREE.LinearFilter;
    texture.format = THREE.RGBFormat;

    var chromaVertexShader = document.getElementById('chromaKeyVertexShader').innerHTML;
    var chromaFragmentShader = document.getElementById('chromaKeyFragmentShader').innerHTML;

    var videoMaterial = new THREE.ShaderMaterial({
        vertexShader: chromaVertexShader,
        fragmentShader: chromaFragmentShader,
        uniforms: {
          texture: { type: "t", value: texture },
          color: { type: "c", value: new THREE.Color(0x1e2841) },
          opacity: { type: "f", value: 1 },
        },
        transparent: true,
    });

    var invisibleMaterial = new THREE.MeshBasicMaterial({opacity: 0, transparent: true});

    var cubeMaterialArray = [];
    cubeMaterialArray.push(invisibleMaterial);
    cubeMaterialArray.push(invisibleMaterial);
    cubeMaterialArray.push(invisibleMaterial);
    cubeMaterialArray.push(invisibleMaterial);
    cubeMaterialArray.push(videoMaterial);
    cubeMaterialArray.push(invisibleMaterial);

    var cubeMaterials = new THREE.MeshFaceMaterial(cubeMaterialArray);

    this.mesh = new THREE.Mesh(
      new THREE.BoxGeometry(160 * 0.8, 90 * 0.8, 2),
      cubeMaterials
    );

    this.offset = options.offset;

    this.visible = false;
  }
}

module.exports = InfoView;
<commit_msg>fix black box as infoView showing being video texture is loaded<commit_after>class InfoView {
  constructor(options) {
    var texture = new THREE.VideoTexture(options.videoSource);
    texture.minFilter = THREE.LinearFilter;
    texture.magFilter = THREE.LinearFilter;
    texture.format = THREE.RGBFormat;

    var chromaVertexShader = document.getElementById('chromaKeyVertexShader').innerHTML;
    var chromaFragmentShader = document.getElementById('chromaKeyFragmentShader').innerHTML;

    var videoMaterial = new THREE.ShaderMaterial({
        vertexShader: chromaVertexShader,
        fragmentShader: chromaFragmentShader,
        uniforms: {
          texture: { type: "t", value: texture },
          color: { type: "c", value: new THREE.Color(0x1e2841) },
          opacity: { type: "f", value: 1 },
        },
        transparent: true,
    });

    var invisibleMaterial = new THREE.MeshBasicMaterial({opacity: 0, transparent: true});

    var cubeMaterialArray = [];
    cubeMaterialArray.push(invisibleMaterial);
    cubeMaterialArray.push(invisibleMaterial);
    cubeMaterialArray.push(invisibleMaterial);
    cubeMaterialArray.push(invisibleMaterial);
    cubeMaterialArray.push(videoMaterial);
    cubeMaterialArray.push(invisibleMaterial);

    var cubeMaterials = new THREE.MeshFaceMaterial(cubeMaterialArray);

    this.mesh = new THREE.Mesh(
      new THREE.BoxGeometry(160 * 0.8, 90 * 0.8, 2),
      cubeMaterials
    );

    // Set the initial position to be way off the screen, so that a black box
    // doesn't show while waiting for the video texture to load
    this.mesh.position.set(9999, 9999, 9999);

    this.offset = options.offset;

    this.visible = false;
  }
}

module.exports = InfoView;
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react'
import { withRouter } from 'react-router'

class ClearAuthPage extends PureComponent {
  state = {
    countdown: 5,
    hasAttemptedConfirm: false
  }

  componentDidMount() {
    this.decrementCountdown()
  }

  decrementCountdown() {
    setTimeout(() => {
      const { countdown } = this.state
      this.setState({ countdown: countdown - 1 })
      if (countdown > 1) {
        this.decrementCountdown()
      }
    }, 1000)
  }

  clearData = () => {
    if (this.state.hasAttemptedConfirm) {
      localStorage.clear()
      window.location = `${this.props.location.query.redirect_uri}://?authCleared=1`
    }
    else {
      this.setState({ hasAttemptedConfirm: true })
    }
  }

  cancel = () => {
    window.location = `${this.props.location.query.redirect_uri}://?authCleared=0`
  }

  render() {
    const { countdown, hasAttemptedConfirm } = this.state

    return (

Sign Out

          Warning: This will erase your keychain and settings
          from this device. You’ll be able to restore your keychain, or create
          a new one afterwards.

          If you plan to restore your keychain, make sure you have
          recorded your backup information. You will either need your
          12 word secret phrase, or your magic recovery code and password to
          do so.

          <button
            className="btn btn-danger btn-block"
            onClick={this.clearData}
            disabled={countdown > 0}
          >
            {hasAttemptedConfirm ? (
              Press again to confirm
            ) : (
              Delete my data {countdown > 0 && `(${countdown})`}
            )}

          </button>

          <button className="btn btn-tertiary btn-block" onClick={this.cancel}>
            Cancel
          </button>

    )
  }
}

export default withRouter(ClearAuthPage)
<commit_msg>Adjust copy.<commit_after>import React, { PureComponent } from 'react'
import { withRouter } from 'react-router'

class ClearAuthPage extends PureComponent {
  state = {
    countdown: 5,
    hasAttemptedConfirm: false
  }

  componentDidMount() {
    this.decrementCountdown()
  }

  decrementCountdown() {
    setTimeout(() => {
      const { countdown } = this.state
      this.setState({ countdown: countdown - 1 })
      if (countdown > 1) {
        this.decrementCountdown()
      }
    }, 1000)
  }

  clearData = () => {
    if (this.state.hasAttemptedConfirm) {
      localStorage.clear()
      window.location = `${this.props.location.query.redirect_uri}://?authCleared=1`
    }
    else {
      this.setState({ hasAttemptedConfirm: true })
    }
  }

  cancel = () => {
    window.location = `${this.props.location.query.redirect_uri}://?authCleared=0`
  }

  render() {
    const { countdown, hasAttemptedConfirm } = this.state

    return (

Sign Out

          Warning: This will reset your keychain and settings
          on this device. You’ll be able to restore your keychain, or create
          a new one afterwards.

          If you plan to restore your keychain, make sure you have
          recorded your backup information. You will either need your
          12 word secret key, or your magic recovery code and password to
          do so.

          <button
            className="btn btn-danger btn-block"
            onClick={this.clearData}
            disabled={countdown > 0}
          >
            {hasAttemptedConfirm ? (
              Press again to confirm
            ) : (
              Reset my data {countdown > 0 && `(${countdown})`}
            )}

          </button>

          <button className="btn btn-tertiary btn-block" onClick={this.cancel}>
            Cancel
          </button>

    )
  }
}

export default withRouter(ClearAuthPage)
<|endoftext|>


<commit_before>/**
 * Improved Bandcamp Scrobbler by Malachi Soord.
 *
 * - Build on the work of George Pollard
 * - Inspired by the Spotify scrobbler
 */

$(function() {
	'use strict';

	$(document).ready(function() {

		/**
		 * Audio player reference.
		 */
		var player = document.getElementsByTagName('audio')[0];

		/**
		 * Duration selector.
		 *
		 * @type {string}
		 */
		var durationPart = '.track_info .time';

		/**
		 * Duration regex.
		 *
		 * @type {string}
		 */
		var durationRegex = /[ \n]*(\d+):(\d+)[ \n]*\/[ \n]*(\d+):(\d+)[ \n]*/;

		/**
		 * Last playing track.
		 *
		 * @type {string}
		 */
		var lastTrack = null;

		/**
		 * If true then we are on a 'discovery' type of page.
		 *
		 * @type {boolean}
		 */
		var onDiscovery = $('#discover').length > 0;

		/**
		 * To determine whether the current page is an album.
		 *
		 * @type {boolean}
		 */
		var isAlbum = $('.trackView[itemtype="http://schema.org/MusicAlbum"]').length > 0;

		/**
		 * To determine whether the current page is a compilation or not.
		 *
		 * @type {Boolean}
		 */
		var isCompilation = isAlbum && allTracksDashed();

		console.log('BandCampScrobbler: Loaded! onDiscovery:' + onDiscovery + ' isAlbum:' + isAlbum + ' isCompilation:' + isCompilation);

		/**
		 * Initial bind of listeners.
		 */
		player.addEventListener('playing', scrobble);
		player.addEventListener('ended', reset);

		/**
		 * Audio is already playing on visit so scrobble.
		 */
		if (!player.paused) {
			scrobble();
		}

		/**
		 * Scrobble function
		 */
		function scrobble() {
			console.log('BandCampScrobbler: scrobble!');

			var artist = parseArtist();
			var track = parseTitle();
			var album = parseAlbum();

			/**
			 * Work out artist from the track if either:
			 *
			 *  - The artist is set to 'Various' or 'Various Artists' and track has a dash
			 *  - This is a compilation
			 */
			var dashIndex = track.indexOf('-');
			if ((/^Various(\sArtists)?$/.test(artist) && dashIndex > 0) || isCompilation) {
				artist = track.substring(0, dashIndex);
				track = track.substring(dashIndex + 1);
			}

			if (track !== lastTrack) {

				var duration = parseDuration();

				console.log('BandCampScrobbler: scrobbling - Artist: ' + artist + '; Album:  ' + album + '; Track: ' + track + '; duration: ' + duration.total);

				lastTrack = track;

				chrome.runtime.sendMessage({
					type: 'validate',
					artist: artist,
					track: track
				}, function(response) {
					if (response !== false) {
						chrome.runtime.sendMessage({
							type: 'nowPlaying',
							artist: response.artist,
							track: response.track,
							duration: duration.total,
							album: album
						});
					} else {
						chrome.runtime.sendMessage({
							type: 'nowPlaying',
							duration: duration.total
						});
					}
				});
			}
		}

		/**
		 * Set lastTrack to null to reset playing and enabling re-scrobbling
		 * of the same song again once audio has finished playing.
		 */
		function reset() {
			console.log('BandCampScrobbler: reset');
			lastTrack = null;
			chrome.runtime.sendMessage({
				type: 'reset'
			});
		}

		/**
		 * Parse artist information.
		 */
		function parseArtist() {
			var artist;
			if (onDiscovery) {
				artist = $('.detail_item_link_2').text();
			} else {
				var byLine = '';
				if (isAlbum) {
					byLine = $('dt.hiddenAccess:contains("band name") ~ dd').text();
				} else {
					byLine = $('.albumTitle nobr').text();
				}

				artist = $.trim($.trim(byLine).substring(2));
				if (!artist) {
					artist = $('span[itemprop=byArtist]').text();
				}
			}

			return $.trim(artist);
		}

		/**
		 * Parse album information.
		 */
		function parseAlbum() {
			var album;

			if (onDiscovery) {
				album = $('.detail_item_link').text();
			} else {
				if (isAlbum) {
					album = $('h2.trackTitle').text().trim();
				} else { // isTrack
					album = $('[itemprop="inAlbum"] [itemprop="name"]').text();
				}
			}

			return $.trim(album);
		}

		/**
		 * Parse title information.
		 */
		function parseTitle() {
			var title;
			if (onDiscovery) {
				title = $('.track_info .title').first().text();
			} else {
				if (isAlbum) {
					title = $('.track_info .title').first().text();
				} else {
					title = $('.trackTitle').first().text();
				}
			}

			return $.trim(title);
		}

		/**
		 * Parse duration information.
		 */
		function parseDuration() {
			try {
				var m = durationRegex.exec($(durationPart).text());
				return {
					current: parseInt(m[1], 10) * 60 + parseInt(m[2], 10),
					total: parseInt(m[3], 10) * 60 + parseInt(m[4], 10)
				};
			} catch (err) {
				return 0;
			}
		}

		/**
		 * Check to see if all tracks are dashed.
		 *
		 * @return bool the result
		 */
		function allTracksDashed() {
			var allDashed = true;

			$('.track_list span[itemprop="name"]').each(function() {
				if ($(this).text().indexOf('-') === -1) {
					allDashed = false;
					return false; // Break out loop
				}
			});

			return allDashed;
		}
	});

	// bind page unload function to discard current 'now listening'
	cancel();

	function cancel() {
		$(window).unload(function() {
			// reset the background scrobbler song data
			chrome.runtime.sendMessage({
				type: 'reset'
			});
			return true;
		});
	}
});
<commit_msg>Removed zero width characters from BC artist, album, and title<commit_after>/**
 * Improved Bandcamp Scrobbler by Malachi Soord.
 *
 * - Build on the work of George Pollard
 * - Inspired by the Spotify scrobbler
 */

$(function() {
	'use strict';

	$(document).ready(function() {

		/**
		 * Audio player reference.
		 */
		var player = document.getElementsByTagName('audio')[0];

		/**
		 * Duration selector.
		 *
		 * @type {string}
		 */
		var durationPart = '.track_info .time';

		/**
		 * Duration regex.
		 *
		 * @type {string}
		 */
		var durationRegex = /[ \n]*(\d+):(\d+)[ \n]*\/[ \n]*(\d+):(\d+)[ \n]*/;

		/**
		 * Last playing track.
		 *
		 * @type {string}
		 */
		var lastTrack = null;

		/**
		 * If true then we are on a 'discovery' type of page.
		 *
		 * @type {boolean}
		 */
		var onDiscovery = $('#discover').length > 0;

		/**
		 * To determine whether the current page is an album.
		 *
		 * @type {boolean}
		 */
		var isAlbum = $('.trackView[itemtype="http://schema.org/MusicAlbum"]').length > 0;

		/**
		 * To determine whether the current page is a compilation or not.
		 *
		 * @type {Boolean}
		 */
		var isCompilation = isAlbum && allTracksDashed();

		console.log('BandCampScrobbler: Loaded! onDiscovery:' + onDiscovery + ' isAlbum:' + isAlbum + ' isCompilation:' + isCompilation);

		/**
		 * Initial bind of listeners.
		 */
		player.addEventListener('playing', scrobble);
		player.addEventListener('ended', reset);

		/**
		 * Audio is already playing on visit so scrobble.
		 */
		if (!player.paused) {
			scrobble();
		}

		/**
		 * Scrobble function
		 */
		function scrobble() {
			console.log('BandCampScrobbler: scrobble!');

			var artist = parseArtist();
			var track = parseTitle();
			var album = parseAlbum();

			/**
			 * Work out artist from the track if either:
			 *
			 *  - The artist is set to 'Various' or 'Various Artists' and track has a dash
			 *  - This is a compilation
			 */
			var dashIndex = track.indexOf('-');
			if ((/^Various(\sArtists)?$/.test(artist) && dashIndex > 0) || isCompilation) {
				artist = track.substring(0, dashIndex);
				track = track.substring(dashIndex + 1);
			}

			if (track !== lastTrack) {

				var duration = parseDuration();

				console.log('BandCampScrobbler: scrobbling - Artist: ' + artist + '; Album:  ' + album + '; Track: ' + track + '; duration: ' + duration.total);

				lastTrack = track;

				chrome.runtime.sendMessage({
					type: 'validate',
					artist: artist,
					track: track
				}, function(response) {
					if (response !== false) {
						chrome.runtime.sendMessage({
							type: 'nowPlaying',
							artist: response.artist,
							track: response.track,
							duration: duration.total,
							album: album
						});
					} else {
						chrome.runtime.sendMessage({
							type: 'nowPlaying',
							duration: duration.total
						});
					}
				});
			}
		}

		/**
		 * Set lastTrack to null to reset playing and enabling re-scrobbling
		 * of the same song again once audio has finished playing.
		 */
		function reset() {
			console.log('BandCampScrobbler: reset');
			lastTrack = null;
			chrome.runtime.sendMessage({
				type: 'reset'
			});
		}

		/**
		 * Parse artist information.
		 */
		function parseArtist() {
			var artist;
			if (onDiscovery) {
				artist = $('.detail_item_link_2').text();
			} else {
				var byLine = '';
				if (isAlbum) {
					byLine = $('dt.hiddenAccess:contains("band name") ~ dd').text();
				} else {
					byLine = $('.albumTitle nobr').text();
				}

				artist = $.trim($.trim(byLine).substring(2));
				if (!artist) {
					artist = $('span[itemprop=byArtist]').text();
				}
			}

			return cleanUpMatched(artist);
		}

		/**
		 * Parse album information.
		 */
		function parseAlbum() {
			var album;

			if (onDiscovery) {
				album = $('.detail_item_link').text();
			} else {
				if (isAlbum) {
					album = $('h2.trackTitle').text().trim();
				} else { // isTrack
					album = $('[itemprop="inAlbum"] [itemprop="name"]').text();
				}
			}

			return cleanUpMatched(album);
		}

		/**
		 * Parse title information.
		 */
		function parseTitle() {
			var title;
			if (onDiscovery) {
				title = $('.track_info .title').first().text();
			} else {
				if (isAlbum) {
					title = $('.track_info .title').first().text();
				} else {
					title = $('.trackTitle').first().text();
				}
			}

			return cleanUpMatched(title);
		}

		/**
		 * Parse duration information.
		 */
		function parseDuration() {
			try {
				var m = durationRegex.exec($(durationPart).text());
				return {
					current: parseInt(m[1], 10) * 60 + parseInt(m[2], 10),
					total: parseInt(m[3], 10) * 60 + parseInt(m[4], 10)
				};
			} catch (err) {
				return 0;
			}
		}

		/**
		 * Check to see if all tracks are dashed.
		 *
		 * @return bool the result
		 */
		function allTracksDashed() {
			var allDashed = true;

			$('.track_list span[itemprop="name"]').each(function() {
				if ($(this).text().indexOf('-') === -1) {
					allDashed = false;
					return false; // Break out loop
				}
			});

			return allDashed;
		}

		/**
		 * Clean up matched text.
		 * @param  string input to clean up
		 * @return string cleaned up matched text
		 */
		function cleanUpMatched(input) {
			if (input === null) {
				return input;
			}
			input = stripZeroWidthChars(input);
			return $.trim(input);
		}

		/**
		 * Strip zero width characters.
		 * @param  string input to clean up
		 * @return string stripped input
		 */
		function stripZeroWidthChars(input) {
			return input.replace(/[\u200B-\u200D\uFEFF]/g, '');
		}
	});

	// bind page unload function to discard current 'now listening'
	cancel();

	function cancel() {
		$(window).unload(function() {
			// reset the background scrobbler song data
			chrome.runtime.sendMessage({
				type: 'reset'
			});
			return true;
		});
	}
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Stack Overflow Extras (SOX)
// @namespace    https://github.com/soscripted/sox
// @homepage     https://github.com/soscripted/sox
// @homepageURL  https://github.com/soscripted/sox
// @version      2.6.8 DEV
// @description  Extra optional features for Stack Overflow and Stack Exchange sites
// @contributor  ᴉʞuǝ (https://stackoverflow.com/users/1454538/, https://github.com/mezmi)
// @contributor  ᔕᖺᘎᕊ (https://stackexchange.com/users/4337810/, https://github.com/shu8)
// @contributor  Sir-Cumference (https://stackexchange.com/users/4119142/, https://github.com/Sir-Cumference)
// @contributor  GaurangTandon (https://github.com/GaurangTandon)
// @contributor  double-beep (https://stackexchange.com/users/14688437/double-beep, https://github.com/double-beep)
// @updateURL    https://cdn.jsdelivr.net/gh/soscripted/sox@dev/sox.user.js

// @match        *://*.stackoverflow.com/*
// @match        *://*.stackexchange.com/*
// @match        *://*.superuser.com/*
// @match        *://*.serverfault.com/*
// @match        *://*.askubuntu.com/*
// @match        *://*.stackapps.com/*
// @match        *://*.mathoverflow.net/*
// @match        *://github.com/soscripted/*
// @match        *://soscripted.github.io/sox/*

// @exclude      *://data.stackexchange.com/*
// @exclude      *://api.stackexchange.com/*
// @exclude      *://stackoverflow.com/c/*

// @require      https://code.jquery.com/jquery-3.3.1.min.js
// @require      https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
// @require      https://api.stackexchange.com/js/2.0/all.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.5.3/jquery.timeago.min.js

// @require      sox.common.js
// @require      sox.github.js
// @require      sox.dialog.js
// @require      sox.features.js

// @resource     css sox.css
// @resource     dialog sox.dialog.html
// @resource     featuresJSON sox.features.info.json
// @resource     common sox.common.info.json
// @resource     sprites sox.sprites.svg

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @grant        GM_info
// ==/UserScript==
/*jshint loopfunc: true*/
(function(sox, $) {
  'use strict';

  function init() {
    if (sox.location.on('github.com/soscripted')) {
      try {
        sox.github.init(sox.info.version, sox.info.handler);
      } catch (e) {
        throw ('SOX: There was an error while attempting to initialize the sox.github.js file, please report this on GitHub.\n' + e);
      }
      return;
    }

    if (sox.location.on('soscripted.github.io/sox/#access_token')) { //save access token
      try {
        const access_token = window.location.href.split('=')[1].split('&')[0];
        sox.loginfo('ACCESS TOKEN: ', access_token);
        GM_setValue('SOX-accessToken', access_token);
        alert('Access token successfully saved! You can close this window :)');
      } catch (e) {
        throw ('SOX: There was an error saving your access token');
      }
      return;
    }
    if (sox.info.debugging) {
      sox.debug('DEBUGGING SOX VERSION ' + sox.info.version);
      sox.debug('----------------saved variables---------------------');
      sox.settings.writeToConsole(true); //true => hide access token
      sox.debug('----------------end saved variables---------------------');
    }

    const spritesDiv = $('<div/>', { html: GM_getResourceText('sprites') });
    $('head').append(spritesDiv);

    GM_addStyle(GM_getResourceText('css'));

    const settings = sox.settings.load();
    //returns undefined if not set

    const featureInfo = JSON.parse(GM_getResourceText('featuresJSON'));

    try {
      sox.debug('SOX object', sox);
      sox.dialog.init({
        version: sox.info.version,
        features: featureInfo,
        settings: settings,
        lastVersionInstalled: sox.info.lastVersionInstalled,
      });
    } catch (e) {
      throw ('SOX: There was an error while attempting to initialize the SOX Settings Dialog, please report this on GitHub.\n' + e);
    }

    if (sox.settings.available) {
      // Execute features
      performance.mark('allFeatures-start');
      for (let i = 0; i < settings.length; ++i) {
        const category = settings[i].split('-')[0];
        const featureId = settings[i].split('-')[1];

        if (!(category in featureInfo.categories)) { //if we ever rename a category
          sox.loginfo('Deleting feature "' + settings[i] + '" (category rename?)');
          settings.splice(i, 1);
          sox.settings.save(settings);
          continue;
        }

        const feature = featureInfo.categories[category].filter(obj => {
          return obj.name == featureId;
        })[0];

        let runFeature = true;
        try {
          //NOTE: there is no else if() because it is possible to have both match and exclude patterns..
          //which could have minor exceptions making it neccessary to check both
          if (feature.match !== '') {
            const sites = feature.match.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (!sox.location.matchWithPattern(sites[pattern])) {
                runFeature = false; //none of the patterns match the current site.. yet.
              } else {
                runFeature = true;
                break; //if it does match, then stop looping; we want the feature to run
              }
            }
          }
          if (feature.exclude !== '') {
            const sites = feature.exclude.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (sox.location.matchWithPattern(sites[pattern])) { //if current site is in list, DON'T run feature
                runFeature = false; //don't run feature
                break; //no need to keep on looping
              }
            }
          }
          if (runFeature) {
            sox.debug('running ' + featureId);
            performance.mark(`${featureId}-start`);
            if (feature.settings) {
              const settingsToPass = GM_getValue('SOX-' + featureId + '-settings') ? JSON.parse(GM_getValue('SOX-' + featureId + '-settings')) : {};
              sox.features[featureId](settingsToPass); //run the feature if match and exclude conditions are met, pass on settings object
            } else {
              sox.features[featureId](); //run the feature if match and exclude conditions are met
            }
            performance.mark(`${featureId}-end`);
            performance.measure(featureId, `${featureId}-start`, `${featureId}-end`);
          }
        } catch (err) {
          if (!sox.features[featureId] || !feature) { //remove deprecated/'corrupt' feature IDs from saved settings
            sox.loginfo('Deleting feature "' + settings[i] + '" (feature not found)');
            settings.splice(i, 1);
            sox.settings.save(settings);
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().parent().remove();
          } else {
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().css('color', 'red').attr('title', 'There was an error loading this feature. Please raise an issue on GitHub.');
            sox.error('There was an error loading the feature "' + settings[i] + '". Please raise an issue on GitHub, and copy the following error log:\n' + err);
          }
        }
      }
      performance.mark('allFeatures-end');
      performance.measure('allFeatures', 'allFeatures-start', 'allFeatures-end');
      sox.debug('Performance Data', performance.getEntriesByType('measure'));
    }

    //custom events....
    sox.helpers.observe([...document.getElementsByClassName('post-layout')], '.new_comment, .comment, .comments, .comment-text', node => {
      sox.debug('sox-new-comment event triggered');
      $(document).trigger('sox-new-comment', [node]);
    });

    sox.helpers.observe(document.body, 'textarea[id^="wmd-input"]', node => {
      sox.debug('sox-edit-window event triggered');
      $(document).trigger('sox-edit-window', [node]);
    });

    sox.helpers.observe(document.body, '.reviewable-post, .review-content', node => {
      sox.debug('sox-new-review-post-appeared event triggered');
      $(document).trigger('sox-new-review-post-appeared', [node]);
    });

    const chatBody = document.getElementById('chat-body');
    if (chatBody) {
      sox.helpers.observe(chatBody, '.user-popup', node => {
        sox.debug('sox-chat-user-popup event triggered');
        $(document).trigger('sox-chat-user-popup', [node]);
      });
    }

    if (GM_getValue('SOX-accessToken', -1) == -1) { //set access token
      //This was originally a series of IIFEs appended to the head which used the SE API JS SDK but
      //it was very uncertain and often caused issues, especially in FF
      //it now uses a Github page to show the access token
      //and detects that page and saves it automatically.
      //this seems to be a much cleaner and easier-to-debug method!
      GM_setValue('SOX-accessToken', -2); //once we ask the user once, don't ask them again: set the value to -2 so this IF never evaluates to true
      const askUserToAuthorise = window.confirm('To get the most out of SOX, you should get an access token! Please press "OK" to continue and follow the instructions in the window that opens. NOTE: this message will not appear again; if you choose not to, you can click the key at the bottom of the settings dialog at anytime to get one.');
      if (askUserToAuthorise) window.open('https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
      sox.warn('Please go to the following URL to get your access token for certain SOX features', 'https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
    }
  }
  sox.ready(init);
})(window.sox = window.sox || {}, jQuery);
<commit_msg>Bump version number<commit_after>// ==UserScript==
// @name         Stack Overflow Extras (SOX)
// @namespace    https://github.com/soscripted/sox
// @homepage     https://github.com/soscripted/sox
// @homepageURL  https://github.com/soscripted/sox
// @version      2.6.9 DEV
// @description  Extra optional features for Stack Overflow and Stack Exchange sites
// @contributor  ᴉʞuǝ (https://stackoverflow.com/users/1454538/, https://github.com/mezmi)
// @contributor  ᔕᖺᘎᕊ (https://stackexchange.com/users/4337810/, https://github.com/shu8)
// @contributor  Sir-Cumference (https://stackexchange.com/users/4119142/, https://github.com/Sir-Cumference)
// @contributor  GaurangTandon (https://github.com/GaurangTandon)
// @contributor  double-beep (https://stackexchange.com/users/14688437/double-beep, https://github.com/double-beep)
// @updateURL    https://cdn.jsdelivr.net/gh/soscripted/sox@dev/sox.user.js

// @match        *://*.stackoverflow.com/*
// @match        *://*.stackexchange.com/*
// @match        *://*.superuser.com/*
// @match        *://*.serverfault.com/*
// @match        *://*.askubuntu.com/*
// @match        *://*.stackapps.com/*
// @match        *://*.mathoverflow.net/*
// @match        *://github.com/soscripted/*
// @match        *://soscripted.github.io/sox/*

// @exclude      *://data.stackexchange.com/*
// @exclude      *://api.stackexchange.com/*
// @exclude      *://stackoverflow.com/c/*

// @require      https://code.jquery.com/jquery-3.3.1.min.js
// @require      https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
// @require      https://api.stackexchange.com/js/2.0/all.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.5.3/jquery.timeago.min.js

// @require      sox.common.js
// @require      sox.github.js
// @require      sox.dialog.js
// @require      sox.features.js

// @resource     css sox.css
// @resource     dialog sox.dialog.html
// @resource     featuresJSON sox.features.info.json
// @resource     common sox.common.info.json
// @resource     sprites sox.sprites.svg

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_getResourceText
// @grant        GM_addStyle
// @grant        GM_info
// ==/UserScript==
/*jshint loopfunc: true*/
(function(sox, $) {
  'use strict';

  function init() {
    if (sox.location.on('github.com/soscripted')) {
      try {
        sox.github.init(sox.info.version, sox.info.handler);
      } catch (e) {
        throw ('SOX: There was an error while attempting to initialize the sox.github.js file, please report this on GitHub.\n' + e);
      }
      return;
    }

    if (sox.location.on('soscripted.github.io/sox/#access_token')) { //save access token
      try {
        const access_token = window.location.href.split('=')[1].split('&')[0];
        sox.loginfo('ACCESS TOKEN: ', access_token);
        GM_setValue('SOX-accessToken', access_token);
        alert('Access token successfully saved! You can close this window :)');
      } catch (e) {
        throw ('SOX: There was an error saving your access token');
      }
      return;
    }
    if (sox.info.debugging) {
      sox.debug('DEBUGGING SOX VERSION ' + sox.info.version);
      sox.debug('----------------saved variables---------------------');
      sox.settings.writeToConsole(true); //true => hide access token
      sox.debug('----------------end saved variables---------------------');
    }

    const spritesDiv = $('<div/>', { html: GM_getResourceText('sprites') });
    $('head').append(spritesDiv);

    GM_addStyle(GM_getResourceText('css'));

    const settings = sox.settings.load();
    //returns undefined if not set

    const featureInfo = JSON.parse(GM_getResourceText('featuresJSON'));

    try {
      sox.debug('SOX object', sox);
      sox.dialog.init({
        version: sox.info.version,
        features: featureInfo,
        settings: settings,
        lastVersionInstalled: sox.info.lastVersionInstalled,
      });
    } catch (e) {
      throw ('SOX: There was an error while attempting to initialize the SOX Settings Dialog, please report this on GitHub.\n' + e);
    }

    if (sox.settings.available) {
      // Execute features
      performance.mark('allFeatures-start');
      for (let i = 0; i < settings.length; ++i) {
        const category = settings[i].split('-')[0];
        const featureId = settings[i].split('-')[1];

        if (!(category in featureInfo.categories)) { //if we ever rename a category
          sox.loginfo('Deleting feature "' + settings[i] + '" (category rename?)');
          settings.splice(i, 1);
          sox.settings.save(settings);
          continue;
        }

        const feature = featureInfo.categories[category].filter(obj => {
          return obj.name == featureId;
        })[0];

        let runFeature = true;
        try {
          //NOTE: there is no else if() because it is possible to have both match and exclude patterns..
          //which could have minor exceptions making it neccessary to check both
          if (feature.match !== '') {
            const sites = feature.match.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (!sox.location.matchWithPattern(sites[pattern])) {
                runFeature = false; //none of the patterns match the current site.. yet.
              } else {
                runFeature = true;
                break; //if it does match, then stop looping; we want the feature to run
              }
            }
          }
          if (feature.exclude !== '') {
            const sites = feature.exclude.split(',');

            for (let pattern = 0; pattern < sites.length; pattern++) {
              if (sox.location.matchWithPattern(sites[pattern])) { //if current site is in list, DON'T run feature
                runFeature = false; //don't run feature
                break; //no need to keep on looping
              }
            }
          }
          if (runFeature) {
            sox.debug('running ' + featureId);
            performance.mark(`${featureId}-start`);
            if (feature.settings) {
              const settingsToPass = GM_getValue('SOX-' + featureId + '-settings') ? JSON.parse(GM_getValue('SOX-' + featureId + '-settings')) : {};
              sox.features[featureId](settingsToPass); //run the feature if match and exclude conditions are met, pass on settings object
            } else {
              sox.features[featureId](); //run the feature if match and exclude conditions are met
            }
            performance.mark(`${featureId}-end`);
            performance.measure(featureId, `${featureId}-start`, `${featureId}-end`);
          }
        } catch (err) {
          if (!sox.features[featureId] || !feature) { //remove deprecated/'corrupt' feature IDs from saved settings
            sox.loginfo('Deleting feature "' + settings[i] + '" (feature not found)');
            settings.splice(i, 1);
            sox.settings.save(settings);
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().parent().remove();
          } else {
            $('#sox-settings-dialog-features').find('#' + settings[i].split('-')[1]).parent().css('color', 'red').attr('title', 'There was an error loading this feature. Please raise an issue on GitHub.');
            sox.error('There was an error loading the feature "' + settings[i] + '". Please raise an issue on GitHub, and copy the following error log:\n' + err);
          }
        }
      }
      performance.mark('allFeatures-end');
      performance.measure('allFeatures', 'allFeatures-start', 'allFeatures-end');
      sox.debug('Performance Data', performance.getEntriesByType('measure'));
    }

    //custom events....
    sox.helpers.observe([...document.getElementsByClassName('post-layout')], '.new_comment, .comment, .comments, .comment-text', node => {
      sox.debug('sox-new-comment event triggered');
      $(document).trigger('sox-new-comment', [node]);
    });

    sox.helpers.observe(document.body, 'textarea[id^="wmd-input"]', node => {
      sox.debug('sox-edit-window event triggered');
      $(document).trigger('sox-edit-window', [node]);
    });

    sox.helpers.observe(document.body, '.reviewable-post, .review-content', node => {
      sox.debug('sox-new-review-post-appeared event triggered');
      $(document).trigger('sox-new-review-post-appeared', [node]);
    });

    const chatBody = document.getElementById('chat-body');
    if (chatBody) {
      sox.helpers.observe(chatBody, '.user-popup', node => {
        sox.debug('sox-chat-user-popup event triggered');
        $(document).trigger('sox-chat-user-popup', [node]);
      });
    }

    if (GM_getValue('SOX-accessToken', -1) == -1) { //set access token
      //This was originally a series of IIFEs appended to the head which used the SE API JS SDK but
      //it was very uncertain and often caused issues, especially in FF
      //it now uses a Github page to show the access token
      //and detects that page and saves it automatically.
      //this seems to be a much cleaner and easier-to-debug method!
      GM_setValue('SOX-accessToken', -2); //once we ask the user once, don't ask them again: set the value to -2 so this IF never evaluates to true
      const askUserToAuthorise = window.confirm('To get the most out of SOX, you should get an access token! Please press "OK" to continue and follow the instructions in the window that opens. NOTE: this message will not appear again; if you choose not to, you can click the key at the bottom of the settings dialog at anytime to get one.');
      if (askUserToAuthorise) window.open('https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
      sox.warn('Please go to the following URL to get your access token for certain SOX features', 'https://stackexchange.com/oauth/dialog?client_id=7138&scope=no_expiry&redirect_uri=http://soscripted.github.io/sox/');
    }
  }
  sox.ready(init);
})(window.sox = window.sox || {}, jQuery);
<|endoftext|>


<commit_before>if (typeof (require) != 'undefined') {
	var loader = require('../../node_modules/Butterfly-js/dist/butterfly.min.js').loader;
}

/**
 * Module providing game data
 */
loader.addModule('data',
function () {
	var data = {
		maps: [
			{width: 20, height: 15, start: [[2, 5], [1, 5]], map:
			[[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
			 [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1],
			 [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1],
			 [1,0,0,5,1,1,1,1,1,1,1,1,1,1,1,1,0,0,2,1],
			 [1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,1],
			 [1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,1],
			 [1,3,3,1,0,0,1,1,1,1,1,1,1,0,0,1,0,0,0,1],
			 [1,0,0,1,0,0,0,0,0,1,2,2,1,0,0,1,0,0,0,1],
			 [1,0,0,1,0,0,0,0,0,1,1,1,1,0,0,1,1,0,0,1],
			 [1,0,0,1,1,1,5,0,0,1,0,0,0,0,0,1,0,0,0,1],
			 [1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1],
			 [1,0,0,1,0,0,0,0,0,1,0,0,5,1,1,1,0,0,2,1],
			 [1,0,0,0,0,0,2,2,1,1,0,0,0,0,0,0,0,0,2,1],
			 [1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,2,1],
			 [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]}
		],
		resourcesMap: {TILE_ROAD: 0, TILE_WALL: 1, TILE_GRASS: 2, TILE_START: 3, CAR: 4, TILE_FLAG: 5},
		resources: [
			{url: 'images/road.png', obstacle: false},
			{url: 'images/wall.png', obstacle: true},
			{url: 'images/grass.png', obstacle: false},
			{url: 'images/start.png', obstacle: false},
			{url: 'images/player1car.png'},
			{url: 'images/flag.png', obstacle: true}
		]
	};

	return data;
});
<commit_msg>starting point moved<commit_after>if (typeof (require) != 'undefined') {
	var loader = require('../../node_modules/Butterfly-js/dist/butterfly.min.js').loader;
}

/**
 * Module providing game data
 */
loader.addModule('data',
function () {
	var data = {
		maps: [
			{width: 20, height: 15, start: [[2, 7], [1, 7]], map:
			[[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
			 [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1],
			 [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1],
			 [1,0,0,5,1,1,1,1,1,1,1,1,1,1,1,1,0,0,2,1],
			 [1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,1],
			 [1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,2,1],
			 [1,3,3,1,0,0,1,1,1,1,1,1,1,0,0,1,0,0,0,1],
			 [1,0,0,1,0,0,0,0,0,1,2,2,1,0,0,1,0,0,0,1],
			 [1,0,0,1,0,0,0,0,0,1,1,1,1,0,0,1,1,0,0,1],
			 [1,0,0,1,1,1,5,0,0,1,0,0,0,0,0,1,0,0,0,1],
			 [1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1],
			 [1,0,0,1,0,0,0,0,0,1,0,0,5,1,1,1,0,0,2,1],
			 [1,0,0,0,0,0,2,2,1,1,0,0,0,0,0,0,0,0,2,1],
			 [1,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,2,1],
			 [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]]}
		],
		resourcesMap: {TILE_ROAD: 0, TILE_WALL: 1, TILE_GRASS: 2, TILE_START: 3, CAR: 4, TILE_FLAG: 5},
		resources: [
			{url: 'images/road.png', obstacle: false},
			{url: 'images/wall.png', obstacle: true},
			{url: 'images/grass.png', obstacle: false},
			{url: 'images/start.png', obstacle: false},
			{url: 'images/player1car.png'},
			{url: 'images/flag.png', obstacle: true}
		]
	};

	return data;
});
<|endoftext|>


<commit_before>import Ember from "ember";
export default Ember.Mixin.create({
    getPatientDisplayId: function(patient) {
        var externalPatientId = Ember.get(patient, 'externalPatientId'),
            friendlyId = Ember.get(patient, 'friendlyId'),
            id = Ember.get(patient, 'id');
        if (!Ember.isEmpty(friendlyId)) {
            return friendlyId;
        } else if (!Ember.isEmpty(externalPatientId)) {
            return externalPatientId;
        } else {
            return id;
        }
    },

    getPatientDisplayName: function(patient) {
        var firstName = Ember.get(patient, 'firstName'),
            lastName = Ember.get(patient, 'lastName'),
            returnName = '';
        if (!Ember.isEmpty(firstName)) {
            returnName += firstName;
        }
        if (!Ember.isEmpty(returnName) && !Ember.isEmpty(lastName)) {
            returnName += ' ';
        }
        if (!Ember.isEmpty(lastName)) {
            returnName += lastName;
        }
        return returnName;
    }
});<commit_msg>Added middle name to patient record<commit_after>import Ember from "ember";
export default Ember.Mixin.create({
    getPatientDisplayId: function(patient) {
        var externalPatientId = Ember.get(patient, 'externalPatientId'),
            friendlyId = Ember.get(patient, 'friendlyId'),
            id = Ember.get(patient, 'id');
        if (!Ember.isEmpty(friendlyId)) {
            return friendlyId;
        } else if (!Ember.isEmpty(externalPatientId)) {
            return externalPatientId;
        } else {
            return id;
        }
    },

    getPatientDisplayName: function(patient) {
        var firstName = Ember.get(patient, 'firstName'),
            lastName = Ember.get(patient, 'lastName'),
            middleName = Ember.get(patient, 'middleName'),
            nameArray = [];
        if (!Ember.isEmpty(firstName)) {
            nameArray.push(firstName);
        }
        if (!Ember.isEmpty(middleName)) {
            nameArray.push(middleName);
        }
        if (!Ember.isEmpty(lastName)) {
            nameArray.push(lastName);
        }
        return nameArray.join(' ');
    }
});<|endoftext|>


<commit_before>var socket = io.connect(window.location.href);

$(document).ready(function() {

  $('#song-count').text($('.track').length + ' tracks')

  // Get total time
  var total = 0;
  $('.time').each(function(index) {
    total += $(this).data('time');
  });
  var time = moment.duration({s: total });
  var hours = ' hours ';
  var minutes = ' minutes';

  if (time.hours() === 1) hours = ' hour ';
  if (time.minutes() === 1) minutes = ' minute';

  $('#total-time').text(time.hours() + hours + time.minutes() + minutes);

  socket.on('connect', function() {
    if (localStorage.getItem('isHost')) {
      socket.emit('hostComeback', this.socket.sessionid);
      socket.emit('checkIfHost', this.socket.sessionid);
    }

    socket.emit('checkIfHost', this.socket.sessionid);
    socket.on('checkIfHostAnswer', function(isHost) {
      if (!isHost) {
        console.log('You are not a host');
        $('#play').unbind('click');
        $('.track').unbind('dblclick');
        $('#pause').unbind('click');
        $('#forward').unbind('click');
        $('#backward').unbind('click');
        $('.guest-mode').show();
      } else {
        console.log('Yes you are a host');
        $('.host-mode').show();
        localStorage.setItem('isHost', true);

        // If host, set localstorage variable isHost = true
        // When disconnected, check on server if host is disconnected.
        // If it is, create a 60 sec timeout, after which hostId is next socket in line
        // If host comes back within 60 sec, clear the timeout

      }
    });

  });

  var latency;
  var latencyInterval;
  var isPlaying = false;
  var startTime;

  socket.on('pong', function() {
    latency = Date.now() - startTime;
  });

  $('#play').click(function(player) {
    if (isPlaying) {
      return;
    }
    $('.track').first().trigger('dblclick');
  });

  $('#pause').click(function () {
    isPlaying = false;
    clearInterval(latencyInterval);
    socket.emit('pause');
  });

  $('#forward').click(function() {
    if (isPlaying) {
      $('.highlight').next().trigger('dblclick');
    }
  });

  $('#backward').click(function() {
    if (isPlaying) {
      $('.highlight').prev().trigger('dblclick');
    }
  });
});<commit_msg>Update number of clients on socket "count" event<commit_after>var socket = io.connect(window.location.href);

$(document).ready(function() {

  $('#song-count').text($('.track').length + ' tracks')

  // Get total time
  var total = 0;
  $('.time').each(function(index) {
    total += $(this).data('time');
  });
  var time = moment.duration({s: total });
  var hours = ' hours ';
  var minutes = ' minutes';

  if (time.hours() === 1) hours = ' hour ';
  if (time.minutes() === 1) minutes = ' minute';

  $('#total-time').text(time.hours() + hours + time.minutes() + minutes);

  socket.on('connect', function() {
    if (localStorage.getItem('isHost')) {
      socket.emit('hostComeback', this.socket.sessionid);
      socket.emit('checkIfHost', this.socket.sessionid);
    }

    socket.emit('checkIfHost', this.socket.sessionid);
    socket.on('checkIfHostAnswer', function(isHost) {
      if (!isHost) {
        console.log('You are not a host');
        $('#play').unbind('click');
        $('.track').unbind('dblclick');
        $('#pause').unbind('click');
        $('#forward').unbind('click');
        $('#backward').unbind('click');
        $('.guest-mode').show();
      } else {
        console.log('Yes you are a host');
        $('.host-mode').show();
        localStorage.setItem('isHost', true);

        // If host, set localstorage variable isHost = true
        // When disconnected, check on server if host is disconnected.
        // If it is, create a 60 sec timeout, after which hostId is next socket in line
        // If host comes back within 60 sec, clear the timeout

      }
    });

  });

  var latency;
  var latencyInterval;
  var isPlaying = false;
  var startTime;

  socket.on('pong', function() {
    latency = Date.now() - startTime;
  });

  $('#play').click(function(player) {
    if (isPlaying) {
      return;
    }
    $('.track').first().trigger('dblclick');
  });

  $('#pause').click(function () {
    isPlaying = false;
    clearInterval(latencyInterval);
    socket.emit('pause');
  });

  $('#forward').click(function() {
    if (isPlaying) {
      $('.highlight').next().trigger('dblclick');
    }
  });

  $('#backward').click(function() {
    if (isPlaying) {
      $('.highlight').prev().trigger('dblclick');
    }
  });

  // Display number of connected users
  socket.on('count', function (data) {
    // clear everything in the dropdown menu
    $('.connected-counter ul.dropdown-menu').html('');
    for (var i = 0; i < data.clients.length; i++) {
      var client = data.clients[i];
      console.log(client)
      $('.connected-counter ul.dropdown-menu').append('<li>' + client.address.address + '</li>')
    }
    console.log(data.clients);
    $('#numberOfClients').hide().fadeIn(200).text(data.numberOfClients);
  });
});<|endoftext|>


<commit_before>'use strict';

const mongoose = require('mongoose');
const Promise = require('bluebird');
const moment = require('moment');

const logger = require('../../lib/logger');
const Message = require('./Message');
const helpers = require('../../lib/helpers');
const front = require('../../lib/front');
const twilio = require('../../lib/twilio');
const bertly = require('../../lib/bertly');

const config = require('../../config/app/models/conversation');

/**
 * Schema.
 */
const conversationSchema = new mongoose.Schema({
  userId: {
    type: String,
    index: true,
  },
  platform: String,
  platformUserId: {
    type: String,
    index: true,
  },
  topic: String,
  campaignId: Number,
  signupStatus: String,
  lastOutboundMessage: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Message',
  },
}, { timestamps: true });

conversationSchema.index({ createdAt: 1 });
conversationSchema.index({ updatedAt: 1 });
conversationSchema.index({ userId: 1, platform: 1 });

/**
 * @param {Object} req - Express request
 * @return {Promise}
 */
conversationSchema.statics.createFromReq = function (req) {
  const data = {
    userId: req.userId,
    platform: req.platform,
    platformUserId: req.platformUserId,
    topic: config.topics.default,
  };

  return this.create(data);
};

/**
 * @param {Object} req - Express request
 * @return {Promise}
 */
conversationSchema.statics.getFromReq = function (req) {
  const queryByUserId = { userId: req.userId, platform: req.platform };

  return this.findOneAndPopulateLastOutboundMessage(queryByUserId, req)
    .then((conversation) => {
      if (conversation) {
        return Promise.resolve(conversation);
      }
      // Have we already saved a Conversation for this User before we added userId?
      // TODO: Remove this when all Conversations have been backfilled with userId.
      const queryByPlatformUserId = { platform: 'sms', platformUserId: req.platformUserId };
      return this.findOneAndPopulateLastOutboundMessage(queryByPlatformUserId, req);
    });
};

/**
 * @param {Object} query - Mongoose query object
 * @param {Object} req - Express request
 * @return {Promise}
 */
conversationSchema.statics.findOneAndPopulateLastOutboundMessage = function (query, req) {
  logger.debug('Conversation.findOne', query, req);
  return this.findOne(query).populate('lastOutboundMessage');
};

/**
 * Updates topic and campaign per given topic object.
 * @param {Object} topic
 * @return {Promise}
 */
conversationSchema.methods.changeTopic = function (topicObject) {
  const topicId = topicObject.id;
  logger.debug('conversation.changeTopic', { topicId });
  this.topic = topicId;
  this.campaignId = topicObject.campaign ? topicObject.campaign.id : null;
  return this.save();
};

/**
 * TODO: This will be deprecated once broadcast content type is split out into multiple types.
 * Sets Rivescript topic property to a string.
 * @param {String} topic
 * @return {Promise}
 */
conversationSchema.methods.setTopic = function (topic) {
  if (topic === this.topic) {
    return Promise.resolve();
  }
  logger.debug('Conversation.setTopic', { topic });
  this.topic = topic;
  return this.save();
};

/**
 * @return {Promise}
 */
conversationSchema.methods.setDefaultTopic = function () {
  return this.setTopic(config.topics.default);
};

/**
 * @return {Promise}
 */
conversationSchema.methods.setCampaignTopic = function () {
  return this.setTopic(config.topics.campaign);
};

/**
 * @return {Promise}
 */
conversationSchema.methods.setSupportTopic = function () {
  return this.setTopic(config.topics.support);
};

/**
 * Returns save of User for updating given Campaign and its topic.
 * TODO: Refactor to just pass a campaignId. We were initially passing a campaign
 * model to set Conversation.topic (if Campaign had its own Rivescript topic defined).
 *
 * @param {Campaign} campaign
 * @return {Promise}
 */
conversationSchema.methods.setCampaignWithSignupStatus = function (campaign, signupStatus) {
  this.campaignId = campaign.id;
  this.signupStatus = signupStatus;
  logger.debug('setCampaignWithSignupStatus', { campaign: this.campaignId, signupStatus });

  return this.setCampaignTopic();
};

/**
 * Post signup for current campaign and set it as the topic.
 * @param {Campaign} campaign
 * @param {string} source
 * @param {string} keyword
 */
conversationSchema.methods.setCampaign = function (campaign) {
  return this.setCampaignWithSignupStatus(campaign, 'doing');
};

/**
 * Prompt signup for current campaign.
 * @param {Campaign} campaign
 * @param {string} source
 * @param {string} keyword
 */
conversationSchema.methods.promptSignupForCampaign = function (campaign) {
  return this.setCampaignWithSignupStatus(campaign, 'prompt');
};

/**
 * Decline signup for current campaign.
 * @param {Campaign} campaign
 * @param {string} source
 * @param {string} keyword
 */
conversationSchema.methods.declineSignup = function () {
  this.signupStatus = 'declined';
  return this.save();
};

/**
 * Gets data for a Conversation Message.
 * @param {string} text
 * @param {string} template
 * @return {object}
 */
conversationSchema.methods.getDefaultMessagePayload = function (text, template) {
  const data = {
    conversationId: this,
    campaignId: this.campaignId,
    topic: this.topic,
    userId: this.userId,
  };
  if (text) {
    data.text = text;
  }
  if (template) {
    data.template = template;
  }
  return data;
};

/**
 * Gets data from a req object for a Conversation Message.
 * TODO: refactor to use the req.inbound and req.outbound properties
 * @param {string} text
 * @param {string} template
 * @return {object}
 */
conversationSchema.methods.getMessagePayloadFromReq = function (req = {}, direction = '') {
  let broadcastId = null;

  // Attachments are stored in sub objects named according to the direction of the message
  // 'inbound' or 'outbound'
  const isOutbound = direction.includes('outbound');
  const isInbound = !isOutbound;
  const attachmentDirection = isOutbound ? 'outbound' : 'inbound';

  // Should only exist in outbound broadcast messages
  if (req.broadcastId) {
    broadcastId = req.broadcastId;
  // Set broadcastId when this is an inbound message responding to an outbound broadcast:
  } else if (direction === 'inbound') {
    broadcastId = req.lastOutboundBroadcastId;
  }

  // TODO: Handle platform dependent message properties here
  const data = {
    broadcastId,
    metadata: req.metadata || {},
    attachments: req.attachments ? req.attachments[attachmentDirection] : [],
  };

  // Add extras if present.

  // If inbound message and includes platformMessageId
  if (isInbound && req.platformMessageId) {
    data.platformMessageId = req.platformMessageId;
  }
  if (req.agentId) {
    data.agentId = req.agentId;
  }
  if (req.rivescriptMatch) {
    data.match = req.rivescriptMatch;
  }
  if (req.macro) {
    data.macro = req.macro;
  }
  return data;
};

/**
 * Creates Message for a Conversation with given params.
 * @param {string} direction
 * @param {string} text
 * @param {string} template
 * @param {array} attachments
 * @return {Promise}
 */
conversationSchema.methods.createMessage = async function (direction, text, template, req) {
  logger.debug('createMessage', { direction }, req);
  let messageText;
  if (direction !== 'inbound') {
    messageText = helpers.tags.render(text, req);

    if (bertly.isEnabled() && bertly.textHasLinks(messageText)) {
      messageText = await bertly.parseLinksInTextIntoRedirects(messageText);
    }
  } else {
    messageText = text;
  }

  const data = {
    text: messageText,
    direction,
    template,
  };

  // Merge default payload and payload from req
  const defaultPayload = this.getDefaultMessagePayload();
  Object.assign(data, defaultPayload, this.getMessagePayloadFromReq(req, direction));

  return Message.create(data);
};

/**
 * Sets and populates lastOutboundMessage for this conversation
 *
 * @param  {object} outboundMessage
 * @return {promise}
 */
conversationSchema.methods.setLastOutboundMessage = function (outboundMessage) {
  this.lastOutboundMessage = outboundMessage;
  return this.save()
    .then(() => this.populate('lastOutboundMessage').execPopulate());
};

/**
 * Creates Message with given params and saves it to lastOutboundMessage.
 * @param {string} direction
 * @param {string} text
 * @param {string} template
 * @return {Promise}
 */
conversationSchema.methods.createAndSetLastOutboundMessage = function (direction, text, template, req) { // eslint-disable-line max-len
  return this.createMessage(direction, text, template, req)
    .then((message) => {
      logger.debug('created message', { messageId: message.id }, req);
      // Backfill Conversations that may not have userId set.
      if (!this.userId) {
        logger.debug('Backfilling Conversation.userId', {
          userId: req.userId,
          conversationId: this.id,
        }, req);
        this.userId = req.userId;
      }
      return this.setLastOutboundMessage(message);
    });
};

/**
 * Posts the Last Outbound Message to Twilio for SMS conversations.
 */
conversationSchema.methods.postLastOutboundMessageToPlatform = function (req) {
  const messageText = this.lastOutboundMessage.text;

  if (!messageText || !this.isSms()) {
    return Promise.resolve();
  }

  const mediaUrl = this.lastOutboundMessage.attachments.map(attachment => attachment.url);

  return new Promise((resolve, reject) => {
    twilio.postMessage(req.platformUserId, messageText, mediaUrl)
      .then((twilioRes) => {
        const sid = twilioRes.sid;
        const status = twilioRes.status;
        logger.debug('twilio.postMessage', { sid, status }, req);

        // @see https://www.twilio.com/docs/api/messaging/message#resource-properties
        this.lastOutboundMessage.platformMessageId = sid;
        this.lastOutboundMessage.metadata.delivery.queuedAt = twilioRes.dateCreated;
        this.lastOutboundMessage.metadata.delivery.totalSegments = twilioRes.numSegments;
        return resolve(this.lastOutboundMessage.save());
      })
      .catch((error) => {
        /**
         * Save failure metadata before bubbling the error up the Promise chain when the error is
         * an unrecoverable error, since retries are suppressed when they are detected.
         */
        if (helpers.twilio.isBadRequestError(error)) {
          this.lastOutboundMessage.metadata.delivery.failedAt = moment().format();
          this.lastOutboundMessage.metadata.delivery.failureData = {
            code: error.code,
            message: error.message,
          };
          this.lastOutboundMessage.save()
            .then(() => reject(error))
            .catch((saveError) => {
              logger.error(
                'twilio.postMessage error: this.lastOutboundMessage.save() failed',
                saveError, req);
              reject(error);
            });
        } else {
          /**
           * Bubble up error so we can either suppress (unrecoverable error) or retry.
           */
          reject(error);
        }
      });
  });
};

/**
 * Posts the given Message from User to Support inbox for SMS conversations.
 */
conversationSchema.methods.postMessageToSupport = function (req, message) {
  if (!this.isSms()) {
    logger.debug('Support not available for platform.', { platform: this.platform }, req);
    return Promise.resolve();
  }

  return front.postMessage(req.userId, message.text)
    .then((res) => {
      logger.debug('front.postMessage response', { body: res.body }, req);
      return res;
    });
};

/**
 * @return {boolean}
 */
conversationSchema.methods.isSms = function () {
  return this.platform === 'sms';
};

/**
 * @return {boolean}
 */
conversationSchema.methods.isSupportTopic = function () {
  return this.topic === config.topics.support;
};

module.exports = mongoose.model('Conversation', conversationSchema);
<commit_msg>More cleanup per https://github.com/DoSomething/gambit-conversations/pull/339#discussion_r194618545<commit_after>'use strict';

const mongoose = require('mongoose');
const Promise = require('bluebird');
const moment = require('moment');

const logger = require('../../lib/logger');
const Message = require('./Message');
const helpers = require('../../lib/helpers');
const front = require('../../lib/front');
const twilio = require('../../lib/twilio');
const bertly = require('../../lib/bertly');

const config = require('../../config/app/models/conversation');

/**
 * Schema.
 */
const conversationSchema = new mongoose.Schema({
  userId: {
    type: String,
    index: true,
  },
  platform: String,
  platformUserId: {
    type: String,
    index: true,
  },
  topic: String,
  campaignId: Number,
  signupStatus: String,
  lastOutboundMessage: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Message',
  },
}, { timestamps: true });

conversationSchema.index({ createdAt: 1 });
conversationSchema.index({ updatedAt: 1 });
conversationSchema.index({ userId: 1, platform: 1 });

/**
 * @param {Object} req - Express request
 * @return {Promise}
 */
conversationSchema.statics.createFromReq = function (req) {
  const data = {
    userId: req.userId,
    platform: req.platform,
    platformUserId: req.platformUserId,
    topic: config.topics.default,
  };

  return this.create(data);
};

/**
 * @param {Object} req - Express request
 * @return {Promise}
 */
conversationSchema.statics.getFromReq = function (req) {
  const queryByUserId = { userId: req.userId, platform: req.platform };

  return this.findOneAndPopulateLastOutboundMessage(queryByUserId, req)
    .then((conversation) => {
      if (conversation) {
        return Promise.resolve(conversation);
      }
      // Have we already saved a Conversation for this User before we added userId?
      // TODO: Remove this when all Conversations have been backfilled with userId.
      const queryByPlatformUserId = { platform: 'sms', platformUserId: req.platformUserId };
      return this.findOneAndPopulateLastOutboundMessage(queryByPlatformUserId, req);
    });
};

/**
 * @param {Object} query - Mongoose query object
 * @param {Object} req - Express request
 * @return {Promise}
 */
conversationSchema.statics.findOneAndPopulateLastOutboundMessage = function (query, req) {
  logger.debug('Conversation.findOne', query, req);
  return this.findOne(query).populate('lastOutboundMessage');
};

/**
 * Updates topic and campaign per given topic object.
 * @param {Object} topic
 * @return {Promise}
 */
conversationSchema.methods.changeTopic = function (topicObject) {
  this.topic = topicObject.id;
  this.campaignId = topicObject.campaign ? topicObject.campaign.id : null;
  logger.debug('conversation.changeTopic', { topic: this.topic, campaignId: this.capmaignId });
  return this.save();
};

/**
 * TODO: This will be deprecated once broadcast content type is split out into multiple types.
 * Sets Rivescript topic property to a string.
 * @param {String} topic
 * @return {Promise}
 */
conversationSchema.methods.setTopic = function (topic) {
  if (topic === this.topic) {
    return Promise.resolve();
  }
  logger.debug('Conversation.setTopic', { topic });
  this.topic = topic;
  return this.save();
};

/**
 * @return {Promise}
 */
conversationSchema.methods.setDefaultTopic = function () {
  return this.setTopic(config.topics.default);
};

/**
 * @return {Promise}
 */
conversationSchema.methods.setCampaignTopic = function () {
  return this.setTopic(config.topics.campaign);
};

/**
 * @return {Promise}
 */
conversationSchema.methods.setSupportTopic = function () {
  return this.setTopic(config.topics.support);
};

/**
 * Returns save of User for updating given Campaign and its topic.
 * TODO: Refactor to just pass a campaignId. We were initially passing a campaign
 * model to set Conversation.topic (if Campaign had its own Rivescript topic defined).
 *
 * @param {Campaign} campaign
 * @return {Promise}
 */
conversationSchema.methods.setCampaignWithSignupStatus = function (campaign, signupStatus) {
  this.campaignId = campaign.id;
  this.signupStatus = signupStatus;
  logger.debug('setCampaignWithSignupStatus', { campaign: this.campaignId, signupStatus });

  return this.setCampaignTopic();
};

/**
 * Post signup for current campaign and set it as the topic.
 * @param {Campaign} campaign
 * @param {string} source
 * @param {string} keyword
 */
conversationSchema.methods.setCampaign = function (campaign) {
  return this.setCampaignWithSignupStatus(campaign, 'doing');
};

/**
 * Prompt signup for current campaign.
 * @param {Campaign} campaign
 * @param {string} source
 * @param {string} keyword
 */
conversationSchema.methods.promptSignupForCampaign = function (campaign) {
  return this.setCampaignWithSignupStatus(campaign, 'prompt');
};

/**
 * Decline signup for current campaign.
 * @param {Campaign} campaign
 * @param {string} source
 * @param {string} keyword
 */
conversationSchema.methods.declineSignup = function () {
  this.signupStatus = 'declined';
  return this.save();
};

/**
 * Gets data for a Conversation Message.
 * @param {string} text
 * @param {string} template
 * @return {object}
 */
conversationSchema.methods.getDefaultMessagePayload = function (text, template) {
  const data = {
    conversationId: this,
    campaignId: this.campaignId,
    topic: this.topic,
    userId: this.userId,
  };
  if (text) {
    data.text = text;
  }
  if (template) {
    data.template = template;
  }
  return data;
};

/**
 * Gets data from a req object for a Conversation Message.
 * TODO: refactor to use the req.inbound and req.outbound properties
 * @param {string} text
 * @param {string} template
 * @return {object}
 */
conversationSchema.methods.getMessagePayloadFromReq = function (req = {}, direction = '') {
  let broadcastId = null;

  // Attachments are stored in sub objects named according to the direction of the message
  // 'inbound' or 'outbound'
  const isOutbound = direction.includes('outbound');
  const isInbound = !isOutbound;
  const attachmentDirection = isOutbound ? 'outbound' : 'inbound';

  // Should only exist in outbound broadcast messages
  if (req.broadcastId) {
    broadcastId = req.broadcastId;
  // Set broadcastId when this is an inbound message responding to an outbound broadcast:
  } else if (direction === 'inbound') {
    broadcastId = req.lastOutboundBroadcastId;
  }

  // TODO: Handle platform dependent message properties here
  const data = {
    broadcastId,
    metadata: req.metadata || {},
    attachments: req.attachments ? req.attachments[attachmentDirection] : [],
  };

  // Add extras if present.

  // If inbound message and includes platformMessageId
  if (isInbound && req.platformMessageId) {
    data.platformMessageId = req.platformMessageId;
  }
  if (req.agentId) {
    data.agentId = req.agentId;
  }
  if (req.rivescriptMatch) {
    data.match = req.rivescriptMatch;
  }
  if (req.macro) {
    data.macro = req.macro;
  }
  return data;
};

/**
 * Creates Message for a Conversation with given params.
 * @param {string} direction
 * @param {string} text
 * @param {string} template
 * @param {array} attachments
 * @return {Promise}
 */
conversationSchema.methods.createMessage = async function (direction, text, template, req) {
  logger.debug('createMessage', { direction }, req);
  let messageText;
  if (direction !== 'inbound') {
    messageText = helpers.tags.render(text, req);

    if (bertly.isEnabled() && bertly.textHasLinks(messageText)) {
      messageText = await bertly.parseLinksInTextIntoRedirects(messageText);
    }
  } else {
    messageText = text;
  }

  const data = {
    text: messageText,
    direction,
    template,
  };

  // Merge default payload and payload from req
  const defaultPayload = this.getDefaultMessagePayload();
  Object.assign(data, defaultPayload, this.getMessagePayloadFromReq(req, direction));

  return Message.create(data);
};

/**
 * Sets and populates lastOutboundMessage for this conversation
 *
 * @param  {object} outboundMessage
 * @return {promise}
 */
conversationSchema.methods.setLastOutboundMessage = function (outboundMessage) {
  this.lastOutboundMessage = outboundMessage;
  return this.save()
    .then(() => this.populate('lastOutboundMessage').execPopulate());
};

/**
 * Creates Message with given params and saves it to lastOutboundMessage.
 * @param {string} direction
 * @param {string} text
 * @param {string} template
 * @return {Promise}
 */
conversationSchema.methods.createAndSetLastOutboundMessage = function (direction, text, template, req) { // eslint-disable-line max-len
  return this.createMessage(direction, text, template, req)
    .then((message) => {
      logger.debug('created message', { messageId: message.id }, req);
      // Backfill Conversations that may not have userId set.
      if (!this.userId) {
        logger.debug('Backfilling Conversation.userId', {
          userId: req.userId,
          conversationId: this.id,
        }, req);
        this.userId = req.userId;
      }
      return this.setLastOutboundMessage(message);
    });
};

/**
 * Posts the Last Outbound Message to Twilio for SMS conversations.
 */
conversationSchema.methods.postLastOutboundMessageToPlatform = function (req) {
  const messageText = this.lastOutboundMessage.text;

  if (!messageText || !this.isSms()) {
    return Promise.resolve();
  }

  const mediaUrl = this.lastOutboundMessage.attachments.map(attachment => attachment.url);

  return new Promise((resolve, reject) => {
    twilio.postMessage(req.platformUserId, messageText, mediaUrl)
      .then((twilioRes) => {
        const sid = twilioRes.sid;
        const status = twilioRes.status;
        logger.debug('twilio.postMessage', { sid, status }, req);

        // @see https://www.twilio.com/docs/api/messaging/message#resource-properties
        this.lastOutboundMessage.platformMessageId = sid;
        this.lastOutboundMessage.metadata.delivery.queuedAt = twilioRes.dateCreated;
        this.lastOutboundMessage.metadata.delivery.totalSegments = twilioRes.numSegments;
        return resolve(this.lastOutboundMessage.save());
      })
      .catch((error) => {
        /**
         * Save failure metadata before bubbling the error up the Promise chain when the error is
         * an unrecoverable error, since retries are suppressed when they are detected.
         */
        if (helpers.twilio.isBadRequestError(error)) {
          this.lastOutboundMessage.metadata.delivery.failedAt = moment().format();
          this.lastOutboundMessage.metadata.delivery.failureData = {
            code: error.code,
            message: error.message,
          };
          this.lastOutboundMessage.save()
            .then(() => reject(error))
            .catch((saveError) => {
              logger.error(
                'twilio.postMessage error: this.lastOutboundMessage.save() failed',
                saveError, req);
              reject(error);
            });
        } else {
          /**
           * Bubble up error so we can either suppress (unrecoverable error) or retry.
           */
          reject(error);
        }
      });
  });
};

/**
 * Posts the given Message from User to Support inbox for SMS conversations.
 */
conversationSchema.methods.postMessageToSupport = function (req, message) {
  if (!this.isSms()) {
    logger.debug('Support not available for platform.', { platform: this.platform }, req);
    return Promise.resolve();
  }

  return front.postMessage(req.userId, message.text)
    .then((res) => {
      logger.debug('front.postMessage response', { body: res.body }, req);
      return res;
    });
};

/**
 * @return {boolean}
 */
conversationSchema.methods.isSms = function () {
  return this.platform === 'sms';
};

/**
 * @return {boolean}
 */
conversationSchema.methods.isSupportTopic = function () {
  return this.topic === config.topics.support;
};

module.exports = mongoose.model('Conversation', conversationSchema);
<|endoftext|>


<commit_before>var mongodb = require ('@onehilltech/blueprint-mongodb')
  ;

var schema = new mongodb.Schema({
  name:       {type: String, required: true, trim: true},
  location:   {type: String, required: true, trim: true},
  website:    {type: String, required: true, trim: true},
  industry:   {type: String, required: true, trim: true},
});

const COLLECTION_NAME = 'org';

module.exports = exports = mongodb.model (COLLECTION_NAME, schema);
<commit_msg>wording consistency fixes<commit_after>var mongodb = require ('@onehilltech/blueprint-mongodb')
  ;

var schema = new mongodb.Schema({
  name:       {type: String, required: true, trim: true},
  location:   {type: String, required: true, trim: true},
  website:    {type: String, required: true, trim: true},
  industry:   {type: String, required: true, trim: true},
});

const COLLECTION_NAME = 'organization';

module.exports = exports = mongodb.model (COLLECTION_NAME, schema);
<|endoftext|>