Lines 5–7: Load RSVP.js, RenderJS, and jIO sources. RSVP.js must be above both RenderJS and jIO, since they both depend on RSVP.js. Otherwise, the order does not matter. Each gadget should always have these lines to ensure that it can be loaded standalone.

Line 8: Load the gadget script, index.js, which contains all the logic for the gadget. It should be the last script, since it will depend on RenderJS and jIO.

Line 9: Load the application stylesheet. Only the index gadget should have stylesheets in small apps, where the added complexity outweighs the flexibility and modularity of using stylesheets in subgadgets.

Line 10–15: Generic boilerplate content. Notice the empty paragraph, which will shortly be rendered by RenderJS.

Index Gadget JavaScript

Save the following stylesheet and JavaScript as index.css and index.js, respectively. The stylesheet is only here to demonstrate that gadgets are just ordinary HTML files, which can also be styled with CSS.

Lines 1 and 6: The entire JavaScript file is wrapped in an immediately-invoked function expression, henceforth referred to as the IIFE, which is good JavaScript practice and not specific to RenderJS. window and rJS are always required for a RenderJS app…

Line 2: …because rJS(window) is the RenderJS gadget itself, which must always be at the start of every gadget chain.

Line 3: The declareService() handler is triggered when the gadget is loaded in the DOM.

Line 4: In all RenderJS methods, this is rJS(window), which is the gadget itself. The DOM element on which the gadget is anchored is automatically stored as this.element. For example, if you have <div data-gadget-url="index.html"></div> in another page, then this.element would be that div.

Since the whole page is the gadget, the gadget element in the DOM, this.element, is actually the whole <body>. You can modify this.element however you wish, for example by using querySelector().

Line 5: All RenderJS methods are chained together, so that the entire structure of a gadget looks something like rJS(window).declareService().declareMethod().onEvent().onEvent() and so on. This gadget chain may look strange at first, but you'll quickly get used to it.

One of the most common errors is forgetting to close the parenthesis—})—in such a method, so remember to do so even after a function statement.

Hello, World!

You've just made your first OfficeJS app!

Open index.html in a web browser to see it.

Notice how <p> is empty in the HTML source, but actually dynamically filled up with content due to RenderJS.

This is because the function in the declareService() handler triggered immediately when the gadget was loaded in the DOM.

That function used querySelector() on this.element, which is <body> for the index gadget, to insert text into the paragraph tag.

Basic RenderJS

New HTML Elements

Now that you have a minimal working example set up, in this chapter, you will get a glimpse into what RenderJS can really do.

Gadgets, which are just HTML pages, can act as JavaScript objects, with their own properties and methods, when a RenderJS script like index.js is included.

Add the following code into <body> in index.html to give us some new elements to play around with:

<form>
<input type="text">
</form>
<ul>
</ul>

Event Listeners

Replace the entirety of index.js with the following code. Remember to always read all the details throughout the entire tutorial.

Lines 1 and 17: Put document in the local scope of the IIFE, since lines 8 and 9 reference it. This makes accessing global variables a tiny bit faster and helps you remember that they're actually global variables.

Lines 6–10: Declare your first actual method, called addItem(), which takes as input a string called item and appends it to the list.

Line 11: Bind an event handler to submit, in pretty much exactly the same way as this.element.addEventListener().

Lines 12–13: Get and reset the input value, knowing that event.target is the <form>, which has only one child.

Line 14: This is the syntax for calling RenderJS methods on a gadget. Basically, they act just like normal JavaScript methods… for now.

Line 15: These two optional boolean parameters to onEvent() are useCapture and preventDefault respectively, the same as in addEventListener().

RenderJS In Action

Go to your app at index.html, type something in the input, and submit the form by pressing Enter or Return. Watch the list grow.

Remember to disable caching to ensure that your app always refreshes with the latest changes in the HTML.

If all goes well, then congratulations, you've just seen your first custom gadget method in action!

New Gadget Methods

You can add more methods by inserting them anywhere in the gadget chain.

