<commit_before>var con = require('./database.js').connection;
var moment = require('moment');
var settings = require('./settings').system_settings;
var middleware = require('./roles.js');

// A message to students who haven't signed up after the student cutoff time has passed
// Can't be a system setting because it is text
// Will reflect an administrator decision
var message_students_notsignedup = "Meet Mr. Ware in the dining hall!";

module.exports = {
 // Gets the number of students in an offering on a day
 numStudents: function(uid_day, uid_offering, getStudentInfo, callback)	{
 	var numStud = 0;
 	var studList = [];
  //	TO DO:
  //	REPLACE THIS CALLBACK STRING WITH A JOIN QUERY
  //	EITHER USE GETSTUDENTINFO IN ANOTHER FILE OR CUT IT
  //
 	con.query('SELECT * FROM choices', function(err, row) {
    	if(!err) {
	    	for(var i=0; i<row.length; i++) {
	         	if(uid_offering == row[i].uid_offering && uid_day == row[i].uid_day) {
		           	numStud += 1;
		           	if(getStudentInfo) {
		             	studList.push(row[i].uid_student);
		           	}
	         	}
	       	}
	       	if(getStudentInfo) {
	         	var infoList = [];
	         	con.query('SELECT * FROM students', function(err, row) {
		           	if(!err) {
		            	for(var i=0; i<row.length; i++) {
		               		for(var j=0; j<studList.length; j++) {
			                 	if(row[i].uid_student == studList[j]) {
			                		infoList.push(row[i].firstname + " " + row[i].lastname);
			                 	}
		               		}
		            	}
		            	callback(numStud, infoList);
		            } else {
		            	console.log("SELECT FROM STUDENTS DONE ERRD");
	             		console.log(err);
		            }
	         	});
	       	} else {
	         callback(numStud, null);
	     	}
       	} else {
       		console.log("SELECT FROM CHOICES DONE ERRD");
       		console.log(err);
 		}
 	});
 },

 //	Pretty Self Explanatory
 isOfferingFull: function (uid_day, uid_offering, callback) {
   con.query('SELECT max_size FROM offerings WHERE uid_offering = ?', [uid_offering], function(err, data) {
    if(!err) {
      module.exports.numStudents(uid_day, uid_offering, false, function(num, infoList){
        if(num >= data[0].max_size) {
          callback("disabled");
        } else {
          callback("able");
        }
      });
    } else {
      console.log("The function produced an error.");
    }
  });
 },

 // Function gets all offerings on a certain day (uid_day)
 // Returns a list ('offerList') of Offering objects, with all necessary properties (although the teacher will be a Name NOT a uid)
 getOfferings: function (uid_day, callback) {
   function Offering(uid, name, description, maxSize, recurring, teacher) {
    this.uid = uid;
    this.name = name;
    this.description = description;
    this.maxSize = maxSize;
    this.recur = recurring;
    this.teacher = teacher;
    this.disabled;
  }
  var offerList = [];
  var trueOffers = [];
  //	TO DO:
  //	REPLACE THIS CALLBACK STRING WITH A JOIN QUERY
  //
  //	Gets all offerings associated with that day
  con.query('SELECT * FROM calendar', function(err, dayList){
    for(var i=0; i<dayList.length; i++) {
      if(dayList[i].uid_day == uid_day) {
        trueOffers.push(dayList[i].uid_offering);
      }
    }
    //	Gets the info associated with those offerings
    con.query('SELECT * FROM offerings', function(err, rowList) {
      if(!err) {
        for(var i=0; i<rowList.length; i++) {
          for(var j=0; j<trueOffers.length; j++) {
            if(rowList[i].uid_offering == trueOffers[j]) {
              var offering = new Offering(rowList[i].uid_offering, rowList[i].name, rowList[i].description, rowList[i].max_size, rowList[i].recurring, rowList[i].uid_teacher);
              offerList.push(offering);
            }
          }
        }
        //	Gets the teacher's names rather than their uids
        con.query('SELECT * FROM teachers', function(err, row) {
          if(!err) {
            for(var j=0; j<row.length; j++) {
              for(var i=0; i<offerList.length; i++) {
                if(row[j].uid_teacher == offerList[i].teacher) {
                  offerList[i].teacher = row[j].prefix + " " + row[j].teacher_lastname;
                };
              };
            };
            callback(offerList);
          } else {
            console.log("We're sorry. getOfferings() produced an error.");
          }
        });
      } else {
        console.log("We're sorry. getOfferings() produced an error.");
      }
    });
  });
 },

// Function takes in an Oppblock day
// Returns a list of unfilled Offering Objects for that day
 getAvailableOfferings: function (uid_day, callback) {
  //	TO DO:
  //	REMOVE THIS FUNCTION AND PUT THE DISABLING LOOP IN GETOFFERINGS
  var availableList = []
  module.exports.getOfferings(uid_day, function(response) {
    var j = 0;
    for(var i = 0; i <response.length; i++) {
      module.exports.isOfferingFull(uid_day, response[i].uid, function(truth){
		  response[j].disabled = truth;
		  availableList.push(response[j]);
		  j+=1;
		  if(j==response.length) {
			callback(availableList);
		  }
      });
    }
  });
 },

// Takes in nothing
// Returns two values:
// A uid_day of the coming oppblock (null if the students can't yet choose) AND
// A boolean cutOff, signifying whether the user is now past the cutoff time for students' choices
 getSoonestOppblockDay: function(callback) {
 	//	TO DO:
 	//	FIND OUT ANYWHERE ELSE IN PROJECT WHERE THIS FUNCTION MIGHT BE NECESSARY//MAKE IT MORE GENERAL FOR THOSE PURPOSES
	con.query('SELECT * FROM opp_block_day', function(err, results){
		// Gets all Oppblock days
		if(!err){
			var uid_day = null;
			var closest = moment().add(1, 'y');
			for (var i=0; i<results.length; i++) {
				//	Loops to find soonest Oppblock
				var curr = moment(results[i].day, 'YYYY-MM-DD');
				//	Checks if the current oppBlock day is in the future and before the closest
				//	If so, replaces closest with the current
				if (curr.isBefore(closest) && curr.isSameOrAfter(moment())) {
					closest = curr;
					uid_day = results[i].uid_day;
				}
			}
			// 	Creates Cutoff time variables relative to closest oppblock, based on admin settings
			var studentCutoff = moment(closest.add({hours:settings["hours_close_student"].value_int}));
			var teacherCutoff = moment(closest.add({hours:settings["hours_close_teacher"].value_int}));
			// 	Specficies the oppblock to a specific time on that specific date
			closest = closest.add({hours:settings["hours_close_oppblock"].value_int, minutes:settings["minutes_close_oppblock"].value_int});

			//	Checks to see whether the date is before or after the student and teacher cutoff times
			if (moment().isSameOrAfter(teacherCutoff)) {
				if (moment().isSameOrAfter(studentCutoff)) {
					callback(uid_day, true);
				} else {
					callback(uid_day, false);
				}
			} else {
				callback(null, null);
			}
		} else {
			console.log("Error in finding the upcoming oppblock");
		}
	});
 },
 init: function(app) {
	app.get('/student', middleware.isStudent, function(req, res){
		// Gets Student's id from middleware
		var student = req.user.local[0];
		var uid_student = student.uid_student;
		// This query gets the student's first name for the display page, thereby checking whether the url contained a valid uid
		// This function finds the upcoming oppblock, whether it is time for students to choose, and whether it is past the cutoff time
		module.exports.getSoonestOppblockDay(function(uid_day, cutOff) {
		// Checks whether it is time for students to choose yet
			if (uid_day == null) {
				res.render('student.html', {Student:student.firstname, Choice:"No Choice Selected", Description:"We're sorry, but the next OppBlock choices aren't ready yet. Check back soon!", oppTime:true, notExcluded:true});
			} else {
				// Knowing there is an upcoming oppblock day with choices, the system queries to find the student's current choice
				con.query('SELECT uid_offering FROM choices WHERE uid_student = ? AND uid_day = ?', [uid_student, uid_day], function(err, currentChoice) { // only gets the uid not the name
					if(!err) {
						// Checks if the student is in the choice table at all, thereby seeing if he/she is excluded from the oppblock day
						if(currentChoice.length != 0) {
							//	Checks to see if the student hasn't chosen yet
							if (currentChoice[0].uid_offering == null) {
								// Checks to see if it is past the cutoff time for the students to choose
								if (cutOff) {
									// Renders the page with an admin's message for the student, as they have neglected to sign up.
									res.render('student.html', {Student:student.firstname, Choice:"None (You Forgot to Sign Up!)", Description:message_students_notsignedup, oppTime:true, notExcluded:true});
								} else {
									// Gets all offerings for the user to choose from
									module.exports.getAvailableOfferings(uid_day, function(offerings) {
										// At last, renders the page with the lack of choice, and the choices table
										res.render('student.html', {Student:student.firstname, Choice:"None", Description:"Choose an offering from the table below!", uid_day:uid_day, data:offerings, cutOffStudent:settings["hours_close_student"].value_int, notExcluded:true});
									});
								}
							} else {
								//	Knowing the student has chosen, it gets the name of their choice
								con.query('SELECT * FROM offerings WHERE uid_offering = ?', [currentChoice[0].uid_offering], function(err, choice) {
									if(!err) {
										// Checks to see if it is past the cutoff time for the students to choose
										if (cutOff) {
											// Renders the page only with the user's current choice
											res.render('student.html', {Student:student.firstname, Choice:choice[0].name, Description:"The time for choosing has passed. At 2:45, head to " + choice[0].location + "!", oppTime:true, notExcluded:true});
										} else {
											// Gets all offerings for the user to choose from
											module.exports.getAvailableOfferings(uid_day, function(offerings) {
												// At last, renders the page with the current choice, and the choices table
												res.render('student.html', {Student:student.firstname, Choice:choice[0].name, Description:"You've already chosen, but if you'd like to change your choice, choose a different offering!", uid_day:uid_day, data:offerings, cutOffStudent:settings["hours_close_student"].value_int, notExcluded:true});
											});
										}
									} else {
										res.render('error.html', {err:err});
									}
								});
							}
						} else {
							// Renders the page without any choices, since the student is excluded
							res.render('student.html', {Student:student.firstname, Choice:"No Choice Required", Description:"Due to a sport or perhaps some other commitment, you will not participate in Oppblock today. Press Override if this doesn't apply to you.", uid_day:uid_day, oppTime:true});
						}
					} else {
						console.log("An error done occured.");
						res.render('error.html', {err:err});
					}
				});
			}
		});
	});

	app.post('/student', middleware.isStudent, function(req, response) {
		// Gets Student's id from middleware
		var uid_student = req.user.local[0].uid_student;

		// checks if post request comes from an override or a choice
		if(req.body.choice != undefined) {
			// Updates Choice
			con.query('UPDATE choices SET uid_offering = ? WHERE uid_student = ? AND uid_day = ?', [req.body.choice, uid_student, req.body.uid_day], function(err, results) {
				response.redirect('/student');
				response.end();
			});
		} else {
			// Overrides excluded group by adding student into choice table
			con.query('INSERT INTO choices (uid_offering, uid_student, uid_day) values (?, ?, ?)', [null, uid_student, req.body.uid_day], function(err) {
				if(!err) {
					response.redirect('/student');
					response.end();
				} else {
					res.send(err);
					res.render('error.html', {err:err});
				}
			});
		}
	});
	return this;
 }
}
<commit_msg>Tested Every Case and Further Cleaned Student Page<commit_after>var con = require('./database.js').connection;
var moment = require('moment');
var settings = require('./settings').system_settings;
var middleware = require('./roles.js');

