Whakerexa > Extras > Slides > Pagination > Design

What this is

In short

An author writes a slide. When its page opens, the browser measures what the slide holds, and lays it out on as many slides as it takes. Nothing is hidden, nothing is shrunk, and the written document is left alone.

This page is the design of that work: what it does, what it deliberately does not do, the decisions that explain the code where it surprises, and the classes that carry it. Read it before reporting a behaviour as a bug: several of them are choices.

Where it comes from

From one requirement of the bibliography: a bibliography is shown whole in a presentation. A bibliography is built for a page, where it has all the room it needs, and a slide has one screen.

Reading the operations of the bibliography showed that this is not the operation that composes references: it is the one that lays a composed document on surfaces of fixed height. Printing is the other case of it. On paper, the browser does the work. On a slide, nobody does.

So the work belongs to the slides, not to the bibliography, and it holds for any content that is too tall, a bibliography being only what revealed it.

What it does not do

  • It hides nothing, and shrinks nothing. A content that does not fit takes one slide more.
  • It never cuts inside a block: a table row, a list item and a paragraph are laid down whole.
  • It does not touch the document the author wrote.
  • It asks nothing of the internet, and keeps nothing from one opening to the next.

What it has to do

What the slides have to do
No.Requirement
S1The author writes a content in one piece, and never writes the slides it takes.
S2A content taller than one slide is laid on as many as it takes.
S3Nothing is hidden and nothing is shrunk: what does not fit takes one slide more.
S4A block is laid down whole: a table row, a list item, a paragraph are never cut inside.
S5The document the author wrote is left as it is.
S6A block taller than a slide on its own is shown all the same, and what could not be laid down is said in the console.
S7The reader stands at one slide at a time, and where he stands is kept when the way of showing changes.
S8The supports are shown one at a time, all at once, each with what is said about it, or one after the other for the hand.
S9The author writes about a slide what the audience does not see.
S10A reading stands at a slide that exists, and a slide that shows nothing of what the reader looks for is never the one it stands at.
S11What can be done with a mouse can be done without one.
S12Nothing is asked of the internet, and nothing is kept from one opening to the next.
S13A presentation is read where it is opened, served or taken from a disk.
S14Printing lays the content on the pages, the browser doing there what the program does on a screen.

S1, S2, S3 and S4 come from one requirement of the bibliography: a bibliography is shown whole in a presentation (bibliography-design.html).

Not in the first version

Laying out again when the window is resized, or when the contrast mode changes the font sizes. The need is established, the first version lays out once, when the page opens.

Decisions

Where the cut is made

Between two blocks. A block is the smallest part that can be laid down on its own: a row for a table, an item for a list, a child otherwise.

One class knows this, SlideBlockReader. Everywhere else, a block is a block.

A container that holds a table or a list is read through: what makes it tall is inside it, and a bibliography written in a slide is exactly that, a container holding a table. A container that holds neither stays whole: what was written side by side is not taken apart.

A block taller than a slide

It stays whole, on its own slide, and overflows. A message says it in the console, for whoever wrote it.

The program cannot cut inside a block without deciding in the author's place. An overflow is seen; an arbitrary cut is not.

The title

A title is not a block, and is never distributed: it stays on the slide the author wrote it on, and is not written again on the slides that continue it.

The notes written for a slide stay with the first one.

The measure

In the slide itself, at its width and its font sizes, and slide by slide. A height measured anywhere else is worth nothing: the font sizes of a slide are not those of a page.

A slide only has the height of a slide once the body wears the class of the view. The layout therefore comes after the view is set, and before the slides are counted: measured before, a slide is as tall as what it holds, and nothing ever overflows.

When the work is done

Once, when the page opens, after the contents the program builds, and before the slides are counted and the overview is built.

That is the only waiting point: measuring a table that does not exist yet measures nothing. Counting before laying out would give wrong numbers.

A place reached, and what it does not touch

On a page, reaching a place brings it under the eyes and leaves the reading there: the document is longer than the screen, so where the reader stands inside it has to be said. A slide is not longer than the screen. The support is shown whole, and the place is under the eyes as soon as that support is the one shown.

So nothing inside the support is touched: the place takes no focus, and the support is not scrolled. Moving to what is already seen gains nothing, and it costs the rendering: the supports are laid where they are by the view, and a browser going to a place inside one of them lays them over one another.

