diff --git a/_includes/scripts.html b/_includes/scripts.html
index ccd8c9ccfb0..32fe28aae30 100644
--- a/_includes/scripts.html
+++ b/_includes/scripts.html
@@ -77,6 +77,16 @@
<% }) %>
+
<% let count=0 %> <% _.each(projects, function(project) { %> <%
diff --git a/javascripts/main.js b/javascripts/main.js
index 77a2401e044..c4258e2ab7b 100644
--- a/javascripts/main.js
+++ b/javascripts/main.js
@@ -64,12 +64,19 @@ define([
return `about ${Math.round(elapsed / msPerYear)} years ago`;
}
- const renderProjects = function (projectService, tags, names, labels, date) {
+ const renderProjects = function (
+ projectService,
+ tags,
+ names,
+ labels,
+ date,
+ platforms
+ ) {
const allTags = projectService.getTags();
projectsPanel.html(
compiledtemplateFn({
- projects: projectService.get(tags, names, labels, date),
+ projects: projectService.get(tags, names, labels, date, platforms),
relativeTime,
tags: allTags,
popularTags: projectService.getPopularTags(6),
@@ -78,6 +85,9 @@ define([
selectedNames: names,
labels: projectService.getLabels(),
selectedLabels: labels,
+ platforms: projectService.getPlatforms(),
+ popularPlatforms: projectService.getPopularPlatforms(4),
+ selectedPlatforms: platforms,
})
);
date = date || 'invalid';
@@ -172,6 +182,40 @@ define([
}
});
});
+
+ projectsPanel.find('ul.popular-platforms li a').each((i, elem) => {
+ // add selected class on load
+ let selPlatforms = getParameterByName('platforms') || '';
+ if (selPlatforms) {
+ selPlatforms = selPlatforms.split(',');
+ for (let i = 0; i < selPlatforms.length; i++) {
+ $(`a:contains(${selPlatforms[i]})`).addClass('selected');
+ }
+ }
+
+ $(elem).on('click', function (e) {
+ e.preventDefault();
+
+ $(this).toggleClass('selected');
+
+ let curPlatform = $(this).text().split('(')[0].trim() || '';
+ let selPlatforms = getParameterByName('platforms') || '';
+ if (selPlatforms.indexOf(curPlatform) === -1) {
+ selPlatforms += selPlatforms ? `,${curPlatform}` : curPlatform;
+ } else {
+ selPlatforms = selPlatforms
+ .split(',')
+ .filter((platform) => platform !== curPlatform)
+ .join(',');
+ }
+
+ location.href = updateQueryStringParameter(
+ getFilterUrl(),
+ 'platforms',
+ encodeURIComponent(selPlatforms)
+ );
+ });
+ });
};
/*
@@ -228,6 +272,7 @@ define([
$('#back2Top').fadeOut();
}
});
+
$(document).ready(() => {
$('#back2Top').click((event) => {
event.preventDefault();
@@ -335,8 +380,9 @@ define([
const labels = prepareForHTML(getParameterByName('labels'));
const names = prepareForHTML(getParameterByName('names'));
const tags = prepareForHTML(getParameterByName('tags'));
+ const platforms = prepareForHTML(getParameterByName('platforms'));
const date = getParameterByName('date');
- renderProjects(projectsSvc, tags, names, labels, date);
+ renderProjects(projectsSvc, tags, names, labels, date, platforms);
});
this.get('/', () => {
diff --git a/javascripts/projectsService.js b/javascripts/projectsService.js
index 41e43cdce35..448b22cae1a 100644
--- a/javascripts/projectsService.js
+++ b/javascripts/projectsService.js
@@ -196,6 +196,46 @@ define(['underscore', 'tag-builder', 'project-ordering'], (
return _.flatten(results, (arr1, arr2) => arr1.append(arr2));
};
+ /*
+ * The function here is used for front end filtering when given
+ * selecting certain projects. It ensures that only the selected projects
+ * are returned. If none of the platforms was added to the filter.
+ * Then it fallsback to show all the projects.
+ * @param Array projects : An array having all the Projects in _data
+ * @param Array projectPlatformsSorted : This is another array showing all the
+ * projects in a sorted order
+ * @param Array platforms : This is an array with the given platform filters.
+ */
+ const applyPlatformsFilter = function (
+ projects,
+ projectPlatformsSorted,
+ platforms
+ ) {
+ if (typeof platforms === 'string') {
+ platforms = platforms.split(',');
+ }
+
+ platforms = _.map(
+ platforms,
+ (entry) => entry && entry.replace(/^\s+|\s+$/g, '')
+ );
+
+ if (!platforms || !platforms.length || platforms[0] == '') {
+ return projects;
+ }
+
+ // find all projects with the given platforms
+ results = _.map(platforms, (hostname) => {
+ return _.filter(projects, (project) => {
+ const curHostname = new URL(String(project.upforgrabs.link)).hostname;
+ return curHostname === hostname;
+ });
+ });
+
+ // the above statements returns n arrays in an array, which we flatten here and return then
+ return _.flatten(results, (arr1, arr2) => arr1.append(arr2));
+ };
+
const extractTags = function (projectsData) {
const tagBuilder = new TagBuilder();
_.each(projectsData, (entry) => {
@@ -218,6 +258,7 @@ define(['underscore', 'tag-builder', 'project-ordering'], (
const tagsMap = {};
const namesMap = {};
const labelsMap = {};
+ const platformsMap = {};
const projects = orderAllProjects(_projectsData.projects, (length) =>
_.shuffle(_.range(length))
@@ -228,7 +269,7 @@ define(['underscore', 'tag-builder', 'project-ordering'], (
});
_.each(_projectsData.projects, (project) => {
- if (project.name.toLowerCase) {
+ if (project.name) {
namesMap[project.name.toLowerCase()] = project;
}
});
@@ -237,7 +278,20 @@ define(['underscore', 'tag-builder', 'project-ordering'], (
labelsMap[project.upforgrabs.name.toLowerCase()] = project.upforgrabs;
});
- this.get = function (tags, names, labels, date) {
+ _.each(_projectsData.projects, (project) => {
+ const platform = new URL(project.upforgrabs.link);
+
+ if (platform.hostname in platformsMap) {
+ platformsMap[platform.hostname].frequency += 1;
+ } else {
+ platformsMap[platform.hostname] = {
+ hostname: platform.hostname,
+ frequency: 1,
+ };
+ }
+ });
+
+ this.get = function (tags, names, labels, date, platforms) {
let filteredProjects = projects;
if (names && names.length) {
filteredProjects = applyNamesFilter(
@@ -263,6 +317,13 @@ define(['underscore', 'tag-builder', 'project-ordering'], (
tags
);
}
+ if (platforms && platforms.length) {
+ filteredProjects = applyPlatformsFilter(
+ filteredProjects,
+ this.getPlatforms(),
+ platforms
+ );
+ }
return filteredProjects;
};
@@ -270,6 +331,10 @@ define(['underscore', 'tag-builder', 'project-ordering'], (
return _.sortBy(tagsMap, (entry) => entry.name.toLowerCase());
};
+ this.getPlatforms = function () {
+ return _.sortBy(platformsMap, (entry) => entry.hostname.toLowerCase());
+ };
+
this.getNames = function () {
return _.sortBy(namesMap, (entry) => entry.name.toLowerCase());
};
@@ -281,6 +346,16 @@ define(['underscore', 'tag-builder', 'project-ordering'], (
this.getPopularTags = function (popularTagCount) {
return _.take(_.values(tagsMap), popularTagCount || 10);
};
+
+ this.getPopularPlatforms = function (popularPlatformCount) {
+ return _.take(
+ _.sortBy(
+ _.values(platformsMap),
+ (platform) => platform.frequency
+ ).reverse(),
+ popularPlatformCount || 6
+ );
+ };
};
return ProjectsService;
diff --git a/package-lock.json b/package-lock.json
index f4780eb8c34..052aa9d25c1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3690,6 +3690,7 @@
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"@jest/core": "^29.7.0",
"@jest/types": "^29.6.3",
diff --git a/stylesheets/stylesheet.css b/stylesheets/stylesheet.css
index b5d4bea7925..c30545e29e9 100644
--- a/stylesheets/stylesheet.css
+++ b/stylesheets/stylesheet.css
@@ -883,20 +883,23 @@ form {
width: 16px;
}
-.popular-tags,
+.popular-tags,
+.popular-platforms,
.tags {
display: flex;
flex-wrap: wrap;
gap: 1em 0.5em;
}
-ul.popular-tags li,
+ul.popular-tags li,
+ul.popular-platforms li,
ul.tags li {
list-style: none;
padding-left: 0;
}
.popular-tags a,
+.popular-platforms a,
.tags a {
display: inline-flex;
align-content: center;
@@ -916,6 +919,7 @@ ul.tags li {
.popular-tags a:hover,
.popular-tags a:focus,
.popular-tags a:focus-within,
+.popular-platforms a:hover,
.tags a:hover,
.tags a:focus,
.tags a:focus-within {
@@ -924,12 +928,22 @@ ul.tags li {
color: var(--databox-text);
}
-.popular-tags a span.popular-tags__frequency {
+.popular-tags a span.popular-tags__frequency,
+.popular-platforms a span.popular-platforms__frequency {
margin-left: 0.45em;
font-size: 10px;
color: var(--body-color);
}
+.popular-platforms a.selected {
+ background-color: var(--databox-text);
+ color: var(--abs);
+}
+
+.popular-platforms a.selected span.popular-platforms__frequency {
+ color: var(--abs);
+}
+
@media (max-width: 768px) {
body {
padding: 0 0;