For example, imagine you have a gadget chain like so: rJS(window).declareService().declareMethod().declareMethod().onEvent();

Then, you can add another .onEvent() method at the front, middle, or back:

Lines 1–4: Set the initial state of the gadget to {item_list: [], current_item: null}.

This is called automatically, as with declareService(). The state itself is just a JavaScript object, state, and can store anything.

Line 6: One of the main RenderJS methods is changeState(). Calling changeState() will immediately trigger onStateChange().

However, contrary to its name, onStateChange() is not triggered if you manually change the state with state.key = value.

So in order to stay sane, make sure you only change the gadget state using changeState().

Line 7: The parameter in onStateChange() is a JavaScript object that contains all the properties in the state that have changed due to changeState(). Only reassignments, like current_item = item, count as changes; mutations of objects, like item_list.push(), do not.

Line 8: This calls addItem() if someone changes state.current_item using changeState().

Line 3: Remember data-gadget-scope? This is where it is used. To access the methods and state of a subgadget, the parent gadget can call getDeclaredGadget() and pass in the child gadget's scope.

Line 4: getDeclaredGadget() returns the child gadget itself. Just like in a promise chain, the resolved value of the previous promise is passed into the parameters of the next promise in a promise queue. Putting this together, the result is that anything the model gadget can do with this, the parent gadget can now do with model_gadget!

Line 5: Instead of having to call getDeclaredGadget() every time you need to use a child gadget, you can just assign the result of one call to model_gadget in line 2, outside the promise queue, to use it everywhere inside

When you want to save some variable from one queue push to another, this is the way to do it, by declaring it outside the entire promise queue.

Line 6: Calling model_gadget.put() here is exactly the same as calling this.put() inside model_gadget.js.

Lines 7–9: This puts the document {title: "Test", completed: false} inside the jIO storage with a hard-coded ID of "/".

Inspecting Storages

If you do not see the IndexedDB database, and especially if you see an error, perhaps your browser does not support IndexedDB.

In that case, pass the following configuration to createJIO() instead:

However, they can manipulate documents and attachments in substorages.

So by adding a DocumentStorage handler on top of LocalStorage, you can use put() directly on the DocumentStorage, which automatically puts an attachment on the document with ID document_id in its sub_storage, the LocalStorage.

The power of jIO is that regardless of which connector, which handler, or which handler on which connector in a storage tree of whatever complexity, the same nine methods always apply to all of them, eliminating all the wasted development time on interfacing with all these APIs.

You should see the document you put() into "/" on the console, {title: "Test", completed: false}, showing again that jIO works.

The real goal of storing all our todos in some storage is to be able to retrieve them all on initial page load—in declareService().

But you don't know the IDs of your todos in declareService(), so you can't get() them.

You do know that the IDs are strictly increasing natural numbers starting from 0, so some of you may have the bright idea of storing the current length of item_list length with a hard-coded ID, retrieving it, and then calling get() on all numbers up to that length.

However, this custom assignment of IDs is not thread-safe, when you consider the possibility of modifying the same todo list in multiple tabs.

So you need a better way of finding these IDs.

jIO AllDocs

Thankfully, jIO has a method to get the IDs of all documents in storage.

In index.js, delete the put(), get(), and console.log(), then append the following instead:

Line 5: You have to retrieve all these documents by passing their IDs to get(), but all jIO methods, except for createJIO(), are asynchronous.

You know how to wait for the resolved value of a single promise, by returning it in a promise queue and catching its result in the next promise, but waiting for a list of promises to all resolve requires… a list of promises.

promise_list is the list that stores all the promises you need to wait for.

Lines 6–8: Since get() returns a promise queue, you can call it for every document in the jIO storage to get them all, then push the resulting unresolved queues into promise_list.

Line 9: The way to wait for some promises to all resolve is to return RSVP.all().

Line 11: If all promises in the given list do resolve, then RSVP.all() returns a list containing, for each promise, its resolved value.

If any promise in the given list is rejected, then RSVP.all() throws the first rejection.