// A message to students who haven't signed up after the student cutoff time has passed
// Can't be a system setting because it is text
// Will reflect an administrator decision
var message_students_notsignedup = "Meet Mr. Ware in the dining hall!";

module.exports = {
 // Gets the number of students in an offering on a day
 numStudents: function(uid_day, uid_offering, getStudentInfo, callback)	{
 	var numStud = 0;
 	var studList = [];
  //	TO DO:
  //	REPLACE THIS CALLBACK STRING WITH A JOIN QUERY
  //	EITHER USE GETSTUDENTINFO IN ANOTHER FILE OR CUT IT
  //
 	con.query('SELECT * FROM choices', function(err, row) {
    	if(!err) {
	    	for(var i=0; i<row.length; i++) {
	         	if(uid_offering == row[i].uid_offering && uid_day == row[i].uid_day) {
		           	numStud += 1;
		           	if(getStudentInfo) {
		             	studList.push(row[i].uid_student);
		           	}
	         	}
	       	}
	       	if(getStudentInfo) {
	         	var infoList = [];
	         	con.query('SELECT * FROM students', function(err, row) {
		           	if(!err) {
		            	for(var i=0; i<row.length; i++) {
		               		for(var j=0; j<studList.length; j++) {
			                 	if(row[i].uid_student == studList[j]) {
			                		infoList.push(row[i].firstname + " " + row[i].lastname);
			                 	}
		               		}
		            	}
		            	callback(numStud, infoList);
		            } else {
		            	console.log("SELECT FROM STUDENTS DONE ERRD");
	             		console.log(err);
		            }
	         	});
	       	} else {
	         callback(numStud, null);
	     	}
       	} else {
       		console.log("SELECT FROM CHOICES DONE ERRD");
       		console.log(err);
 		}
 	});
 },

 //	Pretty Self Explanatory
 isOfferingFull: function (uid_day, uid_offering, callback) {
   con.query('SELECT max_size FROM offerings WHERE uid_offering = ?', [uid_offering], function(err, data) {
    if(!err) {
      module.exports.numStudents(uid_day, uid_offering, false, function(num, infoList){
        if(num >= data[0].max_size) {
          callback("disabled");
        } else {
          callback("able");
        }
      });
    } else {
      console.log("The function produced an error.");
    }
  });
 },

 // Function gets all offerings on a certain day (uid_day)
 // Returns a list ('offerList') of Offering objects, with all necessary properties (although the teacher will be a Name NOT a uid)
 getOfferings: function (uid_day, callback) {
   function Offering(uid, name, description, maxSize, recurring, teacher, location) {
    this.uid = uid;
    this.name = name;
    this.description = description;
    this.maxSize = maxSize;
    this.recur = recurring;
    this.teacher = teacher;
    this.location = location;
    this.disabled;
  }
  var offerList = [];
  var trueOffers = [];
  //	TO DO:
  //	REPLACE THIS CALLBACK STRING WITH A JOIN QUERY
  //
  //	Gets all offerings associated with that day
  con.query('SELECT * FROM calendar', function(err, dayList){
    for(var i=0; i<dayList.length; i++) {
      if(dayList[i].uid_day == uid_day) {
        trueOffers.push(dayList[i].uid_offering);
      }
    }
    //	Gets the info associated with those offerings
    con.query('SELECT * FROM offerings', function(err, rowList) {
      if(!err) {
        for(var i=0; i<rowList.length; i++) {
          for(var j=0; j<trueOffers.length; j++) {
            if(rowList[i].uid_offering == trueOffers[j]) {
              var offering = new Offering(rowList[i].uid_offering, rowList[i].name, rowList[i].description, rowList[i].max_size, rowList[i].recurring, rowList[i].uid_teacher, rowList[i].location);
              offerList.push(offering);
            }
          }
        }
        //	Gets the teacher's names rather than their uids
        con.query('SELECT * FROM teachers', function(err, row) {
          if(!err) {
            for(var j=0; j<row.length; j++) {
              for(var i=0; i<offerList.length; i++) {
                if(row[j].uid_teacher == offerList[i].teacher) {
                  offerList[i].teacher = row[j].prefix + " " + row[j].teacher_lastname;
                };
              };
            };
            callback(offerList);
          } else {
            console.log("We're sorry. getOfferings() produced an error.");
          }
        });
      } else {
        console.log("We're sorry. getOfferings() produced an error.");
      }
    });
  });
 },

// Function takes in an Oppblock day
// Returns a list of unfilled Offering Objects for that day
 getAvailableOfferings: function (uid_day, callback) {
  //	TO DO:
  //	REMOVE THIS FUNCTION AND PUT THE DISABLING LOOP IN GETOFFERINGS
  var availableList = []
  module.exports.getOfferings(uid_day, function(response) {
    var j = 0;
    for(var i = 0; i <response.length; i++) {
      module.exports.isOfferingFull(uid_day, response[i].uid, function(truth){
		  response[j].disabled = truth;
		  availableList.push(response[j]);
		  j+=1;
		  if(j==response.length) {
			callback(availableList);
		  }
      });
    }
  });
 },

// Takes in nothing
// Returns two values:
// A uid_day of the coming oppblock (null if the students can't yet choose) AND
// A boolean cutOff, signifying whether the user is now past the cutoff time for students' choices
 getSoonestOppblockDay: function(callback) {
 	//	TO DO:
 	//	FIND OUT ANYWHERE ELSE IN PROJECT WHERE THIS FUNCTION MIGHT BE NECESSARY//MAKE IT MORE GENERAL FOR THOSE PURPOSES
	con.query('SELECT * FROM opp_block_day', function(err, results){
		// Gets all Oppblock days
		if(!err){
			var uid_day = null;
			var closest = moment().add(1, 'y');
			for (var i=0; i<results.length; i++) {
				//	Loops to find soonest Oppblock
				var curr = moment(results[i].day, 'YYYY-MM-DD');
				// 	Specficies the oppblock to the end of oppblock on that date
				//	TO DO: SIMPLY THIS CODE
				curr.add({hours:settings["hours_close_oppblock"].value_int, minutes:settings["minutes_close_oppblock"].value_int});
				curr.add({minutes:settings["minutes_length_oppblock"].value_int});
				//	Checks if the current oppBlock day is in the future and before the closest
				//	If so, replaces closest with the current
				console.log(curr);
				if (curr.isBefore(closest) && curr.isSameOrAfter(moment())) {
					closest = moment(curr.format('YYYY-MM-DD'));
					uid_day = results[i].uid_day;
				}
			}
			// 	Creates Cutoff time variables relative to closest oppblock, based on admin settings
			var studentCutoff = moment(closest.add({hours:settings["hours_close_student"].value_int}));
			var teacherCutoff = moment(closest.add({hours:settings["hours_close_teacher"].value_int}));

			//	Checks to see whether the date is before or after the student and teacher cutoff times
			if (moment().isSameOrAfter(teacherCutoff)) {
				if (moment().isSameOrAfter(studentCutoff)) {
					callback(uid_day, true);
				} else {
					callback(uid_day, false);
				}
			} else {
				callback(null, null);
			}
		} else {
			console.log("Error in finding the upcoming oppblock");
		}
	});
 },
 init: function(app) {
	app.get('/student', middleware.isStudent, function(req, res){
		// Gets Student's id from middleware
		var student = req.user.local[0];
		var uid_student = student.uid_student;
		// This query gets the student's first name for the display page, thereby checking whether the url contained a valid uid
		// This function finds the upcoming oppblock, whether it is time for students to choose, and whether it is past the cutoff time
		module.exports.getSoonestOppblockDay(function(uid_day, cutOff) {
		// Checks whether it is time for students to choose yet
			if (uid_day == null) {
				res.render('student.html', {Student:student.firstname, Choice:"No Choice Selected", Description:"We're sorry, but the next OppBlock choices aren't ready yet. Check back soon!", oppTime:true, notExcluded:true});
			} else {
				// Knowing there is an upcoming oppblock day with choices, the system queries to find the student's current choice
				con.query('SELECT uid_offering FROM choices WHERE uid_student = ? AND uid_day = ?', [uid_student, uid_day], function(err, currentChoice) { // only gets the uid not the name
					if(!err) {
						// Checks if the student is in the choice table at all, thereby seeing if he/she is excluded from the oppblock day
						if(currentChoice.length != 0) {
							//	Checks to see if the student hasn't chosen yet
							if (currentChoice[0].uid_offering == null) {
								// Checks to see if it is past the cutoff time for the students to choose
								if (cutOff) {
									// Renders the page with an admin's message for the student, as they have neglected to sign up.
									res.render('student.html', {Student:student.firstname, Choice:"None (You Forgot to Sign Up!)", Description:message_students_notsignedup, oppTime:true, notExcluded:true});
								} else {
									// Gets all offerings for the user to choose from
									module.exports.getAvailableOfferings(uid_day, function(offerings) {
										// At last, renders the page with the lack of choice, and the choices table
										res.render('student.html', {Student:student.firstname, Choice:"None", Description:"Choose an offering from the table below!", uid_day:uid_day, data:offerings, cutOffStudent:settings["hours_close_student"].value_int, notExcluded:true});
									});
								}
							} else {
								//	Knowing the student has chosen, it gets the name of their choice
								con.query('SELECT * FROM offerings WHERE uid_offering = ?', [currentChoice[0].uid_offering], function(err, choice) {
									if(!err) {
										// Checks to see if it is past the cutoff time for the students to choose
										if (cutOff) {
											// Renders the page only with the user's current choice
											res.render('student.html', {Student:student.firstname, Choice:choice[0].name, Description:"The time for choosing has passed. At 2:45, head to " + choice[0].location + "!", oppTime:true, notExcluded:true}); //Change Constant Time
										} else {
											// Gets all offerings for the user to choose from
											module.exports.getAvailableOfferings(uid_day, function(offerings) {
												// At last, renders the page with the current choice, and the choices table
												res.render('student.html', {Student:student.firstname, Choice:choice[0].name, Description:"You've already chosen, but if you'd like to change your choice, choose a different offering!", uid_day:uid_day, data:offerings, cutOffStudent:settings["hours_close_student"].value_int, notExcluded:true});
											});
										}
									} else {
										res.render('error.html', {err:err});
									}
								});
							}
						} else {
							// Renders the page without any choices, since the student is excluded
							res.render('student.html', {Student:student.firstname, Choice:"No Choice Required", Description:"Due to a sport or perhaps some other commitment, you will not participate in Oppblock today. Press Override if this doesn't apply to you.", uid_day:uid_day, oppTime:true});
						}
					} else {
						console.log("An error done occured.");
						res.render('error.html', {err:err});
					}
				});
			}
		});
	});

	app.post('/student', middleware.isStudent, function(req, response) {
		// Gets Student's id from middleware
		var uid_student = req.user.local[0].uid_student;

		// checks if post request comes from an override or a choice
		if(req.body.choice != undefined) {
			// Updates Choice
			con.query('UPDATE choices SET uid_offering = ? WHERE uid_student = ? AND uid_day = ?', [req.body.choice, uid_student, req.body.uid_day], function(err, results) {
				response.redirect('/student');
				response.end();
			});
		} else {
			// Overrides excluded group by adding student into choice table
			con.query('INSERT INTO choices (uid_offering, uid_student, uid_day) values (?, ?, ?)', [null, uid_student, req.body.uid_day], function(err) {
				if(!err) {
					response.redirect('/student');
					response.end();
				} else {
					res.send(err);
					res.render('error.html', {err:err});
				}
			});
		}
	});
	return this;
 }
}
<|endoftext|>


