General app development tutorial

Introduction

Unlike applications for platforms like iOS and Android, the first-tier languages of Web applications are HTML, CSS, and JavaScript. These languages are supported by a worldwide web of developer resources. This "Web stack" is available on all modern mobile and desktop hardware and it enables you to maximize code reuse and potentially reach a massive audience on different platforms and devices with little additional effort. In this tutorial we'll explore the different aspects of web app creation, the strategies for designing a flexible, functional app, and coding an app to completion.

Over the course of the tutorial, we'll be employing these techniques and technologies to create a Twitter-based application meant for desktop, mobile phone, and mobile tablet devices. This app will show tweets from selected geographic areas, and is called Area Tweet. Once the app is completed, we'll create the appropriate manifest for submitting our app to the Firefox Marketplace!

Tutorial steps

1. Foundations of an HTML5 Web app 2. Configuring your development environment 3. App design 4. App code 5. Creating the Area Tweet app 6. Debugging the app 7. Testing the app 8. Publishing the app 9. Maintaining the app 10. Profiting from your app 11. Create your app! Foundations of an HTML5 Web app

An Open Web App is basically an HTML5 app. While the role of HTML, CSS, and JavaScript are obvious to some, let's do a quick recap of how each plays its own important role in an HTML5 app.

HTML5 for content and structure

HTML is used to create the content structure for the web application. New features in HTML5 include new types, form validation support, media tags like

 HTML documentation  HTML5 features  Learn HTML  HTML5 Rocks!  HTML5 Test

 HTML5 Boilerplate  HTML Living Standard  Simple HTML5 Guide

CSS3 for style

CSS provides the visual presentation, letting you control the basic layout of content. CSS can do more than just format content though; you can use the power of CSS to animate elements, create precise gradients to avoid having to unnecessarily use images, and create media queries which let you adjust the appearance of your content based on the type of device it's being viewed on.

 CSS documentation  CSS Reference  CSS Animations Specification  CSS Gradient Generator  CSS Media Queries Specification  Media Queries for Standard Devices  Mobile Safari Supported CSS Properties

JavaScript for dynamic interaction JavaScript provides the level of dynamism and user interaction for the web app. JavaScript lets you listen for and respond to user interactions, load content dynamically, and get access to device- specific features, like the Camera API and Geolocation API.

 JavaScript Guide  JavaScript Reference

 All JavaScript documentation on MDN  Canvas Tutorial  Element.classList API  DOM Storage API (localStorage object)  Writing Modular JavaScript With AMD, CommonJS & ES Harmony  Battery Status API  document.querySelectorAll API  Using the Page Visibility API  WebSockets

It's important to note that support for individual features varies from browser to browser; feature detection and research should be done before using each feature. How to use these strategies will be covered in depth below. App design

There are many considerations to ponder before designing your web app.

Design considerations

Device support and design emulation

Knowing the types of devices your application should work on is of utmost importance. Knowing the target devices and their capabilities tells us what features we can likely count on, what UI components should look like, and so on. Many mobile JavaScript frameworks, Dojo Mobile being one of them, provide multiple device themes for easy device theme implementation. Even if you prefer not to use the given JavaScript framework, you can still study the device theme stylesheet and borrow styles as desired.

Orientation support

Orientation support is a huge consideration when creating a web app capable of working on mobile devices. One obvious orientation consideration is that desktop apps use horizontal orientation by default, whereas most mobile devices use portrait as the default orientation. An ideal web application looks good and works well in both orientations. Both JavaScript and CSS media queries provide developers the capability to detect and react to orientation changes as they happen.

Gestures vs. simple touch events

Consider the navigation interaction paths you want for your application. Will the application be navigable by button touches or gestures? The answer to that question decides whether your application must make room for static controls or must subtly communicate to the user that gestures should be available. JavaScript libraries like jGestures exist to aid in gesture detection.

Basic app design principles

