# HTML Panel Widget Overview

An HTML Panel is an element that allows HTML to be placed in the element from external sources. The HTML can from a HTML fragment or from an ID from a complete HTML page.

This widget allows any amount of content to be pulled into a single page, one piece at a time. When using the techniques of progressive enhancement, this will allow the page to degrade gracefully in a non-Javascript environment.

### Sample Usage

    <div id="mainContent">
    	This is static content that will remain in place until the user clicks on a link below!
    </div>

    <script type="text/javascript">
       var mc = new Spry.Widget.HTMLPanel("mainContent");

    </script>

    ...

    <a href="products.html" onclick="mc.loadContent(this.href, {id: "mainContent"}); return false;"">Products</a>
    <a href="about.html" onclick="mc.loadContent(this.href, {id: "mainContent"}); return false;"">About</a>

In the example above, an HTML Panel object is created for the div container with the "mainContent" id. This is the HTML Panel. The links on the page has an onclick attribute defined that will load the value of the href via the HTML Panel object. If you look at each onclick attribute, you will see a call to 'mc.loadContent()'. 'mc' is the name of the widget instance. This causes an asynchronous load request for the URL in the link's href attribute. Notice that an id: "mainContent" is being passed into the loadContent() call. This tells the HTML Panel to load only the content within the "mainContent" element. If you are loading HTML fragments instead of whole pages, there is no need to specify the id of the element to extract from the data being requested.

## Before you begin

### Prepare your files

Before you add Spry to your web pages, download and link the appropriate files.

1. Locate the Spry ZIP file on the Adobe® Labs web site.
2. Download and unzip the Spry ZIP file to your hard drive.
3. Open the unzipped Spry folder and locate the required files folder.
4. For the HTML Panel, you will need the 'SpryHTMLPanel.js' file in the 'includes' folder.

### Building a HTML Panel widget

1. Link the required files for the widget in the head of the page. Ensure that the paths are correct for your system.

       <head>
         ...
         <script src="includes/SpryHTMLPanel.js" language="javascript" type="text/javascript">
         <link href="SpryHTMLPanel.css" rel="stylesheet" type="text/css">
         ...
       </head>

2. Add a container tag to the body. This container will act as the panel. Give the panel an ID. The ID is used later in the constructor. The container can contain whatever content you wish.

        <body>
       ...
       <div id="myPanel">This is default text.</div>

3. We will need a fragment to pull in. Create a separate file and save it in the same folder as your Spry page. For this example, we will use a file called 'frag.html' The entire content of this file is:

       <table width="200" border="1">
             <tr>
               <td>1</td>
               <td>2</td>
               <td>3</td>
             </tr>
             <tr>
               <td>4</td>
               <td>5</td>
               <td>6</td>
             </tr>
             <tr>
               <td>7</td>
               <td>8</td>
               <td>9</td>
             </tr>
           </table>

4. Back on the Spry page, add a constructor script below your panel markup.

        <body>
       ...
       <div id="myPanel">This is default text.</div>

       <script>
          var panelWidget = new Spry.Widget.HTMLPanel("myPanel");
       </script>

   This code gives the panel a name: 'panelWidget'. It also creates a HTML Panel on the "myPanel" element. Remember that javascript is case sensitive.
5. Now add a link that loads the panel when clicked.

        <body>
       ...
       <div id="myPanel">This is default text.</div>
       <a href="#" onclick="panelWidget.loadContent("frag.html");>Load Frag</a>

       <script>
          var panelWidget = new Spry.Widget.HTMLPanel("myPanel");
       </script>

   This link has an onclick event that causes the frag to load into the panel widget. The format of the method is 'widgetname.loadContent("path to fragment");'

### Progressive Enhancement

Progressive Enhancement is a methodology of building pages. The idea is to start out with a basic, static web page, and incrementally add functionality to it, ensuring that it works correctly in a browser with javascript turned off or on other devices. The goal is to add polish to the page without interfering with its ability to deliver the content. Read more about it here: http://en.wikipedia.org/wiki/Progressive_enhancement.

In the above steps, we used the most basic example possible, just so understanding the concept was clear. Now, with a simple modification, we can progressively enhance the widget.

Using the code above, if javascript was turned off, there would be no way to get to the frag. There is no real value in the href field, yet, the onclick attribute has a file path to the frag. So we will modify the code to ensure this will work in a non-scripting environment.

     <body>
    ...
    <div id="myPanel">This is default text.</div>
    <a href="frag.html" onclick="panelWidget.loadContent(this.href);return false;">Load Frag</a>

    <script>
       var panelWidget = new Spry.Widget.HTMLPanel("myPanel");
    </script>

In this code above, we moved the file path 'frag.html' to the href of the link, where it usually goes in a link. To the onclick event, we replaces the file name with 'this.href'. This is a javascript object property who's value IS the href value for 'this' link. Therefore, we grab the actual href value and use that in the loadContent function. The 'return false' prevents the link from being followed and instead, keeps us on the same page and loads the panel.

