PageSourceSearch

https://www.pollsmax.com/assets/editorial-ratings.js?v=gov-no-gray-tie-1

js pollsmax.com collected 2026-09-28 06:37:19 UTC 24,667 bytes, 540 lines download raw bytes

1/* global ET */
2(function () {
3    if (!window.ET) window.ET = {};
4
5    /** @type {Record<string, string>} supersolidD|solidD|likelyD|leanD|tiltD|tossup|tiltR|leanR|likelyR|solidR|supersolidR */
6    ET.SENATE_EDITORIAL_RATINGS = {
7        AK: 'leanD', AL: 'solidR', AR: 'supersolidR', CO: 'supersolidD', DE: 'solidD', FL: 'likelyR',
8        GA: 'tiltD', IA: 'solidR', ID: 'solidR', IL: 'solidD', KS: 'solidR', KY: 'solidR',
9        LA: 'supersolidR', MA: 'solidD', ME: 'tiltD', MI: 'likelyR', MN: 'likelyD', MS: 'solidR',
10        MT: 'likelyR', NC: 'leanD', NE: 'leanR', NH: 'likelyD', NJ: 'solidD', NM: 'supersolidD',
11        OH: 'likelyR', OK: 'solidR', OR: 'solidD', RI: 'solidD', SC: 'solidR', SD: 'solidR',
12        TN: 'solidR', TX: 'leanR', VA: 'solidD', WV: 'solidR', WY: 'solidR'
13    };
14
15    /** Senate forecast blend / simulation cook tier (may differ from display SENATE_EDITORIAL_RATINGS). */
16    ET.SENATE_EDITORIAL_RATINGS_BASE = (function () {
17        var base = Object.assign({}, ET.SENATE_EDITORIAL_RATINGS);
18        base.FL = 'solidR';
19        base.NE = 'likelyR';
20        base.ME = 'solidR';
21        base.MI = 'likelyR';
22        base.TX = 'supersolidR';
23        return base;
24    })();
25
26    /**
27     * Senate blend tier applies only on/after this calendar date (YYYY-MM-DD).
28     * Before the date, SENATE_EDITORIAL_BLEND_PRIOR (if set) else SENATE_EDITORIAL_RATINGS is used.
29     */
30    ET.SENATE_EDITORIAL_BLEND_EFFECTIVE_FROM = {
31        ME: '2026-06-07',
32        MI: '2026-06-08'
33    };
34
35    /** Pre-effective-date cook tier when display SENATE_EDITORIAL_RATINGS already reflects the new rating. */
36    ET.SENATE_EDITORIAL_BLEND_PRIOR = {
37        MI: 'tiltD'
38    };
39
40    /**
41     * Senate-only cook-component margin overrides (rare).
42     * All other states use ET.INPUT_RATING_TO_MARGIN via editorial rating tier.
43     */
44    ET.SENATE_EDITORIAL_BLEND_MARGINS = {};
45
46    /** Bump when blend inputs change — busts race-page + hub forecast localStorage caches. */
47    ET.SENATE_EDITORIAL_BLEND_REVISION = 39;
48
49    /** House flip stripes vs rating: held-seat party baseline (2026 map, redistricting-aware). */
50    ET.HOUSE_FLIP_HELD_PARTY_BY_CODE = {
51        'AL-2': 'D',
52        'LA-6': 'D',
53    };
54
55    /** Senate flip stripes vs rating: held-seat party baseline (open seats + overrides). */
56    ET.SENATE_EDITORIAL_FLIP_INCUMBENT_PARTY = {
57        ME: 'R',
58        AL: 'R', IA: 'R', IL: 'D', KY: 'R', LA: 'R', MI: 'D', MN: 'D', MT: 'R', NH: 'D',
59        NC: 'R', OK: 'R', TX: 'R', WY: 'R'
60    };
61
62    /** @type {Record<string, string>} */
63    ET.GOV_EDITORIAL_RATINGS = {
64        AK: 'solidR', AL: 'supersolidR', AR: 'solidR', AZ: 'leanD', CA: 'solidD', CO: 'solidD',
65        CT: 'supersolidD', FL: 'solidR', GA: 'leanR', HI: 'solidD', IA: 'leanR', ID: 'solidR',
66        IL: 'supersolidD', KS: 'likelyR', MA: 'solidD', MD: 'solidD', ME: 'likelyD', MI: 'tiltD', MN: 'solidD',
67        NE: 'solidR', NH: 'supersolidR', NM: 'likelyD', NV: 'likelyR', NY: 'solidD', OH: 'leanR',
68        OK: 'supersolidR', OR: 'solidD', PA: 'solidD', RI: 'solidD', SC: 'supersolidR', SD: 'supersolidR',
69        TN: 'supersolidR', TX: 'likelyR', VT: 'solidR', WI: 'leanR', WY: 'supersolidR'
70    };
71
72    /** Governor forecast blend / simulation cook tier (may differ from display GOV_EDITORIAL_RATINGS). */
73    ET.GOV_EDITORIAL_RATINGS_BASE = (function () {
74        var base = Object.assign({}, ET.GOV_EDITORIAL_RATINGS);
75        base.GA = 'likelyR';
76        base.OH = 'solidR';
77        return base;
78    })();
79
80    /** Governor-only cook-component margin overrides (rare); else INPUT_RATING_TO_MARGIN. */
81    ET.GOV_EDITORIAL_BLEND_MARGINS = {};
82
83    ET.GOV_EDITORIAL_BLEND_REVISION = 25;
84
85    ET.EDITORIAL_RATING_FILL = {
86        supersolidD: '#0c6df6',
87        solidD: '#0c6df6',
88        likelyD: '#469fff',
89        leanD: '#89c3ff',
90        tiltD: '#bdddff',
91        tossup: '#c4b5fd',
92        leanR: '#ffa7a8',
93        likelyR: '#fc6969',
94        solidR: '#f42726',
95        supersolidR: '#f42726',
96        tiltR: '#ffd2d2'
97    };
98
99    ET.editorialRatingLabel = function (rating) {
100        var map = {
101            megaSolidD: 'Mega Solid D', megaSolidR: 'Mega Solid R',
102            ultraSolidD: 'Ultra Solid D', ultraSolidR: 'Ultra Solid R',
103            supersolidD: 'Super Solid D', supersolidR: 'Super Solid R',
104            solidD: 'Solid D', likelyD: 'Likely D', leanD: 'Lean D', tiltD: 'Tilt D',
105            tossup: 'Tossup',
106            leanR: 'Lean R', likelyR: 'Likely R', solidR: 'Solid R', tiltR: 'Tilt R'
107        };
108        return map[rating] || '';
109    };
110
111    ET.editorialRatingTierWord = function (rating) {
112        if (rating && rating.indexOf('megaSolid') === 0) return 'Mega Solid';
113        if (rating && rating.indexOf('ultraSolid') === 0) return 'Ultra Solid';
114        if (rating === 'supersolidD' || rating === 'supersolidR') return 'Super Solid';
115        var tier = ET.editorialRatingToMarginTier(rating);
116        if (tier === 'solid') return 'Solid';
117        if (tier === 'likely') return 'Likely';
118        if (tier === 'lean') return 'Lean';
119        if (tier === 'tilt') return 'Tilt';
120        return '';
121    };
122
123    /** Chip label: tier word (600) + one space + party letter (800). */
124    ET.editorialRatingChipLabelInnerHtml = function (rating) {
125        var lead = ET.editorialRatingPartyLead(rating);
126        var tierWord = ET.editorialRatingTierWord(rating);
127        if (!lead || lead === 'Tie' || !tierWord) {
128            return ET.escapeHtml ? ET.escapeHtml(ET.editorialRatingLabel(rating)) : ET.editorialRatingLabel(rating);
129        }
130        var tierHtml = tierWord;
131        var partyHtml = lead;
132        if (ET.escapeHtml) {
133            tierHtml = ET.escapeHtml(tierWord);
134            partyHtml = ET.escapeHtml(lead);
135        }
136        return '<span style="font-weight:600;font-style:normal;">' + tierHtml + '</span>' +
137            ' <span style="font-weight:800;font-style:normal;">' + partyHtml + '</span>';
138    };
139
140    ET.editorialRatingPartyLead = function (rating) {
141        if (!rating || rating === 'tossup') return rating === 'tossup' ? 'Tie' : null;
142        if (rating.indexOf('D') !== -1) return 'D';
143        if (rating.indexOf('R') !== -1) return 'R';
144        return null;
145    };
146
147    /** Race Overview / snapshot: color forecast rating by leading party (Lean D, Tilt R, etc.). */
148    ET.formatRaceSnapshotForecastRatingHtml = function (rating) {
149        if (rating == null || rating === '' || rating === '—') {
150            return ET.escapeHtml ? ET.escapeHtml('—') : '—';
151        }
152        var label = ET.editorialRatingLabel && ET.editorialRatingLabel(rating)
153            ? ET.editorialRatingLabel(rating)
154            : String(rating);
155        var lead = ET.editorialRatingPartyLead(rating);
156        if (!lead || lead === 'Tie') {
157            return ET.escapeHtml ? ET.escapeHtml(label) : label;
158        }
159        var cls = lead === 'R' ? 'race-snapshot__party--r' : 'race-snapshot__party--d';
160        var escaped = ET.escapeHtml ? ET.escapeHtml(label) : label;
161        return '<span class="race-snapshot__party ' + cls + '">' + escaped + '</span>';
162    };
163
164    ET.editorialRatingToMarginTier = function (rating) {
165        if (!rating || rating === 'tossup') return 'tilt';
166        if (rating.indexOf('megaSolid') === 0) return 'solid';
167        if (rating.indexOf('ultraSolid') === 0) return 'solid';
168        if (rating.indexOf('supersolid') === 0) return 'solid';
169        if (rating.indexOf('solid') === 0) return 'solid';
170        if (rating.indexOf('likely') === 0) return 'likely';
171        if (rating.indexOf('lean') === 0) return 'lean';
172        if (rating.indexOf('tilt') === 0) return 'tilt';
173        return 'solid';
174    };
175
176    ET.editorialRatingFill = function (rating) {
177        if (!rating) return '#121820';
178        return ET.EDITORIAL_RATING_FILL[rating] || '#121820';
179    };
180
181    /** Map fill from editorial rating (ratings mode, or polling mode when there is no GE average). */
182    ET.forecastHubEditorialRatingStateFill = function (kind, key, name, ui) {
183        ui = ui || { showFlips: true, showMargins: false };
184        if (kind === 'senate' && ui.showFlips &&
185            typeof ET.senateHubMapFlipPatternFill === 'function') {
186            var senateFlipFill = ET.senateHubMapFlipPatternFill(name, key, ui);
187            if (senateFlipFill) return senateFlipFill;
188        }
189        var ratingsMode = ui.mapSource === 'ratings';
190        if (typeof ET.forecastHubModelStateFill === 'function') {
191            var modelFill = ET.forecastHubModelStateFill(kind, key, name, ui);
192            if (modelFill) return modelFill;
193        }
194        var ratings = kind === 'gov' ? ET.GOV_EDITORIAL_RATINGS : ET.SENATE_EDITORIAL_RATINGS;
195        if (!ratings || !key) return null;
196        var rating = ratings[key];
197        if (!rating) return null;
198        if (rating === 'tossup') {
199            // Governors: never purple/gray tossup — tilt to party holding the seat.
200            if (kind === 'gov') {
201                var heldTossup = (typeof ET.govFlipBaselineParty === 'function' && ET.govFlipBaselineParty(key))
202                    || (ET.GOV_INCUMBENT_PARTY && ET.GOV_INCUMBENT_PARTY[key])
203                    || 'R';
204                if (heldTossup !== 'D' && heldTossup !== 'R') heldTossup = 'R';
205                if (typeof ET.forecastHubMapUsesTierColors === 'function'
206                    ? ET.forecastHubMapUsesTierColors(ui)
207                    : !!ui.showMargins) {
208                    return typeof ET.forecastHubMarginFill === 'function'
209                        ? ET.forecastHubMarginFill(heldTossup, 'tilt')
210                        : (heldTossup === 'D' ? ET.COLORS.BLUE : ET.COLORS.RED);
211                }
212                return heldTossup === 'D' ? ET.COLORS.BLUE : ET.COLORS.RED;
213            }
214            return ET.editorialRatingFill('tossup');
215        }
216        var lead = ET.editorialRatingPartyLead(rating);
217        var tier = ET.editorialRatingToMarginTier(rating);
218        var flip = ET.editorialRatingFlipLead(kind, key, rating, name);
219        var useTierColors = typeof ET.forecastHubMapUsesTierColors === 'function'
220            ? ET.forecastHubMapUsesTierColors(ui)
221            : !!ui.showMargins;
222        if (useTierColors) {
223            if (ui.showFlips && flip && typeof ET.forecastHubMarginFlipPatternId === 'function') {
224                return 'url(#' + ET.forecastHubMarginFlipPatternId(kind, flip, tier) + ')';
225            }
226            if (typeof ET.forecastHubMarginFill === 'function') {
227                return ET.forecastHubMarginFill(lead, tier);
228            }
229        }
230        if (typeof ET.forecastHubSimpleMapPartyFill === 'function') {
231            return ET.forecastHubSimpleMapPartyFill(kind, lead, flip, ui);
232        }
233        return lead === 'D' ? ET.COLORS.BLUE : ET.COLORS.RED;
234    };
235
236    ET.senateEditorialIncumbentParty = function (stateName, stateKey) {
237        if (typeof ET.senateFlipBaselineParty === 'function') {
238            var held = ET.senateFlipBaselineParty(stateKey, stateName);
239            if (held === 'D' || held === 'R') return held;
240        }
241        if (ET.SENATE_EDITORIAL_FLIP_INCUMBENT_PARTY && stateKey && ET.SENATE_EDITORIAL_FLIP_INCUMBENT_PARTY[stateKey]) {
242            return ET.SENATE_EDITORIAL_FLIP_INCUMBENT_PARTY[stateKey];
243        }
244        if (stateKey && ET.NAME_BY_KEY && ET.INCUMBENT_2026) {
245            var nmKey = ET.NAME_BY_KEY[stateKey];
246            if (nmKey && ET.INCUMBENT_2026[nmKey] != null) {
247                return ET.INCUMBENT_2026[nmKey] === 'R' ? 'R' : 'D';
248            }
249        }
250        if (!stateName || !ET.INCUMBENT_2026) return 'D';
251        return ET.INCUMBENT_2026[stateName] === 'R' ? 'R' : 'D';
252    };
253
254    ET.editorialRatingFlipLead = function (kind, stateKey, rating, stateName) {
255        var lead = ET.editorialRatingPartyLead(rating);
256        if (!lead || lead === 'Tie') return null;
257        var inc = 'D';
258        if (kind === 'senate') {
259            inc = ET.senateEditorialIncumbentParty(stateName, stateKey);
260        } else if (kind === 'gov') {
261            if (stateKey && ET.GOV_INCUMBENT_FULL_2026 && ET.GOV_INCUMBENT_FULL_2026[stateKey]) {
262                inc = ET.GOV_INCUMBENT_FULL_2026[stateKey].party;
263            } else {
264                var ge = ET.forecastHubGeBaseline && ET.forecastHubGeBaseline('gov', stateKey);
265                if (ge && (ge.w === 'R' || ge.w === 'D')) inc = ge.w;
266            }
267        } else if (kind === 'house') {
268            return null;
269        }
270        return lead === inc ? null : lead;
271    };
272
273    ET.sleekHoverEditorialRatingChipHtml = function (rating, flip) {
274        var lead = ET.editorialRatingPartyLead(rating);
275        if (!lead || lead === 'Tie') {
276            return ET.sleekHoverChipHtml ? ET.sleekHoverChipHtml('Tie', 0) : '';
277        }
278        var fullD = ET.COLORS.BLUE;
279        var fullR = ET.COLORS.RED;
280        var liteB = ET.SENATE_FLIP_CHIP_LIGHT_BLUE || '#2990f7';
281        var liteR = ET.SENATE_FLIP_LIGHT_RED || '#fb6868';
282        var chipOutline = flip
283            ? (ET.sleekMarginChipOutlineCss ? ET.sleekMarginChipOutlineCss(true) : 'box-shadow:inset 0 0 0 1px #ffffff;')
284            : 'box-shadow:inset 0 0 0 1px rgba(255,255,255,.38);';
285        var baseCore = 'display:inline-block;box-sizing:border-box;font-size:14px;font-weight:800;letter-spacing:0.04em;padding:3px 10px;border-radius:6px;color:#fff;border:none;filter:none;text-shadow:none;' + chipOutline;
286        var labelInner = ET.editorialRatingChipLabelInnerHtml
287            ? ET.editorialRatingChipLabelInnerHtml(rating)
288            : ET.editorialRatingLabel(rating);
289        var inner = '<span style="position:relative;top:1px;display:inline-block">' + labelInner + '</span>';
290        var bandPx = 7;
291        var seamPx = 0.45;
292        var ang = '-45deg';
293        var bgStyle;
294        if (flip) {
295            if (lead === 'D') {
296                bgStyle = 'repeating-linear-gradient(' + ang + ',' + fullD + ' 0,' + fullD + ' ' + (bandPx + seamPx) + 'px,' + liteB + ' ' + (bandPx - seamPx) + 'px,' + liteB + ' ' + (bandPx * 2) + 'px)';
297            } else {
298                bgStyle = 'repeating-linear-gradient(' + ang + ',' + fullR + ' 0,' + fullR + ' ' + (bandPx + seamPx) + 'px,' + liteR + ' ' + (bandPx - seamPx) + 'px,' + liteR + ' ' + (bandPx * 2) + 'px)';
299            }
300        } else {
301            bgStyle = lead === 'D' ? fullD : fullR;
302        }
303        return '<span style="' + baseCore + 'background:' + bgStyle + '">' + inner + '</span>';
304    };
305
306    ET.hubEditorialRatingChipHtml = function (kind, stateKey, stateName, rating, priorParty) {
307        if (!rating) return '';
308        var flip = null;
309        if (kind === 'house' && priorParty) {
310            var lead = ET.editorialRatingPartyLead(rating);
311            if (lead && lead !== 'Tie' && priorParty !== lead) flip = lead;
312        } else if (typeof ET.editorialRatingFlipLead === 'function') {
313            flip = ET.editorialRatingFlipLead(kind, stateKey, rating, stateName);
314        }
315        return ET.sleekHoverEditorialRatingChipHtml(rating, !!flip);
316    };
317
318    ET.countSenateSeatsFromEditorialRatings = function () {
319        var r = ET.NOT_UP_BASE_R || 0;
320        var d = ET.NOT_UP_BASE_D || 0;
321        var toss = 0;
322        if (!ET.ELECTION_2026) return { r: r, d: d, tossup: toss };
323        for (var f in ET.ELECTION_2026) {
324            if (!ET.ELECTION_2026.hasOwnProperty(f)) continue;
325            var nm = ET.NAME_BY_FIPS[f];
326            var key = ET.KEY_BY_NAME[nm];
327            var rating = ET.SENATE_EDITORIAL_RATINGS && ET.SENATE_EDITORIAL_RATINGS[key];
328            var lead = ET.editorialRatingPartyLead(rating);
329            if (lead === 'Tie') toss++;
330            else if (lead === 'R') r++;
331            else if (lead === 'D') d++;
332        }
333        return { r: r, d: d, tossup: toss };
334    };
335
336    ET.govEditorialNoElectionKeys = function () {
337        return new Set(['WA', 'UT', 'ND', 'MO', 'LA', 'MS', 'IN', 'KY', 'WV', 'VA', 'NC', 'DE', 'NJ', 'MT']);
338    };
339
340    ET.countGovSeatsFromEditorialRatings = function () {
341        var notUp = ET.forecastHubNotUpCounts('gov');
342        var r = notUp.r;
343        var d = notUp.d;
344        var toss = 0;
345        var skip = ET.govEditorialNoElectionKeys();
346        var ratings = ET.GOV_EDITORIAL_RATINGS || {};
347        Object.keys(ratings).forEach(function (key) {
348            if (skip.has(key)) return;
349            var lead = ET.editorialRatingPartyLead(ratings[key]);
350            if (lead === 'Tie') {
351                // Never gray for governors — assign to party holding the seat.
352                var held = (typeof ET.govFlipBaselineParty === 'function' && ET.govFlipBaselineParty(key))
353                    || (ET.GOV_INCUMBENT_PARTY && ET.GOV_INCUMBENT_PARTY[key])
354                    || 'R';
355                if (held === 'D') d++;
356                else r++;
357            } else if (lead === 'R') r++;
358            else if (lead === 'D') d++;
359        });
360        return { r: r, d: d, tossup: toss };
361    };
362
363    /** Flip seat counts for hub seat bar (ratings, no detail): stripe width ∝ seats gained by each party. */
364    ET.countEditorialFlipSeats = function (kind) {
365        var flipR = 0;
366        var flipD = 0;
367        if (kind === 'gov') {
368            if (!ET.GOV_EDITORIAL_RATINGS || typeof ET.editorialRatingFlipLead !== 'function') {
369                return { flipR: flipR, flipD: flipD };
370            }
371            var skipGov = ET.govEditorialNoElectionKeys();
372            Object.keys(ET.GOV_EDITORIAL_RATINGS).forEach(function (key) {
373                if (skipGov.has(key)) return;
374                var fl = ET.editorialRatingFlipLead('gov', key, ET.GOV_EDITORIAL_RATINGS[key], null);
375                if (fl === 'R') flipR++;
376                else if (fl === 'D') flipD++;
377            });
378        } else if (kind === 'senate') {
379            if (!ET.ELECTION_2026 || typeof ET.editorialRatingFlipLead !== 'function') {
380                return { flipR: flipR, flipD: flipD };
381            }
382            for (var f in ET.ELECTION_2026) {
383                if (!ET.ELECTION_2026.hasOwnProperty(f)) continue;
384                var nm = ET.NAME_BY_FIPS[f];
385                var keySen = ET.KEY_BY_NAME[nm];
386                if (!keySen || !ET.SENATE_EDITORIAL_RATINGS || !ET.SENATE_EDITORIAL_RATINGS[keySen]) continue;
387                var flSen = ET.editorialRatingFlipLead('senate', keySen, ET.SENATE_EDITORIAL_RATINGS[keySen], nm);
388                if (flSen === 'R') flipR++;
389                else if (flSen === 'D') flipD++;
390            }
391        }
392        return { flipR: flipR, flipD: flipD };
393    };
394
395    ET.forecastHubEditorialSeatCounts = function (kind) {
396        if (kind === 'gov') {
397            if (typeof ET.countGovSeatsFromForecastSim === 'function') {
398                var govSim = ET.countGovSeatsFromForecastSim();
399                if (govSim) return govSim;
400            }
401            if (typeof ET.countGovSeatsFromEditorialRatings === 'function') {
402                return ET.countGovSeatsFromEditorialRatings();
403            }
404            return null;
405        }
406        if (typeof ET.countSenateSeatsFromForecastSim === 'function') {
407            var senSim = ET.countSenateSeatsFromForecastSim();
408            if (senSim) return senSim;
409        }
410        if (typeof ET.countSenateSeatsFromEditorialRatings === 'function') {
411            return ET.countSenateSeatsFromEditorialRatings();
412        }
413        return null;
414    };
415
416    ET.setForecastHubSeatBarPartyNumbers = function (ids, kind) {
417        if (!ids) return;
418        var counts = ET.forecastHubEditorialSeatCounts(kind);
419        if (!counts) return;
420        var nr = document.getElementById(ids.nr);
421        var nd = document.getElementById(ids.nd);
422        if (nr) nr.textContent = String(counts.r);
423        if (nd) nd.textContent = String(counts.d);
424    };
425
426    ET.senateForecastHubRaceSlotFromRating = function (name, key) {
427        var rating = ET.SENATE_EDITORIAL_RATINGS && ET.SENATE_EDITORIAL_RATINGS[key];
428        if (!rating) return null;
429        var lead = ET.editorialRatingPartyLead(rating);
430        var winner = lead === 'Tie' ? 'TIE' : lead;
431        var tier = ET.editorialRatingToMarginTier(rating);
432        var flip = !!(ET.editorialRatingFlipLead('senate', key, rating, name));
433        return {
434            winner: winner,
435            tier: tier,
436            flip: flip,
437            hasPolls: false,
438            key: key,
439            name: name,
440            marginAbs: 0,
441            editorialRating: rating
442        };
443    };
444
445    ET.govForecastHubRaceSlotFromRating = function (key) {
446        var rating = ET.GOV_EDITORIAL_RATINGS && ET.GOV_EDITORIAL_RATINGS[key];
447        if (!rating) return null;
448        var lead = ET.editorialRatingPartyLead(rating);
449        var winner = lead === 'Tie' ? 'TIE' : lead;
450        var tier = ET.editorialRatingToMarginTier(rating);
451        // Never gray/TIE for governors — perfect tossup → party holding the governorship.
452        if (winner === 'TIE') {
453            var held = (typeof ET.govFlipBaselineParty === 'function' && ET.govFlipBaselineParty(key))
454                || (ET.GOV_INCUMBENT_PARTY && ET.GOV_INCUMBENT_PARTY[key])
455                || 'R';
456            winner = (held === 'D' || held === 'R') ? held : 'R';
457            tier = 'tilt';
458            rating = winner === 'D' ? 'tiltD' : 'tiltR';
459        }
460        var flip = !!(ET.editorialRatingFlipLead('gov', key, rating, null));
461        return {
462            winner: winner,
463            tier: tier,
464            flip: flip,
465            hasPolls: false,
466            key: key,
467            marginAbs: 0,
468            editorialRating: rating
469        };
470    };
471
472    /** Load Cook PVI + forecast model stack on race pages (hubs include these explicitly). */
473    if (!window.POLLSMAX_SKIP_FORECAST_MODEL) {
474        (function () {
475            var path = typeof location !== 'undefined' ? location.pathname : '';
476            var onForecastPage = /\/forecast\/?$/.test(path);
477            if (onForecastPage) {
478                window.POLLSMAX_SKIP_FORECAST_MODEL = true;
479                return;
480            }
481            var chain = [
482                '/assets/generic-ballot-polls-data.js?v=7',
483                '/assets/trump-approval-polls-data.js?v=7',
484                '/assets/cook-pvi-data.js?v=1',
485                '/assets/cook-race-ratings.js?v=2',
486                '/assets/forecast-senate-electoral.js?v=3',
487                '/assets/forecast-governor-electoral.js?v=2',
488                '/assets/forecast-model.js?v=ia-gov-tilt-r-1',
489                '/assets/forecast-calibration.js?v=forecast-cache-gov-rebuild-20260620'
490            ];
491            function loadNext(i) {
492                if (i >= chain.length) {
493                    if (typeof ET.applyForecastModelRatings === 'function') {
494                        ET.applyForecastModelRatings();
495                    }
496                    if (typeof ET.refreshStateRacePageHeadFromForecast === 'function' &&
497                        typeof ET.racePageViewMode === 'function' &&
498                        ET.racePageViewMode() === 'forecast') {
499                        ET.refreshStateRacePageHeadFromForecast();
500                    }
501                    return;
502                }
503                var s = document.createElement('script');
504                s.src = chain[i];
505                s.defer = true;
506                s.onload = function () { loadNext(i + 1); };
507                document.head.appendChild(s);
508            }
509            loadNext(0);
510        })();
511    }
512
513    function stableRatingDictString(obj) {
514        if (!obj || typeof obj !== 'object') return '{}';
515        return '{' + Object.keys(obj).sort().map(function (k) {
516            return JSON.stringify(k) + ':' + JSON.stringify(obj[k]);
517        }).join(',') + '}';
518    }
519
520    /** Hash of editorial / Cook inputs that affect forecast blend — busts baked charts when ratings change. */
521    ET.editorialRatingsBakeFingerprint = function () {
522        var parts = [
523            stableRatingDictString(ET.SENATE_EDITORIAL_RATINGS_BASE),
524            stableRatingDictString(ET.SENATE_EDITORIAL_BLEND_EFFECTIVE_FROM || {}),
525            stableRatingDictString(ET.SENATE_EDITORIAL_BLEND_PRIOR || {}),
526            stableRatingDictString(ET.SENATE_EDITORIAL_BLEND_MARGINS || {}),
527            stableRatingDictString(ET.GOV_EDITORIAL_RATINGS),
528            stableRatingDictString(ET.GOV_EDITORIAL_BLEND_MARGINS || {}),
529            stableRatingDictString(ET.COOK_HOUSE_RATINGS || {})
530        ];
531        var s = parts.join('|');
532        var h = 2166136261;
533        var i;
534        for (i = 0; i < s.length; i++) {
535            h ^= s.charCodeAt(i);
536            h = Math.imul(h, 16777619);
537        }
538        return (h >>> 0).toString(36);
539    };
540})();

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.