added filtering for categories, improved collapsing div look, tweaked filters, and other stuff

This commit is contained in:
Dave Lane 2024-09-24 17:09:24 +12:00
parent 0d9762206f
commit ef93b55b19
5 changed files with 352 additions and 260 deletions

View file

@ -18,9 +18,10 @@
//
// digest and return useful info from the Webservices JSON feed
function processData(webservices) {
let instances = 0;
let current = [];
let future = [];
let stats = { 'instances': 0, 'technologies': 0, 'licenses': 0,'weak': 0, 'copyleft': 0 };
let all = []; // all services
let current = []; // those with active instances
let future = []; // those that are planned
// properties of technologies
let category_list = [];
let analogue_list = [];
@ -33,6 +34,7 @@
// actual objects
let hosts = [];
let affiliates = [];
//
// just a trivial reassignment
//let hosts = webservices.hosts;
@ -40,7 +42,10 @@
// pull out relevant info in useful chunks.
for (let key in webservices.technologies) {
let tech = webservices.technologies[key];
tech['name'] = key;
console.log('tech '+key+' ('+tech.categories.length+'):', tech.categories);
if (tech.hasOwnProperty('categories') && tech.categories.constructor === Array) {
//console.log('tech '+key+' ('+tech.categories.length+'):', tech.categories);
tech.categories.forEach(function (category, index) {
if (category_list.hasOwnProperty(category)) category_list[category]++;
else category_list[category] = 1;
@ -57,13 +62,11 @@
if (license_list.hasOwnProperty(license)) license_list[license]++;
else license_list[license] = 1;
}
if (hasInstances(tech)) {
tech['name'] = key;
current.push(tech);
//console.log(tech.name + ': ' + tech.instances.length + ' instances...');
tech.instances.forEach(function (instance, i) {
instances++;
stats.instances++;
if (instance.hasOwnProperty('status')) {
let tag = instance.status;
if (status_list.hasOwnProperty(tag)) status_list[tag]++;
@ -84,6 +87,7 @@
} else {
future[key] = tech;
}
all[key] = tech;
}
for (let key in webservices.hosts) {
//console.log('key: ', key);
@ -102,8 +106,15 @@
affiliates[key] = affiliate;
}
// assign stats based on gathered data...
stats.current = current.length;
stats.future = future.length;
console.log('license_list: ', license_list);
stats.licenses = license_list.length;
return {
total_instances: instances,
stats: stats,
all_services: all,
active_services: current,
candidate_services: future,
tech_lists: {
@ -121,18 +132,7 @@
};
}
// console.log(technologies);
// return true if a tech object includes valid instances
function hasInstances(tech) {
if (
tech.hasOwnProperty('instances') &&
tech.instances.constructor === Array &&
tech.instances.length
) return true;
return false;
}
// return an object with ob's keys ordered alphabetically, with an idfrom an object
// return an object with ob's keys ordered alphabetically, with an id from an object
function getSortedFilter(ob) {
let keys = [];
let i = 0;
@ -150,6 +150,17 @@
return key_array;
}
// return true if a tech object includes valid instances
function hasInstances(tech) {
if (
tech.hasOwnProperty('instances') &&
tech.instances.constructor === Array &&
tech.instances.length
) return true;
return false;
}
// get intersection: ref https://bobbyhadz.com/blog/javascript-get-intersection-of-two-arrays
function getIntersection(a, b) {
const set1 = new Set(a);
@ -160,22 +171,6 @@
return intersection;
}
// combine an array of terms into a sentence with proper Oxford commas, dealing with the special cases
// of one or two elements.
function toOxfordCommaString(arr) {
if (arr.length == 1) return arr;
if (arr.length == 2) return arr.join(' and ');
else {
var last = arr.pop();
return arr.join(', ') + ', and ' + last;
}
}
// return a singular or plural term depending on the value of 'count'
function pluraliser(singular, plural, count) {
if (count > 1) return plural;
return singular;
}
// assign colours from a set of differentiated colours to a list of tags...
// this one is for 'hosts'
@ -240,7 +235,7 @@
const set2 = new Set(b);
const intersection = [...set1].filter((element) => set2.has(element));
console.log('intersection = ', intersection);
//console.log('intersection = ', intersection);
return intersection;
}
@ -255,13 +250,14 @@
// filter technologies based on a list of Categories
function filterTechnologiesByCategoryList(technologies, list) {
console.log('looking for tech in categories: ', list);
//console.log('looking for tech in categories: ', list);
const included = [];
technologies.forEach(function (tech) {
if (hasInstances(tech)) {
const intersection = inCommon(tech.categories, list);
//console.log('categories: ', tech.categories);
//console.log('list: ', list);
console.log('for tech: ', tech.name);
console.log('categories: ', tech.categories);
console.log('list: ', list);
console.log('intersection: ', intersection);
if (intersection && intersection.constructor === Array) {
//console.log('found intersection!', intersection.constructor);
@ -323,8 +319,9 @@
// reactive stuff...
$: {
console.log('about to filterTechnologiesByCategoryList');
//console.log('about to filterTechnologiesByCategoryList');
filteredTechnologies = filterTechnologiesByCategoryList(results.active_services, flattenFilter($categoryFilter));
//filteredTechnologies = filterTechnologiesByCategoryList(results.all_services, flattenFilter($categoryFilter));
const technologies = sortTechnologies(filteredTechnologies);
}
@ -336,61 +333,75 @@
</script>
<div class="webservices">
<h1>Web Services</h1>
<div class="header">
<div class="introduction">
<h1>Web Services</h1>
<p>This sites exists to provide an 'always-up-to-date', in-depth description of the <a
href="https://tech.oeru.org/foss-libresoftware-its-about-clarity-and-values"
title="What do I mean by 'libre' software?">libre software</a> web services <a href="https://davelane.nz" title="Who is this Dave Lane character?">I</a> have set
up and maintain.
</p>
<div class="introduction">
<p>This sites exists to provide an 'always-up-to-date', in-depth description of the <a
href="https://tech.oeru.org/foss-libresoftware-its-about-clarity-and-values"
title="What do I mean by 'libre' software?">libre software</a> web services <a href="https://davelane.nz" title="Who is this Dave Lane character?">I</a> have set
up and maintain.
</p>
</div>
<div class="summary">
<ul>
<li>Total number of services: {results.total_instances}</li>
</ul>
</div>
<div class="summary">
<h2>Statistics</h2>
<ul>
<li>Number of individual services: {results.stats.instances}</li>
<li>Number of individual technologies: {results.stats.current}</li>
<li>Number of new technologies to be added: {results.stats.future}</li>
<li>Number of libre licenses used by these technologies: {results.stats.licenses}</li>
<li>&nbsp;&nbsp;'weak' corporate-exploitation-friendly open source licenses : {results.stats.weak}</li>
<li>&nbsp;&nbsp;'strong' user-protecting copyleft licenses: {results.stats.copyleft}</li>
<li>Total number of services: {results.stats.instances}</li>
</ul>
</div>
</div>
<CollapsibleSection headerText={'Filters'}>
<div class="filters">
<CollapsibleSection headerText={'Show or Hide Software Catergory Filter'}>
<CollapsibleSection headerText={'Software Catergory Filter'}>
<div class="filter-group categories">
<Filterable filterValues={categoryFilter} context={'categories'}>
<Filterable filterValues={categoryFilter}
histogram={results.tech_lists.category_list} context={'categories'}>
<h2 slot="h2">Categories</h2>
</Filterable>
</div>
</CollapsibleSection>
<CollapsibleSection headerText={'Software Analogue Filter'}>
<div class="filter-group analogues">
<Filterable filterValues={analogueFilter} context={'analogues'}>
<Filterable filterValues={analogueFilter}
histogram={results.tech_lists.analogue_list} context={'analogues'}>
<h2 slot="h2">Analogues</h2>
</Filterable>
</div>
</CollapsibleSection>
<CollapsibleSection headerText={'License Filter'}>
<div class="filter-group licenses">
<Filterable filterValues={licenseFilter} context={'licenses'}>
<Filterable filterValues={licenseFilter}
histogram={results.tech_lists.license_list} context={'licenses'}>
<h2 slot="h2">Open Source & Copyleft Licenses</h2>
</Filterable>
</div>
</CollapsibleSection>
<CollapsibleSection headerText={'Service Status Filter'}>
<div class="filter-group statuses">
<Filterable filterValues={statusFilter} context={'statuses'}>
<Filterable filterValues={statusFilter}
histogram={results.instance_lists.status_list} context={'statuses'}>
<h2 slot="h2">Statuses</h2>
</Filterable>
</div>
</CollapsibleSection>
<CollapsibleSection headerText={'Service Affiliates Filter'}>
<div class="filter-group affilates">
<Filterable filterValues={affiliateFilter} context={'affiliates'}>
<Filterable filterValues={affiliateFilter}
histogram={results.instance_lists.affiliate_list} context={'affiliates'}>
<h2 slot="h2">Affiliates</h2>
</Filterable>
</div>
</CollapsibleSection>
<CollapsibleSection headerText={'Service Host filter'}>
<div class="filter-group hosts">
<Filterable filterValues={hostFilter} context={'hosts'}>
<Filterable filterValues={hostFilter}
histogram={results.instance_lists.host_list} context={'hosts'}>
<h2 slot="h2">Hosts</h2>
</Filterable>
</div>
@ -399,56 +410,7 @@
</CollapsibleSection>
<div class="tiles">
{#each filteredTechnologies as technology}
<div class="tile technology">
<div class="links">
{#if technology.repository}<span class="repository"
><a
href={technology.repository}
title="The source code repository for {technology.name}">R</a
></span
>{/if}
{#if technology.wikipedia}<span class="wikipedia"
><a href={technology.wikipedia} title="Wikipedia Page for {technology.name}">W</a
></span
>{/if}
</div>
<h2><a href={technology.website}>{technology.name}</a></h2>
<p class="description">
{technology.description}{#if technology.extended_description}<span
title={technology.extended_description}>i</span
>{/if}
</p>
{#if technology.categories}<p class="categories">
{pluraliser('Category', 'Categories', technology.categories.length)}:
<span class="value">{toOxfordCommaString(technology.categories)}</span>
</p>{/if}
{#if technology.analogues}<p class="analogues">
Alternative to <span class="value">{toOxfordCommaString(technology.analogues)}</span>
</p>{/if}
{#if technology.license}<p
class="license"
title="The libre license for this project is {technology.license}"
>
License: <span class="value">{technology.license}</span>
</p>{/if}
{#if hasInstances(technology)}
<div class="instances">
<p>
{#each technology.instances as instance}
<a
href="https://{instance.domain}"
title="{technology.name} instance {instance.domain} hosted on '{instance.host}' by {instance.affiliation}"
><span
class="marker circle"
style="background-color: {affiliate_colours[instance.affiliation].colour}"
></span></a
>{/each}
</p>
</div>
{:else}
<div class="instances">Nothing here yet...</div>
{/if}
</div>
<Tile technology={technology} affiliate_colours={affiliate_colours} />
{/each}
</div>
</div>
@ -458,121 +420,31 @@
display: grid;
margin: 0 auto 3em auto;
padding: 0;
position: relative;
}
.introduction {
width: 50%;
}
.summary {
border-radius: 15px;
/*position: absolute;
right: 20px;*/
background-color: #ccc;
display: block;
/*display: block;*/
width: 50%;
border: 2px #aaa solid;
margin: 0;
padding: 0 20px;
}
/* .filters { margin-bottom: 1em;}
.filters .tags {
margin: 1em 0;
display: block;
}*/
.collapsible {
color: #eee !important;
background-color: #666 !important;
}
.tiles {
display: grid;
/*display: grid;
grid-gap: 15px;
grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
}
/* flip card stuff: https://www.w3schools.com/howto/howto_css_flip_card.asp */
.tile {
height: 515px;
min-width: 240px;
max-width: 300px;
/*width: 290px;*/
overflow: hidden;
box-sizing: border-box;
box-shadow: 5px 5px 3px #6a6d6a;
border: solid 3px #1e6831;
text-overflow: ellipsis;
overflow: hidden;
position: relative;
background-color: #eee;
}
.tile .links {
position: absolute;
top: 0;
right: 0;
padding: 2px 6px;
font-size: 90%;
}
.tile:hover {
box-shadow: 10px 10px 6px #727372;
}
.tile p.description {
height: 200px;
overflow: scroll;
}
.tile .links h2 a {
color: #e6c4fc;
}
.tile .links a:visited {
color: #ced0ff;
}
.tile .links a:hover,
.tile:hover h2 a {
color: #fff !important;
}
.tile h2 {
background-color: #999;
text-align: center;
padding: 0.5em;
margin: 0;
}
.tile h2 a {
color: #e6c4fc;
}
.tile h2 a:visited {
color: #ced0ff;
}
.tile h2 a:hover {
color: #fff;
}
.tile .description {
height: 260px;
text-overflow: ellipsis;
overflow: hidden;
}
.tile p {
padding: 0 1em 0.2em 1em;
font-size: 80%;
color: #555;
}
.instances {
background-color: #ddd;
position: absolute;
bottom: 0;
height: 3em;
width: 100%;
}
.instances .marker {
margin-right: 3px;
}
.webservices li {
list-style-type: none;
}
.value {
font-weight: bold;
color: #000;
}
.marker {
vertical-align: middle;
}
.circle {
height: 20px;
width: 20px;
background-color: #555;
border-radius: 50%;
display: inline-block;
}
.square {
height: 20px;
width: 20px;
background-color: #555;
display: inline-block;
grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));*/
/*grid-template-columns: repeat(auto-fit);*/
}
</style>