<commit_before>define(["js/views/baseview", "jquery", "js/views/edit_textbook", "js/views/show_textbook"],
        function(BaseView, $, EditTextbookView, ShowTextbookView) {
    var ListTextbooks = BaseView.extend({
        initialize: function() {
            this.emptyTemplate = this.loadTemplate('no-textbooks');
            this.listenTo(this.collection, 'all', this.render);
            this.listenTo(this.collection, 'destroy', this.handleDestroy);
        },
        tagName: "div",
        className: "textbooks-list",
        render: function() {
            var textbooks = this.collection;
            if(textbooks.length === 0) {
                this.$el.html(this.emptyTemplate());
            } else {
                this.$el.empty();
                var that = this;
                textbooks.each(function(textbook) {
                    var view;
                    if (textbook.get("editing")) {
                        view = new EditTextbookView({model: textbook});
                    } else {
                        view = new ShowTextbookView({model: textbook});
                    }
                    that.$el.append(view.render().el);
                });
            }
            return this;
        },
        events: {
            "click .new-button": "addOne"
        },
        addOne: function(e) {
            if(e && e.preventDefault) { e.preventDefault(); }
            this.collection.add([{editing: true}]);
        },
        handleDestroy: function(model, collection, options) {
            collection.remove(model);
        }
    });
    return ListTextbooks;
});
<commit_msg>scroll to newly created section and make first input element active on creation of a new textbook in the textbooks list page (fix for https://openedx.atlassian.net/browse/TNL-130)<commit_after>define(["js/views/baseview", "jquery", "js/views/edit_textbook", "js/views/show_textbook", "js/views/utils/view_utils"],
        function(BaseView, $, EditTextbookView, ShowTextbookView, ViewUtils) {
    var ListTextbooks = BaseView.extend({
        initialize: function() {
            this.emptyTemplate = this.loadTemplate('no-textbooks');
            this.listenTo(this.collection, 'all', this.render);
            this.listenTo(this.collection, 'destroy', this.handleDestroy);
        },
        tagName: "div",
        className: "textbooks-list",
        render: function() {
            var textbooks = this.collection;
            if(textbooks.length === 0) {
                this.$el.html(this.emptyTemplate());
            } else {
                this.$el.empty();
                var that = this;
                textbooks.each(function(textbook) {
                    var view;
                    if (textbook.get("editing")) {
                        view = new EditTextbookView({model: textbook});
                    } else {
                        view = new ShowTextbookView({model: textbook});
                    }
                    that.$el.append(view.render().el);
                });
            }
            return this;
        },
        events: {
            "click .new-button": "addOne"
        },
        addOne: function(e) {
            if(e && e.preventDefault) { e.preventDefault(); }
            this.collection.add([{editing: true}]); // (render() call triggered here)
            // find the outer 'section' tag for the newly added textbook
            $sectionEl = this.$el.find('section:last');
            // scroll to put this at top of viewport
            ViewUtils.setScrollOffset($sectionEl, 0);
            // find the first input element in this section
            $inputEl = $sectionEl.find('input:first');
            // activate the text box (so user can go ahead and start typing straight away)
            $inputEl.focus().select();
        },
        handleDestroy: function(model, collection, options) {
            collection.remove(model);
        }
    });
    return ListTextbooks;
});
<|endoftext|>


<commit_before>"use strict";
let fetch = require('../../helpers/fetch-json');
let Promise = require('bluebird');
let _ = require("lodash");
let levenshteinStringDistance = require("levenshtein-string-distance");
let writeFile = Promise.promisify(require('fs').writeFile);
const NAME = "public-api.nazk.gov.ua";
const input = require("./../../input/index");
const output = require("./../../output/index");
const inJudgeModel = require("./../../input/judge.json");
const outJudgeModel = require("./../../output/judge.json");
const homonymsBlacklist = {
    melnik_oleksandr_mihaylovich_novomoskovskiy_miskrayonniy_dnipropetrovskoyi_oblasti: [
        "0a80428c-5247-4b1f-99c9-aa0e8c4b958c",
        "388949a9-a197-4b99-b0fd-fbb6422d8b71"
    ],
    melnik_oleksandr_mihaylovich_mikolayivskiy_okruzhniy_administrativniy_sud: [
        "0a80428c-5247-4b1f-99c9-aa0e8c4b958c",
        "50f8fe46-6cf8-4331-9c92-5be7d39d65c6"
    ],
    tkachenko_oleg_mikolayovich: ["efb72a4d-2f8a-4265-8341-29763365f515"],
    mikulyak_pavlo_pavlovich_zakarpatskiy_okruzhniy_administrativniy_sud: [],
    mikulyak_pavlo_pavlovich_uzhgorodskiy_miskrayonniy_sud_zakarpatskoyi_oblasti: ["80acf347-3f54-4364-92c7-e434e204b949"],
    savchenko_sergiy_ivanovich: [
        "1928fde2-67d6-4bdf-b7c2-2de8f5b0edf1"
    ],
    shevchenko_oleksandr_volodimirovich: [
        "a8a23353-206a-45ce-8cee-633478323a5e",
        "153c9c19-cba7-42b4-87bc-a09c5e35d485",
        "c8d22a43-6eca-4aad-b557-f598158fc077",
        "46842bc6-491a-43b4-8a0a-d51c81fd7df2"
    ],
    vasilieva_lyubov_mikolayivna: [
        "53bd9d55-c537-4c4a-a8fa-d7b15c5df2e3"
    ],
    dyachuk_vasil_mikolayovich: ["9b83d351-f2a5-4c32-a5cc-304bde8f7ccd"]
};
function stringifyParse(object) {
    return JSON.parse(JSON.stringify(object));
}
function getSearchLink(s) {
    s = encodeURI(s);
    return `https://public-api.nazk.gov.ua/v1/declaration/?q=${s}`;
}
function getDeclarationLink(id) {
    return `https://public-api.nazk.gov.ua/v1/declaration/${id}`;
}

module.exports = function searchDeclaration(judge) {

    return fetch(getSearchLink(judge[inJudgeModel.name]))
    // return Promise.resolve(stringifyParse(require("./declarations-pointers-exapmle.json")))
        .then(response => {
            return _.chain(_.get(response, "items"))
                .filter(declarationPointer => {
                    const given = _.lowerCase(judge[inJudgeModel.name]);
                    const fetched = _.lowerCase(declarationPointer.lastname + " " + declarationPointer.firstname);
                    return levenshteinStringDistance(given, fetched) <= 3;
                })
                .filter(function (declarationPointer, index, declarations) {
                    return !_.includes(homonymsBlacklist[judge.key], declarationPointer.id);
                })
                .value();
        })
        .then(declarationPointers => {
            return Promise.map(declarationPointers, function (declarationPointer) {
                return fetch(getDeclarationLink(declarationPointer.id))
                // return Promise.resolve(stringifyParse(require("./declaration-exapmle.json")))
                    .then(serverResponse => {
                        serverResponse.data.id = serverResponse.id;
                        return writeFile(`../edeclarations/${serverResponse.id}.json`, JSON.stringify(serverResponse.data))
                            .then(() => {
                                return serverResponse.data;
                            });
                    })
            });
        })
        .then(declarations => {
            // declarations = filter declarationType ===1
            return _.sortBy(declarations, declaration => -parseInt(_.get(declaration, "step_0.declarationYear1"), 10));
        })
        .then(declarations => {
            return _.map(declarations, declaration => {
                return {
                    provider: NAME,
                    document: declaration
                };
            });
        })
        .catch(function (e) {
            throw new Error(e.message);
        })
};

//
// searchDeclaration({
//     Name: "Аліна Сніжана Степанівна",
//     key: "_abba",
// });
<commit_msg>extend homonymsBlacklist Кучеренко Оксана іванівна<commit_after>"use strict";
let fetch = require('../../helpers/fetch-json');
let Promise = require('bluebird');
let _ = require("lodash");
let levenshteinStringDistance = require("levenshtein-string-distance");
let writeFile = Promise.promisify(require('fs').writeFile);
const NAME = "public-api.nazk.gov.ua";
const input = require("./../../input/index");
const output = require("./../../output/index");
const inJudgeModel = require("./../../input/judge.json");
const outJudgeModel = require("./../../output/judge.json");
const homonymsBlacklist = {
    melnik_oleksandr_mihaylovich_novomoskovskiy_miskrayonniy_dnipropetrovskoyi_oblasti: [
        "0a80428c-5247-4b1f-99c9-aa0e8c4b958c",
        "388949a9-a197-4b99-b0fd-fbb6422d8b71"
    ],
    melnik_oleksandr_mihaylovich_mikolayivskiy_okruzhniy_administrativniy_sud: [
        "0a80428c-5247-4b1f-99c9-aa0e8c4b958c",
        "50f8fe46-6cf8-4331-9c92-5be7d39d65c6"
    ],
    tkachenko_oleg_mikolayovich: ["efb72a4d-2f8a-4265-8341-29763365f515"],
    mikulyak_pavlo_pavlovich_zakarpatskiy_okruzhniy_administrativniy_sud: [],
    mikulyak_pavlo_pavlovich_uzhgorodskiy_miskrayonniy_sud_zakarpatskoyi_oblasti: ["80acf347-3f54-4364-92c7-e434e204b949"],
    savchenko_sergiy_ivanovich: [
        "1928fde2-67d6-4bdf-b7c2-2de8f5b0edf1"
    ],
    kucherenko_oksana_ivanivna: [
        "8cf77cc7-bd0f-49f7-a885-a2213625166a"
    ],
    shevchenko_oleksandr_volodimirovich: [
        "a8a23353-206a-45ce-8cee-633478323a5e",
        "153c9c19-cba7-42b4-87bc-a09c5e35d485",
        "c8d22a43-6eca-4aad-b557-f598158fc077",
        "46842bc6-491a-43b4-8a0a-d51c81fd7df2"
    ],
    vasilieva_lyubov_mikolayivna: [
        "53bd9d55-c537-4c4a-a8fa-d7b15c5df2e3"
    ],
    dyachuk_vasil_mikolayovich: ["9b83d351-f2a5-4c32-a5cc-304bde8f7ccd"]
};
function stringifyParse(object) {
    return JSON.parse(JSON.stringify(object));
}
function getSearchLink(s) {
    s = encodeURI(s);
    return `https://public-api.nazk.gov.ua/v1/declaration/?q=${s}`;
}
function getDeclarationLink(id) {
    return `https://public-api.nazk.gov.ua/v1/declaration/${id}`;
}

