top

Creating a News Application

Published 2014-10-28 | (Compatible with SDK 3.5,4.5,5.0,5.1 and 2012,2013,2014 models)

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

  1. Execute Samsung TV SDK.

  2. 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>
    
  3. 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>
    
  4. In the app.json file, add the scenes and UI component modules as given below.

    {
        "scenes" : ["Main","Contents"],
        "files" : [],
        "theme" : "base",
        "modules" : ["ui.*"]
    }
    
  5. 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

  1. 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');
    }
    
  2. 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.

  3. 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>
    
  4. 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);
    }
    
  5. 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.

  1. Create a function to parse the XML files.

  2. Call this function in the start() function of the Controller passing the XML URL as a parameter.

  3. Save the data (article titles, descriptions) in arrayArticles. The XML structure is as shown below.

    Figure. XML structure

  4. 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:

  1. 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>
    
  2. 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;
    }
    
  3. Add the code given below in the Controller.js file to implement the following:

    1. Data is passed from Controller to the Main scene.
    2. Main scene receives the data and initializes the pages.
    3. 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!!");
            }
        });
    }
    
  4. Add the code given below in the Main.js file to implement the following:

    1. Save the data in the initMainpage() method in Main.js.
    2. Call the showMainpage() to render the Main page.
    3. 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.

  5. 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.

  1. 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)";
    }
    
  2. 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. */
    }
    
  3. 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.

  1. 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;
        ...
    }
    
  2. 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');
    }
    
  3. 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;
        }
    }
    
  4. 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>
    
  5. 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);
    }
    
  6. 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

  1. 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.

  2. 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;
    }
    
  3. 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;
        }
    }
    
  4. Add the following code to the Controller.js file.

    Controller.showMain = function (articleindex) {
        sf.scene.show('Main', {Index: articleindex});
        sf.scene.focus('Main');
    }
    
  5. 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.

  1. 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 */
    }
    
  2. 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;
        }
    }
    
  3. 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.

  1. Add the following code to the Main.html file.

    <div id='MainScrollBar'>
        <div id="MainScrollBead"></div>
    </div>
    
  2. 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);
    }
    
  3. 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.

  1. 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);
     }
    
  2. 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