A story is an ordered collection of content from a website, relevant to a specific theme, e.g., C++.
Its purpose is to provide:
A starting point point for viewing the topic.
An ordered sequence of content on the topic, with each step in the sequence building on earlier steps.
A way of knowing when to stop. The sequence presents every resource for the topic available on the site.
StoryTeller is a mechanism for choosing one of several stories and manually stepping through its content.
Design:
We will discuss four views of the design: UI, activities, call graph, and a JavaScript code fragment.
StoryTeller User Interface (UI)
The UI consists of a static web page with an embedded iframe. The iframe, shown in the top left
quadrant of Fig 1., displays story content. Buttons on the right are used for navigation through
the story timeline, and header and footer provide information that helps user navigation.
Figure 2. StoryTeller_LocalStorage Activities
StoryTeller Activities:
When the StoryTeller loads the initialization process is triggered by the end of browser document
parsing. That results in a list of available stories being loaded into the iframe. We will see
how that is done in the next section.
Nothing happens until the user selects a story. When its storyTOC loads, JavaScript analyzes its
own content and writes relevant information into local storage, e.g., the url, name, and page note text
for each link on the page - one link per story page.
When storyTOC loading completes, the information has been saved in localStorage and the iframe's
onLoad event fires, calling StoryTeller's srcChange() function. StoryTeller reads story information
out of localStorage and builds an array of JavaScript page objects. Each page object has attributes for
page url, used to load the page, page name, used to build a Table Of Contents (TOC), and page notes text,
used to supply additional information should the user click the [?] button.
When a user clicks next, prev, ... buttons the button listeners process the information, usually by calling
render to display a new page. For example, next() adds 1 to curr, the page index, and calls render to
display the next page. That continues until the user clicks the [Exit] button.
Figure 3. StoryTeller_LocalStorage Call Graph
StoryTeller Call Graph:
The call graph, shown in Fig 3., demonstrates how that works. Processing starts with:
initialize() adds event listeners for key presses, then calls render(0), which loads
the StoryList.
When the user selects a story link, that changes the iframe document which loads a
storyTOC page. Its JavaScript evaluates its own content with getContent() and save()s
it to localStorage. When the storyTOC page loading is complete, that invokes the
srcChange() function when the iframe onLoad event is fired.
srcChange(), if key "saveStory" is in local storage, calls retrieve to read story
information from localStorage. Otherwise it does nothing except return.
retrieve() reads localStorage, builds the pages array, and calls loadTOC() to build
the table of contents. It then calls render(1) to show the first story page. finally,
it clears localStorage so that key "saveStory" is no longer there.
render(index), if index = 0, it is rendering the StoryList, so it clears pages array and
localStorage and loads the StoryList into the iframe.
If index > 0, retrieve has already been called, so it loads the page url into the iframe,
which displays pages[index-1].url.
Figure 4. Javascript Fragment
JavaScript Code Fragment:
Finally, a note about the structure of StoryTeller's JavaScript. This github site has
been accumulating a lot of JavaScript libraries, so variable and function name collisions
started to happen.
To handle that, all of the more recent libraries define a JavaScript object for that specific library.
For StoryTeller, the object shown in Fig 4. was defined. All of the data needed by many of the
functions and all of the functions are defined as properties of the storyTeller object.
Now, the code uses things like storyTeller.render(index). For variables and functions that are
used many times in a single function, we can define an alias variable, e.g., let rend = storyTeller.render,
and then simply invoke rend(index);