The site runs on an Apache server using a MySQL database. Pages are pre-built using Perl and PHP into HTML or SHTML. No dynamic content is served except for registering/signing in. Additionally, Varnish caching sits in front of Apache.
All directories that are paralleled in the site structure sit at the root - other directories are inside /_etc.
Substantial URL-rewriting is performed:
- /[section]/[p] is rewritten to /[section]/page.shtml?p=[p]
- /[section]/[page]/[p] is rewritten to /[section]/[page].shtml?p=[p]
An .htaccess file sits at the root to take care of these rewrites. In production, this file is included into the virtual host configuration. This allows us to forbid all overrides and (marginally) improves performance.
A 'stateful' page is maintained throughout the user's session. This complicates things somewhat but has several advantages:
- The various javascript files do not have to be reloaded and initiated for every new page.
- Some aspects of the user experience, such as code browsing, can be independent of page browsing.
- Smaller chunks of html are requested when the user views a new page.
- We are able to pre-process HTML content through client-side scripting while avoiding flashes of unstyled content (not implemented).
- We are able to cache and, where desirable, pre-load content.
All links are standard http links. When new content is loaded, the JS functions in NB.Nav.links cancel the default click behaviour and bind the click event to NB.Nav.links.internal. This sends the requested url to NB.Nav.fetch which, in turn, makes an AJAX request for the page, specifying that we are not requesting the entire HTML page ("?scope=page").
On the back-end /etc/layouts/render.shtml uses the scope parameter to determine how much of the page should be returned.
Forward and back button clicks are detected by jQUery.hashchange and changes are passed on to NB.Nav.fetch as above.
When new content is loaded, an event is trigerred to notify any page elements that might need updating.
Content is cached at three levels:
- Page elements retrieved through AJAX, as described above, are stored inside an array (NB.cache) and re-used.
- Server-side script output is stored as SHTML content in /cache. A URL rewrite takes care to redirect requests to the generating script when the HTML content does not exist in /cache. Scripts that receive new content (notes, comments, edits, etc) ensure that stale content in /cache is refreshed.
- Varnish provides an additional layer of caching between Apache and the client.
All content has parameters and values incorporated into the file name and is set to expire after ten minutes. Assets have unique version numbers and expire after at least two months. In addition, cookies are only used client-side (except for registering/signing in, which is lazy-loaded into the page). Identical content is served to all users on all browsers.
Raw images are stored at /_assets/images/raw. Images are requested from /_assets/images/cut. A URL rewrite checks whether the image exists; if not, it redirects to /_assets/images/static/serve.php. If /_assets/images/templates contains an image cropped at the requested aspect ratio then an image with the requested dimensions is derived from it. If no such template exists then a new one is guessed from the raw image.
Notes are currently imported from Evernote. Although it would be possible to use other capturing tools, Evernote offers a variety of methods for capturing notes, images, URLs and sounds - all dated, versioned, tagged and geo-tagged.
Notes are stored in a simple un-normalised model to parallel that of Evernote's. Four tables in the database are relevant: notes, tags, resources, lookup.
Bibliography, Links and Topics are modelled as Notes and simply rely on customised views to differentiate their presentation.
The Enface/Anagram system used to edit and monitor Wutz works as follows:
- The table character_count contains a static negative count of each letter's frequency in the German-language text as well as a row that stores the count for each character in every paragraph. An SQL query for the SUM of a given character therefore returns the difference in the occurrence of a character:
Difference = (FrequencyDE * -1) + SUM[frequencyEN]
- When a page (consisting of several paragraphs) of Wutz is loaded, the array NB.Anagram.anagram is populated. This is a list of the frequency difference between the German-language and English-language texts for each letter and numeral.
- When a user enters a new letter, the function NB.Anagram.letter checks whether the new letter is desirable (decreases the difference) or not, and indicates accordingly.
- The new total is calculated by the function NB.Anagram.recalculate_paragraph. First it discounts the differences for the current paragraph and then recalculates them and updates the array NB.Anagram.anagram. (Merely recalculating according to the new letter would not account for text pasted in.)
- When a paragraph is saved, the current total and the improvement achieved by the new paragraph is also sent to the server.
- The entry for each letter/paragraph is updated in the table character_count.