Now, with this code, it works fine with javascript on. If javascript is off, the onclick is ignored and clicking the link will take you to 'frag.html', as expected. This makes your content readable in both environments!

### Options

Option | Default | Type | Description
--- | --- | --- | ---
id | null | string | This is the ID of the content within a complete HTML page that would be loaded in the widget.

### Methods API

Method | Description
--- | ---
addObserver(observer) | Adds an observer to the region. As with other components within Spry, the observer can be either an object or a function callback. The set of notifications can be found here.
removeObserver(observer) | Removes an observer that was registered with addObserver.

enableNotifications()

Enables observer notifications. Notifications are enabled by default within an HTML Panel.

* Note that the enabling/disabling of notifications is managed by an internal count, so calls to enableNotifications() and disableNotifications() can be nested within the call stack. As the callstack unwinds, the number of enableNotifications() must match the number of disableNotifications() that were previously called before notifications will be enabled again.

disableNotifications() | Disables any notifications to observers.

loadContent(url, opts)

Fires off a request for the HTML fragment at the specified URL. The opts arg is an object that may include the following properties:

- async
  - Boolean.
  - If true, the request for the HTML Fragment will be processed asynchronously, which means that the request will be made, but the response may come back some time *after* the call to updateContent() has finished. If false, the request will block until a response has been received from the server, or the request has timed out.