module.exports = function searchDeclaration(judge) {

    return fetch(getSearchLink(judge[inJudgeModel.name]))
    // return Promise.resolve(stringifyParse(require("./declarations-pointers-exapmle.json")))
        .then(response => {
            return _.chain(_.get(response, "items"))
                .filter(declarationPointer => {
                    const given = _.lowerCase(judge[inJudgeModel.name]);
                    const fetched = _.lowerCase(declarationPointer.lastname + " " + declarationPointer.firstname);
                    return levenshteinStringDistance(given, fetched) <= 3;
                })
                .filter(function (declarationPointer, index, declarations) {
                    return !_.includes(homonymsBlacklist[judge.key], declarationPointer.id);
                })
                .value();
        })
        .then(declarationPointers => {
            return Promise.map(declarationPointers, function (declarationPointer) {
                return fetch(getDeclarationLink(declarationPointer.id))
                // return Promise.resolve(stringifyParse(require("./declaration-exapmle.json")))
                    .then(serverResponse => {
                        serverResponse.data.id = serverResponse.id;
                        return writeFile(`../edeclarations/${serverResponse.id}.json`, JSON.stringify(serverResponse.data))
                            .then(() => {
                                return serverResponse.data;
                            });
                    })
            });
        })
        .then(declarations => {
            // declarations = filter declarationType ===1
            return _.sortBy(declarations, declaration => -parseInt(_.get(declaration, "step_0.declarationYear1"), 10));
        })
        .then(declarations => {
            return _.map(declarations, declaration => {
                return {
                    provider: NAME,
                    document: declaration
                };
            });
        })
        .catch(function (e) {
            throw new Error(e.message);
        })
};

//
// searchDeclaration({
//     Name: "Аліна Сніжана Степанівна",
//     key: "_abba",
// });
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2000-2018 TeamDev Ltd. All rights reserved.
 * TeamDev PROPRIETARY and CONFIDENTIAL.
 * Use is subject to license terms.
 */

"use strict";

/**
 * The client of the License Server backend.
 *
 * Orchestrates the work of the HTTP and Firebase clients and
 * the {@link ActorRequestFactory}.
 */
export class BackendClient {

  /**
   * Creates a new BackendClient.
   *
   * @param httpClient          the {@link HttpClient} to connect to
   *                            the backend with
   * @param firebaseClient      the {@link FirebaseClient} to read the query
   *                            results with
   * @param actorRequestFactory the {@link ActorRequestFactory} to instantiate
   *                            the actor requests with
   */
  constructor(httpClient, firebaseClient, actorRequestFactory) {
    this._httpClient = httpClient;
    this._firebase = firebaseClient;
    this._actorRequestFactory = actorRequestFactory;
  }

  /**
   * Fetches all the entities of the given type.
   *
   * @param type          the target {@link TypeUrl}
   * @param dataCallback  the callback which receives the data, one-by-one, in
   *                      a form of a JS object
   * @param errorCallback the callback which receives the errors
   */
  fetchAll(type, dataCallback, errorCallback = null) {
    let query = this._actorRequestFactory.queryAll(type);
    this._fetch(query, dataCallback, errorCallback);
  }

  /**
   * Fetches a single entity of the given type.
   *
   * @param type          the target {@link TypeUrl}
   * @param id            the target entity ID, as a {@link TypedMessage}
   * @param dataCallback  the callback which receives the single data item, in
   *                      a form of a JS object
   * @param errorCallback the callback which receives the errors
   */
  fetchById(type, id, dataCallback, errorCallback = null) {
    let query = this._actorRequestFactory.queryById(type.value, id);
    this._fetch(query, dataCallback, errorCallback);
  }

  /**
   * Sends the given command to the server.
   *
   * @param commandMessage    the {@link TypedMessage} representing the command
   *                          message
   * @param successListener   the no-argument callback invoked if the command
   *                          is acknowledged
   * @param errorCallback     the callback which receives the errors
   * @param rejectionCallback the callback which receives the command rejections
   */
  sendCommand(commandMessage,
              successListener,
              errorCallback,
              rejectionCallback) {
    let command = this._actorRequestFactory.command(commandMessage);
    this._httpClient.postMessage("/command", command).then(
        response => {
          response.json().then(ack => {
            let status = ack.status;
            if (status.hasOwnProperty("ok")) {
              successListener();
            } else if (status.hasOwnProperty("error")) {
              errorCallback(status.error)
            } else if (status.hasOwnProperty("rejection")) {
              console.log("Command rejected.");
              rejectionCallback(status.rejection);
            }
          }, errorCallback);
        }, errorCallback
    );
  }

  _fetch(query, dataCallback, errorCallback=null) {
    let onError = errorCallback || function (e) {};
    this._httpClient.postMessage("/query", query)
        .then(
            response => response.text().then(
                path => this._firebase.subscribeTo(path, dataCallback),
                onError),
            onError);
  }
}
<commit_msg>Remove references to the `License Server` app.<commit_after>/*
 * Copyright (c) 2000-2018 TeamDev Ltd. All rights reserved.
 * TeamDev PROPRIETARY and CONFIDENTIAL.
 * Use is subject to license terms.
 */

"use strict";

/**
 * The client of the application backend.
 *
 * Orchestrates the work of the HTTP and Firebase clients and
 * the {@link ActorRequestFactory}.
 */
export class BackendClient {

  /**
   * Creates a new BackendClient.
   *
   * @param httpClient          the {@link HttpClient} to connect to
   *                            the backend with
   * @param firebaseClient      the {@link FirebaseClient} to read the query
   *                            results with
   * @param actorRequestFactory the {@link ActorRequestFactory} to instantiate
   *                            the actor requests with
   */
  constructor(httpClient, firebaseClient, actorRequestFactory) {
    this._httpClient = httpClient;
    this._firebase = firebaseClient;
    this._actorRequestFactory = actorRequestFactory;
  }

  /**
   * Fetches all the entities of the given type.
   *
   * @param type          the target {@link TypeUrl}
   * @param dataCallback  the callback which receives the data, one-by-one, in
   *                      a form of a JS object
   * @param errorCallback the callback which receives the errors
   */
  fetchAll(type, dataCallback, errorCallback = null) {
    let query = this._actorRequestFactory.queryAll(type);
    this._fetch(query, dataCallback, errorCallback);
  }

  /**
   * Fetches a single entity of the given type.
   *
   * @param type          the target {@link TypeUrl}
   * @param id            the target entity ID, as a {@link TypedMessage}
   * @param dataCallback  the callback which receives the single data item, in
   *                      a form of a JS object
   * @param errorCallback the callback which receives the errors
   */
  fetchById(type, id, dataCallback, errorCallback = null) {
    let query = this._actorRequestFactory.queryById(type.value, id);
    this._fetch(query, dataCallback, errorCallback);
  }

  /**
   * Sends the given command to the server.
   *
   * @param commandMessage    the {@link TypedMessage} representing the command
   *                          message
   * @param successListener   the no-argument callback invoked if the command
   *                          is acknowledged
   * @param errorCallback     the callback which receives the errors
   * @param rejectionCallback the callback which receives the command rejections
   */
  sendCommand(commandMessage,
              successListener,
              errorCallback,
              rejectionCallback) {
    let command = this._actorRequestFactory.command(commandMessage);
    this._httpClient.postMessage("/command", command).then(
        response => {
          response.json().then(ack => {
            let status = ack.status;
            if (status.hasOwnProperty("ok")) {
              successListener();
            } else if (status.hasOwnProperty("error")) {
              errorCallback(status.error)
            } else if (status.hasOwnProperty("rejection")) {
              console.log("Command rejected.");
              rejectionCallback(status.rejection);
            }
          }, errorCallback);
        }, errorCallback
    );
  }

  _fetch(query, dataCallback, errorCallback=null) {
    let onError = errorCallback || function (e) {};
    this._httpClient.postMessage("/query", query)
        .then(
            response => response.text().then(
                path => this._firebase.subscribeTo(path, dataCallback),
                onError),
            onError);
  }
}
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name loopbackApp.controller:AppCtrl
 * @description
 * # MainCtrl
 * Controller of the loopbackApp
 */
angular.module('loopbackApp')
  .config(function($stateProvider) {
    $stateProvider
    .state('app', {
      abstract: true,
      url: '/app',
      templateUrl: 'views/app.html',
      controller: 'MainCtrl'
    });
  })
  .controller('MainCtrl', function ($scope, $state, $location, $notification, AppAuth, User) {

    AppAuth.ensureHasCurrentUser(User);
    $scope.currentUser = AppAuth.currentUser;

    $scope.appName = 'Loopback + AngularJS + Bootstrap';

    $scope.menuoptions = [{
      name: 'Home',
      sref: '.home'
    } , {
      name: 'Items',
      sref: '.items.list'
    // } , {
    //   name: 'Users',
    //   sref: '.users'
    }];

    $scope.toplinks = [{
      name: 'Logout',
      action: function() {
        User.logout(function() {
          $scope.currentUser = AppAuth.currentUser = null;
          $state.go('login');
          $notification.info('Logged out', 'You are logged out!');
        });
      }
    }];

  });
<commit_msg>Fixes links in Sandbox<commit_after>'use strict';

/**
 * @ngdoc function
 * @name loopbackApp.controller:AppCtrl
 * @description
 * # MainCtrl
 * Controller of the loopbackApp
 */
angular.module('loopbackApp')
  .config(function($stateProvider) {
    $stateProvider
    .state('app', {
      abstract: true,
      url: '/app',
      templateUrl: 'views/app.html',
      controller: 'MainCtrl'
    });
  })
  .controller('MainCtrl', function ($scope, $state, $location, $notification, AppAuth, User) {

    AppAuth.ensureHasCurrentUser(User);
    $scope.currentUser = AppAuth.currentUser;

    $scope.appName = 'Loopback + AngularJS + Bootstrap';

    $scope.menuoptions = [{
      name: 'Home',
      sref: 'app.home'
    } , {
      name: 'Items',
      sref: 'app.items.list'
    }];

    $scope.toplinks = [{
      name: 'Logout',
      action: function() {
        User.logout(function() {
          $scope.currentUser = AppAuth.currentUser = null;
          $state.go('login');
          $notification.info('Logged out', 'You are logged out!');
        });
      }
    }];

  });
