Whakerexa > Getting started > JavaScript

JavaScript

Overview

The JavaScript of Whakerexa is a set of ES6 modules, one class per file, with no dependency and no build step. A single module gathers them: wexa.js imports the classes of the framework, exports them, and puts them under the global Wexa. A page loads that one file and has the whole API.

Each component has its own page in this documentation, with a living demonstration. This page describes what holds them together: how the API is loaded, what it contains, when the code runs, and the conventions a new module follows.

One entry, two ways to load it

ES6 modules are refused by a browser on file://. Whakerexa therefore ships the same code twice: the modules, and wexa.bundle.js, a single file that defines window.Wexa without any import. Both give the same names to a page.

Over http, the modules. This is the form to use, and the only one under which the unit tests run.

<script type="module" src="wexa_statics/js/wexa.js"></script>

From the disk, the bundle. It is generated from the sources and is never edited by hand; see Bundle.

<script src="wexa_statics/js/wexa.bundle.js"></script>

A page that has to work both ways says neither of the two: it names the loader, which reads the protocol and takes the modules over http, the bundle from the disk. Every page of this documentation ends with this line.

<script src="wexa_statics/js/wexa.loader.js"
        data-base="wexa_statics/"></script>

One tag does the choosing, and says at the same time what the page brings: its themes, its sets of icons, the extras it loads. Everything is registered before the framework reads the address, so a theme or a set named in the address is known when it is asked for.

<script src="wexa_statics/js/wexa.loader.js"
        data-base="wexa_statics/"
        data-default="wexa_theme"
        data-links="btn-back,nav-menu"></script>
What the tag of the loader carries
AttributeWhat it says
data-baseWhere wexa_statics/ stands, seen from the page. Required.
data-defaultThe theme to apply when the address names none.
data-themesThe themes the page takes, in the order it cycles through them: a name alone chooses among those of the framework, name:path is one the page brings. Naming none of the framework's takes them all.
data-themes-baseWhere the themes of the framework stand, when they are not under css/themes/ of the base.
data-iconsThe sets the page brings, one per line, written name:path:file,file.
data-icons-defaultThe set to show when the address names none.
data-icons-fallbackThe set that answers what the others leave unanswered.
data-linksThe identifiers whose address carries the theme and the set.
data-extrasThe files to load besides wexa.js, separated by commas. Ignored on file://, where the bundle already holds them.

A path a page writes — a theme, a set of icons, an extra — is read from data-base only when it is written bare, as the files of the framework are. One written ./ or ../ is read from the page, one written / from the root of the site, one carrying a scheme from its host.

What the framework says goes to the console, and a page says how much of it it wants to hear. It is written before the loader, on the namespace: a call would come after the messages of the loading. A name as Python writes it, or a number between 0 and 50; nothing said, the level is 20.

<script>
    window.Wexa = window.Wexa || {};
    window.Wexa.logLevel = 'warning';
</script>

<script src="wexa_statics/js/wexa.loader.js" data-base="wexa_statics/"></script>

Once the page is open, Wexa.logger.setLogLevel('debug') changes it, for an author looking at what he is writing.

A page that has something of its own to start declares a function named bootPage: it is called once everything is loaded, and receives the namespace of the framework and the extras it asked for.

Two ways, and only two. Either the loader is asked for everything — it reads the protocol, registers the themes and the sets, and hands the namespace to bootPage —, or the page does the wiring itself: it names wexa.js or wexa.bundle.js, builds its ThemeManager, registers what it wants and declares its sets of icons. What must not be done is half of each: a theme registered after the loader has read the address is a name the manager has already refused.

What this is for, what it deliberately does not do, and why: logger-design.html for what the framework says, javascript-design.html for the rest.

What Wexa holds

The namespace makes a difference the framework maintains everywhere: a service that must exist exactly once is already built and answers under a lowercase name; a component a page may own several of is exposed as a class, under its own name.

Services, already built

