Compare commits
2 commits
d2aab294bd
...
ef93b55b19
Author | SHA1 | Date | |
---|---|---|---|
|
ef93b55b19 | ||
|
0d9762206f |
5 changed files with 402 additions and 317 deletions
|
@ -4,40 +4,67 @@
|
|||
// Inclusive Components by Heydon Pickering https://inclusive-components.design/collapsible-sections/
|
||||
export let headerText;
|
||||
|
||||
let expanded = false
|
||||
let expanded = false;
|
||||
</script>
|
||||
|
||||
<div class="collapsible">
|
||||
<h3>
|
||||
<button aria-expanded={expanded} on:click={() => expanded = !expanded}>{headerText}
|
||||
<svg viewBox="0 0 20 20" fill="none" >
|
||||
<svg class="indicator" viewBox="0 0 20 20" fill="none" >
|
||||
<path class="vert" d="M10 1V19" stroke="black" stroke-width="2"/>
|
||||
<path d="M1 10L19 10" stroke="black" stroke-width="2"/>
|
||||
</svg>
|
||||
</button>
|
||||
</h3>
|
||||
|
||||
<!-- <div class='tree' hidden={!expanded}>
|
||||
<svg viewBox="0 0 60 60" fill="#ccc">
|
||||
<path class="open" d="M40 1 V69" stroke="#444" stroke-width="4" />
|
||||
</svg>
|
||||
</div> -->
|
||||
<div class='contents' hidden={!expanded}>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.collapsible { border-bottom: 1px solid var(--gray-light, #eee); }
|
||||
h3 { margin: 0; }
|
||||
button {
|
||||
background-color: var(--background, #fff);
|
||||
color: var(--gray-darkest, #282828);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
border: none;
|
||||
margin: 0;
|
||||
padding: 1em 0.5em;
|
||||
}
|
||||
button[aria-expanded="true"] { border-bottom: 1px solid var(--gray-light, #eee); }
|
||||
button[aria-expanded="true"] .vert { display: none; }
|
||||
button:focus svg { outline: 2px solid; }
|
||||
button [aria-expanded="true"] rect { fill: currentColor; }
|
||||
svg { height: 0.7em; width: 0.7em; }
|
||||
.collapsible {
|
||||
margin: 8px 0 4px 0;
|
||||
}
|
||||
.collapsible .collapsible h3 button {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.tree { float: left; }
|
||||
.contents {
|
||||
float: left;
|
||||
clear: both;
|
||||
width: 100%;
|
||||
border-bottom: dashed #aaa 1px;
|
||||
padding-bottom: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
h3 {
|
||||
margin: 0;
|
||||
}
|
||||
button {
|
||||
background-color: var(--background, #fff);
|
||||
color: var(--gray-darkest, #282828);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border: 1px #aaa solid;
|
||||
margin-left: 0px;
|
||||
padding: 0.7em 0.7em;
|
||||
}
|
||||
button[aria-expanded="true"] { border-bottom: 2px solid var(--gray-light, #eee); }
|
||||
button[aria-expanded="true"] .vert { display: none; }
|
||||
button:focus svg { outline: 1firtpx solid; }
|
||||
button[aria-expanded="true"] rect { fill: currentColor; }
|
||||
svg.indicator {
|
||||
height: 1.0em;
|
||||
width: 1.0em;
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
margin-top: 4px;;
|
||||
}
|
||||
.tree:hover svg { outline: 1firtpx solid; }
|
||||
|
||||
</style>
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
export let tabindex = 0;
|
||||
export let context = 'none';
|
||||
export let filterValues;
|
||||
console.log('Filterable contextFilter: ', filterValues);
|
||||
export let histogram; // an array with key = filter value, val = number of technologies
|
||||
//console.log('Filterable contextFilter: ', filterValues);
|
||||
let activeFiltersString = 'None';
|
||||
|
||||
//
|
||||
|
@ -20,8 +21,8 @@
|
|||
});
|
||||
|
||||
function joinActive(isActive) {
|
||||
console.log('joinActive');
|
||||
console.log('filterValues: ', filterValues);
|
||||
//console.log('joinActive');
|
||||
//console.log('filterValues: ', filterValues);
|
||||
if (isActive.length) return formatter.format(isActive);
|
||||
return 'No';
|
||||
}
|
||||
|
@ -40,7 +41,7 @@
|
|||
|
||||
// active is an array of active filter filterValues
|
||||
export function filterVerbage(active) {
|
||||
console.log('l = ', active.length);
|
||||
//console.log('l = ', active.length);
|
||||
if (active.length == 1) return 'filter is';
|
||||
else return 'filters are';
|
||||
}
|
||||
|
@ -48,9 +49,9 @@
|
|||
//
|
||||
// managing filter values
|
||||
function updateFilterValues(filter) {
|
||||
console.log('filter ('+ filter.id + ') has state ' + filter.active + '...');
|
||||
//console.log('filter ('+ filter.id + ') has state ' + filter.active + '...');
|
||||
$filterValues.forEach(function(b, i) {
|
||||
console.log('b: ', b);
|
||||
//console.log('b: ', b);
|
||||
if (b.id == filter.id) { $filterValues[i].active = filter.active; }
|
||||
})
|
||||
return filterValues;
|
||||
|
@ -73,6 +74,8 @@
|
|||
activeFiltersString = joinActive(isActive);
|
||||
filterText = filterVerbage(isActive);
|
||||
}
|
||||
|
||||
console.log('histogram: ', histogram);
|
||||
</script>
|
||||
|
||||
<slot name="h2"><h2>Test Array</h2></slot>
|
||||
|
@ -88,7 +91,7 @@
|
|||
<span class="filter {context}" class:active={filter.active}
|
||||
on:click="{() => { filter.active = !filter.active; updateFilterValues(filter);}}"
|
||||
on:keypress="{() => {filter.active = !filter.active; updateFilterValues(filter);}}"
|
||||
name="{filter.id}" role="button" tabindex={tabindex + filter.id}>{filter.name}</span><wbr/>
|
||||
name="{filter.id}" role="button" tabindex={tabindex + filter.id}>{filter.name}{#if (histogram[filter.name] > 1) } <span class='histogram' title="There are {histogram[filter.name]} webservices in this category"> ({histogram[filter.name]})</span>{/if}</span><wbr/>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
@ -97,6 +100,7 @@
|
|||
<style>
|
||||
h2 {
|
||||
margin-bottom: 0.2em;
|
||||
color: #444;
|
||||
}
|
||||
.actions {
|
||||
display: inline;
|
||||
|
@ -107,6 +111,7 @@
|
|||
padding: 4px 8px;
|
||||
}
|
||||
.actions span {
|
||||
font-size: 90%;
|
||||
margin-right: 1em;
|
||||
margin-bottom: 1em;
|
||||
cursor: pointer;
|
||||
|
@ -114,15 +119,13 @@
|
|||
.actions span:hover { color: #000; }
|
||||
.actions span:last { margin-right: 0px; }
|
||||
.container {
|
||||
line-height: 3em;
|
||||
line-height: 1.5em;
|
||||
width: 90%;
|
||||
padding: 1em 0 0 0;
|
||||
/*display: grid;
|
||||
grid-gap: 4px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));*/
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.filter {
|
||||
font-size: 80%;
|
||||
font-size: 80%;
|
||||
width: 80px;
|
||||
height: auto;
|
||||
border: 1px #aaa solid;
|
||||
|
@ -141,6 +144,48 @@
|
|||
}
|
||||
.active {
|
||||
background-color: lightblue;
|
||||
border-color: blue;
|
||||
/*border-color: blue;*/
|
||||
}
|
||||
|
||||
/* instance-specific styles */
|
||||
.filter {
|
||||
font-size: 80%;
|
||||
margin-right: 0.5em;
|
||||
line-height: 2.5;
|
||||
padding: 6px 8px;
|
||||
border-radius: 10px;
|
||||
white-space: nowrap;
|
||||
word-break: normal;
|
||||
box-shadow: 3px 3px 3px #6a6d6a;
|
||||
color: #222;
|
||||
}
|
||||
.filter:hover { box-shadow: 4px 4px 3px #727372; color: #000; }
|
||||
.filter:hover.active { color: #fff;}
|
||||
.filter.categories.active {
|
||||
background-color: #9aa34d;
|
||||
color: #eee;
|
||||
}
|
||||
.filter.analogues.active {
|
||||
background-color: #a34b62;
|
||||
color: #eee;
|
||||
}
|
||||
.filter.licenses.active {
|
||||
background-color: #6498a3;
|
||||
color: #eee;
|
||||
}
|
||||
.filter.statuses.active {
|
||||
background-color: #a369a2;
|
||||
color: #eee;
|
||||
}
|
||||
.filter.affiliates.active {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.filter.hosts.active {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.filter .histogram {
|
||||
color: #aaa;
|
||||
}
|
||||
</style>
|
||||
|
|
189
src/lib/components/Tile.svelte
Normal file
189
src/lib/components/Tile.svelte
Normal file
|
@ -0,0 +1,189 @@
|
|||
<script>
|
||||
// a 'technology' object
|
||||
export let technology;
|
||||
export let affiliate_colours;
|
||||
|
||||
const formatter = new Intl.ListFormat('en', {
|
||||
style: 'long',
|
||||
type: 'conjunction',
|
||||
});
|
||||
|
||||
// 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) {
|
||||
//console.log('arr: ', arr);
|
||||
return formatter.format(arr);
|
||||
}
|
||||
|
||||
// return a singular or plural term depending on the value of 'count'
|
||||
function pluraliser(singular, plural, count) {
|
||||
if (count > 1) return plural;
|
||||
return singular;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
</script>
|
||||
|
||||
<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>
|
||||
|
||||
<style>
|
||||
.tile {
|
||||
display: inline-block;
|
||||
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 #516855;
|
||||
border-radius: 15px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background-color: #eee;
|
||||
margin-top: 8px;
|
||||
margin-right: 18px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
</style>
|
|
@ -3,17 +3,17 @@ import { getContext, setContext } from 'svelte';
|
|||
|
||||
export function setFilter(context) {
|
||||
let contextFilter = writable(0);
|
||||
console.log('setFilter: ', contextFilter);
|
||||
//console.log('setFilter: ', contextFilter);
|
||||
contextFilter.subscribe((value) => {
|
||||
console.log('initial value: ', value);
|
||||
//console.log('initial value: ', value);
|
||||
});
|
||||
setContext(context, contextFilter);
|
||||
console.log('contextFilter (' + context + '): ', contextFilter);
|
||||
//console.log('contextFilter (' + context + '): ', contextFilter);
|
||||
}
|
||||
|
||||
export function getFilter(context) {
|
||||
console.log('looking for writable for context ', context);
|
||||
//console.log('looking for writable for context ', context);
|
||||
let f = getContext(context);
|
||||
console.log('returning filter writable: ', f);
|
||||
//console.log('returning filter writable: ', f);
|
||||
return f;
|
||||
}
|
||||
|
|
|
@ -10,16 +10,18 @@
|
|||
import { setFilter, getFilter } from '$lib/components/filter.js';
|
||||
import Filterable from '$lib/components/Filterable.svelte';
|
||||
// collapsible sections
|
||||
import CollapsibleSection from '$lib/components/CollapsibleSection.svelte'
|
||||
import CollapsibleSection from '$lib/components/CollapsibleSection.svelte';
|
||||
import Tile from '$lib/components/Tile.svelte';
|
||||
|
||||
//console.log('colours: ', colours);
|
||||
|
||||
//
|
||||
// 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 = [];
|
||||
|
@ -32,6 +34,7 @@
|
|||
// actual objects
|
||||
let hosts = [];
|
||||
let affiliates = [];
|
||||
|
||||
//
|
||||
// just a trivial reassignment
|
||||
//let hosts = webservices.hosts;
|
||||
|
@ -39,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;
|
||||
|
@ -56,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]++;
|
||||
|
@ -83,6 +87,7 @@
|
|||
} else {
|
||||
future[key] = tech;
|
||||
}
|
||||
all[key] = tech;
|
||||
}
|
||||
for (let key in webservices.hosts) {
|
||||
//console.log('key: ', key);
|
||||
|
@ -101,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: {
|
||||
|
@ -120,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;
|
||||
|
@ -149,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);
|
||||
|
@ -159,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'
|
||||
|
@ -239,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;
|
||||
}
|
||||
|
||||
|
@ -254,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);
|
||||
|
@ -322,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);
|
||||
}
|
||||
|
||||
|
@ -335,61 +333,75 @@
|
|||
</script>
|
||||
|
||||
<div class="webservices">
|
||||
<h1>Web Services</h1>
|
||||
|
||||
<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 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>
|
||||
<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> 'weak' corporate-exploitation-friendly open source licenses : {results.stats.weak}</li>
|
||||
<li> 'strong' user-protecting copyleft licenses: {results.stats.copyleft}</li>
|
||||
<li>Total number of services: {results.stats.instances}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="summary">
|
||||
<ul>
|
||||
<li>Total number of services: {results.total_instances}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<CollapsibleSection headerText={'Show or Hide Filters'}>
|
||||
<CollapsibleSection headerText={'Filters'}>
|
||||
<div class="filters">
|
||||
<CollapsibleSection headerText={'Show or Hide Software Catergory Filter'}>
|
||||
<div class="filter categories">
|
||||
<Filterable filterValues={categoryFilter} context={'categories'}>
|
||||
<CollapsibleSection headerText={'Software Catergory Filter'}>
|
||||
<div class="filter-group categories">
|
||||
<Filterable filterValues={categoryFilter}
|
||||
histogram={results.tech_lists.category_list} context={'categories'}>
|
||||
<h2 slot="h2">Categories</h2>
|
||||
</Filterable>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<CollapsibleSection headerText={'Show or Hide Software Analogue Filter'}>
|
||||
<div class="filter analogues">
|
||||
<Filterable filterValues={analogueFilter} context={'analogues'}>
|
||||
<CollapsibleSection headerText={'Software Analogue Filter'}>
|
||||
<div class="filter-group analogues">
|
||||
<Filterable filterValues={analogueFilter}
|
||||
histogram={results.tech_lists.analogue_list} context={'analogues'}>
|
||||
<h2 slot="h2">Analogues</h2>
|
||||
</Filterable>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<CollapsibleSection headerText={'Show or Hide License Filter'}>
|
||||
<div class="filter licenses">
|
||||
<Filterable filterValues={licenseFilter} context={'licenses'}>
|
||||
<CollapsibleSection headerText={'License Filter'}>
|
||||
<div class="filter-group 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={'Show or Hide Service Status Filter'}>
|
||||
<div class="filter statuses">
|
||||
<Filterable filterValues={statusFilter} context={'statuses'}>
|
||||
<CollapsibleSection headerText={'Service Status Filter'}>
|
||||
<div class="filter-group statuses">
|
||||
<Filterable filterValues={statusFilter}
|
||||
histogram={results.instance_lists.status_list} context={'statuses'}>
|
||||
<h2 slot="h2">Statuses</h2>
|
||||
</Filterable>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<CollapsibleSection headerText={'Show or Hide Service Affiliates Filter'}>
|
||||
<div class="filter affilates">
|
||||
<Filterable filterValues={affiliateFilter} context={'affiliates'}>
|
||||
<CollapsibleSection headerText={'Service Affiliates Filter'}>
|
||||
<div class="filter-group affilates">
|
||||
<Filterable filterValues={affiliateFilter}
|
||||
histogram={results.instance_lists.affiliate_list} context={'affiliates'}>
|
||||
<h2 slot="h2">Affiliates</h2>
|
||||
</Filterable>
|
||||
</div>
|
||||
</CollapsibleSection>
|
||||
<CollapsibleSection headerText={'Show or Hide Service Host filter'}>
|
||||
<div class="filter hosts">
|
||||
<Filterable filterValues={hostFilter} context={'hosts'}>
|
||||
<CollapsibleSection headerText={'Service Host filter'}>
|
||||
<div class="filter-group hosts">
|
||||
<Filterable filterValues={hostFilter}
|
||||
histogram={results.instance_lists.host_list} context={'hosts'}>
|
||||
<h2 slot="h2">Hosts</h2>
|
||||
</Filterable>
|
||||
</div>
|
||||
|
@ -398,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>
|
||||
|
@ -457,170 +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;
|
||||
}
|
||||
.filters {
|
||||
/*background-color: #f1ff94;
|
||||
padding: 0.5em;
|
||||
border: 3px solid #cbea77;
|
||||
margin: 1em 0;*/
|
||||
margin-bottom: 1em;
|
||||
/*display: block;*/
|
||||
width: 50%;
|
||||
border: 2px #aaa solid;
|
||||
margin: 0;
|
||||
padding: 0 20px;
|
||||
}
|
||||
/* .filters { margin-bottom: 1em;}
|
||||
.filters .tags {
|
||||
/*padding: 0.5em;*/
|
||||
margin: 1em 0;
|
||||
display: block;
|
||||
}
|
||||
.tag-list {
|
||||
/* white-space: normal;
|
||||
word-break: normal;
|
||||
display: inline;*/
|
||||
}
|
||||
.tags {
|
||||
/* white-space: normal;
|
||||
word-break: normal;
|
||||
display: inline;*/
|
||||
}
|
||||
.tag {
|
||||
font-size: 80%;
|
||||
margin-right: 0.5em;
|
||||
line-height: 2.5;
|
||||
padding: 6px 8px;
|
||||
border-radius: 10px;
|
||||
white-space: nowrap;
|
||||
word-break: normal;
|
||||
box-shadow: 3px 3px 3px #6a6d6a;
|
||||
}
|
||||
.tag:hover { box-shadow: 4px 4px 3px #727372;}
|
||||
.tag.category {
|
||||
background-color: #9aa34d;
|
||||
color: #fff;
|
||||
}
|
||||
.tag.license {
|
||||
background-color: #6498a3;
|
||||
color: #fff;
|
||||
}
|
||||
.tag.status {
|
||||
background-color: #a369a2;
|
||||
color: #fff;
|
||||
}
|
||||
.tag.affiliate {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.tag.host {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.tag.on {
|
||||
padding-right: 25px;
|
||||
background-image: url('/images/check-black-in-white-20px.png');
|
||||
background-position: right center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.tag.off {
|
||||
color: #ccc;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}*/
|
||||
.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 .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>
|
||||
|
|
Loading…
Reference in a new issue