Lines 13–15: The list of results of get() is simply a list of documents, whose titles can now be added to the list.

When you refresh the page, you'll realize that there has been a race condition sleeping in your code the entire time.

If declareService() in index.js reaches model_gadget.allDocs() before declareService() in gadget_model.js reaches jIO.createJIO(), then there is no storage to allDocs() from!

In the index gadget, you need to somehow wait for the model gadget to finish initializing before doing anything else. Alternatively, in the model gadget, you need to somehow tell the index gadget to wait for you to finish your initialization before doing anything else.

That is exactly what the ready() handler is for. Replace declareService in gadget_model.js with ready(), and the error disappears. After ten seconds, your todo list will also load correctly.

RenderJS Ready

The ready() handler blocks everything else.

A gadget with a ready() handler must wait until all the code in ready() is executed—for a promise queue, that means that the last promise in the queue is resolved—before doing anything else, which ensures that the gadget is "ready" for the rest of its functions.

In particular, declareService(), onEvent(), and everything else only runs after ready(), so no event listeners are declared while ready() is running.

If you try to add a new todo during those 10 seconds of delay in the model gadget, you'll be redirected to /? because there are no event listeners on the input, so it devolves into acting as a basic HTML form.

A parent gadget that contains a subgadget with a ready() handler must also wait, which ensures that the subgadget is "ready" for anything the parent wants to do. This has some interesting implications.

A gadget with ready() blocks itself and its parent, and its parent blocks its parent's parent.

Therefore, literally every gadget in the chain of declared subgadgets must wait for all their ready() handlers to finish executing before doing anything else.

Using ready() makes your code synchronous, so here are some guidelines:

Only use ready() when you really need to wait. For example, creating the jIO storage must be done first, because other functions depend on it. Something like synchronizing storages is not as critical.

Keep everything in one ready() handler. You can have many of them, but they just execute one after another in the order that they're declared, which is the sole exception where RenderJS method order actually matters. Having only one ready() will get rid of all this confusion and make you think carefully about the necessity of every line you put in.

Never access other gadget methods from ready(). It is tempting to call a manual gadget initialization method, such as render(), but ready() blocks, so all your parent gadgets must now wait even longer. Try to initialize in declareService() handlers as much as possible.

All in all, these are just guidelines; you can put almost anything in ready(), and minimizing the code you put in merely decreases the synchronous delay for other gadgets.

Nevertheless, there is one kind of code that you should never put in.

Since ready() is immediately called when the gadget is in memory, before it is rendered in the DOM, you cannot put any code that uses the DOM in ready(), including all event binding, for example.

Small gadgets are often rendered faster than ready() is executed, so that you won't see any explicit errors at first, but it's still a race condition as bad as using declareService() in your model gadget.

To avoid this, again, use declareService() instead of ready().

declareService() is called only when the entire gadget is rendered on the DOM, freeing you to bind events without fearing that something does not exist.

RenderJS OnEvent

Of course, declareService() is not limited to event binding, but that's just the most common use case, especially for small apps.

Originally, RenderJS only had ready() and declareService().

However, Nexedi engineers often found themselves writing the same functions over and over again for common use cases such as updating a gadget's state or listening to events with a never-ending promise queue.

Since the code to promisify event listening was several lines long and copied around everywhere, it was eventually exported as onEvent().

Under the hood, onEvent() is just an event listener in declareService() bound to this.element, so everything you know about declareService()—that it runs after ready() and after the gadget element is loaded in the DOM—applies to onEvent() as well.

Before, the click would not register, because the event listener was only bound to the gadget element, <body>.

Now, the event listener is bound to document, so you can click anywhere on the entire document and it still captures the events.

RenderJS DeclareJob

The latest addition to RenderJS is declareJob(), which is basically a declareService() that is triggered by being called, rather than automatically triggered when the gadget loads in the DOM.

Unlike a service, a job can be triggered multiple times, and doing so will cancel the last call of the job if it hasn't finished.

This could be useful if you want to "fork" a promise queue.

