Creating a News Application
This tutorial describes basic ways to use the AppsFramework by creating an application showing news data retrieved from XML files by parsing them.
** This class will not be supported in 2015.
All functionalities of AppsFramework are more improved, integrating with CAPH. Therefore Appsframework is not supported since 2015 Smart TV. To use this functionalities of Appsframework, refer to here.
The application being developed in this tutorial consists of scenes (Main scene and Contents scene), and demonstrates how to select an article and change the scene on input of a remote control key.
The Main scene shows the titles of all the articles.

Figure. Main Scene
The contents scene shows a selected article.

Figure. Contents Scene
Prerequisites
To create applications that run on a TV screen, you need:
- Samsung TV connected to the Internet
- SDK or a text editor for creating HTML, JavaScript and CSS files (using Samsung Smart TV SDK is recommended)
You can downlaod the tutorial application source code.
Development Environment
Use Samsung Smart TV SDK to create the application. You can also use the emulator provided with the SDK to debug and test the application before uploading it in your TV. Later, you can run the application on a TV; see Testing Your Application on a TV. Note that applications may perform better on the TV than on the emulator. You can find general instructions for creating applications in Implementing Your Application Code.
The directory structure for the tutorial application:
Directory/File | Description |
---|---|
app/htmls | Contains the scene HTML files. |
app/scenes | Contains the scene class files Main.js and Contents.js. |
app/stylesheets | Contains the scene stylesheet (CSS) files. |
app/init.js | File containing the initialize (onStart) and de-initialize (onDestroy) functions. onStart is the entry point of the application. |
app/Controller.js | File managing Scene and data. |
Common | Contains the common module provided by AppsFramework. |
Icon | Contains the application icon files. |
Images | Contains the application images. |
Lang | Contains the language text resources. |
XML | Contains News data XML files. |
app.json | Descriptor for AppsFramework. |
config.xml | Descriptor for Samsung TV application. |
index.html | The main page of the application. All elements from AppsFramework are added to this file. |
A controller is designed to control the scenes in the application.