The six singletons of Wexa
NameClassAnswers for
Wexa.logger WexaLogger The messages of the framework, filtered by level.
Wexa.onload OnLoadManager The code that waits for the page to be there, in the order it was given.
Wexa.icons IconManager The sets of icons, the one in force, and the drawing a name is answered with.
Wexa.accessibility AccessibilityManager Color mode and contrast mode, and the parameters that carry them.
Wexa.dialog DialogManager Opening and closing dialogs and video popups.
Wexa.links LinkController Elements navigating through data-href, with or without parameters.

A service is called, never instantiated.

Wexa.accessibility.switchColorScheme();
Wexa.dialog.open('dlg-about', true);
Wexa.logger.info('Page ready.');

Components, to be built

The classes exported by wexa.js
ClassWhat it doesPage
MenuManager A navigation bar, its submenus, its side and mobile states. Menus
ProgressBar A progress bar, driven by a page or by a request. Progress bar
ToggleSelector A group of checkboxes with a button that checks or clears them. Switch
AccessibilityNav The bar that switches the theme, the contrast and the color mode. Accessibility
KeyboardController The keys a page answers, and the guard that gives back the others. Accessibility
RequestManager Requests to the server that serves the page, with a JSON answer or not. —
BaseManager What a manager talking to a server inherits: the form it submits, the result it shows. —

The classes behind the services are exported too, under their own name. A page that owns several navigation bars builds them itself, and a page that needs a second dialog manager is not forbidden one.

Code that waits for the page

A component looking for an element it decorates cannot run before that element exists. Wexa.onload holds the functions that wait, and runs them in the order they were given, once the document is loaded. The framework uses it for itself: wexa.js registers there what makes every data-href element reachable with the Tab key.

Registering is enough; nothing else has to be listened to.

Wexa.onload.addLoadFunction(() => {
    const menu = new Wexa.MenuManager();
    menu.registerSubmenu('appmenu-framework', 'submenu-toggle-framework');
    menu.initMobileToggle();
});

Saying what happened

A module never writes into the console directly: it says it to the logger, which prefixes the message and drops it if it is below the level of the page. A level is a name as Python writes it, or the number that goes with it, and the default is 20 — information, warnings and errors are shown, debug messages are not. A page says its own before the loader, as above.

The levels of Wexa.logger
LevelMethodFor
10debug()What is followed while a component is being written.
20info()What a page does and succeeds at.
30warn()What is odd but has an answer.
40error()What failed, with the exception if there is one.
50critical()What stops the component from working at all.

A page asking to see everything lowers the level; 0 silences nothing, it shows all of it.

Wexa.logger.setLogLevel('debug');
Wexa.logger.debug('Slides: 57 pages laid out.');

Extras

The extras are not in Wexa: a page that does not display a bibliography has no reason to download the parser of one. They live in js/extras/ and are imported by the page that needs them. The bundle carries them all, so on file:// they are found under Wexa as well.

Five extras are available: the table of contents, the sortable table, the theme switcher, the piano keyboard and the slides.

<script type="module">
    import { Book } from '../wexa_statics/js/extras/book.js';
    import { SortaTable } from '../wexa_statics/js/extras/sortatable.js';
    import { ThemeManager } from '../wexa_statics/js/customize/theme_manager.js';

    const themes = new ThemeManager();
    themes.register('wexa_theme', '../wexa_statics/css/themes/wexa_theme.css');
    themes.register('aurora',     '../wexa_statics/css/themes/wexa_theme_aurora.css');
</script>

Components speak through events

A component that has something to announce dispatches a CustomEvent on the document. The name carries its domain first, then what happened — slides:navigate, slides:viewmode, wexa:slides:ready. This is also what a keyboard shortcut may be given instead of a function.

The shortcut says the name of the event; whoever navigates listens for it.

const keyboard = new Wexa.KeyboardController();
keyboard.register({keys: ['ArrowRight'], action: 'page:next',
                   label: 'Next page', preventsDefault: true});
keyboard.init();

document.addEventListener('page:next', () => book.nextPage());

Conventions of a module

A new file of the framework is written like the ones already there. What follows is what they all do.