From cd4acc1a4dc513dbe228fd1e445174a347d78cdb Mon Sep 17 00:00:00 2001 From: Allen Bai Date: Jun 25 2020 20:09:49 +0000 Subject: coreos-download: clean up AWS cloud launchable regions This changes cleans up AWS cloud launchable regions such that by default only us-east-1 is displayed and only display other regions through selection of target region in region dropdown list. Additional changes include updating the "View Releases" button to link to corresponding stream in release note page instead of to default stable stream, and formatting the js files. Signed-off-by: Allen Bai --- diff --git a/sites/static/js/coreos-download.js b/sites/static/js/coreos-download.js index 8395148..e064fe2 100644 --- a/sites/static/js/coreos-download.js +++ b/sites/static/js/coreos-download.js @@ -79,13 +79,13 @@ function getPrettyPlatform(platform, extension) { function getDownloadsFromFormat(formatData, downloads) { for (var download in formatData) { downloadData = formatData[download]; - entry = {location: downloadData.location, signature: downloadData.signature, sha256: downloadData.sha256}; + entry = { location: downloadData.location, signature: downloadData.signature, sha256: downloadData.sha256 }; downloads[download] = entry; } } var coreos_download_app = new Vue({ el: '#coreos-download-app', - created: function() { this.refreshStream() }, + created: function () { this.refreshStream() }, data: { // currently shown tab shownId: IdPool.cloud_launchable, @@ -109,10 +109,10 @@ var coreos_download_app = new Vue({ }, }, watch: { - stream: function() { + stream: function () { this.refreshStream(); }, - loading: function() { + loading: function () { // Scrolls the view to navigation bar after user has clicked one of the download button // This needs to watch loading variable since the whole page is re-rendered Vue.nextTick(function () { @@ -125,7 +125,7 @@ var coreos_download_app = new Vue({ }); }, // watching nested data: https://stackoverflow.com/a/46331968 - "streamDataAll.stable": function(newVal, oldVal) { + "streamDataAll.stable": function (newVal, oldVal) { if (isEmptyObj(this.streamDataAll.stable)) { return } @@ -137,7 +137,7 @@ var coreos_download_app = new Vue({ $("#stable-json").append(`JSON`); $("#stable-json").append(` — ${this.timeSince(this.streamDataAll.stable.metadata['last-modified'])}`); }, - "streamDataAll.testing": function(newVal, oldVal) { + "streamDataAll.testing": function (newVal, oldVal) { if (isEmptyObj(this.streamDataAll.testing)) { return } @@ -149,7 +149,7 @@ var coreos_download_app = new Vue({ $("#testing-json").append(`JSON`); $("#testing-json").append(` — ${this.timeSince(this.streamDataAll.testing.metadata['last-modified'])}`); }, - "streamDataAll.next": function(newVal, oldVal) { + "streamDataAll.next": function (newVal, oldVal) { if (isEmptyObj(this.streamDataAll.next)) { return } @@ -163,11 +163,11 @@ var coreos_download_app = new Vue({ } }, methods: { - getObjectUrl: function(path) { + getObjectUrl: function (path) { return getArtifactUrl(this.streamUrl, path); }, // Adapted from https://stackoverflow.com/a/6109105 - timeSince: function(rfc3339_timestamp) { + timeSince: function (rfc3339_timestamp) { var current = Date.now(); var timestamp = Date.parse(rfc3339_timestamp); var elapsed = current - timestamp; @@ -180,24 +180,24 @@ var coreos_download_app = new Vue({ return n + ` ${s}` + (n == 1 ? "" : "s") + ' ago'; }; if (elapsed < msPerMinute) { - return stringize(Math.floor(elapsed/1000), "second"); + return stringize(Math.floor(elapsed / 1000), "second"); } else if (elapsed < msPerHour) { - return stringize(Math.floor(elapsed/msPerMinute), "minute"); + return stringize(Math.floor(elapsed / msPerMinute), "minute"); } else if (elapsed < msPerDay) { - return stringize(Math.floor(elapsed/msPerHour), "hour"); + return stringize(Math.floor(elapsed / msPerHour), "hour"); } else if (elapsed < msPerMonth) { - return stringize(Math.floor(elapsed/msPerDay), "day"); + return stringize(Math.floor(elapsed / msPerDay), "day"); } else if (elapsed < msPerYear) { - return stringize(Math.floor(elapsed/msPerMonth), "month"); + return stringize(Math.floor(elapsed / msPerMonth), "month"); } else { - return stringize(Math.floor(elapsed/msPerYear), "year"); + return stringize(Math.floor(elapsed / msPerYear), "year"); } }, // Callback function for the navigation bar // Effects: // - hides the other tabs other than the clicked one // - replace the current URL parameter with the clicked one - toggleHidden: function(e) { + toggleHidden: function (e) { const idList = Object.values(IdPool); Object.entries(tabInnerText).map(pair => { const key = pair[0]; @@ -212,24 +212,24 @@ var coreos_download_app = new Vue({ }); }, // Render a navbar section - getNavbar: function(h) { + getNavbar: function (h) { cloudIcon = h('i', { class: "fas fa-cloud mr-2" }) - navCloudLaunchableBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.shownId === IdPool.cloud_launchable ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: this.toggleHidden } }, [ cloudIcon, tabInnerText.cloud_launchable ]); - navCloudLaunchable = h('li', { class: "nav-item col-12 col-sm-4" }, [ navCloudLaunchableBtn ]); + navCloudLaunchableBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.shownId === IdPool.cloud_launchable ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: this.toggleHidden } }, [cloudIcon, tabInnerText.cloud_launchable]); + navCloudLaunchable = h('li', { class: "nav-item col-12 col-sm-4" }, [navCloudLaunchableBtn]); serverIcon = h('i', { class: "fas fa-server mr-2" }) - navMetalVirtBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.shownId === IdPool.metal_virtualized ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: this.toggleHidden } }, [ serverIcon, tabInnerText.metal_virtualized ]); - navMetalVirt = h('li', { class: "nav-item col-12 col-sm-4" }, [ navMetalVirtBtn ]); + navMetalVirtBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.shownId === IdPool.metal_virtualized ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: this.toggleHidden } }, [serverIcon, tabInnerText.metal_virtualized]); + navMetalVirt = h('li', { class: "nav-item col-12 col-sm-4" }, [navMetalVirtBtn]); cloudUploadIcon = h('i', { class: "fas fa-cloud-upload-alt mr-2" }) - navCloudOperatorsBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.shownId === IdPool.cloud_operators ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: this.toggleHidden } }, [ cloudUploadIcon, tabInnerText.cloud_operators ]); - navCloudOperators = h('li', { class: "nav-item col-12 col-sm-4" }, [ navCloudOperatorsBtn ]); + navCloudOperatorsBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.shownId === IdPool.cloud_operators ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: this.toggleHidden } }, [cloudUploadIcon, tabInnerText.cloud_operators]); + navCloudOperators = h('li', { class: "nav-item col-12 col-sm-4" }, [navCloudOperatorsBtn]); - navbar = h('ul', { class: "nav nav-tabs mt-3" }, [ navCloudLaunchable, navMetalVirt, navCloudOperators ]); + navbar = h('ul', { class: "nav nav-tabs mt-3" }, [navCloudLaunchable, navMetalVirt, navCloudOperators]); return navbar; }, // Introduction section for streams, the section above the navigation bar for platforms - getStreamIntro: function(h) { + getStreamIntro: function (h) { function onClick(e) { e.preventDefault(); @@ -260,7 +260,7 @@ var coreos_download_app = new Vue({ class: "fas fa-shield-alt fa-2x rounded-circle bg-fedora-blue text-white p-3 ml-4", }, ""); stableHeading = h("h3", { class: "font-weight-light" }, "Stable"); - stableReleaseVersion = h("h6", { class: "text-gray-500 mb-0", attrs: { id: "stable-version" }}, isEmptyObj(this.streamDataAll.stable) ? "" : "v " + this.streamDataAll.stable.architectures.x86_64.artifacts.metal.release); + stableReleaseVersion = h("h6", { class: "text-gray-500 mb-0", attrs: { id: "stable-version" } }, isEmptyObj(this.streamDataAll.stable) ? "" : "v " + this.streamDataAll.stable.architectures.x86_64.artifacts.metal.release); stableJSON = h('p', { class: "text-gray-500", attrs: { id: "stable-json" } }, [ h('span', {}, [ h('a', { class: "font-weight-bold text-gray-500", attrs: { href: `${baseUrl}/stable.json` } }, "JSON") @@ -268,26 +268,26 @@ var coreos_download_app = new Vue({ (isEmptyObj(this.streamDataAll.stable)) ? null : " — ", (isEmptyObj(this.streamDataAll.stable)) ? null : h('span', { class: "font-weight-normal" }, this.timeSince(this.streamDataAll.stable.metadata['last-modified'])) ]); - stableIconContainer = h("div", { class: "col-4" }, [ stableIcon ]) - stableReleaseJSONContainer = h("div", { class: "col-8" }, [ stableHeading, stableReleaseVersion, stableJSON ]) - stableHeadingContainer = h("div", { class: "row" }, [ stableIconContainer, stableReleaseJSONContainer ]) + stableIconContainer = h("div", { class: "col-4" }, [stableIcon]) + stableReleaseJSONContainer = h("div", { class: "col-8" }, [stableHeading, stableReleaseVersion, stableJSON]) + stableHeadingContainer = h("div", { class: "row" }, [stableIconContainer, stableReleaseJSONContainer]) stableIntroText = h("p", { class: "pl-3 pr-2", style: { height: "9em" } }, "The Stable stream should be used by production clusters. Versions of Fedora CoreOS are battle-tested within the Testing and Next streams before being promoted."); stableReleaseLink = h('button', - { - class: "d-block mx-auto mb-4 py-1 px-3 btn btn-sm btn-fedora-blue", - attrs: { id: "stable" }, - on: { - click: onClick - } - }, "Show Downloads"); + { + class: "d-block mx-auto mb-4 py-1 px-3 btn btn-sm btn-fedora-blue", + attrs: { id: "stable" }, + on: { + click: onClick + } + }, "Show Downloads"); // then Testing stream testingIcon = h("i", { class: "fas fa-flask fa-2x rounded-circle bg-fedora-green text-white p-3 ml-4", }, ""); testingHeading = h("h3", { class: "font-weight-light" }, "Testing"); - testingReleaseVersion = h("h6", { class: "text-gray-500 mb-0", attrs: { id: "testing-version" }}, isEmptyObj(this.streamDataAll.testing) ? "" : "v " + this.streamDataAll.testing.architectures.x86_64.artifacts.metal.release); + testingReleaseVersion = h("h6", { class: "text-gray-500 mb-0", attrs: { id: "testing-version" } }, isEmptyObj(this.streamDataAll.testing) ? "" : "v " + this.streamDataAll.testing.architectures.x86_64.artifacts.metal.release); testingJSON = h('p', { class: "text-gray-500", attrs: { id: "testing-json" } }, [ h('span', {}, [ h('a', { class: "font-weight-bold text-gray-500", attrs: { href: `${baseUrl}/testing.json` } }, "JSON") @@ -295,26 +295,26 @@ var coreos_download_app = new Vue({ (isEmptyObj(this.streamDataAll.testing)) ? null : " — ", (isEmptyObj(this.streamDataAll.testing)) ? null : h('span', { class: "font-weight-normal" }, this.timeSince(this.streamDataAll.testing.metadata['last-modified'])) ]); - testingIconContainer = h("div", { class: "col-4" }, [ testingIcon ]) - testingReleaseJSONContainer = h("div", { class: "col-8" }, [ testingHeading, testingReleaseVersion, testingJSON ]) - testingHeadingContainer = h("div", { class: "row" }, [ testingIconContainer, testingReleaseJSONContainer ]) + testingIconContainer = h("div", { class: "col-4" }, [testingIcon]) + testingReleaseJSONContainer = h("div", { class: "col-8" }, [testingHeading, testingReleaseVersion, testingJSON]) + testingHeadingContainer = h("div", { class: "row" }, [testingIconContainer, testingReleaseJSONContainer]) testingIntroText = h("p", { class: "pl-3 pr-2", style: { height: "9em" } }, "The Testing stream consists of promoted Next releases. Mix a few Testing machines into your production clusters to catch any bugs specific to your hardware or configuration."); testingReleaseLink = h('button', - { - class: "d-block mx-auto mb-4 py-1 px-3 btn btn-sm btn-fedora-green", - attrs: { id: "testing" }, - on: { - click: onClick - } - }, "Show Downloads"); + { + class: "d-block mx-auto mb-4 py-1 px-3 btn btn-sm btn-fedora-green", + attrs: { id: "testing" }, + on: { + click: onClick + } + }, "Show Downloads"); // then Next stream nextIcon = h("i", { class: "fas fa-layer-group fa-2x rounded-circle bg-fedora-orange text-white p-3 ml-4", }, ""); nextHeading = h("h3", { class: "font-weight-light" }, "Next"); - nextReleaseVersion = h("h6", { class: "text-gray-500 mb-0", attrs: { id: "next-version" }}, isEmptyObj(this.streamDataAll.next) ? "" : "v " + this.streamDataAll.next.architectures.x86_64.artifacts.metal.release); + nextReleaseVersion = h("h6", { class: "text-gray-500 mb-0", attrs: { id: "next-version" } }, isEmptyObj(this.streamDataAll.next) ? "" : "v " + this.streamDataAll.next.architectures.x86_64.artifacts.metal.release); nextJSON = h('p', { class: "text-gray-500", attrs: { id: "next-json" } }, [ h('span', {}, [ h('a', { class: "font-weight-bold text-gray-500", attrs: { href: `${baseUrl}/next.json` } }, "JSON") @@ -322,20 +322,20 @@ var coreos_download_app = new Vue({ (isEmptyObj(this.streamDataAll.next)) ? null : " — ", (isEmptyObj(this.streamDataAll.next)) ? null : h('span', { class: "font-weight-normal" }, this.timeSince(this.streamDataAll.next.metadata['last-modified'])) ]); - nextIconContainer = h("div", { class: "col-4" }, [ nextIcon ]) - nextReleaseJSONContainer = h("div", { class: "col-8" }, [ nextHeading, nextReleaseVersion, nextJSON ]) - nextHeadingContainer = h("div", { class: "row" }, [ nextIconContainer, nextReleaseJSONContainer ]) + nextIconContainer = h("div", { class: "col-4" }, [nextIcon]) + nextReleaseJSONContainer = h("div", { class: "col-8" }, [nextHeading, nextReleaseVersion, nextJSON]) + nextHeadingContainer = h("div", { class: "row" }, [nextIconContainer, nextReleaseJSONContainer]) nextIntroText = h("p", { class: "pl-3 pr-2", style: { height: "9em" } }, "The Next stream closely tracks current development work and is released frequently. The newest versions of the Linux kernel, Systemd, and other components will be available for testing."); nextReleaseLink = h('button', - { - class: "d-block mx-auto mb-4 py-1 px-3 btn btn-sm btn-fedora-orange", - attrs: { id: "next" }, - on: { - click: onClick - } - }, "Show Downloads"); + { + class: "d-block mx-auto mb-4 py-1 px-3 btn btn-sm btn-fedora-orange", + attrs: { id: "next" }, + on: { + click: onClick + } + }, "Show Downloads"); stableDiv = h('div', { class: "col-12 col-lg-4 border-left border-fedora-blue pt-3", @@ -373,7 +373,7 @@ var coreos_download_app = new Vue({ h('a', { class: "font-weight-bold text-gray-500", attrs: { - href: overviewPageUrl + href: `${overviewPageUrl}?stream=${coreos_download_app.stream}` } }, `View Releases`), `)` @@ -382,25 +382,25 @@ var coreos_download_app = new Vue({ wrapperDiv = h('div', {}, [title, streamsIntroDiv, displayedStreamTitle]); return wrapperDiv; }, - isAws: function(platform) { + isAws: function (platform) { return platform == "aws"; }, - isGcp: function(platform) { + isGcp: function (platform) { return platform == "gcp"; }, - isVirtualizedImage: function(platform) { + isVirtualizedImage: function (platform) { return virtualizedImages.includes(platform); }, - isCloudImage: function(platform) { + isCloudImage: function (platform) { return cloudImages.includes(platform); }, - isBareMetalImage: function(platform) { + isBareMetalImage: function (platform) { return platform == "metal"; }, // Load stream information to display. Note that `loadStreamDisplay` does // not deep-copy information from `streamData` or elsewhere into // `streamDisplay`. - loadStreamDisplay: function() { + loadStreamDisplay: function () { this.streamDisplay = { cloudLaunchable: {}, bareMetal: {}, @@ -431,36 +431,36 @@ var coreos_download_app = new Vue({ for (var region in regions) { const release = getMember(regions[region], "release"); const image = getMember(regions[region], "image"); - displayEntries.push({platform: prettyPlatform, region: region, release: release, image: image}); + displayEntries.push({ platform: prettyPlatform, region: region, release: release, image: image }); } // put 'us', 'eu', and 'ap' first since those are the cheapest and // most popular; then everything else const continentOrdering = ["us", "eu", "ap"]; - displayEntries = displayEntries.sort(function(a, b) { - const aIdx = continentOrdering.indexOf(a.region.slice(0, 2)); - const bIdx = continentOrdering.indexOf(b.region.slice(0, 2)); - if (aIdx == bIdx) { - return a.region.localeCompare(b.region); - } else if (aIdx == -1) { - return 1; - } else if (bIdx == -1){ - return -1; - } else { - return aIdx - bIdx; - } + displayEntries = displayEntries.sort(function (a, b) { + const aIdx = continentOrdering.indexOf(a.region.slice(0, 2)); + const bIdx = continentOrdering.indexOf(b.region.slice(0, 2)); + if (aIdx == bIdx) { + return a.region.localeCompare(b.region); + } else if (aIdx == -1) { + return 1; + } else if (bIdx == -1) { + return -1; + } else { + return aIdx - bIdx; + } }); } - Vue.set(this.streamDisplay.cloudLaunchable, platform, {list: displayEntries}); + Vue.set(this.streamDisplay.cloudLaunchable, platform, { list: displayEntries }); } else if (this.isGcp(platform)) { const name = getMember(images[platform], "name"); const family = getMember(images[platform], "family"); const project = getMember(images[platform], "project"); - Vue.set(this.streamDisplay.cloudLaunchable, platform, {platform: prettyPlatform, name, family, project}); + Vue.set(this.streamDisplay.cloudLaunchable, platform, { platform: prettyPlatform, name, family, project }); } else { const image = getMember(images[platform], "image"); - Vue.set(this.streamDisplay.cloudLaunchable, platform, {platform: prettyPlatform, image: image}); + Vue.set(this.streamDisplay.cloudLaunchable, platform, { platform: prettyPlatform, image: image }); } } const artifacts = getMember(architectureData, "artifacts"); @@ -475,10 +475,10 @@ var coreos_download_app = new Vue({ // in the case where each individual format has a separate pretty // name, we want the artifacts listed in alphabetical order for (var format in formats) { - pretty = getPrettyPlatform(getPrettyPlatform(platform, format)); - prettyFormats.push({format: format, pretty: pretty}); + pretty = getPrettyPlatform(getPrettyPlatform(platform, format)); + prettyFormats.push({ format: format, pretty: pretty }); } - prettyFormats.sort(function(a, b) { return a.pretty.localeCompare(b.pretty); }); + prettyFormats.sort(function (a, b) { return a.pretty.localeCompare(b.pretty); }); for (i = 0; i < prettyFormats.length; i++) { const format = prettyFormats[i].format; const prettyPlatform = prettyFormats[i].pretty; @@ -491,7 +491,7 @@ var coreos_download_app = new Vue({ function addDisplayEntry(display, platform, format, formats, release, prettyPlatform, extension) { downloads = {}; getDownloadsFromFormat(formats[format], downloads); - displayEntry = {platform: prettyPlatform, release: release, downloads: downloads, extension: extension}; + displayEntry = { platform: prettyPlatform, release: release, downloads: downloads, extension: extension }; Vue.set(display, platform + "-" + format, displayEntry); } if (this.isCloudImage(platform)) { @@ -508,46 +508,46 @@ var coreos_download_app = new Vue({ } }, // Load all stream metadata for rendering - refreshStream: function() { + refreshStream: function () { const self = this; self.loading = true self.streamUrl = baseUrl fetchStreamData(baseUrl, "stable") - .then(streamData => { - self.streamDataAll.stable = streamData; - return fetchStreamData(baseUrl, "testing"); - }) - .then(streamData => { - self.streamDataAll.testing = streamData; - return fetchStreamData(baseUrl, "next"); - }) - .then(streamData => { - self.streamDataAll.next = streamData; - return; - }) - .then(() =>{ - const streamData = self.streamDataAll[self.stream]; - self.loading = false; - self.streamData = isEmptyObj(streamData) ? null : streamData; - self.loadStreamDisplay(); - }) + .then(streamData => { + self.streamDataAll.stable = streamData; + return fetchStreamData(baseUrl, "testing"); + }) + .then(streamData => { + self.streamDataAll.testing = streamData; + return fetchStreamData(baseUrl, "next"); + }) + .then(streamData => { + self.streamDataAll.next = streamData; + return; + }) + .then(() => { + const streamData = self.streamDataAll[self.stream]; + self.loading = false; + self.streamData = isEmptyObj(streamData) ? null : streamData; + self.loadStreamDisplay(); + }) }, // Render the `Verify signature & SHA256` modal template - getSignatureAndShaModal: function(h) { - return h('div', { class: "modal", attrs: { id: "signatureAndShaModal", tabindex: "-1", role: "dialog", "aria-labelledby": "signatureAndShaModalLabel", "aria-hidden": "true" }}, [ - h('div', { class: "modal-dialog modal-lg modal-dialog-centered", attrs: { role: "document" }}, [ + getSignatureAndShaModal: function (h) { + return h('div', { class: "modal", attrs: { id: "signatureAndShaModal", tabindex: "-1", role: "dialog", "aria-labelledby": "signatureAndShaModalLabel", "aria-hidden": "true" } }, [ + h('div', { class: "modal-dialog modal-lg modal-dialog-centered", attrs: { role: "document" } }, [ h('div', { class: "modal-content" }, [ h('div', { class: "modal-header" }, [ - h('h5', { class: "modal-title", attrs: { id: "signatureAndShaModalTitle" }}, [ + h('h5', { class: "modal-title", attrs: { id: "signatureAndShaModalTitle" } }, [ "Verify signature & SHA256" ]), - h('button', { class: "close", attrs: { type: "button", "data-dismiss": "modal", "aria-label": "Close" }}, [ - h('span', { attrs: { "aria-hidden": "true" }}, [ "×" ]) + h('button', { class: "close", attrs: { type: "button", "data-dismiss": "modal", "aria-label": "Close" } }, [ + h('span', { attrs: { "aria-hidden": "true" } }, ["×"]) ]) ]), - h('div', { class: "modal-body", attrs: { id: "modal-body" }}, [ "Loading..." ]), + h('div', { class: "modal-body", attrs: { id: "modal-body" } }, ["Loading..."]), h('div', { class: "modal-footer" }, [ - h('button', { class: "btn btn-secondary", attrs: { type: "button", "data-dismiss": "modal" }}, [ + h('button', { class: "btn btn-secondary", attrs: { type: "button", "data-dismiss": "modal" } }, [ "Close" ]) ]) @@ -556,7 +556,7 @@ var coreos_download_app = new Vue({ ]) } }, - render: function(h) { + render: function (h) { if (window.location.href.match(/^.*\/coreos\/download/) == null) { return } @@ -564,7 +564,7 @@ var coreos_download_app = new Vue({ searchParams = new URLSearchParams(window.location.search); // switch to specified tab if `tab` parameter is set if (searchParams.has('tab')) { - switch(searchParams.get('tab')) { + switch (searchParams.get('tab')) { case 'cloud_launchable': this.shownId = IdPool.cloud_launchable; break; @@ -582,7 +582,7 @@ var coreos_download_app = new Vue({ } // switch to specified stream if `stream` parameter is set if (searchParams.has('stream')) { - switch(searchParams.get('stream')) { + switch (searchParams.get('stream')) { case 'stable': this.stream = "stable"; break; @@ -603,85 +603,161 @@ var coreos_download_app = new Vue({ var signatureSha256VerificationModal = this.getSignatureAndShaModal(h); h1Title = h('h1', { class: "font-weight-light text-center my-5" }, "Download Fedora CoreOS"); - streamSelectContainer = h('div', { class: "pb-0 mb-3" }, [ this.getStreamIntro(h), this.getNavbar(h) ]); + streamSelectContainer = h('div', { class: "pb-0 mb-3" }, [this.getStreamIntro(h), this.getNavbar(h)]); if (this.loading) { - streamInfoDiv = h('div', { class: "bg-light" }, [ h('div', { class: "container font-weight-light" }, [ streamSelectContainer ]) ]); - downloadDiv = h('div', { class: "bg-white pb-5" }, [ h('div', { class: "container font-weight-light" }, "Loading...") ]); - return h('div', {}, [ h1Title, streamInfoDiv, downloadDiv ]); + streamInfoDiv = h('div', { class: "bg-light" }, [h('div', { class: "container font-weight-light" }, [streamSelectContainer])]); + downloadDiv = h('div', { class: "bg-white pb-5" }, [h('div', { class: "container font-weight-light" }, "Loading...")]); + return h('div', {}, [h1Title, streamInfoDiv, downloadDiv]); } else if (this.streamData) { cloudLaunchableSection = {}; cloudLaunchable = {}; - virtualizedTitle = h('h3', { class:"font-weight-light" }, "Virtualized"); + virtualizedTitle = h('h3', { class: "font-weight-light" }, "Virtualized"); virtualizedSection = {}; virtualized = {}; - bareMetalTitle = h('h3', { class:"font-weight-light" }, "Bare Metal"); + bareMetalTitle = h('h3', { class: "font-weight-light" }, "Bare Metal"); bareMetalSection = {}; bareMetal = {}; cloudSection = {}; cloud = {}; if (this.streamDisplay.cloudLaunchable) { - cloudLaunchableSection = h('div', {}, Object.entries(this.streamDisplay.cloudLaunchable).map(function(entry) { + cloudLaunchableSection = Object.entries(this.streamDisplay.cloudLaunchable).map(function (entry) { platform = entry[0]; displayInfo = entry[1]; if (coreos_download_app.isAws(platform)) { if (displayInfo.list) { - return h('div', {}, displayInfo.list.map(function(amiInfo) { - return h('div', { class: "p-2 m-2" }, [ - amiInfo.platform ? h('div', { class: "font-weight-bold" }, amiInfo.platform) : null, - amiInfo.region ? h('div', {}, [ "(", amiInfo.region, ")" ]) : null, - amiInfo.release ? h('div', { class: "ml-2" }, [ - h('span', {}, [ amiInfo.release, " " ]), - h('span', { class: "text-secondary" }, coreos_download_app.streamData.stream) - ]) : null, - amiInfo.image ? h('div', { class: "ml-2" }, [ - h('a', { - attrs: { - href: "https://console.aws.amazon.com/ec2/home?region=" + amiInfo.region + "#launchAmi=" + amiInfo.image - } - }, amiInfo.image) - ]) : null - ]) - })); + const toggleRegion = function (e) { + e.preventDefault(); + let awsCloudLaunchableDiv = document.getElementById("aws-cloud-launchables"); + awsCloudLaunchableDiv.childNodes.forEach(childNode => { + if (childNode.id == null || childNode.id.length == 0) return; + if (childNode.id == e.target.innerText) { + childNode.hidden = false; + } else { + childNode.hidden = true; + } + }); + }; + return h('div', { class: "col-12 col-lg-6", attrs: { id: "aws-cloud-launchables" } }, [ + h('div', { class: "px-2 mx-2 pt-2 mt-2 pb-0 mb-0" }, [ + h('div', { class: "font-weight-bold" }, getPrettyPlatform(platform, null)), + h('span', { class: "text-secondary" }, [ + coreos_download_app.streamData.stream, + h('div', { class: "dropdown" }, [ + h('a', { + class: "dropdown-toggle", + attrs: { + "href": "#", + "role": "button", + "data-toggle": "dropdown", + "aria-haspopup": true, + "aria-expanded": false + }, + on: { + click: function (e) { + e.preventDefault(); + } + } + }, "Regions"), + h('div', { class: "dropdown-menu" }, [ + h('div', { class: "container" }, [ + h('div', { class: "row" }, [ + // display region list in two columns + h('div', { class: "col-12 col-sm-6 border-right px-0" }, [ + displayInfo.list.slice(0, Math.ceil(displayInfo.list.length / 2)).map(amiInfo => { + if (amiInfo.region == null || amiInfo.region.length == 0) return; + return h('a', { + class: "dropdown-item", + attrs: { + href: "#", + }, + on: { + click: toggleRegion + } + }, + amiInfo.region); + }) + ]), + h('div', { class: "col-12 col-sm-6 px-0" }, [ + displayInfo.list.slice(Math.ceil(displayInfo.list.length / 2), displayInfo.list.length).map(amiInfo => { + if (amiInfo.region == null || amiInfo.region.length == 0) return; + return h('a', { + class: "dropdown-item", + attrs: { + href: "#", + }, + on: { + click: toggleRegion + } + }, + amiInfo.region); + }) + ]) + ]) + ]) + ]), + ]), + ]) + ]), + displayInfo.list.map(function (amiInfo) { + // by default, only show us-east-1 + return h('div', { class: "px-2 mx-2 pb-2 mb-2 pt-0 mt-0", attrs: { id: amiInfo.region, hidden: amiInfo.region != "us-east-1" } }, [ + amiInfo.region ? h('div', {}, ["- Region: ", amiInfo.region]) : null, + amiInfo.release ? h('div', { class: "ml-2" }, [ + h('span', {}, ["Release: ", amiInfo.release]) + ]) : null, + amiInfo.image ? h('div', { class: "ml-2" }, [ + "Image: ", + h('a', { + attrs: { + href: "https://console.aws.amazon.com/ec2/home?region=" + amiInfo.region + "#launchAmi=" + amiInfo.image + } + }, amiInfo.image) + ]) : null + ]) + }) + ]); } } if (coreos_download_app.isGcp(platform)) { - return h('div', { class: "p-2 m-2" }, [ - displayInfo.platform ? h('div', { class: "font-weight-bold" }, displayInfo.platform) : null, - coreos_download_app.streamData.stream ? h('span', { class: "text-secondary" }, coreos_download_app.streamData.stream) : null, - displayInfo.project ? h('div', { class: "ml-2" }, [ "Project: ", displayInfo.project ]) : null, - displayInfo.family ? h('div', { class: "ml-2" }, [ - "Family: ", - h('a', { - attrs: { - href: `https://console.cloud.google.com/marketplace/details/fedora-coreos-cloud/fedora-coreos-${coreos_download_app.streamData.stream}` - } - }, displayInfo.family), - " (", - h('a', { - attrs: { - href: "#" - }, - on: { - click: function(e) { - e.preventDefault(); - let gcpNameElement = e.target.parentElement.nextSibling; - gcpNameElement.hidden = !gcpNameElement.hidden; + return h('div', { class: "col-12 col-lg-6" }, [ + h('div', { class: "p-2 m-2" }, [ + displayInfo.platform ? h('div', { class: "font-weight-bold" }, displayInfo.platform) : null, + coreos_download_app.streamData.stream ? h('span', { class: "text-secondary" }, coreos_download_app.streamData.stream) : null, + displayInfo.project ? h('div', { class: "ml-2" }, ["Project: ", displayInfo.project]) : null, + displayInfo.family ? h('div', { class: "ml-2" }, [ + "Family: ", + h('a', { + attrs: { + href: `https://console.cloud.google.com/marketplace/details/fedora-coreos-cloud/fedora-coreos-${coreos_download_app.streamData.stream}` } - } - }, 'details'), - ")"] - ) : null, - displayInfo.name ? h('div', { class: "ml-2", attrs: { hidden: true } }, [ - h('span', {}, [ - `- The current latest image in the`, - h('span', { class: "font-weight-normal font-italic" }, ` ${displayInfo.family}`), - " image family is ", - h('span', { class: "font-weight-normal font-italic" }, displayInfo.name), - "." - ]) - ]) : null + }, displayInfo.family), + " (", + h('a', { + attrs: { + href: "#" + }, + on: { + click: function (e) { + e.preventDefault(); + let gcpNameElement = e.target.parentElement.nextSibling; + gcpNameElement.hidden = !gcpNameElement.hidden; + } + } + }, 'details'), + ")"] + ) : null, + displayInfo.name ? h('div', { class: "ml-2", attrs: { hidden: true } }, [ + h('span', {}, [ + `- The current latest image in the`, + h('span', { class: "font-weight-normal font-italic" }, ` ${displayInfo.family}`), + " image family is ", + h('span', { class: "font-weight-normal font-italic" }, displayInfo.name), + "." + ]) + ]) : null + ]) ]); } else { @@ -690,16 +766,16 @@ var coreos_download_app = new Vue({ displayInfo.image ? h('div', { class: "ml-2" }, displayInfo.image) : null ]); } - })); + }); } else { cloudLaunchableSection = h('div', {}, "No cloud launchable images found."); } - cloudLaunchable = h('div', { class: "col-12 py-2 my-2" }, [ cloudLaunchableSection ]); + cloudLaunchable = h('div', { class: "row col-12 py-2 my-2" }, [cloudLaunchableSection]); function createDownloadsSubSection(displayDownloads, contentType, showTitle, imageType) { verifyBlurb = - `
+ `
Verify your download using the detached signature after importing Fedora's GPG signing keys. The detached signature is for the released artifact itself. If there is a good signature from one of the Fedora keys, and the SHA256 checksum matches, then the download is valid.
` @@ -711,7 +787,7 @@ var coreos_download_app = new Vue({ h('div', {}, [ h('button', { on: { - click: function(e) { + click: function (e) { // on click edit the content of popup modal if (e.target !== e.currentTarget) { return; @@ -722,21 +798,21 @@ var coreos_download_app = new Vue({ let aChecksum = null; let aSignature = null; // Show SHA256 and initialize the tags if data is available - if(displayDownloads.sha256) { + if (displayDownloads.sha256) { let d = document.createElement('div'); $(d).addClass("overflow-auto") - .html("SHA256: " + displayDownloads.sha256) - .appendTo(p); + .html("SHA256: " + displayDownloads.sha256) + .appendTo(p); aChecksum = document.createElement('a'); $(aChecksum).attr("href", "data:text/plain;charset=utf-8," + encodeURIComponent("SHA256 (" + getFilename(displayDownloads.location) + ") = " + displayDownloads.sha256)) - .attr("download", getFilename(displayDownloads.location) + "-CHECKSUM") - .html("checksum file"); + .attr("download", getFilename(displayDownloads.location) + "-CHECKSUM") + .html("checksum file"); } - if(displayDownloads.signature) { + if (displayDownloads.signature) { aSignature = document.createElement('a'); $(aSignature).attr("href", displayDownloads.signature) - .html("signature"); + .html("signature"); } $(p).appendTo("#modal-body"); $(verifyBlurb).appendTo("#modal-body"); @@ -747,9 +823,9 @@ var coreos_download_app = new Vue({ let li = document.createElement('li'); p = document.createElement('p'); $(p).append("Download the ") - .append(aChecksum) - .append(aChecksum && aSignature ? " and " : "") - .append(aSignature); + .append(aChecksum) + .append(aChecksum && aSignature ? " and " : "") + .append(aSignature); $(p).appendTo(li); $(li).appendTo(ol); } @@ -761,7 +837,7 @@ var coreos_download_app = new Vue({ code = document.createElement('code'); pre = document.createElement('pre'); $(code).html("curl https://getfedora.org/static/fedora.gpg | gpg --import") - .appendTo(pre); + .appendTo(pre); $(p).appendTo(li); $(pre).appendTo(li); $(li).appendTo(ol); @@ -773,7 +849,7 @@ var coreos_download_app = new Vue({ code = document.createElement('code'); pre = document.createElement('pre'); $(code).html("gpg --verify " + getFilename(displayDownloads.signature) + " " + getFilename(displayDownloads.location)) - .appendTo(pre); + .appendTo(pre); $(p).appendTo(li); $(pre).appendTo(li); $(li).appendTo(ol); @@ -785,7 +861,7 @@ var coreos_download_app = new Vue({ code = document.createElement('code'); pre = document.createElement('pre'); $(code).html("sha256sum -c " + getFilename(displayDownloads.location) + "-CHECKSUM") - .appendTo(pre); + .appendTo(pre); $(p).appendTo(li); $(pre).appendTo(li); $(li).appendTo(ol); @@ -806,14 +882,14 @@ var coreos_download_app = new Vue({ ]) : null } function createArtifactsSection(displayArtifacts, imageType) { - return h('div', {}, Object.entries(displayArtifacts).map(function(entry) { + return h('div', { class: imageType == "cloud" ? "row" : "" }, Object.entries(displayArtifacts).map(function (entry) { platformFormat = entry[0]; displayInfo = entry[1]; - return h('div', { class: "p-2 m-2" }, [ + return h('div', { class: imageType == "cloud" ? "py-2 my-2 col-12 col-lg-6" : "p-2 m-2" }, [ displayInfo.platform ? h('div', { class: "font-weight-bold" }, displayInfo.platform) : null, - displayInfo.extension ? h('div', {}, [ "(", displayInfo.extension, ")" ]) : null, + displayInfo.extension ? h('div', {}, ["(", displayInfo.extension, ")"]) : null, displayInfo.release ? h('div', { class: "ml-2" }, [ - h('span', {}, [ displayInfo.release, " " ]), + h('span', {}, [displayInfo.release, " "]), h('span', { class: "text-secondary" }, coreos_download_app.streamData.stream) ]) : null, displayInfo.downloads ? h('div', { class: "ml-2" }, [ @@ -830,7 +906,7 @@ var coreos_download_app = new Vue({ else { bareMetalSection = h('div', {}, "No bare metal images found."); } - bareMetal = h('div', { class: "col-12 py-2 my-2" }, [ bareMetalSection ]); + bareMetal = h('div', { class: "col-12" }, [bareMetalSection]); if (this.streamDisplay.virtualized) { virtualizedSection = createArtifactsSection(this.streamDisplay.virtualized, 'virtualized'); @@ -838,7 +914,7 @@ var coreos_download_app = new Vue({ else { virtualizedSection = h('div', {}, "No virtualized images found."); } - virtualized = h('div', { class: "col-12 py-2 my-2" }, [ virtualizedSection ]); + virtualized = h('div', { class: "col-12" }, [virtualizedSection]); if (this.streamDisplay.cloud) { cloudSection = createArtifactsSection(this.streamDisplay.cloud, 'cloud'); @@ -846,16 +922,16 @@ var coreos_download_app = new Vue({ else { cloudSection = h('div', {}, "No cloud images found."); } - cloud = h('div', { class: "col-12 py-2 my-2" }, [ cloudSection ]); + cloud = h('div', { class: "col-12 py-2 my-2" }, [cloudSection]); - let bareMetalContainer = h('div', { class: "col-lg-6" }, [ bareMetalTitle, bareMetal ]); - let virtualizedContainer = h('div', { class: "col-lg-6" }, [ virtualizedTitle, virtualized ]); + let bareMetalContainer = h('div', { class: "col-lg-6 my-2 py-2" }, [bareMetalTitle, bareMetal]); + let virtualizedContainer = h('div', { class: "col-lg-6 my-2 py-2" }, [virtualizedTitle, virtualized]); - let cloudLaunchableContainer = h('div', { class: "col-12 py-2 my-2", attrs: { id: IdPool.cloud_launchable, hidden: this.shownId !== IdPool.cloud_launchable } }, [ cloudLaunchable ]); - let metalVirtContainer = h('div', { class: "row col-12 py-2 my-2", attrs: { id: IdPool.metal_virtualized, hidden: this.shownId !== IdPool.metal_virtualized } }, [ bareMetalContainer, virtualizedContainer ]); - let cloudOperatorsContainer = h('div', { class: "col-12 py-2 my-2", attrs: { id: IdPool.cloud_operators, hidden: this.shownId !== IdPool.cloud_operators } }, [ cloud ]); + let cloudLaunchableContainer = h('div', { class: "col-12 py-2 my-2", attrs: { id: IdPool.cloud_launchable, hidden: this.shownId !== IdPool.cloud_launchable } }, [cloudLaunchable]); + let metalVirtContainer = h('div', { class: "row col-12 py-2 my-2", attrs: { id: IdPool.metal_virtualized, hidden: this.shownId !== IdPool.metal_virtualized } }, [bareMetalContainer, virtualizedContainer]); + let cloudOperatorsContainer = h('div', { class: "col-12 py-2 my-2", attrs: { id: IdPool.cloud_operators, hidden: this.shownId !== IdPool.cloud_operators } }, [cloud]); - streamInfoDiv = h('div', { class: "bg-light" }, [ h('div', { class: "container font-weight-light" }, [ streamSelectContainer ]) ]); + streamInfoDiv = h('div', { class: "bg-light" }, [h('div', { class: "container font-weight-light" }, [streamSelectContainer])]); downloadDiv = h('div', { class: "bg-white pb-5" }, [ h('div', { class: "container font-weight-light" }, [ signatureSha256VerificationModal, @@ -868,8 +944,8 @@ var coreos_download_app = new Vue({ return h('div', {}, [h1Title, streamInfoDiv, downloadDiv]); } else { - errorDiv = h('div', { class: "bg-transparent py-5" }, [ h('div', { class: "container font-weight-light" }, "No stream data found!") ]); - return h('div', {}, [ errorDiv ]); + errorDiv = h('div', { class: "bg-transparent py-5" }, [h('div', { class: "container font-weight-light" }, "No stream data found!")]); + return h('div', {}, [errorDiv]); } } }) diff --git a/sites/static/js/coreos-release-notes.js b/sites/static/js/coreos-release-notes.js index 7c4f235..d89a736 100644 --- a/sites/static/js/coreos-release-notes.js +++ b/sites/static/js/coreos-release-notes.js @@ -30,26 +30,26 @@ function timestampToPrettyString(date) { } function getBaseUrl(stream, developer) { - return stream != "developer" - ? `${baseProdUrl}/${stream}` - : `${baseDevelUrl}/${developer}`; + return stream != "developer" + ? `${baseProdUrl}/${stream}` + : `${baseDevelUrl}/${developer}`; } function sortPkgDiff(meta) { if ("pkgdiff" in meta) { - var newdiff = {}; - diffType.forEach(t => newdiff[t] = []); - meta["pkgdiff"].forEach(d => newdiff[diffType[d[1]]].push(d)); - meta["pkgdiff"] = newdiff; + var newdiff = {}; + diffType.forEach(t => newdiff[t] = []); + meta["pkgdiff"].forEach(d => newdiff[diffType[d[1]]].push(d)); + meta["pkgdiff"] = newdiff; } } function findImportantPkgs(commitmeta) { var r = []; commitmeta["rpmostree.rpmdb.pkglist"].forEach(pkg => { - if (importantPkgs.includes(pkg[0])) { - r.push(pkg); - } + if (importantPkgs.includes(pkg[0])) { + r.push(pkg); + } }); return r; } @@ -57,24 +57,24 @@ function findImportantPkgs(commitmeta) { // The actual fetch function for `releases.json` function fetchReleases(base) { return fetch(`${base}/releases.json`) - .then(response => response.ok ? response.json() : {"releases": []}) - .then(data => { - return data.releases.map(release => release.version); - }); + .then(response => response.ok ? response.json() : { "releases": [] }) + .then(data => { + return data.releases.map(release => release.version); + }); } // The actual fetch function for `builds.json` function fetchBuilds(base) { - return fetch(`${base}/builds.json`) - .then(response => response.ok ? response.json() : {"builds": []}) - .then(data => { - if (!('schema-version' in data) || data["schema-version"] != "1.0.0") { - // in legacy mode, just assume we only built x86_64 - return [true, data.builds.map(id => ({'id': id, 'arches': ['x86_64'], 'meta': null, 'commitmeta': null}))]; - } else { - return [false, data.builds.map(build => ({'id': build.id, 'arches': build.arches, 'meta': null, 'commitmeta': null}))]; - } - }); + return fetch(`${base}/builds.json`) + .then(response => response.ok ? response.json() : { "builds": [] }) + .then(data => { + if (!('schema-version' in data) || data["schema-version"] != "1.0.0") { + // in legacy mode, just assume we only built x86_64 + return [true, data.builds.map(id => ({ 'id': id, 'arches': ['x86_64'], 'meta': null, 'commitmeta': null }))]; + } else { + return [false, data.builds.map(build => ({ 'id': build.id, 'arches': build.arches, 'meta': null, 'commitmeta': null }))]; + } + }); } // Gather a metadata list of builds between releases @@ -113,10 +113,10 @@ function gatherMetadataBtwReleases(currentReleaseIdx, targetReleaseIdx, config) // Get an accumulated pkgdiff given a list of metadata // e.g. given a list of build metadata fetched between two consecutive releases // we can compute the overall accumulated pkgdiff between two releases -function getPkgDiffFromMetaList (metaList) { +function getPkgDiffFromMetaList(metaList) { function getPkgDiffReducer(pkgDiffAcc, currentMeta) { // NOTE: pkgDiffAcc is the most recent diff accumulated, and currentMeta has the older pkgdiff - if (! ("pkgdiff" in currentMeta[1])) { + if (!("pkgdiff" in currentMeta[1])) { return pkgDiffAcc; } currentMeta[1].pkgdiff.map(d => { @@ -287,12 +287,12 @@ function fetchBuild(base, legacy, builds, fromIdx, toIdx) { // The actual fetch function for `meta.json` function fetchBuildMeta(base, build, legacy) { if (legacy) { - return fetch(`${base}/${build.id}/meta.json`) - .then(response => Promise.all([build.arches[0], response.ok ? response.json() : {}])); + return fetch(`${base}/${build.id}/meta.json`) + .then(response => Promise.all([build.arches[0], response.ok ? response.json() : {}])); } // XXX: just fetch the meta for the first arch right now return fetch(`${base}/${build.id}/${build.arches[0]}/meta.json`) - .then(response => Promise.all([build.arches[0], response.ok ? response.json() : {}])); + .then(response => Promise.all([build.arches[0], response.ok ? response.json() : {}])); // return Promise.all(build.arches.map(arch => { // fetch(`${base}/${build.id}/${arch}/meta.json`) @@ -303,16 +303,16 @@ function fetchBuildMeta(base, build, legacy) { // The actual fetch function for `commitmeta.json` function fetchBuildCommitMeta(base, build, basearch, legacy) { if (legacy) { - return fetch(`${base}/${build.id}/commitmeta.json`) - .then(response => response.ok ? response.json() : {}); + return fetch(`${base}/${build.id}/commitmeta.json`) + .then(response => response.ok ? response.json() : {}); } return fetch(`${base}/${build.id}/${basearch}/commitmeta.json`) - .then(response => response.ok ? response.json() : {}); + .then(response => response.ok ? response.json() : {}); } var coreos_release_notes = new Vue({ el: '#coreos-release-notes', - created: function() { this.refreshBuilds() }, + created: function () { this.refreshBuilds() }, data: { // source of truth for streams streamList: ['stable', 'testing', 'next'], @@ -336,54 +336,54 @@ var coreos_release_notes = new Vue({ loading: true }, watch: { - stream: function() { + stream: function () { this.refreshBuilds(); } }, methods: { - getPkgNevra: function(tuple) { + getPkgNevra: function (tuple) { return `${tuple[0]}-${tuple[1]}.${tuple[2]}`; }, - getPkgNevraFull: function(tuple) { - if (tuple[1] != 0) { - return `${tuple[0]}-${tuple[1]}:${tuple[2]}-${tuple[3]}.${tuple[4]}`; - } - return `${tuple[0]}-${tuple[2]}-${tuple[3]}.${tuple[4]}`; + getPkgNevraFull: function (tuple) { + if (tuple[1] != 0) { + return `${tuple[0]}-${tuple[1]}:${tuple[2]}-${tuple[3]}.${tuple[4]}`; + } + return `${tuple[0]}-${tuple[2]}-${tuple[3]}.${tuple[4]}`; }, - getPkgEvra: function(tuple) { - return `${tuple[1]}.${tuple[2]}`; + getPkgEvra: function (tuple) { + return `${tuple[1]}.${tuple[2]}`; }, - getNavbar: function(h) { + getNavbar: function (h) { const self = this; const changeStream = e => { if (e.target.innerText === "Stable Stream") { - self.stream = "stable" + self.stream = "stable" } if (e.target.innerText === "Testing Stream") { - self.stream = "testing" + self.stream = "testing" } if (e.target.innerText === "Next Stream") { - self.stream = "next" + self.stream = "next" } const overviewPageUrl = window.location.href.match(/^.*\/coreos/)[0]; history.replaceState(null, null, `${overviewPageUrl}?stream=${self.stream}`); } let shieldIcon = h('i', { class: "fas fa-shield-alt mr-2" }) - let navStableBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.stream === "stable" ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: changeStream } }, [ shieldIcon, "Stable Stream" ]); - let navStable = h('li', { class: "nav-item col-12 col-sm-4" }, [ navStableBtn ]); + let navStableBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.stream === "stable" ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: changeStream } }, [shieldIcon, "Stable Stream"]); + let navStable = h('li', { class: "nav-item col-12 col-sm-4" }, [navStableBtn]); let flaskIcon = h('i', { class: "fas fa-flask mr-2" }) - let navTestingBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.stream === "testing" ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: changeStream } }, [ flaskIcon, "Testing Stream" ]); - let navTesting = h('li', { class: "nav-item col-12 col-sm-4" }, [ navTestingBtn ]); + let navTestingBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.stream === "testing" ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: changeStream } }, [flaskIcon, "Testing Stream"]); + let navTesting = h('li', { class: "nav-item col-12 col-sm-4" }, [navTestingBtn]); let layerIcon = h('i', { class: "fas fa-layer-group mr-2" }) - let navNextBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.stream === "next" ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: changeStream } }, [ layerIcon, "Next Stream" ]); - let navNext = h('li', { class: "nav-item col-12 col-sm-4" }, [ navNextBtn ]); + let navNextBtn = h('button', { class: "nav-link col-12 h-100 overflow-hidden".concat(this.stream === "next" ? " active" : ""), attrs: { "data-toggle": "tab" }, on: { click: changeStream } }, [layerIcon, "Next Stream"]); + let navNext = h('li', { class: "nav-item col-12 col-sm-4" }, [navNextBtn]); - let navbar = h('ul', { class: "nav nav-tabs" }, [ navStable, navTesting, navNext ]); + let navbar = h('ul', { class: "nav nav-tabs" }, [navStable, navTesting, navNext]); return navbar; }, - getReleaseNoteCards: function(h) { + getReleaseNoteCards: function (h) { const self = this; // check if all release metadata has been fetched if (self.loading) { @@ -403,7 +403,7 @@ var coreos_release_notes = new Vue({ build.arches.forEach((arch, _) => { headingListArches.push(h('h6', {}, arch)); }); - let leftPane = h('div', { class: "col-lg-2" }, [ headingBuildId, headingListArches ]); + let leftPane = h('div', { class: "col-lg-2" }, [headingBuildId, headingListArches]); // Right pane consists of detailed package information let date = h('p', {}, `Release Date: ${timestampToPrettyString(build.meta['coreos-assembler.build-timestamp'])}`); @@ -423,7 +423,7 @@ var coreos_release_notes = new Vue({ href: "#" }, on: { - click: function(e) { + click: function (e) { e.preventDefault(); let totalPkgListElement = e.target.parentElement.nextSibling; if (totalPkgListElement.hidden == true) { @@ -444,29 +444,29 @@ var coreos_release_notes = new Vue({ pkgSummaryElements = pkgSummaryElements.concat( `${build.meta.pkgdiff.added.length} added (` ) - .concat( - h('a', { - attrs: { - href: "#" - }, - on: { - click: function(e) { - e.preventDefault(); - let totalPkgListElement = e.target.parentElement - .nextSibling - .nextSibling; - if (totalPkgListElement.hidden == true) { - totalPkgListElement.hidden = false; - e.target.innerText = 'collapse'; - } else { - totalPkgListElement.hidden = true; - e.target.innerText = 'expand'; + .concat( + h('a', { + attrs: { + href: "#" + }, + on: { + click: function (e) { + e.preventDefault(); + let totalPkgListElement = e.target.parentElement + .nextSibling + .nextSibling; + if (totalPkgListElement.hidden == true) { + totalPkgListElement.hidden = false; + e.target.innerText = 'collapse'; + } else { + totalPkgListElement.hidden = true; + e.target.innerText = 'expand'; + } } } - } - }, 'expand') - ) - .concat('); '); + }, 'expand') + ) + .concat('); '); } // `removed` summary and expand button @@ -474,30 +474,30 @@ var coreos_release_notes = new Vue({ pkgSummaryElements = pkgSummaryElements.concat( `${build.meta.pkgdiff.removed.length} removed (` ) - .concat( - h('a', { - attrs: { - href: "#" - }, - on: { - click: function(e) { - e.preventDefault(); - let totalPkgListElement = e.target.parentElement - .nextSibling - .nextSibling - .nextSibling; - if (totalPkgListElement.hidden == true) { - totalPkgListElement.hidden = false; - e.target.innerText = 'collapse'; - } else { - totalPkgListElement.hidden = true; - e.target.innerText = 'expand'; + .concat( + h('a', { + attrs: { + href: "#" + }, + on: { + click: function (e) { + e.preventDefault(); + let totalPkgListElement = e.target.parentElement + .nextSibling + .nextSibling + .nextSibling; + if (totalPkgListElement.hidden == true) { + totalPkgListElement.hidden = false; + e.target.innerText = 'collapse'; + } else { + totalPkgListElement.hidden = true; + e.target.innerText = 'expand'; + } } } - } - }, 'expand') - ) - .concat('); '); + }, 'expand') + ) + .concat('); '); } // `upgraded` summary and expand button @@ -505,31 +505,31 @@ var coreos_release_notes = new Vue({ pkgSummaryElements = pkgSummaryElements.concat( `${build.meta.pkgdiff.upgraded.length} upgraded (` ) - .concat( - h('a', { - attrs: { - href: "#" - }, - on: { - click: function(e) { - e.preventDefault(); - let totalPkgListElement = e.target.parentElement - .nextSibling - .nextSibling - .nextSibling - .nextSibling; - if (totalPkgListElement.hidden == true) { - totalPkgListElement.hidden = false; - e.target.innerText = 'collapse'; - } else { - totalPkgListElement.hidden = true; - e.target.innerText = 'expand'; + .concat( + h('a', { + attrs: { + href: "#" + }, + on: { + click: function (e) { + e.preventDefault(); + let totalPkgListElement = e.target.parentElement + .nextSibling + .nextSibling + .nextSibling + .nextSibling; + if (totalPkgListElement.hidden == true) { + totalPkgListElement.hidden = false; + e.target.innerText = 'collapse'; + } else { + totalPkgListElement.hidden = true; + e.target.innerText = 'expand'; + } } } - } - }, 'expand') - ) - .concat('); '); + }, 'expand') + ) + .concat('); '); } // `downgraded` summary and expand button @@ -537,32 +537,32 @@ var coreos_release_notes = new Vue({ pkgSummaryElements = pkgSummaryElements.concat( `${build.meta.pkgdiff.downgraded.length} downgraded (` ) - .concat( - h('a', { - attrs: { - href: "#" - }, - on: { - click: function(e) { - e.preventDefault(); - let totalPkgListElement = e.target.parentElement - .nextSibling - .nextSibling - .nextSibling - .nextSibling - .nextSibling; - if (totalPkgListElement.hidden == true) { - totalPkgListElement.hidden = false; - e.target.innerText = 'collapse'; - } else { - totalPkgListElement.hidden = true; - e.target.innerText = 'expand'; + .concat( + h('a', { + attrs: { + href: "#" + }, + on: { + click: function (e) { + e.preventDefault(); + let totalPkgListElement = e.target.parentElement + .nextSibling + .nextSibling + .nextSibling + .nextSibling + .nextSibling; + if (totalPkgListElement.hidden == true) { + totalPkgListElement.hidden = false; + e.target.innerText = 'collapse'; + } else { + totalPkgListElement.hidden = true; + e.target.innerText = 'expand'; + } } } - } - }, 'expand') - ) - .concat('); '); + }, 'expand') + ) + .concat('); '); } let pkgSummaryDiv = h('div', { class: "mt-3" }, pkgSummaryElements); @@ -576,7 +576,7 @@ var coreos_release_notes = new Vue({ }); totalPkgsHeading = h('p', { class: "mt-3" }, "Package List:") } - let totalPkgsElements = h('div', { attrs: { hidden: true } }, [ totalPkgsHeading, h('ul', {}, totalPkgsElementsList) ]); + let totalPkgsElements = h('div', { attrs: { hidden: true } }, [totalPkgsHeading, h('ul', {}, totalPkgsElementsList)]); // Added package list let addedPkgsElementsList = []; @@ -587,7 +587,7 @@ var coreos_release_notes = new Vue({ }); addedPkgsHeading = h('p', { class: "mt-3" }, "Added:") } - let addedPkgsElements = h('div', { attrs: { hidden: true } }, [ addedPkgsHeading, h('ul', {}, addedPkgsElementsList) ]); + let addedPkgsElements = h('div', { attrs: { hidden: true } }, [addedPkgsHeading, h('ul', {}, addedPkgsElementsList)]); // Removed package list let removedPkgsElementsList = []; @@ -598,7 +598,7 @@ var coreos_release_notes = new Vue({ }); removedPkgsHeading = h('p', { class: "mt-3" }, "Removed:"); } - let removedPkgsElements = h('div', { attrs: { hidden: true } }, [ removedPkgsHeading, h('ul', {}, removedPkgsElementsList) ]); + let removedPkgsElements = h('div', { attrs: { hidden: true } }, [removedPkgsHeading, h('ul', {}, removedPkgsElementsList)]); // Upgraded package list let upgradedPkgsElementsList = []; @@ -609,7 +609,7 @@ var coreos_release_notes = new Vue({ }); upgradedPkgsHeading = h('p', { class: "mt-3" }, "Upgraded:"); } - let upgradedPkgsElements = h('div', { attrs: { hidden: true } }, [ upgradedPkgsHeading, h('ul', {}, upgradedPkgsElementsList) ]); + let upgradedPkgsElements = h('div', { attrs: { hidden: true } }, [upgradedPkgsHeading, h('ul', {}, upgradedPkgsElementsList)]); // Downgraded package list let downgradedPkgsElementsList = []; @@ -620,43 +620,43 @@ var coreos_release_notes = new Vue({ }); downgradedPkgsHeading = h('p', { class: "mt-3" }, "Downgraded:"); } - let downgradedPkgsElements = h('div', { attrs: { hidden: true } }, [ downgradedPkgsHeading, h('ul', {}, downgradedPkgsElementsList) ]); + let downgradedPkgsElements = h('div', { attrs: { hidden: true } }, [downgradedPkgsHeading, h('ul', {}, downgradedPkgsElementsList)]); - let rightPane = h('div', { class: "col-lg-10 border-bottom mb-5 pb-4" }, [ date, importantPkgsElements, pkgSummaryDiv, totalPkgsElements, addedPkgsElements, removedPkgsElements, upgradedPkgsElements, downgradedPkgsElements ]); - let row = h('div', { class: "row" }, [ leftPane, rightPane ]); + let rightPane = h('div', { class: "col-lg-10 border-bottom mb-5 pb-4" }, [date, importantPkgsElements, pkgSummaryDiv, totalPkgsElements, addedPkgsElements, removedPkgsElements, upgradedPkgsElements, downgradedPkgsElements]); + let row = h('div', { class: "row" }, [leftPane, rightPane]); rows.push(row); }) return h('div', { class: "my-5" }, rows); }, - refreshBuilds: function() { - this.loading = true - this.releasesUrl = getBaseUrl(this.stream, this.developer); - this.buildsUrl = getBaseUrl(this.stream, this.developer) + "/builds"; - fetchReleases(this.releasesUrl).then(releaseVersions => { - fetchBuilds(this.buildsUrl).then(result => { - [legacy, builds] = result; - // first populate and show the build list - this.legacy = legacy; - this.releases = []; - this.unshown_builds = []; - // counter for the number of release metadata fetched since fetch is asnyc operation - let counter = 0; - - // get the index list of release builds in the build list - const releaseIdxList = builds.map((build, idx) => releaseVersions.includes(build.id) ? idx : -1).filter(idx => idx != -1); - const numReleases = releaseIdxList.length; - - // fetch the metadata and compute the pkgdiff for subsequent releases - // since the oldest release does not have a pkgdiff, the pkgdiff for oldest release is an empty array - for (let i = 0; i < numReleases; i++) { - const releaseIdx = releaseIdxList[i]; - // in case of oldest release, there's no older release - const nextReleaseIdx = releaseIdxList[i + 1] == null ? releaseIdxList[i] : releaseIdxList[i + 1]; - if (i < initialBuildsShown) { - // NOTE: here only the `builds` array have the actual values, all other variables are pointers to the elements of this array - this.releases.push(builds[releaseIdx]); - // fetchBuild mutates the `builds` array - fetchBuild(this.buildsUrl, this.legacy, builds, releaseIdx, nextReleaseIdx) + refreshBuilds: function () { + this.loading = true + this.releasesUrl = getBaseUrl(this.stream, this.developer); + this.buildsUrl = getBaseUrl(this.stream, this.developer) + "/builds"; + fetchReleases(this.releasesUrl).then(releaseVersions => { + fetchBuilds(this.buildsUrl).then(result => { + [legacy, builds] = result; + // first populate and show the build list + this.legacy = legacy; + this.releases = []; + this.unshown_builds = []; + // counter for the number of release metadata fetched since fetch is asnyc operation + let counter = 0; + + // get the index list of release builds in the build list + const releaseIdxList = builds.map((build, idx) => releaseVersions.includes(build.id) ? idx : -1).filter(idx => idx != -1); + const numReleases = releaseIdxList.length; + + // fetch the metadata and compute the pkgdiff for subsequent releases + // since the oldest release does not have a pkgdiff, the pkgdiff for oldest release is an empty array + for (let i = 0; i < numReleases; i++) { + const releaseIdx = releaseIdxList[i]; + // in case of oldest release, there's no older release + const nextReleaseIdx = releaseIdxList[i + 1] == null ? releaseIdxList[i] : releaseIdxList[i + 1]; + if (i < initialBuildsShown) { + // NOTE: here only the `builds` array have the actual values, all other variables are pointers to the elements of this array + this.releases.push(builds[releaseIdx]); + // fetchBuild mutates the `builds` array + fetchBuild(this.buildsUrl, this.legacy, builds, releaseIdx, nextReleaseIdx) .then(() => { counter++; if (counter === numReleases) { @@ -664,31 +664,31 @@ var coreos_release_notes = new Vue({ this.loading = false; } }); - } else { - // XXX: unshown/unprocessed releases, could be handled later according to needs - this.unshown_builds.push(builds[releaseIdx]); - counter++; - if (counter === numReleases) { - // fetched all metadata - this.loading = false; - } + } else { + // XXX: unshown/unprocessed releases, could be handled later according to needs + this.unshown_builds.push(builds[releaseIdx]); + counter++; + if (counter === numReleases) { + // fetched all metadata + this.loading = false; } } - }); + } }); + }); } }, - render: function(h) { + render: function (h) { // Duplicate logic from coreos-download.js // URL paramters checking and setting default values - if(window.location.href.match(/^.*\/coreos/) == null) { + if (window.location.href.match(/^.*\/coreos/) == null) { return } const overviewPageUrl = window.location.href.match(/^.*\/coreos/)[0]; searchParams = new URLSearchParams(window.location.search); // switch to specified stream if `stream` parameter is set if (searchParams.has('stream')) { - switch(searchParams.get('stream')) { + switch (searchParams.get('stream')) { case 'stable': this.stream = "stable"; break; @@ -710,11 +710,11 @@ var coreos_release_notes = new Vue({ let navBar = this.getNavbar(h); if (this.loading) { - let loadingDiv = h('div', { class: "bg-white pb-5" }, [ h('div', { class: "container font-weight-light" }, "Loading...") ]); - return h('div', {}, [ navBar, loadingDiv ]); + let loadingDiv = h('div', { class: "bg-white pb-5" }, [h('div', { class: "container font-weight-light" }, "Loading...")]); + return h('div', {}, [navBar, loadingDiv]); } else { let releaseNoteCards = this.getReleaseNoteCards(h); - return h('div', {}, [ navBar, releaseNoteCards ]); + return h('div', {}, [navBar, releaseNoteCards]); } } });