Figure. Package Diagram
Class Description
The participating classes of the application are as follows.
Class | Description |
---|---|
Controller | Responsible for managing the data shared by scenes, parsing the XML files, and changing the scenes. |
MainScene | This is the first scene and is responsible for changing categories, selecting articles and presenting data lists. |
ContentsScene | This scene is responsible for displaying the contents of the article selected. |
News Application
Opening the Application
-
Execute Samsung TV SDK.
-
Create a new project. A config.xml file is created automatically. Add the following code. For more details, see Coding Your AppsFramework Application.
<?xml version="1.0" encoding="UTF-8"?> <widget> <ThumbIcon>icon/default_106.png</ThumbIcon> <BigThumbIcon>icon/default_115.png</BigThumbIcon> <ListIcon>icon/default_85.png</ListIcon> <BigListIcon>icon/default_95.png</BigListIcon> <category>lifestyle</category> <autoUpdate>y</autoUpdate> <cpname>News_af</cpname> <cpauthjs></cpauthjs> <login>n</login> <ver>0.100</ver> <mgrver>2.250</mgrver> <fullwidget>y</fullwidget> <srcctl>y</srcctl> <ticker>n</ticker> <childlock>n</childlock> <audiomute>y</audiomute> <videomute>y</videomute> <dcont>y</dcont> <movie>y</movie> <widgetname>News_af</widgetname> <description>Samsung Framework default application</description> <width>960</width> <height>540</height> <author> <name>Samsung Electronics Co. Ltd.</name> <email></email> <link>http://www.sec.co.kr/</link> <organization>Samsung Electronics Co. Ltd.</organization> </author> </widget>
-
Add the following code to the index.html file.
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Default</title> <script type="text/javascript" src="$MANAGER_WIDGET/Common/af/2.0.0/loader.js"></script> </head> <body> </body> </html>
-
In the app.json file, add the scenes and UI component modules as given below.
{ "scenes" : ["Main","Contents"], "files" : [], "theme" : "base", "modules" : ["ui.*"] }
-
When an application is loaded, the init.js file is loaded. The onStart function is called and the application is started. Call the Main scene through the Controller. sf.core.loadJS() loads the Controller and initializes the application.
function onStart() { var arrPathToIncluded = new Array(); arrPathToIncluded.push('app /Controller.js'); sf.core.loadJS(arrPathToIncluded, function(){ Controller.initialize(); Controller.start(); }); } function onDestroy () { // stop your XHR or Ajax operation and put codes to distroy your application here }
Controller.js
var Controller = { } Controller.initialize = function () { alert("Controller.initialize()"); } Controller.start = function () { alert("Controller.start()"); }
Displaying the First Screen
-
To display the Main scene, the Controller calls sf.scene.show('Main'). Add the following code to the Main class of Controller.js file. The Main class must have the basic methods as shown below.
Controller.start = function () { alert("Controller.start()"); sf.scene.show('Main'); }
-
Add Main.js:
function SceneMain() { } SceneMain.prototype.initialize = function () { alert("SceneMain.initialize()"); } SceneMain.prototype.handleShow = function (data) { alert("SceneMain.handleShow()"); } SceneMain.prototype.handleHide = function () { alert("SceneMain.handleHide()"); } SceneMain.prototype.handleFocus = function () { alert("SceneMain.handleFocus()"); } SceneMain.prototype.handleBlur = function () { alert("SceneMain.handleBlur()"); } SceneMain.prototype.handleKeyDown = function (keyCode) { alert("SceneMain.handleKeyDown(" + keyCode + ")"); switch (keyCode) { case sf.key.LEFT: break; case sf.key.RIGHT: break; case sf.key.UP: break; case sf.key.DOWN: break; case sf.key.ENTER: break; } }
Once the code is added, the background image can be seen.
-
Add <div> in the Main.html file.
<div id="MainNewspaperBG"></div> <div id="MainCoffeeBG"></div> <img id="MainLogoImage" src="images/logo_l.png"/> <div id="MainNewspaperLine"></div>
-
Add the following code in the Main.css file to set the HTML style.
#SceneMain { position: absolute; left: 0px; top: 0px; width: 960px; height: 540px; background-image: url(../../images/news_bg.jpg); } #MainNewspaperBG { position: absolute; left: 145px; top: 0px; width: 815px; height: 505px; background-image: url(../../images/news_paper_bg.png); } #MainCoffeeBG { position: absolute; left: 0px; top: 35px; width: 136px; height: 186px; background-image: url(../../images/news_coffee_bg.png); } #MainLogoImage { position: absolute; left: 460px; top: 54px; z-index: 2; } #MainNewspaperLine { position: absolute; left: 214px; top: 109px; width: 653px; height: 4px; background-image: url(../../images/news_paper_bg_line.png); }
-
Start the application. The following screen is displayed.
Figure: Main Scene
Parsing the XML File and Managing Data
News data is provided by XML files. The XML path is in the form of a URL. These files are parsed using Ajax.
-
Create a function to parse the XML files.
-
Call this function in the start() function of the Controller passing the XML URL as a parameter.
-
Save the data (article titles, descriptions) in arrayArticles. The XML structure is as shown below.
Figure. XML structure
-
Add to Controller.js:
var Controller = { arrayArticles: null, } Controller.initialize = function () { alert("Controller.initialize()"); } Controller.start = function () { alert("Controller.start()"); sf.scene.show('Main'); Controller.ParseXML("XML/category1.xml"); } Controller.ParseXML = function (categoryurl) { var _THIS_ = this, count = 0; alert("Controller.ParseXML()"); this.arrayArticles = []; $.ajax({ type: "get", dataType: "xml", url: categoryurl, success: function(xml){ if($(xml).find("item").length > 0){ $(xml).find("item").each(function(){ // loop var t_title = $(this).find("title").text(), t_description = $(this).find("description").text(), oArticle = {}; oArticle.title = t_title; oArticle.description = t_description; _THIS_.arrayArticles.push(oArticle); alert("<title>" + _THIS_.arrayArticles[count].title); count++; }); } }, error: function(){ alert("xml error!!"); } }); }
Displaying the Article List
Create a title list is and display the data of the selected article as follows:
-
Add the following code in the Main.html file. List division presenting 7 titles, and preview division presenting the simple information article is needed.
<div id="MainPreviewTitle" class="title_style"></div> <div id="MainPreviewDescriptionFrame"> <div id="MainPreviewDescription"></div> </div> <div id="MainListTitles"> <div id="MainListTitle0"></div> <div id="MainListTitle1"></div> <div id="MainListTitle2"></div> <div id="MainListTitle3"></div> <div id="MainListTitle4"></div> <div id="MainListTitle5"></div> <div id="MainListTitle6"></div> </div>
-
Add to Main.css:
#MainPreviewTitle { position: absolute; left: 210px; top: 124px; } .MainPreviewTitletitle_style { position: absolute; width: 650px; height: 32px; color: #f55; font-size: 18px; text-align: left; overflow: auto; text-overflow: ellipsis; } #MainPreviewDescriptionFrame { position: absolute; left: 215px; top: 155px; width: 645px; height: 100px; overflow: auto; text-overflow: ellipsis; } #MainPreviewDescription { position: absolute; color: #000; font-size: 16px; line-height: 160%; } #MainListTitles { position: absolute; left: 185px; top: 274px; width: 731px; height: 210px; color: #fff; } #MainListTitle0 { position: absolute; left: 0px; top: 0px; width: 731px; height: 30px; } #MainListTitle1 { position: absolute; left: 0px; top: 30px; width: 731px; height: 30px; } #MainListTitle2 { position: absolute; left: 0px; top: 60px; width: 731px; height: 30px; } #MainListTitle3 { position: absolute; left: 0px; top: 90px; width: 731px; height: 30px; } #MainListTitle4 { position: absolute; left: 0px; top: 120px; width: 731px; height: 30px; } #MainListTitle5 { position: absolute; left: 0px; top: 150px; width: 731px; height: 30px; } #MainListTitle6 { position: absolute; left: 0px; top: 180px; width: 731px; height: 30px; } .MainListTitle_style { position: absolute; left: 0px; top: 0px; width: 550px; height: 30px; font-size: 16px; color: #000; text-align: left; vertical-align: middle; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-left: 20px; padding-top: 5px; }
-
Add the code given below in the Controller.js file to implement the following:
- Data is passed from Controller to the Main scene.
- Main scene receives the data and initializes the pages.
- Controller calls this method and sets the focus to Main scene.
These are done after the XML is parsed.
Controller.ParseXML = function (categoryurl) { alert("Controller.ParseXML()"); this.arrayArticles = []; var _THIS_ = this; $.ajax({ type: "get", dataType: "xml", url: categoryurl, success: function(xml){ if($(xml).find("item").length > 0){ $(xml).find("item").each(function(){ // loop var t_title = $(this).find("title").text(); var t_description = $(this).find("description").text(); var oArticle = {}; oArticle.title = t_title; oArticle.description = t_description; _THIS_.arrayArticles.push(oArticle); }); sf.scene.get('Main').initMainpage(_THIS_.arrayArticles); /* added */ sf.scene.focus('Main'); /* added */ } }, error: function(){ alert("xml error!!"); } }); }
-
Add the code given below in the Main.js file to implement the following:
- Save the data in the initMainpage() method in Main.js.
- Call the showMainpage() to render the Main page.
- Show the data with the showTitleList() and showPreview() methods.
SceneMain.prototype.initMainpage = function (Array) { this.arrArticles = Array; this.showMainpage(this.articleIdx); } SceneMain.prototype.showMainpage = function (articleIndex) { this.showTitleList(articleIndex); this.showPreview(articleIndex); } SceneMain.prototype.showTitleList = function (index) { // this index is starting //index of articles shows firtst. var UIListTitles = document.getElementById("MainListTitles") i = 0, a = 0, article = null; for (i = 0; i < this.title_max_num; i++) { this.arrTitles[i] = document.getElementById("MainListTitle"+i); } for(a = 0; a < this.title_max_num; a++) { article = this.arrArticles[index + a]; if (article) { this.arrTitles[a].innerHTML = this.wrapInTable(article.title, "", "MainListTitle_style"); } else { this.arrTitles[a].innerHTML = ""; } } } SceneMain.prototype.showPreview = function (index) { var previewTitle = document.getElementById("MainPreviewTitle"), previewDescription = document.getElementById("MainPreviewDescription"); previewTitle.innerHTML = this.wrapInTable(this.arrArticles[index].title, "", "MainPreviewTitletitle_style"); previewDescription.innerHTML = this.arrArticles[index].description.replace(/<img [^>]+>/g, "").replace(/<br>/g, " "); } SceneMain.prototype.wrapInTable = function (pStrContents, pStyle, pClass) { // insert the contents in table var retValue = "", strStyle = "", strClass = ""; if (pStyle) { strStyle = " style='" + pStyle + "' "; } if (pClass) { strClass = " class='" + pClass + "' "; } retValue += "<table cellpadding='0px' cellspacing='0px'>"; retValue += "<tr>"; retValue += "<td" + strStyle + strClass + ">"; retValue += "<nobr>"; retValue += pStrContents; retValue += "</nobr>"; retValue += "</td>"; retValue += "</tr>"; retValue += "</table>"; alert("Util.wrapInTable() returns [" + retValue + "]"); return retValue; }
The wrapInTable() method is used to support multiple languages as each font has a different height. This method makes the text align vertically centered using the vertical-align property.
-
The following screen is displayed with the list of articles and preview.
Figure: Article list with preview
Highlight and Handling the UP and DOWN Keys
Next, select a title from the list. Set the highlight on the selected title, and it moves when pressing the UP or DOWN key. The preview is changed when the highlight moves. The titleIdx indicating the index of selected article changes when the UP and DOWN keys are pressed.
-
Add the 2 methods given below to Main.js to change the background image for showing the highlight.
SceneMain.prototype.highlightTitle = function (index) { this.arrTitles[index].style.backgroundImage = "url(../images/news_highright_2.png)"; } SceneMain.prototype.blurTitle = function (index) { this.arrTitles[index].style.backgroundImage = "url(none)"; }
-
The SceneMain.handleKeyDown method is called when the user presses a key in the Main scene. The key pressed action is handled in this method. Pressing the UP key calls the upArticle method. Pressing the DOWN key calls the downArticle method. Add the following code to the Main.js file.
SceneMain.prototype.handleKeyDown = function (keyCode) { alert("SceneMain.handleKeyDown(" + keyCode + ")"); switch (keyCode) { case sf.key.LEFT: break; case sf.key.RIGHT: break; case sf.key.UP: this.upArticle(); /* added */ break; case sf.key.DOWN: this.downArticle(); /* added */ break; case sf.key.ENTER: break; } } SceneMain.prototype.upArticle = function () { this.blurTitle(this.titleIdx); this.articleIdx--; this.titleIdx--; if(this.titleIdx < 0) { this.titleIdx = this.title_max_num - 1; if(this.articleIdx < 0) { this.articleIdx = this.arrArticles.length - 1; this.titleIdx = this.articleIdx % this.title_max_num; } this.showTitleList(this.articleIdx - this.titleIdx); } this.showPreview(this.articleIdx); this.highlightTitle(this.titleIdx); } SceneMain.prototype.downArticle = function () { this.blurTitle(this.titleIdx); this.articleIdx++; this.titleIdx++; if(this.articleIdx % this.title_max_num == 0 || this.articleIdx > this.arrArticles.length - 1) { this.titleIdx = 0; if(this.articleIdx > this.arrArticles.length - 1) { this.articleIdx = 0; } this.showTitleList(this.articleIdx); } this.showPreview(this.articleIdx); this.highlightTitle(this.titleIdx); } SceneMain.prototype.initMainpage = function (Array) { this.arrArticles = Array; this.showMainpage(this.articleIdx); this.highlightTitle(this.titleIdx); /* highlight the firtst title. */ }
-
The Main scene shows only 7 articles. If the number of articles is greater than 7, the 8th to 14th titles must be shown when the focus is moved to the end of title. The starting index of the lists is passed to the showTitleList() method. The following screen is displayed.
Figure: Preview with an article highlighted
Showing Data in Contents Scene
When ENTER key is pressed, the Contents scene is displayed with the title and contents of the article selected in the Main scene. The Main scene passes the article index to the Controller method that shows the Contents scene.
-
Add the following code in the Main.js file.
SceneMain.prototype.handleKeyDown = function (keyCode) { ... case sf.key.ENTER: /* added */ sf.scene.hide('Main'); Controller.showContents(this.articleIdx); break; ... }
-
Add the following code in the Controller.js file.
Controller.showContents = function (articleindex) { sf.scene.show('Contents', {index: articleindex, array: this.arrayArticles}); // pass the index of articles and array contains article data. sf.scene.focus('Contents'); }
-
Add the following code in the Contents.js file.
alert('SceneContents.js loaded'); function SceneContents() { } SceneContents.prototype.initialize = function () { alert("SceneContents.initialize()");} SceneContents.prototype.handleShow = function (data) { alert("SceneContents.handleShow()"); } SceneContents.prototype.handleHide = function () { alert("SceneContents.handleHide()"); } SceneContents.prototype.handleFocus = function () { alert("SceneContents.handleFocus()"); } SceneContents.prototype.handleBlur = function () { alert("SceneContents.handleBlur()"); } SceneContents.prototype.handleKeyDown = function (keyCode) { alert("SceneContents.handleKeyDown(" + keyCode + ")"); // TODO : write an key event handler when this scene get focued switch (keyCode) { case sf.key.LEFT: break; case sf.key.RIGHT: break; case sf.key.UP: break; case sf.key.DOWN: break; case sf.key.ENTER: break; } }
-
Add the following code to the Contents.html file.
<div id="ContentsBG" ></div> <img id="ContentsLogo" src="../images/logo_s.png" width="91px" height="29px"> <div id="ContentsNewpaperLine"></div> <div id="ContentsTitle"></div> <div id="ContentsTitleLeft"></div> <div id="ContentsTitleRight"></div> <div id="ContentsFrame"> <div id="ContentsDescription"></div> </div>
-
Add the following code to the Contents.css file.
#SceneContents { position: absolute; left: 0px; top: 0px; width: 960px; height: 540px; background-image: url(../../../images/news_bg.jpg); } #ContentsBG { position: absolute; left: 70px; top: 0px; width: 815px; height: 505px; background-image: url(../../../images/news_paper_bg2.png); } #ContentsNewpaperLine { position: absolute; left: 141px; top: 85px; width: 653px; height: 4px; background-image: url(../../../images/news_detail_line.png); } #ContentsLogo { position: absolute; left: 721px; top: 50px; z-index: 2; } #ContentsTitle { position: absolute; left: 142px; top: 108px; position: absolute; width: 670px; height: 39px; font-size: 22px; color: #f55; font-weight: bolder; text-align: left; vertical-align: middle; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } #ContentsFrame { position: absolute; left: 145px; top: 152px; width: 670px; height: 310px; overflow: auto; } #ContentsDescription { position: absolute; text-align: left; font-size: 16px; color: #000; line-height: 180%; } #ContentsTitleLeft { position: absolute; left: 21px; top: 236px; width: 30px; height: 61px; background-image: url(../../../images/news_arrow_left.png); } #ContentsTitleRight { position: absolute; left: 910px; top: 240px; width: 30px; height: 61px; background-image: url(../../../images/news_arrow_right.png); }
-
Save the data passed from Controller in the handleShow(data) method. Add the showContents() method in the Contents.js file to show the article.
function SceneContents() { this.articleTitle = null; this.articleDescription = null; this.contentsFrame = null; this.contentsIdx; this.arrArticles = []; } SceneContents.prototype.initialize = function () { this.articleTitle = document.getElementById("ContentsTitle"); this.articleDescription = document.getElementById("ContentsDescription"); this.contentsFrame = document.getElementById("ContentsFrame"); } SceneContents.prototype.handleShow = function (data) { this.contentsIdx = data.index; this.arrArticles = data.array; this.showContents(this.contentsIdx); } SceneContents.prototype.showContents = function (index) { alert("SceneContents.showContents()"); // title this.articleTitle.innerHTML = this.arrArticles[index].title; // Description $('#ContentsDescription').css("top", 0); this.articleDescription.innerHTML = this.arrArticles[index].description; }
The following screen is displayed with the Contents scene showing the article selected in the Main scene.

Figure: Contents scene showing the selected article
Handling Moves in Contents Scene
-
Pressing the UP and DOWN keys in the Contents scene should move the article. Move up the element having the article when the DOWN key is pressed, and vice versa. Add the following code in the Contents.js file.
SceneContents.prototype.handleKeyDown = function (keyCode) { alert("SceneContents.handleKeyDown(" + keyCode + ")"); // TODO : write an key event handler when this scene get focued switch (keyCode) { case sf.key.LEFT: break; case sf.key.RIGHT: break; case sf.key.UP: var DesPosition = $('#ContentsDescription').position().top; if(DesPosition < 0) { $('#ContentsDescription').css("top", DesPosition+60); } break; case sf.key.DOWN: var DesPosition = $('#ContentsDescription').position().top; if(this.articleDescription.scrollHeight-this.contentsFrame.offsetHeight > DesPosition*(-1)) { $('#ContentsDescription').css("top", DesPosition-60); } break; case sf.key.ENTER: break; } }
The articleDescription.scrollHeight value represents the height of whole article, and contentsFrame.offsetHeight represents the height of the article layout.
-
Pressing the LEFT or RIGHT key shows the next or previous article respectively. This works in a circular manner. Pressing the LEFT key when the first article has focus shows the last article, and pressing the RIGHT key on the last article shows the first article. Add the following code to the Contents.js file.
switch (keyCode) { case sf.key.LEFT: this.contentsIdx--; if (this.contentsIdx < 0) { this.contentsIdx = this.arrArticles.length - 1; } this.showContents(this.contentsIdx); break; case sf.key.RIGHT: this.contentsIdx++; if(this.contentsIdx > this.arrArticles.length - 1) { this.contentsIdx = 0; } this.showContents(this.contentsIdx); break; }
-
Pressing the RETURN key in the Contents scene, calls the Controller.showMain() function to move to the Main scene. Add the following code to the Contents.js file.
SceneContents.prototype.handleKeyDown = function (keyCode) { switch (keyCode) { case sf.key.RETURN: /* added */ sf.scene.hide('Contents'); Controller.showMain(this.contentsIdx); sf.key.preventDefault(); // Block default action of the RETURN key. Without this line, this application exits to Smart Hub. break; } }
-
Add the following code to the Controller.js file.
Controller.showMain = function (articleindex) { sf.scene.show('Main', {Index: articleindex}); sf.scene.focus('Main'); }
-
Add the following code to the Main.js file.
SceneMain.prototype.handleShow = function (data) { alert("SceneMain.handleShow()"); if(data) { this.blurTitle(this.titleIdx); this.articleIdx = data.Index; this.titleIdx = this.articleIdx%this.title_max_num; alert("articleIdx == " + this.articleIdx + "titleIdx == " + this.titleIdx); this.showTitleList(this.articleIdx - this.titleIdx); this.showPreview(this.articleIdx); this.highlightTitle(this.titleIdx); } }
Changing Category
In this section, 3 categories with 3 XML files are added, and the list of articles is changed based on the selected category.
-
Add the following code to init.js to save the category information with the category name and XML path in the initialize method of the Controller.
function onStart() { var arrPathToIncluded = new Array(); arrPathToIncluded.push('app/Controller.js'); sf.core.loadJS(arrPathToIncluded, function(){ Controller.initialize([{ /* modified */ name:"Corporate", url:"XML/category1.xml" },{ name:"Exhibition", url:"XML/category2.xml" },{ name:"Product", url:"XML/category3.xml" } ]); Controller.start(Controller.categoryData[0]); /* modified */ }); }
Add to Controller.js:
var Controller = { arrayArticles: null, categoryData: [], /* added */ categoryName : [], /* added */ categoryURL : [], /* added */ } Controller.initialize = function (categories) { alert("Controller.initialize()"); for(var i=0; i<categories.length; i++) { this.categoryData.push(categories[i]); } } Controller.start = function (category) { alert("Controller.start()"); sf.scene.show('Main'); Controller.ParseXML(category.url); /* modified */ }
-
The category list is created at the bottom left side of application using the List component of the AppsFramework. The focus can be move between the Category list and Title list using the LEFT and RIGHT key. When the category list has focus, pressing the UP and DOWN keys selects a category. categoryFlag indicates whether the focus is on category list. Add the following code in the Main.js file.
function SceneMain() { this.titleIdx = 0; // title index this.articleIdx = 0; // article index this.arrTitles = new Array(); this.title_max_num = 7; // category this.categoryList = null; this.categoryIdx = 0; // category index (url) this.categoryFlag = new Boolean(true); } SceneMain.prototype.initialize = function () { this.categoryList = document.getElementById("MainCategoryList"); $('#MainCategoryList').sfList({ data:[Controller.categoryData[0].name,Controller.categoryData[1].name,Controller.categoryData[2].name], itemsPerPage:3, }); $('#MainCategoryList').sfList('focus'); } SceneMain.prototype.initMainpage = function (Array) { this.arrArticles = Array; this.showMainpage(this.articleIdx); // this.highlightTitle(this.titleIdx); // DELETE } SceneMain.prototype.refreshCategory = function () { this.blurTitle(this.titleIdx); this.titleIdx = 0; this.articleIdx = 0; delete this.arrArticles; Controller.start(Controller.categoryData[this.categoryIdx]); } SceneMain.prototype.handleKeyDown = function (keyCode) { alert("SceneMain.handleKeyDown(" + keyCode + ")"); switch (keyCode) { case sf.key.LEFT: if(this.categoryFlag == false) { this.categoryFlag = true; $('#MainCategoryList').sfList('focus'); this.blurTitle(this.titleIdx); } break; case sf.key.RIGHT: if(this.categoryFlag == true) { this.categoryFlag = false; $('#MainCategoryList').sfList('blur'); this.highlightTitle(this.titleIdx); } break; case sf.key.UP: if(this.categoryFlag == false) { this.upArticle(); } else { // If the focus is move to category. (moves to previous category.) this.categoryIdx--; if(this.categoryIdx < 0) { $('#MainCategoryList').sfList('move', 2); this.categoryIdx = 2; } else { $('#MainCategoryList').sfList('prev'); } this.refreshCategory(); } break; case sf.key.DOWN: if(this.categoryFlag == false) { this.downArticle(); } else { // If the focus is move to category. (moves to next category.) this.categoryIdx++; if(this.categoryIdx > 2) { $('#MainCategoryList').sfList('move', 0); this.categoryIdx = 0; } else { $('#MainCategoryList').sfList('next'); } this.refreshCategory(); } break; case sf.key.ENTER: if(this.categoryFlag == false) { // Move to Contents scene sf.scene.hide('Main'); Controller.showContents(this.articleIdx); } break; } }
-
Add the following code to Main.html to customize the category list component.
<div id="MainCategoryList"></div>
Add to Main.css:
#MainCategoryList { position: absolute; left: 25px; top: 380px; width: 150px; height: 30px; font-size: 16px; color: #000; } /* customize the category list style */ .sf-ui-list td { /*border: 1px solid #f00;*/ height: 33px; text-align: right; } .sf-ui-list td.sf-ui-list-item-left { width: 0px; } .sf-ui-list td.sf-ui-list-item-right { width: 0px; } .sf-ui-list td.sf-ui-list-item-center { width: 119px; padding-right:10px; } .sf-ui-list-focused td.sf-ui-list-item-left { background: url('') no-repeat; } .sf-ui-list-focused td.sf-ui-list-item-center { color: #fff;background: url('../../../images/news_highright_1.png') no-repeat; } .sf-ui-list-focused td.sf-ui-list-item-right { background: url('') no-repeat; } .sf-ui-list-blured td.sf-ui-list-item-left { background: url('') no-repeat; } .sf-ui-list-blured td.sf-ui-list-item-center { color: #000000;background: url('') no-repeat; } .sf-ui-list-blured td.sf-ui-list-item-right { background: url('') no-repeat; } .sf-ui-list-selected td.sf-ui-list-item-left { background: url('') no-repeat; } .sf-ui-list-selected td.sf-ui-list-item-center { color: #fff; background: url('') no-repeat; } .sf-ui-list-selected td.sf-ui-list-item-right { background: url('') no-repeat; }
The following screen is displayed.
Figure. Main scene with the categories in the lower left corner
Creating a Scroll Bar
Next, create scroll bars in the Main and Contents scenes.
Main Page Scroll Bar
In the Main scene, the scroll bar indicates the pages of the article list. If the number of articles is less than 7, hide the scroll bar.
-
Add the following code to the Main.html file.
<div id='MainScrollBar'> <div id="MainScrollBead"></div> </div>
-
Add the following code to the Main.css file.
#MainScrollBar { position: absolute; left: 927px; top: 273px; width: 17px; height: 180px; } #MainScrollBead { position: absolute; left: 0px; top: 0px; width: 33px; height: 37px; background-image: url(../../../images/news_scroll2.png); }
-
Add the following code to the Main.js file.
function SceneMain() { this.titleIdx = 0; this.articleIdx = 0; this.arrTitles = new Array(); this.title_max_num = 7; this.categoryList = null; this.categoryIdx = 0; this.categoryFlag = new Boolean(true); // page this.totalPage = 0; /* added */ // scroll this.scrollBar = null; /* added */ this.scrollBead = null; /* added */ this.scrollBarSize = 180; /* added */ } SceneMain.prototype.initMainpage = function (Array) { this.arrArticles = Array; // total page this.totalPage = ((this.arrArticles.length - 1) / this.title_max_num) + 1; /* added */ // scroll this.scrollBar = document.getElementById("MainScrollBar"); /* added */ this.scrollBead = document.getElementById("MainScrollBead"); /* added */ this.adjustScrollBar(this.articleIdx, this.arrArticles.length); /* added */ this.showMainpage(this.articleIdx); } SceneMain.prototype.handleShow = function (data) { if(data) { this.blurTitle(this.titleIdx); this.articleIdx = data.Index; this.titleIdx = this.articleIdx%this.title_max_num; alert("articleIdx == " + this.articleIdx + "titleIdx == " + this.titleIdx); this.showTitleList(this.articleIdx - this.titleIdx); this.showPreview(this.articleIdx); this.highlightTitle(this.titleIdx); this.adjustScrollBar(this.articleIdx, this.arrArticles.length); /* added */ } } SceneMain.prototype.adjustScrollBar = function (artIndex, totlaArticle) { alert("SceneMain.adjustScrollBar()"); var curPage = parseInt(this.articleIdx / this.title_max_num); if(this.totalPage <= 1) { this.hideScrollBar(); } else { var position = Math.round((curPage*this.scrollBarSize)/(this.totalPage-1)); this.scrollBead.style.top = position + "px"; this.showScrollBar(); } } SceneMain.prototype.showScrollBar = function () { alert("SceneMain.showScrollBar()"); this.scrollBar.style.display = "block"; } SceneMain.prototype.hideScrollBar = function () { alert("SceneMain.hideScrollBar()"); this.scrollBar.style.display = "none"; } SceneMain.prototype.upArticle = function () { this.blurTitle(this.titleIdx); this.articleIdx--; this.titleIdx--; if(this.titleIdx < 0) { this.titleIdx = this.title_max_num - 1; if(this.articleIdx < 0) { this.articleIdx = this.arrArticles.length - 1; this.titleIdx = this.articleIdx % this.title_max_num; } this.adjustScrollBar(this.articleIdx, this.arrArticles.length); /* added */ this.showTitleList(this.articleIdx - this.titleIdx); } this.showPreview(this.articleIdx); this.highlightTitle(this.titleIdx); } SceneMain.prototype.downArticle = function () { this.blurTitle(this.titleIdx); this.articleIdx++; this.titleIdx++; if(this.articleIdx % this.title_max_num == 0 || this.articleIdx > this.arrArticles.length - 1) { this.titleIdx = 0; if(this.articleIdx > this.arrArticles.length - 1) { this.articleIdx = 0; } this.adjustScrollBar(this.articleIdx, this.arrArticles.length); /* added */ this.showTitleList(this.articleIdx); } this.showPreview(this.articleIdx); this.highlightTitle(this.titleIdx); }
Contents Page Scroll Bar
The scroll bar of the Contents scene must be moved by the rates of the shown area to the whole area. The size of movement should be calculated with the rate and added to the current position by pages.
-
Add the following code to Contents.html file.
<div id="ContentsScrollBar"> <div id="ContentsScrollBead"></div> </div>
Add to Contents.css :
#ContentsScrollBar { position: absolute; left: 853px; top: 85px; width: 17px; height: 333px; } #ContentsScrollBead { position: absolute; left: 0px; top: 0px; width: 56px; height: 48px; background-image: url(../../../images/news_scroll.png); }
-
Add to Contents.js :
function SceneContents() { this.articleTitle = null; this.articleDescription = null; this.contentsFrame = null; this.contentsIdx; this.arrArticles = []; // Scroll this.scrollBar = null; /* added */ this.scrollBead = null; /* added */ this.scrollBarSize = 333; /* added */ } SceneContents.prototype.initialize = function () { this.articleTitle = document.getElementById("ContentsTitle"); this.articleDescription = document.getElementById("ContentsDescription"); this.contentsFrame = document.getElementById("ContentsFrame"); // scroll this.scrollBar = document.getElementById("ContentsScrollBar"); /* added */ this.scrollBead = document.getElementById("ContentsScrollBead"); /* added */ } SceneContents.prototype.showContents = function (index) { alert("SceneContents.showContents()"); // title this.articleTitle.innerHTML = this.arrArticles[index].title; // Description $('#ContentsDescription').css("top", 0); this.articleDescription.innerHTML = this.arrArticles[index].description; this.adjustScrollBar(); /* added */ } SceneContents.prototype.adjustScrollBar = function () { alert("SceneContents.adjustScrollBar()"); var scrollHeight = this.articleDescription.scrollHeight; // Height of whole article. var offsetHeight = this.contentsFrame.offsetHeight; // height of the frame var DesPosition = $('#ContentsDescription').position().top; var rate = DesPosition*(-1) / scrollHeight; var position = this.scrollBarSize * rate; if (scrollHeight <= offsetHeight) { this.hideScrollBar(); } else { var position = this.scrollBarSize * rate; this.scrollBead.style.top = position + "px"; } } SceneContents.prototype.showScrollBar = function () { alert("SceneContents.showScrollBar()"); this.scrollBar.style.display = "block"; } SceneContents.prototype.hideScrollBar = function () { alert("SceneContents.hideScrollBar()"); this.scrollBar.style.display = "none"; } SceneContents.prototype.handleKeyDown = function (keyCode) { switch (keyCode) { case sf.key.UP: var DesPosition = $('#ContentsDescription').position().top; if(DesPosition < 0) { $('#ContentsDescription').css("top", DesPosition+60); this.adjustScrollBar(); /* added */ } break; case sf.key.DOWN: var DesPosition = $('#ContentsDescription').position().top; if(this.articleDescription.scrollHeight-this.contentsFrame.offsetHeight > DesPosition*(-1)) { $('#ContentsDescription').css("top", DesPosition-60); this.adjustScrollBar(); /* added */ } break; } }
The following screens are displayed with the scroll bar.

Figure: Contents and Main scenes with the scroll bars
