@wq/app
======

[@wq/app]

**@wq/app** is a [wq.app] module that provides a configuration-driven JavaScript application controller to facilitate the creation of complete mobile web apps for viewing and submitting field data.   @wq/app is primarily intended for use as a client for [wq.db.rest], but can be used with any REST service with the same [URL structure].

## Overview

@wq/app is the highest-level wq.app module, and brings together a number of lower-level modules and wq conventions into an integrated API.  As a primarily configuration-driven module, @wq/app is relatively easy to use, but enforces a number of constraints on the design of your project.  In particular, it works best with [wq.db.rest] or a compatible REST service.

The specific concepts leveraged by @wq/app include:

 * A specific [URL structure] that is observed by both the application itself and its REST API.
 * A client-side URL router and page renderer that can fall back to server-rendered pages via [shared client/server templates].
 * A model-driven, `localForage`-backed cache of REST API responses ([@wq/model]), with an offline-capable outbox to sync form submissions back to the server ([@wq/outbox]).
 * A [wq configuration object] generated by the URL router on the server, that describes the available routes and underlying models.
 * An optional [authentication] endpoint provided by the REST API, including CSRF tokens to prevent cross-site attacks.
 * A simple [plugin API][plugins] to facilitate adding optional functionality such as interactive [maps][@wq/map] and [charts][@wq/chart].

## API

`@wq/app` is typically imported via [AMD] as `app`, though any local variable name can be used.

```javascript
// myapp.js
define(['wq/app', ...], function(app, ...) {
    app.init(config);
});
```

The app module provides the following methods and properties.

### Initialization

In a simple project, the only required usage of @wq/app is to initialize it with a configuration.  The configuration object is described in the Configuration section below.  `app.init()` returns a [Promise] that will be resolved when the initialization is complete.

The version of jQuery Mobile included with wq.app is customized to disable automatic initialization on startup.  The purpose of this change is to make it easier to register custom plugins and events before jQuery Mobile starts up, to ensure they are executed when it does.  To start up jQuery mobile, call `app.jqmInit()` after calling `app.init()` and registering your custom routes.  If you have no custom events, you can set `config.jqmInit = true` to have `app.init()` call `app.jqmInit()` for you.

While not required, it is often useful to call `app.prefetchAll()` after initialization.  `app.prefetchAll()` preloads and caches data from all registered models (i.e. all entries in the `pages` configuration that have `list` set to `true`).

```javascript
// No custom events, no prefetch
config.jqmInit = true;
app.init(config);

// Custom events via low-level router API, then prefetch all
app.init(config).then(function() {
    router.addRoute(...);
    app.jqmInit(); 
    app.prefetchAll();
});
```
#### Plugins

@wq/app provides a simple [plugin API][plugins] and a number of predefined optional plugins such as [@wq/map] and [@wq/chart].   Plugins should always be registered via `app.use(plugin)` before calling `app.init()`.

```javascript
// map is a @wq/app plugin
config.jqmInit = true;
app.use(map);
app.init(config);
```

While it is possible to register arbitrary jQuery Mobile event handlers and/or low-level routes using @wq/router, the [plugin API][plugins] is the recommended way to add custom functionality to pages before and/or after they render.

### `<form>` Handler

`app.init()` registers a custom submit handler that takes normal form POSTs and converts them to [@wq/outbox] entries.  For model-backed list pages, these forms would normally be placed in `[page]_edit.html` templates and accessed via `/[page_url]/new` and/or `/[page_url]/[id]/edit` .

In addition to the other standard form input types, the `<form>` handler supports saving photos and other files, which are stored in [@wq/outbox] as `Blob` instances.  See the [@wq/app/photos] documentation for more information about this feature.

To avoid conflicts with jQuery Mobile's own AJAX form handler, it is recommended to set `data-ajax="false"` on all forms using the @wq/outbox functionality.

```xml
<form method="post" action="/items" data-ajax="false">
```

By default @wq/app's form handler is applied to every form in the website.  This functionality can be disabled on a per-form basis by setting `data-wq-json=false` on the form tag:

```xml
<form method="post" action="/custom" data-wq-json="false">
```

To disable both jQuery Mobile's AJAX form handler and @wq/app's AJAX+JSON form handlers, specify both properties:

