PageSourceSearch

https://voteview.com/static/js/decorate.js

js voteview.com collected 2026-10-01 10:13:06 UTC 13,377 bytes, 352 lines download raw bytes

1var isDoingSelect=0;
2var delayUpdateToolip;
3
4/* 
5    Add sponsor circle
6*/
7function addSponsorCircle(oc,dat) {
8    var scaleX = d3.scale.linear().domain([-1,1]).range([oc.margins()['left'],oc.width()-oc.margins()['right']]);
9    var scaleY = d3.scale.linear().domain([-1,1]).range([oc.height()-oc.margins()['bottom'],oc.margins()['top']]);
10    var vc = globalData.rollcalls[0];
11    for (var i = 0, len = vc.votes.length; i < len; i++) {
12        if (vc.votes[i].icpsr == vc.sponsor) break;
13    }
14    if (i<len) {
15	var ocSVG = d3.select(oc.g()[0][0]), sponsor = vc.votes[i];
16	ocSVG.append("circle")
17             .attr("stroke",blendColors([sponsor],false))
18             .attr("stroke-width","1px")
19             .attr("fill","none")
20             .attr("r", 9)
21             .attr("cx", scaleX(sponsor.x))
22             .attr("cy", scaleY(sponsor.y)+1.5); // JBL: Not sure why we need the extra 1.5 here...
23    }
24}
25
26
27/*
28    Draws the background circles, labels and text for the scatter chart
29*/
30function decorateNominate(oc,data) {
31        var scmargins = oc.margins()
32	var width = oc.width();
33        var height = oc.height();
34	var margin = scmargins['left']-scmargins['right'];
35	var marginCircle = scmargins['right']; // Distance of the main circle to the axis
36	var tickLength = 15;
37
38	// Calculate ellipse attrs
39	var radiusX = (width - margin)/2 - marginCircle;
40        var radiusY = nomDWeight*radiusX;
41	var circleCenter = { "x": (width + margin)/2, "y": (height - margin)/2 };
42
43	// Select the base SVG
44	var ocSVG = d3.select(oc.g()[0][0]);
45
46	// Remove everything that's an axis.
47	ocSVG.selectAll(".axis").remove();
48
49	// Place bg stuff in SVG tree in front of .chart-body scatter points
50	var svgbg = ocSVG.insert("g",".chart-body");
51        var gg = svgbg.append("g").attr("id","scatter-background");
52
53	// X-axis
54        var xAxisMin = circleCenter.x - radiusX;
55        var xAxisMax = circleCenter.x + radiusX;
56        var xAxisLen = xAxisMax - xAxisMin;
57        var yDimPos = circleCenter.y + radiusY + 10;
58
59	gg.append('polyline')
60		.attr("class", "scatter-axis")
61		.attr("points", sprintf("%d,%d %d,%d %d,%d %d,%d",
62				xAxisMin, yDimPos + tickLength,
63				xAxisMin, yDimPos,
64				xAxisMax, yDimPos,
65				xAxisMax, yDimPos + tickLength));
66
67	gg.append('text')
68            .text("Liberal")
69            .attr("x", xAxisMin + 0.02 * xAxisLen)
70            .attr("y", yDimPos + 20)
71            .attr("style","text-anchor: start; fill: #808080;");
72	gg.append('text')
73            .text("Conservative")
74            .attr("x", xAxisMin + 0.98 * xAxisLen)
75            .attr("y", yDimPos + 20)
76            .attr("style","text-anchor: end; fill: #808080;");
77	gg.append('text')
78             .text("DW-Nominate Dimension 1: Economic/Redistributive")
79             .attr("x", xAxisMin + xAxisLen/2).attr("y", yDimPos + 20)
80             .attr("style","text-anchor:middle");
81	// End X axis.
82
83	// Y-axis
84        var yAxisMin = circleCenter.y - radiusY;
85        var yAxisMax = circleCenter.y + radiusY;
86        var yAxisLen = yAxisMax - yAxisMin;
87
88	gg.append('polyline')
89	     .attr("class","scatter-axis")
90	     .attr("points", sprintf("%d,%d  %d,%d  %d,%d  %d,%d",
91	   			     margin, yAxisMin,
92				     margin + tickLength, yAxisMin,
93				     margin + tickLength, yAxisMax,
94				     margin, yAxisMax));
95	gg.append('text')
96	     .text("Conservative")
97	     .attr("x", 50)
98             .attr("y", yAxisMin + 0.05 * yAxisLen)
99             .attr("style","text-anchor: end; fill: #808080;")
100             .attr("transform", sprintf("rotate(-90 50 %d)", yAxisMin + 0.05 * yAxisLen));
101	gg.append('text')
102             .text("Liberal")
103	     .attr("x", 50)
104             .attr("y", yAxisMin + 0.95 * yAxisLen)
105             .attr("style","text-anchor: start; fill: #808080;")
106             .attr("transform", sprintf("rotate(-90 50 %d)", yAxisMin + 0.95 * yAxisLen));
107
108	var seconddim_label = "NOMINATE Dimension 2: Other Votes";
109	if(congressNum != null)
110	{
111		switch(true) {
112			case (congressNum <= 24):
113				break;
114			case (congressNum <= 32):
115				seconddim_label = "NOMINATE Dimension 2: Slavery / Racial";
116				break;
117			default:
118				break;
119		}
120	}
121
122	gg.append('text')
123             .text(seconddim_label)
124             .attr("x", 30)
125             .attr("y", yAxisMin + yAxisLen/2)
126             .attr("style","text-anchor:middle")
127             .attr("transform", sprintf("rotate(-90 30 %d)", yAxisMin + yAxisLen/2));
128	// End Y axis
129
130	// Add yea and nay locations to the plot on top of the dots
131	// Problem is that with Y/N on top we can't select point under/near the Y/N
132	// Need a way to insert after the dots but before the brush. Putting the Y/N group right
133	// before the brush group does it. --JBL
134
135	var ggg = ocSVG.insert("g",".brush");
136
137        var hasNominate = 1;
138        if (data.rollcalls == undefined || data.rollcalls[0].nominate == undefined || data.rollcalls[0].nominate.spread == undefined)
139        {
140                console.log("Vote type 1: Missing critical data / rollcalls, nominate, or spread.");
141                hasNominate=0;
142                gg
143			.append("ellipse")
144                        .attr("stroke","black")
145                        .attr("stroke-width","1px")
146                        .attr("fill","none")
147			.attr("rx", radiusX)
148                        .attr("ry", radiusY)
149			.attr("cx", circleCenter.x)
150			.attr("cy", circleCenter.y);
151		var voteShare=0;
152        }
153        else
154        {
155            if(data.rollcalls[0].congress==0) var vn = data.rollcalls[0].nominate.imputed;
156            else var vn = data.rollcalls[0].nominate;
157            var voteShare = Math.max(data.rollcalls[0].yea, data.rollcalls[0].nay)/(data.rollcalls[0].yea+data.rollcalls[0].nay);
158        }
159
160
161	// Fit box regardless if cutline exists
162	if(vn != undefined && vn.pre != undefined && vn.pre != "")
163	{
164		ggg.append('text').text(sprintf("Proportionate Reduction in Error (PRE): %4.2f", (vn.pre == null || isNaN(vn.pre) || vn.pre=="") ? 0 : vn.pre))
165		    .attr("class", "fitbox")
166		    .attr("x", xAxisMin + 0.98 * xAxisLen)
167	            .attr("style","text-anchor: end;")
168		    .attr("y", yDimPos + 40);
169		ggg.append('text').text(sprintf("Proportion of votes correctly classified: %4.2f", (vn.classified == null || isNaN(vn.classified) || vn.classified=="") ? 0 : vn.classified ))
170		    .attr("class", "fitbox")
171		    .attr("x", xAxisMin + 0.02 * xAxisLen)
172		    .attr("y", yDimPos + 40);
173	}
174
175	var legendType=1;
176
177
178	var doHM = 1;
179
180	if (hasNominate && (vn.spread[0]!=0 | vn.spread[1]!=0) && (voteShare<=0.975 | data.rollcalls[0].nominate.pre > 0) ) { // Only drawn if there is a cutline!
181		console.log("Vote type 2: Has actual division");
182		var hmTranslate = {x: scmargins['left'], y: (oc.height()/2) - radiusY - scmargins['top']};
183		var gggg = gg.append("g")
184				.attr("id","heat-map")
185				.attr("transform","translate(" + hmTranslate.x + "," + hmTranslate.y+ ")");
186
187		nominateHeatmap(gggg, vn.mid[0], vn.mid[1], vn.spread[0], vn.spread[1],
188			nomBeta, nomDWeight, 2 * radiusX, 2 * radiusY, 30, ["#FFFFFF","#FFFFAA"]);
189
190	       // Calculate where the YN text axis goes.
191	       function closestpt(vn) {
192		    var b = vn.slope;
193                    var angle = Math.atan( (vn.spread[1] * nomDWeight) / (vn.spread[0]) );
194		    var a = vn.mid[1] - b * vn.mid[0];
195		    var xstar = -b * a / (1 + b * b);
196		    var obj = {
197			angle: angle,
198			b: b,
199			a: a,
200		        xstar: xstar,
201			ystar: b * xstar + a,
202			offsetX: 0.05 * Math.cos(angle) * Math.sign(vn.spread[0]),
203			offsetY: (vn.slope > 0 ? 1 : -1) * 0.05 * Math.sin(angle) * Math.sign(vn.spread[1]) / nomDWeight
204		    }
205		    return(obj);
206		}
207	        
208                var ynp = closestpt(vn);
209
210 		var ynpts =    [circleCenter.x + radiusX*(ynp.xstar+ynp.offsetX),
211				circleCenter.y - radiusY*(ynp.ystar-ynp.offsetY),
212				circleCenter.x + radiusX*(ynp.xstar-ynp.offsetX),
213				circleCenter.y - radiusY*(ynp.ystar+ynp.offsetY)];
214
215	        var angle = 57.29578*ynp.angle;
216
217		// This is a hack based on what quadrant the text angle is in.
218		var cs = (angle>0?1:0) + 2*(vn.spread[0]>0?1:0);
219		switch(cs)
220		{
221			case 0: angle = 90-angle; break;
222			case 1: angle = 90-angle; break;
223			case 2: angle = 270-angle; break;
224			case 3: angle = -90-angle; break;
225		}
226
227		// Plot the Y text using ynpts[2], [3] and rotating by the angle above.
228		ggg.append('text').text('Y')
229			.attr("class","yeanay")
230			.attr("x", ynpts[2])
231			.attr("y", ynpts[3])
232			.attr("transform",sprintf("rotate(%d %d %d)", angle, ynpts[2], ynpts[3]));
233
234		// Plot the N text using ynpts[0], [1], and rotating by the angle above + 180
235		ggg.append('text').text('N')
236			.attr("class","yeanay")
237			.attr("x", ynpts[0])
238			.attr("y", ynpts[1])
239			.attr("transform",sprintf("rotate(%d %d %d)", 180+angle, ynpts[0], ynpts[1]));
240
241	}
242	else
243        {
244		if(data.rollcalls != undefined)
245		{
246		        var nomData = data.rollcalls[0].nominate;
247			if(nomData != undefined && nomData.intercept != undefined && nomData.pre === "")
248			{
249				console.log("Vote type 3: PRE is undefined but NOMINATE was estimated.");
250				// This is actually the case where NOMINATE is estimated but the PRE/probability stuff is not.
251			        ggg.append('text').text("NOMINATE not")
252					.attr("class","fitbox").attr("x", xAxisMax - 110)
253					.attr("y", yAxisMax - 12);
254				ggg.append('text').text("yet estimated")
255					.attr("class","fitbox").attr("x", xAxisMax - 110)
256					.attr("y", yAxisMax - 0);
257				doHM = 0;
258			}
259			else if(nomData != undefined && nomData.spread != undefined)
260			{
261				console.log("Vote type 4: Lopsided vote.");
262				ggg.append('text').text("Lopsided vote with")
263					.attr("class","fitbox").attr("x", xAxisMax - 110)
264					.attr("y", yAxisMax - 12);
265				ggg.append('text').text("no ideological division")
266					.attr("class","fitbox").attr("x", xAxisMax - 110)
267					.attr("y", yAxisMax - 0);
268				var legendType=2;
269			}
270			else
271		        {
272				console.log("Vote type 5: NOMINATE actually not estimated.");
273			        ggg.append('text').text("NOMINATE not")
274					.attr("class","fitbox").attr("x", xAxisMax - 110)
275					.attr("y", yAxisMax - 12);
276				ggg.append('text').text("yet estimated")
277					.attr("class","fitbox").attr("x", xAxisMax - 110)
278					.attr("y", yAxisMax - 0);
279				var legendType=3;
280			}
281
282
283			if(doHM)
284			{
285		 	        var hmTranslate = {x: scmargins['left'], y: (oc.height() / 2) - radiusY - scmargins['top']};
286	                	var gggg = gg.append("g")
287	                          	.attr("id", "heat-map")
288	                          	.attr("transform", "translate(" + hmTranslate.x + "," + hmTranslate.y + ")");
289
290		        	var pctYea = globalData.rollcalls[0].yea_count / (globalData.rollcalls[0].yea_count + globalData.rollcalls[0].nay_count);
291	   	        	lopsidedHeatmap(gggg, nomDWeight, 2*radiusX, 2*radiusY, ["#FFFFFF","#ffffcc"], pctYea);
292			}
293		}
294	}
295
296        // Tooltip for Prob(yea)
297        if (hasNominate==1 && doHM) { //Show it as long as nominate has been fit
298
299            // Collecting info to find vote prob for heat map
300	    if(!$("#hm_tooltip").length) // Make sure we don't already have a tooltip
301	    {
302		$("<div></div>")
303			.attr("id","hm_tooltip")
304			.addClass("d3-tip")
305			.addClass("heatmapTip")
306			.appendTo(document.body);
307	    }
308            var yunits = radiusY;
309	    var xunits = radiusX;
310            var brush = ocSVG.select(".brush");
311	    brush.on("mouseover",function() { if(!isDoingSelect) { $("#hm_tooltip").css("visibility","visible"); }});
312            brush.on("mousemove",function() {
313			var ypdiv = $("#hm_tooltip");
314                        var cx = circleCenter.x-hmTranslate.x,
315                            cy = circleCenter.y-hmTranslate.y,
316                            x  =  (d3.mouse(this)[0] - cx)/xunits,
317                            y  = -(d3.mouse(this)[1] - cy)/yunits;
318		        if (vn.spread[0]!=0 & vn.spread[1]!=0) {
319		            // Votes with cutlines...
320			    var yeaProb = nomProbYea(x,y,vn.mid[0],vn.mid[1],vn.spread[0],
321                                                 vn.spread[1],nomDWeight,nomBeta);
322			} else {
323			    // Lopsided vote case...
324			    var yeaProb = data.rollcalls[0].yea_count/(data.rollcalls[0].yea_count+data.rollcalls[0].nay_count);
325			}
326	                    if ((x*x+y*y)<1.0) {
327                                if(yeaProb>0.99) {
328        		           dispProb = " > 0.99"
329                                } else if(yeaProb<0.01) {
330                                   dispProb = " < 0.01"
331                                } else {
332                                   dispProb = Math.round(100*yeaProb)/100;
333                                }
334				if(!isDoingSelect) ypdiv.css("visibility","visible");
335				ypdiv.html("<strong>Probability of Voting Yea: </strong>" + dispProb + "<br/> The calculated probability that a member at this position would vote 'Yea' on the rollcall.")
336				   .css("left",d3.event.pageX + 20 + "px")
337				   .css("top",(d3.event.pageY - 48) + "px");
338			    }
339			    else
340			    {
341 				// Cursor is on nominate plot, but not in the Oval
342				ypdiv.css("visibility","hidden");
343			    }
344
345                      });
346            // Don't show tooltip if user is making a range selection
347            brush.on("mousedown",function() { $("#hm_tooltip").css("visibility","hidden"); isDoingSelect=1; });
348            brush.on("mouseup",function() { $("#hm_tooltip").css("visibility","visible"); isDoingSelect=0; });
349	    brush.on("mouseout", function() { $("#hm_tooltip").css("visibility","hidden"); });
350	};
351}
352

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.