Since each promise in a promise queue has to wait for the previous one, and each call of a RenderJS method must wait for the returned promise queue, so it might be useful to split a promise off of the promise queue.

Now, please undo all of the changes you made in this chapter. They were meant purely to illustrate the inner workings of RenderJS methods. Don't worry, the rest of the changes in this chapter are actual changes!

The specific use case that declareJob() was developed for was inside a page that waits for all its gadgets to load before displaying any content.

Within the page, a table gadget often takes seconds to fetch data from a central database, which slows down the entire user experience.

By using declareJob(), the loading of that specific gadget is forked.

This makes the page display some content as soon as all its other gadgets load, and only fully render the table gadget afterwards when the job is completed.

Furthermore, if the table is queried and must fetch some other data instead, then the new job cancels the previous job, since there's no point rendering a gadget right before it is rendered anew by a newer job.

These intricacies make jobs less intuitive than the other RenderJS methods, so please carefully consider other options before using declareJob().

Nexedi's modification of RSVP.js added support for cancelling promise queues.

Cancellation is a very advanced topic and almost always limited to RenderJS's internal implementation; nevertheless, you may still run into some issues with it.

In declareService(), the promise queue returned by it will be cancelled when the gadget is removed from the DOM, which could be useful to remove event listeners and such.

In declareJob(), the promise queue returned by it will be cancelled when the job is triggered again, which could be useful to avoid unnecessary work.

In onEvent(), the promise queue returned by it will be cancelled when the same event is fired again, which could be useful to avoid duplicating responses.

RenderJS State

Each gadget has its own state object, that can be directly manipulated as this.state or less directly manipulated with changeState(), which then automatically triggers onStateChange().

A new onStateChange() is triggered every time changeState() is called, and they are all executed in order, so if you call RSVP.delay(300000), then RenderJS will block the next onStateChange() for five minutes.

As for setState(), it is simply the first ready() handler, run before anything else and blocking everything else.

Seventh, delete everything except this.state.item_list.push(item); from addItem(), since the previous line does the actual list rendering.

You can delete document from the IIFE as well, since it's unused now.

Everything is finally rendered all in one go inside onStateChange(), but nothing shows up if you refresh your app, since all the rendering only happens when you update current_item.

In the beginning, current_item and changeState() were used in the submit event listener to introduce onStateChange(). But now, you know more than enough about that, so remove current_item entirely; it's worse than useless, since it prevents you from adding duplicates.

Since put() is now in addItem(), you don't want to put() again all the documents you got from allDocs(), nor do you want to run onStateChange() for every todo you retrieve from storage at the beginning.

In this <script> tag, the class allows you to refer to the script inside your JavaScript code, no matter how many copies of the same gadget are on the same page.

The type specifies that it is not a JavaScript script, but instead a Handlebars script.

TodoMVC Templates

Declare var handlebars_template; as a global variable, between "use strict" and rJS(window).

As you saw with the loopEventListener, anything inside the IIFE but outside the gadget chain acts a global variable for the gadget.

Consider when the Handlebars template must be compiled.

Since declareService() immediately calls changeState() in the promise queue, and changeState() needs the Handlebars template to render todos, so you must compile the Handlebars template before all of that.

This code applies the Handlebars template by replacing everything in <main class="handlebars-anchor"> with the HTML in <script class="handlebars-template">, where for example, the content of <span class="todo-count"> is filled by the value of todo_count, and elements are checked or hidden based on the Booleans todo_exists and all_completed.

In effect, the entire HTML is rendered from scratch every time the state changes, without using querySelector() or appendChild(), drastically simplifying the code.

This is the recommended procedure for OfficeJS apps, but as always, you're free to do whatever you want.

Add Handlebars to the IIFE, refresh your app, and notice that all the todos are blank.

This is because you gave Handlebars many parameters involving this, like {{this.title}} to display actual content, but the elements of todo_list are just strings.

Rectify this by pushing full todo objects to item_list, such as {title: "laundry", completed: false}. Perhaps you should rename it to todo_list too.

TodoMVC Style