- headers
  - Object.
  - Specifies additional HTTP Request header fields that should be sent along with the request. Each property in this object is the name of an HTTP header field to send. The value of the property, is the value of that field. Example:

        // Example of creating a header object the literal way:
        var header = { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" };

        // Example of creating a header object manually:
        var header = new Object;  header["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8";

- id
  - If defined, the HTML Panel will search for an element with the given id inside the HTML fragment that was just loaded. If it finds it, it inserts the content *underneath* this node into the element on the page that the region is bound to. If not defined, the region inserts the entire HTML Fragment into the element on the page that it is bound to.
- method
  - String.
  - Must be "GET", "POST" or "HEAD".
  - The default if unspecified is "GET".
- password
  - String.
  - The password to send to the server along with the request.
- postData
  - String.
  - The format of the data in the string is up to the developer so this must be used in conjunction with the "headers" option, mentioned above, to specify the "Content-type" so the server knows how to deal with it.
- username
  - String.
  - The username to send to the server along with the request.

Calling this method will result in the following notifications:

- onPreLoad
- onPostLoad
- onPreUpdate
- onPostUpdate

The notifications above are listed in the order that they should fire. Should the load fail, an onLoadError notification will be fired in place of an onPostLoad notification.

If the HTML Panel is in the midst of loading content when this method is called. The previous load request is cancelled prior to making the new request.

cancelLoad()

Cancels any pending request for content.

If a pending request is cancelled, this method will result in the following notifications:

- onLoadCancelled

setContent(htmlFrag, id)

Replaces the contents of the region container with the content in the specified html fragment string.

- htmlFrag
  - String.
  - A string containing HTML markup.
- id
  - String. (Optional)
  - The id of an element inside the specified HTML fragment. If defined, the content underneath the specified element will be inserted into the region container instead of the entire fragment.

Calling this method will result in the following notifications:

- onPreUpdate
- onPostUpdate

    <input type="button" onclick="panelWidget.setContent('<strong>This is strong text</strong>');">

removeStateClasses() | Removes any CSS Behavior class names from the region container element.

### Notifications

The following notifications will be fired off during loadContent() or setContent() calls.

Notification | Description
--- | ---

onPreLoad

The HTML Panel is about to load a new HTML Fragment.

An object that describes the load request will be passed to all observers. This object will have the following properties:

- async
  - If true, the data will be loaded asynchronously, otherwise, the data will be loaded synchronously.
- id
  - The id of an element within the data being fetched. The content underneath this element will be inserted into the region container.
- method
  - A string with the value of "GET" or "POST"
  url
  - The URL that will be used to load the data.

This object may also contain other optional properties specified by the user during the call to loadContent(), which may include:

- headers
  - An object with properties that specify the HTTP headers to send with the request. For an example, click here
- password
  - A string that specifies the password to send along with the username when the request is made
- postData
  - The format of this property is up to the developer so this must be used in conjunction with the "headers" option, mentioned above, to specify the "Content-type" so the server knows how to deal with it.
- username
  - A string that specifies the username to send along with the data request.

It should be noted that observers are allowed to modify this object to alter the loading behavior. For example, observers can use this notification to tack on extra query parameters to the URL or postData, or even add username/password info.

onPostLoad

The HTML Panel has successfully loaded the new HTML Fragment.

An object that describes the load request will be passed to all observers. This object will contain the same properties as those listed above for the onPreLoad notification, but will also contain the following properties:

- xhRequest
  - The native XMLHttpRequest object used to load the data.

onPreUpdate

The content within the region container is about to be updated/replaced.

An object with the following properties will be passed to all observers:

- content
  - The HTML Fragment to be inserted into the region container.
- id
  - The id of an element within the data being fetched. The content underneath this element will be inserted into the region container.

It should be noted that observers are allowed to modify the data in this object to alter the actual content that is inserted into the region container.

onPostUpdate | The content within the region container has been updated. The same object passed during the onPreUpdate notification is passed to the observers for this notification.

onLoadError

The request for the HTML Fragment failed.

An object that describes the load request will be passed to all observers. This object contains the same properties as those listed for the onPreLoad notification, but will also contain the following properties:

- xhRequest
  - The native XMLHttpRequest object used to load the data.

onLoadCancelled

The request for the HTML Fragment was canceled.

An object that describes the load request will be passed to all observers. This object contains the same properties as those listed for the onPreLoad notification, but will also contain the following properties:

- xhRequest
  - The native XMLHttpRequest object used to load the data.

### CSS Behavior Classes

The HTML Panel object will place the following CSS class names on the region container element:

Class Name | Description
--- | ---
HTMLPanelLoading | The HTML Panel is in the process of loading content. This class is placed on the container element just before a load is kicked off.
HTMLPanelError | The HTML Panel encountered an error while loading content. This class is placed on the container if the region fails to load the content.

These classes are automatically placed on the region container at the appropriate times. They will be automatically removed from the container when a call to loadContent() or setContent() is called. If the developer wishes to remove these classes, they can manually remove them, or call removeStateClasses().

### State Markup

State markup allows for 'loading' and 'error' content to be displayed as the new content is loading in the region or when an error occurs. This emulates the spry:state attribute. In order to avoid custom attributes, a set of pre-defined class names is used to identify the "state" markup.

Class Name | Description
--- | ---
HTMLPanelLoadingContent | The content to be used when the HTML Panel is loading data.
HTMLPanelErrorContent | The content to be used when the HTML Panel encounters an error while loading data.

Upon instantiation, the HTML Panel will search the region container for any elements that have one of the classes listed above. Any elements found, will be removed from the document and serialized. The HTML Panel will then inject the appropriate content into the region container during the loading or error states.

It should be noted that the state content will be injected into the region container *WITHOUT* the state classes on it. The reason is that for the purposes of graceful degradation, when JavaScript is off, the designer may not want that content to be visible.

The default style sheet for the HTML Panel is:

    /* This class is placed on the HTML Panel Container element
     * whenever it is loading data.
     */

    .HTMLPanelLoading {
    }

    /* This class is placed on the HTML Panel Container element
     * when a load request has failed.
     */

    .HTMLPanelError {
    }

    /* These classes are used to hide any HTML Panel state markup
     * within an HTML Panel container.
     */
    .HTMLPanelLoadingContent, .HTMLPanelErrorContent {
    	display: none;
    }

The region only supports one element per state. That is, if you place the "HTMLPanelLoadingContent" class on 2 divs underneath the region container, the HTML Panel object will only use one of those divs.

### Embedded Scripts

By default, the HTML Panel will not execute scripts embedded within the loaded content.

Developers may want to enable the execution of scripts. The HTML Panel has 2 built-in switches for controlling the execution of embedded scripts.
The first is a global 'Spry.Widget.HTMLPanel.evalScripts'.
By default, the value of this global is 'false', which means script will not be executed. The developer can globally enable the execution of JS in *all* HTML Panels by setting this global to 'true' before the instantiation of an HTML Panel.

Developers also have the ability to specify whether or not each region instantiated will process scripts by passing an 'evalScripts' constructor option. When set to true, if it finds any scripts, it will remove/extract the script out of the HTML fragment, insert the HTML fragment into the DOM, and then execute the script with a call to eval().

    <script src="../../includes/SpryHTMLPanel.js" type="text/javascript"></script>
    <script type="text/javascript">

    // hr1 will not evaluate embedded script because evalScripts is false by default.

    var hr1 = new Spry.Widget.HTMLPanel("region1");

    // hr2 *WILL* evaluate embedded script because of the
    // the evalScripts constructor option below.

    var hr2 = new Spry.Widget.HTMLPanel("region2", { evalScripts: true }); // hr2 *WILL* evaluate embedded script.

or

    <script src="../../includes/SpryHTMLPanel.js" type="text/javascript"></script>
    <script type="text/javascript">

    Spry.Widget.HTMLPanel.evalScripts = true;

    ...

    // hr1 will evaluate embedded script because the global var has been changed from false to true.

    var hr1 = new Spry.Widget.HTMLPanel("region1");

    // hr2 *WILL NOT* evaluate embedded script because of the
    // the evalScripts constructor option below.

    var hr2 = new Spry.Widget.HTMLPanel("region2", { evalScripts: false }); // hr2 *WILL NOT* evaluate embedded script.

Copyright © 2007. Adobe Systems Incorporated.
All rights reserved.