<|endoftext|>


<commit_before>import { default as express } from 'express';
import { default as dataStore } from '../dataStore/dataStore.js';

// TODO: Configurable port
const port = 3001;

// TODO: Configure from a config file or env (should match crawler config)

dataStore.configure({
  backendName: 'redis',
  backendOpts: {
    url: 'redis://localhost:6379',
  },
}).then(() => {
  const app = express();
  app.get('/get-data', getData);
  app.use(express.static('dist/dashboard/static'));
  app.listen(port, () => {
    console.log(`Dashboard running on port ${port}`);
  });
});

function getData(req, res) {
  console.log(`getData, req=${req}`);
  const data = dataStore.getHistoricalURLData('google.com', 10);
  res.send(JSON.stringify(data));
}
<commit_msg>Correctly use dataStore.getHistoricalURLData<commit_after>import { default as express } from 'express';
import { default as dataStore } from '../dataStore/dataStore.js';

// TODO: Configurable port
const port = 3001;

// TODO: Configure from a config file or env (should match crawler config)

dataStore.configure({
  backendName: 'redis',
  backendOpts: {
    url: 'redis://localhost:6379',
  },
}).then(() => {
  const app = express();
  app.get('/get-data', getData);
  app.use(express.static('dist/dashboard/static'));
  app.listen(port, () => {
    console.log(`Dashboard running on port ${port}`);
  });
});

function getData(req, res) {
  console.log(`getData, req=${req}`);
  dataStore.getHistoricalURLData('google.com', 10).then((data) => {
    res.send(JSON.stringify(data));
  });
}
<|endoftext|>


<commit_before>$(document).ready(function() {
    'use strict';

  // define variables for code legibility
    var dropdownMenuToggle = $('.dropdown');
    var dropdownMenu = $('.dropdown-menu');
    var menuItems = dropdownMenu.find('a');

  // bind menu toggle click for later use
    dropdownMenuToggle.toggle(function() {
        dropdownMenu.addClass('expanded').find('a').first().focus();
        dropdownMenuToggle.addClass('active').attr('aria-expanded', 'true');
    }, function() {
        dropdownMenu.removeClass('expanded');
        dropdownMenuToggle.removeClass('active').attr('aria-expanded', 'false').focus();
    });

  // catch keypresses when focused on dropdownMenuToggle (we only care about spacebar keypresses here)
    dropdownMenuToggle.on('keydown', function(event) {
    // if space key pressed
        if (event.which == 32) {
            dropdownMenuToggle.click();
            event.preventDefault();
        }
    });

  // catch keypresses when inside dropdownMenu (we want to catch spacebar; escape; up arrow or shift+tab; and down arrow or tab)
    dropdownMenu.on('keydown', function(event) {
        catchKeyPress($(this), event);
    });

    function catchKeyPress(object, event) {
    // get currently focused item
        var focusedItem = jQuery(':focus');

    // get the number of focusable items
        var numberOfMenuItems = menuItems.length;

    // get the index of the currently focused item
        var focusedItemIndex = menuItems.index(focusedItem);

    // var to store next focused item index
        var itemToFocusIndex;

    // if space key pressed
        if (event.which == 32) {
            dropdownMenuToggle.click();
            event.preventDefault();
        }

    // if escape key pressed
        if (event.which == 27) {
            dropdownMenuToggle.click();
            event.preventDefault();
        }

        // if up arrow key pressed or shift+tab else down key or tab is pressed
        if (event.which === 38 || (event.which === 9 && event.shiftKey)) {
            // if first item go to last
            if (focusedItemIndex === 0) {
                menuItems.last().focus();
            } else {
                itemToFocusIndex = focusedItemIndex - 1;
                menuItems.get(itemToFocusIndex).focus();
            }
            event.preventDefault();
        } else if (event.which === 40 || event.which === 9) {
            // if last item go to first
            if (focusedItemIndex === numberOfMenuItems - 1) {
                menuItems.first().focus();
            } else {
                itemToFocusIndex = focusedItemIndex + 1;
                menuItems.get(itemToFocusIndex).focus();
            }
            event.preventDefault();
        }
    }
});
<commit_msg>Added keycodes dict for readability<commit_after>$(document).ready(function() {
    'use strict';

    // define variables for code legibility
    var dropdownMenuToggle = $('.dropdown');
    var dropdownMenu = $('.dropdown-menu');
    var menuItems = dropdownMenu.find('a');

    var keyCodes = {
        leftArrow: 37,
        upArrow: 38,
        rightArrow: 39,
        downArrow: 40,
        tab: 9,
        escape: 27,
        space: 32
    };

    // bind menu toggle click for later use
    dropdownMenuToggle.toggle(function() {
        dropdownMenu.addClass('expanded').find('a').first().focus();
        dropdownMenuToggle.addClass('active').attr('aria-expanded', 'true');
    }, function() {
        dropdownMenu.removeClass('expanded');
        dropdownMenuToggle.removeClass('active').attr('aria-expanded', 'false').focus();
    });

    // catch keypresses when focused on dropdownMenuToggle (we only care about spacebar keypresses here)
    dropdownMenuToggle.on('keydown', function(event) {
        // if space key pressed
        if (event.which === keyCodes.space) {
            dropdownMenuToggle.click();
            event.preventDefault();
        }
    });

    // catch keypresses when inside dropdownMenu
    // (we want to catch spacebar; escape; up arrow or shift+tab; and down arrow or tab)
    dropdownMenu.on('keydown', function(event) {
        catchKeyPress($(this), event);
    });

    function catchKeyPress(object, event) {
        // get currently focused item
        var focusedItem = jQuery(':focus');

        // get the number of focusable items
        var numberOfMenuItems = menuItems.length;

        // get the index of the currently focused item
        var focusedItemIndex = menuItems.index(focusedItem);

        // var to store next focused item index
        var itemToFocusIndex;

        // if space key pressed
        if (event.which === keyCodes.space) {
            dropdownMenuToggle.click();
            event.preventDefault();
        }

        // if escape key pressed
        if (event.which === keyCodes.escape) {
            dropdownMenuToggle.click();
            event.preventDefault();
        }

        // if up arrow key pressed or shift+tab else down key or tab is pressed
        if (event.which === keyCodes.upArrow || event.which === keyCodes.leftArrow ||
            (event.which === keyCodes.tab && event.shiftKey)) {
            // if first item go to last
            if (focusedItemIndex === 0) {
                menuItems.last().focus();
            } else {
                itemToFocusIndex = focusedItemIndex - 1;
                menuItems.get(itemToFocusIndex).focus();
            }
            event.preventDefault();
        } else if (event.which === keyCodes.downArrow || event.which === keyCodes.rightArrow ||
            event.which === keyCodes.tab) {
            // if last item go to first
            if (focusedItemIndex === numberOfMenuItems - 1) {
                menuItems.first().focus();
            } else {
                itemToFocusIndex = focusedItemIndex + 1;
                menuItems.get(itemToFocusIndex).focus();
            }
            event.preventDefault();
        }
    }
});
<|endoftext|>


<commit_before><commit_msg>Future proofing magazine view.<commit_after><|endoftext|>


<commit_before><commit_msg>Categories weren't working due to GET/POST.<commit_after><|endoftext|>


<commit_before>const DrawCard = require('../../../drawcard.js');

class SerDavosSeaworth extends DrawCard {
    setupCardAbilities() {
        this.interrupt({
            when: {
                onCharacterKilled: (event, player, card) => card === this
            },
            handler: () => {
                this.game.addMessage('{0} uses {1} to return {1} to their hand instead of their dead pile', this.controller, this, this);
                this.controller.moveCard(this, 'hand');
            }
        });
    }
}

SerDavosSeaworth.code = '01050';

module.exports = SerDavosSeaworth;
<commit_msg>Fix Davos Seaworth's interrupt. (#285)<commit_after>const DrawCard = require('../../../drawcard.js');

class SerDavosSeaworth extends DrawCard {
    setupCardAbilities() {
        this.interrupt({
            when: {
                onCharacterKilled: (event, player, card) => card === this
            },
            handler: (context) => {
                context.skipHandler();
                this.game.addMessage('{0} uses {1} to return {1} to their hand instead of their dead pile', this.controller, this, this);
                this.controller.moveCard(this, 'hand');
            }
        });
    }
}

SerDavosSeaworth.code = '01050';

module.exports = SerDavosSeaworth;
<|endoftext|>


<commit_before>/* @flow */

import React, {Component} from 'react'
import {globalStyles, globalColors} from '../../../styles/style-guide'
import {Text, Button, Checkbox, Icon} from '../../../common-adapters'
import {specialStyles as textStyles} from '../../../common-adapters/text'
import Container from '../../forms/container'

import type {Props} from './index.render'

export default class Render extends Component {
  props: Props;

  state: {
    inWallet: boolean
  };

  constructor (props: Props) {
    super(props)
    this.state = {inWallet: false}
  }

  render () {
    return (
      <Container onBack={this.props.onBack} style={styles.container}>
        <Text type='Header' style={styles.header}>Congratulations, you’ve just joined Keybase!</Text>
        <Text type='Body' style={styles.body}>Here is your unique paper key, it will allow you to perform important Keybase tasks in the future. This is the only time you’ll see this so be sure to write it down.</Text>

          <Text type='Body' style={styles.paperkey}>{this.props.paperkey.stringValue()}</Text>
          <Icon type='paper-key-corner' style={styles.paperCorner}/>

        <Checkbox style={styles.check} label='Yes, I wrote this down.' checked={this.state.inWallet} onCheck={inWallet => this.setState({inWallet})} />
        <Button style={styles.button} type='Primary' label='Done' onClick={() => this.props.onFinish()} disabled={!this.state.inWallet} />
      </Container>
    )
  }
}

const styles = {
  container: {
    ...globalStyles.flexBoxColumn,
    alignItems: 'center',
    marginTop: 15
  },
  header: {
    marginTop: 22,
    marginBottom: 5
  },
  body: {
    paddingLeft: 15,
    paddingRight: 15,
    marginBottom: 35,
    textAlign: 'center'
  },
  paperKeyContainer: {
    position: 'relative',
    width: 400,
    marginBottom: 35,
    paddingTop: 12,
    paddingLeft: 30,
    paddingRight: 45,
    borderRadius: 1,
    backgroundColor: globalColors.white,
    border: `solid 4px ${globalColors.darkBlue}`
  },
  paperCorner: {
    position: 'absolute',
    top: -4,
    right: -4
  },
  check: {
    marginBottom: 60
  },
  button: {
    alignSelf: 'flex-end'
  },
  paperkey: {
    ...textStyles.paperKey,
    ...globalStyles.selectable,
    marginBottom: 15,
    display: 'inline-block'
  }
}
<commit_msg>Remove back button on success screen, doesn't make sense<commit_after>/* @flow */

import React, {Component} from 'react'
import {globalStyles, globalColors} from '../../../styles/style-guide'
import {Text, Button, Checkbox, Icon} from '../../../common-adapters'
import {specialStyles as textStyles} from '../../../common-adapters/text'
import Container from '../../forms/container'