That is not the whole of it. The supports are laid over one another all the same when a renvoi of a bibliography is followed, and the cause has not been found. The two renvoi are not offered in a presentation, which Op3 says, and this is a requirement left unsatisfied rather than a decision.

The author writes nothing more

One slide of references, and the program makes of it what it takes. No attribute, no class, no cutting by hand.

The classes

What carries the data

SlideBlock carries an element, its place in the order the content is written, and its height once measured. Its only question is whether it fits in a given room, which compares two numbers.

SlideLayout carries the result: the blocks of each slide, in order. It says how many slides are asked for, and which blocks are taller than a slide on their own.

What does the work

  • SlideBlockReader reads a written slide and gives its blocks, in order. It is the only one that knows what the smallest part is.
  • SlideMeasure measures. It is the only one that reads the rendering, and it decides nothing.
  • SlidePaginator lays out. It knows neither the document nor the browser: it receives heights and gives a SlideLayout back.
  • SlideComposer engenders the slides and moves the blocks. It is the only one that changes the document shown.
  • SlidesPagination does the work in order, and is the only one that catches an error.

Names

Every class carries Slide. The code goes into a shared library, where Block or Layout would be claimed by any other part of it. No class carries the name of the support: in this layer, the support is a slide.

What the classes promise

  • SlidePaginator knows neither the document nor the browser.
  • SlideMeasure is the only one that measures.
  • SlideBlockReader is the only one that knows what a smallest part is.
  • SlideComposer is the only one that changes the document shown.
  • The document the author wrote is never modified.

When the page opens

In order

  1. The assembly waits for the contents the program builds, the bibliography being one of them.
  2. The body wears the class of the view, so that a slide has the height of a slide.
  3. Each written slide is read: its blocks, in order.
  4. Each block is measured, then the room the slide leaves them: what it holds, minus the title, the head of a table, the margins of what holds them, and the line of the page counter.
  5. The blocks are laid out: one slide more as soon as the next one does not fit.
  6. What could not be laid down is said in the console.
  7. The slides that are missing are engendered, and the blocks are moved.
  8. The slides are counted, and the overview is built.

Steps 2 to 5 happen even when nothing overflows. A presentation whose slides all fit is measured, laid out, and comes out unchanged.

Nothing is kept

The same content, in the same window, gives the same layout. Nothing is stored, and the written document is not modified: reopening the page does the whole work again.

When something goes wrong

What is missing What happens
A content the program builds does not build The layout is done on what exists
A block is taller than a slide It stays whole, on its slide, and a message goes to the console
JavaScript None of this happens: the slide keeps all its content, and overflows

An error in JavaScript must never do worse than the absence of JavaScript. That is the worst the layout is allowed to produce.

How this was designed

The way, and why it is written here

Design first, then the tests, then the code. The design is a Merise analysis, from the communication between the actors down to where the code goes, then the UML diagrams that lead to it. It is written here, in full, so that whoever picks the work up has everything on one page.

The working notes are in notes/slides/. This chapter says the same thing, and is the one that is kept up to date.

MCC: who exchanges what

ActorWhat it is
The authorWrites the slides and the data the program formats. She does not write the supports
The readerAsks for the page, then watches the presentation. He never asks for the layout
The programRuns in the browser of whoever opens the page, and lays the content out
The browserRenders the document. On paper it lays the content on the pages. On a slide it lays nothing
FlowFromToWhat
F1The authorThe documentThe written content, in one piece
F2The readerThe programThe request for the page
F3The programThe documentThe supports, and the content laid on them
F4The programThe authorWhat could not be laid down, in the console
F5The browserThe readerThe presentation shown
F6The browserThe paperThe document laid on the pages, when printing

No data leaves the document: nothing is asked of a server, nothing is stored, nothing is sent back.

MCD: what the data are made of

Four entities.

  • SUPPORT, known by its rank in the document. It has a height and a width, both fixed. Paper and slide are two forms of it.
  • WRITTEN CONTENT, what the author writes in one piece. It has a title, when it has one.
  • BLOCK, which exists only inside a written content. It has its place in the order the content is written, and is the smallest part that can be laid down whole.
  • READING, where the reader stands: the support shown, and how far the reader has gone inside it. There is one, and only one, for a presentation being read.
  • VIEW, the way the supports are shown: one at a time, all at once, each with what is said about it, or all one after the other for the hand. There is one at a time, and the reading is kept whichever it is.
  • NOTE, what the author writes about a support and does not show to the audience. A support has one, or none.
