As I discussed in "Build a jQuery HTML5 Web Application: The Account at a Glance App," it's important for web developers to get comfortable working with the latest technologies, such as HTML5, Cascading Style Sheets Level 3 (CSS3), and JavaScript, so that they can build leading-edge business applications. The Account at a Glance application, introduced in last month's article, demonstrates the use of a number of modern web development technologies. In the previous article in this series, I talked about the back-end technologies used to build the Account at a Glance app: Entity Framework 4.1 Code First and ASP.NET MVC 3. I also covered the data-access techniques used to query the database, such as the Repository Pattern, as well as how C# model objects were converted into JavaScript Object Notation (JSON) using ASP.NET MVC. In this article, I'll focus on the client-side aspect of the application and demonstrate a few of the key HTML5, JavaScript, and jQuery features that it offers.

As a quick recap, the Account at a Glance application started as a simple whiteboard idea, shown in Figure 1 (this was my good friend John Papa's office whiteboard at Microsoft). John, Corey Schuman, Jarod Ferguson, and I then took the whiteboard concept and created the final application, shown in Figure 2. The application relies on multiple technologies, such as Entity Framework, ASP.NET MVC, JSON, Ajax, jQuery, JavaScript patterns, HTML5 semantic tags, canvas, Scalable Vector Graphics (SVG), video, and more.

Figure 1: The initial application concept on the whiteboard

Client-Side Object Framework

Account at a Glance has a client-side layout framework that is responsible for animating tiles, performing drag-and-drop operations, and changing scenes. The application initially loads with a "cloud-view" scene, then dynamically changes to the scene shown in Figure 2.

Figure 2: The Account at a Glance application screen

Figure 3 shows the key files used in the client-side framework.

Figure 3: Client-side objects used in the Account at a Glance application
Script Purpose
scene.startup.js Performs start-up logic and animations. It also handles switching between tile scenes.
scene.layoutservice.js Defines two different tile scenes, including the one shown in Figure 1, as well as a "cloud-view" scene that is displayed when the application first loads. Details about formatting tiles, positions, colors, and more are defined in this script using JSON objects.
scene.statemanager.js The engine that handles creating tiles dynamically at runtime as well as drag-and-drop functionality and changes between scenes.
scene.dataservice.js Handles performing Ajax calls to the server. As JSON data is retrieved, callback functions are invoked that process the data. This script is similar to a proxy object used to call services in other application frameworks.
scene.tile.renderer.js Tiles created within scene.tile.binder.js are rendered using this script, depending upon what tile size is requested. This script doesn't know how to get the data or HTML for a tile template. It's responsible for placing templates into the appropriate container within the page and calling any required formatters to provide custom formatting functionality.
scene.tile.formatter.js Several tiles require custom formatting after they have been rendered by scene.tile.renderer.js. For example, some decimal values need to be formatted as a currency, and canvas charts need to be generated for quote tiles. This script is called as tiles are rendered to handle formatting data, generating SVG and canvas charts, and more.

Here's how the different scripts are used from start to finish:

  1. The scene.startup.js script is loaded by the main page.
  2. Scene.statemanager.js loads all the information about the tiles from scene.layoutservice.js. This includes knowing how to render tiles in "cloud view" and "tile view." Each tile has two "scenes" defined, which control where the tile is positioned in the page.
  3. Scene.statemanager.js also handles calling into a DataService object located in scene.dataservice.js, which is responsible for accessing JSON data on the server using jQuery's Ajax functionality.
  4. Once JSON data is available, scene.tile.binder.js handles the downloading of each tile's HTML template and binds the JSON to the template using jQuery Templates functionality.
  5. Once the tiles have data, scene.tile.renderer.js handles rendering of the tiles in the page based upon the target size (each tile has three different potential sizes).
  6. Finally, tiles that need special formatting (such as canvas or SVG rendering) call into scene.tile.formatter.js, which performs custom functionality for different tiles.

The scene.layoutservice.js file contains a JSON array that defines all the tiles shown earlier in Figure 2. The tile's unique ID, layout scenes, and formatter (used for custom functionality) are defined in the file. Figure 4 shows an example of a single tile's definition.

Figure 4: Defining tiles in the scene.layoutservice.js file
{ name: 'Account Details',
    tileId: 'AccountDetails',
    formatter: TileFormatter.formatAccountDetails,
    scenes: [
        { height: s1Mh, width: s1Mw, top: 0, left: 0, opacity: 1, size: 1,
          borderColor: '#5E1B6B', z: 0 },
        { height: 90, width: 210, top: 80, left: 250, size: 0,
          borderColor: '#5E1B6B', z: '2000', opacity: .5 }
    ]
}

The tile data in scene.layoutservice.js is processed by scene.statemanager.js, which handles iterating through the JSON array to process each tile and generate a div container for it. Once that's done, a call is made to a DataService object to retrieve JSON data from the server; this data is then fed into a renderTile() function. Figure 5 shows a few key functions from scene.statemanager.js. (Note that this sample has been minimized because of space constraints -- you can find the complete file in the sample code download for more details. See the Downloading the App section of this article for the code-download URL.)

Figure 5: Starting the data-retrieval and tile-rendering process
renderTiles = function(acctNumber) {
    DataService.getMarketIndexes(renderMarketTiles);
}

renderMarketTiles = function(json) {
    renderTile(json, $('#DOW'), 0);
    renderTile(json, $('#NASDAQ'), 0);
    renderTile(json, $('#SP500'), 0);
}

renderTile = function(json, tile, fadeInAmount) {
    TileBinder.bind(tile, json, TileRenderer.render);
}

Looking through the code in Figure 5, you can see that it calls a TileBinder object (located in scene.tile.binder.js). Figure 6 shows the scene.tile.binder.js script that is used to convert JSON data retrieved from the server to HTML using jQuery templates. The TileBinder object (as well as others within the Account at a Glance application) follows the JavaScript Revealing Module Pattern detailed here.

Figure 6: Scene.tile.binder.js, which uses jQuery templates to bind JSON data to HTML templates
//Handles loading jQuery templates dynamically from server
//and rendering them based upon tile data
var TileBinder = function () {
    var templateBase = '/Templates/',

    bind = function (tileDiv, json, renderer) {
        var tileName = tileDiv.attr('id');
        $.get(templateBase + tileName + '.html', function (templates) {
            $('body').append(templates);
            var acctTemplates = [
                tmpl(tileName, 'Small', json),
                tmpl(tileName, 'Medium', json),
                tmpl(tileName, 'Large', json)
            ];
            tileDiv.data().templates = acctTemplates;
            tileDiv.data().json = json;
            renderer(tileDiv);
        });
    },

    tmpl = function (tileName, size, json) {
        var template = $('#' + tileName + 'Template_' + size);
        if (json != null)
            return template.tmpl(json);
        else
            return template.html();
    };

    return {
        bind: bind
    };
} ();

The bind() function shown in Figure 6 receives the tile that must be rendered, the JSON data used to render it, and a renderer object that handles placing rendered HTML into the appropriate container in the page displayed to the user. The bind() function uses the jQuery get() function to call the server and retrieve the small, medium, and large templates for a given tile.

The different template sizes for a tile are defined in a single file that lives within the AccountAtAGlance project's Templates folder. Figure 7 shows an example of templates used to render the S&P 500 tiles. The ${ token } token syntax found in each of the templates defines the JSON properties that should be bound once each template is rendered. See my blog post "Reducing Code by Using jQuery Templates" for more details about jQuery templates.

Figure 7: HTML templates used along with jQuery template functionality to render the tiles
<script id="SP500Template_Small" type="text/x-jquery-tmpl">
    <div class="content">
        <header>
            <div class="Left">S&P 500</div>
            <div class="MarketQuoteLast Right">${ SP500.Last }</div>
        </header>
        <section>
            <div class="MarketQuoteDetails">        
                {{tmpl '#SP500QuoteDetails_Template' }}
            </div>
        </section>
    </div>
</script>

<script id="SP500Template_Medium" type="text/x-jquery-tmpl">
    <div class="content">
        <header>
            <div class="Left">S&P 500</div>
            <div class="MarketQuoteLast Right">${ SP500.Last }</div>
        </header>
        <section>
            <div class="MarketQuoteDetails">        
                {{tmpl '#SP500QuoteDetails_Template' }}
            </div>
            <div id="SP500Canvas" class="canvas"></div>
        </section>
    </div>
</script>

<script id="SP500Template_Large" type="text/x-jquery-tmpl">
    <div class="content">
        <header>
            <div class="Left">S&P 500</div>
            <div class="MarketQuoteLast Right">${ SP500.Last }</div>
        </header>
        <section>
            <div class="MarketQuoteDetails">        
                {{tmpl '#SP500QuoteDetails_Template' }}
            </div>
            <div id="SP500Canvas" class="canvas"></div>
        </section>
    </div>
</script>

<script id="SP500QuoteDetails_Template" type="text/x-jquery-tmpl">
    <span class="MarketQuoteChange">{{if parseFloat(SP500.Change) > 0}}+{{/if}}
      ${ SP500.Change }
    </span>&nbsp;&nbsp;
    <span class="MarketQuotePercentChange">${ SP500.PercentChange }%</span>
</script>

The tile templates rely on HTML5 semantic elements such as header and section to define containers for content. Each tile uses these elements in a similar manner.

The ID of the tile defined in the scene.layoutservice.js file (see Figure 4) determines which template file to download -- convention is used for this functionality. Once the template for a given tile is downloaded, it is added into the body of the page using the jQuery append() function. Then each tile size is rendered by calling the tmpl function, shown in Figure 6. As the different tile sizes are rendered, they're stored along with the JSON data in the tile by using the jQuery data() function.

The final step in the process is to call the tile renderer passed into the bind() function (see Figure 6) to load the appropriate tile size into the page. Figure 8 shows the renderer object (called TileRenderer) that is responsible for adding HTML content into the page for display to the user.

Figure 8: The scene.tile.renderer.js file defining a TileRenderer object
var TileRenderer = function () {

    var render = function (tileDiv, sceneId) {
        if (sceneId == null) {
            sceneId = 0;
        }
        var size = tileDiv.data().scenes[sceneId].size,
            template = tileDiv.data().templates[size],
            formatterFunc = tileDiv.data().formatter;
       
        tileDiv.html(template);
        if (formatterFunc != null) {
            formatterFunc(tileDiv);
        }
    };

    return {
        render: render
    };

} ();

Working with Canvas and SVG

HTML5-enabled browsers such as Internet Explorer 9 and Chrome provide canvas and SVG support that are used to generate charts used in the Account at a Glance application. The canvas tag can be used to render shapes, text, and graphics using a pixel-based approach. The application uses the canvas tag to render stock-quote charts. SVG relies on a variety of tags to render vector graphics and is used to generate a pie chart for account positions within the application. Generating charts and graphs can be an involved process, so the Account at a Glance application relies on two jQuery plug-ins to simplify the process. The scene.tile.formatter.js script (available in the code download file) contains the code to handle rendering canvas and SVG charts.

A jQuery plug-in named Flot is used to render stock-quote charts in the application. It provides a significant boost to development productivity and can result in charts being created in only a few hours (including learning the programming interface -- you can find an example of building a simple canvas chart at tinyurl.com/cqsxyzb). Figure 9 shows an example of the charts in action within the Quote tile, and Figure 10 shows the code used to render the chart.

Figure 9: The Quote tile, which uses the canvas tag to render a stock quote chart

Figure 10: Using the Flot jQuery plug-in to render a canvas chart
renderCanvas = function(canvasDiv, width, height, color, itemJson, dataPointsJson) {
    if (dataPointsJson != null && dataPointsJson.length > 0) {
        var quoteData = [];
        for (var i in dataPointsJson) {
            var dp = dataPointsJson[i];
            quoteData.push([dp.JSTicks, dp.Value]);
        }
        var maxY = itemJson.Last + (itemJson.Last * .3);

        var chartOptions = {
            series: {
                lines: { show: true, fill: true },
                points: { show: true, radius: 5 }
            },
            grid: { hoverable: true, autoHighlight: true },
            legend: { position: 'se' },
            yaxis: { max: maxY, min: 0 },
            xaxis: { minTickSize: [1, 'hour'], mode: 'time', timeformat: '%h %P',
                     twelveHourClock: true }
        };

        canvasDiv.attr('style', 'width:' + width + 'px;height:' + height + 'px;');
         //Required....css() doesn't work properly for this

        $.plot(canvasDiv, [{
            color: color,
            shadowSize: 4,
            label: 'Simulated Data',
            data: quoteData
        }], chartOptions);

        canvasDiv.bind('plothover', function(event, pos, item) {
            if (item) {
                if (previousPoint != item.datapoint) {
                    previousPoint = item.datapoint;

                    $('#CanvasTooltip').remove();
                    //var x = item.datapoint[0].toFixed(2),
                    var y = item.datapoint[1].toFixed(2);

                    showTooltip(item.pageX, item.pageY, y);
                }
            }
            else {
                $("#CanvasTooltip").remove();
                previousPoint = null;
            }
        });
    }
}

Figure 11 shows the SVG chart displayed in the Account Details tile that is used to display security positions within the account.

Figure 11: The SVG positions pie chart displayed in the Account Details tile

It's generated using a jQuery plug-in named Raphael along with a specific Raphael plug-in used to handle pie charts (Raphael-pie.js in the project). Figure 12 shows the code that handles rendering the positions pie chart.

Figure 12: The Raphael jQuery plug-in, which is used to render an SVG chart
formatAccountDetails = function(tileDiv) {
    tileDiv.find('.Currency').formatCurrency();
    var scene = tileDiv.data().scenes[0];

    if (Modernizr.inlinesvg) {
        if ($('#AccountPositionsSVG').length > 0) {
            var values = [];
            var labels = [];
            $(tileDiv.data().json.Positions).each(function() {
                labels.push(this.Security.Symbol + '\r\n' + this.Shares + ' shares');
                values.push(this.Shares);
            });
            raphael('AccountPositionsSVG', 500, 420).pieChart(scene.width / 2, scene.height / 4 + 10, 66, values, labels, "#fff");
        }
    }
}

Integrating Video

Video is an important part of many web-based applications and is used in the Account at a Glance application to display video news, as shown in Figure 13.

Figure 13: Displaying video using the HTML5 <video> element

The Video News tile relies on the new HTML5 video element available in modern browsers to display a market news video. The following code demonstrates using the video element:

<video id="VideoPlayer" controls
preload="auto" poster="/content/images/video-poster.jpg">
<source type="video/mp4" src=".../031411hubpmmarkets_320k.mp4" />
</video>

Downloading the App

Download the Account at a Glance application. Perform the following steps to run the application:

  1. Extract the application files from the .zip archive. You'll need Visual Studio 2010 with SP1 and a SQL Server 2008 database.
  2. Locate the AccountsAtAGlance.exe file in the root folder and run it (you'll need a SQL Server 2008 database). This will create a database named AccountsAtAGlance.
  3. Locate the web.config file in the AccountAtAGlance project and update the connection string to point to the AccountsAtAGlance database you created in step 2.

Jump In to Modern Web Development

If you're looking to see how multiple server-side and client-side technologies can be used together, the Account at a Glance application provides a nice starting point. In this article, you've seen the client-side features provided by the application and learned how different HTML5 technologies can work together with jQuery and other JavaScript frameworks. This information can help you become more confident in working with the latest technologies to build modern business apps for the web. Now, Michael Palermo will show you tips and tricks using CSS3 and HTML5 to optimize your user experience for your web application in "HTML5 Is in Style: Working with CSS3 and HTML5."