IntroductionThis tutorial is meant to provide an introduction to SmartClient fundamentals by building a small news reader application. The application displays the list of articles from an RSS feed at the top and below the selected article is displayed. The amount of code needed is surprisingly small.
Install SmartClient SDKStart by downloading the latest SmartClient SDK from
http://www.smartclient.com/releases/SmartClient_70RC_LGPL.zip. This is the 7.0 Release Candidate, however, all code should apply to release 6.5.1 as well.
To install the SDK just unpack the zip archive into a convenient location. The location I chose is C:\SmartClient_70RC. Once unpacked, run the batch file C:\SmartClient_70RC\smartclientSDK\start_embedded_server.bat to start the local Tomcat server on port 8080 for testing and local documentation access.
Point your browser to
http://localhost:8080 and select
Getting Started->Feature Explorer to see some of the cool features of SmartClient. The API reference is also available at
Docs->SmartClient Reference.
Now you are ready to start developing your own SmartClient applications.
TemplateAll SmartClient applications are hosted within a (single) web page. Certain libraries are required and must be loaded in a specific order. For the purposes of this article the web page holding our app will be placed in the C:\SmartClient_70RC\smartclientSDK directory.
Open notepad and create
newsreader.html as follows:
<html>
<head>
<title>SmartClient Wired News Reader</title>
<!------ References to SmartClient Objects ------->
<script>var isomorphicDir="isomorphic/";</script>
<script src=isomorphic/system/modules/ISC_Core.js></script>
<script src=isomorphic/system/modules/ISC_Foundation.js></script>
<script src=isomorphic/system/modules/ISC_Containers.js></script>
<script src=isomorphic/system/modules/ISC_Grids.js></script>
<script src=isomorphic/system/modules/ISC_Forms.js></script>
<script src=isomorphic/system/modules/ISC_DataBinding.js></script>
<script src=isomorphic/system/development/ISC_FileLoader.js></script>
<script src=isomorphic/skins/TreeFrog/load_skin.js></script>
</head>
<body>
<script>
// SmartClient application goes here
</script>
</body>
</html>
Just to make sure the page is correct, point your browser to
http://localhost:8080/newsreader.html. If you see a nice empty page you probably have a good template.
Hello, World!Now we are ready to see SmartClient in action. Let's start with the standard Hello, World! application. Between the
script tags at the bottom, enter the following:
isc.Label.create({
contents: "<i>Hello, World!</i>"
})
Here a label is created whose contents is Hello, World! in italics. The
contents property holds the HTML to be displayed in the label. Give this a try by refreshing your browser.
DataSourceA DataSource is a SmartClient object that describes basic CRUD access to server data. We will be reading from an RSS feed so a dataSource that can read and parse an XML document into records and fields is what we need. For this tutorial, we will be reading from the Wired.com feed at http://feeds.feedburner.com/wired. The format of the data we want looks like the following:
<rss>
<channel>
<title>Wired Top Stories</title>
<link>http://www.wired.com/rss/index.xml</link>
<description>Top Stories</description>
<language>en-us</language>
<copyright>Copyright 2007 CondeNet Inc. All rights reserved.</copyright>
<pubDate>Tue, 19 May 2009 15:32:00 GMT</pubDate>
<category />
...
<item>
<title>Review: <cite>Punch-Out!!</cite> Is an Absolute Old-School Knockout</title>
<link>http://feeds.wired.com/~r/wired/index/~3/oEop7fjF6OA/</link>
<description>Punch-Out!! is an awesome new Wii game that revives the classic 80's boxing game franchise. An excellent game for hardcore and casual players alike.
<p><a href=" net="" at="" mey98bmqydiesj8t8smbb0m5xz8="" 0="" da="" img="" src="http://feedads.g.doubleclick.net/%7Eat/MEy98bMQyDiEsj8t8SmBB0m5XZ8/0/di" border="0" ismap="true" br="">
<a href="http://feedads.g.doubleclick.net/~at/MEy98bMQyDiEsj8t8SmBB0m5XZ8/1/da"><img src="http://feedads.g.doubleclick.net/~at/MEy98bMQyDiEsj8t8SmBB0m5XZ8/1/di" border="0" ismap="true"></img></a></p><img src="http://feeds2.feedburner.com/~r/wired/index/~4/oEop7fjF6OA" height="1" width="1"/></description>
<pubDate>Tue, 19 May 2009 15:32:00 GMT</pubDate>
<guid isPermaLink="false">http://www.wired.com/gamelife/2009/05/punch-out-review/</guid>
</item>
<item>
...
</item>
</channel>
</rss>
Great. Except for one issue we might as well deal with now: cross-site restrictions.
Cross-site accessSo what's the problem? Well, to prevent scripts running in your local browser from doing things that shouldn't, scripts are limited to accessing the same server that provides the web page hosting it. See
Same Origin Policy on Wikipedia for more details.
That's pretty limiting because how can a news reader we write and host locally access an RSS feed from Wired? There is a solution in Yahoo! Query Language (YQL). See more details at
http://ajaxian.com/archives/yql-converting-the-web-to-json-with-mock-sql and
http://developer.yahoo.com/yql/. Using YQL we can request that the RSS feed we want be converted from XML to JSON.
Wait a minute. How does converting the XML to JSON help us? SmartClient provides a special DataSource called XJSONDataSource that can pull JSON data from any site. So, if cross-site scripting is not allowed, how can this data source get around that limitation. The trick is that JSON is valid JavaScript code so the JSON response can be processed with a callback to convert it into an object.
I won't go into details on JSON or XJSONDataSource here because this is not something you will be doing in a production application.
Using YQL a query can be built to return details we want from the Wired RSS feed as a JSON message. Here is our YQL query:
http://query.yahooapis.com/v1/public/yql?q=select title,pubDate,link,description from rss where url%3D"http%3A%2F%2Ffeeds.feedburner.com%2Fwired"&format=json
The URL looks pretty complicated but if you are familiar with SQL it's pretty simple. Basically select four fields from the RSS feed at
feeds.feedburner.com and return them in JSON format. The escaping makes it look messy.
The results look something like:
{
"query": {
"count":"30",
"created":"2009-05-19T07:56:05Z",
"lang":"en-US",
"updated":"2009-05-19T07:56:05Z",
"uri":"http://query.yahooapis.com/v1/yql?q=select+title%2CpubDate%2Clink%2Cdescription+from+rss+where+url%3D%22http%3A%2F%2Ffeeds.feedburner.com%2Fwired%22",
"diagnostics": {
"publiclyCallable":"true",
"url": {
"execution-time":"328",
"content":"http://feeds.feedburner.com/wired"
},
"user-time":"341",
"service-time":"328",
"build-version":"1432"
},
"results": {
"item": [
{ "title":"ACLU Sues School Districts for Blocking Gay-Rights Websites",
"link":"http://feeds.wired.com/~r/wired/index/~3/8K7UBiGeDyQ/",
"description":"School districts representing thousands of Tennessee ...",
"pubDate":"Tue, 19 May 2009 17:00:00 GMT"
},
{ "title":"40 Years After Apollo, 'Peanuts' Still Seeing Stars",
"link":"http://feeds.wired.com/~r/wired/index/~3/kEOSwobq4PM/",
"description":"Four decades ago this week, Snoopy and Charlie Brown ...",
"pubDate":"Tue, 19 May 2009 15:36:00 GMT"
}
]
}
}
}
Now that we have a URL and data to process we can put together a DataSource.
// Our news feed dataSource
//
isc.XJSONDataSource.create({
ID:"newsFeed",
recordXPath: "/query/results/item",
dataURL:'http://query.yahooapis.com/v1/public/yql?q=select title,pubDate,link,description from rss where url%3D"http%3A%2F%2Ffeeds.feedburner.com%2Fwired"&format=json',
fields:[
{ name: "title" },
{ name: "pubDate" },
{ name: "link", type: "link" },
{ name: "description" }
]
});
Let's take a look at this object definition. We create an instance of XJSONDataSource and name the instance
newsFeed. This ID is important because we will use it to uniquely reference this object instance. In the SmartClient object structure, each object must have a unique ID. If you omit the ID, SmartClient will create one for you and the
.create() call will return it for your future reference.
The
dataURL is our YQL query. Looking back at the output from YQL you will note that each record is a new
item. Viewing the JSON output like it was XML we could write an Xpath to each record as "/query/results/item" and that's exactly what we set in the data source
recordXPath property. Finally we define the fields in each record specifying the field name and optionally its type. Here the type
link is used to let SmartClient know that the string in the link field can be shown as a link in the browser. Otherwise it will be treated as a regular text field.
Article ListUsing our data source created above, let's display the list of articles found in a grid. First, remove the Hello, World! label creation from our web page and add the data source.
isc.ListGrid.create({
ID: "articleList",
width: "100%",
dataSource:"newsFeed",
autoFetchData: true,
fields: [
{ name: "title" },
{ name: "pubDate", title: "Publish Date" },
{ name: "link" }
]
});
Refresh your browser. If all went well you now see a screen showing a list of articles:

