diff --git a/code/html/base.jinja b/code/html/base.jinja
index cab7b545..661a0dbf 100644
--- a/code/html/base.jinja
+++ b/code/html/base.jinja
@@ -7,10 +7,13 @@
{{ base['title'] }}
-
-
+ {%- for css in base['css'] %}
+
+ {%- endfor %}
-
+ {%- for js in base['js'] %}
+
+ {%- endfor %}
{#- navigation bar -#}
diff --git a/code/html/css/simple-datatables.css b/code/html/css/simple-datatables.css
new file mode 100644
index 00000000..0bc5cdef
--- /dev/null
+++ b/code/html/css/simple-datatables.css
@@ -0,0 +1,173 @@
+.dataTable-wrapper.no-header .dataTable-container {
+ border-top: 1px solid #d9d9d9;
+}
+
+.dataTable-wrapper.no-footer .dataTable-container {
+ border-bottom: 1px solid #d9d9d9;
+}
+
+.dataTable-top,
+.dataTable-bottom {
+ padding: 8px 10px;
+}
+
+.dataTable-top > nav:first-child,
+.dataTable-top > div:first-child,
+.dataTable-bottom > nav:first-child,
+.dataTable-bottom > div:first-child {
+ float: left;
+}
+
+.dataTable-top > nav:last-child,
+.dataTable-top > div:last-child,
+.dataTable-bottom > nav:last-child,
+.dataTable-bottom > div:last-child {
+ float: right;
+}
+
+.dataTable-selector {
+ padding: 6px;
+}
+
+.dataTable-input {
+ padding: 6px 12px;
+}
+
+.dataTable-info {
+ margin: 7px 0;
+}
+
+/* PAGER */
+.dataTable-pagination ul {
+ margin: 0;
+ padding-left: 0;
+}
+
+.dataTable-pagination li {
+ list-style: none;
+ float: left;
+}
+
+.dataTable-pagination a {
+ border: 1px solid transparent;
+ float: left;
+ margin-left: 2px;
+ padding: 6px 12px;
+ position: relative;
+ text-decoration: none;
+ color: #333;
+}
+
+.dataTable-pagination a:hover {
+ background-color: #d9d9d9;
+}
+
+.dataTable-pagination .active a,
+.dataTable-pagination .active a:focus,
+.dataTable-pagination .active a:hover {
+ background-color: #d9d9d9;
+ cursor: default;
+}
+
+.dataTable-pagination .ellipsis a,
+.dataTable-pagination .disabled a,
+.dataTable-pagination .disabled a:focus,
+.dataTable-pagination .disabled a:hover {
+ cursor: not-allowed;
+}
+
+.dataTable-pagination .disabled a,
+.dataTable-pagination .disabled a:focus,
+.dataTable-pagination .disabled a:hover {
+ cursor: not-allowed;
+ opacity: 0.4;
+}
+
+.dataTable-pagination .pager a {
+ font-weight: bold;
+}
+
+/* TABLE */
+.dataTable-table {
+ max-width: 100%;
+ width: 100%;
+ border-spacing: 0;
+ border-collapse: separate;
+}
+
+.dataTable-table > tbody > tr > td,
+.dataTable-table > tbody > tr > th,
+.dataTable-table > tfoot > tr > td,
+.dataTable-table > tfoot > tr > th,
+.dataTable-table > thead > tr > td,
+.dataTable-table > thead > tr > th {
+ vertical-align: top;
+ padding: 8px 10px;
+}
+
+.dataTable-table > thead > tr > th {
+ vertical-align: bottom;
+ text-align: left;
+ border-bottom: 1px solid #d9d9d9;
+}
+
+.dataTable-table > tfoot > tr > th {
+ vertical-align: bottom;
+ text-align: left;
+ border-top: 1px solid #d9d9d9;
+}
+
+.dataTable-table th {
+ vertical-align: bottom;
+ text-align: left;
+}
+
+.dataTable-table th a {
+ text-decoration: none;
+ color: inherit;
+}
+
+.dataTable-sorter {
+ display: inline-block;
+ height: 100%;
+ position: relative;
+ width: 100%;
+}
+
+.dataTable-sorter::before,
+.dataTable-sorter::after {
+ content: "";
+ height: 0;
+ width: 0;
+ position: absolute;
+ right: 4px;
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ opacity: 0.2;
+}
+
+.dataTable-sorter::before {
+ border-top: 4px solid #000;
+ bottom: 0px;
+}
+
+.dataTable-sorter::after {
+ border-bottom: 4px solid #000;
+ border-top: 4px solid transparent;
+ top: 0px;
+}
+
+.asc .dataTable-sorter::after,
+.desc .dataTable-sorter::before {
+ opacity: 0.6;
+}
+
+.dataTables-empty {
+ text-align: center;
+}
+
+.dataTable-top::after, .dataTable-bottom::after {
+ clear: both;
+ content: " ";
+ display: table;
+}
diff --git a/code/html/generate_static_website.py b/code/html/generate_static_website.py
index 1f6b49fd..ed939200 100644
--- a/code/html/generate_static_website.py
+++ b/code/html/generate_static_website.py
@@ -7,6 +7,14 @@ Sitemaps is not needed, only for large projects with lots of JavaScript und many
"""
+# TODO simple data table with columns: name (homepage as link), keywords (at least essential ones), inspiration, language, code license
+
+# TODO languages: ? at the end sort
+
+# TODO too many spans, especially for text (maybe just plain text), also text with URLs inside is difficult (but why)
+
+# TODO keywords: content, multiplayer replace by icons (open, commercial (dollar signs))
+
# TODO most people only come to the main page, put more information there (direct links to genres, ...)
# TODO put more explanations on the category pages and the categories (number and short sentences)
@@ -31,6 +39,13 @@ Sitemaps is not needed, only for large projects with lots of JavaScript und many
# TODO games: platform icons and mature, state larger (but maybe not on mobile)
+# TODO games: platform information larger printed, keyword tags underlined to indicate links
+# TODO devs: icons mark as links (how? ask on ux.stackexchange.com?)
+
+# TODO everywhere: better link replacements
+
+# TODO game: first homepage link bold
+
# TODO statistics: better and more statistics with links where possible
# TODO statistics: with nice graphics (pie charts in SVG) with matplotlib
# TODO statistics: get it from common statistics generator
@@ -89,6 +104,7 @@ import shutil
import math
import datetime
import time
+import json
from functools import partial
from utils import osg, constants as c, utils, osg_statistics as stat, osg_parse
from jinja2 import Environment, FileSystemLoader
@@ -172,7 +188,7 @@ non_game_category_names = {
'tool': 'Tools',
'framework': 'Frameworks',
'library': 'Libraries',
- 'game engine': 'Game Engine'
+ 'game engine': 'Game Engines'
}
# we check the output html structure every time
@@ -842,6 +858,37 @@ def add_screenshot_information(entries):
entry['screenshots'] = screenshots
+def create_table_json_data(entries):
+ """
+ We assume that everything including internal is setup correctly.
+ Columns are Title, Link (entry, first homepage), State, Essential Keywords, Language, License
+ :param entries:
+ :return:
+ """
+ # create json structure
+ db = {'headings': ['Title', 'State', 'Tags', 'Platform', 'Language', 'License']}
+ data = []
+ for entry in entries:
+ title = '{} (Entry)'.format(entry['Home'][0], entry['Title'], url_to([], entry['href']))
+ state = ', '.join(entry['State'])
+ tags = entry['Keyword']
+ tags = [tag for tag in tags if tag in c.recommended_keywords]
+ tags = ', '.join(tags)
+ platform = entry.get('Platform', ['-'])
+ platform = ', '.join(platform)
+ language = ', '.join(entry['Code language'])
+ license = [x[-1] for x in entry['Code license']]
+ license = ', '.join(license)
+ data.append([title, state, tags, platform, language, license])
+ data.sort(key=lambda x: str.casefold(x[0]))
+ db['data'] = data
+
+ # write out
+ text = json.dumps(db, indent=1)
+ os.makedirs(c.web_data_path, exist_ok=True)
+ utils.write_text(os.path.join(c.web_data_path, 'entries.json'), text)
+
+
def generate(entries, inspirations, developers):
"""
Regenerates the whole static website given an already imported set of entries, inspirations and developers.
@@ -874,6 +921,9 @@ def generate(entries, inspirations, developers):
# set external links up
add_license_links_to_entries(games)
+ # create entries.json for the table
+ create_table_json_data(entries)
+
# sort into categories
sorter = lambda item, category: category == item['letter']
games_by_alphabet = sort_into_categories(games, extended_alphabet, sorter)
@@ -893,7 +943,9 @@ def generate(entries, inspirations, developers):
# base dictionary
base = {
'title': 'OSGL',
- 'creation-date': datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M')
+ 'creation-date': datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M'),
+ 'css': ['bulma.min.css', 'osgl.min.css'],
+ 'js': ['osgl.js']
}
# copy css and js
@@ -925,16 +977,18 @@ def generate(entries, inspirations, developers):
# index.html
base['active_nav'] = 'index'
- index = {'subtitle': make_text('Contains information about {} open source games and {} frameworks/tools.'.format(len(games), len(non_games))) }
+ index = {'subtitle': make_text('Contains information about {} open source games and {} game engines/tools.'.format(len(games), len(non_games))) }
template = environment.get_template('index.jinja')
write(template.render(index=index), ['index.html'])
# contribute page
+ base['title'] = 'OSGL | Contributions'
base['active_nav'] = 'contribute'
template = environment.get_template('contribute.jinja')
write(template.render(), ['contribute.html'])
# statistics page in statistics folder
+ base['title'] = 'OSGL | Statistics'
base['url_to'] = partial(url_to, statistics_path)
base['active_nav'] = 'statistics'
@@ -960,6 +1014,7 @@ def generate(entries, inspirations, developers):
write(template.render(data=data), statistics_index_path)
# non-games folder
+ base['title'] = 'OSGL | Game engines, frameworks, tools'
base['url_to'] = partial(url_to, non_games_path)
base['active_nav'] = 'frameworks'
@@ -985,6 +1040,7 @@ def generate(entries, inspirations, developers):
write(template_listing_entries.render(listing=listing), non_games_path + ['{}.html'.format(keyword)])
# games folder
+ base['title'] = 'OSGL | Games | Alphabetical'
base['url_to'] = partial(url_to, games_path)
base['active_nav'] = 'games'
@@ -999,7 +1055,7 @@ def generate(entries, inspirations, developers):
# generate games index
index = divide_in_three_columns_and_transform(games_by_alphabet, game_index)
index['title'] = make_text('Open source games')
- index['subtitle'] = make_text('Alphabetical index of informations about {} games'.format(len(games)))
+ index['subtitle'] = [make_text('Alphabetical index of informations about {} games (or see the '.format(len(games))), make_url(['table.html'], 'table'), ')']
index['categories'] = extended_alphabet
index['category-names'] = extended_alphabet_names
index['category-icons'] = {}
@@ -1009,10 +1065,11 @@ def generate(entries, inspirations, developers):
write(template_categorical_index.render(index=index), games_index_path)
# genres
+ base['title'] = 'OSGL | Games | Genres'
base['active_nav'] = ['filter', 'genres']
index = divide_in_three_columns_and_transform(games_by_genre, game_index)
index['title'] = make_text('Open source games')
- index['subtitle'] = make_text('Index by game genre')
+ index['subtitle'] = [make_text('Index by game genre (or see the '), make_url(['table.html'], 'table'), ')']
index['categories'] = genres
index['category-names'] = {k: make_text(k) for k in index['categories']}
index['category-icons'] = {k: make_icon(genre_icon_map[k]) for k in index['categories'] if k in genre_icon_map}
@@ -1022,10 +1079,11 @@ def generate(entries, inspirations, developers):
write(template_categorical_index.render(index=index), games_by_genres_path)
# games by language
+ base['title'] = 'OSGL | Games | Programming language'
base['active_nav'] = ['filter', 'code language']
index = divide_in_three_columns_and_transform(games_by_language, game_index)
index['title'] = 'Open source games and frameworks'
- index['subtitle'] = make_text('Index by programming language')
+ index['subtitle'] = [make_text('Index by programming language (or see the '), make_url(['table.html'], 'table'), ')']
index['categories'] = c.known_languages
index['category-names'] = {k:k for k in index['categories']}
index['category-icons'] = {}
@@ -1035,10 +1093,11 @@ def generate(entries, inspirations, developers):
write(template_categorical_index.render(index=index), games_by_language_path)
# games by platform
+ base['title'] = 'OSGL | Games | Supported Platform'
base['active_nav'] = ['filter', 'platforms']
index = divide_in_three_columns_and_transform(games_by_platform, game_index)
index['title'] = 'Open source games and frameworks'
- index['subtitle'] = make_text('Index by supported platform')
+ index['subtitle'] = [make_text('Index by supported platform (or see the '), make_url(['table.html'], 'table'), ')']
index['categories'] = c.valid_platforms + ('Unspecified',)
index['category-names'] = {k: make_text(k) for k in index['categories']}
index['category-icons'] = {k: make_icon(platform_icon_map[k]) for k in index['categories']}
@@ -1049,6 +1108,7 @@ def generate(entries, inspirations, developers):
write(template_categorical_index.render(index=index), games_by_platform_path)
# top 50 games
+ base['title'] = 'OSGL | Games | GitHub Top 50'
base['active_nav'] = ['filter', 'top50']
# there are no other games coming afterwards, can actually number them
for index, game in enumerate(top50_games):
@@ -1061,6 +1121,7 @@ def generate(entries, inspirations, developers):
write(template_listing_entries.render(listing=listing), games_top50_path)
# inspirations folder
+ base['title'] = 'OSGL | Inspirational games'
base['url_to'] = partial(url_to, inspirations_path)
base['active_nav'] = 'inspirations'
@@ -1088,6 +1149,7 @@ def generate(entries, inspirations, developers):
write(template_listing_inspirations.render(listing=listing), inspirations_path + ['{}.html'.format(letter.capitalize())])
# developers folder
+ base['title'] = 'OSGL | Games | Developers'
base['url_to'] = partial(url_to, developers_path)
base['active_nav'] = 'developers'
@@ -1112,6 +1174,15 @@ def generate(entries, inspirations, developers):
index['category-infos'] = {}
write(template_categorical_index.render(index=index), developers_index_path)
+ # dynamic table (is in top level folder)
+ base['title'] = 'OSGL | Entries | Table'
+ base['url_to'] = partial(url_to, [])
+ base['css'].append('simple-datatables.css')
+ base['js'].append('simple-datatables.js')
+ base['active_nav'] = []
+ template = environment.get_template('table.jinja')
+ write(template.render(), ['table.html'])
+
if __name__ == "__main__":
diff --git a/code/html/js/simple-datatables.js b/code/html/js/simple-datatables.js
new file mode 100644
index 00000000..71cbe8b8
--- /dev/null
+++ b/code/html/js/simple-datatables.js
@@ -0,0 +1,8 @@
+/**
+ * Minified by jsDelivr using Terser v5.7.1.
+ * Original file: /npm/simple-datatables@3.1.2/dist/umd/simple-datatables.js
+ *
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
+ */
+!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).simpleDatatables=t()}}((function(){return function t(e,s,i){function a(r,o){if(!s[r]){if(!e[r]){var h="function"==typeof require&&require;if(!o&&h)return h(r,!0);if(n)return n(r,!0);var l=new Error("Cannot find module '"+r+"'");throw l.code="MODULE_NOT_FOUND",l}var d=s[r]={exports:{}};e[r][0].call(d.exports,(function(t){return a(e[r][1][t]||t)}),d,d.exports,t,e,s,i)}return s[r].exports}for(var n="function"==typeof require&&require,r=0;r=e?t:""+Array(e+1-i.length).join(s)+t},b={s:m,z:function(t){var e=-t.utcOffset(),s=Math.abs(e),i=Math.floor(s/60),a=s%60;return(e<=0?"+":"-")+m(i,2,"0")+":"+m(a,2,"0")},m:function t(e,s){if(e.date()68?1900:2e3)},o=function(t){return function(e){this[t]=+e}},h=[/[+-]\d\d:?(\d\d)?|Z/,function(t){(this.zone||(this.zone={})).offset=function(t){if(!t)return 0;if("Z"===t)return 0;var e=t.match(/([+-]|\d\d)/g),s=60*e[1]+(+e[2]||0);return 0===s?0:"+"===e[0]?-s:s}(t)}],l=function(t){var e=n[t];return e&&(e.indexOf?e:e.s.concat(e.f))},d=function(t,e){var s,i=n.meridiem;if(i){for(var a=1;a<=24;a+=1)if(t.indexOf(i(a,0,e))>-1){s=a>12;break}}else s=t===(e?"pm":"PM");return s},c={A:[a,function(t){this.afternoon=d(t,!1)}],a:[a,function(t){this.afternoon=d(t,!0)}],S:[/\d/,function(t){this.milliseconds=100*+t}],SS:[s,function(t){this.milliseconds=10*+t}],SSS:[/\d{3}/,function(t){this.milliseconds=+t}],s:[i,o("seconds")],ss:[i,o("seconds")],m:[i,o("minutes")],mm:[i,o("minutes")],H:[i,o("hours")],h:[i,o("hours")],HH:[i,o("hours")],hh:[i,o("hours")],D:[i,o("day")],DD:[s,o("day")],Do:[a,function(t){var e=n.ordinal,s=t.match(/\d+/);if(this.day=s[0],e)for(var i=1;i<=31;i+=1)e(i).replace(/\[|\]/g,"")===t&&(this.day=i)}],M:[i,o("month")],MM:[s,o("month")],MMM:[a,function(t){var e=l("months"),s=(l("monthsShort")||e.map((function(t){return t.substr(0,3)}))).indexOf(t)+1;if(s<1)throw new Error;this.month=s%12||s}],MMMM:[a,function(t){var e=l("months").indexOf(t)+1;if(e<1)throw new Error;this.month=e%12||e}],Y:[/[+-]?\d+/,o("year")],YY:[s,function(t){this.year=r(t)}],YYYY:[/\d{4}/,o("year")],Z:h,ZZ:h};function u(s){var i,a;i=s,a=n&&n.formats;for(var r=(s=i.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g,(function(e,s,i){var n=i&&i.toUpperCase();return s||a[i]||t[i]||a[n].replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g,(function(t,e,s){return e||s.slice(1)}))}))).match(e),o=r.length,h=0;h-1)return new Date(("X"===e?1e3:1)*t);var i=u(e)(t),a=i.year,n=i.month,r=i.day,o=i.hours,h=i.minutes,l=i.seconds,d=i.milliseconds,c=i.zone,p=new Date,f=r||(a||n?1:p.getDate()),g=a||p.getFullYear(),m=0;a&&!n||(m=n>0?n-1:p.getMonth());var b=o||0,y=h||0,v=l||0,w=d||0;return c?new Date(Date.UTC(g,m,f,b,y,v,w+60*c.offset*1e3)):s?new Date(Date.UTC(g,m,f,b,y,v,w)):new Date(g,m,f,b,y,v,w)}catch(t){return new Date("")}}(e,o,i),this.init(),c&&!0!==c&&(this.$L=this.locale(c).$L),d&&e!==this.format(o)&&(this.$d=new Date("")),n={}}else if(o instanceof Array)for(var p=o.length,f=1;f<=p;f+=1){r[1]=o[f-1];var g=s.apply(this,r);if(g.isValid()){this.$d=g.$d,this.$L=g.$L,this.init();break}f===p&&(this.$d=new Date(""))}else a.call(this,t)}}}()}));i.extend(a),s.parseDate=(t,e)=>{let s=!1;if(e)switch(e){case"ISO_8601":s=t;break;case"RFC_2822":s=i(t.slice(5),"DD MMM YYYY HH:mm:ss ZZ").unix();break;case"MYSQL":s=i(t,"YYYY-MM-DD hh:mm:ss").unix();break;case"UNIX":s=i(t).unix();break;default:s=i(t,e).valueOf()}return s}}).call(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],2:[function(t,e,s){"use strict";Object.defineProperty(s,"__esModule",{value:!0});const i=t=>"[object Object]"===Object.prototype.toString.call(t),a=(t,e)=>{const s=document.createElement(t);if(e&&"object"==typeof e)for(const t in e)"html"===t?s.innerHTML=e[t]:s.setAttribute(t,e[t]);return s},n=t=>{t instanceof NodeList?t.forEach((t=>n(t))):t.innerHTML=""},r=(t,e,s)=>a("li",{class:t,html:`${s}`}),o=(t,e)=>{let s,i;1===e?(s=0,i=t.length):-1===e&&(s=t.length-1,i=-1);for(let a=!0;a;){a=!1;for(let n=s;n!=i;n+=e)if(t[n+e]&&t[n].value>t[n+e].value){const s=t[n],i=t[n+e],r=s;t[n]=i,t[n+e]=r,a=!0}}return t};class h{constructor(t,e){return this.dt=t,this.rows=e,this}build(t){const e=a("tr");let s=this.dt.headings;return s.length||(s=t.map((()=>""))),s.forEach(((s,i)=>{const n=a("td");t[i]&&t[i].length||(t[i]=""),n.innerHTML=t[i],n.data=t[i],e.appendChild(n)})),e}render(t){return t}add(t){if(Array.isArray(t)){const e=this.dt;Array.isArray(t[0])?t.forEach((t=>{e.data.push(this.build(t))})):e.data.push(this.build(t)),e.data.length&&(e.hasRows=!0),this.update(),e.columns().rebuild()}}remove(t){const e=this.dt;Array.isArray(t)?(t.sort(((t,e)=>e-t)),t.forEach((t=>{e.data.splice(t,1)}))):"all"==t?e.data=[]:e.data.splice(t,1),e.data.length||(e.hasRows=!1),this.update(),e.columns().rebuild()}update(){this.dt.data.forEach(((t,e)=>{t.dataIndex=e}))}}class l{constructor(t){return this.dt=t,this}swap(t){if(t.length&&2===t.length){const e=[];this.dt.headings.forEach(((t,s)=>{e.push(s)}));const s=t[0],i=t[1],a=e[i];e[i]=e[s],e[s]=a,this.order(e)}}order(t){let e,s,i,a,n,r,o;const h=[[],[],[],[]],l=this.dt;t.forEach(((t,i)=>{n=l.headings[t],r="false"!==n.getAttribute("data-sortable"),e=n.cloneNode(!0),e.originalCellIndex=i,e.sortable=r,h[0].push(e),l.hiddenColumns.includes(t)||(s=n.cloneNode(!0),s.originalCellIndex=i,s.sortable=r,h[1].push(s))})),l.data.forEach(((e,s)=>{i=e.cloneNode(!1),a=e.cloneNode(!1),i.dataIndex=a.dataIndex=s,null!==e.searchIndex&&void 0!==e.searchIndex&&(i.searchIndex=a.searchIndex=e.searchIndex),t.forEach((t=>{o=e.cells[t].cloneNode(!0),o.data=e.cells[t].data,i.appendChild(o),l.hiddenColumns.includes(t)||(o=e.cells[t].cloneNode(!0),o.data=e.cells[t].data,a.appendChild(o))})),h[2].push(i),h[3].push(a)})),l.headings=h[0],l.activeHeadings=h[1],l.data=h[2],l.activeRows=h[3],l.update()}hide(t){if(t.length){const e=this.dt;t.forEach((t=>{e.hiddenColumns.includes(t)||e.hiddenColumns.push(t)})),this.rebuild()}}show(t){if(t.length){let e;const s=this.dt;t.forEach((t=>{e=s.hiddenColumns.indexOf(t),e>-1&&s.hiddenColumns.splice(e,1)})),this.rebuild()}}visible(t){let e;const s=this.dt;return t=t||s.headings.map((t=>t.originalCellIndex)),isNaN(t)?Array.isArray(t)&&(e=[],t.forEach((t=>{e.push(!s.hiddenColumns.includes(t))}))):e=!s.hiddenColumns.includes(t),e}add(t){let e;const s=document.createElement("th");if(!this.dt.headings.length)return this.dt.insert({headings:[t.heading],data:t.data.map((t=>[t]))}),void this.rebuild();this.dt.hiddenHeader?s.innerHTML="":t.heading.nodeName?s.appendChild(t.heading):s.innerHTML=t.heading,this.dt.headings.push(s),this.dt.data.forEach(((s,i)=>{t.data[i]&&(e=document.createElement("td"),t.data[i].nodeName?e.appendChild(t.data[i]):e.innerHTML=t.data[i],e.data=e.innerHTML,t.render&&(e.innerHTML=t.render.call(this,e.data,e,s)),s.appendChild(e))})),t.type&&s.setAttribute("data-type",t.type),t.format&&s.setAttribute("data-format",t.format),t.hasOwnProperty("sortable")&&(s.sortable=t.sortable,s.setAttribute("data-sortable",!0===t.sortable?"true":"false")),this.rebuild(),this.dt.renderHeader()}remove(t){Array.isArray(t)?(t.sort(((t,e)=>e-t)),t.forEach((t=>this.remove(t)))):(this.dt.headings.splice(t,1),this.dt.data.forEach((e=>{e.removeChild(e.cells[t])}))),this.rebuild()}filter(t,e,s,i){const a=this.dt;if(a.filterState||(a.filterState={originalData:a.data}),!a.filterState[t]){const e=[...i,()=>!0];a.filterState[t]=function(){let t=0;return()=>e[t++%e.length]}()}const n=a.filterState[t](),r=Array.from(a.filterState.originalData).filter((e=>{const s=e.cells[t],i=s.hasAttribute("data-content")?s.getAttribute("data-content"):s.innerText;return"function"==typeof n?n(i):i===n}));a.data=r,a.data.length?(this.rebuild(),a.update()):(a.clear(),a.setMessage(a.options.labels.noRows)),s||a.emit("datatable.sort",t,e)}sort(e,s,i){const a=this.dt;if(a.hasHeadings&&(e<0||e>a.headings.length))return!1;const n=a.options.filters&&a.options.filters[a.headings[e].textContent];if(n&&0!==n.length)return void this.filter(e,s,i,n);a.sorting=!0,i||a.emit("datatable.sorting",e,s);let r=a.data;const h=[],l=[];let d=0,c=0;const u=a.headings[e],p=[];if("date"===u.getAttribute("data-type")){let e=!1;u.hasAttribute("data-format")&&(e=u.getAttribute("data-format")),p.push(Promise.resolve().then((function(){return t("./date-ee454f51.js")})).then((({parseDate:t})=>s=>t(s,e))))}Promise.all(p).then((t=>{const n=t[0];let p,f;Array.from(r).forEach((t=>{const s=t.cells[e],i=s.hasAttribute("data-content")?s.getAttribute("data-content"):s.innerText;let a;a=n?n(i):"string"==typeof i?i.replace(/(\$|,|\s|%)/g,""):i,parseFloat(a)==a?l[c++]={value:Number(a),row:t}:h[d++]={value:"string"==typeof i?i.toLowerCase():i,row:t}})),s||(s=u.classList.contains("asc")?"desc":"asc"),"desc"==s?(p=o(h,-1),f=o(l,-1),u.classList.remove("asc"),u.classList.add("desc")):(p=o(l,1),f=o(h,1),u.classList.remove("desc"),u.classList.add("asc")),a.lastTh&&u!=a.lastTh&&(a.lastTh.classList.remove("desc"),a.lastTh.classList.remove("asc")),a.lastTh=u,r=p.concat(f),a.data=[];const g=[];r.forEach(((t,e)=>{a.data.push(t.row),null!==t.row.searchIndex&&void 0!==t.row.searchIndex&&g.push(e)})),a.searchData=g,this.rebuild(),a.update(),i||a.emit("datatable.sort",e,s)}))}rebuild(){let t,e,s,i;const a=this.dt,n=[];a.activeRows=[],a.activeHeadings=[],a.headings.forEach(((t,e)=>{t.originalCellIndex=e,t.sortable="false"!==t.getAttribute("data-sortable"),a.hiddenColumns.includes(e)||a.activeHeadings.push(t)})),a.data.forEach(((r,o)=>{t=r.cloneNode(!1),e=r.cloneNode(!1),t.dataIndex=e.dataIndex=o,null!==r.searchIndex&&void 0!==r.searchIndex&&(t.searchIndex=e.searchIndex=r.searchIndex),Array.from(r.cells).forEach((n=>{s=n.cloneNode(!0),s.data=n.data,t.appendChild(s),a.hiddenColumns.includes(s.cellIndex)||(i=s.cloneNode(!0),i.data=s.data,e.appendChild(i))})),n.push(t),a.activeRows.push(e)})),a.data=n,a.update()}}const d=function(t){let e=!1,s=!1;if((t=t||this.options.data).headings){e=a("thead");const s=a("tr");t.headings.forEach((t=>{const e=a("th",{html:t});s.appendChild(e)})),e.appendChild(s)}t.data&&t.data.length&&(s=a("tbody"),t.data.forEach((e=>{if(t.headings&&t.headings.length!==e.length)throw new Error("The number of rows do not match the number of headings.");const i=a("tr");e.forEach((t=>{const e=a("td",{html:t});i.appendChild(e)})),s.appendChild(i)}))),e&&(null!==this.dom.tHead&&this.dom.removeChild(this.dom.tHead),this.dom.appendChild(e)),s&&(this.dom.tBodies.length&&this.dom.removeChild(this.dom.tBodies[0]),this.dom.appendChild(s))},c={sortable:!0,searchable:!0,paging:!0,perPage:10,perPageSelect:[5,10,15,20,25],nextPrev:!0,firstLast:!1,prevText:"‹",nextText:"›",firstText:"«",lastText:"»",ellipsisText:"…",ascText:"▴",descText:"▾",truncatePager:!0,pagerDelta:2,scrollY:"",fixedColumns:!0,fixedHeight:!1,header:!0,hiddenHeader:!1,footer:!1,labels:{placeholder:"Search...",perPage:"{select} entries per page",noRows:"No entries found",info:"Showing {start} to {end} of {rows} entries"},layout:{top:"{select}{search}",bottom:"{info}{pager}"}};class u{constructor(t,e={}){const s="string"==typeof t?document.querySelector(t):t;if(this.options={...c,...e,layout:{...c.layout,...e.layout},labels:{...c.labels,...e.labels}},this.initialized=!1,this.initialLayout=s.innerHTML,this.initialSortable=this.options.sortable,this.options.header||(this.options.sortable=!1),null===s.tHead&&(!this.options.data||this.options.data&&!this.options.data.headings)&&(this.options.sortable=!1),s.tBodies.length&&!s.tBodies[0].rows.length&&this.options.data&&!this.options.data.data)throw new Error("You seem to be using the data option, but you've not defined any rows.");this.dom=s,this.table=this.dom,this.listeners={onResize:t=>this.onResize(t)},this.init()}static extend(t,e){"function"==typeof e?u.prototype[t]=e:u[t]=e}init(t){if(this.initialized||this.dom.classList.contains("dataTable-table"))return!1;Object.assign(this.options,t||{}),this.currentPage=1,this.onFirstPage=!0,this.hiddenColumns=[],this.columnRenderers=[],this.selectedColumns=[],this.render(),setTimeout((()=>{this.emit("datatable.init"),this.initialized=!0,this.options.plugins&&Object.entries(this.options.plugins).forEach((([t,e])=>{this[t]&&"function"==typeof this[t]&&(this[t]=this[t](e,{createElement:a}),e.enabled&&this[t].init&&"function"==typeof this[t].init&&this[t].init())}))}),10)}render(t){if(t){switch(t){case"page":this.renderPage();break;case"pager":this.renderPager();break;case"header":this.renderHeader()}return!1}const e=this.options;let s="";if(e.data&&d.call(this),this.body=this.dom.tBodies[0],this.head=this.dom.tHead,this.foot=this.dom.tFoot,this.body||(this.body=a("tbody"),this.dom.appendChild(this.body)),this.hasRows=this.body.rows.length>0,!this.head){const t=a("thead"),s=a("tr");this.hasRows&&(Array.from(this.body.rows[0].cells).forEach((()=>{s.appendChild(a("th"))})),t.appendChild(s)),this.head=t,this.dom.insertBefore(this.head,this.body),this.hiddenHeader=e.hiddenHeader}if(this.headings=[],this.hasHeadings=this.head.rows.length>0,this.hasHeadings&&(this.header=this.head.rows[0],this.headings=[].slice.call(this.header.cells)),e.header||this.head&&this.dom.removeChild(this.dom.tHead),e.footer?this.head&&!this.foot&&(this.foot=a("tfoot",{html:this.head.innerHTML}),this.dom.appendChild(this.foot)):this.foot&&this.dom.removeChild(this.dom.tFoot),this.wrapper=a("div",{class:"dataTable-wrapper dataTable-loading"}),s+="",s+=e.layout.top,s+="
",e.scrollY.length?s+=``:s+="",s+="",s+=e.layout.bottom,s+="
",s=s.replace("{info}",e.paging?"":""),e.paging&&e.perPageSelect){let t="";const i=a("select",{class:"dataTable-selector"});e.perPageSelect.forEach((t=>{const s=t===e.perPage,a=new Option(t,t,s,s);i.add(a)})),t=t.replace("{select}",i.outerHTML),s=s.replace("{select}",t)}else s=s.replace("{select}","");if(e.searchable){const t=``;s=s.replace("{search}",t)}else s=s.replace("{search}","");this.hasHeadings&&this.render("header"),this.dom.classList.add("dataTable-table");const i=a("nav",{class:"dataTable-pagination"}),n=a("ul",{class:"dataTable-pagination-list"});i.appendChild(n),s=s.replace(/\{pager\}/g,i.outerHTML),this.wrapper.innerHTML=s,this.container=this.wrapper.querySelector(".dataTable-container"),this.pagers=this.wrapper.querySelectorAll(".dataTable-pagination-list"),this.label=this.wrapper.querySelector(".dataTable-info"),this.dom.parentNode.replaceChild(this.wrapper,this.dom),this.container.appendChild(this.dom),this.rect=this.dom.getBoundingClientRect(),this.data=Array.from(this.body.rows),this.activeRows=this.data.slice(),this.activeHeadings=this.headings.slice(),this.update(),this.setColumns(),this.fixHeight(),this.fixColumns(),e.header||this.wrapper.classList.add("no-header"),e.footer||this.wrapper.classList.add("no-footer"),e.sortable&&this.wrapper.classList.add("sortable"),e.searchable&&this.wrapper.classList.add("searchable"),e.fixedHeight&&this.wrapper.classList.add("fixed-height"),e.fixedColumns&&this.wrapper.classList.add("fixed-columns"),this.bindEvents()}renderPage(){if(this.hasHeadings&&(n(this.header),this.activeHeadings.forEach((t=>this.header.appendChild(t)))),this.hasRows&&this.totalPages){this.currentPage>this.totalPages&&(this.currentPage=1);const t=this.currentPage-1,e=document.createDocumentFragment();this.pages[t].forEach((t=>e.appendChild(this.rows().render(t)))),this.clear(e),this.onFirstPage=1===this.currentPage,this.onLastPage=this.currentPage===this.lastPage}else this.setMessage(this.options.labels.noRows);let t,e=0,s=0,i=0;if(this.totalPages&&(e=this.currentPage-1,s=e*this.options.perPage,i=s+this.pages[e].length,s+=1,t=this.searching?this.searchData.length:this.data.length),this.label&&this.options.labels.info.length){const e=this.options.labels.info.replace("{start}",s).replace("{end}",i).replace("{page}",this.currentPage).replace("{pages}",this.totalPages).replace("{rows}",t);this.label.innerHTML=t?e:""}1==this.currentPage&&this.fixHeight()}renderPager(){if(n(this.pagers),this.totalPages>1){const t="pager",e=document.createDocumentFragment(),s=this.onFirstPage?1:this.currentPage-1,i=this.onLastPage?this.totalPages:this.currentPage+1;this.options.firstLast&&e.appendChild(r(t,1,this.options.firstText)),this.options.nextPrev&&!this.onFirstPage&&e.appendChild(r(t,s,this.options.prevText));let n=this.links;this.options.truncatePager&&(n=((t,e,s,i,n)=>{let r;const o=2*(i=i||2);let h=e-i,l=e+i;const d=[],c=[];e<4-i+o?l=3+o:e>s-(3-i+o)&&(h=s-(2+o));for(let e=1;e<=s;e++)if(1==e||e==s||e>=h&&e<=l){const s=t[e-1];s.classList.remove("active"),d.push(s)}return d.forEach((e=>{const s=e.children[0].getAttribute("data-page");if(r){const e=r.children[0].getAttribute("data-page");if(s-e==2)c.push(t[e]);else if(s-e!=1){const t=a("li",{class:"ellipsis",html:`${n}`});c.push(t)}}c.push(e),r=e})),c})(this.links,this.currentPage,this.pages.length,this.options.pagerDelta,this.options.ellipsisText)),this.links[this.currentPage-1].classList.add("active"),n.forEach((t=>{t.classList.remove("active"),e.appendChild(t)})),this.links[this.currentPage-1].classList.add("active"),this.options.nextPrev&&!this.onLastPage&&e.appendChild(r(t,i,this.options.nextText)),this.options.firstLast&&e.appendChild(r(t,this.totalPages,this.options.lastText)),this.pagers.forEach((t=>{t.appendChild(e.cloneNode(!0))}))}}renderHeader(){this.labels=[],this.headings&&this.headings.length&&this.headings.forEach(((t,e)=>{if(this.labels[e]=t.textContent,t.firstElementChild&&t.firstElementChild.classList.contains("dataTable-sorter")&&(t.innerHTML=t.firstElementChild.innerHTML),t.sortable="false"!==t.getAttribute("data-sortable"),t.originalCellIndex=e,this.options.sortable&&t.sortable){const e=a("a",{href:"#",class:"dataTable-sorter",html:t.innerHTML});t.innerHTML="",t.setAttribute("data-sortable",""),t.appendChild(e)}})),this.fixColumns()}bindEvents(){const t=this.options;if(t.perPageSelect){const e=this.wrapper.querySelector(".dataTable-selector");e&&e.addEventListener("change",(()=>{t.perPage=parseInt(e.value,10),this.update(),this.fixHeight(),this.emit("datatable.perpage",t.perPage)}),!1)}t.searchable&&(this.input=this.wrapper.querySelector(".dataTable-input"),this.input&&this.input.addEventListener("keyup",(()=>this.search(this.input.value)),!1)),this.wrapper.addEventListener("click",(e=>{const s=e.target.closest("a");s&&"a"===s.nodeName.toLowerCase()&&(s.hasAttribute("data-page")?(this.page(s.getAttribute("data-page")),e.preventDefault()):t.sortable&&s.classList.contains("dataTable-sorter")&&"false"!=s.parentNode.getAttribute("data-sortable")&&(this.columns().sort(this.headings.indexOf(s.parentNode)),e.preventDefault()))}),!1),window.addEventListener("resize",this.listeners.onResize)}onResize(){this.rect=this.container.getBoundingClientRect(),this.rect.width&&this.fixColumns()}setColumns(t){t||this.data.forEach((t=>{Array.from(t.cells).forEach((t=>{t.data=t.innerHTML}))})),this.options.columns&&this.headings.length&&this.options.columns.forEach((t=>{Array.isArray(t.select)||(t.select=[t.select]),t.hasOwnProperty("render")&&"function"==typeof t.render&&(this.selectedColumns=this.selectedColumns.concat(t.select),this.columnRenderers.push({columns:t.select,renderer:t.render})),t.select.forEach((e=>{const s=this.headings[e];t.type&&s.setAttribute("data-type",t.type),t.format&&s.setAttribute("data-format",t.format),t.hasOwnProperty("sortable")&&s.setAttribute("data-sortable",t.sortable),t.hasOwnProperty("hidden")&&!1!==t.hidden&&this.columns().hide([e]),t.hasOwnProperty("sort")&&1===t.select.length&&this.columns().sort(t.select[0],t.sort,!0)}))})),this.hasRows&&(this.data.forEach(((t,e)=>{t.dataIndex=e,Array.from(t.cells).forEach((t=>{t.data=t.innerHTML}))})),this.selectedColumns.length&&this.data.forEach((t=>{Array.from(t.cells).forEach(((e,s)=>{this.selectedColumns.includes(s)&&this.columnRenderers.forEach((i=>{i.columns.includes(s)&&(e.innerHTML=i.renderer.call(this,e.data,e,t))}))}))})),this.columns().rebuild()),this.render("header")}destroy(){this.dom.innerHTML=this.initialLayout,this.dom.classList.remove("dataTable-table"),this.wrapper.parentNode.replaceChild(this.dom,this.wrapper),this.initialized=!1,window.removeEventListener("resize",this.listeners.onResize)}update(){this.wrapper.classList.remove("dataTable-empty"),this.paginate(this),this.render("page"),this.links=[];let t=this.pages.length;for(;t--;){const e=t+1;this.links[t]=r(0===t?"active":"",e,e)}this.sorting=!1,this.render("pager"),this.rows().update(),this.emit("datatable.update")}paginate(){const t=this.options.perPage;let e=this.activeRows;return this.searching&&(e=[],this.searchData.forEach((t=>e.push(this.activeRows[t])))),this.options.paging?this.pages=e.map(((s,i)=>i%t==0?e.slice(i,i+t):null)).filter((t=>t)):this.pages=[e],this.totalPages=this.lastPage=this.pages.length,this.totalPages}fixColumns(){if((this.options.scrollY.length||this.options.fixedColumns)&&this.activeHeadings&&this.activeHeadings.length){let t,e=!1;if(this.columnWidths=[],this.dom.tHead){if(this.options.scrollY.length&&(e=a("thead"),e.appendChild(a("tr")),e.style.height="0px",this.headerTable&&(this.dom.tHead=this.headerTable.tHead)),this.activeHeadings.forEach((t=>{t.style.width=""})),this.activeHeadings.forEach(((t,s)=>{const i=t.offsetWidth,n=i/this.rect.width*100;if(t.style.width=`${n}%`,this.columnWidths[s]=i,this.options.scrollY.length){const t=a("th");e.firstElementChild.appendChild(t),t.style.width=`${n}%`,t.style.paddingTop="0",t.style.paddingBottom="0",t.style.border="0"}})),this.options.scrollY.length){const t=this.dom.parentElement;if(!this.headerTable){this.headerTable=a("table",{class:"dataTable-table"});const e=a("div",{class:"dataTable-headercontainer"});e.appendChild(this.headerTable),t.parentElement.insertBefore(e,t)}const s=this.dom.tHead;this.dom.replaceChild(e,s),this.headerTable.tHead=s,this.headerTable.parentElement.style.paddingRight=`${this.headerTable.clientWidth-this.dom.clientWidth+parseInt(this.headerTable.parentElement.style.paddingRight||"0",10)}px`,t.scrollHeight>t.clientHeight&&(t.style.overflowY="scroll")}}else{t=[],e=a("thead");const s=a("tr");Array.from(this.dom.tBodies[0].rows[0].cells).forEach((()=>{const e=a("th");s.appendChild(e),t.push(e)})),e.appendChild(s),this.dom.insertBefore(e,this.body);const i=[];t.forEach(((t,e)=>{const s=t.offsetWidth,a=s/this.rect.width*100;i.push(a),this.columnWidths[e]=s})),this.data.forEach((t=>{Array.from(t.cells).forEach(((t,e)=>{this.columns(t.cellIndex).visible()&&(t.style.width=`${i[e]}%`)}))})),this.dom.removeChild(e)}}}fixHeight(){this.options.fixedHeight&&(this.container.style.height=null,this.rect=this.container.getBoundingClientRect(),this.container.style.height=`${this.rect.height}px`)}search(t){return!!this.hasRows&&(t=t.toLowerCase(),this.currentPage=1,this.searching=!0,this.searchData=[],t.length?(this.clear(),this.data.forEach(((e,s)=>{const i=this.searchData.includes(e);t.split(" ").reduce(((t,s)=>{let i=!1,a=null,n=null;for(let t=0;tthis.pages.length||t<0)&&(this.render("page"),this.render("pager"),void this.emit("datatable.page",t)))}sortColumn(t,e){this.columns().sort(t,e)}insert(t){let e=[];if(i(t)){if(t.headings&&!this.hasHeadings&&!this.hasRows){const e=a("tr");t.headings.forEach((t=>{const s=a("th",{html:t});e.appendChild(s)})),this.head.appendChild(e),this.header=e,this.headings=[].slice.call(e.cells),this.hasHeadings=!0,this.options.sortable=this.initialSortable,this.render("header"),this.activeHeadings=this.headings.slice()}t.data&&Array.isArray(t.data)&&(e=t.data)}else Array.isArray(t)&&t.forEach((t=>{const s=[];Object.entries(t).forEach((([t,e])=>{const i=this.labels.indexOf(t);i>-1&&(s[i]=e)})),e.push(s)}));e.length&&(this.rows().add(e),this.hasRows=!0),this.update(),this.setColumns(),this.fixColumns()}refresh(){this.options.searchable&&(this.input.value="",this.searching=!1),this.currentPage=1,this.onFirstPage=!0,this.update(),this.emit("datatable.refresh")}clear(t){this.body&&n(this.body);let e=this.body;this.body||(e=this.dom),t&&("string"==typeof t&&(document.createDocumentFragment().innerHTML=t),e.appendChild(t))}export(t){if(!this.hasHeadings&&!this.hasRows)return!1;const e=this.activeHeadings;let s=[];const a=[];let n,r,o,h;if(!i(t))return!1;const l={download:!0,skipColumn:[],lineDelimiter:"\n",columnDelimiter:",",tableName:"myTable",replacer:null,space:4,...t};if(l.type){if("txt"!==l.type&&"csv"!==l.type||(s[0]=this.header),l.selection)if(isNaN(l.selection)){if(Array.isArray(l.selection))for(n=0;n{e.data[i]=[];const a=t.split(s.columnDelimiter);a.length&&a.forEach((t=>{e.data[i].push(t)}))})))}else if("json"===s.type){const t=(t=>{let e=!1;try{e=JSON.parse(t)}catch(t){return!1}return!(null===e||!Array.isArray(e)&&!i(e))&&e})(s.data);t&&(e={headings:[],data:[]},t.forEach(((t,s)=>{e.data[s]=[],Object.entries(t).forEach((([t,i])=>{e.headings.includes(t)||e.headings.push(t),e.data[s].push(i)}))})))}i(s.data)&&(e=s.data),e&&this.insert(e)}return!1}print(){const t=this.activeHeadings,e=this.activeRows,s=a("table"),i=a("thead"),n=a("tbody"),r=a("tr");t.forEach((t=>{r.appendChild(a("th",{html:t.textContent}))})),i.appendChild(r),e.forEach((t=>{const e=a("tr");Array.from(t.cells).forEach((t=>{e.appendChild(a("td",{html:t.textContent}))})),n.appendChild(e)})),s.appendChild(i),s.appendChild(n);const o=window.open();o.document.body.appendChild(s),o.print()}setMessage(t){let e=1;this.hasRows?e=this.data[0].cells.length:this.activeHeadings.length&&(e=this.activeHeadings.length),this.wrapper.classList.add("dataTable-empty"),this.label&&(this.label.innerHTML=""),this.totalPages=0,this.render("pager"),this.clear(a("tr",{html:`${t} | `}))}columns(t){return new l(this,t)}rows(t){return new h(this,t)}on(t,e){this.events=this.events||{},this.events[t]=this.events[t]||[],this.events[t].push(e)}off(t,e){this.events=this.events||{},t in this.events!=0&&this.events[t].splice(this.events[t].indexOf(e),1)}emit(t){if(this.events=this.events||{},t in this.events!=0)for(let e=0;e
+
+
+
Table
+
Sortable and searchable.
+
+
+
+
+
{% endblock %}
\ No newline at end of file
diff --git a/code/maintenance_entries.py b/code/maintenance_entries.py
index a85d7d39..7361bb33 100644
--- a/code/maintenance_entries.py
+++ b/code/maintenance_entries.py
@@ -42,6 +42,7 @@ def check_validity_backlog():
if r.is_redirect or r.history:
print('{} redirected to {}, {}'.format(url, r.url, r.history))
+
def create_toc(title, file, entries):
"""
@@ -697,15 +698,6 @@ class EntriesMaintainer:
print('statistics updated')
- def update_html(self):
- """
- """
- if not self.entries:
- print('entries not yet loaded')
- return
-
- print('HTML not updated')
-
def update_repos(self):
"""
export to json for local repository update of primary repos
@@ -905,7 +897,6 @@ if __name__ == "__main__":
'Clean backlog': m.clean_backlog,
'Update Readme and TOCs': m.update_readme_tocs,
'Update statistics': m.update_statistics,
- 'Update HTML': m.update_html,
'Update repository list': m.update_repos,
'Special': m.special_ops,
'Complete run': m.complete_run
diff --git a/code/utils/constants.py b/code/utils/constants.py
index 37b10d45..ed850873 100644
--- a/code/utils/constants.py
+++ b/code/utils/constants.py
@@ -7,15 +7,17 @@ import configparser
# paths
root_path = os.path.realpath(os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir))
+code_path = os.path.join(root_path, 'code')
+web_template_path = os.path.join(code_path, 'html')
entries_path = os.path.join(root_path, 'entries')
tocs_path = os.path.join(entries_path, 'tocs')
-code_path = os.path.join(root_path, 'code')
-web_path = os.path.join(root_path, 'docs')
screenshots_path = os.path.join(entries_path, 'screenshots')
-web_template_path = os.path.join(code_path, 'html')
+
+web_path = os.path.join(root_path, 'docs')
web_css_path = os.path.join(web_path, 'css')
web_js_path = os.path.join(web_path, 'js')
web_screenshots_path = os.path.join(web_path, 'screenshots')
+web_data_path = os.path.join(web_path, 'data')
private_properties_file = os.path.join(root_path, 'private.properties')
inspirations_file = os.path.join(root_path, 'inspirations.md')
diff --git a/code/utils/osg.py b/code/utils/osg.py
index 9da71b9b..5b685162 100644
--- a/code/utils/osg.py
+++ b/code/utils/osg.py
@@ -657,9 +657,13 @@ def write_screenshots_overview(overview):
text = utils.read_text(c.screenshots_file)
text = text.split('\n# ')[0] + '\n'
- for name, a in overview.items():
+ # write out each entry sorted by name
+ for name in sorted(overview.keys()):
+ a = overview[name]
t = '# {}\n\n'.format(name)
- for id, ai in a.items():
+ # write out each line sorted by id
+ for id in sorted(a.keys()):
+ ai = a[id]
if ai[-1] is None:
ai = ai[:-1]
t += ' '.join(['{:02d}'.format(id)] + [str(x) for x in ai]) + '\n'
diff --git a/docs/contribute.html b/docs/contribute.html
index 87b1a41e..04ed08fb 100644
--- a/docs/contribute.html
+++ b/docs/contribute.html
@@ -5,7 +5,7 @@
- OSGL
+ OSGL | Contributions
@@ -81,7 +81,7 @@
The content (games descriptions) is licensed CC-0.
Used icons are licensed under CC BY-SA 3.0 (Iconic or Linecons), CC0 1.0 (Simple Icons),
CC BY 4.0 (Font Awesome or IcoMoon Free), CC BY-SA 4.0 (Entypo+) or Apache License 2.0 (Material Icons).
- This website is built using Python, Lark, Jinja2 and Bulma. Last updated: 2021-10-13 14:06
+ This website is built using Python, Lark, Jinja2 and Bulma. Last updated: 2021-10-15 21:32