import type {Props} from './index.render'

export default class Render extends Component {
  props: Props;

  state: {
    inWallet: boolean
  };

  constructor (props: Props) {
    super(props)
    this.state = {inWallet: false}
  }

  render () {
    return (

        <Text type='Header' style={styles.header}>Congratulations, you’ve just joined Keybase!</Text>
        <Text type='Body' style={styles.body}>Here is your unique paper key, it will allow you to perform important Keybase tasks in the future. This is the only time you’ll see this so be sure to write it down.</Text>

          <Text type='Body' style={styles.paperkey}>{this.props.paperkey.stringValue()}</Text>
          <Icon type='paper-key-corner' style={styles.paperCorner}/>

        <Checkbox style={styles.check} label='Yes, I wrote this down.' checked={this.state.inWallet} onCheck={inWallet => this.setState({inWallet})} />
        <Button style={styles.button} type='Primary' label='Done' onClick={() => this.props.onFinish()} disabled={!this.state.inWallet} />

    )
  }
}

const styles = {
  container: {
    ...globalStyles.flexBoxColumn,
    alignItems: 'center',
    padding: 60
  },
  header: {
    marginTop: 60,
    marginBottom: 5
  },
  body: {
    paddingLeft: 15,
    paddingRight: 15,
    marginBottom: 35,
    textAlign: 'center'
  },
  paperKeyContainer: {
    position: 'relative',
    width: 400,
    marginBottom: 35,
    paddingTop: 12,
    paddingLeft: 30,
    paddingRight: 45,
    borderRadius: 1,
    backgroundColor: globalColors.white,
    border: `solid 4px ${globalColors.darkBlue}`
  },
  paperCorner: {
    position: 'absolute',
    top: -4,
    right: -4
  },
  check: {
    marginBottom: 60
  },
  button: {
    alignSelf: 'flex-end'
  },
  paperkey: {
    ...textStyles.paperKey,
    ...globalStyles.selectable,
    marginBottom: 15,
    display: 'inline-block'
  }
}
<|endoftext|>


