From 2f8abaf0b3f7341e2ddd34c32ecd9b28a55e904e Mon Sep 17 00:00:00 2001 From: Dan Macpherson Date: Fri, 22 Nov 2024 00:50:57 +1000 Subject: [PATCH 1/4] Fixing dates for content --- content/patterns/ansible-edge-gitops-kasten/_index.md | 1 + content/patterns/cockroachdb/_index.md | 1 + content/patterns/coco-pattern/_index.adoc | 4 ++-- content/patterns/connected-vehicle-architecture/_index.md | 1 + content/patterns/kong-gateway/_index.md | 1 + content/patterns/multicloud-gitops-Portworx/_index.md | 2 +- 6 files changed, 7 insertions(+), 3 deletions(-) diff --git a/content/patterns/ansible-edge-gitops-kasten/_index.md b/content/patterns/ansible-edge-gitops-kasten/_index.md index a87081b7f..e4983212b 100644 --- a/content/patterns/ansible-edge-gitops-kasten/_index.md +++ b/content/patterns/ansible-edge-gitops-kasten/_index.md @@ -1,5 +1,6 @@ --- title: OpenShift Virtualization Data Protection with Veeam Kasten +date: 2024-11-06 tier: sandbox summary: This pattern uses OpenShift Virtualization to simulate an edge environment for VMs, protected by Veeam Kasten. rh_products: diff --git a/content/patterns/cockroachdb/_index.md b/content/patterns/cockroachdb/_index.md index 64acd5c29..a8c26d508 100644 --- a/content/patterns/cockroachdb/_index.md +++ b/content/patterns/cockroachdb/_index.md @@ -1,5 +1,6 @@ --- title: Cockroach +date: 2022-12-14 tier: sandbox Summary: A multicloud pattern using cockroachdb and submariner, deployed via RHACM. rh_products: diff --git a/content/patterns/coco-pattern/_index.adoc b/content/patterns/coco-pattern/_index.adoc index c593ee0b4..9dc143c08 100644 --- a/content/patterns/coco-pattern/_index.adoc +++ b/content/patterns/coco-pattern/_index.adoc @@ -31,7 +31,7 @@ A core goal of confidential computing is to use this technology to isolate the w image::coco-pattern/isolation.png[Schematic describing the isolation of confidential contains from the hosting system] -This pattern uses https://docs.redhat.com/en/documentation/openshift_sandboxed_containers/1.7/html/user_guide/deploying-on-azure#deploying-cc_azure-cc[Red Hat OpenShift sandbox containers] to deploy and configure confidential containers on Microsoft Azure. +This pattern uses https://docs.redhat.com/en/documentation/openshift_sandboxed_containers/1.7/html/user_guide/deploying-on-azure#deploying-cc_azure-cc[Red Hat OpenShift sandbox containers] to deploy and configure confidential containers on Microsoft Azure. It deploys three copies of 'Hello OpenShift' to demonstrate some of the security boundaries that enforced with confidential containers. @@ -46,7 +46,7 @@ It deploys three copies of 'Hello OpenShift' to demonstrate some of the security **This pattern is a demonstration only and contains configuration that is not best practice** - The default configuration deploys everything in a single cluster for testing purposes. The https://www.ietf.org/archive/id/draft-ietf-rats-architecture-22.html[RATS] architecture mandates that the Key Broker Service (e.g. https://github.com/confidential-containers/trustee[Trustee]) is in a trusted security zone. -- The https://github.com/confidential-containers/trustee/tree/main/attestation-service[Attestation Service] has wide open security policies. +- The https://github.com/confidential-containers/trustee/tree/main/attestation-service[Attestation Service] has wide open security policies. == Future work diff --git a/content/patterns/connected-vehicle-architecture/_index.md b/content/patterns/connected-vehicle-architecture/_index.md index e0ef43792..18ad2d37a 100644 --- a/content/patterns/connected-vehicle-architecture/_index.md +++ b/content/patterns/connected-vehicle-architecture/_index.md @@ -1,5 +1,6 @@ --- title: Connected Vehicle Architecture +date: 2022-12-14 tier: sandbox Summary: A distributed cloud-native application that implements key aspects of a modern IoT architecture. rh_products: diff --git a/content/patterns/kong-gateway/_index.md b/content/patterns/kong-gateway/_index.md index bffd3b980..4c9bc4ac9 100644 --- a/content/patterns/kong-gateway/_index.md +++ b/content/patterns/kong-gateway/_index.md @@ -1,5 +1,6 @@ --- title: Kong +date: 2022-12-14 tier: sandbox Summary: A pattern for Kong Gateway Control Plane and Data Plane demo. rh_products: diff --git a/content/patterns/multicloud-gitops-Portworx/_index.md b/content/patterns/multicloud-gitops-Portworx/_index.md index 1a9adab6b..d138f59d8 100644 --- a/content/patterns/multicloud-gitops-Portworx/_index.md +++ b/content/patterns/multicloud-gitops-Portworx/_index.md @@ -1,6 +1,6 @@ --- title: Multicloud GitOps with Portworx Enterprise -date: 2023-18-05 +date: 2023-05-18 tier: sandbox summary: This pattern helps you develop and deploy applications on an open hybrid cloud in a stable, simple, and secure way. It includes persistent storage for stateful applications. rh_products: From 10b1a6953d4657e7dbcd0a90c1aa3c56717693dd Mon Sep 17 00:00:00 2001 From: Dan Macpherson Date: Fri, 22 Nov 2024 00:51:45 +1000 Subject: [PATCH 2/4] Creation of patterns browser v2 --- layouts/partials/menu-patterns-browser.html | 38 +--- layouts/partials/patterns-browser.html | 54 +++--- layouts/patterns/list.json.json | 43 ++++- static/js/patterns-browser-v2.js | 200 ++++++++++++++++++++ 4 files changed, 263 insertions(+), 72 deletions(-) create mode 100644 static/js/patterns-browser-v2.js diff --git a/layouts/partials/menu-patterns-browser.html b/layouts/partials/menu-patterns-browser.html index 50e89818f..2874d0576 100644 --- a/layouts/partials/menu-patterns-browser.html +++ b/layouts/partials/menu-patterns-browser.html @@ -20,19 +20,7 @@