Because of the default SmartClient handling of link fields you can click on any article link and it will be displayed in another window or tab.
What does this code do? The
width setting was used to tall the grid to use all of the width you can. In this case it uses the full width of the browser window.
The SmartClient ListGrid is a
DataBoundComponent. That means it knows how to interact with DataSources automatically. By linking the ListGrid with the newsFeed dataSource, the ListGrid can fetch records as needed. Setting
autoFetchData to true tells the grid to attempt to load records from the dataSource when first rendered.
Finally, a list of fields to display is provided. We don't want to include the
description field because it can be very large. Just those fields that we want shown are included. Note that the order specified on the ListGrid dictates the display order no matter what order the fields are defined in the dataSource. A different
title is given to the publish date because
pubDate is not very descriptive. We will return to the fields later to show more features.
LayoutWe now have an article list with very little code - really just object instantiations. Now lets add a little bit of polish. A header above the list would be helpful.
Here is a header that displays "Wired" on the left and "News Reader" on the right in a blue background.
isc.HLayout.create({
ID: "newsreaderHead",
width: "100%",
height: 50,
backgroundColor: "navy",
members: [
isc.Label.create({
contents: "Wired",
align: "left",
overflow: "hidden",
width: "50%",
className: "readerHeaderText"
}),
isc.Label.create({
contents: "News Reader",
align: "right",
overflow: "hidden",
width: "50%",
className: "readerHeaderText"
})
]
});
Add this code before the ListGrid create because we want it above the grid. Additionally we need to define a new style class to control the font, color and size of our text. Add this style definition in the page header just below the last
<script src=...> line.
<style type="text/css">
.readerHeaderText {
color: white;
font-family: verdana,arial,helvetica,sans-serif;
font-size: 20px;
line-height: 24px;
padding: 4px;
}
</style>
Now refresh your browser window. But where is the header? If you just define some components on the page and don't use any form of layout manager each component will be shown fixed at position 0,0. That's what's happening here. The grid is on top of the header.
Before we fix this issue, let's look at the header definition above. To place two labels side by side, I have used an HLayout object - horizontal layout. This component holds one or more components and lays them out horizontally. I want the header to take up the entire row (width: "100%"), be 50 pixels high and have a navy background color. Then it should manage two labels each taking up 50% of the HLayout width. Note the use of
className to specify a style class name for the component. Using styles is the way to control output directly. In this case, font, color and size of the text in the label.
So how do we put the header above the grid? How about a VLayout? Yep. VLayout manages that layout of multiple components in a vertical manner. Putting together a VLayout yields something like:
isc.VLayout.create({
width:"100%",
height:"100%",
members:[
"newsreaderHead",
"articleList"
]
});
Add this code after the ListGrid creation and refresh your browser window. Wow, that works! Notice that the article list now extends to the bottom of the window. This is because VLayout manages the heights of each member automatically. For the newsreaderHead, we defined an explicit height of 50 pixels so VLayout doesn't change that. But for the article grid there is no height specified so the VLayout gave it the remainder of the window.
Debugging interludeThere is something subtle going on behind the scenes that needs to be fixed. To see this problem let's take a look at an extremely powerful feature of SmartClient: the Developer Console. The console can be launched from any development or production SmartClient application at any time. To do so, type
javascript:isc.showConsole(); into the browser address bar and press Enter. The console will be displayed in another browser window.