You should now have a stylish todo app truly befitting the name TodoMVC, even though none of the buttons work.

Advanced jIO

Model Remodelling

The reason for using the ugly apply syntax was to quickly introduce some jIO methods, but now you all know how to put() documents (JavaScript objects) with string IDs, get() documents using their IDs, and get allDocs() from a whole storage.

Since your gadget is a model gadget, it should abstract this away from your index gadget.

In this chapter, you will rewrite the model gadget into a real model, and dive in-depth into the remainder of the jIO API.

For example, instead of making the index gadget call allDocs() and then get(), the model gadget should have a function, maybe getTodoList(), that does both:

You've never had to use it before, but promise queues can have both resolve and reject callbacks, just as ordinary JavaScript promises have.

The reject callback in lines 12 to 14 is called when the previous promise throws, that is, in the case that no document with the given ID exists in the jIO storage.

Without a reject callback, the rejection is propagated down the promise queue until either another reject callback is reached, or it falls off the end of the queue, in which case the entire queue throws the rejection.

It's common in RenderJS applications to see trailing from the end of many promise queues, .push(null, function (error) { console.log(error) }); in order to catch and log these rejections without letting the entire promise queue throw.

This is a great way to debug, because this one line at the end of a promise queue with no reject callbacks will catch all rejections from inside the entire queue!

Multiple Tabs

With these functions in place, it is up to you to delete all the previous jIO methods and update the places in index.js that use model_gadget.

Hint: the promise queue in declareService() should be eight lines long.

You should make sure that your app has the exact same functionality after this refactoring, including the multiple tabs conflict that's come up again and again.

None of the other frameworks represented in TodoMVC bothered to solve it, but with jIO, it's so easy that we might as well.

Why do multiple tabs cause conflicts in the first place?

Because different tabs update in-memory objects like state.todo_list separately, so all you have to do is ignore memory and reload the entire todo list from getTodoList() every update.

Since you only render in onStateChange(), you only need to change that one method.

To do so, start up a promise queue that returns model_gadget.getTodoList(), then use its resolved value to replace this.state.todo_list in the method.

You can now remove todo_list from the state and the rest of the code, which further separates the concerns of the model gadget and the index—the view.

Functions that use todo_list, like declareService() and addItem(), may become drastically simplified or even themselves removed by its removal, but that's all up to you.

Since all the previous code in onStateChange() will now be in a promise queue, the scope will change, so remember to replace this with gadget as well.

By now, you've probably encountered this this issue several times.

According to JSLint, this is very bad style.

Since you have to change this to gadget whenever it gets used inside a gadget chain, why not just call it gadget everywhere so you don't need to change it?

Add var gadget = this; to the top of every method, and replace this with gadget everywhere else.

Henceforth, you will never need to think about promise queue scoping ever again.

UUID Storage

Refresh your app, open it up in two different tabs, and see if they still overwrite each other. They do, even though the entire todo list is reloaded from jIO storage every time.

Although reloading everything is all you need to do from jIO's perspective, the design of the todo list storage is holding you back.

If you had a proper storage design from the start, everything would be trivial, though the learning curve would be rather steep.

The problem is that you are assigning IDs for putTodo() based on the length of todo_list, so that the IDs on each tab are almost always the same, and they end up overwriting each other all the time.

The reason for assigning IDs like so was mainly because it is a common beginner mistake.

It's also because allDocs() retrieves documents in order of ID, so a numerically increasing ID puts your todos in chronological order.

However, that's not quite true, because IDs are strings sorted in lexicographic order, so "10" comes before "2" and "42" comes after "419".

Therefore, if you find any other way to assign IDs, you will have no reason at all to use the current system.

This is where the Advanced jIO comes in. Remember way back in the Basic jIO chapter, when you used a storage handler over LocalStorage in order to use put()?

Well, jIO also has a storage handler to automatically assign IDs to new documents.

This is the UUIDStorage handler, which makes available a post() method that puts a document in storage with a randomly-generated UUID, then returns that UUID.