<commit_before>(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define([], factory);
  } else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
  } else {
    root.surpass = factory();
  }
}(this, function () {
  // http://www.isthe.com/chongo/tech/comp/fnv/index.html
  function fnv1a(bytes) {
    return bytes.reduce(function fnv1aCore(hash, byte) {
      hash ^= byte;
      // calculate hash * 16777619 in int32 high-end-overflow arithmetic,
      // instead of JS's native floating-point low-end-rounding arithmetic
      return hash + (hash << 1) + (hash << 4) +
        (hash << 7) + (hash << 8) + (hash << 24);
    }, 2166136261) >>> 0;
  }
  function utf8Bytes(str) {
    return unescape(encodeURIComponent(str)).split('').map(function (s) {
      return s.charCodeAt(0);
    });
  }
  function grossSimplify(str) {
    var hash = fnv1a(utf8Bytes(str));
    var high = hash >>> 16;
    var low = hash & 0xffff;
    return (high ^ low) % 144;
  }
  var colorSets = ['rgb', 'rbg', 'grb', 'gbr', 'brg', 'bgr'];
  var shapeSets = ['cts', 'cst', 'tcs', 'tsc', 'sct', 'stc'];
  var arrangements = ['t', 'r', 'b', 'l']; // clockwise from top aka CSS order
  function colorsShapesArrangement(grossIndex) {
    return {
      arrangement: arrangements[grossIndex % 4],
      colors: colorSets[(grossIndex / 4 | 0) % 6],
      shapes: shapeSets[grossIndex / 24 | 0]
    };
  }

  function namespaced(classNames) {
    if (!Array.isArray(classNames)) {
      classNames = classNames.split(/\s+/g);
    }
    return classNames.map(function(className) {
      return 'surpass_' + className;
    }).join(' ');
  }

  function elementClassState(el, className) {
    return {
      enter: function addClass() {
        el.classList.add(className);
      },
      exit: function removeClass() {
        el.classList.remove(className);
      }
    };
  }

  var teSurpassContainer = document.createElement('div');
  teSurpassContainer.className = namespaced('container');

  var teInputContainer = document.createElement('div');
  teInputContainer.className = namespaced('input-container');
  // the actual input gets added to the container in construction
  var teInputShroud = document.createElement('div');
  teInputShroud.className = namespaced('input-shroud');

  var teSurpassButton = document.createElement('button');
  var teMaskModeButton = teSurpassButton.cloneNode(teSurpassButton);
  teMaskModeButton.className = namespaced('button mask-button');
  var teExposeModeButton = teSurpassButton.cloneNode(teSurpassButton);
  teExposeModeButton.className = namespaced('button expose-button');
  teExposeModeButton.textContent =
    decodeURIComponent('%F0%9F%91%81%EF%B8%8E'); // eye
  var teShroudModeButton = teSurpassButton.cloneNode(teSurpassButton);
  teShroudModeButton.className = namespaced('button shroud-button');
  teShroudModeButton.textContent =
    decodeURIComponent("%E2%97%BC%EF%B8%8F"); // black square

  var maskSpotClassPrefix = namespaced(
    'gross-simpl-spot gross-simpl-round gross-simpl-x');
  var maskDotShapeClassName = namespaced(
    'gross-simpl-shape gross-simpl-block gross-simpl-dot');

  var teGrossSimplSpot = document.createElement('div');
  var teGrossSimplShape = document.createElement('div');
  teGrossSimplShape.className = maskDotShapeClassName;
  teGrossSimplSpot.appendChild(teGrossSimplShape);

  return function surpass(baseInput, opts) {
    // TODO: verify that `baseInput` is, in fact, a (password) input
    opts = opts || {};

    var container = teSurpassContainer.cloneNode(false);
    var inputContainer = teInputContainer.cloneNode(false);
    container.appendChild(inputContainer);

    var inputShroud = teInputShroud.cloneNode(false);
    var shroudModeButton = teShroudModeButton.cloneNode(true);

    var shroudActiveState = elementClassState(
      inputShroud, namespaced("input-shroud-active"));
    var shroudInactiveState = elementClassState(
      inputShroud, namespaced("input-shroud-inactive"));
    var emptyShroudClass = namespaced("input-shroud-empty");

    var maskedTypingClassName = namespaced("mask-button-typing");

    var maskModeButton = teMaskModeButton.cloneNode(false);

    var spots = [];
    for (var i = 0; i < 3; i++) {
      spots[i] = teGrossSimplSpot.cloneNode(true);
      spots[i].className = maskSpotClassPrefix + i;
      maskModeButton.appendChild(spots[i]);
    }

    function setMaskButtonDots() {
      for (var i = 0; i < 3; i++) {
        spots[i].className = maskSpotClassPrefix + i;
        spots[i].children[0].className = maskDotShapeClassName;
      }
    }

    function updateGrossSimplification() {
      var value = baseInput.value;

      // don't display the empty-string simplification
      if (value == '') {
        return setMaskButtonDots();
      }

      var csa = colorsShapesArrangement(grossSimplify(value));
      var posPrefix = 'gross-simpl-' + csa.arrangement;

      for (var i = 0; i < 3; i++) {
        var spotEl = spots[i];
        var shapeEl = spotEl.children[0];
        var color = csa.colors[i];
        var shape = csa.shapes[i];
        spotEl.className = namespaced(['gross-simpl-spot', posPrefix + i,
          shape == 'c' ? 'gross-simpl-round' : 'gross-simpl-pointy']);
        shapeEl.className = namespaced(['gross-simpl-shape',
          shape == 't' ? 'gross-simpl-tri' : 'gross-simpl-block',
          'gross-simpl-' + color]);
      }
    }

    // TODO: make this configurable
    var typingTime = 1000;
    var grossSimplificationTimeout = null;
    function stopGrossSimplificationTimeout() {
      if (grossSimplificationTimeout) {
        maskModeButton.classList.remove(maskedTypingClassName);
        clearTimeout(grossSimplificationTimeout);
        grossSimplificationTimeout = null;
      }
    }
    function finishGrossSimplificationTimeout() {
      maskModeButton.classList.remove(maskedTypingClassName);
      grossSimplificationTimeout = null;
      updateGrossSimplification();
    }
    function bumpGrossSimplificationTimeout() {
      if (grossSimplificationTimeout) {
        clearTimeout(grossSimplificationTimeout);
      } else {
        setMaskButtonDots();
        maskModeButton.classList.add(maskedTypingClassName);
      }
      grossSimplificationTimeout = setTimeout(
        finishGrossSimplificationTimeout, typingTime);
    }

    function setBaseInputTypeState(type) {
      return {enter: function() {
        baseInput.type = type;
      }};
    }

    function updateShroudEmptiness() {
      if (baseInput.value == '') {
        inputShroud.classList.add(emptyShroudClass);
      } else {
        inputShroud.classList.remove(emptyShroudClass);
      }
    }

    var modeSubstates = {
      shroud: [
        shroudActiveState,
        setBaseInputTypeState('password'),
        {enter: setMaskButtonDots},
        {enter: stopGrossSimplificationTimeout},
        {enter: updateShroudEmptiness}
      ],
      mask: [
        shroudInactiveState,
        setBaseInputTypeState('password'),
        {enter: function () {
          if (mode == 'shroud' && baseInput.value != '') {
            bumpGrossSimplificationTimeout();
          }
        }}
      ],
      expose: [
        shroudInactiveState,
        setBaseInputTypeState('text'),
        {enter: stopGrossSimplificationTimeout},
        {enter: updateGrossSimplification}
      ]
    };
    var mode;
    function setMode(newMode) {
      var i;
      function runOrNop(f) {
        if (f) f();
      }
      if (mode) {
        var oldSubstates = modeSubstates[mode];
        for (i = 0; i < oldSubstates.length; i++) {
          runOrNop(oldSubstates[i].exit);
        }
      }
      var newSubstates = modeSubstates[newMode];
      for (i = 0; i < newSubstates.length; i++) {
        runOrNop(newSubstates[i].enter);
      }
      mode = newMode;
    }
    setMode(opts.mode ||
      baseInput.type == 'password' ? 'mask' : 'expose');

    inputShroud.addEventListener('click', function(evt) {
      baseInput.focus();
    });

    shroudModeButton.addEventListener('click', function(evt) {
      setMode(mode == 'shroud' ? 'mask' : 'shroud');
      baseInput.focus();
    });

    container.appendChild(shroudModeButton);

    maskModeButton.addEventListener('click', function(evt) {
      setMode('mask');
    });

    container.appendChild(maskModeButton);

    var exposeModeButton = teExposeModeButton.cloneNode(true);

    exposeModeButton.addEventListener('click', function(evt) {
      setMode(mode == 'expose' ? 'mask' : 'expose');
    });

    container.appendChild(exposeModeButton);

    baseInput.addEventListener('input', function(evt) {
      updateShroudEmptiness();
      if (mode == 'mask') {
        if (baseInput.value != '') {
          bumpGrossSimplificationTimeout();
        } else {
          stopGrossSimplificationTimeout();
          setMaskButtonDots();
        }
      } else if (mode == 'expose') {
        // update gross simplification without delay when exposing
        updateGrossSimplification();
      }
    });

    baseInput.addEventListener('focus', function(evt) {
      inputShroud.textContent = '|';
    });

    baseInput.addEventListener('blur', function(evt) {
      inputShroud.textContent = '';
    });

    baseInput.parentNode.replaceChild(container, baseInput);
    inputContainer.appendChild(baseInput);
    inputContainer.appendChild(inputShroud);
    return container;
  };
}));
<commit_msg>Cut (default) hint lead time to half a second<commit_after>(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define([], factory);
  } else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
  } else {
    root.surpass = factory();
  }
}(this, function () {
  // http://www.isthe.com/chongo/tech/comp/fnv/index.html
  function fnv1a(bytes) {
    return bytes.reduce(function fnv1aCore(hash, byte) {
      hash ^= byte;
      // calculate hash * 16777619 in int32 high-end-overflow arithmetic,
      // instead of JS's native floating-point low-end-rounding arithmetic
      return hash + (hash << 1) + (hash << 4) +
        (hash << 7) + (hash << 8) + (hash << 24);
    }, 2166136261) >>> 0;
  }
  function utf8Bytes(str) {
    return unescape(encodeURIComponent(str)).split('').map(function (s) {
      return s.charCodeAt(0);
    });
  }
  function grossSimplify(str) {
    var hash = fnv1a(utf8Bytes(str));
    var high = hash >>> 16;
    var low = hash & 0xffff;
    return (high ^ low) % 144;
  }
  var colorSets = ['rgb', 'rbg', 'grb', 'gbr', 'brg', 'bgr'];
  var shapeSets = ['cts', 'cst', 'tcs', 'tsc', 'sct', 'stc'];
  var arrangements = ['t', 'r', 'b', 'l']; // clockwise from top aka CSS order
  function colorsShapesArrangement(grossIndex) {
    return {
      arrangement: arrangements[grossIndex % 4],
      colors: colorSets[(grossIndex / 4 | 0) % 6],
      shapes: shapeSets[grossIndex / 24 | 0]
    };
  }

  function namespaced(classNames) {
    if (!Array.isArray(classNames)) {
      classNames = classNames.split(/\s+/g);
    }
    return classNames.map(function(className) {
      return 'surpass_' + className;
    }).join(' ');
  }

  function elementClassState(el, className) {
    return {
      enter: function addClass() {
        el.classList.add(className);
      },
      exit: function removeClass() {
        el.classList.remove(className);
      }
    };
  }

  var teSurpassContainer = document.createElement('div');
  teSurpassContainer.className = namespaced('container');

  var teInputContainer = document.createElement('div');
  teInputContainer.className = namespaced('input-container');
  // the actual input gets added to the container in construction
  var teInputShroud = document.createElement('div');
  teInputShroud.className = namespaced('input-shroud');

  var teSurpassButton = document.createElement('button');
  var teMaskModeButton = teSurpassButton.cloneNode(teSurpassButton);
  teMaskModeButton.className = namespaced('button mask-button');
  var teExposeModeButton = teSurpassButton.cloneNode(teSurpassButton);
  teExposeModeButton.className = namespaced('button expose-button');
  teExposeModeButton.textContent =
    decodeURIComponent('%F0%9F%91%81%EF%B8%8E'); // eye
  var teShroudModeButton = teSurpassButton.cloneNode(teSurpassButton);
  teShroudModeButton.className = namespaced('button shroud-button');
  teShroudModeButton.textContent =
    decodeURIComponent("%E2%97%BC%EF%B8%8F"); // black square

  var maskSpotClassPrefix = namespaced(
    'gross-simpl-spot gross-simpl-round gross-simpl-x');
  var maskDotShapeClassName = namespaced(
    'gross-simpl-shape gross-simpl-block gross-simpl-dot');

  var teGrossSimplSpot = document.createElement('div');
  var teGrossSimplShape = document.createElement('div');
  teGrossSimplShape.className = maskDotShapeClassName;
  teGrossSimplSpot.appendChild(teGrossSimplShape);

  return function surpass(baseInput, opts) {
    // TODO: verify that `baseInput` is, in fact, a (password) input
    opts = opts || {};

    var container = teSurpassContainer.cloneNode(false);
    var inputContainer = teInputContainer.cloneNode(false);
    container.appendChild(inputContainer);

    var inputShroud = teInputShroud.cloneNode(false);
    var shroudModeButton = teShroudModeButton.cloneNode(true);

    var shroudActiveState = elementClassState(
      inputShroud, namespaced("input-shroud-active"));
    var shroudInactiveState = elementClassState(
      inputShroud, namespaced("input-shroud-inactive"));
    var emptyShroudClass = namespaced("input-shroud-empty");

    var maskedTypingClassName = namespaced("mask-button-typing");

    var maskModeButton = teMaskModeButton.cloneNode(false);

    var spots = [];
    for (var i = 0; i < 3; i++) {
      spots[i] = teGrossSimplSpot.cloneNode(true);
      spots[i].className = maskSpotClassPrefix + i;
      maskModeButton.appendChild(spots[i]);
    }

    function setMaskButtonDots() {
      for (var i = 0; i < 3; i++) {
        spots[i].className = maskSpotClassPrefix + i;
        spots[i].children[0].className = maskDotShapeClassName;
      }
    }

    function updateGrossSimplification() {
      var value = baseInput.value;

      // don't display the empty-string simplification
      if (value == '') {
        return setMaskButtonDots();
      }

      var csa = colorsShapesArrangement(grossSimplify(value));
      var posPrefix = 'gross-simpl-' + csa.arrangement;

      for (var i = 0; i < 3; i++) {
        var spotEl = spots[i];
        var shapeEl = spotEl.children[0];
        var color = csa.colors[i];
        var shape = csa.shapes[i];
        spotEl.className = namespaced(['gross-simpl-spot', posPrefix + i,
          shape == 'c' ? 'gross-simpl-round' : 'gross-simpl-pointy']);
        shapeEl.className = namespaced(['gross-simpl-shape',
          shape == 't' ? 'gross-simpl-tri' : 'gross-simpl-block',
          'gross-simpl-' + color]);
      }
    }

    // TODO: make this configurable
    var typingTime = 500;
    var grossSimplificationTimeout = null;
    function stopGrossSimplificationTimeout() {
      if (grossSimplificationTimeout) {
        maskModeButton.classList.remove(maskedTypingClassName);
        clearTimeout(grossSimplificationTimeout);
        grossSimplificationTimeout = null;
      }
    }
    function finishGrossSimplificationTimeout() {
      maskModeButton.classList.remove(maskedTypingClassName);
      grossSimplificationTimeout = null;
      updateGrossSimplification();
    }
    function bumpGrossSimplificationTimeout() {
      if (grossSimplificationTimeout) {
        clearTimeout(grossSimplificationTimeout);
      } else {
        setMaskButtonDots();
        maskModeButton.classList.add(maskedTypingClassName);
      }
      grossSimplificationTimeout = setTimeout(
        finishGrossSimplificationTimeout, typingTime);
    }

    function setBaseInputTypeState(type) {
      return {enter: function() {
        baseInput.type = type;
      }};
    }

    function updateShroudEmptiness() {
      if (baseInput.value == '') {
        inputShroud.classList.add(emptyShroudClass);
      } else {
        inputShroud.classList.remove(emptyShroudClass);
      }
    }

    var modeSubstates = {
      shroud: [
        shroudActiveState,
        setBaseInputTypeState('password'),
        {enter: setMaskButtonDots},
        {enter: stopGrossSimplificationTimeout},
        {enter: updateShroudEmptiness}
      ],
      mask: [
        shroudInactiveState,
        setBaseInputTypeState('password'),
        {enter: function () {
          if (mode == 'shroud' && baseInput.value != '') {
            bumpGrossSimplificationTimeout();
          }
        }}
      ],
      expose: [
        shroudInactiveState,
        setBaseInputTypeState('text'),
        {enter: stopGrossSimplificationTimeout},
        {enter: updateGrossSimplification}
      ]
    };
    var mode;
    function setMode(newMode) {
      var i;
      function runOrNop(f) {
        if (f) f();
      }
      if (mode) {
        var oldSubstates = modeSubstates[mode];
        for (i = 0; i < oldSubstates.length; i++) {
          runOrNop(oldSubstates[i].exit);
        }
      }
      var newSubstates = modeSubstates[newMode];
      for (i = 0; i < newSubstates.length; i++) {
        runOrNop(newSubstates[i].enter);
      }
      mode = newMode;
    }
    setMode(opts.mode ||
      baseInput.type == 'password' ? 'mask' : 'expose');

    inputShroud.addEventListener('click', function(evt) {
      baseInput.focus();
    });

    shroudModeButton.addEventListener('click', function(evt) {
      setMode(mode == 'shroud' ? 'mask' : 'shroud');
      baseInput.focus();
    });

    container.appendChild(shroudModeButton);

    maskModeButton.addEventListener('click', function(evt) {
      setMode('mask');
    });

    container.appendChild(maskModeButton);

    var exposeModeButton = teExposeModeButton.cloneNode(true);

    exposeModeButton.addEventListener('click', function(evt) {
      setMode(mode == 'expose' ? 'mask' : 'expose');
    });

    container.appendChild(exposeModeButton);

    baseInput.addEventListener('input', function(evt) {
      updateShroudEmptiness();
      if (mode == 'mask') {
        if (baseInput.value != '') {
          bumpGrossSimplificationTimeout();
        } else {
          stopGrossSimplificationTimeout();
          setMaskButtonDots();
        }
      } else if (mode == 'expose') {
        // update gross simplification without delay when exposing
        updateGrossSimplification();
      }
    });

    baseInput.addEventListener('focus', function(evt) {
      inputShroud.textContent = '|';
    });

    baseInput.addEventListener('blur', function(evt) {
      inputShroud.textContent = '';
    });

    baseInput.parentNode.replaceChild(container, baseInput);
    inputContainer.appendChild(baseInput);
    inputContainer.appendChild(inputShroud);
    return container;
  };
}));
<|endoftext|>


<commit_before>/**
 * Worker serving as main web application
 * Serves web/API requests
 **/
const config = require('../config');
const utility = require('../util/utility');
const redis = require('../store/redis');
const db = require('../store/db');
const queries = require('../store/queries');
const api = require('../routes/api');
const request = require('request');
const compression = require('compression');
const session = require('cookie-session');
const moment = require('moment');
const express = require('express');
const passport = require('passport');
const SteamStrategy = require('passport-steam').Strategy;
const cors = require('cors');
const async = require('async');
const uuidV4 = require('uuid/v4');