AssociationLeg 1Leg 2
is made ofWRITTEN CONTENT (1,n)BLOCK (1,1)
is laid onBLOCK (1,1)SUPPORT (0,n)
is rendered byWRITTEN CONTENT (1,n)SUPPORT (1,1)
stands atREADING (1,1)SUPPORT (0,n)
is shown inREADING (1,1)VIEW (0,n)
is said aboutNOTE (1,1)SUPPORT (0,1)

Two things are computed, never stored: the height of a block, which is only known once it is laid in a support, and the number of supports a content takes.

Three rules the keys do not hold: the blocks are laid in the order they are written; the blocks of a support fit in its height, unless a block is taller than the support on its own; two blocks of a content never share a place.

Two rules more, for the reading: it stands at a support that exists, never outside the presentation; and a support that shows nothing of what a reader is looking for is never the one it stands at, which is what makes an internal link an event of its own.

MCT: the three operations

Op1, lay a written content on a paginated support.

Triggered when: the document is rendered on a support of fixed height, and the written content is there, whole.

Does: measures the blocks at the width of the support; lays them in the order they are written, as long as they fit; goes to the next support when the next block no longer fits.

ProducesWhen
The content laid on one or more supportsAlways
A message in the consoleWhen a block is taller than the support on its own

Op1 formats nothing and changes no content: it only lays it down.

SupportWho runs Op1What is left to do
PaperThe browser, when it printsNothing
SlideNobodyOp1

Op2, move the reading.

Triggered when: the reader asks to move — to the next support, to the previous one, to the first, to the last, or to a support named by rank.

Produces: the reading stands at the support asked for, and that support is shown. A move asked for beyond the first or the last support produces nothing: the reading does not leave the presentation.

Op3, reach a place in the document.

Triggered when: the reader follows a renvoi to a place written in the same document. Every renvoi, whoever wrote it: a table of contents, a note, and the two of a bibliography, which name a reference and the places it is cited in — Op7 of the bibliography produces such a request and nothing else.

Produces: that place is shown and read. What it takes to show it depends on the support, and that is the only thing that does: on a page, the place is brought under the eyes; in a presentation, the support carrying it becomes the support the reading stands at, and nothing inside that support is touched, a support being shown whole; on paper, nothing is produced, everything being laid down already.

Not satisfied, and the bug is not found: the two renvoi of a bibliography are not offered in a presentation. Following one lays the supports over one another and leaves them half drawn. What is asked for is right — the support carrying the place is the one the reading stands at, and nothing inside that support is touched — and what undoes the rendering has not been found, in either direction. The links are written as they are on a page, and hidden where a slide carries them, so showing them again is all it takes to look for it once more. C26 and C27 of the bibliography hold on a page and on paper, and not here.

Op3 is not Op2 with another trigger. Op2 receives a support and moves to it; Op3 receives a place and does not know, when it starts, which support carries it. In a presentation Op3 ends by asking Op2 for the support it found, which is the only tie between them.

Op4, change the view.

Triggered when: the reader asks for another way of showing the supports.

Produces: the supports shown that way, and the reading unchanged. A view that shows them all says which one the reading stands at; leaving it for one support at a time shows that one.

Op5, consult the shortcuts.

Triggered when: the reader asks what can be asked for.

Produces: the list of what the reader can ask, and the presentation untouched behind it. Closing it gives the reading back where it was.

What triggers a request is not part of these operations. The same request comes from a key, from a button, or from a finger drawn across the screen, and the operation is the same one: three ways in, one treatment. This is why what listens to a key knows nothing of the reading, and what holds the reading knows nothing of the keys.

Three requirements are not operations: nothing is kept from one opening to the next, the author writes nothing more than today, and a content laid on a support is never cut inside a block.

MOT: where, when, and by whom

In the browser of whoever opens the page. Three procedures. PF1 lays a written content on as many supports as it takes: once, when the page is asked for, with no human intervention. PF2 moves the reading, and PF3 reaches a place in the document: both on a request from the reader, as often as asked.

