1const sectionsEl = document.querySelector("#project-sections"); 2const navEl = document.querySelector("#category-nav"); 3 4const makeId = (value) => 5 value 6 .toLowerCase() 7 .replace(/&/g, "and") 8 .replace(/[^a-z0-9]+/g, "-") 9 .replace(/^-|-$/g, ""); 10 11const makeEl = (tag, className, text) => { 12 const el = document.createElement(tag); 13 14 if (className) { 15 el.className = className; 16 } 17 18 if (text) { 19 el.textContent = text; 20 } 21 22 return el; 23}; 24 25const makeProjectCard = (project, sectionLayout) => { 26 const card = makeEl("article", "project-card"); 27 28 if (sectionLayout === "featured") { 29 card.classList.add("project-card-featured"); 30 } 31 32 if (sectionLayout === "wide") { 33 card.classList.add("project-card-wide"); 34 } 35 36 const imageLink = makeEl("a"); 37 imageLink.href = project.url; 38 imageLink.setAttribute("aria-label", `Open ${project.name}`); 39 40 const image = makeEl("img"); 41 image.src = project.image; 42 image.alt = project.alt; 43 image.loading = "lazy"; 44 imageLink.append(image); 45 46 const copy = makeEl("div", "project-copy"); 47 const title = makeEl("h3"); 48 const titleLink = makeEl("a", "", project.name); 49 titleLink.href = project.url; 50 title.append(titleLink); 51 52 copy.append(title, makeEl("p", "", project.description)); 53 card.append(imageLink, copy); 54 55 return card; 56}; 57 58const renderProjects = ({ sections }) => { 59 const fragment = document.createDocumentFragment(); 60 const navFragment = document.createDocumentFragment(); 61 62 sections.forEach((section) => { 63 const sectionId = section.id || makeId(section.label); 64 const titleId = `${sectionId}-title`; 65 66 const navLink = makeEl("a", "", section.navLabel || section.label); 67 navLink.href = `#${sectionId}`; 68 navFragment.append(navLink); 69 70 const sectionEl = makeEl("section", "project-section"); 71 sectionEl.id = sectionId; 72 sectionEl.setAttribute("aria-labelledby", titleId); 73 74 const heading = makeEl("div", "section-heading"); 75 heading.append( 76 makeEl("p", "section-kicker", section.label), 77 makeEl("h2", "", section.title) 78 ); 79 heading.querySelector("h2").id = titleId; 80 81 const grid = makeEl("div", `project-grid ${section.grid || "compact-grid"}`); 82 section.projects.forEach((project) => { 83 grid.append(makeProjectCard(project, section.cardLayout)); 84 }); 85 86 sectionEl.append(heading, grid); 87 fragment.append(sectionEl); 88 }); 89 90 navEl.append(navFragment); 91 sectionsEl.append(fragment); 92}; 93 94if (window.appsPageData) { 95 renderProjects(window.appsPageData); 96} else { 97 sectionsEl.append(makeEl("p", "load-error", "Project data did not load.")); 98}
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.