Removed PCIM Asia, IMAPS Onshoring Workshop, and SEMICON Taiwan (not happening) and ICSCRM 2026 (no international travel to Japan). Flagged WiPDA-Europe (Aalborg) as a real candidate worth raising at the next meeting. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
345 lines
17 KiB
JavaScript
345 lines
17 KiB
JavaScript
/* ---------- Opportunities calendar ----------
|
||
Each entry: { start: "YYYY-MM-DD", end: "YYYY-MM-DD" (defaults to start),
|
||
title, location, attendee (optional) }.
|
||
Multi-day entries render as one bar spanning their full date range;
|
||
bars continuing past a week boundary lose the rounded corner on that side. */
|
||
var OPPORTUNITIES = [
|
||
{ start: "2026-08-23", end: "2026-08-27", title: "SPIE", location: "San Diego, CA", attendee: "Peter", url: "https://spie.org/conferences-and-exhibitions/optics-and-photonics",
|
||
abstract: "SPIE's flagship annual optics and photonics conference and exhibition, held every August in San Diego since the 1960s. Brings together 10,000+ scientists, engineers, and industry professionals across optical engineering, nanoscience, quantum science, and organic photonics." },
|
||
{ start: "2026-11-10", end: "2026-11-13", title: "SEMICON Europa", location: "Munich, Germany", url: "https://www.semiconeuropa.org/",
|
||
abstract: "Semiconductor and electronics manufacturing trade show co-located with electronica in Munich. Covers fab management, advanced packaging, and smart manufacturing across the chip supply chain, drawing 8,000+ visitors from equipment vendors, fabs, and OSATs." },
|
||
{ start: "2026-09-01", title: "NCN Mix & Match", location: "DESY Campus, Hamburg, Germany", url: "https://northernchipnetwork.de/veranstaltungen/event",
|
||
abstract: "Northern Chip Network's first networking event for microelectronics professionals in Northern Germany, held on TXproducts' home DESY campus (1pm-6pm). Lightning-pitch format for presenting projects/research; open to members and prospective members." },
|
||
{ start: "2026-09-08", end: "2026-09-10", title: "SNIB2026", location: "University of Hamburg, Germany", attendee: "Peter Gaal, Daniel Schmidt", url: "https://www.sni-portal.de/en/sni-conferences/snib2026",
|
||
abstract: "German Conference on Research with Synchrotron Radiation, Neutrons, Ions and Accelerators at Large-Scale Facilities — brings together researchers, industry leaders, and policymakers, with a focus on AI, digitalisation, sustainability, and technology transfer. Hosted by DESY, European XFEL, and University of Hamburg." }
|
||
];
|
||
var MONTH_NAMES = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
||
var MONTH_ABBR = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
||
var oppCursor = new Date();
|
||
oppCursor.setDate(1);
|
||
function oppKey(d) {
|
||
return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0");
|
||
}
|
||
function parseOppDate(s) {
|
||
var parts = s.split("-").map(Number);
|
||
return new Date(parts[0], parts[1] - 1, parts[2]);
|
||
}
|
||
function addOppDays(d, n) {
|
||
var r = new Date(d);
|
||
r.setDate(r.getDate() + n);
|
||
return r;
|
||
}
|
||
function buildOppEvents() {
|
||
return OPPORTUNITIES.map(function (o) {
|
||
return {
|
||
title: o.title, location: o.location, attendee: o.attendee, url: o.url, abstract: o.abstract,
|
||
start: parseOppDate(o.start), end: parseOppDate(o.end || o.start)
|
||
};
|
||
}).sort(function (a, b) { return a.start - b.start; });
|
||
}
|
||
function formatOppDateRange(start, end) {
|
||
if (start.getTime() === end.getTime()) {
|
||
return MONTH_ABBR[start.getMonth()] + " " + start.getDate() + ", " + start.getFullYear();
|
||
}
|
||
if (start.getFullYear() === end.getFullYear()) {
|
||
if (start.getMonth() === end.getMonth()) {
|
||
return MONTH_ABBR[start.getMonth()] + " " + start.getDate() + "–" + end.getDate() + ", " + start.getFullYear();
|
||
}
|
||
return MONTH_ABBR[start.getMonth()] + " " + start.getDate() + " – " + MONTH_ABBR[end.getMonth()] + " " + end.getDate() + ", " + start.getFullYear();
|
||
}
|
||
return MONTH_ABBR[start.getMonth()] + " " + start.getDate() + ", " + start.getFullYear() + " – " + MONTH_ABBR[end.getMonth()] + " " + end.getDate() + ", " + end.getFullYear();
|
||
}
|
||
function renderOppEventList() {
|
||
var listEl = document.getElementById("opp-event-list");
|
||
if (!listEl) return;
|
||
listEl.innerHTML = "";
|
||
var events = buildOppEvents();
|
||
if (!events.length) {
|
||
var empty = document.createElement("div");
|
||
empty.className = "opp-list-empty";
|
||
empty.textContent = "No events yet.";
|
||
listEl.appendChild(empty);
|
||
return;
|
||
}
|
||
events.forEach(function (e) {
|
||
var row = document.createElement("div");
|
||
row.className = "opp-list-row";
|
||
|
||
var meta = document.createElement("div");
|
||
meta.className = "opp-list-meta";
|
||
var datesEl = document.createElement("div");
|
||
datesEl.className = "opp-list-dates";
|
||
datesEl.textContent = formatOppDateRange(e.start, e.end);
|
||
meta.appendChild(datesEl);
|
||
if (e.location) {
|
||
var locEl = document.createElement("div");
|
||
locEl.className = "opp-list-loc";
|
||
locEl.textContent = e.location;
|
||
meta.appendChild(locEl);
|
||
}
|
||
if (e.attendee) {
|
||
var attEl = document.createElement("div");
|
||
attEl.className = "opp-list-attendee";
|
||
attEl.textContent = "Attending: " + e.attendee;
|
||
meta.appendChild(attEl);
|
||
}
|
||
if (e.url) {
|
||
var linkEl = document.createElement("a");
|
||
linkEl.className = "opp-list-link";
|
||
linkEl.href = e.url;
|
||
linkEl.target = "_blank";
|
||
linkEl.rel = "noopener";
|
||
linkEl.textContent = "Website";
|
||
meta.appendChild(linkEl);
|
||
}
|
||
row.appendChild(meta);
|
||
|
||
var about = document.createElement("div");
|
||
about.className = "opp-list-about";
|
||
var badge = document.createElement(e.url ? "a" : "span");
|
||
badge.className = "opp-list-badge";
|
||
badge.textContent = e.title;
|
||
if (e.url) {
|
||
badge.href = e.url;
|
||
badge.target = "_blank";
|
||
badge.rel = "noopener";
|
||
}
|
||
about.appendChild(badge);
|
||
if (e.abstract) {
|
||
var abstractEl = document.createElement("p");
|
||
abstractEl.className = "opp-list-abstract";
|
||
abstractEl.textContent = e.abstract;
|
||
about.appendChild(abstractEl);
|
||
}
|
||
row.appendChild(about);
|
||
|
||
row.addEventListener("click", function () {
|
||
oppCursor = new Date(e.start.getFullYear(), e.start.getMonth(), 1);
|
||
renderOppCalendar();
|
||
});
|
||
listEl.appendChild(row);
|
||
});
|
||
}
|
||
function renderOppCalendar() {
|
||
var label = document.getElementById("opp-month-label");
|
||
var grid = document.getElementById("opp-cal-grid");
|
||
if (!label || !grid) return;
|
||
label.textContent = MONTH_NAMES[oppCursor.getMonth()] + " " + oppCursor.getFullYear();
|
||
grid.innerHTML = "";
|
||
|
||
var weekdayRow = document.createElement("div");
|
||
weekdayRow.className = "opp-cal-weekdays";
|
||
["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].forEach(function (wd) {
|
||
var wdEl = document.createElement("div");
|
||
wdEl.className = "opp-cal-weekday";
|
||
wdEl.textContent = wd;
|
||
weekdayRow.appendChild(wdEl);
|
||
});
|
||
grid.appendChild(weekdayRow);
|
||
|
||
var year = oppCursor.getFullYear(), month = oppCursor.getMonth();
|
||
var firstDow = new Date(year, month, 1).getDay();
|
||
var daysInMonth = new Date(year, month + 1, 0).getDate();
|
||
var totalCells = Math.ceil((firstDow + daysInMonth) / 7) * 7;
|
||
var gridStart = new Date(year, month, 1 - firstDow);
|
||
var todayKey = oppKey(new Date());
|
||
|
||
var events = buildOppEvents();
|
||
|
||
for (var w = 0; w < totalCells / 7; w++) {
|
||
var weekStart = addOppDays(gridStart, w * 7);
|
||
var weekEnd = addOppDays(weekStart, 6);
|
||
var weekEl = document.createElement("div");
|
||
weekEl.className = "opp-cal-week";
|
||
|
||
var weekEvents = events
|
||
.filter(function (e) { return e.start <= weekEnd && e.end >= weekStart; })
|
||
.sort(function (a, b) { return a.start - b.start; });
|
||
var lanes = [];
|
||
weekEvents.forEach(function (e) {
|
||
var colStart = Math.max(0, Math.round((Math.max(e.start, weekStart) - weekStart) / 86400000));
|
||
var colEnd = Math.min(6, Math.round((Math.min(e.end, weekEnd) - weekStart) / 86400000));
|
||
var lane = 0;
|
||
while (lanes[lane] && lanes[lane].some(function (r) { return !(colEnd < r[0] || colStart > r[1]); })) lane++;
|
||
if (!lanes[lane]) lanes[lane] = [];
|
||
lanes[lane].push([colStart, colEnd]);
|
||
e._col = [colStart, colEnd];
|
||
e._lane = lane;
|
||
});
|
||
var cellMinHeight = Math.max(96, 34 + lanes.length * 24 + 10);
|
||
|
||
for (var d = 0; d < 7; d++) {
|
||
var cellDate = addOppDays(weekStart, d);
|
||
var outside = cellDate.getMonth() !== month;
|
||
var key = oppKey(cellDate);
|
||
var cell = document.createElement("div");
|
||
cell.className = "opp-cal-cell" + (outside ? " outside" : "") + (key === todayKey ? " today" : "");
|
||
cell.style.minHeight = cellMinHeight + "px";
|
||
var numEl = document.createElement("div");
|
||
numEl.className = "opp-cal-daynum";
|
||
numEl.textContent = cellDate.getDate();
|
||
cell.appendChild(numEl);
|
||
weekEl.appendChild(cell);
|
||
}
|
||
|
||
weekEvents.forEach(function (e) {
|
||
var bar = document.createElement("div");
|
||
bar.className = "opp-cal-event-bar";
|
||
if (e.start < weekStart) bar.classList.add("continues-left");
|
||
if (e.end > weekEnd) bar.classList.add("continues-right");
|
||
bar.style.left = "calc(" + (e._col[0] / 7 * 100) + "% + 4px)";
|
||
bar.style.width = "calc(" + ((e._col[1] - e._col[0] + 1) / 7 * 100) + "% - 8px)";
|
||
bar.style.top = (34 + e._lane * 24) + "px";
|
||
var titleEl = document.createElement(e.url ? "a" : "span");
|
||
titleEl.className = "opp-cal-event-title";
|
||
titleEl.textContent = e.title;
|
||
if (e.url) {
|
||
titleEl.href = e.url;
|
||
titleEl.target = "_blank";
|
||
titleEl.rel = "noopener";
|
||
}
|
||
bar.appendChild(titleEl);
|
||
if (e.location) {
|
||
var locEl = document.createElement("span");
|
||
locEl.className = "opp-cal-event-loc";
|
||
locEl.textContent = e.location;
|
||
bar.appendChild(locEl);
|
||
}
|
||
bar.title = e.title + (e.location ? " — " + e.location : "") + (e.attendee ? " (" + e.attendee + ")" : "");
|
||
weekEl.appendChild(bar);
|
||
});
|
||
|
||
grid.appendChild(weekEl);
|
||
}
|
||
}
|
||
/* ---------- Conference Scout results ----------
|
||
Snapshot of the weekly research agent's report, hand-reviewed and pasted in here.
|
||
Each entry: { title, dates (display text), location, url, reason }. */
|
||
var SCOUT_RESULTS = [
|
||
{ title: "WiPDA-Europe 2026", dates: "Sept 20–22, 2026", location: "Aalborg, Denmark",
|
||
url: "https://www.energy.aau.dk/wipda-europe-2026-e156452",
|
||
reason: "IEEE workshop on wide-bandgap (GaN/SiC) power devices — niche but highly targeted technical audience. Existing focus area. Flagged as a real candidate — worth raising at the meeting." },
|
||
{ title: "XTOP 2026", dates: "Sept 21–25, 2026", location: "Karlsruhe, Germany",
|
||
url: "https://xtop2026.kit.edu",
|
||
reason: "16th Biennial Conference on High-Resolution X-Ray Diffraction and Imaging — direct X-ray diffraction/imaging overlap. Existing focus area." },
|
||
{ title: "NOBUGS 2026", dates: "Sept 21–25, 2026 (core Sept 22–24)", location: "Schenefeld Campus (European XFEL), near Hamburg, Germany",
|
||
url: "https://nobugs2026.xfel.eu/",
|
||
reason: "Explicitly covers metadata/data formats and FAIR data management for beamline software; co-hosted by DESY/European XFEL on TXproducts' home campus — the single best topical fit found. Existing focus area." },
|
||
{ title: "RDA 27th Plenary Meeting (P27)", dates: "Oct 6–8, 2026", location: "London, UK",
|
||
url: "https://www.rd-alliance.org/plenaries/p27/",
|
||
reason: "Research Data Alliance plenary — strong FAIR/research-data-infrastructure networking, though less domain-specific than NOBUGS. Existing focus area." },
|
||
{ title: "AFLS2026", dates: "Nov 16–20, 2026", location: "Harare, Zimbabwe",
|
||
url: "https://events.saip.org.za/event/255",
|
||
reason: "African Light Source / African Physical Society joint meeting — the Africa-based synchrotron-development community, with materials/X-ray characterization topics. Existing focus area." },
|
||
{ title: "MRS Fall Meeting & Exhibit 2026", dates: "Nov 29–Dec 4, 2026", location: "Boston, MA, USA",
|
||
url: "https://www.mrs.org/",
|
||
reason: "Broad materials-science conference with strong X-ray characterization tracks. Existing focus area." },
|
||
{ title: "GOMACTech 2027", dates: "March 22–25, 2027", location: "Pasadena, CA, USA",
|
||
url: "https://www.gomactech.net/",
|
||
reason: "The premier US DoD microelectronics conference — covers GaN/SiC power devices and X-ray characterization/reliability for defense microelectronics. Potential new business (defense)." },
|
||
{ title: "IEEE RadarConf'27", dates: "May 1–6, 2027", location: "Bangalore, India",
|
||
url: "https://radarconf2027.org/",
|
||
reason: "Radar hardware relies heavily on GaN/SiC RF power amplifiers — a defense-radar audience in a growing Indian defense-electronics market. Potential new business (defense)." }
|
||
];
|
||
function renderOppScoutList() {
|
||
var listEl = document.getElementById("opp-scout-list");
|
||
if (!listEl) return;
|
||
listEl.innerHTML = "";
|
||
if (!SCOUT_RESULTS.length) {
|
||
var empty = document.createElement("div");
|
||
empty.className = "opp-list-empty";
|
||
empty.textContent = "No results yet.";
|
||
listEl.appendChild(empty);
|
||
return;
|
||
}
|
||
SCOUT_RESULTS.forEach(function (s) {
|
||
var row = document.createElement("div");
|
||
row.className = "opp-list-row";
|
||
row.style.cursor = "default";
|
||
|
||
var meta = document.createElement("div");
|
||
meta.className = "opp-list-meta";
|
||
var datesEl = document.createElement("div");
|
||
datesEl.className = "opp-list-dates";
|
||
datesEl.textContent = s.dates;
|
||
meta.appendChild(datesEl);
|
||
if (s.location) {
|
||
var locEl = document.createElement("div");
|
||
locEl.className = "opp-list-loc";
|
||
locEl.textContent = s.location;
|
||
meta.appendChild(locEl);
|
||
}
|
||
if (s.url) {
|
||
var linkEl = document.createElement("a");
|
||
linkEl.className = "opp-list-link";
|
||
linkEl.href = s.url;
|
||
linkEl.target = "_blank";
|
||
linkEl.rel = "noopener";
|
||
linkEl.textContent = "Website";
|
||
meta.appendChild(linkEl);
|
||
}
|
||
row.appendChild(meta);
|
||
|
||
var about = document.createElement("div");
|
||
about.className = "opp-list-about";
|
||
var badge = document.createElement(s.url ? "a" : "span");
|
||
badge.className = "opp-list-badge";
|
||
badge.textContent = s.title;
|
||
if (s.url) {
|
||
badge.href = s.url;
|
||
badge.target = "_blank";
|
||
badge.rel = "noopener";
|
||
}
|
||
about.appendChild(badge);
|
||
if (s.reason) {
|
||
var reasonEl = document.createElement("p");
|
||
reasonEl.className = "opp-list-abstract";
|
||
reasonEl.textContent = s.reason;
|
||
about.appendChild(reasonEl);
|
||
}
|
||
row.appendChild(about);
|
||
|
||
listEl.appendChild(row);
|
||
});
|
||
}
|
||
document.getElementById("opp-prev-btn").addEventListener("click", function () {
|
||
oppCursor.setMonth(oppCursor.getMonth() - 1);
|
||
renderOppCalendar();
|
||
});
|
||
document.getElementById("opp-next-btn").addEventListener("click", function () {
|
||
oppCursor.setMonth(oppCursor.getMonth() + 1);
|
||
renderOppCalendar();
|
||
});
|
||
document.getElementById("opp-today-btn").addEventListener("click", function () {
|
||
oppCursor = new Date();
|
||
oppCursor.setDate(1);
|
||
renderOppCalendar();
|
||
});
|
||
renderOppEventList();
|
||
renderOppCalendar();
|
||
renderOppScoutList();
|
||
|
||
/* ---------- Module self-registration ----------
|
||
Everything below wires this module into the shared shell (app.js):
|
||
the drill-view registry, the hero-nav button, and the tab's default view.
|
||
This runs once, right after opportunities.html is injected into the DOM. */
|
||
HERO_NAV_BY_TAB.opportunities = ["hero-nav-scout"];
|
||
DRILL_DEFAULT_VIEW.opportunities = "opportunities-root";
|
||
DRILL_CONFIG.opportunities = {
|
||
crumbsEl: "opp-crumbs",
|
||
crumbs: {
|
||
"opportunities-root": [["Opportunities", "opportunities-root"]],
|
||
"scout": [["Opportunities", "opportunities-root"], ["Conference Scout", "scout"]]
|
||
}
|
||
};
|
||
document.querySelectorAll("#tab-opportunities .drill-back, #tab-opportunities .drill-next").forEach(function (b) {
|
||
b.addEventListener("click", function () { setDrillView("opportunities", b.getAttribute("data-target")); });
|
||
});
|
||
setDrillView("opportunities", DRILL_DEFAULT_VIEW.opportunities);
|
||
document.getElementById("hero-nav-scout-btn").addEventListener("click", function () {
|
||
setDrillView("opportunities", "scout");
|
||
});
|
||
if (document.querySelector(".sidebar-nav-btn.active[data-tab=\"opportunities\"]")) {
|
||
document.getElementById("hero-nav-scout").style.display = "flex";
|
||
}
|