What do these tiers mean?
-
- - - +
@@ -56,13 +44,7 @@

-
- {{ range $name, $taxonomy := .Site.Taxonomies.industries }} - - {{ end }} +
@@ -86,13 +68,7 @@

-
- {{ range $name, $taxonomy := .Site.Taxonomies.rh_products }} - - {{ end }} +
@@ -116,13 +92,7 @@

-
- {{ range $name, $taxonomy := .Site.Taxonomies.other_products }} - - {{ end }} +
diff --git a/layouts/partials/patterns-browser.html b/layouts/partials/patterns-browser.html index 7086203b3..3eaa124c7 100644 --- a/layouts/partials/patterns-browser.html +++ b/layouts/partials/patterns-browser.html @@ -25,45 +25,35 @@

{{ $patterns := where $.Pages "Section" "patterns"}}
- {{- len $patterns -}} of {{ len $patterns }} patterns displayed + + +
+
+
+
-

- +
+ {{ partial "footer.html" . }} diff --git a/layouts/patterns/list.json.json b/layouts/patterns/list.json.json index eb458b605..ceee62d0e 100644 --- a/layouts/patterns/list.json.json +++ b/layouts/patterns/list.json.json @@ -1,9 +1,40 @@ {{- if (eq .RelPermalink "/patterns/") }} + + {{/* Patterns */}} {{- $patterns := where $.Pages "Section" "patterns"}} - {{- $patterns_len := $patterns | len }} - {{- $pattern_list := slice }} - {{- range $index, $pattern := $patterns.ByTitle }} - {{- $pattern_list = $pattern_list | append (dict $pattern.Title $pattern.Params) }} - {{- end }} - {{- $pattern_list | jsonify }} + {{- $pattern_list := slice }} + {{- range $index, $pattern := $patterns.ByTitle }} + {{- $object := dict "Name" $pattern.LinkTitle "Link" $pattern.RelPermalink "Params" $pattern.Params -}} + {{- $pattern_list = $pattern_list | append $object -}} + {{- end }} + + {{/* Red Hat Products */}} + {{- $rh_products := .Site.Taxonomies.rh_products }} + {{- $rh_products_list := slice }} + {{- range $index, $rh_product := $rh_products.Alphabetical }} + {{- $rh_products_list = $rh_products_list | append (dict "Name" $rh_product.Name "LinkTitle" $rh_product.Page.LinkTitle) }} + {{- end }} + + {{/* Other Products */}} + {{- $other_products := .Site.Taxonomies.other_products }} + {{- $other_products_list := slice }} + {{- range $index, $other_product := $other_products.Alphabetical }} + {{- $other_products_list = $other_products_list | append (dict "Name" $other_product.Name "LinkTitle" $other_product.Page.LinkTitle) }} + {{- end }} + + {{/* Industries */}} + {{- $industries := .Site.Taxonomies.industries }} + {{- $industries_list := slice }} + {{- range $index, $industry := $industries.Alphabetical }} + {{- $industries_list = $industries_list | append (dict "Name" $industry.Name "LinkTitle" $industry.Page.LinkTitle) }} + {{- end }} + + {{/* Tiers */}} + {{- $tiers_list := slice -}} + {{- $tiers_list = $tiers_list | append (dict "Name" "maintained" "LinkTitle" "Maintained") }} + {{- $tiers_list = $tiers_list | append (dict "Name" "tested" "LinkTitle" "Tested") }} + {{- $tiers_list = $tiers_list | append (dict "Name" "sandbox" "LinkTitle" "Sandbox") }} + + {{- $filter_categories := dict "rh_products" $rh_products_list "other_products" $other_products_list "industries" $industries_list "tier" $tiers_list -}} + {{- dict "patterns" $pattern_list "filter_categories" $filter_categories | jsonify }} {{- end }} diff --git a/static/js/patterns-browser-v2.js b/static/js/patterns-browser-v2.js new file mode 100644 index 000000000..de795b97e --- /dev/null +++ b/static/js/patterns-browser-v2.js @@ -0,0 +1,200 @@ +async function getData() { + const url = "/patterns/index.json"; + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + const json = await response.json(); + return json; + } catch (error) { + console.error(error.message); + } +} + +function sleep (time) { + return new Promise((resolve) => setTimeout(resolve, time)); +} + +function cleanString(string) { + return string.replace(/ /g, "-") +} + +function capitalizeFirstLetter(string) { + return string[0].toUpperCase() + string.slice(1); +} + +function sortAtoZ(a, b){ + const nameA = a.Name.toUpperCase() + const nameB = b.Name.toUpperCase() + if (nameA < nameB) { + return -1; + } + if (nameA > nameB) { + return 1; + } + return 0; +} + +function sortDate(a, b){ + if (a.Params.date != undefined) { + var dateA = new Date(a.Params.date) + } else { + var dateA = new Date("2000-01-01T00:00:00Z"); + }; + if (b.Params.date != undefined) { + var dateB = new Date(b.Params.date) + } else { + var dateB = new Date("2000-01-01T00:00:00Z"); + }; + if (dateA < dateB) { + return -1; + } + if (dateA > dateB) { + return 1; + } + return 0; +} + +function renderSpinner() { + return '
' + + '
' + + '' + + '' + + '' + + '
' + + '
'; +} + +function renderFilterItem(type, name, linkTitle) { + var filterItem = ''; + return filterItem; +} + +function renderFilter(elementId, filterType, filterData) { + for (item = 0; item < filterData.length; item++) { + const element = document.getElementById(elementId); + element.innerHTML += renderFilterItem(filterType, filterData[item].Name, filterData[item].LinkTitle); + }; +} + +function renderLabel(tier) { + if (tier != undefined) { + if (tier == "maintained") { var color = "green" } + else if (tier == "tested") { var color = "blue" } + else if (tier == "sandbox") { var color = "orange" }; + var renderedLabel = '' + + '' + + '' + capitalizeFirstLetter(tier) + '' + + capitalizeFirstLetter(tier) + + '' + + ''; + return renderedLabel; + } else { + return ""; + }; +} + +function renderCard(pattern) { + var renderCard = ''; + return renderCard; +} + +function renderFilteredCards(patterns, filter_categories) { + const element = document.getElementById("patternCards"); + const patternLoaderSpinner = document.getElementById("patternLoaderSpinner"); + element.innerHTML = ""; + patternLoaderSpinner.innerHTML = renderSpinner(); + var filter = new Object(); + var filteredPatterns = []; + var sortValue = document.getElementById("select-pattern-sort"); + for (const [category, terms] of Object.entries(filter_categories)) { + for (item = 0; item < terms.length; item++) { + var checkboxId = category + ":" + cleanString(terms[item].Name); + var checkbox = document.getElementById(checkboxId); + if (checkbox.checked) { + if (filter[category] == undefined) { filter[category] = [] }; + filter[category].push(terms[item].LinkTitle); + }; + }; + }; + for (item = 0; item < patterns.length; item++) { + var checksPassed = new Object(); + for (const [category, terms] of Object.entries(filter)) { + if (typeof(patterns[item].Params[category]) == "string") { + for (termId = 0; termId < terms.length; termId++) { + if (terms[termId].toLowerCase() == patterns[item].Params[category].toLowerCase()) { + checksPassed[category] = true; + } + } + } else if (typeof(patterns[item].Params[category]) == "object" && patterns[item].Params[category] != null) { + var patternTerms = patterns[item].Params[category].map(v => v.toLowerCase()); + var filterTerms = terms.map(v => v.toLowerCase()); + if(filterTerms.every(r => patternTerms.includes(r))) { + checksPassed[category] = true; + }; + }; + }; + var patternPassed = true; + for (const [category, terms] of Object.entries(filter)) { + if (checksPassed[category] != true) { + patternPassed = false; + }; + }; + if (patternPassed == true) { filteredPatterns.push(patterns[item]); }; + }; + if (sortValue.value == "atoz") { + filteredPatterns.sort(sortAtoZ); + } else if (sortValue.value == "ztoa") { + filteredPatterns.sort(sortAtoZ); + filteredPatterns.reverse(); + } else if (sortValue.value == "oldest") { + filteredPatterns.sort(sortDate); + } else if (sortValue.value == "newest") { + filteredPatterns.sort(sortDate); + filteredPatterns.reverse(); + }; + patternLoaderSpinner.innerHTML = ""; + for (item = 0; item < filteredPatterns.length; item++) { + const element = document.getElementById("patternCards"); + element.innerHTML += renderCard(filteredPatterns[item]); + }; + var totalPatternsCount = patterns.length + var filteredPatternsCount = filteredPatterns.length + const counter = document.getElementById("pattern-counter"); + counter.innerHTML = filteredPatternsCount + " of " + totalPatternsCount + " patterns displayed"; +} + +function filterSelection(filter) { + // Declaring variables + const patternsData = getData() + patternsData.then(output => { + renderFilteredCards(output.patterns, output.filter_categories) + }); +} + +const patternsData = getData() +patternsData.then(output => { + renderFilter("TiersItems", "tier", output.filter_categories.tier); + renderFilter("IndustriesItems", "industries", output.filter_categories.industries); + renderFilter("RhProductsItems", "rh_products", output.filter_categories.rh_products); + renderFilter("OtherProductsItems", "other_products", output.filter_categories.other_products); + renderFilteredCards(output.patterns, output.filter_categories) +}); From aafbf894c15556e85cd70430e8cd0daad51c84e2 Mon Sep 17 00:00:00 2001 From: Dan Macpherson Date: Fri, 22 Nov 2024 23:41:22 +1000 Subject: [PATCH 3/4] Adding AND and OR operators to certain filters --- layouts/patterns/list.json.json | 10 +++++- static/js/patterns-browser-v2.js | 62 ++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/layouts/patterns/list.json.json b/layouts/patterns/list.json.json index ceee62d0e..c0a538254 100644 --- a/layouts/patterns/list.json.json +++ b/layouts/patterns/list.json.json @@ -14,6 +14,8 @@ {{- range $index, $rh_product := $rh_products.Alphabetical }} {{- $rh_products_list = $rh_products_list | append (dict "Name" $rh_product.Name "LinkTitle" $rh_product.Page.LinkTitle) }} {{- end }} + {{- $rh_products_filter_types := slice "AND" "OR" }} + {{- $rh_products_dict := dict "filter_list" $rh_products_list "filter_types" $rh_products_filter_types }} {{/* Other Products */}} {{- $other_products := .Site.Taxonomies.other_products }} @@ -21,6 +23,8 @@ {{- range $index, $other_product := $other_products.Alphabetical }} {{- $other_products_list = $other_products_list | append (dict "Name" $other_product.Name "LinkTitle" $other_product.Page.LinkTitle) }} {{- end }} + {{- $other_products_filter_types := slice "AND" "OR" }} + {{- $other_products_dict := dict "filter_list" $other_products_list "filter_types" $other_products_filter_types }} {{/* Industries */}} {{- $industries := .Site.Taxonomies.industries }} @@ -28,13 +32,17 @@ {{- range $index, $industry := $industries.Alphabetical }} {{- $industries_list = $industries_list | append (dict "Name" $industry.Name "LinkTitle" $industry.Page.LinkTitle) }} {{- end }} + {{- $industries_filter_types := slice "AND" "OR" }} + {{- $industries_dict := dict "filter_list" $industries_list "filter_types" $industries_filter_types }} {{/* Tiers */}} {{- $tiers_list := slice -}} {{- $tiers_list = $tiers_list | append (dict "Name" "maintained" "LinkTitle" "Maintained") }} {{- $tiers_list = $tiers_list | append (dict "Name" "tested" "LinkTitle" "Tested") }} {{- $tiers_list = $tiers_list | append (dict "Name" "sandbox" "LinkTitle" "Sandbox") }} + {{- $tiers_filter_types := slice "OR" }} + {{- $tiers_dict := dict "filter_list" $tiers_list "filter_types" $tiers_filter_types }} - {{- $filter_categories := dict "rh_products" $rh_products_list "other_products" $other_products_list "industries" $industries_list "tier" $tiers_list -}} + {{- $filter_categories := dict "rh_products" $rh_products_dict "other_products" $other_products_dict "industries" $industries_dict "tier" $tiers_dict -}} {{- dict "patterns" $pattern_list "filter_categories" $filter_categories | jsonify }} {{- end }} diff --git a/static/js/patterns-browser-v2.js b/static/js/patterns-browser-v2.js index de795b97e..c40b8c323 100644 --- a/static/js/patterns-browser-v2.js +++ b/static/js/patterns-browser-v2.js @@ -74,11 +74,32 @@ function renderFilterItem(type, name, linkTitle) { return filterItem; } +function renderFilterButtons(filterButtonTypes, name) { + if (filterButtonTypes.length > 1) { + var filterButtons = '
'; + var firstSelected = ""; + for (item = 0; item < filterButtonTypes.length; item++) { + firstSelected = ""; + console.log(item) + if (item == 0) { firstSelected = " pf-m-selected"; } + filterButtons += '
' + + '' + + '
' + } + filterButtons += '
' + return filterButtons; + } + return ""; +} + function renderFilter(elementId, filterType, filterData) { - for (item = 0; item < filterData.length; item++) { - const element = document.getElementById(elementId); - element.innerHTML += renderFilterItem(filterType, filterData[item].Name, filterData[item].LinkTitle); + const element = document.getElementById(elementId); + for (item = 0; item < filterData.filter_list.length; item++) { + element.innerHTML += renderFilterItem(filterType, filterData.filter_list[item].Name, filterData.filter_list[item].LinkTitle); }; + element.innerHTML += renderFilterButtons(filterData.filter_types, filterType); } function renderLabel(tier) { @@ -124,14 +145,23 @@ function renderFilteredCards(patterns, filter_categories) { patternLoaderSpinner.innerHTML = renderSpinner(); var filter = new Object(); var filteredPatterns = []; + var filterType = new Object(); var sortValue = document.getElementById("select-pattern-sort"); for (const [category, terms] of Object.entries(filter_categories)) { - for (item = 0; item < terms.length; item++) { - var checkboxId = category + ":" + cleanString(terms[item].Name); + for (item = 0; item < terms.filter_list.length; item++) { + var checkboxId = category + ":" + cleanString(terms.filter_list[item].Name); var checkbox = document.getElementById(checkboxId); if (checkbox.checked) { if (filter[category] == undefined) { filter[category] = [] }; - filter[category].push(terms[item].LinkTitle); + filter[category].push(terms.filter_list[item].LinkTitle); + }; + }; + for (item = 0; item < terms.filter_types.length; item++) { + var filterTypeID = category + "_button:" + terms.filter_types[item].toLowerCase(); + var filterTypeItem = document.getElementById(filterTypeID); + console.log(filterTypeItem) + if (filterTypeItem != null && filterTypeItem.classList.contains("pf-m-selected")) { + filterType[category] = terms.filter_types[item].toLowerCase(); }; }; }; @@ -147,7 +177,9 @@ function renderFilteredCards(patterns, filter_categories) { } else if (typeof(patterns[item].Params[category]) == "object" && patterns[item].Params[category] != null) { var patternTerms = patterns[item].Params[category].map(v => v.toLowerCase()); var filterTerms = terms.map(v => v.toLowerCase()); - if(filterTerms.every(r => patternTerms.includes(r))) { + if(filterType[category] == "and" && filterTerms.every(r => patternTerms.includes(r))) { + checksPassed[category] = true; + } else if (filterType[category] == "or" && filterTerms.some(r => patternTerms.includes(r))) { checksPassed[category] = true; }; }; @@ -190,6 +222,22 @@ function filterSelection(filter) { }); } +function changeFilterType(id) { + var filterType = id.split(":"); + var filter_category = filterType[0].replace('_button',''); + const patternsData = getData(); + patternsData.then(output => { + for (item = 0; item < output.filter_categories[filter_category].filter_types.length; item++) { + var unselectId = filter_category + "_button:" + output.filter_categories[filter_category].filter_types[item].toLowerCase() + const unselectButton = document.getElementById(unselectId); + unselectButton.classList.remove("pf-m-selected"); + } + const selectButton = document.getElementById(id); + selectButton.classList.add("pf-m-selected"); + renderFilteredCards(output.patterns, output.filter_categories) + }); +} + const patternsData = getData() patternsData.then(output => { renderFilter("TiersItems", "tier", output.filter_categories.tier); From d8cd93f99f84ca8274eef4dd04cde4a9b7a78ae2 Mon Sep 17 00:00:00 2001 From: Dan Macpherson Date: Mon, 25 Nov 2024 21:41:40 +1000 Subject: [PATCH 4/4] Minor refactoring and cleanup --- layouts/patterns/list.json.json | 6 +- static/js/patterns-browser-v2.js | 259 +++++++++++++++++++++---------- 2 files changed, 177 insertions(+), 88 deletions(-) diff --git a/layouts/patterns/list.json.json b/layouts/patterns/list.json.json index c0a538254..a78835d9d 100644 --- a/layouts/patterns/list.json.json +++ b/layouts/patterns/list.json.json @@ -37,9 +37,9 @@ {{/* Tiers */}} {{- $tiers_list := slice -}} - {{- $tiers_list = $tiers_list | append (dict "Name" "maintained" "LinkTitle" "Maintained") }} - {{- $tiers_list = $tiers_list | append (dict "Name" "tested" "LinkTitle" "Tested") }} - {{- $tiers_list = $tiers_list | append (dict "Name" "sandbox" "LinkTitle" "Sandbox") }} + {{- $tiers_list = $tiers_list | append (dict "Name" "maintained" "LinkTitle" "Maintained" "color" "green") }} + {{- $tiers_list = $tiers_list | append (dict "Name" "tested" "LinkTitle" "Tested" "color" "blue") }} + {{- $tiers_list = $tiers_list | append (dict "Name" "sandbox" "LinkTitle" "Sandbox" "color" "oragne") }} {{- $tiers_filter_types := slice "OR" }} {{- $tiers_dict := dict "filter_list" $tiers_list "filter_types" $tiers_filter_types }} diff --git a/static/js/patterns-browser-v2.js b/static/js/patterns-browser-v2.js index c40b8c323..0b1cd3c77 100644 --- a/static/js/patterns-browser-v2.js +++ b/static/js/patterns-browser-v2.js @@ -1,4 +1,101 @@ +class Filter { + // Class for constructing the filter, which is based on the provided + // filter_categories dictionary checked against options from the filters + // that the user selects. + + constructor(filter_categories) { + this.filter_categories = filter_categories; + this.filter_values = this.get_filter_values(); + this.filter_types = this.get_filter_types(); + } + + get_filter_values() { + // Return the options from the filters that have been filtered + var filterValuesResult = new Object(); + for (const [category, terms] of Object.entries(this.filter_categories)) { + for (item = 0; item < terms.filter_list.length; item++) { + var checkboxId = category + ":" + cleanString(terms.filter_list[item].Name); + var checkbox = document.getElementById(checkboxId); + if (checkbox.checked) { + if (filterValuesResult[category] == undefined) { filterValuesResult[category] = [] }; + filterValuesResult[category].push(terms.filter_list[item].LinkTitle); + } + } + } + return filterValuesResult; + } + + get_filter_types() { + // Return the operator selected from the filter + var filterTypesResult = new Object(); + for (const [category, terms] of Object.entries(this.filter_categories)) { + for (item = 0; item < terms.filter_types.length; item++) { + var filterTypeID = category + "_button:" + terms.filter_types[item].toLowerCase(); + var filterTypeItem = document.getElementById(filterTypeID); + if (filterTypeItem != null && filterTypeItem.classList.contains("pf-m-selected")) { + filterTypesResult[category] = terms.filter_types[item].toLowerCase(); + } + } + } + return filterTypesResult; + } +} + +class FilteredPatterns { + // Class for filtered the patterns based on a Filter object. Also sorts the + // patterns based on sort_value. + + constructor(patterns_filter, patterns, sort_value) { + this.patterns_filter = patterns_filter; + this.patterns = patterns; + this.sort_value = sort_value; + this.filter_patterns = this.get_filter_patterns(); + } + + get_filter_patterns() { + // Create a filtered list of patterns + var filteredPatterns = []; + for (item = 0; item < this.patterns.length; item++) { + var checksPassed = new Object(); + for (const [category, terms] of Object.entries(this.patterns_filter.filter_values)) { + if (typeof(this.patterns[item].Params[category]) == "string") { + checksPassed[category] = checkCategoryString(terms, this.patterns[item].Params[category]) + } else if (typeof(this.patterns[item].Params[category]) == "object" && this.patterns[item].Params[category] != null) { + var patternTerms = this.patterns[item].Params[category].map(v => v.toLowerCase()); + var filterTerms = terms.map(v => v.toLowerCase()); + checksPassed[category] = checkCategoryObject(patternTerms, filterTerms, this.patterns_filter.filter_types[category]); + }; + }; + var patternPassed = true; + for (const [category, terms] of Object.entries(this.patterns_filter.filter_values)) { + if (checksPassed[category] != true) { + patternPassed = false; + }; + }; + if (patternPassed == true) { filteredPatterns.push(this.patterns[item]); }; + }; + this.sort_filtered_patterns(filteredPatterns) + return filteredPatterns + } + + sort_filtered_patterns(filteredPatterns) { + // Sort the filtered list of patterns + if (this.sort_value.value == "atoz") { + filteredPatterns.sort(sortAtoZ); + } else if (this.sort_value.value == "ztoa") { + filteredPatterns.sort(sortAtoZ); + filteredPatterns.reverse(); + } else if (this.sort_value.value == "oldest") { + filteredPatterns.sort(sortDate); + } else if (this.sort_value.value == "newest") { + filteredPatterns.sort(sortDate); + filteredPatterns.reverse(); + }; + } +} + async function getData() { + // Load the JSON file containing all patterns data const url = "/patterns/index.json"; try { const response = await fetch(url); @@ -12,19 +109,18 @@ async function getData() { } } -function sleep (time) { - return new Promise((resolve) => setTimeout(resolve, time)); -} - function cleanString(string) { + // Provide a string that can be used as a HTML id i.e. no spaces. return string.replace(/ /g, "-") } function capitalizeFirstLetter(string) { - return string[0].toUpperCase() + string.slice(1); + // Capitalize the first letter of a string + return string[0].toUpperCase() + string.slice(1); } function sortAtoZ(a, b){ + // Sort alphabetically const nameA = a.Name.toUpperCase() const nameB = b.Name.toUpperCase() if (nameA < nameB) { @@ -37,14 +133,17 @@ function sortAtoZ(a, b){ } function sortDate(a, b){ + // Sort by date if (a.Params.date != undefined) { var dateA = new Date(a.Params.date) } else { + // Set a default date if no date exists var dateA = new Date("2000-01-01T00:00:00Z"); }; if (b.Params.date != undefined) { var dateB = new Date(b.Params.date) } else { + // Set a default date if no date exists var dateB = new Date("2000-01-01T00:00:00Z"); }; if (dateA < dateB) { @@ -56,7 +155,32 @@ function sortDate(a, b){ return 0; } +function checkCategoryString(terms, category) { + // If the category we're checking is a string type, that means it can have + // only one value. Therefore you can only use an OR operator on the filter. + var checksPassed = false; + for (termId = 0; termId < terms.length; termId++) { + if (terms[termId].toLowerCase() == category.toLowerCase()) { + checksPassed = true; + } + } + return checksPassed; +} + +function checkCategoryObject(patternTerms, filterTerms, filter_type) { + // If the category we're checking is a object type, that means it can have + // more than one value. Therefore you can use either AND or OR on the filter. + var checksPassed = false; + if(filter_type == "and" && filterTerms.every(r => patternTerms.includes(r))) { + checksPassed = true; + } else if (filter_type == "or" && filterTerms.some(r => patternTerms.includes(r))) { + checksPassed = true; + }; + return checksPassed; +} + function renderSpinner() { + // HTML for the loading spinner return '
' + '
' + '' + @@ -67,34 +191,36 @@ function renderSpinner() { } function renderFilterItem(type, name, linkTitle) { - var filterItem = '' + linkTitle + ''+ ''; - return filterItem; + return filterItemHtml; } function renderFilterButtons(filterButtonTypes, name) { + // HTML for the AND / OR operator buttons if (filterButtonTypes.length > 1) { - var filterButtons = '
'; + var filterButtonsHtml = '
'; var firstSelected = ""; for (item = 0; item < filterButtonTypes.length; item++) { firstSelected = ""; - console.log(item) if (item == 0) { firstSelected = " pf-m-selected"; } - filterButtons += '
' + + filterButtonsHtml += '
' + '' + '
' } - filterButtons += '
' - return filterButtons; + filterButtonsHtml += '
' + return filterButtonsHtml; } return ""; } function renderFilter(elementId, filterType, filterData) { + // HTML to render all filters const element = document.getElementById(elementId); for (item = 0; item < filterData.filter_list.length; item++) { element.innerHTML += renderFilterItem(filterType, filterData.filter_list[item].Name, filterData.filter_list[item].LinkTitle); @@ -102,25 +228,25 @@ function renderFilter(elementId, filterType, filterData) { element.innerHTML += renderFilterButtons(filterData.filter_types, filterType); } -function renderLabel(tier) { +function renderLabel(tier, tier_categories) { + // HTML to render the pattern tier label if (tier != undefined) { - if (tier == "maintained") { var color = "green" } - else if (tier == "tested") { var color = "blue" } - else if (tier == "sandbox") { var color = "orange" }; - var renderedLabel = '' + + var color= tier_categories.filter_list.find(item => item.Name === tier); + var renderedLabelHtml = '' + '' + '' + capitalizeFirstLetter(tier) + '' + capitalizeFirstLetter(tier) + '' + ''; - return renderedLabel; + return renderedLabelHtml; } else { return ""; }; } -function renderCard(pattern) { - var renderCard = '