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
| No. | Requirement |
|---|---|
| S1 | The author writes a content in one piece, and never writes the slides it takes. |
| S2 | A content taller than one slide is laid on as many as it takes. |
| S3 | Nothing is hidden and nothing is shrunk: what does not fit takes one slide more. |
| S4 | A block is laid down whole: a table row, a list item, a paragraph are never cut inside. |
| S5 | The document the author wrote is left as it is. |
| S6 | A 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. |
| S7 | The reader stands at one slide at a time, and where he stands is kept when the way of showing changes. |
| S8 | The 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. |
| S9 | The author writes about a slide what the audience does not see. |
| S10 | A 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. |
| S11 | What can be done with a mouse can be done without one. |
| S12 | Nothing is asked of the internet, and nothing is kept from one opening to the next. |
| S13 | A presentation is read where it is opened, served or taken from a disk. |
| S14 | Printing 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
SlideBlockReaderreads a written slide and gives its blocks, in order. It is the only one that knows what the smallest part is.SlideMeasuremeasures. It is the only one that reads the rendering, and it decides nothing.SlidePaginatorlays out. It knows neither the document nor the browser: it receives heights and gives aSlideLayoutback.SlideComposerengenders the slides and moves the blocks. It is the only one that changes the document shown.SlidesPaginationdoes 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
SlidePaginatorknows neither the document nor the browser.SlideMeasureis the only one that measures.SlideBlockReaderis the only one that knows what a smallest part is.SlideComposeris the only one that changes the document shown.- The document the author wrote is never modified.
When the page opens
In order
- The assembly waits for the contents the program builds, the bibliography being one of them.
- The body wears the class of the view, so that a slide has the height of a slide.
- Each written slide is read: its blocks, in order.
- 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.
- The blocks are laid out: one slide more as soon as the next one does not fit.
- What could not be laid down is said in the console.
- The slides that are missing are engendered, and the blocks are moved.
- 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
| Actor | What it is |
|---|---|
| The author | Writes the slides and the data the program formats. She does not write the supports |
| The reader | Asks for the page, then watches the presentation. He never asks for the layout |
| The program | Runs in the browser of whoever opens the page, and lays the content out |
| The browser | Renders the document. On paper it lays the content on the pages. On a slide it lays nothing |
| Flow | From | To | What |
|---|---|---|---|
| F1 | The author | The document | The written content, in one piece |
| F2 | The reader | The program | The request for the page |
| F3 | The program | The document | The supports, and the content laid on them |
| F4 | The program | The author | What could not be laid down, in the console |
| F5 | The browser | The reader | The presentation shown |
| F6 | The browser | The paper | The 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.
| Association | Leg 1 | Leg 2 |
|---|---|---|
| is made of | WRITTEN CONTENT (1,n) | BLOCK (1,1) |
| is laid on | BLOCK (1,1) | SUPPORT (0,n) |
| is rendered by | WRITTEN CONTENT (1,n) | SUPPORT (1,1) |
| stands at | READING (1,1) | SUPPORT (0,n) |
| is shown in | READING (1,1) | VIEW (0,n) |
| is said about | NOTE (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.
| Produces | When |
|---|---|
| The content laid on one or more supports | Always |
| A message in the console | When a block is taller than the support on its own |
Op1 formats nothing and changes no content: it only lays it down.
| Support | Who runs Op1 | What is left to do |
|---|---|---|
| Paper | The browser, when it prints | Nothing |
| Slide | Nobody | Op1 |
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.
| PF | What it does | When | Where it comes out |
|---|---|---|---|
| PF1 | Lays the content on the supports | Request for the page | Page and console |
| PF2 | Moves the reading | Request in the page | Page, and the position said in the address |
| PF3 | Reaches a place in the document | Request in the page | Page, through PF2 when the support is not the one shown |
| PF4 | Changes the view | Request in the page | Page |
| PF5 | Shows what can be asked for | Request in the page | Page |
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
| Table | In the source | While running | On screen |
|---|---|---|---|
| WRITTEN_CONTENT | a slide written by the author | the element itself | the first slide of the series |
| BLOCK | what it holds: a child, a table row, a list item | that same element, in document order | laid in one of the slides |
| SUPPORT | nothing | a slide, written or engendered | a slide |
| READING | nothing | the rank and the step held by the program | the slide shown, and the position written in the address |
| VIEW | nothing | a name held beside the reading | the way the supports are laid out |
| NOTE | an aside written next to the slide it speaks of | that same element | nothing, 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
| Module | What it does |
|---|---|
| measure | Gives the room a support leaves, and the height of a block laid in it |
| layout | Says which blocks go on which support, from heights it is given |
| compose | Engenders the supports and puts the blocks in them |
| navigation | Holds the reading and moves it, and says it in the address |
| reaching | From a place named in a renvoi, finds the support carrying it and asks the navigation for it |
| view | Holds which view is in force, and changes it |
| showing | Draws the supports the way the view in force asks: one at a time, all at once, or each with its note |
| ways in | Turns a key, a click or a swipe into the request it means, and knows nothing of what it triggers |
| shortcuts | Shows 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
| State | What is true |
|---|---|
| Written | It carries all its content. Nothing has been read or measured |
| Read | Its blocks are known, in order. Nothing has moved |
| Measured | Every block carries its height, measured in the slide |
| Laid out | Which block goes on which slide is known. Nothing has moved yet |
| Laid down | The 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
- The browser asks the assembly to build the presentation.
- The assembly waits for the promise of the bibliography.
- It puts the class of the view on the body: a slide measured before that is as tall as what it holds.
- It asks
SlidesPagination.run(), which gives a promise back. SlideBlockReader.blocks(slide), once per written slide.SlideMeasure.measure(blocks, slide), thenSlideMeasure.room(slide, blocks): the room left is what the slide holds, minus what surrounds the blocks.SlidePaginator.paginate(blocks, room)gives theSlideLayout.- The blocks taller than a slide are said in the console.
SlideComposer.compose(slide, layout), when the layout asks for more than one slide, with the head of a table on each.- 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
| Level | What happens | What the program does |
|---|---|---|
| 1 | Nothing can be done: the slide does not exist, its room cannot be measured | An exception, caught by SlidesPagination, said in the console |
| 2 | One slide is concerned: a block taller than it | No exception. The block stays whole and overflows |
| 3 | The author has to know | No 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
| Operation | Signature | Contract |
|---|---|---|
| constructor | (element, place) | pre: element is not null, place is 1 or more. post: height is 0, nothing has measured yet |
| get height | (): Number | post: 0 until something measures |
| set height | (value: Number) | pre: value is 0 or more. Written by SlideMeasure, by nobody else |
| fitsIn | (room: Number): Boolean | pre: room is 0 or more. post: true when height does not exceed room |
fitsIn() knows neither slide nor document: it compares two
numbers.
| Operation | Signature | Contract |
|---|---|---|
| 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 | (): Number | post: 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
| Operation | Signature | Contract |
|---|---|---|
| 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[]): Number | pre: 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): void | pre: the blocks are those of that slide. post: every block carries its height, measured in the slide |
| SlidePaginator.paginate | (blocks: SlideBlock[], room: Number): SlideLayout | pre: 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): void | pre: 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): void | pre: 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): Number | pre: 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): Number | pre: 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.