```xml
<form method="post" action="/custom" data-wq-json="false" data-ajax="false">
```

When working with list pages, the same template (`[page]_edit.html`) is used for both new and existing items.  The presence of an `id` attribute in the context object can be used to distinguish between the two use cases when rendering the template.  In addition, a number of other useful context variables are provided to assist with rendering e.g. `<select>` menus for foreign keys.  See the [template context] documentation for more details.

```xml
<!-- Different action/method depending on whether this is a new or existing item -->
<form action="/items/{{id}}" method="post" data-ajax="false">
  {{#id}}
  <!-- Existing item; override HTTP method for Django REST Framework -->
  <input type="hidden" name="_method" value="PUT">
  {{/id}}
```

### `app.go()`

`app.go()` is called automatically whenever the URL changes, e.g. in response to the user clicking on a link.  However, there are rare cases where you may need to call it directly.  app.go() is essentially a wrapper for [router.go()] that automatically generates the appropriate [template context] for list, detail, edit, and simple views.

```javascript
app.go(page, ui, params, [itemid], [edit], [url], [context])
```

`app.go()` accepts up to seven arguments:

name | purpose
-----|---------
`page` | The name of a page listed in `config.pages` (described below)
`ui` | A jQuery Mobile [ui object] describing options for an event
`params` | Any additional URL parameters (`?name1=value1&name2=value2` etc.)
`itemid` | (optional) The id for an individual item in a list (triggers a detail view instead of a list view)
`edit` | (optional) Boolean, if true triggers an edit view instead of a detail view
`url` | (optional) URL to display for the rendered page.  Usually this is automatically computed.
`context` | (optional) Initial template context variable (will be merged with the automatically generated context).

The default template contexts for detail and edit views contain a number of context variables that assist in looking up and displaying information from related models, e.g. a list of potential values for a foreign key.  These lookup contexts can be customized by overriding hooks (see Advanced Customization, below).

### `app.sync()`

