WIP - initial filtering working
This commit is contained in:
parent
e0fd7714be
commit
429087ce80
3 changed files with 215 additions and 31 deletions
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -23,7 +23,6 @@
|
|||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"svelte": "^4.2.7",
|
||||
"svelte-popover": "^2.0.8",
|
||||
"vite": "^5.0.3",
|
||||
"vitest": "^2.0.0"
|
||||
}
|
||||
|
@ -3112,13 +3111,6 @@
|
|||
"svelte": "^3.19.0 || ^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svelte-popover": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/svelte-popover/-/svelte-popover-2.0.8.tgz",
|
||||
"integrity": "sha512-Yvz4FpvvXc5aBGyIE/TlGlqULLKkcfc7N0MoaqNXHMRVXxla+sc7G1xSCCURP3VqP0nZ5cIyPWOLLWFXuJjTRA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/svelte-preprocess": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-6.0.2.tgz",
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"svelte": "^4.2.7",
|
||||
"svelte-popover": "^2.0.8",
|
||||
"vite": "^5.0.3",
|
||||
"vitest": "^2.0.0"
|
||||
},
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
<script>
|
||||
// get the data from the JSON Webservices feed in +page.js
|
||||
export let data;
|
||||
const { webservices } = data;
|
||||
|
||||
//console.log('orig:', webservices);
|
||||
//console.log('orig affiliates:', webservices.affiliates);
|
||||
//console.log('orig hosts:', webservices.hosts);
|
||||
|
||||
// source: https://mokole.com/palette.html 20 colors with default settings otherwise
|
||||
const colours30 = [ "#808080", "#7f0000", "#006400", "#808000", "#483d8b", "#008b8b", "#cd853f", "#00008b", "#7f007f", "#8fbc8f", "#b03060", "#ff0000", "#ff8c00", "#00ff00", "#9400d3", "#00ff7f", "#dc143c", "#00ffff", "#00bfff", "#0000ff", "#f08080", "#adff2f", "#1e90ff", "#ffff54", "#90ee90", "#add8e6", "#ff1493", "#7b68ee", "#ee82ee", "#ffe4b5" ];
|
||||
|
||||
// digest and return useful info from the Webservices JSON feed
|
||||
function processData(webservices) {
|
||||
let instances = 0;
|
||||
let current = [];
|
||||
|
@ -96,12 +94,6 @@
|
|||
// affiliate_data[key] = affiliate;
|
||||
// }
|
||||
}
|
||||
//console.log('categories: ', categories);
|
||||
//console.log('analogues: ', analogues);
|
||||
//console.log('licenses: ', licenses);
|
||||
//console.log('statuses: ', statuses);
|
||||
//console.log('affiliates: ', affiliates);
|
||||
//console.log('hosts: ', hosts);
|
||||
|
||||
return {
|
||||
total_instances: instances,
|
||||
|
@ -123,11 +115,13 @@
|
|||
}
|
||||
|
||||
// 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 array of keys from an object
|
||||
function getKeys(ob) {
|
||||
let keys = [];
|
||||
for (let key in ob) {
|
||||
|
@ -136,14 +130,37 @@
|
|||
return keys;
|
||||
}
|
||||
|
||||
// get intersection: ref https://bobbyhadz.com/blog/javascript-get-intersection-of-two-arrays
|
||||
function getIntersection(a, b) {
|
||||
const set1 = new Set(a);
|
||||
const set2 = new Set(b);
|
||||
|
||||
const intersection = [...set1].filter(
|
||||
element => set2.has(element)
|
||||
);
|
||||
|
||||
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'
|
||||
function hostColours(host_list, colours, hosts) {
|
||||
let host_array = {};
|
||||
let i = 0;
|
||||
|
@ -163,31 +180,76 @@
|
|||
return host_array;
|
||||
}
|
||||
|
||||
// assign colours from a set of differentiated colours to a list of tags...
|
||||
// this one is for 'affiliates'
|
||||
function affiliateColours(affiliate_list, colours, affiliates) {
|
||||
let affiliate_array = {};
|
||||
let affiliate_result = {};
|
||||
let i = 0;
|
||||
|
||||
//console.log('affiliates:', affiliates);
|
||||
console.log('affiliate_list:', affiliate_list);
|
||||
|
||||
for (const affiliate of affiliate_list) {
|
||||
//console.log('affiliate:', affiliate);
|
||||
if (affiliates[affiliate].hasOwnProperty('name') && affiliates[affiliate].hasOwnProperty('website')) {
|
||||
affiliate_array[affiliate] = {
|
||||
affiliate_result[affiliate] = {
|
||||
"colour": colours[i++],
|
||||
"name": affiliates[affiliate].name,
|
||||
"website": affiliates[affiliate].website
|
||||
}
|
||||
}
|
||||
}
|
||||
return affiliate_array;
|
||||
return affiliate_result;
|
||||
}
|
||||
|
||||
// sort technologies alphabetically by name
|
||||
function sortTechnologies(technologies) {
|
||||
return technologies.sort((a,b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
// generic function to find the intersection of two arrays
|
||||
// approach ref: https://bobbyhadz.com/blog/javascript-get-intersection-of-two-arrays
|
||||
function inCommon(a, b) {
|
||||
const set1 = new Set(a);
|
||||
const set2 = new Set(b);
|
||||
|
||||
const intersection = [...set1].filter(
|
||||
element => set2.has(element)
|
||||
);
|
||||
console.log('intersection = ', intersection);
|
||||
return intersection;
|
||||
}
|
||||
|
||||
// filter technologies based on a list of Categories
|
||||
function filterTechnologiesByCategoryList(technologies, list) {
|
||||
console.log('looking for tech in categories: ', list);
|
||||
const included = [];
|
||||
technologies.forEach(function(tech, index) {
|
||||
//console.log('tech(' + index + '): ', tech);
|
||||
if (hasInstances(tech)) {
|
||||
const intersection = inCommon(tech.categories, list);
|
||||
console.log('categories: ', tech.categories);
|
||||
//console.log('list: ', list);
|
||||
console.log('intersection: ', intersection);
|
||||
if (intersection && intersection.constructor === Array) {
|
||||
//console.log('intersection length: '. intersection.length);
|
||||
if (intersection.length > 0) {
|
||||
console.log('including tech: ', tech);
|
||||
included.push(tech);
|
||||
}
|
||||
} else {
|
||||
console.log('intersection not array');
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log('found ' + included.length + ' technologies.')
|
||||
return included;
|
||||
}
|
||||
|
||||
const results = processData(webservices);
|
||||
//console.log('results: ', results);
|
||||
|
||||
//const technologies = webservices.technologies;
|
||||
const technologies = results.active_services;
|
||||
//console.log(technologies);
|
||||
|
||||
const category_list = getKeys(results.tech_lists.category_list).sort();
|
||||
|
@ -203,12 +265,127 @@
|
|||
//console.log('affiliate_data: ', results.affiliates);
|
||||
const host_colours = hostColours(host_list, colours30, hosts);
|
||||
//console.log('host_colours:', host_colours);
|
||||
colours30.sort();
|
||||
const affiliate_colours = affiliateColours(affiliate_list, colours30, affiliates);
|
||||
//console.log('affiliate_colours:',affiliate_colours);
|
||||
|
||||
//console.log('categories array: ', results.tech_tags.categories);
|
||||
//console.log('categories keys: ', categories);
|
||||
|
||||
console.log('category_list: ', category_list);
|
||||
//console.log('analogue_list: ', analogue_list);
|
||||
console.log('license_list: ', license_list);
|
||||
console.log('status_list: ', status_list);
|
||||
console.log('affiliate_list: ', affiliate_list);
|
||||
console.log('hosts_list: ', host_list);
|
||||
|
||||
// filter technolologies based on a subset of categories
|
||||
const full_category_list = [
|
||||
'App Ecosystem',
|
||||
'Application Design',
|
||||
'Asset Management',
|
||||
'Association Management',
|
||||
'Association Management System',
|
||||
'Blog Syndication',
|
||||
'Book Reviews',
|
||||
'Calendar',
|
||||
'Collaborative',
|
||||
'Collaborative Markdown Editing',
|
||||
'Collaborative Wiki',
|
||||
'Content Management Systems',
|
||||
'Digital Media Sales',
|
||||
'Document Management',
|
||||
'Email Marketing Automation',
|
||||
'Email Services',
|
||||
'Enterprise Resource Planning',
|
||||
'Event Management',
|
||||
'Facial Recognition',
|
||||
'Facility Booking',
|
||||
'Federated Messaging',
|
||||
'Fediverse',
|
||||
'File Synchronisation',
|
||||
'Forum',
|
||||
'Framasoft',
|
||||
'Home Automation',
|
||||
'Home Security',
|
||||
'Image Backup',
|
||||
'Image Gallery',
|
||||
'Kanban Project Management',
|
||||
'Learning Management System',
|
||||
'Link Ranking',
|
||||
'Link Sharing',
|
||||
'Link Shortener',
|
||||
'Membership Management',
|
||||
'Messaging Cache',
|
||||
'Micro-blogging',
|
||||
'Multi-Domain Server',
|
||||
'Multimedia',
|
||||
'Music Discovery',
|
||||
'Music Streaming',
|
||||
'Network Infrastructure Monitoring',
|
||||
'Newsletters',
|
||||
'Online Forms',
|
||||
'Password Manager',
|
||||
'Photos',
|
||||
'Portfolio Management',
|
||||
'Privacy',
|
||||
'Productivity',
|
||||
'Reading List',
|
||||
'Remote Desktop Sharing',
|
||||
'Remote Incremental Encrypted System Backups',
|
||||
'Reverse Proxy',
|
||||
'Rich Messaging Client',
|
||||
'Rich Messaging Server',
|
||||
'Scheduling',
|
||||
'Server Monitoring',
|
||||
'Share Secrets Management',
|
||||
'Single Sign-On',
|
||||
'Social Bookmarking',
|
||||
'Social Media',
|
||||
'Software Development Forge',
|
||||
'Software Interface Designer',
|
||||
'Streaming',
|
||||
'Surveys',
|
||||
'TURN/STUN Server',
|
||||
'Video',
|
||||
'Video Conferencing',
|
||||
'Video Surveillance',
|
||||
'Visualisation of Time Series Data',
|
||||
'Webmail',
|
||||
'Webserver',
|
||||
'Website Analytics'
|
||||
];
|
||||
const copyleft_list = [
|
||||
'AGPL-3',
|
||||
'AGPL-3+',
|
||||
'GPL',
|
||||
'GPL-2',
|
||||
'GPL-2+',
|
||||
'GPL-3',
|
||||
'GPL-3+',
|
||||
'LGPL-3',
|
||||
];
|
||||
const category_filter_list = [
|
||||
'Fediverse',
|
||||
'Social Media',
|
||||
'Software Development Forge',
|
||||
'Software Interface Designer',
|
||||
'Streaming',
|
||||
'Surveys',
|
||||
'TURN/STUN Server',
|
||||
'Video',
|
||||
'Video Conferencing',
|
||||
'Video Surveillance',
|
||||
'Visualisation of Time Series Data',
|
||||
'Webmail',
|
||||
'Webserver',
|
||||
'Website Analytics'
|
||||
];
|
||||
|
||||
const filtered_technologies = filterTechnologiesByCategoryList(results.active_services, category_filter_list);
|
||||
const technologies = sortTechnologies(filtered_technologies);
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<div class="webservices">
|
||||
|
@ -231,10 +408,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="tiles">
|
||||
{#each technologies as technology}
|
||||
{#each filtered_technologies 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>
|
||||
{#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 (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}
|
||||
<p class="description">{technology.description}</p>
|
||||
{#if hasInstances(technology)}
|
||||
|
@ -310,12 +492,12 @@
|
|||
.tiles {
|
||||
display: grid;
|
||||
grid-gap: 15px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
|
||||
}
|
||||
/* flip card stuff: https://www.w3schools.com/howto/howto_css_flip_card.asp */
|
||||
.tile {
|
||||
height: 400px;
|
||||
min-width: 250px;
|
||||
height: 500px;
|
||||
min-width: 270px;
|
||||
max-width: 400px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
|
@ -324,7 +506,18 @@
|
|||
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 .links a { color: #e6c4fc; }
|
||||
.tile .links a:visited { color: #ced0ff; }
|
||||
.tile .links a:hover { color: #fff; }
|
||||
.tile h2 {
|
||||
background-color: #999;
|
||||
text-align: center;
|
||||
|
@ -335,7 +528,7 @@
|
|||
.tile h2 a:visited { color: #ced0ff; }
|
||||
.tile h2 a:hover { color: #fff; }
|
||||
.tile .description {
|
||||
height: 200px;
|
||||
height: 260px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -345,7 +538,7 @@
|
|||
color: #555;
|
||||
}
|
||||
.instances {
|
||||
background-color: #eee;
|
||||
background-color: #ddd;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 3em;
|
||||
|
@ -357,14 +550,14 @@
|
|||
.webservices li { list-style-type: none; }
|
||||
.value { font-weight: bold; color: #000; }
|
||||
.marker { vertical-align: middle; }
|
||||
.triangle {
|
||||
/* .triangle {
|
||||
--side-size: 20px;
|
||||
border-left: var(--side-size) solid transparent;
|
||||
border-right: var(--side-size) solid transparent;
|
||||
border-bottom: calc(2 * var(--side-size) * 0.866) solid green;
|
||||
border-top: var(--side-size) solid transparent;
|
||||
display: inline-block;
|
||||
}
|
||||
}*/
|
||||
.circle {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
|
|
Loading…
Reference in a new issue