Because all the actual logic is delegated to the model gadget, the only interesting thing in the HTML is that preventDefault is set to false, which allows checkboxes to be toggled and inputs to be typed.

Now you see why it was important to get() from the jIO storage before calling put() in putTodo(), because changeTodoTitle() and toggleOneTodoStatus() only care about the title and completed properties respectively, and have no idea about the other.

toggleAllTodoStatus() uses the basic allDocs() because all it needs are the IDs of every todo in order to call toggleOneTodoStatus() on each.

removeOneTodo() uses a jIO method, remove(), that removes the document with the given ID.

removeAllCompletedTodo() uses getTodoList() because it needs the completed status of every todo.

Furthermore, removeAllCompletedTodo() needs to know the IDs of all completed todos in order to call removeOneTodo() on them, so I hope you really did implement that properly in getTodoList()!

Editing State

If you refresh your app, you'll see that you can toggle and destroy individual todos, toggle all todos on, and clear completed todos.

However, you can't toggle all todos off, nor edit any.

And if you start typing out a new todo but toggle or destroy another todo in the middle, your input is erased.

You should keep the editing state of todos outside of jIO, because different tabs should be able to edit different todos simultaneously.

The only way to check if all todos are completed, and to ensure that only one todo is being edited at a time, is to step through all of them.

Four final features for you to implement, on your own, remain:

The value of the currently edited todo should be copied from its title in jIO storage into <input class="edit">.

The cursor should automatically focus on the currently edited todo when editing_jio_id exists, or <input class="new-todo"> otherwise. These two can be combined, since the same querySelector can both focus the currently edited todo and update its value.

The current value of <input class="new-todo"> should be saved by assigning its value to some variable before using the Handlebars template, and loading its value from that variable after using the Handlebars template.

However, that value should be cleared in the submit event listener, by passing in a new state property to changeState(). This will also eliminate event.target.elements[0].value = "";, ensuring that every DOM modification in the entire app really only happens in onStateChange().

Query Storage

Unfortunately, new todos are not even added to the bottom of the list anymore, since ordering is still lexicographic on IDs, but these UUIDs are now all completely random.

Enter yet another storage handler to the rescue, QueryStorage, which enables queries, sorting, and property selection in calls to allDocs().

You know how to add new handlers now, so add type: "query" and put your current UUID/Document/LocalStorage inside as a sub_storage.

Before you can sort your todos, you need something to sort them by.

The most obvious is by creation date, so add the new property creation_date: Date.now() to postTodo().

In getTodoList(), you can then customize allDocs() into allDocs({sort_on: [["creation_date", "ascending"]]}).

Refresh your app, try different tabs, and everything is now sorted.

You must have spent a lot of effort to inject IDs in getTodoList().

Unfortunately for your effort, jIO QueryStorage could have done all that for you:

In a QueryStorage, you can pass in a select_list, and allDocs() will return a list of documents that each contain the properties of select_list in value.

QueryStorage also supports limit: [3, 7] to only return between 3 and 7 documents, and query: '(title:"buy%") AND (completed:"true")' to only return completed todos that begin with "buy". Notice the double quotes; jIO cannot parse single quotes.

The moral of the story is to read the docs! You never know what might be useful in a year.

jIO Attachments

Other than a few details in the details about attachments, you now know everything there is to know about jIO!

Please carefully read through all the storage types and handlers, especially ReplicateStorage, because you can still do so much more with jIO.

The last important concept in jIO is the idea of manipulating attachments in documents, using putAttachment(), getAttachment(), removeAttachment(), etc.

Attachments are always Blobs, meant only to store binary data.

However, some storage connectors, such as Dropbox, can only store attachments.

Rather than trying to brute force your way using putAttachment("foo", "bar", new Blob([content], {type: "text/plain"})) everywhere, just use a FileSystemBridgeStorage handler on top of these connectors and stick with put() and get().

Acquisition and Offline

Declaring Gadgets in JavaScript

In this chapter, you will learn about communication between RenderJS gadgets and modern technologies to take your web application offline.

As the final chapter, it is almost entirely self-directed.

