PageSourceSearch

https://nybpc.org/project-showcase

html nybpc.org collected 2026-09-24 11:14:39 UTC 23,024 bytes, 671 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4  <meta charset="UTF-8" />
5  <title>Project Showcase</title>
6
7  <!-- Preconnect for fonts (loads faster and avoids flicker) -->
8  <link rel="preconnect" href="https://fonts.googleapis.com">
9  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10  <link rel="preconnect" href="https://use.typekit.net" crossorigin>
11
12  <!-- leaflet.js -->
13  <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
14  
14<script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
14
15  
15<script src="https://cdn.jsdelivr.net/npm/@turf/turf@6/turf.min.js"></script>
15
16
17  <!-- Bootstrap -->
18  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">
19  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
20  
20<script src="https://code.jquery.com/jquery-3.6.0.min.js" crossorigin="anonymous"></script>
20
21  
21<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
21
22
23  <!-- Google Fonts (Raleway) with swap -->
24  <link href="https://fonts.googleapis.com/css2?family=Raleway:ital,wght@0,400;0,500;0,600;0,700;1,400;1,700&display=swap" rel="stylesheet">
25
26  <!-- Your CSS -->
27  <link rel="stylesheet" href="/css/style.css">
28
29  <!-- Fav Icon -->
30  <link rel="icon" type="image/x-icon" href="/imgs/favicon.ico">
31
32  <!-- Preload Proxima Nova core weights for faster swap -->
33  <link rel="preload" as="font" type="font/woff2" crossorigin
34        href="https://use.typekit.net/af/d7ff92/00000000000000007735e609/31/l?subset_id=2&fvd=n4&v=3">
35  <link rel="preload" as="font" type="font/woff2" crossorigin
36        href="https://use.typekit.net/af/e37e5a/00000000000000007735e60d/31/l?subset_id=2&fvd=n6&v=3">
37
38  <!-- Typekit Fonts (Proxima Nova / Futura PT) -->
39  <style>
40    @font-face {
41      font-family: proxima-nova;
42      src: url(https://use.typekit.net/af/d7ff92/00000000000000007735e609/31/l?subset_id=2&fvd=n4&v=3) format("woff2"),
43           url(https://use.typekit.net/af/d7ff92/00000000000000007735e609/31/d?subset_id=2&fvd=n4&v=3) format("woff");
44      font-weight: 400;
45      font-style: normal;
46      font-display: swap;
47    }
48
49    @font-face {
50      font-family: proxima-nova;
51      src: url(https://use.typekit.net/af/e37e5a/00000000000000007735e60d/31/l?subset_id=2&fvd=n6&v=3) format("woff2"),
52           url(https://use.typekit.net/af/e37e5a/00000000000000007735e60d/31/d?subset_id=2&fvd=n6&v=3) format("woff");
53      font-weight: 600;
54      font-style: normal;
55      font-display: swap;
56    }
57
58    @font-face {
59      font-family: futura-pt;
60      src: url(https://use.typekit.net/af/26b618/000000000000000077586b59/31/l?subset_id=2&fvd=n4&v=3) format("woff2"),
61           url(https://use.typekit.net/af/26b618/000000000000000077586b59/31/d?subset_id=2&fvd=n4&v=3) format("woff");
62      font-weight: 400;
63      font-style: normal;
64      font-display: swap;
65    }
66
67    body {
68      font-family: proxima-nova, "Helvetica Neue", Arial, sans-serif;
69      background-color: #fff;
70    }
71  </style>
72</head>
73<body class="bg-white" style="border: 20px solid #fafafa;">
74
75  <!-- Shared Header -->
76  <!-- Start of global wrapper -->
77
78  <header style="background-color:#fafafa; ">
79    <div style=" max-width:1500px; margin-left:auto; margin-right:auto; padding-top: 24px; padding-bottom: 24px;">
80
81    <nav class="navbar navbar-expand-lg" style="background-color:#fafafa; padding:0 0 0 0; z-index:1000;">
82      <div class="container-fluid">
83
84        <!-- Left: Logo + Brand -->
85        <a class="navbar-brand d-flex align-items-center fw-semibold" href="/">
86          <img src="/imgs/left_logo.png" class="Header-branding-logo me-2" alt="Logo left">
87          <img src="/imgs/right_logo.png" class="Header-branding-logo me-2" alt="Logo right">
88        </a>
89
90      
91
92        <!-- Right: Nav links -->
93        <div id="nav" class="collapse navbar-collapse me-5">
94          <ul class="navbar-nav ms-auto align-items-lg-center">
95            <li class="nav-item"><a class="nav-link Header-Nav-Item" href="/">ABOUT</a></li>
96            <li class="nav-item"><a class="nav-link Header-Nav-Item-Selected" href="/project-showcase">NYBPC</a></li>
97            <li class="nav-item"><a class="nav-link Header-Nav-Item" href="/students">STUDENTS</a></li>
98            <li class="nav-item"><a class="nav-link Header-Nav-Item" href="/judges">JUDGES</a></li>
99            <li class="nav-item"><a class="nav-link Header-Nav-Item" href="/supporters">SUPPORTERS</a></li>
100            <li class="nav-item"><a class="nav-link Header-Nav-Item" href="/news">NEWS</a></li>
101            <li id="signup" class="nav-item"><a class="nav-link Header-Nav-Item" href="/signup">SIGN UP</a></li>
102            <li class="nav-item dropdown" id="profileDropdown" style="display:none;">
103              <div class="dropdown">
104                  <button class="btn dropdown-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
105                      PROFILE
106                  </button>
107
108                  <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
109                      <li><a class="dropdown-item" id="profileLink" href="#">My Profile</a></li>
110                      <li><a class="dropdown-item" id="logout" href="/logout">Logout</a></li>
111                      
112                  </ul>
113              </div>
114            </li>
115         
116          </ul>
117        </div>
118
119      </div>
120    </nav>
121
122    <!-- second navbar once user is signed in, grab quick-links -->
123    <nav id="quicklinksContainer" class="second-bar" style="display:none; border: none; z-index:1;">
124      <ul id="quicklinks" style="border: none; padding: 0px; margin:0px;">
125      </ul>
126      <ul class="navbar-nav">
127        <li class="nav-item dropdown" id="editDropdown" style="display:none;">
128          <button class="btn dropdown-toggle Header-Nav-Item"
129                  style="color: white;"
130                  type="button"
131                  data-bs-toggle="dropdown"
132                  aria-expanded="false">
133            EDIT
134          </button>
135          <ul class="dropdown-menu" id="editDropdownMenu"></ul>
136        </li>
137      </ul>
138    </nav>
139
140    </div>
141  </header>
142  
143<script>
144  
145document.addEventListener("DOMContentLoaded", async () => {
146  const quicklinks = document.getElementById("quicklinks");
147  bar = document.querySelector(".second-bar");
148  try {
149    const res = await fetch("/users/quicklinks", { credentials: "include" });
150    const data = await res.json();
151
152    quicklinks.innerHTML = "";
153
154    data.quickLinks.forEach(link => {
155      const li = document.createElement("li");
156      li.style.listStyle = "none";
157
158      const a = document.createElement("a");
159      a.href = link.href;
160      a.textContent = link.text;
161      a.style.color = "white";
162      a.style.textDecoration = "none";
163      a.style.fontSize = "16px";
164      a.style.display = "inline-block";
165      a.style.whiteSpace = "nowrap";
166      a.style.padding = "4px 10px";
167
168      li.appendChild(a);
169      quicklinks.appendChild(li);
170    });
171    quicklinks.style.display = "flex";
172    quicklinks.style.flexWrap = "wrap";
173    quicklinks.style.alignItems = "center";
174    quicklinks.style.border = "none";
175    quicklinks.style.padding = "6px 0";
176    quicklinks.style.listStyle = "none";
177    quicklinks.style.gap = "4px";
178
179    document.getElementById("quicklinksContainer").style.display = "block"
180  } catch(err) {
181    console.error("Failed to load quick links", err);
182  }
183
184  fetch("/users/profile-link", { credentials: "include" })
185  .then(res => res.json())
186  .then(data => {
187    const profileLink = document.getElementById("profileLink");
188    if (data.loggedIn) {
189      profileLink.href = data.profileHref;
190      document.getElementById("signup").style.display = "none";
191      document.getElementById("profileDropdown").style.display = "block";
192    }
193  });
194
195});
196</script>
196
197  
198
199
200  <!-- Main Content -->
201  <main class="container" style="max-width: 1500px; width: auto; height: auto; padding: 0;">
202    <div class="row ">
203    <div class="col-8">
204        <style>
205    #map {
206        height: 700px;
207        width: 100%;
208        background: #eee;
209    }
210</style>
211
212<div id="map"></div>
213
214<script>
215    const map = L.map('map').setView([43.0, -75.0], 6);
216
217    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
218        maxZoom: 18
219    }).addTo(map);
220
221    const regions = {
222        "North Country": ["Jefferson", "Lewis", "St Lawrence", "Franklin", "Hamilton", "Essex", "Clinton"],
223        "Mohawk Valley": ["Oneida", "Herkimer", "Fulton", "Montgomery", "Otsego", "Schoharie"],
224        "Western New York": ["Niagara", "Erie", "Allegany", "Cattaraugus", "Chautauqua"],
225        "Central New York": ["Oswego", "Cayuga", "Cortland", "Madison", "Onondaga"],
226        "New York City": ["Bronx", "Kings", "New York", "Richmond", "Queens"],
227        "Long Island": ["Nassau", "Suffolk"],
228        "Finger Lakes": ["Orleans", "Monroe", "Wayne", "Genesee", "Wyoming", "Livingston", "Ontario", "Yates", "Seneca"],
229        "Southern Tier": ["Steuben", "Schuyler", "Chemung", "Tompkins", "Tioga", "Broome", "Chenango", "Delaware"],
230        "Mid-Hudson": ["Putnam", "Westchester", "Rockland", "Sullivan", "Ulster", "Orange", "Dutchess"],
231        "Capital District": ["Warren", "Washington", "Saratoga", "Schenectady", "Rensselaer", "Albany", "Greene", "Columbia"]
232    };
233
234    const regionColors = {
235        "Finger Lakes": "#2ca02c",
236        "Southern Tier": "#ff7f0e",
237        "Central New York": "#9467bd",
238        "Mohawk Valley": "#8c564b",
239        "Western New York": "#e377c2",
240        "North Country": "#94c765",
241        "Mid-Hudson": "#bcbd22",
242        "New York City": "#17becf",
243        "Long Island": "#d62728",
244        "Capital District": "#33ffbb"
245    };
246
247    function onRegionClick(regionName) {
248        console.log("Clicked region:", regionName);
249        window.selectedRegion = regionName;
250        window.dispatchEvent(new CustomEvent("region-selected", {
251            detail: { region: regionName }
252        }));
253    }
254
255    const midHudsonBounds = [
256        [40.95, -74],
257        [40.95, -73.5],
258        [42.15, -73.5],
259        [42.15, -74.6],
260        [41.85, -75.35],
261        [40.95, -74]
262    ];
263
264    const midHudsonPolygon = L.polygon(midHudsonBounds, {
265        color: regionColors["Mid-Hudson"],
266        weight: 2,
267        fillColor: regionColors["Mid-Hudson"],
268        fillOpacity: 0.5
269    }).addTo(map);
270
271    midHudsonPolygon.bindTooltip("Mid-Hudson");
272    midHudsonPolygon.on("click", () => {
273        onRegionClick("Mid-Hudson");
274    });
275    
276    fetch('/Counties.json')
277        .then(res => res.json())
278        .then(data => {
279            L.geoJSON(data, {
280                style: {
281                    color: "#444",
282                    weight: 1,
283                    fillOpacity: 0.2
284                }
285            }).addTo(map);
286
287            //add region polygons
288            for (let regionName in regions) {
289                if (regionName === "Mid-Hudson") {
290                    continue;
291                }
292                const countyNames = regions[regionName];
293
294                //map regions
295                const countyFeatures = data.features.filter(
296                    f => countyNames.includes(f.properties.NAME)
297                );
298
299                if (countyFeatures.length === 0) {
300                    console.warn(`No counties found for region: ${regionName}`);
301                    continue;
302                }
303
304                try {
305                    let merged = countyFeatures[0];
306
307                    for (let i = 1; i < countyFeatures.length; i++) {
308                        merged = turf.union(merged, countyFeatures[i]);
309                    }
310
311                    L.geoJSON(merged, {
312                        style: {
313                            color: regionColors[regionName] || "#000",
314                            weight: 2,
315                            fillColor: regionColors[regionName] || "#000",
316                            fillOpacity: 0.5
317                        },
318                        onEachFeature: function(feature, layer) {
319                            layer.bindTooltip(regionName);
320
321                            layer.on("click", () => {
322                                onRegionClick(regionName);
323                            });
324                        }
325                    }).addTo(map);
326
327                    midHudsonPolygon.bringToFront();
328
329                } catch (err) {
330                    console.error(`Error creating region ${regionName}:`, err);
331                }
332            }
333        })
334        .catch(err => {
335            console.error("Error loading Counties.json:", err);
336        });
337</script>
337
338    </div>
339
340    <div class="col-4">
341        <p>Click on a region to view more information.</p>
342        <label><strong>Select Year</strong></label>
343        <select id="year-select" class="form-select mb-3">
344            <option value="">All Years</option>
345        </select>
346
347        <div class="btn-group mb-3" role="group">
348            <button class="btn btn-primary showcase-btn" data-type="companies">Companies</button>
349            <button class="btn btn-outline-primary showcase-btn" data-type="students">Schools and Students</button>
350            <button class="btn btn-outline-primary showcase-btn" data-type="event">Event Info</button>
351        </div>
352        <div id="company-list"></div>
353    </div>
354</div>
355
356  </main>
357
358  <!-- Shared Footer -->
359  <footer class="py-3">
360    <div class="Footer-Container mx-auto border-top" style="max-width:1500px; width:100%; padding:30px 0;">
361  <div class="row g-0" style="text-align: center;">
362   <div class="row g-0" style="max-width:1500px; margin:0 auto; padding:50px 0; align-items:flex-start;">
363
364  <!-- Left Text Column -->
365  <div class="col-md-6 px-4">
366    <h2 style="color:#002d70; font-weight:700; text-transform:uppercase;text-align: left;">SPONSOR TODAY!</h2>
367    <p style="font-family:Raleway; font-size:18px; color:#000; line-height:1.7em; text-align: left;">
368      Please complete the form below and we’ll be in touch to discuss sponsorship options.
369    </p>
370    <h4 style="color:#002d70; font-weight:700; text-transform:uppercase; margin-top:60px;text-align: left;">
371      THANK YOU FOR YOUR SUPPORT!
372    </h4>
373  </div>
374
375  <!-- Right Form Column -->
376  <div class="col-md-6 px-4">
377    <form class="row g-3">
378
379      <!-- Name (split into two fields) -->
380      <div class="col-12">
381<label class="form-label sponsor-label">
382  Name <span>(required)</span>
383</label>        <div class="row g-2">
384          <div class="col-md-6">
385            <input type="text" class="form-control sponsor-input" placeholder="First Name" required>
386          </div>
387          <div class="col-md-6">
388            <input type="text" class="form-control sponsor-input" placeholder="Last Name" required>
389          </div>
390        </div>
391      </div>
392
393      <!-- Email -->
394      <div class="col-12">
395<label class="form-label sponsor-label">
396  Email <span>(required)</span>
397</label>
398        <input type="email" class="form-control sponsor-input" placeholder="Email" required>
399      </div>
400
401      <!-- Phone -->
402      <div class="col-12">
403<label class="form-label sponsor-label">
404  Phone <span>(required)</span>
405</label>        <input type="tel" class="form-control sponsor-input" placeholder="Phone" required>
406      </div>
407
408      <!-- Submit Button -->
409      <div class="col-12">
410        <button type="submit" class="btn sponsor-btn w-100">SUBMIT</button>
411      </div>
412
413    </form>
414  </div>
415
416</div>
417
418  </div>
419 <div class="row g-0">
420  <div class="col-12 text-center"
421       style="
422         font-family: Raleway, sans-serif;
423         font-weight: 700;
424         font-style: normal;
425         font-size: 22px;
426         letter-spacing: 0.05em;
427         line-height: 1.6em;
428         text-transform: uppercase;
429         color: #002d70;
430         padding: 25px 0;
431       ">
432    Need more time? <br class="d-md-none" />
433   <span class="sponsorship-link">
434  Download our Sponsorship Summary
435</span>
436
437  </div>
438</div>
439
440 <div class="row g-0">
441    <div class="col-12" style="text-align: center;"><a href="/s/NYBPC_Sponsorship.pdf"
442   class="sponsor-summary-btn"
443   target="_blank">
444  SPONSORSHIP SUMMARY
445</a>
446</div>
447  </div>
448
449  
450 
451<script>
452document.addEventListener("DOMContentLoaded", async () => {
453  try {
454    const res = await fetch("/events/years/state-released");
455    const years = await res.json();
456
457    const select = document.getElementById("year-select");
458
459    select.innerHTML = '<option value="">All Years</option>';
460
461    years.forEach((year, index) => {
462      const option = document.createElement("option");
463      option.value = year;
464      option.textContent = year;
465
466      // default select most recent year
467      if (index === 0) {
468        option.selected = true;
469      }
470
471      select.appendChild(option);
472    });
473
474  } catch (err) {
475    console.error("Error loading years:", err);
476  }
477});
478</script>
478
479
480
481
482<script>
483$(document).ready(function () {
484
485    let selectedRegionEventId = null;
486    let selectedYear = $("#year-select").val();
487
488    $("#year-select").on("change", function () {
489        selectedYear = $(this).val();
490
491        if (!selectedYear) {
492            selectedRegionEventId = null;
493            setButtonsEnabled(false);
494            return;
495        }
496
497        setButtonsEnabled(true);
498
499        // If a region is already selected, reload companies automatically
500        if (selectedRegionName) {
501            $("#company-list").html(`<p>Reloading data for ${selectedRegionName} (${selectedYear})...</p>`);
502            loadEventIdForRegion(selectedRegionName);
503        }
504    });
505
506    function setButtonsEnabled(enabled) {
507        $(".showcase-btn").prop("disabled", !enabled);
508
509        if (!enabled) {
510            $("#company-list").html("<p>Please select a year to view data.</p>");
511        }
512    }
513
514    function highlightActiveButton(activeType) {
515        $(".showcase-btn").each(function () {
516            if ($(this).data("type") === activeType) {
517                $(this).removeClass("btn-outline-primary").addClass("btn-primary");
518            } else {
519                $(this).removeClass("btn-primary").addClass("btn-outline-primary");
520            }
521        });
522    }
523
524    window.addEventListener("region-selected", async (e) => {
525        if (!selectedYear) {
526            $("#company-list").html("<p>Please select a year first.</p>");
527            return;
528        }
529        const regionName = e.detail.region;
530
531        $("#company-list").html(`<p>Loading event for ${regionName}...</p>`);
532
533        try {
534            //get event id based on region and year
535            const res = await fetch(`/showcase/event/${selectedYear}/${regionName}`);
536            const data = await res.json();
537
538            if (!data.event_id) {
539                $("#company-list").html(`<p>No event found for ${regionName}.</p>`);
540                selectedRegionEventId = null;
541                return;
542            }
543
544            selectedRegionEventId = data.event_id;
545
546            //select companies option first
547            highlightActiveButton("companies");
548            loadCompanies();
549
550        } catch (err) {
551            console.error(err);
552            $("#company-list").html("<p>Failed to load region data.</p>");
553        }
554    });
555
556    // Button handler
557    $(".showcase-btn").on("click", function () {
558        const type = $(this).data("type");
559
560        highlightActiveButton(type);
561
562        if (!selectedRegionEventId) {
563            $("#company-list").html("<p>Select a region first.</p>");
564            return;
565        }
566
567        if (type === "companies") loadCompanies();
568        if (type === "students") loadStudents();
569        if (type === "event") loadEventInfo();
570    });
571
572    async function loadCompanies() {
573        try {
574            const res = await fetch(`/showcase/companies/${selectedRegionEventId}`);
575            const data = await res.json();
576
577            if (!data.companies || data.companies.length === 0) {
578                $("#company-list").html("<p>No companies participating.</p>");
579                return;
580            }
581
582            let html = `<h4>Companies</h4><ul>`;
583            data.companies.forEach(team => {
584                html += `<li>${team.team_name}</li>`;
585            });
586            html += `</ul>`;
587
588            $("#company-list").html(html);
589
590        } catch (err) {
591            console.error(err);
592            $("#company-list").html("<p>Error loading companies.</p>");
593        }
594    }
595
596    async function loadStudents() {
597        console.log("Load:", selectedRegionEventId);
598        try {
599            const res = await fetch(`/showcase/school/${selectedRegionEventId}`);
600            const data = await res.json();
601
602            $("#company-list").html(`
603                <h4>Schools & Students</h4>
604                <p><strong>Students:</strong> ${data.student}</p>
605                <p><strong>Schools:</strong> ${data.schools.length}</p>
606                <ul>${data.schools.map(s => `<li>${s}</li>`).join("")}</ul>
607            `);
608
609        } catch (err) {
610            console.error(err);
611            $("#company-list").html("<p>Error loading schools/students.</p>");
612        }
613    }
614
615    async function loadEventInfo() {
616        try {
617            const res = await fetch(`/showcase/winners/${selectedRegionEventId}`);
618            const data = await res.json();
619
620            if (!data.winners || data.winners.length === 0) {
621                $("#company-list").html("<p>No event information available.</p>");
622                return;
623            }
624
625            // Group winners by track
626            const grouped = data.winners.reduce((acc, w) => {
627                acc[w.track] ??= [];
628                acc[w.track].push(w);
629                return acc;
630            }, {});
631
632            let html = `<h4>Event Winners</h4>`;
633
634            Object.entries(grouped).forEach(([track, winners]) => {
635                html += `<h5 class="mt-3">${track}</h5><ul>`;
636                winners.forEach(w => {
637                    const placeLabel =
638                        w.place === 1 ? "1st Place" :
639                        w.place === 2 ? "2nd Place" :
640                        w.place === 3 ? "3rd Place" :
641                        "Concept Stage Award";
642
643                    html += `
644                        <li>
645                            <strong>${placeLabel}</strong><br>
646                            ${w.name} <em>(${w.school})</em>
647                        </li>
648                    `;
649                });
650                html += `</ul>`;
651            });
652
653            $("#company-list").html(html);
654
655        } catch (err) {
656            console.error(err);
657            $("#company-list").html("<p>Error loading event info.</p>");
658        }
659    }
660    if (!selectedYear) {
661        setButtonsEnabled(false);
662    }
663
664
665});
666</script>
666
667  </footer>
668
669  <!-- 
669<script src="/js/app.js"></script>
669 -->
670</body>
671</html>

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.