Note that it shows a log of events that have already occurred. Note that there are a number of messages similar to the following:
22:56:21.615:WARN:HLayout:newsreaderHead:Adding already drawn widget:isc_Label_0 to new parent:newsreaderHead. Child has been cleared so it can be drawn inside the new parent. This may be a result of autoDraw being enabled for the child.
What does this mean? Well, when creating a new component SmartClient automatically draws it on the page. Then in our case we placed the component (newsreaderHead in this case) within an HLayout component that changes its location and other characteristics. SmartClient redraws the component for us. This means the newsreaderHead is drawn twice (at least) every time the application is started or refreshed. That's not very efficient.
The auto-drawing feature is very nice for simple pages but needs to be disabled for more complicated layout-managed applications. So let's do that by adding a new property to our components:
autoDraw: false. We can do this on all components but the final VLayout because that is the one we want to trigger the drawing.
Now refresh your newsreader browser window and take a look at the developer console. It should now show none of the redraw messages.
The developer console is very handy for watching RPC messages, evaluating test code and for logging your own event for debugging. We'll see more on this later in the tutorial.
Review
Here is the full page code so far. Note that we haven't written a single line of functional code - everything so far is completely declarative.
<html>
<head>
<title>SmartClient Wired NewsReader</title>
<!------ References to SmartClient Objects ------->
<script>var isomorphicDir="isomorphic/";</script>
<script src=isomorphic/system/modules/ISC_Core.js></script>
<script src=isomorphic/system/modules/ISC_Foundation.js></script>
<script src=isomorphic/system/modules/ISC_Containers.js></script>
<script src=isomorphic/system/modules/ISC_Grids.js></script>
<script src=isomorphic/system/modules/ISC_Forms.js></script>
<script src=isomorphic/system/modules/ISC_DataBinding.js></script>
<script src=isomorphic/system/development/ISC_FileLoader.js></script>
<script src=isomorphic/skins/TreeFrog/load_skin.js></script>
<style type="text/css">
.readerHeaderText {
color:white;
font-family: verdana,arial,helvetica,sans-serif;
font-size: 20px;
line-height: 24px;
padding: 4px;
}
</style>
</head>
<body>
<script>
// Our news feed dataSource
//
isc.XJSONDataSource.create({
ID:"newsFeed",
recordXPath: "/query/results/item",
dataURL:'http://query.yahooapis.com/v1/public/yql?q=select title,pubDate,link,description from rss where url%3D"http%3A%2F%2Ffeeds.feedburner.com%2Fwired"&format=json',
fields:[
{ name: "title" },
{ name: "pubDate" },
{ name: "link" },
{ name: "description" }
]
});
// UI Components
//
// Header
isc.HLayout.create({
ID: "newsreaderHead",
width: "100%",
height: 50,
backgroundColor: "navy",
autoDraw: false,
members: [
isc.Label.create({
contents: "Wired",
align: "left",
overflow: "hidden",
width: "50%",
autoDraw: false,
className: "readerHeaderText"
}),
isc.Label.create({
contents: "News Reader",
align: "right",
overflow: "hidden",
width: "50%",
autoDraw: false,
className: "readerHeaderText"
})
]
});
// Article list
isc.ListGrid.create({
ID: "articleList",
width: "100%",
dataSource:"newsFeed",
autoFetchData: true,
autoDraw: false,
fields: [
{ name: "title" },
{ name: "pubDate", title: "Publish Date" },
{ name: "link" }
]
});
// UI Layout
isc.VLayout.create({
width:"100%",
height:"100%",
members:[
"newsreaderHead",
"articleList"
]
});
</script>
</body>
</html>
Show article text
Everything is looking great in the app but it would be much nicer to see the article text on the same page and not have to close a popup window. So let's put an article text pane below the list grid. Looking through the SmartClient reference an HTMLPane looks to be the perfect component for displaying the article. It allows scrolling when the content is too large to show in its entirety. So let's add the pane.
isc.HTMLPane.create({
ID:"articlePane",
width: "100%",
showEdges:false,
contentsType:"page",
autoDraw: false
});
Insert the code above before the VLayout. Then update the VLayout to add the pane to the list of managed members:
isc.VLayout.create({
width:"100%",
height:"100%",
members:[
"newsreaderHead",
"articleList",
"articlePane"
]
});
Great. We now have a pane to display HTML content. When the user clicks on a row (record) in the article list, the page referenced in the link field should be shown in the article pane. To do that we will implement an event handler on the ListGrid - our first active code.
isc.ListGrid.create({
ID: "articleList",
width: "100%",
dataSource:"newsFeed",
autoFetchData: true,
autoDraw: false,
fields: [
{ name: "title" },
{ name: "pubDate", title: "Publish Date" },
{ name: "link" }
],
recordClick : function(viewer, record, recordNum, field, fieldNum, value, rawValue) {
articlePane.setContentsURL(record.link, "page");
}
});
Looking at the SmartClient reference documentation we find the recordClick event is called whenever the user clicks on a record in the grid. That's what we want. Note the passed arguments give us access to various context-specific values allowing flexibility in responding to the event. For this case we just need to grab the link field from the record and tell the articlePane to display it. articlePane is the ID of our HTMLPane and is available as a global variable by default.
Refresh your browser window and click on a record in the article list. It should now show in the article pane. However, note that if you click in the link field a popup still occurs. To disable this "feature" just remove the type: "link" property from the dataSource field. The field is then treated as regular text.
What if you want to show the first article instead of a blank article pane at startup? The ListGrid raises an event dataArrived each time data arrives from the server. This could occur multiple times if the there are a lot records and only a portion are loaded at a time (paging).
isc.ListGrid.create({
ID: "articleList",
width: "100%",
dataSource:"newsFeed",
autoFetchData: true,
autoDraw: false,
fields: [
{ name: "title" },
{ name: "pubDate", title: "Publish Date" },
{ name: "link" }
],
recordClick : function(viewer, record, recordNum, field, fieldNum, value, rawValue) {
articlePane.setContentsURL(record.link, "page");
},
dataArrived : function(startRow, endRow) {
if (startRow == 0) {
articlePane.setContentsURL(articleList.getRecord(0).link, "page");
}
}
});
When the startRow is 0, we will display the first article. Now let's take a look at another way to handle this event. Take a look at the dataArrived event in the SmartClient reference. Notice the label [String Method]. This means the event can be written as a normal method as shown above or as a string. Here is the same code implemented as a string method:
isc.ListGrid.create({
ID: "articleList",
width: "100%",
dataSource:"newsFeed",
autoFetchData: true,
autoDraw: false,
fields: [
{ name: "title" },
{ name: "pubDate", title: "Publish Date" },
{ name: "link" }
],
recordClick : function(viewer, record, recordNum, field, fieldNum, value, rawValue) {
articlePane.setContentsURL(record.link, "page");
},
dataArrived : 'if (startRow == 0) { articlePane.setContentsURL(articleList.getRecord(0).link, "page");}'
});
The parameters for the event are available as normal variables in the string. For very simple handlers a string method is typically more compact. As a matter of personal preference I tend to use actual methods for all my event handlers.
Fine tuning
We now have a fully functional news reader with very little code. Now all we need to do is to tweak the user interface to be a bit more professional. Take a look at the article list. There are three columns all the same size. The publish date doesn't need to be as large as the other two. Let's define the width of the publish date as a fixed size and let the other two have the remaining space. Update the article list ListGrid fields as follows:
fields: [
{ name: "title" },
{ name: "pubDate", title: "Publish Date", width: 200 },
{ name: "link" }
],
Now no matter how the browser window is resized, the publish date column is fixed at 200 pixels wide.
Do you remember when we defined the dataSource that we specified a description field? This is a summary of the article and we haven't used it yet. Let's use the description as a tooltip over the article in the article list. SmartClient calls a tooltip a hover.
isc.ListGrid.create({
ID: "articleList",
width: "100%",
dataSource:"newsFeed",
autoFetchData: true,
autoDraw: false,
fields: [
{ name: "title" },
{ name: "pubDate", title: "Publish Date", width: 200 },
{ name: "link" }
],
recordClick : function(viewer, record, recordNum, field, fieldNum, value, rawValue) {
articlePane.setContentsURL(record.link, "page");
},
canHover: true,
cellHoverHTML : function(record, rowNum, colNum) {
return record.description;
},
dataArrived : 'if (startRow == 0) { articlePane.setContentsURL(articleList.getRecord(0).link, "page");}'
});
Add an event handler for cellHoverHTML to the articleList ListGrid as shown above. With this even handler a different tooltip (hover) can be shown for each row and/or cell. In this example, the same hover is shown for any location on the row: the record.description. The value returned from this event handler can be HTML formatted text. It turns out the description is HTML so that feature fits perfectly.
Refresh your browser window and pause the mouse over different records and take a look at the hover.
Lastly, let's look at allowing the vertical size of the article list and pane to be adjusted with a resize bar.
VLayout and HLayout support a resize bar automatically. Each component in the members can provide a property showResizeBar to tell the layout to display a resize bar after the component. This property is not meaningful to the member component, only the parent layout.
isc.ListGrid.create({
ID: "articleList",
width: "100%",
dataSource:"newsFeed",
autoFetchData: true,
autoDraw: false,
fields: [
{ name: "title" },
{ name: "pubDate", title: "Publish Date", width: 200 },
{ name: "link" }
],
recordClick : function(viewer, record, recordNum, field, fieldNum, value, rawValue) {
articlePane.setContentsURL(record.link, "page");
},
canHover: true,
cellHoverHTML : function(record, rowNum, colNum) {
return record.description;
},
dataArrived : 'if (startRow == 0) { articlePane.setContentsURL(articleList.getRecord(0).link, "page");}',
height: "30%",
showResizeBar:true
});
I added the height setting to limit the default height of the article list to only 30% of the available space leaving more for the article text itself.
Debugging revisited
Earlier you saw some of the power of the debugging console. What if you could use the debugging console for your own debugging? Well you can log anything to the console you want to. A typical JavaScript developer uses alert() calls frequently for debugging. These are inefficient and are limited in usefulness especially in loops and for event handlers.
SmartClient provides some nice logging features you can use whenever you would use alert(). Let's add a couple of logging calls to our news reader to see the results.
isc.ListGrid.create({
ID: "articleList",
width: "100%",
dataSource:"newsFeed",
autoFetchData: true,
autoDraw: false,
fields: [
{ name: "title" },
{ name: "pubDate", title: "Publish Date", width: 200 },
{ name: "link" }
],
recordClick : function(viewer, record, recordNum, field, fieldNum, value, rawValue) {
isc.logEcho(record, "recordClick: ");
articlePane.setContentsURL(record.link, "page");
},
canHover: true,
cellHoverHTML : function(record, rowNum, colNum) {
isc.logWarn("hover: " + record.description);
return record.description;
},
dataArrived : 'if (startRow == 0) { articlePane.setContentsURL(articleList.getRecord(0).link, "page");}',
height: "30%",
showResizeBar:true
});
Start the debugging console if you closed it earlier and refresh your browser window. isc.logWarn writes a message to the log at warning level, the default display level. isc.logEcho logs a message and also the contents of an object instance. For example, here is a recordClick log entry:
09:09:00.530:MUP4:WARN:Log:recordClick: : {title:" music="" labels="" want="" pirate="" bay="" shuttered="">
link: "http://feeds.wired.com/~r/wired/index/~3..."[53],
description: "The recording industry wants to add new ..."[749],
pubDate: "Tue, 19 May 2009 20:13:00 GMT",
_selection_1: true}
These logging methods are extremely helpful in tracking down timing or sequence issues without interfering with the UI.
Conclusion
I hope this basic tutorial has helped you see some of the power of SmartClient architecture. This example didn't even touch on the date entry aspects of the framework nor on the powerful search and filter capabilities of the ListGrid. Browse through the feature explorer for examples of many of the other SmartClient features.
One final tip, when putting together a layout or testing a small piece of code, rather than using your template page pick any JavaScript example in the Feature Explorer and completely replace the code on the JS tab with your test code and click Try It. Or you can paste the test code into the debugging console in the Evaluate JS Expression box and click the Eval JS button.
Full final newsreader source
<html>
<head>
<title>SmartClient Wired NewsReader</title>
<!------ References to SmartClient Objects ------->
<script>var isomorphicDir="isomorphic/";</script>
<script src=isomorphic/system/modules/ISC_Core.js></script>
<script src=isomorphic/system/modules/ISC_Foundation.js></script>
<script src=isomorphic/system/modules/ISC_Containers.js></script>
<script src=isomorphic/system/modules/ISC_Grids.js></script>
<script src=isomorphic/system/modules/ISC_Forms.js></script>
<script src=isomorphic/system/modules/ISC_DataBinding.js></script>
<script src=isomorphic/system/development/ISC_FileLoader.js></script>
<script src=isomorphic/skins/TreeFrog/load_skin.js></script>
<style type="text/css">
.readerHeaderText {
color:white;
font-family: verdana,arial,helvetica,sans-serif;
font-size: 20px;
line-height: 24px;
padding: 4px;
}
</style>
</head>
<body>
<script>
// Our news feed dataSource
//
isc.XJSONDataSource.create({
ID:"newsFeed",
recordXPath: "/query/results/item",
dataURL:'http://query.yahooapis.com/v1/public/yql?q=select title,pubDate,link,description from rss where url%3D"http%3A%2F%2Ffeeds.feedburner.com%2Fwired"&format=json',
fields:[
{ name: "title" },
{ name: "pubDate" },
{ name: "link" },
{ name: "description" }
]
});
// UI Components
//
// Header
isc.HLayout.create({
ID: "newsreaderHead",
width: "100%",
height: 50,
backgroundColor: "navy",
autoDraw: false,
members: [
isc.Label.create({
contents: "Wired",
align: "left",
overflow: "hidden",
width: "50%",
autoDraw: false,
className: "readerHeaderText"
}),
isc.Label.create({
contents: "News Reader",
align: "right",
overflow: "hidden",
width: "50%",
autoDraw: false,
className: "readerHeaderText"
})
]
});
// Article list
isc.ListGrid.create({
ID: "articleList",
width: "100%",
dataSource:"newsFeed",
autoFetchData: true,
autoDraw: false,
fields: [
{ name: "title" },
{ name: "pubDate", title: "Publish Date", width: 200 },
{ name: "link" }
],
recordClick : function(viewer, record, recordNum, field, fieldNum, value, rawValue) {
isc.logEcho(record, "recordClick: ");
articlePane.setContentsURL(record.link, "page");
},
canHover: true,
cellHoverHTML : function(record, rowNum, colNum) {
isc.logWarn("hover: " + record.description);
return record.description;
},
dataArrived : 'if (startRow == 0) { articlePane.setContentsURL(articleList.getRecord(0).link, "page");}',
height: "30%",
showResizeBar:true
});
// Article view
isc.HTMLPane.create({
ID:"articlePane",
width: "100%",
showEdges:false,
contentsType:"page",
autoDraw: false
});
// UI Layout
isc.VLayout.create({
width:"100%",
height:"100%",
members:[
"newsreaderHead",
"articleList",
"articlePane"
]
});
</script>
</body>
</html>