PFWhat it doesWhenWhere it comes out
PF1Lays the content on the supportsRequest for the pagePage and console
PF2Moves the readingRequest in the pagePage, and the position said in the address
PF3Reaches a place in the documentRequest in the pagePage, through PF2 when the support is not the one shown
PF4Changes the viewRequest in the pagePage
PF5Shows what can be asked forRequest in the pagePage

A request in the page comes from a key, a button or a finger. Which one it is changes nothing to the procedure: the way in is written apart from what it triggers, so that a way may be added without a treatment being touched.

Op1 measures, so it needs the contents the program builds to exist, the bibliography being one of them. It happens after them, and before the supports are counted.

What comes out: the supports and what they carry, in the page; the block taller than the support, in the console. Nothing is kept from one opening to the next.

MLD: the tables

  • WRITTEN_CONTENT (content_id, title)
  • BLOCK (content_id, place, support_rank)
  • SUPPORT (rank, height, width)
  • READING (rank_of_the_support, step, view)
  • NOTE (rank_of_the_support, text)

A block exists only in the content where it is written, so it carries its identifier, and its place tells it from the others. A support is known by its rank: two supports differ by nothing else.

Not in the tables: the height of a block, and the number of supports a content takes. Both are measured.

MPD: where each table exists

TableIn the sourceWhile runningOn screen
WRITTEN_CONTENTa slide written by the authorthe element itselfthe first slide of the series
BLOCKwhat it holds: a child, a table row, a list itemthat same element, in document orderlaid in one of the slides
SUPPORTnothinga slide, written or engendereda slide
READINGnothingthe rank and the step held by the programthe slide shown, and the position written in the address
VIEWnothinga name held beside the readingthe way the supports are laid out
NOTEan aside written next to the slide it speaks ofthat same elementnothing, unless the view asks for it

The reading is the only table written outside the program: it is said in the address of the page, after the sharp sign, as the rank of the support and the step inside it. That place holds the reading and nothing else, so that an address, once copied, opens the presentation where it was left. A place named in a renvoi is an entry of Op3 and is never written there: it would take the place of a reading that could no longer be read.

Nothing more is written in the source than today: that is what the requirement asks. The title stays on the slide it was written on.

Two choices the requirement does not determine: the cut is made between blocks, never inside one; a table that is split keeps its head on every support.

MOpT: where the code goes

ModuleWhat it does
measureGives the room a support leaves, and the height of a block laid in it
layoutSays which blocks go on which support, from heights it is given
composeEngenders the supports and puts the blocks in them
navigationHolds the reading and moves it, and says it in the address
reachingFrom a place named in a renvoi, finds the support carrying it and asks the navigation for it
viewHolds which view is in force, and changes it
showingDraws the supports the way the view in force asks: one at a time, all at once, or each with its note
ways inTurns a key, a click or a swipe into the request it means, and knows nothing of what it triggers
shortcutsShows what can be asked for, and gives the reading back when it closes

Under wexa_statics/js/extras/slides/, one file per class. The layout module touches no document: it receives heights and gives a distribution back, which is what makes it testable. The navigation module is testable for the same reason: it reads and writes the reading, and nothing else.

UML: the states of a written slide

StateWhat is true
WrittenIt carries all its content. Nothing has been read or measured
ReadIts blocks are known, in order. Nothing has moved
MeasuredEvery block carries its height, measured in the slide
Laid outWhich block goes on which slide is known. Nothing has moved yet
Laid downThe missing slides exist, every block is on its own

No transition comes from the reader: everything is finished before he sees the presentation. Moving from one slide to another, changing view, opening a reference, printing: none of them changes a state.

UML: the sequence when the page opens

  1. The browser asks the assembly to build the presentation.
  2. The assembly waits for the promise of the bibliography.
  3. It puts the class of the view on the body: a slide measured before that is as tall as what it holds.
  4. It asks SlidesPagination.run(), which gives a promise back.
  5. SlideBlockReader.blocks(slide), once per written slide.
  6. SlideMeasure.measure(blocks, slide), then SlideMeasure.room(slide, blocks): the room left is what the slide holds, minus what surrounds the blocks.
  7. SlidePaginator.paginate(blocks, room) gives the SlideLayout.
  8. The blocks taller than a slide are said in the console.
  9. SlideComposer.compose(slide, layout), when the layout asks for more than one slide, with the head of a table on each.
  10. The promise is kept, and the assembly counts the slides and builds the overview.

The last step comes last on purpose: counting before laying out would give wrong numbers and an incomplete overview.

