<commit_before>// Copyright 2013 Web Notes Technologies Pvt Ltd
// License: MIT. See license.txt

// opts - parent, list, doc, email
wn.views.CommunicationList = Class.extend({
	init: function(opts) {
		this.comm_list = [];
		$.extend(this, opts);

		if(this.doc.__islocal) {
			$(this.parent).empty();
			return;
		}

		var sortfn = function (a, b) { return (b.creation > a.creation) ? 1 : -1; }
		this.list = this.list.sort(sortfn);

		this.make();
	},
	make: function() {
		var me = this;
		this.make_body();

		if(this.list && this.list.length) {
			$.each(this.list, function(i, d) {
				me.prepare(d);
				me.make_line(d);
			});
			// show first
			this.comm_list[0].find('.comm-content').toggle(true);
		} else {
			this.clear_list()
		}
	},
	clear_list: function() {
		this.body.remove();
		$("
" + wn._("No Communication tagged with this ")
			+ this.doc.doctype +" yet.
").appendTo(this.wrapper);
	},
	make_body: function() {
		$(this.parent)
			.empty()
			.css({"margin":"10px 0px"});

		this.wrapper = $("

"+wn._("Communication History")+"
\

\
				<button class='btn' \
					onclick='cur_frm.communication_view.add_reply()'>\
				 "+wn._("Add Message")+"</button>
\

")
			.appendTo(this.parent);

		this.body = $("
")
			.appendTo(this.wrapper);
	},

	add_reply: function() {
		var subject = this.doc.subject;
		if(!subject && this.list.length) {
			// get subject from previous message
			subject = this.list[0].subject || "[No Subject]";
			if(strip(subject.toLowerCase().split(":")[0])!="re") {
				subject = "Re: " + subject;
			}
		}
		new wn.views.CommunicationComposer({
			doc: this.doc,
			subject: subject,
			recipients: this.recipients
		})
	},

	prepare: function(doc) {
		//doc.when = comment_when(this.doc.modified);
		doc.when = doc.modified;
		if(!doc.content) doc.content = "[no content]";
		if(doc.content.indexOf("
")== -1 && doc.content.indexOf("
")== -1) {
			doc.content = doc.content.replace(/\n/g, "
");
		}
		if(!doc.sender) doc.sender = "[unknown sender]";
		doc._sender = doc.sender.replace(/</, "<").replace(/>/, ">");
		doc.content = doc.content.split("-----"+wn._("In response to")+"-----")[0];
		doc.content = doc.content.split("-----"+wn._("Original Message")+"-----")[0];
	},

	make_line: function(doc) {
		var me = this;
		var comm = $(repl('
\
				'
					+wn._('Show Details')+'\

\
					%(_sender)s on %(when)s
\

\

', doc))
			.appendTo(this.body);

		if(!doc.name) {
			comm.find(".show-details").toggle(false);
		}

		comm.find(".comm-header")
			.css({"cursor":"pointer"})
			.click(function() {
				$(this).parent().find(".comm-content").toggle();
			});

		this.comm_list.push(comm);
		comm.find(".comm-content").html(doc.content);
	}
});

wn.views.CommunicationComposer = Class.extend({
	init: function(opts) {
		$.extend(this, opts)
		this.make();
		this.dialog.show();
	},
	make: function() {
		this.dialog = new wn.ui.Dialog({
			width: 640,
			title: wn._("Add Reply") + ": " + (this.subject || ""),
			no_submit_on_enter: true,
			fields: [
				{label:wn._("To"), fieldtype:"Data", reqd: 1, fieldname:"recipients",
					description:wn._("Email addresses, separted by commas")},
				{label:wn._("Subject"), fieldtype:"Data", reqd: 1,
					fieldname:"subject"},
				{label:wn._("Send"), fieldtype:"Button",
					fieldname:"send"},
				{label:wn._("Message"), fieldtype:"Text Editor", reqd: 1,
					fieldname:"content"},
				{label:wn._("Send Email"), fieldtype:"Check",
					fieldname:"send_email"},
				{label:wn._("Send Me A Copy"), fieldtype:"Check",
					fieldname:"send_me_a_copy"},
				{label:wn._("Attach Document Print"), fieldtype:"Check",
					fieldname:"attach_document_print"},
				{label:wn._("Select Print Format"), fieldtype:"Select",
					fieldname:"select_print_format"},
				{label:wn._("Select Attachments"), fieldtype:"HTML",
					fieldname:"select_attachments"}
			]
		});
		this.prepare();
	},
	prepare: function() {
		this.setup_print();
		this.setup_attach();
		this.setup_email();
		this.setup_autosuggest();
		$(this.dialog.fields_dict.recipients.input).val(this.recipients || "").change();
		$(this.dialog.fields_dict.subject.input).val(this.subject || "").change();
		this.setup_earlier_reply();
	},
	setup_print: function() {
		// print formats
		var fields = this.dialog.fields_dict;

		// toggle print format
		$(fields.attach_document_print.input).click(function() {
			$(fields.select_print_format.wrapper).toggle($(this).is(":checked"));
		});

		// select print format
		$(fields.select_print_format.wrapper).toggle(false);
		$(fields.select_print_format.input)
			.empty()
			.add_options(cur_frm.print_formats)
			.val(cur_frm.print_formats[0]);

	},
	setup_attach: function() {
		var fields = this.dialog.fields_dict;
		var attach = $(fields.select_attachments.wrapper);

		var files = cur_frm.get_files();
		if(files.length) {
			$("
"+wn._("Add Attachments")+":
").appendTo(attach);
			$.each(files, function(i, f) {
				$(repl("
<input type='checkbox' \
					data-file-name='%(file)s'> %(file)s
", {file:f})).appendTo(attach)
			});
		}
	},
	setup_email: function() {
		// email
		var me = this;
		var fields = this.dialog.fields_dict;

		if(this.attach_document_print) {
			$(fields.send_me_a_copy.input).click();
			$(fields.attach_document_print.input).click();
			$(fields.select_print_format.wrapper).toggle(true);
		}

		$(fields.send_email.input).attr("checked", "checked")
		$(fields.send.input).click(function() {
			var form_values = me.dialog.get_values();
			if(!form_values) return;

			var selected_attachments = $.map($(me.dialog.wrapper)
				.find("[data-file-name]:checked"), function(element) {
					return $(element).attr("data-file-name");
				})

			_p.build(form_values.select_print_format || "", function(print_format_html) {
				if(form_values.attach_document_print) {
					var print_html = print_format_html
					if(cint(wn.boot.send_print_in_body_and_attachment)) {
						form_values.content = form_values.content
							+ "

" + print_html;
					} else {
						form_values.content = form_values.content + "
"
							+ "Please see attachment for document details.
"
					}
				} else {
					print_html = "";
				}
				wn.call({
					method:"core.doctype.communication.communication.make",
					args: {
						sender: wn.user_info(user).fullname + " <" + wn.boot.profile.email + ">",
						recipients: form_values.recipients,
						subject: form_values.subject,
						content: form_values.content,
						doctype: me.doc.doctype,
						name: me.doc.name,
						lead: me.doc.lead,
						contact: me.doc.contact,
						send_me_a_copy: form_values.send_me_a_copy,
						send_email: form_values.send_email,
						print_html: print_html,
						attachments: selected_attachments
					},
					callback: function(r) {
						if(!r.exc) {
							if(form_values.send_email)
								msgprint("Email sent to " + form_values.recipients);
							me.dialog.hide();
							cur_frm.reload_doc();
						} else {
							msgprint("There were errors while sending email. Please try again.")
						}
					}
				});
			})
		});
	},
	setup_earlier_reply: function() {
		var fields = this.dialog.fields_dict;
		var comm_list = cur_frm.communication_view
			? cur_frm.communication_view.list
			: [];
		var signature = wn.boot.profile.email_signature || "";

		if(signature.indexOf("
")==-1 && signature.indexOf("<p")==-1
			&& signature.indexOf("<img")==-1 && signature.indexOf(" 0) {
			fields.content.input.set_input((this.message || "") +
				"

"
				+ signature
				+"

"
				+"-----"+wn._("In response to")+"-----

"
				+ comm_list[0].content);
		} else {
			fields.content.input.set_input((this.message || "")
				+ "

" + signature)
		}
	},
	setup_autosuggest: function() {
		var me = this;

		function split( val ) {
			return val.split( /,\s*/ );
		}
		function extractLast( term ) {
			return split(term).pop();
		}

		$(this.dialog.fields_dict.recipients.input)
			.bind( "keydown", function(event) {
				if (event.keyCode === $.ui.keyCode.TAB &&
						$(this).data( "autocomplete" ).menu.active ) {
					event.preventDefault();
				}
			})
			.autocomplete({
				source: function(request, response) {
					wn.call({
						method:'webnotes.utils.email_lib.get_contact_list',
						args: {
							'select': "email_id",
							'from': "Contact",
							'where': "email_id",
							'txt': extractLast(request.term).value || '%'
						},
						callback: function(r) {
							response($.ui.autocomplete.filter(
								r.cl || [], extractLast(request.term)));
						}
					});
				},
				focus: function() {
					// prevent value inserted on focus
					return false;
				},
				select: function( event, ui ) {
					var terms = split( this.value );
					// remove the current input
					terms.pop();
					// add the selected item
					terms.push( ui.item.value );
					// add placeholder to get the comma-and-space at the end
					terms.push( "" );
					this.value = terms.join( ", " );
					return false;
				}
			});
	}
});

<commit_msg>[communication] [usability] freeze send button when ajax is called<commit_after>// Copyright 2013 Web Notes Technologies Pvt Ltd
// License: MIT. See license.txt

// opts - parent, list, doc, email
wn.views.CommunicationList = Class.extend({
	init: function(opts) {
		this.comm_list = [];
		$.extend(this, opts);

		if(this.doc.__islocal) {
			$(this.parent).empty();
			return;
		}

		var sortfn = function (a, b) { return (b.creation > a.creation) ? 1 : -1; }
		this.list = this.list.sort(sortfn);

		this.make();
	},
	make: function() {
		var me = this;
		this.make_body();

		if(this.list && this.list.length) {
			$.each(this.list, function(i, d) {
				me.prepare(d);
				me.make_line(d);
			});
			// show first
			this.comm_list[0].find('.comm-content').toggle(true);
		} else {
			this.clear_list()
		}
	},
	clear_list: function() {
		this.body.remove();
		$("
" + wn._("No Communication tagged with this ")
			+ this.doc.doctype +" yet.
").appendTo(this.wrapper);
	},
	make_body: function() {
		$(this.parent)
			.empty()
			.css({"margin":"10px 0px"});

		this.wrapper = $("

"+wn._("Communication History")+"
\

\
				<button class='btn' \
					onclick='cur_frm.communication_view.add_reply()'>\
				 "+wn._("Add Message")+"</button>
\

")
			.appendTo(this.parent);

		this.body = $("
")
			.appendTo(this.wrapper);
	},

	add_reply: function() {
		var subject = this.doc.subject;
		if(!subject && this.list.length) {
			// get subject from previous message
			subject = this.list[0].subject || "[No Subject]";
			if(strip(subject.toLowerCase().split(":")[0])!="re") {
				subject = "Re: " + subject;
			}
		}
		new wn.views.CommunicationComposer({
			doc: this.doc,
			subject: subject,
			recipients: this.recipients
		})
	},

	prepare: function(doc) {
		//doc.when = comment_when(this.doc.modified);
		doc.when = doc.modified;
		if(!doc.content) doc.content = "[no content]";
		if(doc.content.indexOf("
")== -1 && doc.content.indexOf("
")== -1) {
			doc.content = doc.content.replace(/\n/g, "
");
		}
		if(!doc.sender) doc.sender = "[unknown sender]";
		doc._sender = doc.sender.replace(/</, "<").replace(/>/, ">");
		doc.content = doc.content.split("-----"+wn._("In response to")+"-----")[0];
		doc.content = doc.content.split("-----"+wn._("Original Message")+"-----")[0];
	},

	make_line: function(doc) {
		var me = this;
		var comm = $(repl('
\
				'
					+wn._('Show Details')+'\

\
					%(_sender)s on %(when)s
\

\

', doc))
			.appendTo(this.body);

		if(!doc.name) {
			comm.find(".show-details").toggle(false);
		}

		comm.find(".comm-header")
			.css({"cursor":"pointer"})
			.click(function() {
				$(this).parent().find(".comm-content").toggle();
			});

		this.comm_list.push(comm);
		comm.find(".comm-content").html(doc.content);
	}
});

wn.views.CommunicationComposer = Class.extend({
	init: function(opts) {
		$.extend(this, opts)
		this.make();
		this.dialog.show();
	},
	make: function() {
		this.dialog = new wn.ui.Dialog({
			width: 640,
			title: wn._("Add Reply") + ": " + (this.subject || ""),
			no_submit_on_enter: true,
			fields: [
				{label:wn._("To"), fieldtype:"Data", reqd: 1, fieldname:"recipients",
					description:wn._("Email addresses, separted by commas")},
				{label:wn._("Subject"), fieldtype:"Data", reqd: 1,
					fieldname:"subject"},
				{label:wn._("Send"), fieldtype:"Button",
					fieldname:"send"},
				{label:wn._("Message"), fieldtype:"Text Editor", reqd: 1,
					fieldname:"content"},
				{label:wn._("Send Email"), fieldtype:"Check",
					fieldname:"send_email"},
				{label:wn._("Send Me A Copy"), fieldtype:"Check",
					fieldname:"send_me_a_copy"},
				{label:wn._("Attach Document Print"), fieldtype:"Check",
					fieldname:"attach_document_print"},
				{label:wn._("Select Print Format"), fieldtype:"Select",
					fieldname:"select_print_format"},
				{label:wn._("Select Attachments"), fieldtype:"HTML",
					fieldname:"select_attachments"}
			]
		});
		this.prepare();
	},
	prepare: function() {
		this.setup_print();
		this.setup_attach();
		this.setup_email();
		this.setup_autosuggest();
		$(this.dialog.fields_dict.recipients.input).val(this.recipients || "").change();
		$(this.dialog.fields_dict.subject.input).val(this.subject || "").change();
		this.setup_earlier_reply();
	},
	setup_print: function() {
		// print formats
		var fields = this.dialog.fields_dict;

		// toggle print format
		$(fields.attach_document_print.input).click(function() {
			$(fields.select_print_format.wrapper).toggle($(this).is(":checked"));
		});

		// select print format
		$(fields.select_print_format.wrapper).toggle(false);
		$(fields.select_print_format.input)
			.empty()
			.add_options(cur_frm.print_formats)
			.val(cur_frm.print_formats[0]);

	},
	setup_attach: function() {
		var fields = this.dialog.fields_dict;
		var attach = $(fields.select_attachments.wrapper);

		var files = cur_frm.get_files();
		if(files.length) {
			$("
"+wn._("Add Attachments")+":
").appendTo(attach);
			$.each(files, function(i, f) {
				$(repl("
<input type='checkbox' \
					data-file-name='%(file)s'> %(file)s
", {file:f})).appendTo(attach)
			});
		}
	},
	setup_email: function() {
		// email
		var me = this;
		var fields = this.dialog.fields_dict;

		if(this.attach_document_print) {
			$(fields.send_me_a_copy.input).click();
			$(fields.attach_document_print.input).click();
			$(fields.select_print_format.wrapper).toggle(true);
		}

		$(fields.send_email.input).attr("checked", "checked")
		$(fields.send.input).click(function() {
			var btn = this;
			var form_values = me.dialog.get_values();
			if(!form_values) return;

			var selected_attachments = $.map($(me.dialog.wrapper)
				.find("[data-file-name]:checked"), function(element) {
					return $(element).attr("data-file-name");
				})

			_p.build(form_values.select_print_format || "", function(print_format_html) {
				if(form_values.attach_document_print) {
					var print_html = print_format_html
					if(cint(wn.boot.send_print_in_body_and_attachment)) {
						form_values.content = form_values.content
							+ "

" + print_html;
					} else {
						form_values.content = form_values.content + "
"
							+ "Please see attachment for document details.
"
					}
				} else {
					print_html = "";
				}
				wn.call({
					method:"core.doctype.communication.communication.make",
					args: {
						sender: wn.user_info(user).fullname + " <" + wn.boot.profile.email + ">",
						recipients: form_values.recipients,
						subject: form_values.subject,
						content: form_values.content,
						doctype: me.doc.doctype,
						name: me.doc.name,
						lead: me.doc.lead,
						contact: me.doc.contact,
						send_me_a_copy: form_values.send_me_a_copy,
						send_email: form_values.send_email,
						print_html: print_html,
						attachments: selected_attachments
					},
					btn: btn,
					callback: function(r) {
						if(!r.exc) {
							if(form_values.send_email)
								msgprint("Email sent to " + form_values.recipients);
							me.dialog.hide();
							cur_frm.reload_doc();
						} else {
							msgprint("There were errors while sending email. Please try again.")
						}
					}
				});
			})
		});
	},
	setup_earlier_reply: function() {
		var fields = this.dialog.fields_dict;
		var comm_list = cur_frm.communication_view
			? cur_frm.communication_view.list
			: [];
		var signature = wn.boot.profile.email_signature || "";

		if(signature.indexOf("
")==-1 && signature.indexOf("<p")==-1
			&& signature.indexOf("<img")==-1 && signature.indexOf(" 0) {
			fields.content.input.set_input((this.message || "") +
				"

"
				+ signature
				+"

"
				+"-----"+wn._("In response to")+"-----

"
				+ comm_list[0].content);
		} else {
			fields.content.input.set_input((this.message || "")
				+ "

" + signature)
		}
	},
	setup_autosuggest: function() {
		var me = this;

		function split( val ) {
			return val.split( /,\s*/ );
		}
		function extractLast( term ) {
			return split(term).pop();
		}

		$(this.dialog.fields_dict.recipients.input)
			.bind( "keydown", function(event) {
				if (event.keyCode === $.ui.keyCode.TAB &&
						$(this).data( "autocomplete" ).menu.active ) {
					event.preventDefault();
				}
			})
			.autocomplete({
				source: function(request, response) {
					wn.call({
						method:'webnotes.utils.email_lib.get_contact_list',
						args: {
							'select': "email_id",
							'from': "Contact",
							'where': "email_id",
							'txt': extractLast(request.term).value || '%'
						},
						callback: function(r) {
							response($.ui.autocomplete.filter(
								r.cl || [], extractLast(request.term)));
						}
					});
				},
				focus: function() {
					// prevent value inserted on focus
					return false;
				},
				select: function( event, ui ) {
					var terms = split( this.value );
					// remove the current input
					terms.pop();
					// add the selected item
					terms.push( ui.item.value );
					// add placeholder to get the comma-and-space at the end
					terms.push( "" );
					this.value = terms.join( ", " );
					return false;
				}
			});
	}
});

<|endoftext|>


<commit_before>/*!
 * URL Handler - v0.1
 *
 * Copyright (c) 2013 Dave Olsen, http://dmolsen.com
 * Licensed under the MIT license
 *
 * Helps handle the initial iFrame source. Parses a string to see if it matches
 * an expected pattern in Pattern Lab. Supports Pattern Labs fuzzy pattern partial
 * matching style.
 *
 */

var urlHandler = {

	getFileName: function (name) {

		var baseDir     = "patterns";
		var fileName    = "";

		var bits        = this.getPatternInfo(name);
		var patternType = bits[0];
		var pattern     = bits[1];

		if ((patternPaths[patternType] != undefined) && (patternPaths[patternType][pattern] != undefined)) {

			fileName = patternPaths[patternType][pattern];

		} else if (patternPaths[patternType] != undefined) {

			for (patternMatchKey in patternPaths[patternType]) {
				if (patternMatchKey.indexOf(pattern) != -1) {
					fileName = patternPaths[patternType][patternMatchKey];
					break;
				}
			}

		}

		var regex = /\//g;
		return (fileName == "") ? fileName : baseDir+"/"+fileName.replace(regex,"-")+"/"+fileName.replace(regex,"-")+".html";

	},

	getPatternInfo: function (name) {

		var patternBits = name.split("-");

		var i = 1;
		var c = patternBits.length;

		var patternType = patternBits[0];
		while ((patternPaths[patternType] == undefined) && (i < c)) {
			patternType += "-"+patternBits[i];
			i++;
		}

		pattern = name.slice(patternType.length+1,name.length);

		return [patternType, pattern];

	/**
	* search the request vars for a particular item
	*
	* @return {Object}       a search of the window.location.search vars
	*/
	getRequestVars: function() {

		// the following is taken from https://developer.mozilla.org/en-US/docs/Web/API/window.location
		var oGetVars = new (function (sSearch) {
		  if (sSearch.length > 1) {
		    for (var aItKey, nKeyId = 0, aCouples = sSearch.substr(1).split("&"); nKeyId < aCouples.length; nKeyId++) {
		      aItKey = aCouples[nKeyId].split("=");
		      this[unescape(aItKey[0])] = aItKey.length > 1 ? unescape(aItKey[1]) : "";
		    }
		  }
		})(window.location.search);

		return oGetVars;

	}

}

var iFramePath = "";
if ((oGetVars.p != undefined) || (oGetVars.pattern != undefined)) {
	patternName = (oGetVars.p != undefined) ? oGetVars.p : oGetVars.pattern;
	iFramePath = urlHandler.getFileName(patternName);
}

iFramePath = (iFramePath != "") ? iFramePath : "styleguide/html/styleguide.html";

$("#sg-viewport").attr("src",iFramePath);
<commit_msg>moved non-lib logic out to styleguide.js<commit_after>/*!
 * URL Handler - v0.1
 *
 * Copyright (c) 2013 Dave Olsen, http://dmolsen.com
 * Licensed under the MIT license
 *
 * Helps handle the initial iFrame source. Parses a string to see if it matches
 * an expected pattern in Pattern Lab. Supports Pattern Labs fuzzy pattern partial
 * matching style.
 *
 */

var urlHandler = {

	getFileName: function (name) {

		var baseDir     = "patterns";
		var fileName    = "";

		var bits        = this.getPatternInfo(name);
		var patternType = bits[0];
		var pattern     = bits[1];

		if ((patternPaths[patternType] != undefined) && (patternPaths[patternType][pattern] != undefined)) {

			fileName = patternPaths[patternType][pattern];

		} else if (patternPaths[patternType] != undefined) {

			for (patternMatchKey in patternPaths[patternType]) {
				if (patternMatchKey.indexOf(pattern) != -1) {
					fileName = patternPaths[patternType][patternMatchKey];
					break;
				}
			}

		}

		var regex = /\//g;
		return (fileName == "") ? fileName : baseDir+"/"+fileName.replace(regex,"-")+"/"+fileName.replace(regex,"-")+".html";

	},

	getPatternInfo: function (name) {

		var patternBits = name.split("-");

		var i = 1;
		var c = patternBits.length;

		var patternType = patternBits[0];
		while ((patternPaths[patternType] == undefined) && (i < c)) {
			patternType += "-"+patternBits[i];
			i++;
		}

		pattern = name.slice(patternType.length+1,name.length);

		return [patternType, pattern];

	/**
	* search the request vars for a particular item
	*
	* @return {Object}       a search of the window.location.search vars
	*/
	getRequestVars: function() {

		// the following is taken from https://developer.mozilla.org/en-US/docs/Web/API/window.location
		var oGetVars = new (function (sSearch) {
		  if (sSearch.length > 1) {
		    for (var aItKey, nKeyId = 0, aCouples = sSearch.substr(1).split("&"); nKeyId < aCouples.length; nKeyId++) {
		      aItKey = aCouples[nKeyId].split("=");
		      this[unescape(aItKey[0])] = aItKey.length > 1 ? unescape(aItKey[1]) : "";
		    }
		  }
		})(window.location.search);

		return oGetVars;

	}

}
<|endoftext|>


<commit_before>// configure our routes
app.config(function($routeProvider, $locationProvider) {
	$routeProvider

	// route for the home page
		.when('/', {
			controller  : 'HomeController',
			templateUrl : 'angular/templates/home.php'
		})

		// route for the sign in page
		.when('/signin', {
			controller  : 'SigninController',
			templateUrl : 'angular/templates/signin.php'
		})

		// route for the sign up page
		.when('/signup', {
			controller  : 'SignupController',
			templateUrl : 'angular/templates/signup.php'
		})

		// route for the the compass page
		.when('/thecompass', {
			controller  : 'BeerController',
			templateUrl : 'angular/templates/thecompass.php'
		})

		// route for the breweries page
		.when('/brewery', {
			controller  : 'BreweryController',
			templateUrl : 'angular/templates/brewery.php'
		})

		// route for the beer page
		.when('/beer', {
			controller  : 'BeerController',
			templateUrl : 'angular/templates/beer.php'
		})

		// route for the user page
		.when('/user', {
			controller  : 'UserController',
			templateUrl : 'angular/templates/userprofile.php'
		})

		// route for the review page
		.when('/review', {
			controller  : 'ReviewController',
			templateUrl : 'angular/templates/review.php'
		})

		// route for the beer profile pages
		.when('/beerprofile/:id', {
			controller  : 'BeerProfileController',
			templateUrl : 'angular/templates/beerprofile.php'
		})

		// route for the brewery profile pages
		.when('/breweryprofile/:id', {
			controller  : 'BreweryProfileController',
			templateUrl : 'angular/templates/breweryprofile.php'
		})
		.when('/', {
			controller  : 'SigninController',
			templateUrl : 'angular/templates/signin.php'
		})
		// otherwise redirect to home
		.otherwise({
			redirectTo: "/"
		});

	//use the HTML5 History API
	$locationProvider.html5Mode(true);
});
<commit_msg>changed route path for home page back to home<commit_after>// configure our routes
app.config(function($routeProvider, $locationProvider) {
	$routeProvider

	// route for the home page
		.when('/', {
			controller  : 'HomeController',
			templateUrl : 'angular/templates/home.php'
		})

		// route for the sign in page
		.when('/signin', {
			controller  : 'SigninController',
			templateUrl : 'angular/templates/signin.php'
		})

		// route for the sign up page
		.when('/signup', {
			controller  : 'SignupController',
			templateUrl : 'angular/templates/signup.php'
		})

		// route for the the compass page
		.when('/thecompass', {
			controller  : 'BeerController',
			templateUrl : 'angular/templates/thecompass.php'
		})

		// route for the breweries page
		.when('/brewery', {
			controller  : 'BreweryController',
			templateUrl : 'angular/templates/brewery.php'
		})

		// route for the beer page
		.when('/beer', {
			controller  : 'BeerController',
			templateUrl : 'angular/templates/beer.php'
		})

		// route for the user page
		.when('/user', {
			controller  : 'UserController',
			templateUrl : 'angular/templates/userprofile.php'
		})

		// route for the review page
		.when('/review', {
			controller  : 'ReviewController',
			templateUrl : 'angular/templates/review.php'
		})

		// route for the beer profile pages
		.when('/beerprofile/:id', {
			controller  : 'BeerProfileController',
			templateUrl : 'angular/templates/beerprofile.php'
		})

		// route for the brewery profile pages
		.when('/breweryprofile/:id', {
			controller  : 'BreweryProfileController',
			templateUrl : 'angular/templates/breweryprofile.php'
		})

		// otherwise redirect to home
		.otherwise({
			redirectTo: "/"
		});

	//use the HTML5 History API
	$locationProvider.html5Mode(true);
});
<|endoftext|>


<commit_before>/* eslint-disable no-console */

// System documented in https://zulip.readthedocs.io/en/latest/subsystems/logging.html

// This must be included before the first call to $(document).ready
// in order to be able to report exceptions that occur during their
// execution.

const blueslip_stacktrace = require("./blueslip_stacktrace");

if (Error.stackTraceLimit !== undefined) {
    Error.stackTraceLimit = 100000;
}

function Logger() {
    this._memory_log = [];
}

Logger.prototype = (function () {
    function pad(num, width) {
        let ret = num.toString();
        while (ret.length < width) {
            ret = "0" + ret;
        }
        return ret;
    }

    function make_logger_func(name) {
        return function Logger_func() {
            const now = new Date();
            const date_str =
                now.getUTCFullYear() + '-' +
                pad(now.getUTCMonth() + 1, 2) + '-' +
                pad(now.getUTCDate(), 2) + ' ' +
                pad(now.getUTCHours(), 2) + ':' +
                pad(now.getUTCMinutes(), 2) + ':' +
                pad(now.getUTCSeconds(), 2) + '.' +
                pad(now.getUTCMilliseconds(), 3) + ' UTC';

            const str_args = _.map(arguments, function (x) {
                if (typeof x === 'object') {
                    return JSON.stringify(x);
                }
                return x;
            });

            const log_entry = date_str + " " + name.toUpperCase() +
                ': ' + str_args.join("");
            this._memory_log.push(log_entry);

            // Don't let the log grow without bound
            if (this._memory_log.length > 1000) {
                this._memory_log.shift();
            }

            if (console[name] !== undefined) {
                return console[name].apply(console, arguments);
            }
            return;
        };
    }

    const proto = {
        get_log: function Logger_get_log() {
            return this._memory_log;
        },
    };

    const methods = ['debug', 'log', 'info', 'warn', 'error'];
    let i;
    for (i = 0; i < methods.length; i += 1) {
        proto[methods[i]] = make_logger_func(methods[i]);
    }

    return proto;
}());

const logger = new Logger();

exports.get_log = function blueslip_get_log() {
    return logger.get_log();
};

const reported_errors = {};
const last_report_attempt = {};

function report_error(msg, stack, opts) {
    opts = _.extend({show_ui_msg: false}, opts);

    if (stack === undefined) {
        stack = 'No stacktrace available';
    }

    if (page_params.debug_mode) {
        // In development, we display blueslip errors in the web UI,
        // to make them hard to miss.
        blueslip_stacktrace.display_stacktrace(msg, stack);
    }

    const key = ':' + msg + stack;
    if (reported_errors.hasOwnProperty(key)
        || last_report_attempt.hasOwnProperty(key)
            // Only try to report a given error once every 5 minutes
            && Date.now() - last_report_attempt[key] <= 60 * 5 * 1000) {
        return;
    }

    last_report_attempt[key] = Date.now();

    // TODO: If an exception gets thrown before we setup ajax calls
    // to include the CSRF token, our ajax call will fail.  The
    // elegant thing to do in that case is to either wait until that
    // setup is done or do it ourselves and then retry.
    $.ajax({
        type: 'POST',
        url: '/json/report/error',
        dataType: 'json',
        data: {
            message: msg,
            stacktrace: stack,
            ui_message: opts.show_ui_msg,
            more_info: JSON.stringify(opts.more_info),
            href: window.location.href,
            user_agent: window.navigator.userAgent,
            log: logger.get_log().join("\n"),
        },
        timeout: 3 * 1000,
        success: function () {
            reported_errors[key] = true;
            if (opts.show_ui_msg && ui_report !== undefined) {
                // There are a few races here (and below in the error
                // callback):
                // 1) The ui_report module or something it requires might
                //    not have been compiled or initialized yet.
                // 2) The DOM might not be ready yet and so fetching
                //    the #home-error div might fail.

                // For (1) we just don't show the message if the ui
                // hasn't been loaded yet.  The user will probably
                // get another error once it does.  We can't solve
                // (2) by using $(document).ready because the
                // callback never gets called (I think what's going
                // on here is if the exception was raised by a
                // function that was called as a result of the DOM
                // becoming ready then the internal state of jQuery
                // gets messed up and our callback never gets
                // invoked).  In any case, it will pretty clear that
                // something is wrong with the page and the user will
                // probably try to reload anyway.
                ui_report.message("Oops.  It seems something has gone wrong. " +
                                  "The error has been reported to the fine " +
                                  "folks at Zulip, but, in the mean time, " +
                                  "please try reloading the page.",
                                  $("#home-error"), "alert-error");
            }
        },
        error: function () {
            if (opts.show_ui_msg && ui_report !== undefined) {
                ui_report.message("Oops.  It seems something has gone wrong. " +
                                  "Please try reloading the page.",
                                  $("#home-error"), "alert-error");
            }
        },
    });

    if (page_params.save_stacktraces) {
        // Save the stacktrace so it can be examined even in
        // development servers.  (N.B. This assumes you have set DEBUG
        // = False on your development server, or else this code path
        // won't execute to begin with -- useful for testing
        // (un)minification.)
        window.last_stacktrace = stack;
    }
}

function BlueslipError(msg, more_info) {
    // One can't subclass Error normally so we have to play games
    // with setting __proto__
    const self = new Error(msg);
    self.name = "BlueslipError";

    // Indirect access to __proto__ keeps jslint quiet
    const proto = '__proto__';
    self[proto] = BlueslipError.prototype;

    if (more_info !== undefined) {
        self.more_info = more_info;
    }
    return self;
}

BlueslipError.prototype = Object.create(Error.prototype);

exports.exception_msg = function blueslip_exception_msg(ex) {
    let message = ex.message;
    if (ex.hasOwnProperty('fileName')) {
        message += " at " + ex.fileName;
        if (ex.hasOwnProperty('lineNumber')) {
            message += ":" + ex.lineNumber;
        }
    }
    return message;
};

$(window).on('error', function (event) {
    const ex = event.originalEvent.error;
    if (!ex || ex instanceof BlueslipError) {
        return;
    }
    const message = exports.exception_msg(ex);
    report_error(message, ex.stack);
});

function build_arg_list(msg, more_info) {
    const args = [msg];
    if (more_info !== undefined) {
        args.push("\nAdditional information: ", more_info);
    }
    return args;
}

exports.debug = function blueslip_debug(msg, more_info) {
    const args = build_arg_list(msg, more_info);
    logger.debug.apply(logger, args);
};

exports.log = function blueslip_log(msg, more_info) {
    const args = build_arg_list(msg, more_info);
    logger.log.apply(logger, args);
};

exports.info = function blueslip_info(msg, more_info) {
    const args = build_arg_list(msg, more_info);
    logger.info.apply(logger, args);
};

exports.warn = function blueslip_warn(msg, more_info) {
    const args = build_arg_list(msg, more_info);
    logger.warn.apply(logger, args);
    if (page_params.debug_mode) {
        console.trace();
    }
};

exports.error = function blueslip_error(msg, more_info, stack) {
    if (stack === undefined) {
        stack = Error().stack;
    }
    const args = build_arg_list(msg, more_info);
    logger.error.apply(logger, args);
    report_error(msg, stack, {more_info: more_info});

    if (page_params.debug_mode) {
        throw new BlueslipError(msg, more_info);
    }
};

exports.fatal = function blueslip_fatal(msg, more_info) {
    report_error(msg, Error().stack, {more_info: more_info});
    throw new BlueslipError(msg, more_info);
};

exports.timings = new Map();

exports.start_timing = function (label) {
    const t1 = performance.now();

    return function () {
        const t2 = performance.now();
        const elapsed = t2 - t1;
        exports.timings.set(label, elapsed);
    };
};

// Produces an easy-to-read preview on an HTML element.  Currently
// only used for including in error report emails; be sure to discuss
// with other developers before using it in a user-facing context
// because it is not XSS-safe.
exports.preview_node = function (node) {
    if (node.constructor === jQuery) {
        node = node[0];
    }

    const tag = node.tagName.toLowerCase();
    const className = node.className.length ? node.className : false;
    const id = node.id.length ? node.id : false;

    const node_preview = "<" + tag +
       (id ? " id='" + id + "'" : "") +
       (className ? " class='" + className + "'" : "") +
       "></" + tag + ">";

    return node_preview;
};

window.blueslip = exports;
<commit_msg>blueslip: Use modern spread arguments syntax.<commit_after>/* eslint-disable no-console */

// System documented in https://zulip.readthedocs.io/en/latest/subsystems/logging.html

// This must be included before the first call to $(document).ready
// in order to be able to report exceptions that occur during their
// execution.

const blueslip_stacktrace = require("./blueslip_stacktrace");

if (Error.stackTraceLimit !== undefined) {
    Error.stackTraceLimit = 100000;
}

function Logger() {
    this._memory_log = [];
}

Logger.prototype = (function () {
    function pad(num, width) {
        let ret = num.toString();
        while (ret.length < width) {
            ret = "0" + ret;
        }
        return ret;
    }

    function make_logger_func(name) {
        return function Logger_func(...args) {
            const now = new Date();
            const date_str =
                now.getUTCFullYear() + '-' +
                pad(now.getUTCMonth() + 1, 2) + '-' +
                pad(now.getUTCDate(), 2) + ' ' +
                pad(now.getUTCHours(), 2) + ':' +
                pad(now.getUTCMinutes(), 2) + ':' +
                pad(now.getUTCSeconds(), 2) + '.' +
                pad(now.getUTCMilliseconds(), 3) + ' UTC';

            const str_args = args.map(x => typeof x === "object" ? JSON.stringify(x) : x);

            const log_entry = date_str + " " + name.toUpperCase() +
                ': ' + str_args.join("");
            this._memory_log.push(log_entry);

            // Don't let the log grow without bound
            if (this._memory_log.length > 1000) {
                this._memory_log.shift();
            }

            if (console[name] !== undefined) {
                return console[name](...args);
            }
            return;
        };
    }

    const proto = {
        get_log: function Logger_get_log() {
            return this._memory_log;
        },
    };

    const methods = ['debug', 'log', 'info', 'warn', 'error'];
    let i;
    for (i = 0; i < methods.length; i += 1) {
        proto[methods[i]] = make_logger_func(methods[i]);
    }

    return proto;
}());

const logger = new Logger();

exports.get_log = function blueslip_get_log() {
    return logger.get_log();
};

const reported_errors = {};
const last_report_attempt = {};

function report_error(msg, stack, opts) {
    opts = _.extend({show_ui_msg: false}, opts);

    if (stack === undefined) {
        stack = 'No stacktrace available';
    }

    if (page_params.debug_mode) {
        // In development, we display blueslip errors in the web UI,
        // to make them hard to miss.
        blueslip_stacktrace.display_stacktrace(msg, stack);
    }

    const key = ':' + msg + stack;
    if (reported_errors.hasOwnProperty(key)
        || last_report_attempt.hasOwnProperty(key)
            // Only try to report a given error once every 5 minutes
            && Date.now() - last_report_attempt[key] <= 60 * 5 * 1000) {
        return;
    }

    last_report_attempt[key] = Date.now();

    // TODO: If an exception gets thrown before we setup ajax calls
    // to include the CSRF token, our ajax call will fail.  The
    // elegant thing to do in that case is to either wait until that
    // setup is done or do it ourselves and then retry.
    $.ajax({
        type: 'POST',
        url: '/json/report/error',
        dataType: 'json',
        data: {
            message: msg,
            stacktrace: stack,
            ui_message: opts.show_ui_msg,
            more_info: JSON.stringify(opts.more_info),
            href: window.location.href,
            user_agent: window.navigator.userAgent,
            log: logger.get_log().join("\n"),
        },
        timeout: 3 * 1000,
        success: function () {
            reported_errors[key] = true;
            if (opts.show_ui_msg && ui_report !== undefined) {
                // There are a few races here (and below in the error
                // callback):
                // 1) The ui_report module or something it requires might
                //    not have been compiled or initialized yet.
                // 2) The DOM might not be ready yet and so fetching
                //    the #home-error div might fail.

                // For (1) we just don't show the message if the ui
                // hasn't been loaded yet.  The user will probably
                // get another error once it does.  We can't solve
                // (2) by using $(document).ready because the
                // callback never gets called (I think what's going
                // on here is if the exception was raised by a
                // function that was called as a result of the DOM
                // becoming ready then the internal state of jQuery
                // gets messed up and our callback never gets
                // invoked).  In any case, it will pretty clear that
                // something is wrong with the page and the user will
                // probably try to reload anyway.
                ui_report.message("Oops.  It seems something has gone wrong. " +
                                  "The error has been reported to the fine " +
                                  "folks at Zulip, but, in the mean time, " +
                                  "please try reloading the page.",
                                  $("#home-error"), "alert-error");
            }
        },
        error: function () {
            if (opts.show_ui_msg && ui_report !== undefined) {
                ui_report.message("Oops.  It seems something has gone wrong. " +
                                  "Please try reloading the page.",
                                  $("#home-error"), "alert-error");
            }
        },
    });

    if (page_params.save_stacktraces) {
        // Save the stacktrace so it can be examined even in
        // development servers.  (N.B. This assumes you have set DEBUG
        // = False on your development server, or else this code path
        // won't execute to begin with -- useful for testing
        // (un)minification.)
        window.last_stacktrace = stack;
    }
}

function BlueslipError(msg, more_info) {
    // One can't subclass Error normally so we have to play games
    // with setting __proto__
    const self = new Error(msg);
    self.name = "BlueslipError";

    // Indirect access to __proto__ keeps jslint quiet
    const proto = '__proto__';
    self[proto] = BlueslipError.prototype;

    if (more_info !== undefined) {
        self.more_info = more_info;
    }
    return self;
}

BlueslipError.prototype = Object.create(Error.prototype);

exports.exception_msg = function blueslip_exception_msg(ex) {
    let message = ex.message;
    if (ex.hasOwnProperty('fileName')) {
        message += " at " + ex.fileName;
        if (ex.hasOwnProperty('lineNumber')) {
            message += ":" + ex.lineNumber;
        }
    }
    return message;
};

$(window).on('error', function (event) {
    const ex = event.originalEvent.error;
    if (!ex || ex instanceof BlueslipError) {
        return;
    }
    const message = exports.exception_msg(ex);
    report_error(message, ex.stack);
});

function build_arg_list(msg, more_info) {
    const args = [msg];
    if (more_info !== undefined) {
        args.push("\nAdditional information: ", more_info);
    }
    return args;
}

exports.debug = function blueslip_debug(msg, more_info) {
    const args = build_arg_list(msg, more_info);
    logger.debug.apply(logger, args);
};

exports.log = function blueslip_log(msg, more_info) {
    const args = build_arg_list(msg, more_info);
    logger.log.apply(logger, args);
};

exports.info = function blueslip_info(msg, more_info) {
    const args = build_arg_list(msg, more_info);
    logger.info.apply(logger, args);
};

exports.warn = function blueslip_warn(msg, more_info) {
    const args = build_arg_list(msg, more_info);
    logger.warn.apply(logger, args);
    if (page_params.debug_mode) {
        console.trace();
    }
};

exports.error = function blueslip_error(msg, more_info, stack) {
    if (stack === undefined) {
        stack = Error().stack;
    }
    const args = build_arg_list(msg, more_info);
    logger.error.apply(logger, args);
    report_error(msg, stack, {more_info: more_info});

    if (page_params.debug_mode) {
        throw new BlueslipError(msg, more_info);
    }
};

exports.fatal = function blueslip_fatal(msg, more_info) {
    report_error(msg, Error().stack, {more_info: more_info});
    throw new BlueslipError(msg, more_info);
};

exports.timings = new Map();

exports.start_timing = function (label) {
    const t1 = performance.now();

    return function () {
        const t2 = performance.now();
        const elapsed = t2 - t1;
        exports.timings.set(label, elapsed);
    };
};

// Produces an easy-to-read preview on an HTML element.  Currently
// only used for including in error report emails; be sure to discuss
// with other developers before using it in a user-facing context
// because it is not XSS-safe.
exports.preview_node = function (node) {
    if (node.constructor === jQuery) {
        node = node[0];
    }

    const tag = node.tagName.toLowerCase();
    const className = node.className.length ? node.className : false;
    const id = node.id.length ? node.id : false;

    const node_preview = "<" + tag +
       (id ? " id='" + id + "'" : "") +
       (className ? " class='" + className + "'" : "") +
       "></" + tag + ">";

    return node_preview;
};

window.blueslip = exports;
<|endoftext|>


<commit_before>var isDoingSelect=0;
var delayUpdateToolip;

/*
    Draws the background circles, labels and text for the scatter chart
*/
function decorateNominate(oc,data) {

        var scmargins = oc.margins()
	var width = oc.width();
        var height = oc.height();
	var margin = scmargins['left']-scmargins['right'];
	var marginCircle = scmargins['right']; // Distance of the main circle to the axis
	var tickLength = 15;

	// Calculate ellipse attrs
	var radiusX = (width - margin)/2 - marginCircle;
        var radiusY = nomDWeight*radiusX;
	var circleCenter = { "x": (width + margin)/2, "y": (height - margin)/2 };

	// Select the base SVG
	var ocSVG = d3.select(oc.g()[0][0]);

	// Remove everything that's an axis.
	ocSVG.selectAll(".axis").remove();

	// Place bg stuff in SVG tree in front of .chart-body scatter points
	var svgbg = ocSVG.insert("g",".chart-body");
        var gg = svgbg.append("g").attr("id","scatter-background");

	// X-axis
        var xAxisMin = circleCenter.x - radiusX;
        var xAxisMax = circleCenter.x + radiusX;
        var xAxisLen = xAxisMax - xAxisMin;
        var yDimPos = circleCenter.y + radiusY + 10;

	gg.append('polyline')
		.attr("class", "scatter-axis")
		.attr("points", sprintf("%d,%d %d,%d %d,%d %d,%d",
				xAxisMin, yDimPos + tickLength,
				xAxisMin, yDimPos,
				xAxisMax, yDimPos,
				xAxisMax, yDimPos + tickLength));

	gg.append('text')
            .text("Liberal")
	    .attr("x", xAxisMin + 0.2*xAxisLen)
            .attr("y", yDimPos + 20)
	    .attr("style","text-anchor:middle");
	gg.append('text')
            .text("Conservative")
            .attr("x", xAxisMin + 0.8*xAxisLen)
            .attr("y", yDimPos + 20)
            .attr("style","text-anchor:middle");
	gg.append('text')
	     .text("DW-Nominate Dimension 1: Economic/Redistributive")
             .attr("x", xAxisMin + xAxisLen/2).attr("y", yDimPos + 40)
	     .attr("style","text-anchor:middle");
	// End X axis.

	// Y-axis
        var yAxisMin = circleCenter.y - radiusY;
        var yAxisMax = circleCenter.y + radiusY;
        var yAxisLen = yAxisMax - yAxisMin;

	gg.append('polyline')
	     .attr("class","scatter-axis")
	     .attr("points", sprintf("%d,%d  %d,%d  %d,%d  %d,%d",
	   			     margin, yAxisMin,
				     margin + tickLength, yAxisMin,
				     margin + tickLength, yAxisMax,
				     margin, yAxisMax));
	gg.append('text')
	     .text("Conservative")
	     .attr("x", 40)
             .attr("y", yAxisMin + 0.2*yAxisLen)
             .attr("style","text-anchor:middle")
             .attr("transform", sprintf("rotate(-90 40 %d)", yAxisMin + 0.2*yAxisLen));
	gg.append('text')
             .text("Liberal")
	     .attr("x", 40)
             .attr("y", yAxisMin + 0.8*yAxisLen)
             .attr("style","text-anchor:middle")
             .attr("transform", sprintf("rotate(-90 40 %d)", yAxisMin + 0.8*yAxisLen));
	gg.append('text')
             .text("DW-Nominate Dimension 2: Social/Racial")
             .attr("x",20)
             .attr("y", yAxisMin + yAxisLen/2)
             .attr("style","text-anchor:middle")
             .attr("transform", sprintf("rotate(-90 20 %d)", yAxisMin + yAxisLen/2));
	// End Y axis

	// Add yea and nay locations to the plot on top of the dots

	// Problem is that with Y/N on top we can't select point under/near the Y/N
	// Need a way to insert after the dots but before the brush. Putting the Y/N group right
	// before the brush group does it. --JBL

	var ggg = ocSVG.insert("g",".brush");

                plotCut=0;
                gg
  		   .append("ellipse")
                        .attr("stroke","blue")
                        .attr("stroke-width","1px")
                        .attr("fill","none")
			.attr("rx", radiusX)
                        .attr("ry", radiusY)
			.attr("cx", circleCenter.x)
		        .attr("cy", circleCenter.y);

        var plotCut = 1;
        if (data.rollcalls==undefined || data.rollcalls[0].nominate==undefined ||
            data.rollcalls[0].nominate.x==undefined || data.rollcalls[0].nominate.pre < 0.05)
        {
                plotCut=0;
                gg
  		   .append("ellipse")
                        .attr("stroke","black")
                        .attr("stroke-width","1px")
                        .attr("fill","none")
			.attr("rx", radiusX)
                        .attr("ry", radiusY)
			.attr("cx", circleCenter.x)
		        .attr("cy", circleCenter.y);
        }
        else
        {
            if(data.rollcalls[0].congress==0) var vn = data.rollcalls[0].nominate.imputed;
            else var vn = data.rollcalls[0].nominate;
            //console.log(vn);
        }

	if (plotCut && vn.mid[0] * vn.mid[0] != 0) { // Only drawn if there is a cutline!
	    var hmTranslate = {x:scmargins['left'],y:oc.height()/2-radiusY-scmargins['top']};
            var gggg = gg.append("g")
                          .attr("id","heat-map")
                          .attr("transform","translate(" + hmTranslate.x + "," + hmTranslate.y+ ")");

	    nominateHeatmap(gggg, vn.mid[0], vn.mid[1], vn.spread[0], vn.spread[1],
	    		    nomBeta, nomDWeight, 2*radiusX, 2*radiusY, 30, ["#FFFFFF","#ffffcc"]);

	       // Calculate where the YN text axis goes.
	       function closestpt(vn) {
		    var b = vn.slope;
                    var angle = Math.atan( (vn.spread[1]*nomDWeight)/(vn.spread[0]) );
		    var a = vn.mid[1] - b*vn.mid[0];
		    var xstar = -b*a/(1+b*b);
		    var obj = {
			angle: angle,
			b: b,
			a: a,
		        xstar: xstar,
			ystar: b*xstar + a,
			offsetX: 0.05*Math.cos(angle)*Math.sign(vn.spread[0]),
			offsetY: (vn.slope>0?1:-1)*0.05*Math.sin(angle)*Math.sign(vn.spread[1])/nomDWeight
		    }
		    return(obj);
		}

                var ynp = closestpt(vn);

 		var ynpts =    [circleCenter.x + radiusX*(ynp.xstar+ynp.offsetX),
				circleCenter.y - radiusY*(ynp.ystar-ynp.offsetY),
				circleCenter.x + radiusX*(ynp.xstar-ynp.offsetX),
				circleCenter.y - radiusY*(ynp.ystar+ynp.offsetY)];

	        var angle = 57.29578*ynp.angle;

		// This is a hack based on what quadrant the text angle is in.
		var cs = (angle>0?1:0) + 2*(vn.spread[0]>0?1:0);
		switch( cs )
		{
			case 0: angle = 90-angle; break;
			case 1: angle = 90-angle; break;
			case 2: angle = 270-angle; break;
			case 3: angle = -90-angle; break;
		}

		// Plot the Y text using ynpts[2], [3] and rotating by the angle above.
		ggg.append('text').text('Y')
			.attr("class","yeanay")
			.attr("x", ynpts[2])
			.attr("y", ynpts[3])
			.attr("transform",sprintf("rotate(%d %d %d)", angle, ynpts[2], ynpts[3]));

		// Plot the N text using ynpts[0], [1], and rotating by the angle above + 180
		ggg.append('text').text('N')
			.attr("class","yeanay")
			.attr("x", ynpts[0])
			.attr("y", ynpts[1])
			.attr("transform",sprintf("rotate(%d %d %d)", 180+angle, ynpts[0], ynpts[1]));

		// Fit box regardless if cutline exists
		ggg.append('text').text(sprintf("PRE: %4.2f", (vn.pre == null || isNaN(vn.pre) || vn.pre=="") ? 0 : vn.pre))
		    .attr("class", "fitbox")
		    .attr("x", xAxisMax - 75)
		    .attr("y", yAxisMax - 5);

		ggg.append('text').text(sprintf("Classified: %4.2f", (vn.classified == null || isNaN(vn.classified) || vn.classified=="") ? 0 : vn.classified ))
		    .attr("class", "fitbox")
		    .attr("x", xAxisMax - 75)
		    .attr("y", yAxisMax - 25);
	}
	else
	{
		var nomData = data.rollcalls[0].nominate;
		if(nomData != undefined && nomData.pre != undefined)
		{
			ggg.append('text').text("Non-Ideological Vote")
				.attr("class","fitbox").attr("x", xAxisMax - 110)
				.attr("y", yAxisMax - 0);
		}
		else
		{
			ggg.append('text').text("NOMINATE not yet computed")
				.attr("class","fitbox").attr("x", xAxisMax - 75)
				.attr("y", yAxisMax - 25);
		}
		//console.log(data.rollcalls[0].nominate);
	}

        // Tooltip for Prob(yea)
        if (plotCut==1) { // only tooltip if cutline exists...

            // Collecting info to find vote prob for heat map
	    if(!$(".hm_tooltip").length) // Make sure we don't already have a tooltip
	    {
		$("

")
			.attr("id","hm_tooltip")
			.css("z-index",10).css("visibility","hidden")
			.css("opacity",0.8).appendTo(document.body);
	    }
            var yunits = radiusY;
	    var xunits = radiusX;
            var brush = ocSVG.select(".brush")
	    brush.on("mouseover",function() { if(!isDoingSelect) { $("#hm_tooltip").css("visibility","visible").css("opacity",0.8); }});
            brush.on("mousemove",function() {
			var ypdiv = $("#hm_tooltip");
                        var cx = circleCenter.x-hmTranslate.x,
                            cy = circleCenter.y-hmTranslate.y,
                            x  =  (d3.mouse(this)[0] - cx)/xunits,
                            y  = -(d3.mouse(this)[1] - cy)/yunits,
			    yeaProb = nomProbYea(x,y,vn.mid[0],vn.mid[1],vn.spread[0],
                                                     vn.spread[1],nomDWeight,nomBeta);
	                    if ((x*x+y*y)<1.0) {
                                if(yeaProb>0.99) {
        		           dispProb = ">0.99"
                                } else if(yeaProb<0.01) {
                                   dispProb = "<0.01"
                                } else {
                                   dispProb = "=" + Math.round(100*yeaProb)/100;
                                }
				if(!isDoingSelect) ypdiv.css("visibility","visible");
				ypdiv.html("Pr(Yea)" + dispProb).css("z-index",10)
				   .css("left",d3.event.pageX+"px")
				   .css("top",(d3.event.pageY-28)+"px");
			    }
	                    else { // Cursor is on nominate plot, but not in the Oval
                                ypdiv.css("visibility","hidden");
			    }
		            //console.log("x=" + d3.mouse(this)[0] + ",y=" + d3.mouse(this)[1]);
		            //console.log("x=" + x + ",y=" + y);
                      });
            // Don't show tooltip if user is making a range selection
            brush.on("mousedown",function() { $("#hm_tooltip").css("visibility","hidden"); isDoingSelect=1; });
            brush.on("mouseup",function() { $("#hm_tooltip").css("visibility","visible"); isDoingSelect=0; });
	    brush.on("mouseout", function() { $("#hm_tooltip").css("visibility","hidden"); });
	};
}

<commit_msg>Very preliminary legend tooltip<commit_after>var isDoingSelect=0;
var delayUpdateToolip;

/*
    Draws the background circles, labels and text for the scatter chart
*/
function decorateNominate(oc,data) {

        var scmargins = oc.margins()
	var width = oc.width();
        var height = oc.height();
	var margin = scmargins['left']-scmargins['right'];
	var marginCircle = scmargins['right']; // Distance of the main circle to the axis
	var tickLength = 15;

	// Calculate ellipse attrs
	var radiusX = (width - margin)/2 - marginCircle;
        var radiusY = nomDWeight*radiusX;
	var circleCenter = { "x": (width + margin)/2, "y": (height - margin)/2 };

	// Select the base SVG
	var ocSVG = d3.select(oc.g()[0][0]);

	// Remove everything that's an axis.
	ocSVG.selectAll(".axis").remove();

	// Place bg stuff in SVG tree in front of .chart-body scatter points
	var svgbg = ocSVG.insert("g",".chart-body");
        var gg = svgbg.append("g").attr("id","scatter-background");

	// X-axis
        var xAxisMin = circleCenter.x - radiusX;
        var xAxisMax = circleCenter.x + radiusX;
        var xAxisLen = xAxisMax - xAxisMin;
        var yDimPos = circleCenter.y + radiusY + 10;

	gg.append('polyline')
		.attr("class", "scatter-axis")
		.attr("points", sprintf("%d,%d %d,%d %d,%d %d,%d",
				xAxisMin, yDimPos + tickLength,
				xAxisMin, yDimPos,
				xAxisMax, yDimPos,
				xAxisMax, yDimPos + tickLength));

	gg.append('text')
            .text("Liberal")
	    .attr("x", xAxisMin + 0.2*xAxisLen)
            .attr("y", yDimPos + 20)
	    .attr("style","text-anchor:middle");
	gg.append('text')
            .text("Conservative")
            .attr("x", xAxisMin + 0.8*xAxisLen)
            .attr("y", yDimPos + 20)
            .attr("style","text-anchor:middle");
	gg.append('text')
	     .text("DW-Nominate Dimension 1: Economic/Redistributive")
             .attr("x", xAxisMin + xAxisLen/2).attr("y", yDimPos + 40)
	     .attr("style","text-anchor:middle");
	// End X axis.

	// Y-axis
        var yAxisMin = circleCenter.y - radiusY;
        var yAxisMax = circleCenter.y + radiusY;
        var yAxisLen = yAxisMax - yAxisMin;

	gg.append('polyline')
	     .attr("class","scatter-axis")
	     .attr("points", sprintf("%d,%d  %d,%d  %d,%d  %d,%d",
	   			     margin, yAxisMin,
				     margin + tickLength, yAxisMin,
				     margin + tickLength, yAxisMax,
				     margin, yAxisMax));
	gg.append('text')
	     .text("Conservative")
	     .attr("x", 40)
             .attr("y", yAxisMin + 0.2*yAxisLen)
             .attr("style","text-anchor:middle")
             .attr("transform", sprintf("rotate(-90 40 %d)", yAxisMin + 0.2*yAxisLen));
	gg.append('text')
             .text("Liberal")
	     .attr("x", 40)
             .attr("y", yAxisMin + 0.8*yAxisLen)
             .attr("style","text-anchor:middle")
             .attr("transform", sprintf("rotate(-90 40 %d)", yAxisMin + 0.8*yAxisLen));
	gg.append('text')
             .text("DW-Nominate Dimension 2: Social/Racial")
             .attr("x",20)
             .attr("y", yAxisMin + yAxisLen/2)
             .attr("style","text-anchor:middle")
             .attr("transform", sprintf("rotate(-90 20 %d)", yAxisMin + yAxisLen/2));
	// End Y axis

	// Add yea and nay locations to the plot on top of the dots

	// Problem is that with Y/N on top we can't select point under/near the Y/N
	// Need a way to insert after the dots but before the brush. Putting the Y/N group right
	// before the brush group does it. --JBL

	var ggg = ocSVG.insert("g",".brush");

                plotCut=0;
                gg
  		   .append("ellipse")
                        .attr("stroke","blue")
                        .attr("stroke-width","1px")
                        .attr("fill","none")
			.attr("rx", radiusX)
                        .attr("ry", radiusY)
			.attr("cx", circleCenter.x)
		        .attr("cy", circleCenter.y);

        var plotCut = 1;
        if (data.rollcalls==undefined || data.rollcalls[0].nominate==undefined ||
            data.rollcalls[0].nominate.x==undefined || data.rollcalls[0].nominate.pre < 0.05)
        {
                plotCut=0;
                gg
  		   .append("ellipse")
                        .attr("stroke","black")
                        .attr("stroke-width","1px")
                        .attr("fill","none")
			.attr("rx", radiusX)
                        .attr("ry", radiusY)
			.attr("cx", circleCenter.x)
		        .attr("cy", circleCenter.y);
        }
        else
        {
            if(data.rollcalls[0].congress==0) var vn = data.rollcalls[0].nominate.imputed;
            else var vn = data.rollcalls[0].nominate;
            //console.log(vn);
        }

	if (plotCut && vn.mid[0] * vn.mid[0] != 0) { // Only drawn if there is a cutline!
	    var hmTranslate = {x:scmargins['left'],y:oc.height()/2-radiusY-scmargins['top']};
            var gggg = gg.append("g")
                          .attr("id","heat-map")
                          .attr("transform","translate(" + hmTranslate.x + "," + hmTranslate.y+ ")");

	    nominateHeatmap(gggg, vn.mid[0], vn.mid[1], vn.spread[0], vn.spread[1],
	    		    nomBeta, nomDWeight, 2*radiusX, 2*radiusY, 30, ["#FFFFFF","#ffffcc"]);

	       // Calculate where the YN text axis goes.
	       function closestpt(vn) {
		    var b = vn.slope;
                    var angle = Math.atan( (vn.spread[1]*nomDWeight)/(vn.spread[0]) );
		    var a = vn.mid[1] - b*vn.mid[0];
		    var xstar = -b*a/(1+b*b);
		    var obj = {
			angle: angle,
			b: b,
			a: a,
		        xstar: xstar,
			ystar: b*xstar + a,
			offsetX: 0.05*Math.cos(angle)*Math.sign(vn.spread[0]),
			offsetY: (vn.slope>0?1:-1)*0.05*Math.sin(angle)*Math.sign(vn.spread[1])/nomDWeight
		    }
		    return(obj);
		}

                var ynp = closestpt(vn);

 		var ynpts =    [circleCenter.x + radiusX*(ynp.xstar+ynp.offsetX),
				circleCenter.y - radiusY*(ynp.ystar-ynp.offsetY),
				circleCenter.x + radiusX*(ynp.xstar-ynp.offsetX),
				circleCenter.y - radiusY*(ynp.ystar+ynp.offsetY)];

	        var angle = 57.29578*ynp.angle;

		// This is a hack based on what quadrant the text angle is in.
		var cs = (angle>0?1:0) + 2*(vn.spread[0]>0?1:0);
		switch( cs )
		{
			case 0: angle = 90-angle; break;
			case 1: angle = 90-angle; break;
			case 2: angle = 270-angle; break;
			case 3: angle = -90-angle; break;
		}

		// Plot the Y text using ynpts[2], [3] and rotating by the angle above.
		ggg.append('text').text('Y')
			.attr("class","yeanay")
			.attr("x", ynpts[2])
			.attr("y", ynpts[3])
			.attr("transform",sprintf("rotate(%d %d %d)", angle, ynpts[2], ynpts[3]));

		// Plot the N text using ynpts[0], [1], and rotating by the angle above + 180
		ggg.append('text').text('N')
			.attr("class","yeanay")
			.attr("x", ynpts[0])
			.attr("y", ynpts[1])
			.attr("transform",sprintf("rotate(%d %d %d)", 180+angle, ynpts[0], ynpts[1]));

		// Fit box regardless if cutline exists
		ggg.append('text').text(sprintf("PRE: %4.2f", (vn.pre == null || isNaN(vn.pre) || vn.pre=="") ? 0 : vn.pre))
		    .attr("class", "fitbox")
		    .attr("x", xAxisMax - 75)
		    .attr("y", yAxisMax - 5);

		ggg.append('text').text(sprintf("Classified: %4.2f", (vn.classified == null || isNaN(vn.classified) || vn.classified=="") ? 0 : vn.classified ))
		    .attr("class", "fitbox")
		    .attr("x", xAxisMax - 75)
		    .attr("y", yAxisMax - 25);

		var legendType=1;
	}
	else
	{
		var nomData = data.rollcalls[0].nominate;
		if(nomData != undefined && nomData.pre != undefined)
		{
			ggg.append('text').text("Non-Ideological Vote")
				.attr("class","fitbox").attr("x", xAxisMax - 110)
				.attr("y", yAxisMax - 0);
			var legendType=2;
		}
		else
		{
			ggg.append('text').text("NOMINATE not yet computed")
				.attr("class","fitbox").attr("x", xAxisMax - 75)
				.attr("y", yAxisMax - 25);
			var legendType=3;
		}
		//console.log(data.rollcalls[0].nominate);
	}

        // Tooltip for Prob(yea)
        if (plotCut==1) { // only tooltip if cutline exists...

            // Collecting info to find vote prob for heat map
	    if(!$(".hm_tooltip").length) // Make sure we don't already have a tooltip
	    {
		$("

")
			.attr("id","hm_tooltip")
			.css("z-index",10).css("visibility","hidden")
			.css("opacity",0.8).appendTo(document.body);
	    }
            var yunits = radiusY;
	    var xunits = radiusX;
            var brush = ocSVG.select(".brush")
	    brush.on("mouseover",function() { if(!isDoingSelect) { $("#hm_tooltip").css("visibility","visible").css("opacity",0.8); }});
            brush.on("mousemove",function() {
			var ypdiv = $("#hm_tooltip");
                        var cx = circleCenter.x-hmTranslate.x,
                            cy = circleCenter.y-hmTranslate.y,
                            x  =  (d3.mouse(this)[0] - cx)/xunits,
                            y  = -(d3.mouse(this)[1] - cy)/yunits,
			    yeaProb = nomProbYea(x,y,vn.mid[0],vn.mid[1],vn.spread[0],
                                                     vn.spread[1],nomDWeight,nomBeta);
	                    if ((x*x+y*y)<1.0) {
                                if(yeaProb>0.99) {
        		           dispProb = ">0.99"
                                } else if(yeaProb<0.01) {
                                   dispProb = "<0.01"
                                } else {
                                   dispProb = "=" + Math.round(100*yeaProb)/100;
                                }
				if(!isDoingSelect) ypdiv.css("visibility","visible");
				ypdiv.html("Pr(Yea)" + dispProb).css("z-index",10)
				   .css("left",d3.event.pageX+"px")
				   .css("top",(d3.event.pageY-28)+"px");
			    }
	                    else if(x>0.81&&y<=-0.77)
			    {
				if(!isDoingSelect) ypdiv.css("visibility","visible");
				ypdiv.html("PRE: How much our classification improves on a guess.
Classified: The percentage of votes that we classify correctly.");
				ypdiv.css("z-index",10)
				   .css("left",d3.event.pageX+"px")
				   .css("top",(d3.event.pageY-28)+"px");
			    }
			    else
			    {
 				// Cursor is on nominate plot, but not in the Oval
				ypdiv.css("visibility","hidden");
			    }
		            //console.log("x=" + d3.mouse(this)[0] + ",y=" + d3.mouse(this)[1]);
		            //console.log("x=" + x + ",y=" + y);
                      });
            // Don't show tooltip if user is making a range selection
            brush.on("mousedown",function() { $("#hm_tooltip").css("visibility","hidden"); isDoingSelect=1; });
            brush.on("mouseup",function() { $("#hm_tooltip").css("visibility","visible"); isDoingSelect=0; });
	    brush.on("mouseout", function() { $("#hm_tooltip").css("visibility","hidden"); });
	};
}

<|endoftext|>


<commit_before>var tooltipIdeology = $("

").addClass("d3-tip").css("visibility","hidden").attr("id","tooltipIdeology").appendTo(document.body);

// From stackoverflow response, who borrowed it from Shopify--simple ordinal suffix.
var myLat, myLong;
var slowTimer;
var globalEnableLocation = 0;

function getGetOrdinal(n) {
    var s=["th","st","nd","rd"],
    v=n%100;
    return n+(s[(v-20)%10]||s[v]||s[0]);
 }

function congToYears(n) { return [1787+(2*n), 1787+(2*n)+2]; }

function lzPad(t)
{
	if(parseInt(t)<10) { return "0"+t; }
	else { return t; }
}

function loadText(t)
{
	$("#addressInput").val(t);
	latLongWrapper();
}

function resetResults()
{
	$("#resultsMembers").hide().html("");
	$("#addressCorrected").html("");
	$("#google_map").html("");
	$("#warnings").hide().html("");
	$("#loadProgress").hide().html("");
	$("#perm_link_holder").html("");
}

	$(document).ready(function(){
		$("#addressInput").on("focus",function() {
			if($("#addressInput").val()=="MY LOCATION") { $("#addressInput").val(""); }
		});
		$("#submit-address-form").submit(function(event)
		{
			event.preventDefault();
			latLongWrapper();
		});
		$("#submit-geolocation").click(function(event)
		{
			event.preventDefault();
			getLocation();
		});
		if($("#cachedLat").val()) { myLat = $("#cachedLat").val(); }
		if($("#cachedLong").val()) { myLong = $("#cachedLong").val(); }
		if($("#addressInput").val()) { setTimeout(function(){latLongWrapper();},1000); }
  	        $("ul#testData li").on("click",function(){ console.log($(this).val()); loadText(this.innerHTML); });
		$("ul#notableExamples li").on("click",function(){ console.log($(this).val()); loadText(this.innerHTML); });
	});

	if(navigator.geolocation)
	{
		globalEnableLocation=1;
		console.log('html5 location support.');
		$("#locationButton").show();
		function success(position)
		{
			clearTimeout(slowTimer);
			console.log(position.coords);
			myLat = position.coords.latitude;
			myLong = position.coords.longitude;
			$("#cachedLat").val(myLat);
			$("#cachedLong").val(myLong);
			$("#addressInput").val("MY LOCATION");
			resetResults();
			$("#loadProgress").show().html("Loading... Location matched, looking up historical representatives... ");
			doMembers(myLat, myLong);
		}
		function error()
		{
			clearTimeout(slowTimer);
			resetResults();
			$("#warnings").html("").show();
			var errorDiv = $("

").addClass("alert alert-danger").html("Error: Error looking up your location. Most commonly this occurs because you are having internet connection trouble or you have privacy settings designed to block web access to your location.");
			errorDiv.appendTo($("#warnings"));

			return;
		}
		function getLocation(event)
		{
			console.log(event);
			console.log('I AM HERE, IN THE GETLOCATION FUNCTION');
			resetResults();
			$("#loadProgress").show().html("Loading... Looking up your current location... ");
			slowTimer = setTimeout(function() { $("#loadProgress").html($("#loadProgress").html()+"
This process seems to be taking an unusually long time to complete. The delay is related to your internet connection, router, or web browser and is not connected to our server."); }, 5000);
			navigator.geolocation.getCurrentPosition(success, error);
		}
		$("#geolocationTutorial").show();
	}

	function latLongWrapper()
	{
		console.log('I AM HERE, IN THE ADDRESS LOOKUP.');
		resetResults();
		if($("#addressInput").val()=="")
		{
			$("#warnings").html("").show();
			var errorDiv = $("

").addClass("alert alert-danger").html("Error: No address specified.");
			errorDiv.appendTo($("#warnings"));
			return;
		}
		else if($("#addressInput").val()=="MY LOCATION" && globalEnableLocation && $("#cachedLat").val() && $("#cachedLong").val()) { doMembers(parseFloat($("#cachedLat").val()), parseFloat($("#cachedLong").val())); }
		else if($("#addressInput").val()=="MY LOCATION")
		{
			$("#warnings").html("").show();
			var errorDiv = $("

").addClass("alert alert-danger").html("Error: Error accessing your location, either due to internet connectivity or privacy settings. Please manually type an address to continue.");
			errorDiv.appendTo($("#warnings"));
		}
		else
		{
			$("#loadProgress").show().html("Loading... Matching address to map coordinates... ");
			setTimeout(doLatLong, 20);
		}
	}

	function doLatLong()
	{
		console.log('I AM HERE, CACHED LAT LONG TO DISTRICT LOOKUP.');
		$.ajax({
			dataType: "JSON",
			url: "/api/geocode?q="+$("#addressInput").val(),
			success: function(data, status, xhr)
			{
				console.log(data);
				if(data["status"])
				{
					$("#loadProgress").fadeOut();
					console.log("Error! Oh no!");
					var errorDiv = $("

").addClass("alert alert-danger").html("Error: "+data["error_message"])
					errorDiv.appendTo($("#warnings"));
					$("#warnings").fadeIn();
					return;
				}
				else
				{
					console.log(data);
					if(data["warnings"]!=undefined && data["warnings"].length)
					{
						$("#warnings").html("");
						var warningDiv = $("

").addClass("alert alert-warning").html("Warning: "+data["warnings"][0]);
						warningDiv.appendTo($("#warnings"));
						$("#warnings").fadeIn();
					}
					$("#addressCorrected").html("Address Lookup:
"+data["formatted_address"]+"");
					$("#loadProgress").html("Loading... Address matched, looking up historical representatives... ");
					doMembers(data["lat"], data["lng"]);
				}
			}
		});
	}

	function compareSort(a, b)
	{
		if(a.congress > b.congress) { return -1; }
		else if(a.congress < b.congress) { return 1; }
		else
		{
			if(a.party_noun  b.party_noun) { return 1; }
			else
			{
				if(a.bioname < b.bioname) { return -1; }
				else { return 1; }
			}
		}
	}

	function doMembers(lat, lng)
	{
		console.log("Entering this now.");
		console.log(lat);
		console.log(lng);
 		var markerPos = {lat: lat, lng: lng};
		var map = new google.maps.Map(document.getElementById("google_map"), {zoom: 12, center: markerPos, disableDefaultUI: true, scrollwheel: false, draggable: true, zoomControl: true});
		// Put the marker in the lat/long
		var marker = new google.maps.Marker({position: markerPos, map: map});
		// If the user moves the viewport, update the map?
		google.maps.event.addListener(map, 'idle', function()
		{
			console.log("idle now");
			var coords = [map.getCenter().lat(), map.getCenter().lng()];
			//$("#addressInput").val(coords[0]+", "+coords[1]);
			//resetResults();
			//doLatLong();
		});

		$.ajax({
			dataType: "JSON",
			url: "/api/districtLookup?lat="+lat+"&long="+lng,
			success: function(data, status, xhr)
			{
				$("#loadProgress").fadeOut();
				if(data["resCurr"].length)
				{
					$("
Current Congressperson and Senators
").appendTo("#resultsMembers");
					var memberList = $("

").css("columns","auto 3")
								.css("list-style-type","none").css("overflow","auto")
								.css("width","100%").css("margin-left",0).css("padding-left",0).css("margin-bottom","15px")
								.css("display","block").attr("id","memberList");
				        console.log(data["resCurr"]);
					memberList.appendTo("#resultsMembers");
					$.each(data["resCurr"], function(k,v)
					{
						constructPlot(v, 0);
					});
				}

				var permLink = $("").attr("href","/district/"+encodeURI($("#addressInput").val())).html("Permanent Link to this address search.");
				permLink.appendTo($("#perm_link_holder"));

				$("
Historical Representatives
").appendTo("#resultsMembers");
				var table = $("

Congress	District	Ideology	Party	Member

")
						.addClass("table table-hover dc-data-table");
				var tbody = $("

");

				// For visual design, we do a pocket algorithm; save the last guy, compare to current guy, see what's changed.
				var lastResult = {};
				var myResults = data["results"].sort(compareSort);
				$.each(myResults, function(k, v)
				{
					// Check to see if we have other members at the same time
					var multiMember=0;
					if(v["congress"]<90)
					{
						var howMany = $.grep(myResults, function(n,i) { return (n["congress"]==v["congress"]); });
						if(howMany.length>1) { multiMember=1; }
					}

					// Explainers for weird edge cases (partition/joining or the Civil War)
					if(lastResult["congress"]>38 && lastResult["congress"]<=45 && v["congress"]<37 && v["congress"]>=30)
					{
						var civilWarDiv = $("

").addClass("alert alert-info").html("United States Civil War: "+v["state"]+" does not seat a delegation in the US Congress.");
						var tr = $("

");
						var td = $("	");
						civilWarDiv.appendTo(td);
						td.appendTo(tr);
						tr.appendTo(tbody);
					}
					if(lastResult["state"]=="Maryland" && Math.abs(v["congress"]-lastResult["congress"])>20)
					{
						var maryDiv = $("

").addClass("alert alert-info").html("D.C.: The changing shapes of congressional districts occasionally include the address you entered in Maryland. As above, Voteview.com does not track D.C. delegates.");
						var tr = $("

");
						var td = $("	");
						maryDiv.appendTo(td);
						td.appendTo(tr);
						tr.appendTo(tbody);
					}
					if(lastResult["state"]=="West Virginia" && v["state"]=="Virginia")
					{
						var virgDiv = $("

").addClass("alert alert-info").html("United States Civil War: West Virginia breaks away from Virginia to form a new state.");
						var tr = $("

");
						var td = $("	");
						virgDiv.appendTo(td);
						td.appendTo(tr);
						tr.appendTo(tbody);
					}
					if(lastResult["state"]=="Maine" && v["state"]=="Massachusetts")
					{
						var maineDiv = $("

").addClass("alert alert-info").html("1820: Maine votes to secede from Massachusetts and is admitted to the union as a state.");
						var tr = $("

");
						var td = $("	");
						maineDiv.appendTo(td);
						td.appendTo(tr);
						tr.appendTo(tbody);
					}

					var tr = $("

").on("click",function(){window.location='/person/'+v["icpsr"]+"/"+v["seo_name"];});
					dateSet = congToYears(v["congress"]);

					if(v["nominate"]!=undefined && v["nominate"]["dim1"]!=undefined) { var nomOffset = Math.floor((v["nominate"]["dim1"]+1.01)*50); }

					$(""+getGetOrdinal(v["congress"])+" ("+dateSet[0]+"-"+dateSet[1].toString().substr(2,2)+")	").appendTo(tr);
					$(""+v["state_abbrev"]+"-"+lzPad(v["district_code"])+"	").appendTo(tr);
					if(v["nominate"]!=undefined && v["nominate"]["dim1"]!=undefined)
					{
						var nomDiv = $("").css("border-right","3px solid "+colorSchemes[v["party_color"]][0])
										.css("width",nomOffset+"%").css("height","100%").css("overflow","auto").css("display","block");

						var holdingTD = $("	").css("padding","0").css("width","20px").css("border-left","1px solid grey").css("border-right","1px solid grey");
						nomDiv.appendTo(holdingTD);
						holdingTD.appendTo(tr);
					}
					else
					{
						$("	").appendTo(tr);
					}
					$(""+v["party_noun"]+"	").appendTo(tr);
					$(""+v["bioname"]+"	").appendTo(tr);

					// Use a closure to pin tooltips onto each row.
					(function(v){
						tr.on("mouseover", function()
						{
							$("#tooltipIdeology").html("");
							if(v["nominate"]!=undefined) { $("#tooltipIdeology").html(v["nominate"]["dim1"]); }
							else { $("#tooltipIdeology").html("No Ideology Score"); }

							$("#tooltipIdeology").removeClass().addClass("d3-tip");
							$("#tooltipIdeology").css("left",($(this).offset().left+245)+"px");
							$("#tooltipIdeology").css("top",$(this).offset().top+"px");
							$("#tooltipIdeology").css("visibility","visible");
						});
						tr.on("mouseout",function() { $("#tooltipIdeology").css("visibility","hidden"); });
					})(v);

					tr.appendTo(tbody);

					lastResult = v;
				});
				tbody.appendTo(table);
				table.appendTo($("#resultsMembers"));
				//permLink.appendTo($("#resultsMembers"));
				$("#resultsMembers").fadeIn();
			}
		});
	}

<commit_msg>First pass at move viewport -> Update district search.<commit_after>var tooltipIdeology = $("

").addClass("d3-tip").css("visibility","hidden").attr("id","tooltipIdeology").appendTo(document.body);

// From stackoverflow response, who borrowed it from Shopify--simple ordinal suffix.
var myLat, myLong;
var slowTimer;
var globalEnableLocation = 0;

function getGetOrdinal(n) {
    var s=["th","st","nd","rd"],
    v=n%100;
    return n+(s[(v-20)%10]||s[v]||s[0]);
 }

function congToYears(n) { return [1787+(2*n), 1787+(2*n)+2]; }

function lzPad(t)
{
	if(parseInt(t)<10) { return "0"+t; }
	else { return t; }
}

function loadText(t)
{
	$("#addressInput").val(t);
	latLongWrapper();
}

function resetResults()
{
	$("#resultsMembers").hide().html("");
	$("#addressCorrected").html("");
	$("#google_map").html("");
	$("#warnings").hide().html("");
	$("#loadProgress").hide().html("");
	$("#perm_link_holder").html("");
}

	$(document).ready(function(){
		$("#addressInput").on("focus",function() {
			if($("#addressInput").val()=="MY LOCATION") { $("#addressInput").val(""); }
		});
		$("#submit-address-form").submit(function(event)
		{
			event.preventDefault();
			latLongWrapper();
		});
		$("#submit-geolocation").click(function(event)
		{
			event.preventDefault();
			getLocation();
		});
		if($("#cachedLat").val()) { myLat = $("#cachedLat").val(); }
		if($("#cachedLong").val()) { myLong = $("#cachedLong").val(); }
		if($("#addressInput").val()) { setTimeout(function(){latLongWrapper();},1000); }
  	        $("ul#testData li").on("click",function(){ loadText(this.innerHTML); });
		$("ul#notableExamples li").on("click",function(){ loadText(this.innerHTML); });
	});

	if(navigator.geolocation)
	{
		globalEnableLocation=1;
		console.log('html5 location support detected.');
		$("#locationButton").show();
		function success(position)
		{
			clearTimeout(slowTimer);
			console.log(position.coords);
			myLat = position.coords.latitude;
			myLong = position.coords.longitude;
			$("#cachedLat").val(myLat);
			$("#cachedLong").val(myLong);
			$("#addressInput").val("MY LOCATION");
			resetResults();
			$("#loadProgress").show().html("Loading... Location matched, looking up historical representatives... ");
			doMembers(myLat, myLong);
		}
		function error()
		{
			clearTimeout(slowTimer);
			resetResults();
			$("#warnings").html("").show();
			var errorDiv = $("

").addClass("alert alert-danger").html("Error: Error looking up your location. Most commonly this occurs because you are having internet connection trouble or you have privacy settings designed to block web access to your location.");
			errorDiv.appendTo($("#warnings"));

			return;
		}
		function getLocation(event)
		{
			console.log('doing html5 geolocation lookup on user request');
			resetResults();
			$("#loadProgress").show().html("Loading... Looking up your current location... ");
			slowTimer = setTimeout(function() { $("#loadProgress").html($("#loadProgress").html()+"
This process seems to be taking an unusually long time to complete. The delay is related to your internet connection, router, or web browser and is not connected to our server."); }, 5000);
			navigator.geolocation.getCurrentPosition(success, error);
		}
		$("#geolocationTutorial").show();
	}

	function latLongWrapper()
	{
		console.log('wrapper translates box input to geocode.');
		resetResults();
		if($("#addressInput").val()=="")
		{
			$("#warnings").html("").show();
			var errorDiv = $("

").addClass("alert alert-danger").html("Error: No address specified.");
			errorDiv.appendTo($("#warnings"));
			return;
		}
		else if($("#addressInput").val()=="MAP CENTER" && cachedCoords.length==2)
		{
			setTimeout(function(){doMembers(cachedCoords[0], cachedCoords[1])}, 20);
		}
		else if($("#addressInput").val()=="MY LOCATION" && globalEnableLocation && $("#cachedLat").val() && $("#cachedLong").val()) { doMembers(parseFloat($("#cachedLat").val()), parseFloat($("#cachedLong").val())); }
		else if($("#addressInput").val()=="MY LOCATION")
		{
			$("#warnings").html("").show();
			var errorDiv = $("

").addClass("alert alert-danger").html("Error: Error accessing your location, either due to internet connectivity or privacy settings. Please manually type an address to continue.");
			errorDiv.appendTo($("#warnings"));
		}
		else
		{
			$("#loadProgress").show().html("Loading... Matching address to map coordinates... ");
			setTimeout(doLatLong, 20);
		}
	}

	function doLatLong()
	{
		console.log('sending an address to the geocoder.');
		$.ajax({
			dataType: "JSON",
			url: "/api/geocode?q="+$("#addressInput").val(),
			success: function(data, status, xhr)
			{
				if(data["status"])
				{
					$("#loadProgress").fadeOut();
					console.log("Error! Oh no!");
					var errorDiv = $("

").addClass("alert alert-danger").html("Error: "+data["error_message"])
					errorDiv.appendTo($("#warnings"));
					$("#warnings").fadeIn();
					return;
				}
				else
				{
					if(data["warnings"]!=undefined && data["warnings"].length)
					{
						$("#warnings").html("");
						var warningDiv = $("

").addClass("alert alert-warning").html("Warning: "+data["warnings"][0]);
						warningDiv.appendTo($("#warnings"));
						$("#warnings").fadeIn();
					}
					$("#addressCorrected").html("Address Lookup:
"+data["formatted_address"]+"");
					$("#loadProgress").html("Loading... Address matched, looking up historical representatives... ");
					doMembers(data["lat"], data["lng"]);
				}
			}
		});
	}

	function compareSort(a, b)
	{
		if(a.congress > b.congress) { return -1; }
		else if(a.congress < b.congress) { return 1; }
		else
		{
			if(a.party_noun  b.party_noun) { return 1; }
			else
			{
				if(a.bioname < b.bioname) { return -1; }
				else { return 1; }
			}
		}
	}

	function precisionRound(num, p)
	{
		return +(Math.round(num+"e+"+p)+"e-"+p);
	}

	var globalMap;
	var markerSet = [];
	var cachedCoords = [];
	var initialLoad=0;
	function doMembers(lat, lng)
	{
		console.log(lat);
		console.log(lng);

		// We started a load, so don't fire the map move event while we're loading
		initialLoad = 1;
		// Cache the lookup coordinates to make the map mover work
		cachedCoords = [lat, lng];
 		var markerPos = {lat: lat, lng: lng};
		var map = new google.maps.Map(document.getElementById("google_map"), {zoom: 12, center: markerPos, disableDefaultUI: true, scrollwheel: false, draggable: true, zoomControl: true});
		globalMap = map;
		// Put the marker in the lat/long
		var marker = new google.maps.Marker({position: markerPos, map: map});
		markerSet.push(marker);
		// If the user moves the viewport, update the map?
		google.maps.event.addListener(map, 'idle', function()
		{
			// Okay, next time you can fire the event
			if(initialLoad) { initialLoad=0; return; }

			// If the event fired but we didn't move, don't redo the search
			if(precisionRound(map.getCenter().lat(),5)==precisionRound(cachedCoords[0],5) && precisionRound(map.getCenter().lng(),5)==precisionRound(cachedCoords[1],5)) { return; }

			console.log("map viewport move detected.");

			for(var i=0;i<markerSet.length;i++)
			{
				markerSet[i].setMap(null);
			}
			markerSet = [];

			//var marker = new google.maps.Marker({position: {lat: map.getCenter().lat(), lng: map.getCenter().lng()}, map: globalMap});
			//markerSet.push(marker);

			cachedCoords = [map.getCenter().lat(), map.getCenter().lng()];
			console.log("New coords to search: ");
			console.log(cachedCoords);
			$("#addressInput").val("MAP CENTER");
			resetResults();
			latLongWrapper();
		});

		$.ajax({
			dataType: "JSON",
			url: "/api/districtLookup?lat="+lat+"&long="+lng,
			success: function(data, status, xhr)
			{
				$("#loadProgress").fadeOut();
				if(data["resCurr"].length)
				{
					$("
Current Congressperson and Senators
").appendTo("#resultsMembers");
					var memberList = $("

").css("columns","auto 3")
								.css("list-style-type","none").css("overflow","auto")
								.css("width","100%").css("margin-left",0).css("padding-left",0).css("margin-bottom","15px")
								.css("display","block").attr("id","memberList");
					memberList.appendTo("#resultsMembers");
					$.each(data["resCurr"], function(k,v)
					{
						constructPlot(v, 0);
					});
				}

				var permLink = $("").attr("href","/district/"+encodeURI($("#addressInput").val())).html("Permanent Link to this address search.");
				permLink.appendTo($("#perm_link_holder"));

				$("
Historical Representatives
").appendTo("#resultsMembers");
				var table = $("

Congress	District	Ideology	Party	Member

")
						.addClass("table table-hover dc-data-table");
				var tbody = $("

");

				// For visual design, we do a pocket algorithm; save the last guy, compare to current guy, see what's changed.
				var lastResult = {};
				var myResults = data["results"].sort(compareSort);
				$.each(myResults, function(k, v)
				{
					// Check to see if we have other members at the same time
					var multiMember=0;
					if(v["congress"]<90)
					{
						var howMany = $.grep(myResults, function(n,i) { return (n["congress"]==v["congress"]); });
						if(howMany.length>1) { multiMember=1; }
					}

					// Explainers for weird edge cases (partition/joining or the Civil War)
					if(lastResult["congress"]>38 && lastResult["congress"]<=45 && v["congress"]<37 && v["congress"]>=30)
					{
						var civilWarDiv = $("

").addClass("alert alert-info").html("United States Civil War: "+v["state"]+" does not seat a delegation in the US Congress.");
						var tr = $("

");
						var td = $("	");
						civilWarDiv.appendTo(td);
						td.appendTo(tr);
						tr.appendTo(tbody);
					}
					if(lastResult["state"]=="Maryland" && Math.abs(v["congress"]-lastResult["congress"])>20)
					{
						var maryDiv = $("

").addClass("alert alert-info").html("D.C.: The changing shapes of congressional districts occasionally include the address you entered in Maryland. As above, Voteview.com does not track D.C. delegates.");
						var tr = $("

");
						var td = $("	");
						maryDiv.appendTo(td);
						td.appendTo(tr);
						tr.appendTo(tbody);
					}
					if(lastResult["state"]=="West Virginia" && v["state"]=="Virginia")
					{
						var virgDiv = $("

").addClass("alert alert-info").html("United States Civil War: West Virginia breaks away from Virginia to form a new state.");
						var tr = $("

");
						var td = $("	");
						virgDiv.appendTo(td);
						td.appendTo(tr);
						tr.appendTo(tbody);
					}
					if(lastResult["state"]=="Maine" && v["state"]=="Massachusetts")
					{
						var maineDiv = $("

").addClass("alert alert-info").html("1820: Maine votes to secede from Massachusetts and is admitted to the union as a state.");
						var tr = $("

");
						var td = $("	");
						maineDiv.appendTo(td);
						td.appendTo(tr);
						tr.appendTo(tbody);
					}

					var tr = $("

").on("click",function(){window.location='/person/'+v["icpsr"]+"/"+v["seo_name"];});
					dateSet = congToYears(v["congress"]);

					if(v["nominate"]!=undefined && v["nominate"]["dim1"]!=undefined) { var nomOffset = Math.floor((v["nominate"]["dim1"]+1.01)*50); }

					$(""+getGetOrdinal(v["congress"])+" ("+dateSet[0]+"-"+dateSet[1].toString().substr(2,2)+")	").appendTo(tr);
					$(""+v["state_abbrev"]+"-"+lzPad(v["district_code"])+"	").appendTo(tr);
					if(v["nominate"]!=undefined && v["nominate"]["dim1"]!=undefined)
					{
						var nomDiv = $("").css("border-right","3px solid "+colorSchemes[v["party_color"]][0])
										.css("width",nomOffset+"%").css("height","100%").css("overflow","auto").css("display","block");

						var holdingTD = $("	").css("padding","0").css("width","20px").css("border-left","1px solid grey").css("border-right","1px solid grey");
						nomDiv.appendTo(holdingTD);
						holdingTD.appendTo(tr);
					}
					else
					{
						$("	").appendTo(tr);
					}
					$(""+v["party_noun"]+"	").appendTo(tr);
					$(""+v["bioname"]+"	").appendTo(tr);

					// Use a closure to pin tooltips onto each row.
					(function(v){
						tr.on("mouseover", function()
						{
							$("#tooltipIdeology").html("");
							if(v["nominate"]!=undefined) { $("#tooltipIdeology").html(v["nominate"]["dim1"]); }
							else { $("#tooltipIdeology").html("No Ideology Score"); }

							$("#tooltipIdeology").removeClass().addClass("d3-tip");
							$("#tooltipIdeology").css("left",($(this).offset().left+245)+"px");
							$("#tooltipIdeology").css("top",$(this).offset().top+"px");
							$("#tooltipIdeology").css("visibility","visible");
						});
						tr.on("mouseout",function() { $("#tooltipIdeology").css("visibility","hidden"); });
					})(v);

					tr.appendTo(tbody);

					lastResult = v;
				});
				tbody.appendTo(table);
				table.appendTo($("#resultsMembers"));
				//permLink.appendTo($("#resultsMembers"));
				$("#resultsMembers").fadeIn();
			}
		});
	}

<|endoftext|>


<commit_before>$( document ).ready( function() {

    // Variable to hold request
    var request;

    // Bind to the submit event of our form
    $("#expenseForm").submit(function(event){

        // Abort any pending request
        if (request) {
            request.abort();
        }
        // setup some local variables
        var $form = $(this);

        // Let's select and cache all the fields
        var $inputs = $form.find("input, select, button, textarea");

        // Serialize the data in the form
        var serializedData = $form.serialize();

        // Let's disable the inputs for the duration of the Ajax request.
        // Note: we disable elements AFTER the form data has been serialized.
        // Disabled form elements will not be serialized.
        $inputs.prop("disabled", true);

        // Fire off the request to /form.php
        request = $.ajax({
            url: "/expenses/add",
            type: "post",
            data: serializedData
        });

        // Callback handler that will be called on success
        request.done(function (response, textStatus, jqXHR){
            // Log a message to the console
            console.log("Hooray, it worked!");

            setTimeout(function(){ jQuery("#div").hide(); }, 3000);

            $('#successbox').html('
Expense added successfully :)
');
            $('#successbox').css('display','block');

            setTimeout(function(){
                $('#successbox').delay( 2000 ).hide( "drop", { direction: "right" }, "slow" );
            }, 3000);

        });

        // Callback handler that will be called on failure
        request.fail(function (jqXHR, textStatus, errorThrown){
            // Log the error to the console
            console.error(
                "The following error occurred: "+
                textStatus, errorThrown
            );

            $('#failbox').html('
Error adding expenses :(
');
            $('#failbox').css('display','block');

            setTimeout(function(){
                $('#failbox').delay( 2000 ).hide( "drop", { direction: "right" }, "slow" );
            }, 3000);

        });

        // Callback handler that will be called regardless
        // if the request failed or succeeded
        request.always(function () {
            // Reenable the inputs
            $inputs.prop("disabled", false);
        });

        // Prevent default posting of form
        event.preventDefault();

        // Load List of Expenses
        $.ajax({
              url:"/expenses/listing",
              cache:0,
              success:function(result){
                     document.getElementById("expenseListing").innerHTML=result;
               }
        });

    });

    // Refresh list of expenses
    $.ajax({
          url:"/expenses/listing",
          cache:0,
          success:function(result){
                 document.getElementById("expenseListing").innerHTML=result;
           }
    });

    // Get Date
    $(".date-picker").datepicker();
    $(".date-picker").datepicker("option", "dateFormat", "yy-mm-dd");

    $(".date-picker").on("change", function () {
        var id = $(this).attr("id");
        var val = $("label[for='" + id + "']").text();
        $("#msg").text(val + " changed");
    });

});<commit_msg>haaaands<commit_after>$( document ).ready( function() {

    // Variable to hold request
    var request;

    // Bind to the submit event of our form
    $("#expenseForm").submit(function(event){

        // Abort any pending request
        if (request) {
            request.abort();
        }
        // setup some local variables
        var $form = $(this);

        // Let's select and cache all the fields
        var $inputs = $form.find("input, select, button, textarea");

        // Serialize the data in the form
        var serializedData = $form.serialize();

        // Let's disable the inputs for the duration of the Ajax request.
        // Note: we disable elements AFTER the form data has been serialized.
        // Disabled form elements will not be serialized.
        $inputs.prop("disabled", true);

        // Fire off the request to /form.php
        request = $.ajax({
            url: "/expenses/add",
            type: "post",
            data: serializedData
        });

        // Callback handler that will be called on success
        request.done(function (response, textStatus, jqXHR){
            // Log a message to the console
            console.log("Hooray, it worked!");

            setTimeout(function(){ jQuery("#div").hide(); }, 3000);

            $('#successbox').html('
Expense added successfully :)
');
            $('#successbox').css('display','block');

            setTimeout(function(){
                $('#successbox').delay( 2000 ).hide( "drop", { direction: "right" }, "slow" );
            }, 3000);

        });

        // Callback handler that will be called on failure
        request.fail(function (jqXHR, textStatus, errorThrown){
            // Log the error to the console
            console.error(
                "The following error occurred: "+
                textStatus, errorThrown
            );

            $('#failbox').html('
Error adding expenses :(
');
            $('#failbox').css('display','block');

            setTimeout(function(){
                $('#failbox').delay( 2000 ).hide( "drop", { direction: "right" }, "slow" );
            }, 3000);

        });

        // Callback handler that will be called regardless
        // if the request failed or succeeded
        request.always(function () {
            // Reenable the inputs
            $inputs.prop("disabled", false);
        });

        // Prevent default posting of form
        event.preventDefault();

        // Load List of Expenses
        $.ajax({
              url:"/expenses/listing",
              cache:0,
              success:function(result){
                     document.getElementById("expenseListing").innerHTML=result;
               }
        });

    });

    // Refresh list of expenses
    $.ajax({
          url:"/expenses/listing",
          cache:0,
          success:function(result){
                 document.getElementById("expenseListing").innerHTML=result;
           }
    });

    // Get Date
    $(".date-picker").datepicker();
    $(".date-picker").datepicker("option", "dateFormat", "yy-mm-dd");

    $(".date-picker").on("change", function () {
        var id = $(this).attr("id");
        var val = $("label[for='" + id + "']").text();
        $("#msg").text(val + " changed");
    });

    // Checkbox Handler
    $('#deleteForm tr')
        .filter(':has(:checkbox:checked)')
        .addClass('selected')
        .end()
        .click(function(event) {
        $(this).toggleClass('selected');
        if (event.target.type !== 'checkbox') {
            $(':checkbox', this).attr('checked', function() {
                return !this.checked;
            });
        }
    });

});<|endoftext|>