Triggers a background sync of pending outbox items in [@wq/outbox].  Called automatically at regular intervals if `backgroundSync` is enabled (see Configuration below).  Outbox items causing server errors will be excluded from further syncs after 3 sync attempts (This threshold can be controlled via [@wq/outbox]'s `maxRetries` option.)  To try re-sending all unsaved items, including those causing server errors (for example in response to a user-initiated sync), call `app.sync(true)` instead of `app.sync()`.

### `app.user`

If the application supports authentication and the user is logged in, `app.user` will be set with information about the current user provided by the server.  This information will also be available in the [template context], e.g. `{{#is_authenticated}}{{user.username}}{{/is_authenticated}}`.

### `app.config`, `app.wq_config`
A copy of the @wq/app configuration object (see below) and the [wq configuration object], respectively.  Initially `app.config.pages` and `app.wq_config.pages` are the same, but after logging in, `app.wq_config` is overwritten with an updated wq configuration object with permissions information specific to the logged-in user.  `app.config` is made available in the [template context] as `{{app_config}}`, while `app.wq_config` is provided as `{{wq_config}}`.

### `app.models`

After initialization, `app.models` will contain a [@wq/model] instances for each registered model (i.e. each item in the `pages` configuration with `list: true`).

```javascript
app.models.item.filter({'type_id': 2}).then(function(type2items) {
    type2items.forEach(function(item) {
        console.log(item.id + ' - ' + item.label);
    });
});
```

### `app.native`

Whether the application is running under [PhoneGap] / [Cordova] or as a web app (`true` and `false` respectively).  Available in the [template context] as `{{native}}`.

## Configuration

As noted above, @wq/app is primarily configuration driven.  The available configuration options are shown below with their default values.

```javascript
{
    // @wq/app options
    'debug': false,
    'jqmInit': false, // See init() above
    'backgroundSync': true, // alternatively, noBackgroundSync: false
    'loadMissingAsHtml': true, // alternatively, loadMissingAsJson: false
    'pages': { /* ... */ }, // from wq config
    'transitions': { /* ... */ },
    'hookName': function() {} // See hook names below

    // Configuration for core modules
    'router': { /* ... */ },
    'store': { /* ... */ },
    'outbox': { /* ... */ },
    'template': { /* ... */ },
    
    // Configuration for registered plugins
    'map': { /* ... */ },
    'chart': {  /* ... */}
}
```

The configuration sections for the other core modules are passed on to the `init()` function for each module.  In a few instances, @wq/app overrides the default settings for the respective modules.  See the documentation for [@wq/router], [@wq/store], [@wq/outbox], and [@wq/template] for more information about the available configuration options for each module.  Similarly, any registered [plugins] can be configured via sections with the same name as the respective plugins.

### Scalar Options

The `debug` option enables console logging in @wq/app and the other core modules.  If specified as a number, `debug` will set the verbosity level in @wq/store.

The `jqmInit` option tells `app.init()` to immediately trigger `app.jqmInit()` on startup.  The default is false, to give you a chance to register additional custom routes before initializing jQuery Mobile.

The `backgroundSync` option tells @wq/app not to make the user wait for forms to be submitted to the server, and instead to handle all @wq/store syncing in the background.  If specified as a number, `backgroundSync` will set the number of seconds between sync attempts.  Setting `backgroundSync` to `true (the default) will trigger 30 seconds between sync attempts.  It can be disabled by setting `noBackgroundSync` to `true`.  `backgroundSync` can also be enabled or disabled on a per-form basis by setting the `data-wq-background-sync` attribute.  For example, the [login.html] provided by the wq Django template sets `data-wq-background-sync` to false since it makes more sense to wait for a successful login before continuing.

The `loadMissingAsHtml` and  `loadMissingAsJson` options tell @wq/app what to do if the user navigates to a model instance that is not stored locally.  There are three possible outcomes in this case:
  * If the associated model page is configured with `partial: false`, @wq/app will assume the entire model collection is stored locally, assume the page does not exist, and call the `router.notFound()` 404 page.
  * If the associated model page is configured with `partial: true`, and `loadMissingAsHtml` is set, @wq/app will attempt to load the page from the server and assume the server is capable of rendering content as HTML.
  * If the associated model page is configured with `partial: true`, and `loadMissingAsJson` is set, @wq/app will attempt to load the missing data from the server as JSON and render it locally.

### `pages`: URL routes

The `pages` configuration section is equivalent to the option with the same name in the [wq configuration object].  The `pages` configuration is typically generated by the REST service and describes the URL routes in the application.  The full list of page options is described in the documentation for the [wq configuration object].

> **Note**: If you need to customize an option in the server generated `pages`, you should specify it when calling [router.register_model()] in [wq.db.rest] rather than overriding the `pages` section in your `config.js`.  This ensures that the client and the server are on the same "page".

As noted above, [@wq/model] instances for all model-backed pages (those with `list: true`) will be added to `app.models` for convenience.

### `transitions`: Page Transitions

Configuration for jQuery Mobile's built in [page transitions].  Where applicable, this information is mapped to jQuery Mobile's [built-in configuration options].

 Name | Usage
------|-------
`default` | A shortcut for `$.mobile.defaultPageTransition`.  Often set to `slide`.
`dialog` | A shortcut for `$.mobile.defaultDialogTransition`.
`save` | Sets the default transition to use when moving from an edit view back to a detail view after a save.  This is often set to `flip`.
`maxwidth` | A shortcut for `$.mobile.maxTransitionWidth`.  Defaults to 800 (note that vanilla jQuery Mobile defaults to false),

### Creating a Configuration Module

The configuration object can be defined as a variable in the same file as the call to `app.init()`.  However, the conventional approach is to create a separate AMD module `js/myapp/config.js` that depends on the [server-created wq config] and then adds the additional attributes needed to initialize @wq/app.

```javascript
define(['data/config', 'data/templates', function(config, templates) {
// config.pages already exists on server-generated wq config

config.templates = {
    // Configure templates and default context variables
    templates: templates,
    partials: templates.partials,
    defaults: { /* ... */ }
}

config.transitions = {
    // Configure default jQuery Mobile page transitions
}
config.store = {
   // Configure @wq/store
}
return config;
});
```

If you are not utilizing the wq.db-generated configuration, you can define the entire configuration object in config.js:

```javascript
define({
    'pages': { /* ... */ },
    'template': { /* ... */ },
    'transitions': { /* ... */ },
    'store': { /* ... */ }
});
```

### Inlining Templates
The `templates` can be created as regular Mustache HTML files and then inlined into a JavaScript object through the [collectjson] step in the [wq build process].  The resulting object should be of the form:

```javascript
{
     '[modelname]_list': "<html>...</html>",
     '[modelname]_detail': "<html>...</html>",
     '[modelname]_edit': "<html>...</html>",
     // so on for each model...
     
     '[staticpage]': "<html>...</html>",
     'partials': {
          'head': "..."
     }
}
```
By convention, this object is typically wrapped in an AMD `define()` and placed in the file `js/data/templates.js`.

### Putting it all Together

`app.init()` should typically be called immediately when the JavaScript loads.  Assuming `data/template.js` and `myapp/config.js` are created as above, this can be accomplished by creating a main module for your application:

```javascript
// js/myapp.js
requirejs.config({
    'baseUrl': 'lib', // wq and third party libs
    'paths': {
        'myapp': '../myapp',
        'data': '../data'
    }
});
requirejs(['myapp/main']);

// js/myapp/main.js
define(['wq/app', './config'],
function(app, config) {
   app.init(config);
});
```

And then configuring RequireJS to load it in your `index.html`:
```xml
<script src="js/lib/require.js" data-main="js/myapp"></script>
```

If you are utilizing both wq.app and wq.db in your project, you may find it useful to leverage the [Django wq template] which will set up the above project layout for you.

## Advanced Customization

@wq/app provides a number of events and hooks for additional customization.

### Events

#### `login`

Callbacks registered with the `login` event will be called whenever the user logs in (as well as at the start of the application if the user is already logged in).

```javascript
$('body').on('login', function(){
    /*...*/
});
```

#### `logout`

Callbacks registered with the `logout` event will be called whenever the user logs out.

```javascript
$('body').on('logout', function() {
    /*...*/
});
```

### Hooks

These hooks can be customized by adding additional options to the @wq/app config object.

#### `postsave(item, backgroundSync)`

`postsave()` is called after a form submission with the `item` created by [outbox.save()].  It is primarily used to handle navigation to the next screen.  `postsave()` can usually be configured as needed via the `postsave` property in the [page configuration].  The `postsave()` hook is meant to be overridden in cases where `config.pages[name].postsave` does not provide enough flexibility.

The `backgroundSync` argument indicates whether or not the form is being synced in the background, in which case the result of the submission might not be known yet.  If `backgroundSync` is set, the default implementation of `postsave()` will navigate to the list view of the model that was just saved.  If it's not set (i.e. result from the server is already known), `postsave()` will navigate to the detail view of the newly saved item by default.

```javascript
config.postsave = function(item, backgroundSync) {
    if (backgroundSync) {
        app.go('outbox');
    } else if (item.synced) {
        $('#result').html("Successfully saved.");
    }
}
app.init(config);
```

#### `saveerror(item, reason, $form)`

If background sync is disabled, `saveerror()` is called when [outbox.save()] fails, with the `item` from outbox.save(), the reason for the failure, and the jQuery-wrapped `<form>` that initiated the save.  The `reason` will be one of the constants `app.OFFLINE`, `app.FAILURE`, or `app.ERROR`.  `app.FAILURE` usually indicates a server 500 failure, while `app.ERROR` usually indicates a 400 validation error.

If background sync is enabled (the default), then `saveerror` will not be used.

```javascript
config.saveerror = function(item, reason, $form) {
    if (reason == app.OFFLINE) {
        alert("You are currently working offline.  Your record is in the outbox");
    } else {
        app.showOutboxErrors(item, $form);
    }
}
app.init(config);
```

#### `showOutboxErrors(item, $form)`

`showOutboxErrors()` is used to populate the screen with information about why a form was not successfully submitted.  It is called by `saveerror()` by default, or when navigating to a previously saved outbox item.

The default implementation of `showOutboxErrors()` can automatically display any error messages returned from the REST API - both per-field errors and general validation errors.  This functionality can be leveraged by placing `<span>` or `<p>` tags next to the fields and near the submit button.  The tags should have two classes: `error` and `[page]-[field]-errors`, where `page` is the name of the of the wq page config and `field` is the name of the form field.

#### Example

```xml
<!-- item_edit.html -->

<form action="/items/{{id}}" method="post" data-ajax="false" data-wq-background-sync="false">
  {{#id}}
  <input type="hidden" name="_method" value="PUT">
  {{/id}}
  <ul>
  
    <li>
      <label for="item-name">Name</label>
      <input name="name" value="{{name}}" id="item-name">
      
      <!-- Placeholder for "name" errors -->
      <span class="error item-name-errors"></span>
    </li>
    
    <li>
      <label for="item-date">Date</label>
      <input name="date" type="date" value="{{date}}" id="item-date">
      
      <!-- Placeholder for "date" errors -->
      <span class="error item-date-errors"></span>
    </li>
    
    <li>
      <!-- Placeholder for general errors -->
      <span class="error item-errors"></span>
      
      <button type="submit">Save Changes</button>
    </li>
  </ul>
</form>
```

#### `presync()`
Triggered at the beginning of `app.sync()`.  Useful for debugging.

#### `postsync(result)`
Triggered after an `app.sync()`, with the result from `outbox.sendAll()` in [@wq/outbox].  Useful for alerting the user of persistent sync issues.  The default implementation will check for and refresh any screens with `data-wq-sync-refresh` set to true.  (This helps ensure that e.g. list views update automatically after a sync).

### Low-Level APIs

@wq/app relies on three low level modules that are discussed in depth elsewhere:

 * [@wq/router]: Provides direct access to the jQuery Mobile page navigation event flow.
 * [@wq/store]: A wrapper for localForage with built-in networking capabilities.
 * [@wq/template]: A wrapper for configuring Mustache.js with default templates and context variables.

[@wq/app]: https://github.com/wq/wq.app/blob/master/packages/app
[wq.app]: https://wq.io/wq.app
[wq.db]: https://wq.io/wq.db
[@wq/store]: https://wq.io/docs/store-js
[@wq/model]: https://wq.io/docs/model-js
[@wq/outbox]: https://wq.io/docs/outbox-js
[@wq/router]: https://wq.io/docs/router-js
[@wq/template]: https://wq.io/docs/template-js
[URL structure]: https://wq.io/docs/url-structure
[shared client/server templates]: https://wq.io/docs/templates
[authentication]: https://wq.io/docs/auth
[plugins]: https://wq.io/docs/app-plugins
[AMD]: https://wq.io/docs/amd
[router.register_model()]: https://wq.io/docs/router
[wq.db.rest]: https://wq.io/docs/about-rest
[wq configuration object]: https://wq.io/docs/config
[My website is its own REST API]: https://wq.io/docs/website-rest-api
[router.register()]: https://wq.io/docs/router-js
[router.go()]: https://wq.io/docs/router-js
[template context]: https://wq.io/docs/templates
[ui object]: http://api.jquerymobile.com/pagecontainer/
[PhoneGap]: http://phonegap.com
[Cordova]: http://cordova.io
[client-side rendering]: https://wq.io/docs/web-app
[template rendering context]: https://wq.io/docs/templates
[page transitions]: http://demos.jquerymobile.com/1.4.5/transitions/
[built-in configuration options]: https://api.jquerymobile.com/global-config/
[Mustache templates]: https://wq.io/docs/templates
[collectjson]: https://wq.io/docs/collectjson
[wq build process]: https://wq.io/docs/build
[Django wq template]: https://github.com/wq/django-wq-template
[outbox.save()]: https://wq.io/docs/outbox-js
[page configuration]: https://wq.io/docs/config
[design patterns]: https://wq.io/docs/about-patterns
[model.filter()]: https://wq.io/docs/model-js
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
[server-created wq config]: https://wq.io/docs/router
[login.html]: https://github.com/wq/wq-django-template/blob/master/django_project/templates/login.html
[@wq/map]: https://wq.io/docs/map-js
[@wq/app/photos]: https://wq.io/docs/photos-js
[@wq/chart]: https://wq.io/docs/chart-js

@wq/app Plugin API
=================

[@wq/app] provides a simple plugin API to facilitate the incorporation of arbitrary custom functionality beyond the built-in page rendering and offline storage APIs.  While it is certainly possible to register global jQuery events that execute on every page change, or even to define custom `<script>` tags in page-specific HTML templates, these can be tricky to implement consistently due to the potentially asynchronous and offline nature of page rendering in wq.

By using a central plugin API, @wq/app can ensure that any custom code you incorporate is executed at the right time, no matter whether the referenced page is the first one to load or is loaded via AJAX during jQuery Mobile navigation events.  Most plugin hooks even work the same whether a page is [rendered on the client or on the server][templates].

With all the possible rendering modes in mind, the recommended way to customize specific screens in a wq-powered application is to use a plugin if possible.  If the provided plugin hooks do not work for you, [let us know][contact] and we can discuss implementing additional hooks.

## Plugin Properties
A plugin is defined as a simple object with any or all of the following properties:

name | purpose
-----|--------
`name` | A unique identifier for the plugin.  This is used during app initialization to configure the plugin.  If your plugin will not need to be configured, it does not need a name.
`init(config)` | Callback for use during app initialization.  If a configuration section is defined for the plugin it will be passed to the function.
`context(context, routeInfo)` | Callback to call right before rendering a page on the client.  The `context` argument is the context that was initially going to be used for rendering.  The `routeInfo` argument provides path information about the page (see below).  The callback should return an object with any additional properties to be added to the existing context.  If a `Promise` is returned, it will be resolved before continuing.  The `context` callback is called during jQuery Mobile's `pagebeforechange` / `pagecontainerbeforechange` event, though it does not provide direct access to the underlying event.  Note that this callback is not called at all for pages rendered on the server (during initial load or through AJAX).
`run($page, routeInfo)` | Callback to call right after rendering and displaying a page (the `pageshow` / `pagecontainershow` event in jQuery Mobile).  This is the most commonly used plugin callback, and it should always execute regardless of whether the page was rendered on the client or the server.  `$page` is the equivalent of `$.mobile.activePage` and should be used to scope jQuery lookups to avoid conflicts with any previously rendered pages (i.e., use `$page.find('button#customid')` rather than `$('button#customid')` in your callback).  `routeInfo` is a route information object (see below).
`onsave(item, result)` | Callback to call after an item is synced from the [outbox][@wq/outbox] and before continuing to another screen. `item` and `result` are the same as the arguments passed to `outbox.updateModels()`

Plugins should always be registered via `app.use(plugin)` before calling `app.init()`.

## Available Plugins

wq.app comes with a number of predefined plugins for common use cases.  The most essential plugin is probably [@wq/map], which seamlessly integrates configurable Leaflet maps into the @wq/app page rendering flow.  The full list of included plugins is below.  The source code for each plugin should be useful as a reference for creating your own custom plugin.  In particular, note the use of the `name`, `init()`, `context()`, and `run()` properties on each module.

| Module | Description |
|--------|-------------|
| [@wq/app/patterns] | Support for nested forms |
| [@wq/app/photos] | Helpers for requesting and displaying user photos on a mobile device |
| [@wq/chart] | Configurable d3-based reusable charts, including time series and boxplots |
| [@wq/map] | Leaflet integration for displaying and editing geographic information via GeoJSON |
| [@wq/map/locate] | Utilities for requesting the user's location |
| [@wq/markdown] | Markdown (marked.js) and code syntax highlighting (highlight.js) integration |

## Defining a Custom Plugin

### Simple Example

Plugins can be defined within your app's main.js, especially if they are short and anonymous.

```javascript
// myapp/main.js
define(['wq/app', './config'],
function(app, config) {

var myPlugin = {
    'run': function($page, routeInfo) { /* ... */ }
};

app.use(myPlugin);
app.init(config).then(function() {
    app.jqmInit();
    app.prefetchAll();
});

});
```

### Full Example with AMD

For the sake of modularity, named and otherwise complex plugins are typically defined in a separate JavaScript file and imported via [AMD].  Note that plugins typically do not need to AMD-depend on @wq/app directly, as they will be provided with a reference to @wq/app when they are initialized.  Similarly, plugins usually do not need to depend on jQuery Mobile directly as they will be provided with a jQuery object for the current page when executed.

```javascript

// myapp/myplugin.js
define({
    // Name and init are optional if no config is needed
    'name': 'myPlugin',
    'init': function(config) {
        this.config = config;
    },

    // Customize context before rendering page
    'context': function(context, routeInfo) {
    
        // Limit context to a specific route mode        
        if (routeInfo.page == 'item' && routeInfo.mode == 'edit') {
            // Conduct an asynchronous lookup before rendering.
            // Note: this.app === @wq/app
            return this.app.models.itemtype.load().then(function(itemtypes) {
                // Note: depending on how foreign keys are defined,
                // itemtype_list may be defined already on the default
                // context; this is just an example.
                return {
                    'itemtype_list': itemtypes.list
                };
            });
        } else {
            // No additional context for this page
            return {};
        }
    },

    // Customize page after it is rendered (this is the pageshow or 's' event)
    'run': function($page, routeInfo) {
        $page.find('button#custom').on('click', function() {
            $page.find('div#custom-result').html('clicked');
        });
    },

    // Do something custom after saving a record to the server
    'onsave': function(item, result) {
    }
});

// myapp/main.js
define(['wq/app', './myplugin', './config'],
function(app, myPlugin, config) {

app.use(myPlugin);
app.init(config).then(function() {
    app.jqmInit();
    app.prefetchAll();
});

});
```

### `routeInfo` object

The `routeInfo` object provides all available path information about the current page to both the `context()` and the `run()` plugin callbacks.  The most common use of `routeInfo` is to determine whether the plugin applies to the current route or not.

The following properties are set on the routeInfo object.  Note that some properties are restricted to certain rendering modes.

name | restricted to | description
-----|---------------|-------------
`base_url` | - | The base URL of the application
`path` | - | The current path (relative to the base URL).
`full_path` | - | `base_url` + `path`.
`params` | - | Any URL query parameters (i.e. the part after ?), parsed into a simple object
`page` | - | The name of the current page as listed in the [wq configuration] object
`page_config` | - | The full [wq configuration] for the current page.
`mode` | - | The current rendering mode (usually one of `list`, `detail`, or `edit`)
`item_id` | `detail`/`edit` modes | The unique identifier of the currently rendering item
`item` | `detail`/`edit` modes, `run()` callback | The full current item retrieved from the model.  If the model data is not already stored locally, it will be loaded automatically via a JSON AJAX request.  This property is not provided to the `context()` callback since the same information is already available in the `context` argument.
`parent_id` | `list` mode | The id of the parent record being used to filter the list (see [URL structure])
`parent_page` | `list` mode | The name of the parent page as listed in the [wq configuration] object
`parent_url` | `list` mode | The relative path to the parent record (e.g. `parent_page + 's/' + parent_id`)

[@wq/app]: https://wq.io/docs/app-js

[templates]: https://wq.io/docs/templates
[@wq/outbox]: https://wq.io/docs/outbox-js
[AMD]: https://wq.io/docs/amd
[contact]: https://wq.io/community

[@wq/chart]: https://wq.io/docs/chart-js
[@wq/map/locate]: https://wq.io/docs/locate-js
[@wq/map]: https://wq.io/docs/map-js
[@wq/markdown]: https://wq.io/docs/markdown-js
[@wq/app/patterns]: https://wq.io/docs/patterns-js
[@wq/app/photos]: https://wq.io/docs/photos-js

[wq configuration]: https://wq.io/docs/config
[URL structure]: https://wq.io/docs/url-structure

@wq/app/patterns
==============

[@wq/app/patterns]

**@wq/app/patterns** is a [@wq/app plugin] providing support for [nested forms].

FIXME: WIP

[@wq/app/patterns]: https://github.com/wq/wq.app/blob/master/packages/app/src/patterns.js
[@wq/app plugin]: https://wq.io/docs/app-plugins
[nested forms]: https://wq.io/docs/nested-forms

@wq/app/photos
======

[@wq/app/photos]

**@wq/app/photos** is a [@wq/app plugin] that integrates with the [PhoneGap Camera API] and shows previews for user-selected photos.  Together with the file processing functions in [@wq/app] and [@wq/outbox], @wq/app/photos provides a complete solution for allowing volunteers to capture and upload photos via your offline-capable web or mobile app.  Captured photos are saved in an outbox ([@wq/outbox]) until they can be synchronized to the server.

<div data-interactive id='photo-example'>
  <ul data-role="listview">
    <li class="ui-field-contain">
      <img src="https://wq.io/images/empty.png" id="preview">
      <label for="image">Photo</label>
      <input type="file" name="photos" id="image" data-wq-preview="preview">
    </li>
  </ul>
</div>

The [Species Tracker](http://species.wq.io) application provides a complete demonstration of the offline capabilities of @wq/app/photos.

### API
@wq/app/photos does not require a global configuration, as it is configured via data-wq- attributes on the related form elements.

```javascript
// myapp/main.js
define(['wq/app', 'wq/photos', './config'],
function(app, photos, config) {

app.use(photos);

app.init(config).then(function() {
    app.jqmInit();
    app.prefetchAll();
});

});
```

To leverage @wq/app/photos in your template, create your form elements with data-wq- attributes.  If you are using [wq.start] and the default project layout, these will be set automatically in the generated edit templates for models with image fields.

element | attribute | purpose
--------|-----------|---------
`<input type=file>` | `data-wq-preview` | Indicates the id of an `<img>` element to display a preview image in after a photo is selected.
`<button>` | `data-wq-action` | Indicates the function to call (`take` or `pick`) when the button is clicked
`<button>` | `data-wq-input` | The name of a hidden input to populate with the name of the captured photo.  (The photo itself will be saved in offline storage).
`<input type=hidden>` | `data-wq-type` | Notifies @wq/app that the hidden element is intended to be interpreted as the name of a photo captured via @wq/app/photos.  The element should typically have the `data-wq-preview` attribute set as well.

The `take` and `pick` actions are wrappers for PhoneGap/Cordova's camera.getPicture() API, meant to be used in hybrid apps where <input type=file> doesn't work (e.g. on older devices or broken Android implementations).

Below is an example template with the appropriate attributes set:

```xml
<img id=preview-image>

{{^native}}
<input type=file name=file data-wq-preview=preview-image>
{{/native}}

{{#native}}
<button data-wq-action=take data-wq-input=file>
  Take Picture
</button>
<button data-wq-action=pick data-wq-input=file>
  Choose Picture
</button>
<input id=filename type=hidden name=file
   data-wq-type=file data-wq-preview=preview-image>
{{/native}}
```

Note the use of the `{{#native}}` context flag which is set automatically by [@wq/app].  See the [Species Tracker template](https://github.com/powered-by-wq/species.wq.io/blob/master/templates/partials/new_photo.html) for a working example.

## Browser Compatibility Notes
@wq/app/photos, and the related file processing functions in [@wq/app] and [@wq/outbox], rely heavily on two browser features:
 - Offline storage (see Browser Compatibility Notes for [@wq/store])
 - Binary [Blob] support, including the ability to upload `Blob`s via AJAX.

All modern browsers, including Internet Explorer 10, Android 4.4, and later versions, have at least [minimal blob support](https://github.com/nolanlawson/state-of-binary-data-in-the-browser).  For IE 9 and older desktop browsers, the preview functionality in @wq/app/photos will not work, but users should still be able to upload files.  @wq/app will detect the lack of `Blob` support on these browsers and fall back to a normal form post when a `<form>` containing an `<input type=file>` is encountered.  (wq.db's [ModelViewSet] includes built-in support for responding to forms posted in this way).

[Blob]: https://developer.mozilla.org/en-US/docs/Web/API/Blob
[@wq/app/photos]: https://github.com/wq/wq.app/blob/master/packages/app/src/photos.js
[@wq/app plugin]: https://wq.io/docs/app-plugins
[@wq/app]: https://wq.io/docs/app-js
[PhoneGap Camera API]: https://www.npmjs.com/package/cordova-plugin-camera
[camera.getPicture()]: https://www.npmjs.com/package/cordova-plugin-camera
[@wq/outbox]: https://wq.io/docs/outbox-js
[@wq/store]: https://wq.io/docs/store-js
[ModelViewSet]: https://wq.io/docs/views
[wq.start]: https://wq.io/wq.start