const app = express();
const apiKey = config.STEAM_API_KEY.split(',')[0];
const host = config.ROOT_URL;
const sessOptions = {
  domain: config.COOKIE_DOMAIN,
  maxAge: 52 * 7 * 24 * 60 * 60 * 1000,
  secret: config.SESSION_SECRET,
};
// PASSPORT config
passport.serializeUser((user, done) => {
  done(null, user.account_id);
});
passport.deserializeUser((accountId, done) => {
  done(null, {
    account_id: accountId,
  });
});
passport.use(new SteamStrategy({
  returnURL: `${host}/return`,
  realm: host,
  apiKey,
}, (identifier, profile, cb) => {
  const player = profile._json;
  player.last_login = new Date();
  queries.insertPlayer(db, player, (err) => {
    if (err) {
      return cb(err);
    }
    return cb(err, player);
  });
}));
// Compression middleware
app.use(compression());
// Dota 2 images middleware (proxy to Dota 2 CDN to serve over https)
app.use('/apps', (req, res) => {
  res.header('Cache-Control', 'max-age=604800, public');
  request(`http://cdn.dota2.com/${req.originalUrl}`).pipe(res);
});
// Session/Passport middleware
app.use(session(sessOptions));
app.use(passport.initialize());
app.use(passport.session());
// Rate limiter middleware
app.use((req, res, cb) => {
  let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || '';
  ip = ip.replace(/^.*:/, '').split(',')[0];
  const key = `rate_limit:${ip}`;
  console.log('%s visit %s, ip %s', req.user ? req.user.account_id : 'anonymous', req.originalUrl, ip);
  redis.multi().incr(key).expireat(key, utility.getStartOfBlockMinutes(1, 1)).exec((err, resp) => {
    if (err) {
      console.log(err);
      return cb();
    }
    if (config.NODE_ENV === 'development') {
      console.log(resp);
    }
    if (resp[0] > 90 && config.NODE_ENV !== 'test') {
      return res.status(429).json({
        error: 'rate limit exceeded',
      });
    }
    return cb();
  });
});
// Telemetry middleware
app.use((req, res, cb) => {
  const timeStart = new Date();
  res.once('finish', () => {
    const timeEnd = new Date();
    const elapsed = timeEnd - timeStart;
    if (elapsed > 1000 || config.NODE_ENV === 'development') {
      console.log('[SLOWLOG] %s, %s', req.originalUrl, elapsed);
    }
    async.series({
      api_hit: (cb) => {
        if (req.originalUrl.indexOf('/api') === 0) {
          redis.zadd('api_hits', moment().format('X'), `${uuidV4()}:${req.originalUrl}`, cb);
        } else {
          cb();
        }
      },
      visitors: (cb) => {
        if (req.user && req.user.account_id) {
          redis.zadd('visitors', moment().format('X'), req.user.account_id, cb);
        } else {
          cb();
        }
      },
      tracked: (cb) => {
        if (req.user && req.user.account_id) {
          redis.zadd('tracked', moment().add(config.UNTRACK_DAYS, 'days').format('X'), req.user.account_id, cb);
        } else {
          cb();
        }
      },
      load_add: (cb) => {
        redis.lpush('load_times', elapsed, cb);
      },
      load_trim: (cb) => {
        redis.ltrim('load_times', 0, 9999, cb);
      },
    }, (err) => {
      if (err) {
        console.error(err);
      }
    });
  });
  cb();
});
// CORS headers
app.use(cors({
  origin: true,
  credentials: true,
}));
app.route('/healthz').get((req, res) => {
  res.send('ok');
});
app.route('/login').get(passport.authenticate('steam', {
  failureRedirect: '/api',
}));
app.route('/return').get(passport.authenticate('steam', {
  failureRedirect: '/api',
}), (req, res) => {
  if (config.UI_HOST) {
    return res.redirect(`${config.UI_HOST}/players/${req.user.account_id}`);
  }
  return res.redirect('/api');
});
app.route('/logout').get((req, res) => {
  req.logout();
  req.session = null;
  if (config.UI_HOST) {
    return res.redirect(config.UI_HOST);
  }
  return res.redirect('/api');
});
app.use('/api', api);
// 404 route
app.use((req, res) =>
  res.status(404).json({
    error: 'Not Found',
  }),
);
// 500 route
app.use((err, req, res, cb) => {
  redis.zadd('error_500', moment().format('X'), `${uuidV4()}:${req.originalUrl}`);
  if (config.NODE_ENV === 'development') {
    // default express handler
    return cb(err);
  }
  return res.status(500).json({
    error: 'Internal Server Error',
  });
});
const port = config.PORT || config.FRONTEND_PORT;
const server = app.listen(port, () => {
  console.log('[WEB] listening on %s', port);
});
/**
 * Wait for connections to end, then shut down
 **/
function gracefulShutdown() {
  console.log('Received kill signal, shutting down gracefully.');
  server.close(() => {
    console.log('Closed out remaining connections.');
    process.exit();
  });
  // if after
  setTimeout(() => {
    console.error('Could not close connections in time, forcefully shutting down');
    process.exit();
  }, 10 * 1000);
}
// listen for TERM signal .e.g. kill
process.once('SIGTERM', gracefulShutdown);
// listen for INT signal e.g. Ctrl-C
process.once('SIGINT', gracefulShutdown);
module.exports = app;
<commit_msg>raise rate limit<commit_after>/**
 * Worker serving as main web application
 * Serves web/API requests
 **/
const config = require('../config');
const utility = require('../util/utility');
const redis = require('../store/redis');
const db = require('../store/db');
const queries = require('../store/queries');
const api = require('../routes/api');
const request = require('request');
const compression = require('compression');
const session = require('cookie-session');
const moment = require('moment');
const express = require('express');
const passport = require('passport');
const SteamStrategy = require('passport-steam').Strategy;
const cors = require('cors');
const async = require('async');
const uuidV4 = require('uuid/v4');

const app = express();
const apiKey = config.STEAM_API_KEY.split(',')[0];
const host = config.ROOT_URL;
const sessOptions = {
  domain: config.COOKIE_DOMAIN,
  maxAge: 52 * 7 * 24 * 60 * 60 * 1000,
  secret: config.SESSION_SECRET,
};
// PASSPORT config
passport.serializeUser((user, done) => {
  done(null, user.account_id);
});
passport.deserializeUser((accountId, done) => {
  done(null, {
    account_id: accountId,
  });
});
passport.use(new SteamStrategy({
  returnURL: `${host}/return`,
  realm: host,
  apiKey,
}, (identifier, profile, cb) => {
  const player = profile._json;
  player.last_login = new Date();
  queries.insertPlayer(db, player, (err) => {
    if (err) {
      return cb(err);
    }
    return cb(err, player);
  });
}));
// Compression middleware
app.use(compression());
// Dota 2 images middleware (proxy to Dota 2 CDN to serve over https)
app.use('/apps', (req, res) => {
  res.header('Cache-Control', 'max-age=604800, public');
  request(`http://cdn.dota2.com/${req.originalUrl}`).pipe(res);
});
// Session/Passport middleware
app.use(session(sessOptions));
app.use(passport.initialize());
app.use(passport.session());
// Rate limiter middleware
app.use((req, res, cb) => {
  let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || '';
  ip = ip.replace(/^.*:/, '').split(',')[0];
  const key = `rate_limit:${ip}`;
  console.log('%s visit %s, ip %s', req.user ? req.user.account_id : 'anonymous', req.originalUrl, ip);
  redis.multi().incr(key).expireat(key, utility.getStartOfBlockMinutes(1, 1)).exec((err, resp) => {
    if (err) {
      console.log(err);
      return cb();
    }
    if (config.NODE_ENV === 'development') {
      console.log(resp);
    }
    if (resp[0] > 180 && config.NODE_ENV !== 'test') {
      return res.status(429).json({
        error: 'rate limit exceeded',
      });
    }
    return cb();
  });
});
// Telemetry middleware
app.use((req, res, cb) => {
  const timeStart = new Date();
  res.once('finish', () => {
    const timeEnd = new Date();
    const elapsed = timeEnd - timeStart;
    if (elapsed > 1000 || config.NODE_ENV === 'development') {
      console.log('[SLOWLOG] %s, %s', req.originalUrl, elapsed);
    }
    async.series({
      api_hit: (cb) => {
        if (req.originalUrl.indexOf('/api') === 0) {
          redis.zadd('api_hits', moment().format('X'), `${uuidV4()}:${req.originalUrl}`, cb);
        } else {
          cb();
        }
      },
      visitors: (cb) => {
        if (req.user && req.user.account_id) {
          redis.zadd('visitors', moment().format('X'), req.user.account_id, cb);
        } else {
          cb();
        }
      },
      tracked: (cb) => {
        if (req.user && req.user.account_id) {
          redis.zadd('tracked', moment().add(config.UNTRACK_DAYS, 'days').format('X'), req.user.account_id, cb);
        } else {
          cb();
        }
      },
      load_add: (cb) => {
        redis.lpush('load_times', elapsed, cb);
      },
      load_trim: (cb) => {
        redis.ltrim('load_times', 0, 9999, cb);
      },
    }, (err) => {
      if (err) {
        console.error(err);
      }
    });
  });
  cb();
});
// CORS headers
app.use(cors({
  origin: true,
  credentials: true,
}));
app.route('/healthz').get((req, res) => {
  res.send('ok');
});
app.route('/login').get(passport.authenticate('steam', {
  failureRedirect: '/api',
}));
app.route('/return').get(passport.authenticate('steam', {
  failureRedirect: '/api',
}), (req, res) => {
  if (config.UI_HOST) {
    return res.redirect(`${config.UI_HOST}/players/${req.user.account_id}`);
  }
  return res.redirect('/api');
});
app.route('/logout').get((req, res) => {
  req.logout();
  req.session = null;
  if (config.UI_HOST) {
    return res.redirect(config.UI_HOST);
  }
  return res.redirect('/api');
});
app.use('/api', api);
// 404 route
app.use((req, res) =>
  res.status(404).json({
    error: 'Not Found',
  }),
);
// 500 route
app.use((err, req, res, cb) => {
  redis.zadd('error_500', moment().format('X'), `${uuidV4()}:${req.originalUrl}`);
  if (config.NODE_ENV === 'development') {
    // default express handler
    return cb(err);
  }
  return res.status(500).json({
    error: 'Internal Server Error',
  });
});
const port = config.PORT || config.FRONTEND_PORT;
const server = app.listen(port, () => {
  console.log('[WEB] listening on %s', port);
});
/**
 * Wait for connections to end, then shut down
 **/
function gracefulShutdown() {
  console.log('Received kill signal, shutting down gracefully.');
  server.close(() => {
    console.log('Closed out remaining connections.');
    process.exit();
  });
  // if after
  setTimeout(() => {
    console.error('Could not close connections in time, forcefully shutting down');
    process.exit();
  }, 10 * 1000);
}
// listen for TERM signal .e.g. kill
process.once('SIGTERM', gracefulShutdown);
// listen for INT signal e.g. Ctrl-C
process.once('SIGINT', gracefulShutdown);
module.exports = app;
<|endoftext|>