The error policy

LevelWhat happensWhat the program does
1Nothing can be done: the slide does not exist, its room cannot be measuredAn exception, caught by SlidesPagination, said in the console
2One slide is concerned: a block taller than itNo exception. The block stays whole and overflows
3The author has to knowNo exception. A message in the console, and the work goes on

Exceptions are raised at the lowest level and caught at the highest. Only SlidesPagination catches. No other class writes a try. Three of them exist: PaginationError, the mother, MissingSlide and UnmeasurableSlide.

The contracts, class by class

How to read them

+ public, - private, / derived and never stored, {readOnly} does not change after construction, {ordered} the order matters. pre is what has to be true on the way in, and what the operation does not check; post is what the caller can count on. The types are the ones written in the JSDoc.

The attributes are set at construction, except the height of a block, which is measured afterwards: it is the only thing a data class learns later.

The data classes

SlideBlock
OperationSignatureContract
constructor(element, place)pre: element is not null, place is 1 or more. post: height is 0, nothing has measured yet
get height(): Numberpost: 0 until something measures
set height(value: Number)pre: value is 0 or more. Written by SlideMeasure, by nobody else
fitsIn(room: Number): Booleanpre: room is 0 or more. post: true when height does not exceed room

fitsIn() knows neither slide nor document: it compares two numbers.

SlideLayout
OperationSignatureContract
constructor(parts)pre: at least one part. A part is empty only if it is the only one: a slide holding nothing but its title has nothing to lay out. post: the object does not change any more
get parts(): SlideBlock[][]post: in the order the content is written
count(): Numberpost: 1 or more. 1 means nothing is to be engendered
oversized(room: Number): SlideBlock[]post: the blocks that exceed room on their own. May be empty

The working classes

OperationSignatureContract
SlideBlockReader.blocks(slide: HTMLElement): SlideBlock[]pre: slide is not null. post: in document order, place from 1 to n. Empty when the slide holds nothing but its title
SlideMeasure.room(slide: HTMLElement, blocks: SlideBlock[]): Numberpre: slide is a slide of the document, its blocks are measured. post: the height left to the blocks, everything that surrounds them deducted: the title, the head of a table, the margins of what holds them, and the line of the page counter. Raises MissingSlide or UnmeasurableSlide
SlideMeasure.measure(blocks: SlideBlock[], slide: HTMLElement): voidpre: the blocks are those of that slide. post: every block carries its height, measured in the slide
SlidePaginator.paginate(blocks: SlideBlock[], room: Number): SlideLayoutpre: every block carries its height, room is more than 0. post: every block is placed, once, in order; one part more as soon as the next block does not fit; a block taller than room is alone on its part; without a block, one empty part
SlideComposer.compose(slide: HTMLElement, layout: SlideLayout): HTMLElement[]pre: the layout comes from the blocks of that slide. post: the slides, the original one first; every block in its own; nothing is lost
SlideComposer.repeatHead(table: HTMLTableElement, next: HTMLElement): voidpre: the table is split. post: every part carries the head
SlidesPagination.run(): Promise<void>post: every written slide is laid down; never raises; kept when everything is done
NavigationLogic.updateFromHash(fragment: String): voidpre: none. post: the reading stands at the position the fragment holds; an empty fragment gives the first support; a fragment that is not a position leaves the reading where it is, being the name of a place and not a position. Never writes the address
SlideReaching.supportOf(place: String): Numberpre: none. post: the rank of the support carrying that place, 0 when no element bears that name or when it is carried by no support
SlideReaching.reach(place: String): Numberpre: none. post: the reading stands at the support carrying that place, which is shown whole; nothing inside that support is touched, neither what the keyboard stands on nor what is scrolled; 0 and nothing done when no support carries it

The title is never a block: it is repeated on every engendered slide, so it is not to be laid out. The notes stay with the first slide.

What is tested, and where

Without a browser

SlidePaginator receives heights and gives a layout back, so every rule of the work is checked without opening anything: everything fits on one slide, one slide more as soon as the next block does not fit, no block lost or placed twice, a block taller than a slide left whole, and the same result twice.

With a document

SlideBlockReader is checked on written markup: a table read row by row, its head left out, a list read item by item, the title and the notes left out.

The tests are in wexa_statics/js/tests/, one file per class, and are run by tests.html.