// PublicationsView — full publications page with tabs:
// In Press · Articles · Proceedings · Book Chapters.
// Entries grouped by year, color-coded topic tags, DOI + BibTeX actions.
// Data parsed from the .bib (window.WINTEL_PUBLICATIONS); arXiv excluded.
const PV = window.WINTELAcademicDesignSystem_045d4f;
const CONF = {
'IEEECONF': 'Asilomar Conf. Signals, Syst., Comput.',
'ACSSC': 'Asilomar Conf. Signals, Syst., Comput.',
'ICC': 'IEEE Int. Conf. Commun. (ICC)',
'ICC Workshops': 'IEEE ICC Workshops',
'GLOBECOM': 'IEEE Global Commun. Conf. (GLOBECOM)',
'GLOBECOM (Workshops)': 'IEEE GLOBECOM Workshops',
'GLOBECOM Workshops': 'IEEE GLOBECOM Workshops',
'PIMRC': 'IEEE Int. Symp. Pers., Indoor Mobile Radio Commun. (PIMRC)',
'WCNC': 'IEEE Wireless Commun. Netw. Conf. (WCNC)',
'SPAWC': 'IEEE Int. Workshop Signal Process. Adv. Wireless Commun. (SPAWC)',
'SAM': 'IEEE Sensor Array Multichannel Signal Process. Workshop (SAM)',
'ICCSPA': 'Int. Conf. Commun., Signal Process., Appl. (ICCSPA)',
'EMBC': 'IEEE Eng. Med. Biol. Soc. Conf. (EMBC)',
'HealthCom': 'IEEE Int. Conf. E-Health Netw., Appl. Services (HealthCom)',
'BSN': 'IEEE Body Sensor Networks Conf. (BSN)',
'VTC Spring': 'IEEE Veh. Technol. Conf. (VTC-Spring)',
'VTC Fall': 'IEEE Veh. Technol. Conf. (VTC-Fall)',
'CCNC': 'IEEE Consum. Commun. Netw. Conf. (CCNC)',
'ISCAS': 'IEEE Int. Symp. Circuits Syst. (ISCAS)',
'MWSCAS': 'IEEE Int. Midwest Symp. Circuits Syst. (MWSCAS)',
'ICTC': 'Int. Conf. ICT Convergence (ICTC)',
'ICASSP': 'IEEE Int. Conf. Acoust., Speech, Signal Process. (ICASSP)',
'ICNC': 'Int. Conf. Comput., Netw. Commun. (ICNC)',
};
// ---- Topic taxonomy (order = priority; each pub shows up to 3) ----
const TOPICS = [
{ label: 'ISAC', color: '#0ea5e9', re: /\bISAC\b|integrated sensing|sensing and communication|joint .*sensing|beam-?squint/i },
{ label: 'RIS', color: '#2563eb', re: /\bRIS\b|reconfigurable intelligent surface|star-?ris|intelligent surface/i },
{ label: 'AI-Native', color: '#8b5cf6', re: /\bAI\b|deep learning|machine learning|\bDRL\b|\bGAN\b|\bLLM\b|explainabl|generative|neural|\bXAI\b|agentic|autoencoder|reinforcement/i },
{ label: 'Near-Field', color: '#d97706', re: /near-?field|beamfocus|polar-?domain|spatial degrees of freedom|\bURA\b|\bUCA\b|circular array/i },
{ label: 'NTN', color: '#059669', re: /\bNTN\b|non-?terrestrial|\bUAV\b|airborne|satellite|aerial|drone/i },
{ label: 'OWC', color: '#4f46e5', re: /optical wireless|\bOWC\b|opto-?acoustic|underwater|visible light|\bVLC\b|\blaser\b|photonic/i },
{ label: 'IoB', color: '#84cc16', re: /internet of bodies|body communication|human body|\bIoB\b|\bHBC\b|biomedical|\bECG\b|wearable|implant/i },
{ label: 'NOMA', color: '#0d9488', re: /\bNOMA\b|non-?orthogonal|multiple access|rate splitting|\bRSMA\b|\bSWIPT\b/i },
{ label: 'mMIMO', color: '#ca8a04', re: /massive mimo|\bmMIMO\b|beamforming|codebook|channel estimation|cell-?free|\bMIMO\b|mmwave/i },
{ label: 'Security', color: '#dc2626', re: /physical layer security|\bsecurity\b|\bsecure\b|protected zone|jamming|eavesdrop/i },
{ label: 'CRN', color: '#c026d3', re: /cognitive radio|\bCRN\b|spectrum sensing|spectrum sharing|\bcognitive\b/i },
{ label: 'IoT', color: '#65a30d', re: /internet of things|\bIoT\b|device-?to-?device|\bD2D\b|machine-?type|\bM2M\b/i },
];
// ---- Journal venue short codes (short label + full title for tooltip) ----
const JOURNAL_VENUE = {
'IEEE Trans. Wirel. Commun.': ['IEEE TWC', 'IEEE Transactions on Wireless Communications'],
'IEEE Trans. Commun.': ['IEEE TCOM', 'IEEE Transactions on Communications'],
'IEEE Trans. Mob. Comput.': ['IEEE TMC', 'IEEE Transactions on Mobile Computing'],
'IEEE Trans. Cogn. Commun. Netw.': ['IEEE TCCN', 'IEEE Transactions on Cognitive Communications and Networking'],
'IEEE Trans. Green Commun. Netw.': ['IEEE TGCN', 'IEEE Transactions on Green Communications and Networking'],
'IEEE Trans. Netw. Serv. Manag.': ['IEEE TNSM', 'IEEE Transactions on Network and Service Management'],
'IEEE Trans. Circuits Syst. I Regul. Pap.': ['IEEE TCAS-I', 'IEEE Transactions on Circuits and Systems I: Regular Papers'],
'IEEE Trans. Biomed. Eng.': ['IEEE TBME', 'IEEE Transactions on Biomedical Engineering'],
'IEEE Trans. Instrum. Meas.': ['IEEE TIM', 'IEEE Transactions on Instrumentation and Measurement'],
'IEEE J. Sel. Top. Signal Process.': ['IEEE JSTSP', 'IEEE Journal of Selected Topics in Signal Processing'],
'IEEE Open J. Commun. Soc.': ['IEEE OJ-COMS', 'IEEE Open Journal of the Communications Society'],
'IEEE Trans. Mach. Learn. Commun. Netw.': ['IEEE TMLCN', 'IEEE Transactions on Machine Learning in Communications and Networking'],
'Nat. Rev. Electr. Eng.': ['Nat. Rev. EE', 'Nature Reviews Electrical Engineering'],
'Results Eng.': ['Results Eng.', 'Results in Engineering (Elsevier)'],
'Smart Health': ['Smart Health', 'Smart Health (Elsevier)'],
'Balkan J. Elect. Comp. Eng.': ['BAJECE', 'Balkan Journal of Electrical and Computer Engineering'],
'IEEE Commun. Mag.': ['IEEE ComMag', 'IEEE Communications Magazine'],
'IEEE Commun. Stand. Mag.': ['IEEE StdMag', 'IEEE Communications Standards Magazine'],
'IEEE Commun. Lett.': ['IEEE ComLett', 'IEEE Communications Letters'],
'IEEE Commun. Surv. Tutorials': ['IEEE COMST', 'IEEE Communications Surveys & Tutorials'],
'IEEE Wirel. Commun.': ['IEEE WCM', 'IEEE Wireless Communications'],
'IEEE Wirel. Commun. Lett.': ['IEEE WCL', 'IEEE Wireless Communications Letters'],
'IEEE Wireless Commun. Lett.': ['IEEE WCL', 'IEEE Wireless Communications Letters'],
'IEEE Signal Process. Mag.': ['IEEE SPM', 'IEEE Signal Processing Magazine'],
'IEEE Internet Things J.': ['IEEE IoT-J', 'IEEE Internet of Things Journal'],
'IEEE Sensors J.': ['IEEE SensJ', 'IEEE Sensors Journal'],
'IEEE Internet Things Mag.': ['IEEE IoT-M', 'IEEE Internet of Things Magazine'],
'IEEE Veh. Technol. Mag.': ['IEEE VTM', 'IEEE Vehicular Technology Magazine'],
'IEEE Syst. J.': ['IEEE SysJ', 'IEEE Systems Journal'],
'IEEE Access': ['IEEE Access', 'IEEE Access'],
'Ad Hoc Networks': ['AdHocNet', 'Ad Hoc Networks (Elsevier)'],
'IET Commun.': ['IET COM', 'IET Communications'],
'Sci. China Inf. Sci.': ['SCIS', 'Science China Information Sciences'],
'Sensors': ['Sensors', 'Sensors (MDPI)'],
};
// ---- Journal quartile ranking (Scimago / SJR, current) ----
// Keyed by the same venue string as pub.v. Source: scimagojr.com.
// >>> VERIFY / EDIT THESE to match your preferred ranking (Scimago vs JCR) <<<
// Use 'Q1'..'Q4', or null / omit for unranked or too-new venues (no badge shown).
const QUARTILE = {
'IEEE Trans. Wirel. Commun.': 'Q1',
'IEEE Trans. Commun.': 'Q1',
'IEEE Trans. Mob. Comput.': 'Q1',
'IEEE Trans. Cogn. Commun. Netw.': 'Q1',
'IEEE Trans. Green Commun. Netw.': 'Q1',
'IEEE Trans. Netw. Serv. Manag.': 'Q1',
'IEEE Trans. Circuits Syst. I Regul. Pap.': 'Q1',
'IEEE Trans. Biomed. Eng.': 'Q1',
'IEEE Trans. Instrum. Meas.': 'Q1',
'IEEE J. Sel. Top. Signal Process.': 'Q1',
'IEEE Open J. Commun. Soc.': 'Q1',
'IEEE Trans. Mach. Learn. Commun. Netw.': null, // too new for an SJR quartile
'Nat. Rev. Electr. Eng.': 'Q1',
'Results Eng.': 'Q1',
'Smart Health': 'Q2',
'Balkan J. Elect. Comp. Eng.': null, // not indexed in Scimago
'IEEE Commun. Mag.': 'Q1',
'IEEE Commun. Stand. Mag.': 'Q1',
'IEEE Commun. Lett.': 'Q2',
'IEEE Commun. Surv. Tutorials': 'Q1',
'IEEE Wirel. Commun.': 'Q1',
'IEEE Wirel. Commun. Lett.': 'Q1',
'IEEE Wireless Commun. Lett.': 'Q1',
'IEEE Signal Process. Mag.': 'Q1',
'IEEE Internet Things J.': 'Q1',
'IEEE Sensors J.': 'Q1',
'IEEE Internet Things Mag.': 'Q1',
'IEEE Veh. Technol. Mag.': 'Q1',
'IEEE Syst. J.': 'Q1',
'IEEE Access': 'Q1',
'Ad Hoc Networks': 'Q1',
'IET Commun.': 'Q2',
'Sci. China Inf. Sci.': 'Q1',
'Sensors': 'Q1',
'Nature Sensors': 'Q1',
'Nature Reviews Electrical Engineering': 'Q1',
};
const QUARTILE_COLOR = {
Q1: '#059669', // emerald
Q2: '#2563eb', // blue
Q3: '#d97706', // amber
Q4: '#6b7280', // slate
};
// Quartile only applies to journal articles (not conferences, not book chapters).
function quartileFor(pub) {
if (pub.conf || pub.chapter) return null;
return QUARTILE[pub.v] || null;
}
function QuartileTag({ q }) {
const color = QUARTILE_COLOR[q] || '#6b7280';
return (
{q}
);
}
function venueTag(pub) {
if (pub.chapter) return { short: 'Book Ch.', full: pub.v + (pub.pub ? ' · ' + pub.pub : '') };
if (!pub.conf) {
const m = JOURNAL_VENUE[pub.v];
return m ? { short: m[0], full: m[1] } : { short: pub.v, full: pub.v };
}
if (CONF[pub.v]) {
const full = CONF[pub.v];
const abbr = (full.match(/\(([^)]+)\)/) || [])[1] || pub.v;
return { short: abbr, full };
}
if (/asilomar/i.test(pub.v)) {
return { short: 'ASILOMAR', full: 'Asilomar Conference on Signals, Systems, and Computers, Pacific Grove' };
}
const KW = [
[/globecom workshop|glocomw|gcwkshp|gc wkshp/i, 'GC Wkshps', 'IEEE GLOBECOM Workshops'],
[/globecom|glocom/i, 'GLOBECOM', 'IEEE Global Communications Conference (GLOBECOM)'],
[/icc workshop|iccw/i, 'ICC Wkshps', 'IEEE ICC Workshops'],
];
for (const [re, short, full] of KW) { if (re.test(pub.v)) return { short, full }; }
const abbr = (pub.v.match(/\(([^)]+)\)/) || [])[1];
return { short: abbr || 'Conf.', full: pub.v.split(',')[0] };
}
function VenueTag({ info }) {
return (
{info.short}
);
}
function topicsFor(pub) {
const hay = (pub.t || '') + ' ' + (pub.v || '');
return TOPICS.filter((t) => t.re.test(hay)).slice(0, 3);
}
// Country → flag emoji for conference locations (matched against loc + venue string).
const COUNTRY_FLAGS = [
[/\bU\.?S\.?A\.?\b|United States/i, '\uD83C\uDDFA\uD83C\uDDF8'],
[/\bUK\b|United Kingdom|England|Scotland|Glasgow|London|Manchester|Guildford|Southampton/i, '\uD83C\uDDEC\uD83C\uDDE7'],
[/\bUAE\b|United Arab Emirates|Dubai|Abu Dhabi/i, '\uD83C\uDDE6\uD83C\uDDEA'],
[/Saudi Arabia|Jeddah|Dammam|Riyadh|Thuwal/i, '\uD83C\uDDF8\uD83C\uDDE6'],
[/T\u00fcrkiye|Turkey|Istanbul|Ankara/i, '\uD83C\uDDF9\uD83C\uDDF7'],
[/Canada|Ottawa|Montreal|Toronto/i, '\uD83C\uDDE8\uD83C\uDDE6'],
[/Macau|Macao/i, '\uD83C\uDDF2\uD83C\uDDF4'],
[/\bChina\b|Beijing|Shanghai|Shenzhen/i, '\uD83C\uDDE8\uD83C\uDDF3'],
[/Sweden|Stockholm|Gothenburg/i, '\uD83C\uDDF8\uD83C\uDDEA'],
[/Italy|Bologna|Padova|Padua|Rome|Milan/i, '\uD83C\uDDEE\uD83C\uDDF9'],
[/Egypt|Alexandria|Cairo/i, '\uD83C\uDDEA\uD83C\uDDEC'],
[/South Africa|Cape Town|Johannesburg/i, '\uD83C\uDDFF\uD83C\uDDE6'],
[/Malaysia|Kuala Lumpur/i, '\uD83C\uDDF2\uD83C\uDDFE'],
[/Singapore/i, '\uD83C\uDDF8\uD83C\uDDEC'],
[/South Korea|\bKorea\b|Seoul|Busan/i, '\uD83C\uDDF0\uD83C\uDDF7'],
[/Germany|Berlin|Munich|Hamburg/i, '\uD83C\uDDE9\uD83C\uDDEA'],
[/France|Paris/i, '\uD83C\uDDEB\uD83C\uDDF7'],
[/Spain|Barcelona|Madrid/i, '\uD83C\uDDEA\uD83C\uDDF8'],
[/Greece|Athens/i, '\uD83C\uDDEC\uD83C\uDDF7'],
[/Japan|Tokyo|Osaka/i, '\uD83C\uDDEF\uD83C\uDDF5'],
[/Australia|Sydney|Melbourne/i, '\uD83C\uDDE6\uD83C\uDDFA'],
[/\bIndia\b|Delhi|Mumbai|Bangalore/i, '\uD83C\uDDEE\uD83C\uDDF3'],
[/Finland|Helsinki/i, '\uD83C\uDDEB\uD83C\uDDEE'],
[/Morocco|Marrakech|Marrakesh|Rabat/i, '\uD83C\uDDF2\uD83C\uDDE6'],
[/Kazakhstan|Astana|Almaty/i, '\uD83C\uDDF0\uD83C\uDDFF'],
[/Brazil|Rio de Janeiro|S\u00e3o Paulo|Sao Paulo/i, '\uD83C\uDDE7\uD83C\uDDF7'],
];
function flagForText(str) {
const hay = str || '';
for (const [re, flag] of COUNTRY_FLAGS) { if (re.test(hay)) return flag; }
return null;
}
function flagFor(pub) {
if (!pub.conf) return null;
return flagForText((pub.loc || '') + ' ' + (pub.v || ''));
}
function TopicTag({ topic }) {
return (
{topic.label}
);
}
// ---- BibTeX synthesis ----
function bibKey(pub) {
const first = (pub.a && pub.a[0] && pub.a[0].name) || 'celik';
const last = first.replace(/\./g, '').trim().split(/\s+/).pop().toLowerCase().replace(/[^a-z]/g, '');
const word = (pub.t || '').toLowerCase().replace(/[^a-z0-9 ]/g, '').split(/\s+/).find((w) => w.length > 3) || 'paper';
return last + (pub.y || '') + word;
}
function bibtex(pub) {
const type = pub.conf ? 'inproceedings' : (pub.chapter ? 'incollection' : 'article');
const authors = (pub.a || []).map((x) => x.name).join(' and ');
const doi = pub.u ? pub.u.replace(/^https?:\/\/doi\.org\//, '') : null;
const L = [`@${type}{${bibKey(pub)},`];
L.push(` author = {${authors}},`);
L.push(` title = {${pub.t || ''}},`);
if (pub.conf) {
L.push(` booktitle = {Proc. ${CONF[pub.v] || pub.v}},`);
if (pub.loc) L.push(` address = {${pub.loc}},`);
} else if (pub.chapter) {
L.push(` booktitle = {${pub.v}},`);
L.push(` chapter = {${pub.chapter}},`);
} else {
L.push(` journal = {${pub.v || ''}},`);
}
if (pub.vol) L.push(` volume = {${pub.vol}},`);
if (pub.no) L.push(` number = {${pub.no}},`);
if (pub.p) L.push(` pages = {${String(pub.p).replace(/–/g, '--')}},`);
if (pub.y) L.push(` year = {${pub.y}},`);
if (doi) L.push(` doi = {${doi}},`);
if (pub.ip) L.push(` note = {in press},`);
L.push(` publisher = {${pub.pub || 'IEEE'}},`);
L.push(`}`);
return L.join('\n');
}
function AuthorList({ list }) {
return (
{list.map((au, i) => {
let sep = '';
if (i > 0) sep = i === list.length - 1 ? (list.length > 2 ? ', and ' : ' and ') : ', ';
return (
{sep}
{au.name}
);
})}
);
}
function Citation({ pub, index, onBib }) {
const isConf = pub.conf;
const topics = topicsFor(pub);
const venue = venueTag(pub);
const quartile = quartileFor(pub);
const flag = flagFor(pub);
return (
{index}.
{', \u201C'}
{pub.t}
{',\u201D '}
{isConf ? (pub.full
? in Proc. {pub.v}{pub.p ? , pp. {pub.p} : null}.
: in Proc. {CONF[pub.v] || pub.v}{pub.loc ? , {pub.loc} : null}, {pub.y}{pub.ip ? [in press] : (pub.p ? , pp. {pub.p} : null)}.)
: pub.chapter ? in {pub.v}, ch. {pub.chapter}{pub.pub ? . {pub.pub} : null}{pub.p ? , pp. {pub.p} : null}, {pub.y}.
: pub.ip ? {pub.v}, [in press].
: {pub.v}{pub.vol ? , vol. {pub.vol} : null}{pub.no ? , no. {pub.no} : null}{pub.p ? , pp. {pub.p} : null}, {pub.y}.}
{flag ? {flag} : null}
{pub.u ? (
DOI
) : null}
onBib(pub)} aria-label="Extract BibTeX"
style={{ height: '1.85rem', padding: '0 0.6rem', fontSize: 'var(--text-xs)', fontFamily: 'var(--font-mono)', letterSpacing: '0.03em' }}>
BibTeX
{quartile ? : null}
{topics.map((t) => )}
);
}
function groupByYear(list) {
const groups = {};
list.forEach((p) => { const y = p.y || 'Forthcoming'; (groups[y] = groups[y] || []).push(p); });
return Object.keys(groups)
.sort((a, b) => (a === 'Forthcoming' ? -1 : b === 'Forthcoming' ? 1 : Number(b) - Number(a)))
.map((y) => ({ year: y, items: groups[y] }));
}
// ============================================================================
// Publication analytics
// ============================================================================
// >>> EDIT THESE WITH YOUR EXACT GOOGLE SCHOLAR FIGURES <<<
// Source: https://scholar.google.com/citations?user=Xp0rnxQAAAAJ
// `citationsByYear` is the "Cited by" histogram; h-index / i10-index are the
// current all-time values shown in the metrics box.
const SCHOLAR_METRICS = {
hIndex: 38,
i10Index: 81,
totalCitations: 4700,
asOf: 'Jun 22, 2026',
placeholder: false, // set to false once you've entered your real numbers
profileUrl: 'https://scholar.google.com/citations?user=Xp0rnxQAAAAJ&hl=en',
citationsByYear: [
{ year: 2017, c: 32 },
{ year: 2018, c: 104 },
{ year: 2019, c: 204 },
{ year: 2020, c: 295 },
{ year: 2021, c: 366 },
{ year: 2022, c: 517 },
{ year: 2023, c: 579 },
{ year: 2024, c: 804 },
{ year: 2025, c: 1167 },
{ year: 2026, c: 550 },
],
};
// Live chart accent — tracks the --accent token so the accent tweak recolors charts/map.
function chartAccent() {
try { const v = getComputedStyle(document.documentElement).getPropertyValue('--accent').trim(); if (v) return v; } catch (e) {}
return '#2563eb';
}
function tallyVenues(P) {
const all = [...(P.inPress || []), ...(P.articles || []), ...(P.proceedings || []), ...(P.bookChapters || [])];
const counts = {};
all.forEach((p) => {
const v = venueTag(p);
if (!counts[v.short]) counts[v.short] = { short: v.short, full: v.full, n: 0 };
counts[v.short].n += 1;
});
return Object.values(counts).sort((a, b) => b.n - a.n);
}
function tallyTopics(P) {
const all = [...(P.inPress || []), ...(P.articles || []), ...(P.proceedings || []), ...(P.bookChapters || [])];
const counts = {};
TOPICS.forEach((t) => { counts[t.label] = { label: t.label, color: t.color, n: 0 }; });
all.forEach((p) => { topicsFor(p).forEach((t) => { counts[t.label].n += 1; }); });
return Object.values(counts).filter((t) => t.n > 0).sort((a, b) => b.n - a.n);
}
// Journals & magazines only (conferences and book chapters excluded).
function tallyJournalVenues(P) {
const all = [...(P.inPress || []), ...(P.articles || [])];
const counts = {};
all.forEach((p) => {
if (p.conf || p.chapter) return;
const v = venueTag(p);
counts[v.short] = counts[v.short] || { short: v.short, full: v.full, n: 0 };
counts[v.short].n += 1;
});
return Object.values(counts).sort((a, b) => b.n - a.n);
}
// --- Conference geography: city gazetteer (lat/lon + ISO3) for the world map ---
const CITIES = {
'Glasgow': { lat: 55.8642, lon: -4.2518, iso: 'GBR', country: 'United Kingdom' },
'Pacific Grove': { lat: 36.6177, lon: -121.9166, iso: 'USA', country: 'United States' },
'Montreal': { lat: 45.5019, lon: -73.5674, iso: 'CAN', country: 'Canada' },
'Istanbul': { lat: 41.0082, lon: 28.9784, iso: 'TUR', country: 'T\u00fcrkiye' },
'Orlando': { lat: 28.5383, lon: -81.3792, iso: 'USA', country: 'United States' },
'Cape Town': { lat: -33.9249, lon: 18.4241, iso: 'ZAF', country: 'South Africa' },
'Nara': { lat: 34.6851, lon: 135.8048, iso: 'JPN', country: 'Japan' },
'Corvallis': { lat: 44.5646, lon: -123.262, iso: 'USA', country: 'United States' },
'Valencia': { lat: 39.4699, lon: -0.3763, iso: 'ESP', country: 'Spain' },
'Lucca': { lat: 43.8430, lon: 10.5079, iso: 'ITA', country: 'Italy' },
'Dubai': { lat: 25.2048, lon: 55.2708, iso: 'ARE', country: 'United Arab Emirates' },
'Las Vegas': { lat: 36.1699, lon: -115.1398, iso: 'USA', country: 'United States' },
'Kuala Lumpur': { lat: 3.1390, lon: 101.6869, iso: 'MYS', country: 'Malaysia' },
'Rome': { lat: 41.9028, lon: 12.4964, iso: 'ITA', country: 'Italy' },
'Monterey': { lat: 36.6002, lon: -121.8947, iso: 'USA', country: 'United States' },
'Tempe': { lat: 33.4255, lon: -111.9400, iso: 'USA', country: 'United States' },
'Ioannina': { lat: 39.6650, lon: 20.8537, iso: 'GRC', country: 'Greece' },
'Seoul': { lat: 37.5665, lon: 126.9780, iso: 'KOR', country: 'South Korea' },
'Helsinki': { lat: 60.1699, lon: 24.9384, iso: 'FIN', country: 'Finland' },
'London': { lat: 51.5074, lon: -0.1278, iso: 'GBR', country: 'United Kingdom' },
'Madrid': { lat: 40.4168, lon: -3.7038, iso: 'ESP', country: 'Spain' },
'Jeju': { lat: 33.4996, lon: 126.5312, iso: 'KOR', country: 'South Korea' },
'Marrakesh': { lat: 31.6295, lon: -7.9811, iso: 'MAR', country: 'Morocco' },
'Abu Dhabi': { lat: 24.4539, lon: 54.3773, iso: 'ARE', country: 'United Arab Emirates' },
'Calgary': { lat: 51.0447, lon: -114.0719, iso: 'CAN', country: 'Canada' },
'Kansas City': { lat: 39.0997, lon: -94.5786, iso: 'USA', country: 'United States' },
'Kalamata': { lat: 37.0366, lon: 22.1144, iso: 'GRC', country: 'Greece' },
'Barcelona': { lat: 41.3851, lon: 2.1734, iso: 'ESP', country: 'Spain' },
'Singapore': { lat: 1.3521, lon: 103.8198, iso: 'SGP', country: 'Singapore' },
'Paris': { lat: 48.8566, lon: 2.3522, iso: 'FRA', country: 'France' },
'Toronto': { lat: 43.6532, lon: -79.3832, iso: 'CAN', country: 'Canada' },
'Washington': { lat: 38.9072, lon: -77.0369, iso: 'USA', country: 'United States' },
'Garden Grove': { lat: 33.7739, lon: -117.9414, iso: 'USA', country: 'United States' },
'Austin': { lat: 30.2672, lon: -97.7431, iso: 'USA', country: 'United States' },
'Ottawa': { lat: 45.4215, lon: -75.6972, iso: 'CAN', country: 'Canada' },
'Stockholm': { lat: 59.3293, lon: 18.0686, iso: 'SWE', country: 'Sweden' },
'Gothenburg': { lat: 57.7089, lon: 11.9746, iso: 'SWE', country: 'Sweden' },
'Bologna': { lat: 44.4949, lon: 11.3426, iso: 'ITA', country: 'Italy' },
'Padova': { lat: 45.4064, lon: 11.8768, iso: 'ITA', country: 'Italy' },
'Santa Cruz': { lat: 36.9741, lon: -122.0308, iso: 'USA', country: 'United States' },
'Irvine': { lat: 33.6846, lon: -117.8265, iso: 'USA', country: 'United States' },
'Ankara': { lat: 39.9334, lon: 32.8597, iso: 'TUR', country: 'T\u00fcrkiye' },
'Jeddah': { lat: 21.4858, lon: 39.1925, iso: 'SAU', country: 'Saudi Arabia' },
'Dammam': { lat: 26.4207, lon: 50.0888, iso: 'SAU', country: 'Saudi Arabia' },
'Ames': { lat: 42.0308, lon: -93.6319, iso: 'USA', country: 'United States' },
'Macau': { lat: 22.1987, lon: 113.5439, iso: 'CHN', country: 'China' },
'Alexandria': { lat: 31.2001, lon: 29.9187, iso: 'EGY', country: 'Egypt' },
'Manchester': { lat: 53.4808, lon: -2.2426, iso: 'GBR', country: 'United Kingdom' },
'Southampton': { lat: 50.9097, lon: -1.4044, iso: 'GBR', country: 'United Kingdom' },
'Guildford': { lat: 51.2362, lon: -0.5704, iso: 'GBR', country: 'United Kingdom' },
'Astana': { lat: 51.1605, lon: 71.4704, iso: 'KAZ', country: 'Kazakhstan' },
'Rio de Janeiro': { lat: -22.9068, lon: -43.1729, iso: 'BRA', country: 'Brazil' },
'Cairo': { lat: 30.0444, lon: 31.2357, iso: 'EGY', country: 'Egypt' },
'San Antonio': { lat: 29.4241, lon: -98.4936, iso: 'USA', country: 'United States' },
'San Francisco': { lat: 37.7749, lon: -122.4194, iso: 'USA', country: 'United States' },
};
function resolveCity(pub) {
const hay = ((pub.v || '') + ' ' + (pub.loc || '')).toLowerCase();
for (const key of Object.keys(CITIES)) { if (hay.includes(key.toLowerCase())) return Object.assign({ key }, CITIES[key]); }
return null;
}
// Extra map locations that aren't tied to a talk/paper entry (e.g. award trips).
// Shown as pins + country fill on BOTH the conference and dissemination maps.
const EXTRA_PINS = [
{ key: 'Rio de Janeiro', n: 1 },
];
function mergeExtraGeo(geo) {
const countries = geo.countries.map((c) => ({ ...c }));
const cities = geo.cities.map((c) => ({ ...c }));
const isoIndex = {}; countries.forEach((c) => { isoIndex[c.iso] = c; });
const cityIndex = {}; cities.forEach((c) => { cityIndex[c.key] = c; });
EXTRA_PINS.forEach((e) => {
const loc = CITIES[e.key]; if (!loc) return;
const n = e.n || 1;
if (isoIndex[loc.iso]) { isoIndex[loc.iso].n += n; }
else { const o = { iso: loc.iso, name: loc.country, n }; isoIndex[loc.iso] = o; countries.push(o); }
if (cityIndex[e.key]) { cityIndex[e.key].n += n; }
else { const o = { key: e.key, lon: loc.lon, lat: loc.lat, country: loc.country, n }; cityIndex[e.key] = o; cities.push(o); }
});
return { countries: countries.sort((a, b) => b.n - a.n), cities };
}
function tallyConfGeo(P) {
const counts = {}; const cities = {};
(P.proceedings || []).forEach((p) => {
const c = resolveCity(p); if (!c) return;
counts[c.iso] = counts[c.iso] || { iso: c.iso, name: c.country, n: 0 }; counts[c.iso].n += 1;
cities[c.key] = cities[c.key] || { key: c.key, lon: c.lon, lat: c.lat, country: c.country, n: 0 }; cities[c.key].n += 1;
});
return mergeExtraGeo({ countries: Object.values(counts), cities: Object.values(cities) });
}
const SERIES = [
[/globecom/i, 'GLOBECOM'], [/\bICC\b|International Conference on Communications/i, 'ICC'],
[/PIMRC|Personal, Indoor and Mobile/i, 'PIMRC'], [/WCNC|Wireless Communications and Networking/i, 'WCNC'],
[/SPAWC|Signal Processing Advances/i, 'SPAWC'], [/asilomar|ACSSC/i, 'ASILOMAR'],
[/Vehicular Technology|\bVTC\b/i, 'VTC'], [/Medicine and Biology|EMBC/i, 'EMBC'],
[/E-?health|HealthCom/i, 'HealthCom'], [/Body Sensor|\bBSN\b/i, 'BSN'],
[/Consumer Communications|CCNC/i, 'CCNC'], [/Midwest|MWSCAS/i, 'MWSCAS'],
[/ISCAS|Symposium on Circuits/i, 'ISCAS'], [/ICASSP|Acoustics, Speech/i, 'ICASSP'],
[/ICT Convergence|ICTC/i, 'ICTC'], [/Computing, Networking|ICNC/i, 'ICNC'],
[/ICCSPA/i, 'ICCSPA'], [/Sensor Array|\bSAM\b/i, 'SAM'],
[/Middle East Conf|MECOM/i, 'MECOM'], [/Biomedical Circuits|BIOCAS|BioCAS/i, 'BIOCAS'],
[/Radar Conf|RadarConf/i, 'RadarConf'], [/\bSPIE\b|Photonics West/i, 'SPIE'],
[/Biomedical and Healthcare|\bBHI\b/i, 'BHI'],
];
function seriesFor(pub) {
const hay = (pub.v || '') + ' ' + (pub.loc || '');
for (const [re, s] of SERIES) { if (re.test(hay)) return s; }
return (pub.v || 'Other').split(',')[0].slice(0, 16);
}
function tallyConfSeries(P) {
const counts = {};
(P.proceedings || []).forEach((p) => { const s = seriesFor(p); counts[s] = counts[s] || { key: s, n: 0 }; counts[s].n += 1; });
return Object.values(counts).sort((a, b) => b.n - a.n);
}
function WorldMap({ countries, cities, title, hint }) {
const [geo, setGeo] = React.useState(null);
const [failed, setFailed] = React.useState(false);
React.useEffect(() => {
const CK = 'wintel_world_geo_v1';
try { const c = localStorage.getItem(CK); if (c) { setGeo(JSON.parse(c)); return; } } catch (e) {}
fetch('https://cdn.jsdelivr.net/gh/johan/world.geo.json@master/countries.geo.json')
.then((r) => r.ok ? r.json() : Promise.reject())
.then((g) => { setGeo(g); try { localStorage.setItem(CK, JSON.stringify(g)); } catch (e) {} })
.catch(() => setFailed(true));
}, []);
const countByIso = {}; countries.forEach((c) => { countByIso[c.iso] = c.n; });
const max = Math.max(1, ...countries.map((c) => c.n));
const proj = (lon, lat) => [lon + 180, 90 - lat];
const pathFor = (geom) => {
const polys = geom.type === 'Polygon' ? [geom.coordinates] : geom.coordinates;
let d = '';
polys.forEach((poly) => poly.forEach((ring) => {
ring.forEach((pt, i) => { const xy = proj(pt[0], pt[1]); d += (i === 0 ? 'M' : 'L') + xy[0].toFixed(2) + ' ' + xy[1].toFixed(2); });
d += 'Z';
}));
return d;
};
// --- pan & zoom ---
const BASE = { x: 0, y: 16, w: 360, h: 124 };
const clampVB = (x, y, w, h) => ({ x: Math.max(0, Math.min(360 - w, x)), y: Math.max(0, Math.min(180 - h, y)), w, h });
const [vb, setVb] = React.useState(BASE);
const [dragging, setDragging] = React.useState(false);
const svgRef = React.useRef(null);
const vbRef = React.useRef(vb); vbRef.current = vb;
const dragRef = React.useRef(null);
React.useEffect(() => {
const svg = svgRef.current; if (!svg) return;
const onWheel = (e) => {
e.preventDefault();
const rect = svg.getBoundingClientRect();
const px = (e.clientX - rect.left) / rect.width, py = (e.clientY - rect.top) / rect.height;
const v = vbRef.current;
const factor = e.deltaY < 0 ? 0.85 : 1 / 0.85;
const nw = Math.max(24, Math.min(360, v.w * factor)); const nh = nw * (BASE.h / BASE.w);
const cx = v.x + px * v.w, cy = v.y + py * v.h;
setVb(clampVB(cx - px * nw, cy - py * nh, nw, nh));
};
svg.addEventListener('wheel', onWheel, { passive: false });
return () => svg.removeEventListener('wheel', onWheel);
}, []);
const zoomBy = (factor) => setVb((v) => { const nw = Math.max(24, Math.min(360, v.w * factor)); const nh = nw * (BASE.h / BASE.w); const cx = v.x + v.w / 2, cy = v.y + v.h / 2; return clampVB(cx - nw / 2, cy - nh / 2, nw, nh); });
const onPointerDown = (e) => { dragRef.current = { x: e.clientX, y: e.clientY, vb: vbRef.current, rect: e.currentTarget.getBoundingClientRect() }; setDragging(true); e.currentTarget.setPointerCapture(e.pointerId); };
const onPointerMove = (e) => { const d = dragRef.current; if (!d) return; const dx = (e.clientX - d.x) / d.rect.width * d.vb.w; const dy = (e.clientY - d.y) / d.rect.height * d.vb.h; setVb(clampVB(d.vb.x - dx, d.vb.y - dy, d.vb.w, d.vb.h)); };
const onPointerUp = () => { dragRef.current = null; setDragging(false); };
const vbStr = vb.x.toFixed(2) + ' ' + vb.y.toFixed(2) + ' ' + vb.w.toFixed(2) + ' ' + vb.h.toFixed(2);
const mapBtn = { width: '1.6rem', height: '1.6rem', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 0, borderRadius: 'var(--radius-sm)', border: '1px solid var(--border-default)', background: 'var(--surface-card)', color: 'var(--text-body)', cursor: 'pointer', fontSize: 15, lineHeight: 1 };
return (
{failed ? Country outlines need a connection; showing city markers only. : null}
);
}
function ChartPanel({ title, hint, children }) {
return (
{title}
{hint ? {hint} : null}
{children}
);
}
function StatBox({ value, label }) {
return (
{value}
{label}
);
}
function CitationsChart() {
const m = SCHOLAR_METRICS;
const max = Math.max(...m.citationsByYear.map((d) => d.c));
const fmt = (n) => n >= 1000 ? (n / 1000).toFixed(n >= 10000 ? 0 : 1).replace(/\.0$/, '') + 'k' : String(n);
return (
{m.citationsByYear.map((d) => (
{fmt(d.c)}
{String(d.year).slice(2)}
))}
{m.profileUrl ? (
) : null}
);
}
// Count journal articles (not conf / chapters) by quartile.
function tallyQuartiles(P) {
const all = [...(P.inPress || []), ...(P.articles || [])];
const counts = { Q1: 0, Q2: 0, Q3: 0, Q4: 0 };
let unranked = 0;
all.forEach((p) => {
if (p.conf || p.chapter) return;
const q = QUARTILE[p.v];
if (q && counts[q] !== undefined) counts[q] += 1; else unranked += 1;
});
return { counts, unranked };
}
function QuartilePie() {
const P = window.WINTEL_PUBLICATIONS || {};
const { counts, unranked } = tallyQuartiles(P);
const order = ['Q1', 'Q2', 'Q3', 'Q4'];
const data = order.map((q) => ({ q, n: counts[q], color: QUARTILE_COLOR[q] })).filter((d) => d.n > 0);
const total = data.reduce((s, d) => s + d.n, 0);
const cx = 80, cy = 80, R = 62, r = 38;
let angle = -Math.PI / 2;
const arcs = data.map((d) => {
const frac = d.n / total;
const a0 = angle, a1 = angle + frac * 2 * Math.PI - (data.length > 1 ? 0.015 : 0);
angle += frac * 2 * Math.PI;
const large = (a1 - a0) > Math.PI ? 1 : 0;
const p = (ang, rad) => [cx + rad * Math.cos(ang), cy + rad * Math.sin(ang)];
const [x0, y0] = p(a0, R), [x1, y1] = p(a1, R), [xi1, yi1] = p(a1, r), [xi0, yi0] = p(a0, r);
const dPath = `M${x0.toFixed(2)} ${y0.toFixed(2)} A${R} ${R} 0 ${large} 1 ${x1.toFixed(2)} ${y1.toFixed(2)} L${xi1.toFixed(2)} ${yi1.toFixed(2)} A${r} ${r} 0 ${large} 0 ${xi0.toFixed(2)} ${yi0.toFixed(2)} Z`;
return { ...d, dPath, pct: Math.round(frac * 100) };
});
return (
{data.map((d) => (
{d.q}
{d.n} · {Math.round(d.n / total * 100)}%
))}
{unranked ?
+ {unranked} unranked / too new : null}
);
}
function HBarChart({ title, hint, rows, total, colorFor }) {
const max = Math.max(...rows.map((r) => r.n), 1);
return (
);
}
function PubStatBoxes() {
const m = SCHOLAR_METRICS;
const P = window.WINTEL_PUBLICATIONS || {};
const fmt = (n) => n >= 1000 ? (n / 1000).toFixed(n >= 10000 ? 0 : 1).replace(/\.0$/, '') + 'k' : String(n);
const journals = (P.inPress || []).length + (P.articles || []).length;
const proceedings = (P.proceedings || []).length;
const chapters = (P.bookChapters || []).length;
const totalPubs = journals + proceedings + chapters;
return (
);
}
function PubStats() {
const P = window.WINTEL_PUBLICATIONS;
const journalVenues = React.useMemo(() => tallyJournalVenues(P).slice(0, 12).map((v) => ({ ...v, key: v.short })), [P]);
const topics = React.useMemo(() => tallyTopics(P).map((t) => ({ ...t, key: t.label })), [P]);
const geo = React.useMemo(() => tallyConfGeo(P), [P]);
const series = React.useMemo(() => tallyConfSeries(P), [P]);
const countryRows = geo.countries.map((c) => ({ key: c.name, full: c.name, n: c.n }));
return (
chartAccent()} />
r.color} />
chartAccent()} />
chartAccent()} />
);
}
function BibModal({ pub, onClose }) {
const [copied, setCopied] = React.useState(false);
const text = React.useMemo(() => bibtex(pub), [pub]);
React.useEffect(() => {
const onKey = (e) => { if (e.key === 'Escape') onClose(); };
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [onClose]);
const copy = () => {
navigator.clipboard.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1600); });
};
const download = () => {
const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = bibKey(pub) + '.bib';
a.click();
setTimeout(() => URL.revokeObjectURL(a.href), 1000);
};
return (
e.stopPropagation()} style={{ width: '100%', maxWidth: '640px', background: 'var(--surface-card)', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-xl)', boxShadow: 'var(--shadow-lg, 0 20px 50px rgba(0,0,0,0.3))', display: 'flex', flexDirection: 'column', maxHeight: '80vh', overflow: 'hidden' }}>
{text}
Download .bib
{copied ? 'Copied' : 'Copy'}
);
}
function PublicationsView({ data, onNavigate }) {
const P = window.WINTEL_PUBLICATIONS;
const tabs = [
{ key: 'articles', label: 'Articles', list: [...(P.inPress || []), ...P.articles] },
{ key: 'proceedings', label: 'Proceedings', list: P.proceedings },
{ key: 'bookChapters', label: 'Book Chapters', list: P.bookChapters },
];
const [active, setActive] = React.useState('articles');
const [bibPub, setBibPub] = React.useState(null);
const [showStats, setShowStats] = React.useState(false);
const current = tabs.find((t) => t.key === active);
const grouped = groupByYear(current.list);
let counter = 0;
return (
{tabs.map((t) => (
setActive(t.key)}>
{t.label}
{t.list.length}
))}
{current.list.length === 0 ? (
No {current.label.toLowerCase()} in the provided bibliography.
) : (
{grouped.map((g) => (
{g.year === 'Forthcoming' ? 'In Press' : g.year}
{g.items.length}
{g.items.map((pub, i) => { counter += 1; return (
); })}
))}
)}
{bibPub ? setBibPub(null)} /> : null}
);
}
Object.assign(window, { PublicationsView, topicsFor, TopicTag, flagForText, TOPICS, WorldMap, ChartPanel, HBarChart, StatBox, chartAccent, resolveCity, mergeExtraGeo, venueTag, VenueTag });