Regardless of the specific devices you plan to support, there are a few basic principles which should be obeyed when creating web applications:

 Avoid pixel-based widths when designing for multiple devices and/or multiple orientations  Use vector graphics so that their dimensions may be elegantly adjusted between devices  Don't assume features on any device; feature detection is still important  The more flexible the app design, the more portable it will be when considering broader device support

The design of our app will be very typical, with two panes. The first pane will provide a search box for field for entering the user's location, as well as a list of previous location searches. The second pane will simply show a list of tweets from Twitter. Each pane will use a standard heading.

Our app will also be dynamic in that it will lay out properly in both landscape and portrait orientation, respond to a simple swipe gesture, and display reasonably on desktop and mobile devices. The file structure of the app will look like this:

- app - app.css - app.manifest - app.js - images/ - icon-16.png - icon-48.png - icon-128.png - index.html The structure of your application may vary. Since this is a small sample application, there's no need for more specific directory structures. The images are provided in different sizes for different devices. For example, the icon-128.png file will be used for Mac OS X desktop installations. App code

Outside of the different colors and imagery that apps use, an app's design isn't necessarily dynamic: the principles of designing a flexible app are largely the same. But the style and tools a developer uses in coding an application is a completely different story. Numerous libraries and frameworks are available for coding mobile apps.

JavaScript libraries jQuery Mobile jQuery's official mobile framework is jQuery Mobile. jQuery Mobile provides a unified, HTML5-based user interface system for all popular mobile device platforms. jQuery Mobile also provides a theme builder for application design as well as development.

 jQuery Mobile Blog  jQuery Mobile API  jQuery API

Dojo Mobile

Dojo Mobile is the 's mobile framework, boasting a series of flexible views and widgets, automatic device detection and themes, and more. The Dojo Mobile Showcase shows advanced Dojo Mobile usage in action.

 Getting Started with dojox/mobile  Dojo Mobile Documentation  Dojo Mobile 1.8 Work-In-Progress  Dojo Toolkit API

MooTools Moobile

The unofficial mobile framework of MooTools is called Moobile. Moobile provides a mobile application boilerplate as well as a host of simple mobile widgets.

 MooTools Mobile  MooTools Documentation  MooTools Forge Numerous other mobile JavaScript frameworks are available to aid the development of mobile web apps. When looking for a good mobile web app JavaScript framework, look for multi-device support, touch and gesture event support, and a unified API with its host framework.

The web app built in this tutorial will utilize jQuery Mobile. Experiment and evaluate different mobile frameworks based on the goals of your app before selecting one (or deciding against using any at all). In many cases, a framework app will be save you time; in other cases, a framework could be overkill.

CSS libraries, helpers, and techniques

CSS plays an important role in any web-based technology. There are a variety of CSS libraries than may aid you in creating, maintaining, and organizing your CSS.

CSS preprocessors

While CSS is a simple language, its lack of variables and the need for repeated style assignments with vendor prefixes can make CSS difficult to maintain—a pain no mobile app developer needs. CSS preprocessors ease development of apps, allowing for better code organization and faster development.

With one of those CSS preprocessors, LESS, CSS can be written like this:

@someColor: #000; /* color var */

.rounded-corners (@radius: 5px) { /* function */ border-radius: @radius; -webkit-border-radius: @radius; -moz-border-radius: @radius; } p { color: @someColor; a { color: #fff; background: @someColor;

.rounded-corners; } }

