Move HTML generation back to JavaScript.

- Functionality maintained
- File operations removed from server
- Browsers should again be able to cache the tutorial
This commit is contained in:
Chris Wilson 2020-08-14 20:09:59 -04:00
parent 87ea87a2e9
commit b47e8cbbf2
5 changed files with 53 additions and 29 deletions

View File

@ -10,7 +10,6 @@ from flask import Flask, request, redirect, url_for, render_template, Response,
from flask_caching import Cache from flask_caching import Cache
from flaskext.autoversion import Autoversion from flaskext.autoversion import Autoversion
from flask_compress import Compress from flask_compress import Compress
import markdown
from .models import * from .models import *
@ -76,14 +75,8 @@ def register_session():
@app.route('/tutorial') @app.route('/tutorial')
@app.route('/tutorial/<string:lang>') @app.route('/tutorial/<string:lang>')
@cache.memoize(timeout=300) # update every 300 seconds
def tutorial(lang='en'): def tutorial(lang='en'):
try: return render_template(f"tutorial.html", lang=lang)
md_file = open(os.path.join('WebHostLib', 'tutorial', f"tutorial_{lang}.md"), encoding="utf-8-sig")
except FileNotFoundError:
return render_template("tutorial.html", tutorial='The tutorial is not available in that language yet, sorry.')
else:
return render_template("tutorial.html", tutorial=markdown.markdown(md_file.read()))
@app.route('/seed/<suuid:seed>') @app.route('/seed/<suuid:seed>')

View File

@ -5,4 +5,3 @@ flask-caching>=1.9.0
Flask-Autoversion>=0.2.0 Flask-Autoversion>=0.2.0
Flask-Compress>=1.5.0 Flask-Compress>=1.5.0
Flask-Limiter>=1.3.1 Flask-Limiter>=1.3.1
markdown>=3.2.2

View File

@ -1,15 +1,48 @@
window.addEventListener('load', () => { window.addEventListener('load', () => {
const headers = Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6')); const tutorialWrapper = document.getElementById('tutorial-wrapper');
const scrollTargetIndex = window.location.href.search(/#[A-z0-9-_]*$/); new Promise((resolve, reject) => {
for (let i=1; i < headers.length; i++){ const ajax = new XMLHttpRequest();
const headerId = headers[i].innerText.replace(/[ ]/g,'-').toLowerCase() ajax.onreadystatechange = () => {
headers[i].setAttribute('id', headerId); if (ajax.readyState !== 4) { return; }
headers[i].addEventListener('click', () => if (ajax.status === 404) {
window.location.href = window.location.href.substring(0, scrollTargetIndex) + `#${headerId}`); reject("Sorry, the tutorial is not available in that language yet.");
} return;
}
if (ajax.status !== 200) {
reject("Something went wrong while loading the tutorial.");
return;
}
resolve(ajax.responseText);
};
ajax.open('GET', `${window.location.origin}/static/assets/tutorial/tutorial_` +
`${tutorialWrapper.getAttribute('data-language')}.md`, true);
ajax.send();
}).then((results) => {
// Populate page with HTML generated from markdown
tutorialWrapper.innerHTML = (new showdown.Converter()).makeHtml(results);
if (scrollTargetIndex > -1) { // Reset the id of all header divs to something nicer
const scrollTarget = window.location.href.substring(scrollTargetIndex + 1); const headers = Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6'));
document.getElementById(scrollTarget).scrollIntoView({ behavior: "smooth" }); const scrollTargetIndex = window.location.href.search(/#[A-z0-9-_]*$/);
} for (let i=0; i < headers.length; i++){
const headerId = headers[i].innerText.replace(/[ ]/g,'-').toLowerCase()
headers[i].setAttribute('id', headerId);
headers[i].addEventListener('click', () =>
window.location.href = window.location.href.substring(0, scrollTargetIndex) + `#${headerId}`);
}
// Manually scroll the user to the appropriate header if anchor navigation is used
if (scrollTargetIndex > -1) {
try{
const scrollTarget = window.location.href.substring(scrollTargetIndex + 1);
document.getElementById(scrollTarget).scrollIntoView({ behavior: "smooth" });
} catch(error) {
console.error(error);
}
}
}).catch((error) => {
tutorialWrapper.innerHTML =
`<h2>${error}</h2>
<h3>Click <a href="${window.location.origin}/tutorial">here</a> to return to safety.</h3>`;
});
}); });

View File

@ -55,13 +55,11 @@ the players folder and is called `easy.yaml`
### Your YAML file is weighted ### Your YAML file is weighted
Throughout your YAML file, you will see many options which look similar to this: Throughout your YAML file, you will see many options which look similar to this:
<pre> ```yaml
```
map_shuffle: map_shuffle:
on: 5 on: 5
off: 15 off: 15
``` ```
</pre>
In the above example, imagine the generator creates a bucket labelled "map_shuffle", and places a folded In the above example, imagine the generator creates a bucket labelled "map_shuffle", and places a folded
piece of paper into the bucket for each sub-option. Here, there are twenty pieces of paper in the bucket: piece of paper into the bucket for each sub-option. Here, there are twenty pieces of paper in the bucket:
five for "on" and fifteen for "off". When the generator is deciding whether or not to turn on map shuffle five for "on" and fifteen for "off". When the generator is deciding whether or not to turn on map shuffle
@ -83,8 +81,7 @@ which do not affect gameplay. These options are also weighted, in case you want
of your hearts or by the silliness of your overworld palette. of your hearts or by the silliness of your overworld palette.
If you would like to add a sprite to the list, simply include its name and give it a weight like so: If you would like to add a sprite to the list, simply include its name and give it a weight like so:
<pre> ```yaml
```
rom: rom:
sprite: # Enter the name of your preferred sprite and weight it appropriately sprite: # Enter the name of your preferred sprite and weight it appropriately
random: 0 random: 0
@ -95,7 +92,6 @@ rom:
rocko: 5 rocko: 5
luigi: 3 luigi: 3
``` ```
</pre>
### Verifying your YAML file ### Verifying your YAML file
If you would like to validate your YAML file to make sure it works, you may do so on the If you would like to validate your YAML file to make sure it works, you may do so on the

View File

@ -3,11 +3,14 @@
{% block head %} {% block head %}
<title>Setup Tutorial</title> <title>Setup Tutorial</title>
<link rel="stylesheet" type="text/css" href="{{ static_autoversion("styles/tutorial.css") }}" /> <link rel="stylesheet" type="text/css" href="{{ static_autoversion("styles/tutorial.css") }}" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/1.9.1/showdown.min.js"
integrity="sha512-L03kznCrNOfVxOUovR6ESfCz9Gfny7gihUX/huVbQB9zjODtYpxaVtIaAkpetoiyV2eqWbvxMH9fiSv5enX7bw=="
crossorigin="anonymous"></script>
<script type="application/ecmascript" src="{{ static_autoversion("assets/tutorial.js") }}"></script> <script type="application/ecmascript" src="{{ static_autoversion("assets/tutorial.js") }}"></script>
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<div id="tutorial-wrapper" class="main-content"> <div id="tutorial-wrapper" class="main-content" data-language="{{ lang }}">
{{ tutorial|safe }} <!-- Content generated by JavaScript -->
</div> </div>
{% endblock %} {% endblock %}