Nevertheless, if you ever get lost, the source code is available at the end.

First, create a new gadget, gadget_router.html and gadget_router.js.

Try to do this all by memory: fill up both files with boilerplate, declare the new gadget in index.html, then log its birth to the console.

Alternatively, instead of HTML, you can also declare the gadget with JavaScript in index.js:

The parameters of declareGadget exactly mirror the parameters in the HTML.

An additional parameter, element, determines which element the gadget is attached to.

By declaring gadgets in JavaScript, you can dynamically assign scopes and elements, allowing you to treat even single todo list items as separate gadgets if you're so inclined.

In fact, most RenderJS applications do exactly that, with a huge tree of gadgets.

This tutorial only has three, but you'll be able to make gadget trees with what you learn.

Router Gadget JavaScript

The goal of the router gadget is to handle the three links at the bottom of the todo list, so that, for example, navigating to /#/completed only shows the completed todos.

To get started, bind an event listener to the "hashchange" event on window (you can't use onEvent() because it's not this.element), by copying the loopEventListener from Advanced RenderJS into gadget_router.js.

Test your app to make sure that clicking the links triggers the listener.

The router gadget has to somehow communicate with either the model gadget or the index gadget, but so far, you've only seen communication from parent to child by directly accessing all child properties and methods via getDeclaredGadget().

Publically acquirable methods can take many arguments, but RenderJS compresses them all into one array, param_list, in the actual function definition.

These methods occupy a separate namespace from ordinary declared methods, so although you would be able to call setQuery() from all child gadgets, you would not be able to call it from the parent gadget.

To write a method that the parent and its children all need to use, this is the convention:

Since param_list is already an array of arguments, you might as well call apply on the actual declared method with it.

It's important to review the differences between these identically named methods.

The top method can only be called by the child gadgets and its sole parameter is an array.

The bottom method can have many parameters, and can both be called by the parent gadget and indirectly called by the child gadgets through the top method.

For your todo app, you won't need to call setQuery from the index gadget, so you can delete the extra declareMethod().

Now that clicking the links triggers an event listener in the router gadget, and the router gadget can change state.query in the index gadget, filtering the todo list by completion status is merely a matter of adjusting the query of allDocs() in the model gadget.

Add an optional query parameter to getTodoList(), then plug it directly into allDocs(), so that for example, removeAllCompletedTodo() can call getTodoList('completed: "true"') and not worry about checking in the loop for completeness as well.

When debugging, remember that jIO only recognizes double quotes in its queries.

Acquiring Methods

To call acquirable methods from a child gadget like the router gadget, the child gadget must declare the acquired method:

.declareAcquiredMethod("setParentQuery", "setQuery");

And that's it. Now, the child gadget can call gadget.setParentQuery() wherever it likes, and RenderJS will automatically wrap those arguments in an array and send it to be executed by setQuery() in the index gadget.

By renaming "setParentQuery" to "setQuery", all three methods—the acquirable method, the acquired method, and (sometimes) the actual method in the index gadget—merge into one entity, which can relieve some mental burden or confuse you even further.

All that's left to finish routing is making the appropriate conditional statements in the hashchange event listener and the appropriate arguments to getTodoList().

As a challenge problem, figure out how to set todo_count to always be the number of uncompleted todos, even with all these queries being thrown around.

The flow of control goes something like this:

First, the user changes the hash in the URL, likely by clicking a link.

Second, the router gadget receives the hashchange event, and translates the hash to a specific query for allDocs().

Third, the router gadget calls setParentQuery() with said query, which calls setQuery() on the index gadget via acquisition.

Fourth, setQuery() on the index gadget calls changeState() with said query, which triggers onStateChange() with query in modification_dict.

Fifth, onStateChange() calls getTodoList() with said query.

Finally, the model gadget calls allDocs() with said query, and the resulting list of todos has been successfully filtered by the hash changing.

Service Workers

For the final part of the final chapter, you will take your web app offline.

Follow the steps in the details, and make sure your app works offline, as shown in the screenshot above.