This gets converted into the following by the LESS preprocessor: p { color: #000; } p a { color: #fff; background: #000; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }

While LESS is the most popular CSS preprocessor, SASS and Stylus are also available and useful.

CSS3 techniques

Web apps may take advantage of numerous CSS techniques that allow for enriched design and strategies for avoiding the need for imagery.

Gradients

CSS3 provides the ability to create linear and radial gradients with some simple CSS code: h1 { background-image: linear-gradient(#6facd5, #497bae); } Box and text shadows

Box shadows and text shadows provide a subtle depth enhancement to block elements and text:

.shadower { box-shadow: 10px 5px 5px #000; text-shadow: 1px 1px 2px black, 0 0 1em blue, 0 0 0.2em blue; color: white; font: 1.5em Georgia, "Bitstream Charter", "URW Bookman L", "Century Schoolbook L", serif; } Shapes with CSS

CSS also provides the ability to cleverly create shapes likes triangles and circles:

.arrowRight { width: 0; height: 0; border-bottom: 5px solid transparent; border-top: 5px solid transparent; border-left: 5px solid #2f2f2f; font-size: 0; line-height: 0; }

.circle { border-radius: 50%; width: 200px; height: 200px; } Creating responsive layouts

Coding responsive layouts is paramount in creating flexible apps that work on multiple devices and in multiple orientations. The following are some techniques for creating such a design. Our desktop layout

Our mobile device & phone layouts

Percentage widths

Fixed pixel widths should rarely be used. Percentage widths are preferred as they let elements adjust to device viewport dimensions. Fixed widths (and heights) should generally be used only for small, genuinely fixed-sized items.

Viewport size-dependent element display

Showing and hiding elements based on viewport size is a common practice. Instead of cramming a lot of content into a smaller space, or changing an app's design completely, simply hiding those "supporting" elements may be the best option.

Viewport size-dependent imagery

Adapting image sizes to the device viewport size is also a common practice, making vector images (using SVG, for example) the best practice for your web app. Media queries

CSS media queries let developers adjust layout and element styles to viewport size, orientation, aspect ratio, and more:

/* screen size media queries */ @media only screen and (min-device-width : 768px) and (max-device-width : 1024px) {

}

/* orientation media queries */ @media (orientation:portrait) {

} @media (orientation:landscape) {

}

/* advanced media query usage */ @media all and (max-width: 699px) and (min-width: 520px), (min-width: 1151px) {

}

/* aspect ratio */ @media screen and (device-aspect-ratio: 16/9) {

} orientationchange and resize events

Orientation changes can also be detected with JavaScript. Most browsers fire an orientationchange event while all browsers are known to fire the resize event. window.addEventListener("resize", function() { // Detect orientation based on width var orientation = window.innerWidth > window.innerHeight ? "landscape" : "portrait";

// Execute more code accordingly... }, false);

Web app bootstraps

Server side processing of information can be handled by the server side technology of your choice. Popular server side languages include PHP, Python, and NodeJS + express. There's a likelihood that your application will want to implement Mozilla Persona for app sign in. App manifests

Apps submitted to the Firefox Marketplace require an application manifest. This manifest contains basic information that a web browser needs to interact with an app. Manifests are written in JSON. A sample manifest could look like this:

{ "version": "0.1", "name": "Area Tweet", "description": "Find tweets by for any location", "icons": { "16": "http://areatweet.com/images/icon-16.png", "48": "http://areatweet.com/images/icon-48.png", "128": "http://areatweet.com/images/icon-128.png" }, "developer": { "name": "David Walsh", "url": "http://areatweet.com" }, "installs_allowed_from": [ "https://marketplace.firefox.com", "http://areatweet.com" ], "default_locale": "en" }

The application manifest lives on the host server, not the Firefox Marketplace. It's recommended that your manifest be saved with a .webapp extension. The manifest must be served with a Content-Type HTTP header of application/x-web-app-manifest+json. Use of HTTPS is recommended.

A complete listing of all manifest properties can be found here. There's also a Frequently Asked Questions document which provides tips for creating your manifest and explanations for why the manifest hosting process is the way it is.

Use absolute paths within the manifest, even for images. The Firefox Marketplace will not currently accept manifests with relative paths.

Application cache

See Using the application cache for details.

Navigation When an Open Web App is running in the Web runtime, no navigation controls are displayed. If you want to detect this, check the window.locationbar.visible property. If window.locationbar.visible is false, no navigation is displayed. You might want to check for this so you can provide navigation controls for the app user. This is also one way to check if the app is installed, or to check if the app is running in the Web runtime.

For other ways to check if you are running inside an app, see How can my app detect that it is in the Web runtime?

Creating the Area Tweet app

With all of the design goals and coding standards laid out, it's time to get into the coding of our app.

HTML

As with any Web application template, we need an HTML page with the HTML5 doctype, , and elements.

We'll be using jQuery Mobile as the JavaScript framework. That means we must pull in the CSS and JavaScript resources required by jQuery Mobile:

With our basic application boilerplate and jQuery Mobile resource in place, it's time to add content and widgets to the page. Since we've decided to use jQuery Mobile for our application framework, our app's HTML structure will follow their prescribed widget structures. The first pane provides a search box and a history list:

Area Tweet

Location Search

Previous Locations Clear History

    There are a few things to notice with the code above:

     The structure of the HTML5 sticks to semantics, which ensures maximum accessibility.  As with any Web-based app, we assign CSS classes and IDs to elements we want to style and to access to from JavaScript code.

    If you have questions about individual jQuery Mobile capabilities, node attributes, or structures, please consult the jQuery Mobile Documentation.

    The second pane will be used for displaying a list of tweets:

    Back

    Tweets

      CSS

      Our Web app's CSS contains a CSS animation for fading in any element, as well as for overriding jQuery Mobile styles and general element styling.

      /* animations */ @-moz-keyframes fadeIn { 0% { opacity: 0; } 100% { opacity: 1; } } @-webkit-keyframes fadeIn { 0% { opacity: 0; } 100% { opacity: 1; } }

      .fadeIn { -moz-animation-name: fadeIn; -moz-animation-duration: 2s;

      -webkit-animation-name: fadeIn; -webkit-animation-duration: 2s;

      opacity: 1 !important; }

      /* Custom CSS classes */ .clear { clear: both; } .hidden { display: none; } .opaque { opacity: 0; }

      /* Basic styling */ #prevLocationsContainer { margin-top: 40px; }

      /* Customizing jQuery Styles */ #tweetsList li.ui-li-divider, #tweetsList li.ui-li-static { font-weight: normal !important; }

      /* Tweet list */ #tweetsList li {

      }

      #tweetsList li .tweetImage { float: left; margin: 10px 10px 0 10px; }

      #tweetsList li .tweetContent { float: left; /* margin: 10px 0 0 0; */ }

      #tweetsList li .tweetContent strong { display: block; padding-bottom: 5px; }

      /* Media Queries for device support */ @media screen and (max-device-width: 480px) { #prevLocationsContainer { margin-top: 10px; } } JavaScript

      JavaScript is the last major component to add to our Web app. The following encompasses all capabilities for the app:

      $(document).ready(function() {

      // Start from scratch on page load if(window.location.hash) { window.location = "index.html"; }

      // Feature tests and settings var hasLocalStorage = "localStorage" in window, maxHistory = 10;

      // List of elements we'll use var $locationForm = $("#locationForm"), $locationInput = $("#locationInput"),

      $prevLocationsContainer = $("#prevLocationsContainer"),

      $tweetsHeadTerm = $("#tweetsHeadTerm"), $tweetsList = $("#tweetsList");

      // Hold last request jqXHR's so we can cancel to prevent multiple requests var lastRequest;

      // Create an application object app = {

      // App initialization init: function() { var self = this;

      // Focus on the search box focusOnLocationBox();

      // Add the form submission event $locationForm.on("submit", onFormSubmit);

      // Show history if there are items there this.history.init();

      // When the back button is clicked in the tweets pane, reset the form and focus $("#tweetsBackButton").on("click", function(e) { $locationInput.val(""); setTimeout(focusOnLocationBox, 1000); });

      // Geolocate! geolocate();

      // When the tweets pane is swiped, go back to home $("#tweets").on("swiperight", function() { window.location.hash = ""; });

      // Clear history when button clicked $("#clearHistoryButton").on("click", function(e) { e.preventDefault(); localStorage.removeItem("history"); self.history.hideList(); }) },

      // History modules history: { $listNode: $("#prevLocationsList"), $blockNode: $("#homePrev"), init: function() { var history = this.getItemsFromHistory(), self = this;

      // Add items to the list if(history.length) { history.forEach(function(item) { self.addItemToList(item); }); self.showList(); }

      // Use event delegation to look for list items clicked this.$listNode.delegate("a", "click", function(e) { $locationInput.val(e.target.textContent); onFormSubmit(); }); }, getItemsFromHistory: function() { var history = "";

      if(hasLocalStorage) { history = localStorage.getItem("history"); }

      return history ? JSON.parse(history) : []; }, addItemToList: function(text, addToTop) { var $li = $("

    • " + text + "
    • "), listNode = this.$listNode[0];

      if(addToTop && listNode.childNodes.length) { $li.insertBefore(listNode.childNodes[0]); } else { $li.appendTo(this.$listNode); }

      this.$listNode.listview("refresh"); }, addItemToHistory: function(text, addListItem) { var currentItems = this.getItemsFromHistory(), newHistory = [text], self = this, found = false;

      // Cycle through the history, see if this is there $.each(currentItems, function(index, item) { if(item.toLowerCase() != text.toLowerCase()) { newHistory.push(item); } else { // We've hit a "repeater": signal to remove from list found = true; self.moveItemToTop(text); } });

      // Add a new item to the top of the list if(!found && addListItem) { this.addItemToList(text, true); }

      // Limit history to 10 items if(newHistory.length > maxHistory) { newHistory.length = maxHistory; }

      // Set new history if(hasLocalStorage) { // Wrap in try/catch block to prevent mobile safari issues with private browsing // http://frederictorres.blogspot.com/2011/11/quotaexceedederr-with-safari- mobile.html try { localStorage.setItem("history", JSON.stringify(newHistory)); } catch(e){} }

      // Show the list this.showList(); }, showList: function() { $prevLocationsContainer.addClass("fadeIn"); this.$listNode.listview("refresh"); }, hideList: function() { $prevLocationsContainer.removeClass("fadeIn"); }, moveItemToTop: function(text) { var self = this, $listNode = this.$listNode;

      $listNode.children().each(function() { if($.trim(this.textContent.toLowerCase()) == text.toLowerCase()) { $listNode[0].removeChild(this); self.addItemToList(text, true); } });

      $listNode.listview("refresh"); } }

      };

      // Search submission function onFormSubmit(e) { if(e) e.preventDefault();

      // Trim the value var value = $.trim($locationInput.val());

      // Move to the tweets pane if(value) {

      // Add the search to history app.history.addItemToHistory(value, true);

      // Update the pane 2 header $tweetsHeadTerm.html(value);

      // If there's another request at the moment, cancel it if(lastRequest && lastRequest.readyState != 4) { lastRequest.abort(); }

      // Make the JSONP call to Twitter lastRequest = $.("http://search.twitter.com/search.json", { cache: false, crossDomain: true, data: { q: value }, dataType: "jsonp", jsonpCallback: "twitterCallback", timeout: 3000 }); } else { // Focus on the search box focusOnLocationBox(); }

      return false; }

      // Twitter reception window.twitterCallback = function(json) {

      var template = "

    • " +"{from_user}{text}
    • ", tweetHTMLs = [];

      // Basic error handling if(json.error) { // Error for twitter showDialog("Twitter Error", "Twitter cannot provide tweet data."); return; } else if(!json.results.length) { // No results showDialog("Twitter Error", "No tweets could be found in your area."); return; }

      // Format the tweets $.each(json.results, function(index, item) { item.text = item.text. replace(/(https?:\/\/\S+)/gi,'$1'). replace(/(^|\s)@(\w+)/g,'$1@$2'). replace(/(^|\s)#(\w+)/g,'$1#$2') tweetHTMLs.push(substitute(template, item)); });

      // Place tweet data into the form $tweetsList.html(tweetHTMLs.join(""));

      // Refresh the list view for proper formatting try { $tweetsList.listview("refresh"); } catch(e) {}

      // Go to the tweets view window.location.hash = "tweets"; };

      // Template substitution function substitute(str, obj) { return str.replace((/\\?{([^{}]+)}/g), function(match, name){ if (match.charAt(0) == '\\') return match.slice(1); return (obj[name] != null) ? obj[name] : ""; }); }

      // Geolocates the user function geolocate() { if("geolocation" in navigator) { // Attempt to get the user position navigator.geolocation.getCurrentPosition(function(position) { // Set the address position if(position.address && position.address.city) { $locationInput.val(position.address.city); } }); } }

      // Focuses on the input box function focusOnLocationBox() { $locationInput[0].focus(); }

      // Modal function function showDialog(title, message) { $("#errorDialog h2.error-title").html(title); $("#errorDialog p.error-message").html(message); $.mobile.changePage("#errorDialog"); }

      // Initialize the app app.init();

      });

      We won't go over every part of the app's JavaScript, but a few items are worth pointing out:

       The script uses feature testing for both localStorage and geolocation. The app will not attempt to use either feature if they aren't present in the browser.  The geolocation API is used to detect the user's current location and the localStorage API is used to keep the user's search history.  The JavaScript has been written in a modular manner for easier testing.  A swipe event is listened for on the tweet pane. When the pane is swiped, the user is taken back to the first pane.  Twitter is the source of all data. No custom server-side processing is used for this app. Debugging the app

      For debugging an app the first step is, of course, to use a full-capabilities desktop browser such as Firefox with all its available tools. But once the app is installed on a platform (cf. Platform-specific details of app installation) it becomes a native app, it is run from inside the webapp runtime and things get more complicated.

      To debug problems when an app is running inside the webapp runtime one can run it from the command line by enabling the old (but still useful) Error Console.

      For example if an App comes from http://areatweet.com/ and is installed in $HOME/.http\;areatweet.com/ (this is the case for a Linux Desktop cf. Platform-specific details of app installation) the webapp runtime will be found in this directory. One should then run the webapp runtime with the debugging option as follows:

      $ ~/.http\;areatweet.com /webapprt-stub -jsconsole

      Note that on Windows the webapp runtime is not named webapprt-stub but is named after the name of the App, but the principle remains the same.

      Read Hacking Firefox OS for more information on debugging.

      See also

       Platform-specific details of app installation  Hacking Firefox OS  See bug 829573: When using the Web runtime, console methods should output to stdout

      Testing the app

      App testing is of immense importance, especially when supporting multiple platforms and devices. The number of tests and types of testing needed depend on the devices and platforms supported. Test types include:

       Speed  Performance efficiency  Input and output validation  Touch and interactivity While those are the basic test types, there are more ideas to consider while testing.

      Web environment differences

      The following are some things to keep in mind when dealing with different web environments.

      Vendor prefixes

      Both advanced JavaScript and CSS features in browsers can be vendor-prefixed with - webkit, -moz, -o, or -ms. Know the environments you plan to use and prefix features appropriately.

      Note: You should try to avoid using features that are only available on certain devices, unless you either provide appropriate fallbacks or specifically intend your app to be used on a more limited set of devices. Feature detection

      Some environments will support a specific feature, others may not. Feature detection is the best way of knowing which tools are available for a given platform. For example, to detect support of the Geolocation API, you would code: if("geolocation" in navigator) { // Geolocation available, use it! }

      CanIUse.com provides detailed tables of browser and device support for specific features. A helping library like Modernizr automatically detects features upon page load and provides that information accordingly.

      Responsive design

      Using media queries and foresight in design will prevent design problems. Common pitfalls include not using vector graphics for elements that can grow or shrink, using fixed- dimension elements on all devices, not testing different orientations, and simply not looking at your app at different resolutions. Services like Screenfly and responsivepx help aid in testing an app at different sizes, but there's no substitute for having a supported device handy for manual testing.

      Consider also the problems that can arise when different devices have different screen resolutions. A case in point: the problems that cropped up when Apple introduced the third-generation iPad, with its new 2048x1536 display:  New York Times: Is The New iPad Screen Too Good For The Web?  Net Magazine: Web devs battle new iPad's Retina Display

      See also:

       Responsive Web design  Responsive design for mobile  Intro to responsive design  Responsive Design View, one of the Firefox Developer Tools

      Unit testing

      Unit testing is a common practice in all walks of development, and web app testing is no different. Unit testing of CSS and JavaScript is also very easy when an application is coded in a modular fashion. Popular JavaScript unit testing frameworks include Jasmine, QUnit, and YUI Test. Each unit testing website provides code samples for how to use their test framework.

      Performance

      Performance testing can be difficult to prescribe since it's wholly dependent upon the tasks performed by the app. Basic web coding principles like minimizing HTTP requests (JavaScript concatenation or CSS sprites help), JS and CSS minification, placing scripts at the bottom of the page, and properly setting Expires headers all apply. The YSlow team provides more helpful web performance best practices to follow, all of which will enhance your web app. The HTML5Rocks website also provides a list of performance best practices.

      Publishing the app

      Publishing the app

      Once you have finished your app, you need to do the following:

       Deploy the app (hosted apps only) - Arrange for Web hosting and get the app up and running on the Web server. Packaged apps do not require hosting.  Publish the app - Make the app available for end users to install.

      Deploying the app

      You need to host a hosted app on a publicly accessible Web server, just like any other website. There are numerous ways to do this. GitHub

      If a hosted app is purely static (HTML, CSS, JavaScript, but no server-side processing), GitHub Pages is a solid hosting option. It will serve your manifest with the correct MIME type if you give it a .webapp extension.

      Generic hosting solutions

      For an app that needs a server-side processing, use a generic hosting option (like a Web server you may already own or have access to) with the right capabilities, or a hosting provider specifically tailored to the needs of your app, like Heroku or Google App Engine.

      Publishing the app

      Once your Web app has been deployed, you obviously want to make it installable by your intended user base. There are currently two options for publishing your app.

      Submit to Firefox Marketplace

      Submitting your app to the Firefox Marketplace has many advantages, such as increased publicity, no need to implement special APIs on your own Web site, and the possibility of publishing paid apps more easily.

      Publish on your own website

      If you want to let people install your app from a website you own, you must implement some JavaScript code on your Web site to manage installing and updating your app into users' browsers.

      Maintaining the app

      Managing and maintaining your web app is as important as releasing it. Bugs will pop up, users will request more features, and over time, new devices will be released and their software updated.

      App update process

      This section needs to be written once the marketplace is closer to complete.

      Receiving and responding to user feedback

      Receiving and responding to user feedback is an integral part of engaging users. User feedback provides invaluable insight into:  What the general reception of the app is  Criticisms of the app  Ideas for improvement and feature requests  Device-specific bugs

      The right response can elicit buzz and faithfulness from app purchasers; an unprofessional or negative response can ruin your chance to fix app problems.

      App refunds

      Firefox Marketplace users are entitled to refunds within 30 minutes of purchases. After that 30 minutes, developers can review refund requests on an individual basis. Read the Marketplace Payments FAQ for more information about payments and refunds.

      Profiting from your app

      Creating Web apps is also a great way to turn your passion into revenue!

      Firefox Marketplace

      The Firefox Marketplace is an outstanding marketplace for Web app developers. The Firefox Marketplace supports both paid and free apps, in-app purchasing, a variety of device platform apps, and more. The Firefox Marketplace provides competitive pricing and offers consumers a variety of payment methods. Read the Marketplace Payments FAQ to learn more about monetizing your app on the Firefox Marketplace.

      Paid apps

      If you charge for your app on the Marketplace, the buyer will receive a digital receipt after paying for it. Your app should check this receipt to make sure the app is paid for. Usually this is done when the app starts. This tutorial does not cover how to check receipts. For more information, see Validating a receipt.

      In-app payments

      In-app payments are another way to monetize your app with the added bonus of enhancing an existing app instead of requiring the user to purchase a new one. Mozilla's in- app payment model works very similarly to Google's In-Apps Payments system. The Firefox Marketplace system flows as follows:

       The app initiates a payment by signing a JWT request and calling a JavaScript function  This starts the buyflow in a lightbox (served from an