///<reference path="CliPropConfig.js" />
///<reference path="CliController.js" />
//
// library functions
//

var CliMliPageViewTypeDownloads = "download";
var CliMliPageViewTypeBookmarks = "bookmark";

function CliUrlForOnlineLibrary( onlineUrl, pageViewType ) { return CliBuilOnlineUrl( onlineUrl + "?pageViewType=" + pageViewType ); };


//-------------------------------------------------------------------------------
function CliLibraryPage( viewButtonsContainerId )
{
    /// <summary>CliLibraryPage controlls the overall behaviour of the library page, particularly
    /// interaction between left nav and the library view, and responding to user
    /// input to the library view.</summary>

    this._pageViewType = CliMliPageViewTypeDownloads;
    this._viewButtonsContainerId = viewButtonsContainerId;  // id of the div that holds the library view. must exist on page.
    this._libViewCode;      // CliLibraryView
    this._channelFilters;   // CliLibraryChannelFilters
    this._cliController;    //
    this._online = true;    // true for online view.  if offline, pin protected channels are listed grey
    this._text = new CliLibraryText();
    this._channel;  // the channel we are rendering, or null
    this._ignoreFadeEndEvent;
}

CliLibraryPage.prototype =
{
    //--------------------------------------------------------------------------------
    Begin : function( online, channel )
    {
        ///<param name="online">true if we are rendering online library, false for offline library</param>
        ///<param name="channel">channel to render, or null</param>
        
        var self = this;
        
        this.AttachToFadeOut();
        
        if( online != null )
            this._online = online;

        this._cliController = _cliController;
        this._channel = channel;
        this._pageViewType = CliMliCurrentPageViewType();
        
        this._libViewCode = new CliLibraryView( "cli-lib-items-here", _cliController, new CliLibraryImages() );
        this._libViewCode.setOnline( online );
 
        if( this._online )
        {
	        this._libViewCode.OnLibraryItemClicked = function( a ){ self._LibraryItemRowClicked( a ); };
            this._UpdateMetadata();
	    }
	    
	    this._libViewCode.OnColumnHeaderClicked = function( sortCriteria, channel, sortAscending ){ self._ColumnHeaderClicked( sortCriteria, channel, sortAscending ); };
	    this._libViewCode.OnPlayClicked = function( moid ){ self._cliController.playKdxUrn( moid ); };
	    this._libViewCode.OnPauseClicked = function( moid ){ self._cliController.getVideoLibraryActions( moid ).PauseDownload(); };
	    this._libViewCode.OnResumeClicked = function( moid ){ self._cliController.getVideoLibraryActions( moid ).ResumeDownload(); };
	    this._libViewCode.OnDeleteClicked = function( a ){ self._deleteClicked( a ); };
	        
	    this._channelFilters = new CliLibraryChannelFilters( channel, function( a, b ){ self._ClickHandlerForFilters( a, b ); }, this._cliController );
        this._channelFilters.setOnline( online );
 	    this._channelFilters.CreateFilterButtons( "channel-filter-list-container-div" );

        if( this._pageViewType == CliMliPageViewTypeDownloads )
            this._DownloadsClicked();
        else
            this._BookmarksClicked();
    },    

    //-------------------------------------------------------------------------------
    _UpdateMetadata : function()
    {
        this._cliController.ImportMissingMetadata();
    },
    
    //-------------------------------------------------------------------------------
    AttachToFadeOut : function()
    {
        this._ignoreFadeEndEvent = false;
        var oldMethod = window.pageBoxFadeOutComplete;
        var self = this;
        
        window.pageBoxFadeOutComplete = function()
        {
            if( oldMethod != null )
            {
                try { oldMethod(); }catch( e ){}
            }
            
            if( self._ignoreFadeEndEvent == false )
            {
                self.RefreshLibraryDisplay();
            }
            self._ignoreFadeEndEvent = false;
        };
    },
    
    //-------------------------------------------------------------------------------
    //
    // Logic in this method is not optimal but should suffice
    //
    KdxStateChange : function( kdxUrn, state )
    {
        switch (state)
        {
            case 0:	// KDX_STATE_NEW
                try
                {
                    this._cliController.newItem( kdxUrn );
                }
                catch( e )
                {
                }

                this._RefreshPageIfNotDisplayed( kdxUrn );
                this._RefreshItemCounts();
                break;
                
            case 4:	// KDX_STATE_PROGRESS:
                this._libViewCode.RefreshItemDisplay( kdxUrn );
                break;

            case 1:	// KDX_STATE_QUEUED:
            case 2:	// KDX_STATE_CONNECTING:
            case 3:	// KDX_STATE_BACKOFF:
            case 14:// KDX_STATE_PAUSE:
            case 15:// KDX_STATE_RESUME:
            case 10:// KDX_STATE_PRIORITIZED:
            case 11:// KDX_STATE_UNPRIORITIZED:  
            case 5:	// KDX_STATE_UNPACKING:
            case 13:// KDX_STATE_ERROR:
            case 7:	// KDX_STATE_UPDATE: 
                this._libViewCode.RefreshItemDisplay( kdxUrn );
                break;

            case 6:	// KDX_STATE_COMPLETE: 
                this.page_FilterView( this._channelFilters.GetCurrentChannelFilter() );
                this._cliController.completeItemById( kdxUrn );
                break;
                
            case 8:	// KDX_STATE_DELETED:
                this._RefreshPageIfDisplayed( kdxUrn );
                this._RefreshItemCounts();
                break;
        }
    },
    
    _RefreshPageIfDisplayed : function( kdxUrn )
    {
        if( this._libViewCode.IsItemDisplayed( kdxUrn ) )
        {
            this.page_FilterView( this._channelFilters.GetCurrentChannelFilter() );
        }
    },

    _RefreshPageIfNotDisplayed : function( kdxUrn )
    {
        if( !this._libViewCode.IsItemDisplayed( kdxUrn ) )
        {
            this.page_FilterView( this._channelFilters.GetCurrentChannelFilter() );
        }
    },

    //-------------------------------------------------------------------------------
    // channel filter clicked
    _ClickHandlerForFilters : function( channelName, onlineUrl )
    {
        if( this._online )
        {
            loadUrl( CliUrlForOnlineLibrary( onlineUrl, this._pageViewType ) );
        }
        else
        {
            this.page_FilterView( channelName );
        }
    },
    
    //-------------------------------------------------------------------------------
    // called when a user clicks on a download types filter checkbox on the page.
    // The filters are coded directly in the html
    //
    ClickHandlerForDownloadTypeFilters : function()
    {
        this.page_FilterView( this._channelFilters.GetCurrentChannelFilter() );
    },
    
    //-------------------------------------------------------------------------------
    // refresh the display without chaning anything
    //
    RefreshLibraryDisplay : function()
    {
        this.page_FilterView( this._channelFilters.GetCurrentChannelFilter() );
    },
    
    //-------------------------------------------------------------------------------
    page_FilterView : function( channelName )
	{
        this._RemoveOverlays();

	    var title = document.getElementById( "cli-pageviewtype-title" );
	    if( title != null )
	    {
	        if( this._pageViewType == CliMliPageViewTypeDownloads )
	            title.innerText = this._text.DownloadsSectionTitle();
	        else
	            title.innerText = this._text.BookmarksSectionTitle();
	    }
	    
		var typeFilter = this._BuildDowloadTypeFilter();
		this._libViewCode.setDownloadTypesFilter( typeFilter );
		
		if( channelName == null )
		{
			var container = this._libViewCode.ListLibrary( this._pageViewType, "cli-lib-items-here" );
		}
		else
		{
			var container = this._libViewCode.ListLibraryChannel( channelName, this._pageViewType, "cli-lib-items-here" );	
		}
        
		this._RefreshItemCounts();
		
		try{ hide_waiting(); }catch(e){}
	},
	
    //-------------------------------------------------------------------------------
    _RefreshItemCounts : function()
    {
		var libraryStats = this._cliController.CompileStatistics();
		
		var dlcount = document.getElementById( "downloads-count" );
		var bmcount = document.getElementById( "bookmarks-count" );
		
		var dl;
		var bm;
		if( this._channel == null )
		{
		    dl = libraryStats.TotalDownloads;
		    bm = libraryStats.TotalBookmarks;
		}
		else
		{
		    dl = libraryStats.GetTotalDownloads( this._channel );
		    bm = libraryStats.GetTotalBookmarks( this._channel );
		}
		
		if( dlcount != null )
		{
			dlcount.innerText = "(" + dl + ")";
		}
		
		if( bmcount != null )
		{
			bmcount.innerText = "(" + bm + ")";
		}
    },
    	
    //-------------------------------------------------------------------------------
	_ColumnHeaderClicked : function ( sortCriteria, channel, sortAscending )
	{
	    try
	    {
	        this._libViewCode.RefreshLibrarySection( channel, this._pageViewType, sortCriteria, sortAscending );
	    }
	    catch( e )
	    {
	        if( e.number == -10010 )
	            CliDebug( "unsupported sort option." );
	        else
	            CliDebug( "column header clicked: " + e.number + " " + e.message );
	    }
	},
	
    //-------------------------------------------------------------------------------
    // called when the user clicks the 'show downloads' button
    //
    _DownloadsClicked : function()
    {
        var oldDl = document.getElementById( "cli-download-li" );
        var oldBm = document.getElementById( "cli-bookmark-li" );
        
        var newDl = this._CreateNonClickableViewButton( false );
        var newBm = this._CreateClickableViewButton( true );
        
        oldDl.replaceNode( newDl );
        oldBm.replaceNode( newBm );
        
        this._pageViewType = CliMliPageViewTypeDownloads;
        this.page_FilterView( this._channelFilters.GetCurrentChannelFilter() );
    },
 	
    //-------------------------------------------------------------------------------
    // called when the user clicks the 'show bookmarks' button
    //
    _BookmarksClicked : function()
    {
        var oldDl = document.getElementById( "cli-download-li" );
        var oldBm = document.getElementById( "cli-bookmark-li" );
        
        var newDl = this._CreateClickableViewButton( false );
        var newBm = this._CreateNonClickableViewButton( true );
        
        oldDl.replaceNode( newDl );
        oldBm.replaceNode( newBm );

        this._pageViewType = CliMliPageViewTypeBookmarks;
        this.page_FilterView( this._channelFilters.GetCurrentChannelFilter() );
    },

    //--------------------------------------------------------------------------------
    //
    _RemoveOverlays : function()
    {
        this._ignoreFadeEndEvent = true;

        try { fadeout_overlaycontent2(); }catch( e )
        {
            this._ignoreFadeEndEvent = false;
        }
    },
     
    //--------------------------------------------------------------------------------
    // create a button that CANNOT be clicked to change the display between bookmarks
    // and downloads
    //
    // bookmarks : true if the button will switch to bookmark view
    //
    _CreateNonClickableViewButton : function( bookmarks )
    {
        var id;
        var display;
        var idCount;
        if( bookmarks )
        {
            id = 'cli-bookmark-li';
            display = this._text.BookmarksViewButton();
            idCount = "bookmarks-count";
        }
        else
        {
            id = 'cli-download-li';
            display = this._text.DownloadsViewButton();
            idCount = "downloads-count";
        }
        
        var createEl = document.createElement;
        
        var li = createEl( "li" );
        li.id = id;
		
		var outerSpan = createEl('span');
        outerSpan.className = "mylibrary-selected";
        outerSpan.innerText = display;
		li.appendChild(outerSpan);
        
        var span = createEl( "span" );
        outerSpan.appendChild( span );
        span.className = "number_floats_right";
        span.id = idCount;
        
        return li;
    },

    //--------------------------------------------------------------------------------
    // create a button that can be clicked to change the display between bookmarks
    // and downloads
    //
    // bookmarks : true if the button will switch to bookmark view
    //
    _CreateClickableViewButton : function( bookmarks )
    {
        var self = this;
        var callback;
        
        var id;
        var display;
        var idCount;
        if( bookmarks )
        {
            id = 'cli-bookmark-li';
            display = this._text.BookmarksViewButton();
            idCount = "bookmarks-count";
            callback = function( e ) { self._BookmarksClicked(); }
        }
        else
        {
            id = 'cli-download-li';
            onclick = this._DownloadsClicked;
            display = this._text.DownloadsViewButton();
            idCount = "downloads-count";
            callback = function( e ) { self._DownloadsClicked(); }
        }
        
        var createEl = document.createElement;
        
        var li = createEl( "li" );
        li.id = id;
        
        var anchor = createEl( "a" );
        li.appendChild( anchor );
        anchor.href = '#';
        anchor.innerText = display;
        anchor.attachEvent( "onclick", callback );
        
        var span = createEl( "span" );
        anchor.appendChild( span );
        span.className = "number_floats_right";
        span.id = idCount;
        
        return li;
    },
    
    //--------------------------------------------------------------------------------
    // a library row was clicked in an eare that didn't produce a different event
    // i.e. user didnt click play, resume etc.
    //
    _LibraryItemRowClicked : function( kdxUrn )
    {
        try
        {
            if( this._online )
            {
                var item = this._cliController.getVideoLibraryActions( kdxUrn ).GetItem();
                
                if( item != null && item.DetailsPageUrl != null )
                {
                    var url = item.DetailsPageUrl;
                    ajaxAnywhere.getAJAX( url, "content" );
                }
            }
        }
        catch( e )
        {
            try { hide_waiting(); }catch( e ){}
            CliDebug( e.message );
        }
    },
    
    //--------------------------------------------------------------------------------
    // looks at the download types filters and build a comma seperatred list of
    // types that is compatible with the filters provided by the ClientCore
    //
    _BuildDowloadTypeFilter : function() // : filter string
	{
	    var getElement = document.getElementById;
	    
	    var bonusCheck = getElement( "bundle" );
	    var ppvCheck = getElement( "pay" );
	    var ptkCheck = getElement( "buy" );
	    var stdCheck = getElement( "bonus" );
	    
	    var filter = "";
	    
	    if( !bonusCheck.checked )
	    {
	        if( filter.length > 0 ) filter += ",";
	        filter += "extended";
	    }
	    if( !ppvCheck.checked )
	    {
	        if( filter.length > 0 ) filter += ",";
	        filter += "pay-per-view";
	    }
	    if( !ptkCheck.checked )
	    {
	        if( filter.length > 0 ) filter += ",";
	        filter += "pay-to-keep";
	    }
	    if( !stdCheck.checked )
	    {
	        if( filter.length > 0 ) filter += ",";
	        filter += "standard";
	    }

	    return filter;
	},
	
    //-------------------------------------------------------------------------------
	_deleteClicked : function( moid )
	{
	    var self = this;
	    function r() { self._deleteClickedContinuation( moid ); }
	    setTimeout( r, 1 );
	},
	
	_deleteClickedContinuation : function( moid )
	{
	    try
	    {
	        this._cliController.getVideoLibraryActions( moid ).Remove();
	    }
	    catch( e )
	    {
	        CliDisplayError( CliErrorAction_Delete, e.number );
	    }
	    
        this.page_FilterView( this._channelFilters.GetCurrentChannelFilter() );
	}
}//


//-------------------------------------------------------------------------------
// creates a library view object
//
function CliLibraryView( containerDivId, clientController, imageListWrapper )
{
    // function that is called when a row in the library is clicked.
    // signature should be x( moid )
    this.OnLibraryItemClicked = null;

    // function that is called when a column header is clicked
    // signature should be x( sort-criteria, channel, sortAscending )
    this.OnColumnHeaderClicked = null;
    
    // function called when a play button is clicked on
    // signature should be x( moid )
    this.OnPlayClicked = null;

    // function called when a pause button is clicked on
    // signature should be x( moid )
    this.OnPauseClicked = null;

    // function called when a resume button is clicked on
    // signature should be x( moid )
    this.OnResumeClicked = null;

    // function called when a delete/cancel button is clicked on
    // signature should be x( moid )
    this.OnDeleteClicked = null;
    
    this.DownloadTypesFilter = "";
    this.ContainerDivId = containerDivId;
    this.SortAscending = true;  // if sorting, do so in ascending order
    this.SortCriteria = "title"; // sort criteria if any
    this.ClientController = clientController;
    this._online = true;
    
    this.viewType;  // bookmarks or downloads
    this._images = imageListWrapper;
    this._text = new CliLibraryText();
}

CliLibraryView.prototype =
{
    //-------------------------------------------------------------------------------
    // list the library as if it were the first time.  The function will return a
    // newly created div with the specified id.  If a div with this id already exists
    // it will be replaced in the DOM, otherwise you should add it to the dom manually.
    // The div may be recreated by code in the object at any time.
    //
    // viewType: [download | bookmark]
    //
    ListLibrary : function( viewType ) //: initial div
    {
        this.viewType = viewType;
        
        var container = this._InitialiseResultsContainer();
        
        var iterator = this.ClientController.getChannels();
        
        if (!iterator.More)
        {
            return;
        }
        
        var displayedChannelsCount = 0;
        
        while( iterator.More )
        {
            // ignore pin protected content
            if( !iterator.PinProtected )
            {
                var chanData = this._ListLibrarySingleChannel( iterator.Name, iterator.DisplayName, viewType,
                 iterator.TitleDisplayName, iterator.SecondaryTitleDisplayName, true );
                 
                if( chanData != null )
                {
                    displayedChannelsCount++;
                    container.appendChild( chanData );
                }
            }
            
            iterator.Next();
        }		
        
        if( displayedChannelsCount == 0 )
        {
            container.appendChild( this._CreateNoItemsToViewDiv() );
        }
        
        return container;
    },
	
    //-------------------------------------------------------------------------------
	setOnline : function( online )
	{
	    if( online != null )
	        this._online = online;
	},
	
    //-------------------------------------------------------------------------------
    // list the library as if it were the first time.  The function will return a
    // newly created div with the specified id.  If a div with this id already exists
    // it will be replaced in the DOM, otherwise you should add it to the dom manually.
    // The div may be recreated by code in the object at any time.
    //
    // channelName: 
    // viewType: [download | bookmark]
    // divId: id to assign to the div.  must be unique within the page.
    //
    ListLibraryChannel : function( channelName, viewType )
    {
        this.viewType = viewType;
        
        var container = this._InitialiseResultsContainer();
        
        var iterator = this.ClientController.getChannels();
        
        if (!iterator.More)
        {
            return;
        }
            	    
        while( iterator.More )
        {
            if( channelName == null || channelName == iterator.Name )
            {
                var chanData = this._ListLibrarySingleChannel( iterator.Name, iterator.DisplayName, viewType,
                 iterator.TitleDisplayName, iterator.SecondaryTitleDisplayName, false );
                
                if( chanData != null )
                    container.appendChild( chanData );
                
                if( channelName != null )
                    return;
            }
            
            iterator.Next();
        }		
        
        return container;
    },

    //-------------------------------------------------------------------------------
    //
    // hideEmpty : true to not show anything for channels with no data
    //
    _ListLibrarySingleChannel : function( channelName, channelDisplayName, viewType, titleDisplayName, secondaryTitleDisplayName,
        hideEmpty ) // : div
    {
        var libraryData = this.GetLibraryData( channelName, this.DownloadTypesFilter, this.SortCriteria, this.SortAscending );
        
        var count = this._CountItemsOfType( libraryData, viewType );
        libraryData.SkipToStart();
        
        if( count > 0 )
        {
            // normal case, channel has items
            var chanData = this._CreateListSectionForChannel( libraryData, channelName, channelDisplayName, viewType, this.SortCriteria,
                this.SortAscending, titleDisplayName, secondaryTitleDisplayName );
            
            return chanData;
        }
        else if( count == 0 && !hideEmpty )
        {
            // empty channel but asked to show it.  should be single channel view only.
            return this._CreateNoItemsToViewDiv();
        }
        else
        {
            return null;
        }        
    },

    //-------------------------------------------------------------------------------
    _CreateNoItemsToViewDiv : function() // : div
    {
        var chanData = document.createElement( "div" );
        chanData.innerText = "There are no items to view";
        chanData.className = "cli-empty-channel-single-channel-view";
        
        return chanData;
    },
    
    //-------------------------------------------------------------------------------
    // iterates through library items counting the number of the given type
    //
    // libraryData  : IVideoLibraryIterator, assumed to be at the start (index 0 )
    // viewType     : current page view type [ CliMliPageViewTypeBookmarks | CliMliPageViewTypeDownloads ]
    //
    _CountItemsOfType : function( libraryData, viewType ) // : int
    {
        var count = 0;
        var filterFunct;
        
        if( viewType == CliMliPageViewTypeBookmarks )
            filterFunct = Cli_ItemIsBookmark;
        else if( viewType == CliMliPageViewTypeDownloads )
            filterFunct = Cli_ItemIsDownload;

        while( libraryData.More )
        {
            if( filterFunct( libraryData.Current ) )
                count++;

            libraryData.Next();
        }
        
        return count;
    },
    
    //-------------------------------------------------------------------------------
    IsItemDisplayed : function( kdxUrn )
    {
        var rowId = this._CreateLibraryRowId( kdxUrn );
        var row = document.getElementById( rowId );
        
        return row != null;
    },
    
    //-------------------------------------------------------------------------------
    // refreshes the display of a single row if there is a row for the specified item
    //
    // kdxUrn : kdxurn/mediacontentid of the item that should be redisplayed
    //
    RefreshItemDisplay : function( kdxUrn )
    {
        try
        {
            var rowId = this._CreateLibraryRowId( kdxUrn );
            var row = document.getElementById( rowId );
            
            if( row == null )
                return;

            var channel = row.getAttribute( "cli-channelid" );
            var cliindex = row.getAttribute( "cli-index" );
            var libraryItems = this.GetLibraryData( channel, this.DownloadTypesFilter, this.SortCriteria, this.SortAscending );

            while( libraryItems.More )
            {
                var currentItem = libraryItems.Current;
                
                if( currentItem.MediaContentId == kdxUrn )
                {
                    var newDiv = this._CreateListItemEntry( libraryItems.Current, channel, cliindex, rowId );
                    newDiv.style.cssText = row.style.cssText;
                    row.replaceNode( newDiv );
                    
                    
                    break;
                }
                
                libraryItems.Next();
            }
        }
        catch( e )
        {
            //TODO: we should probably log this
        }
    },
    
    //-------------------------------------------------------------------------------
    // create an ID for a single row (one item) of library data
    //
    _CreateLibraryRowId : function( mediaId )
    {
        var rowId = this.ContainerDivId + ".library-rows.row." + mediaId;
        return rowId;
    },
    
    //-------------------------------------------------------------------------------
    //
    GetLibraryData : function( channelName, downloadTypesFilter, sortCriteria, sortAscending )
    {
        var libraryData = this.ClientController.getLibraryItems( channelName, downloadTypesFilter );

        if( sortCriteria != null )
        {
            var promoteDownloading = true;
            libraryData.Sort( sortCriteria, sortAscending, promoteDownloading );
        }
        
        return libraryData;
    },
    
    //-------------------------------------------------------------------------------
    //
    RefreshLibrarySection : function( channelName, viewType, sortCriteria, sortAscending )
    {
        this.viewType = viewType;
        
        var iterator = this.ClientController.getChannels();
        
        if (!iterator.More)
        {
            return;
        }
            	    
        while( iterator.More )
        {
            if( channelName == iterator.Name )
            {
                var libraryData = this.GetLibraryData( iterator.Name, this.DownloadTypesFilter, sortCriteria, sortAscending );
                
                var chanData = this._CreateListSectionForChannel( libraryData, iterator.Name, iterator.DisplayName, viewType,
                    sortCriteria, sortAscending, iterator.TitleDisplayName, iterator.SecondaryTitleDisplayName );
                
                var oldSection = document.getElementById( this.ContainerDivId + "." + channelName );
                oldSection.replaceNode( chanData );
                
                return;
            }
            
            iterator.Next();
        }		
    },
    
    //-------------------------------------------------------------------------------
    // create a div that will indirectly hold all the items in the library.
    //
    _InitialiseResultsContainer : function()
    {
        var oldDiv = document.getElementById( this.ContainerDivId );

        var newDiv = document.createElement( "div" );
        newDiv.id = this.ContainerDivId;
        newDiv.className = "results-container";
        
        if( oldDiv != null )
            oldDiv.replaceNode( newDiv );
            
        return newDiv;
    },

    //-------------------------------------------------------------------------------
    // renders a channels worth of library data in a self contained div
    //
    // libraryItems: iterator of library items
    // channel: channel id to list library for
    // displayName: display name of channel
    // itemTypeFilter: type of item to show [ 'bookmark' | 'download' ]
    //
    _CreateListSectionForChannel : function( libraryItems, channel, displayName, itemTypeFilter,
        sortCriteria, sortAscending, titleDisplayName, secondaryTitleDisplayName ) // : div containing all lib data for channel, with header etc
    {
        var createEl = document.createElement;
        var sectionId = this.ContainerDivId + "." + channel;

        //div all the data goes in
        var containerDiv = createEl( "div" );
        containerDiv.id = sectionId;
        containerDiv.setAttribute( "sort-criteria", sortCriteria );

        // header that holds channel name
        var headerTitleDiv = createEl( "div" );
        containerDiv.appendChild( headerTitleDiv );
        headerTitleDiv.className = "channelHeader";
        
        var strongHead = createEl( "strong" );
        headerTitleDiv.appendChild( strongHead );
        strongHead.innerText = displayName;
        strongHead.className = "channel uppercase";

        // column names
        containerDiv.appendChild( this._CreateColumnHeadersDiv( sectionId, channel, sortCriteria, sortAscending, titleDisplayName, secondaryTitleDisplayName ) );

        // pic a filter function so we only show the correct type of item
        var filterFunct = null;
        
        if( itemTypeFilter == CliMliPageViewTypeBookmarks )
            filterFunct = Cli_ItemIsBookmark;
        else if( itemTypeFilter == CliMliPageViewTypeDownloads )
            filterFunct = Cli_ItemIsDownload;
        
        // extract only appropriate items
        var index = 0;
        while( libraryItems.More )
        {
            if( filterFunct( libraryItems.Current ) )
            {
                var itemType = libraryItems.Current.State;
                var newDiv = this._CreateListItemEntry( libraryItems.Current, channel, index, this._CreateLibraryRowId( libraryItems.Current.MediaContentId ) );
                containerDiv.appendChild( newDiv );
                index++;
            }
            
            libraryItems.Next();
        }

        // no items shown
        if( index == 0 )
        {
            var libraryRow = createEl( "div" );
            libraryRow.className = "library-row even first";
            containerDiv.appendChild( libraryRow );
            
            var noRowsDiv = createEl( "div" );
            noRowsDiv.className = "no-rows";
            noRowsDiv.innerText = "No matching items.";
            libraryRow.appendChild( noRowsDiv );
        }
        
        return containerDiv;
    },

    //-------------------------------------------------------------------------------
    // create the row header div with all supported columns
    //
    // sectionDivId: id of the div holding all the channels data
    // channel: channel name
    // sortCriteria: sort criteria or null
    // sortAscending: true to sort ascending, false for descending
    //
    _CreateColumnHeadersDiv : function( sectionDivId, channel, sortCriteria, sortAscending, titleDisplayName, secondaryTitleDisplayName ) // : div
    {
        var createEl = document.createElement;
        
        var row = createEl( "div" );
        
        var headerColumnsDiv = createEl( "div" );
        row.appendChild( headerColumnsDiv );
        headerColumnsDiv.className = "titleHeader";

        // reverse sort order for click handler
        var ascString = "asc";
        if( sortAscending )
            ascString = "dsc";

        //-------------------------------------------------------------------------------
        headerColumnsDiv.appendChild( this._CreateHeader_Div_LeftPad() ); // col1
        headerColumnsDiv.appendChild( this._CreateHeader_Div_Title( channel, ascString, sortCriteria, titleDisplayName ) ); // col2
        headerColumnsDiv.appendChild( this._CreateHeader_Div_SecondaryTitle( channel, ascString, sortCriteria, secondaryTitleDisplayName ) ); // col3
        headerColumnsDiv.appendChild( this._CreateHeader_Div_Availability( channel, ascString, sortCriteria ) ); // col4
        headerColumnsDiv.appendChild( this._CreateHeader_Div_Status( channel, ascString, sortCriteria ) ); // col5

        return row;
    },

    //-------------------------------------------------------------------------------
    _CreateHeader_Div_LeftPad : function() // : div
    {
        var createEl = document.createElement;
        
        var column = createEl( "div" );
        column.className = "mlibcolumn1Title";
        column.innerText = "\u00a0";
        
        return column;
    },
    
    //-------------------------------------------------------------------------------
    _CreateHeader_Div_Title : function( channel, asc, sortCriteria, displayText ) // : div
    {
        var column = this._CreateHeader_Div_Sortable( channel, displayText, asc, sortCriteria == null || sortCriteria == 'title' );
        column.className = "mlibcolumn2Title";
        column.setAttribute( "sort-criteria", "title" );

        return column;
    },

    //-------------------------------------------------------------------------------
    _CreateHeader_Div_SecondaryTitle : function( channel, asc, sortCriteria, displayText ) // : div
    {
        var column = this._CreateHeader_Div_Sortable( channel, displayText, asc, sortCriteria == "secondary-title" );
        column.className = "mlibcolumn3Title";
        column.setAttribute( "sort-criteria", "secondary-title" );

        return column;
    },

    //-------------------------------------------------------------------------------
    _CreateHeader_Div_Availability : function( channel, asc, sortCriteria ) // : div
    {
        if( this.viewType == CliMliPageViewTypeBookmarks )
        {
            var column = this._CreateHeader_Div_Sortable( channel, this._text.HeaderAvailability( channel ), asc, sortCriteria == "download-availability-days" );
            column.className = "mlibcolumn4Title";
            column.setAttribute( "sort-criteria", "download-availability-days" );

            return column;
        }
        else
        {
            var column = this._CreateHeader_Div_Sortable( channel, this._text.HeaderAvailability( channel ), asc, sortCriteria == "content-expiry-days" );
            column.className = "mlibcolumn4Title";
            column.setAttribute( "sort-criteria", "content-expiry-days" );

            return column;
        }
    },

    //-------------------------------------------------------------------------------
    _CreateHeader_Div_Status : function( channel, asc, sortCriteria ) // : div
    {
        var column = this._CreateHeader_Div_Sortable( channel, this._text.HeaderStatus( channel ), asc, sortCriteria == 'status' );
        column.className = "mlibcolumn5Title";
        column.setAttribute( "sort-criteria", "status" );

        return column;
    },
    
    //-------------------------------------------------------------------------------
    // channel : channel id
    // displayText : inner text to display
    // asc : 'asc' or 'dsc'
    // show : true if the sort arrow should be shown
    //
    _CreateHeader_Div_Sortable : function( channel, displayText, asc, show ) // : div
    {
        var createEl = document.createElement;
        
        var column = createEl( "div" );
        column.setAttribute( "sort-ascending", asc );
        
        var anchor = createEl( "a" );
        column.appendChild( anchor );
        anchor.setAttribute( "href", "#" );
        anchor.className = this._Class_ColumnHeader( channel );
        
        var self = this;
        var callback = function(e)
        {
            e.cancelBubble = true;
            self._ColumnHeaderClicked( e.srcElement, channel );
        }
        anchor.attachEvent( "onclick", callback );
        
        anchor.innerText = displayText;
        
        var arrow = createEl( "span" );
        column.appendChild( arrow );
        
        var img = createEl( "img" );
        arrow.appendChild( img );
        if( show != null && show == true )
            img.id = 'sortarrow2';
        else
            img.id = 'sortarrow';
        
        img.title = "filter status";
        img.alt = "filter status";
        img.src = this._images.HeaderSortArrow( channel, asc );
        img.hidden = 'true';

        return column;
    },
    
    //-------------------------------------------------------------------------------
    //  called when a column header is clicked
    //
    // clickedDiv: the div that was clicked on
    //
    _ColumnHeaderClicked : function ( clickedDiv, channel )
    {
        if( this.OnColumnHeaderClicked != null )
        {
            while( clickedDiv != null )
            {
                var sortCriteria = clickedDiv.getAttribute( "sort-criteria" );
                
                if( sortCriteria != null )
                    break;

                clickedDiv = clickedDiv.parentElement;
            }
            
            if( clickedDiv != null )
            {
                var sortCriteria = clickedDiv.getAttribute( "sort-criteria" );
                
                if( sortCriteria == null )
                    return;
                    
                var sortAscending = clickedDiv.getAttribute( "sort-ascending" );
                
                if( sortAscending == null  || sortAscending == "asc" )
                    sortAscending = true;
                else
                    sortAscending = false;

                this.OnColumnHeaderClicked( sortCriteria, channel, sortAscending );
            }
        }
    },
    
    //-------------------------------------------------------------------------------
    // creates a div that contains a div for each column in a library item
    //
    // item: ILibraryItem
    // channel: channel name. used to make styles unique
    // index: integer index of this row in the current channels data. needed to do
    //        alternate colouring
    //
    _CreateListItemEntry : function( item, channel, index, divId ) // : a div containing a div for each 'column' of item data
    {
        var createEl = document.createElement;
    	
        var rowDiv = createEl( "div" );
        
        try
        {
            rowDiv.setAttribute( "moid", item.MediaContentId );
            rowDiv.id = divId;
            rowDiv.setAttribute( "cli-index", index );
            rowDiv.setAttribute( "cli-channelid", channel );
        	
        	var xx;
        	var yy;
            if( index == 0 )
            {
                rowDiv.className = "rowOdd";
                xx = function(e) { makeOddRollover( divId ); }
                yy = function(e) { makeOddRollout( divId ); }
            }
            else
            {
                if( (index % 2) == 1 )
                {
                    rowDiv.className = "rowEven";
                    xx = function(e) { makeEvenRollover( divId ); }
                    yy = function(e) { makeEvenRollout( divId ); }
                }
                else
                {
                    rowDiv.className = "rowOdd";
                    xx = function(e) { makeOddRollover( divId ); }
                    yy = function(e) { makeOddRollout( divId ); }
                }
            }
        	    
            if( xx != null ) rowDiv.attachEvent( "onmouseover", xx );
            if( yy != null ) rowDiv.attachEvent( "onmouseout", yy );
	
            var self = this;
            var callback = function(e)
             { 
                var y = e.srcElement;
                self._LibraryItemRowClicked(y);
            }
            rowDiv.attachEvent( "onclick", callback );

            //--------------------------------------------------------------------------------
            rowDiv.appendChild( this._CreateItem_Span_LeftPad( item ) ); //col1
            rowDiv.appendChild( this._CreateItem_Span_Title( item ) ); // col2
            rowDiv.appendChild( this._CreateItem_Span_SecondaryTitle( item ) ); // col3
            rowDiv.appendChild( this._CreateItem_Span_Availability( item ) ); // col4
            rowDiv.appendChild( this._CreateItem_Span_Status( item, index ) ); // col5
            rowDiv.appendChild( this._CreateItem_Span_DeleteButton( item ) ); // col6        
        }
        catch( e )
        {
            CliDebug( "librow error: " + e.message );
        }
        
        return rowDiv;
    },

    //-------------------------------------------------------------------------------
    //
    _CreateItem_Span_Status : function( item ) // : span
    {
        try
        {
	        if( this.viewType == CliMliPageViewTypeBookmarks )
	        {
	            return this._CreateItem_Span_Status_Bookmark();
	        }
	        else
	        {
	            if( item.State == Cli_ItemStatusDownloaded || item.state == Cli_ItemStatusBuyToOwn )
	            {
	                return this._CreateItem_Div_Status_Play( item );
	            }
	            else if( item.State == Cli_ItemStatusPaused )
	            {
	                return this._CreateItem_Span_Status_PausedResume( item, false, true );
	            }
	            else if( item.State == Cli_ItemStatusDownloading )
	            {
	                return this._CreateItem_Span_Status_PausedResume( item, true, false );
	            }
	            else
	            {
	                return this._CreateItem_Span_Status_PausedResume( item, false, false );
	            }
	        }
        }
        catch( e )
        {
        	CliDebug( "error creating status column: " + e.message );
			//ensure a span is returned
			var span = document.createElement('span');
			span.className = 'mlibcolumn5';
			return span;
        }
    },

    //-------------------------------------------------------------------------------
    //
    _CreateItem_Span_SecondaryTitle : function( item ) // : span
    {
		var createEl = document.createElement;
	    var titleSpan = createEl( "span" );
	    titleSpan.className = "mlibcolumn3";
        
		try {
			var title = item.SecondaryTitle;
	        var trimmed = CliMliTrimSecondaryTitle( title );
	        
	        if( trimmed != null )
	        {
	            titleSpan.title = title;
	            titleSpan.innerText = trimmed;
	        }
	        else
	        {
	            titleSpan.title = "";
	            titleSpan.innerText = title;
	        }
		} catch(e) {
			CliDebug("error creating secondary title column: " + e.message );
		}
        
        return titleSpan;
    },

    //-------------------------------------------------------------------------------
    //
    _CreateItem_Span_Status_Bookmark : function() // : span
    {
        var createEl = document.createElement;
        
        var column = createEl( "span" );
        column.className = "mlibcolumn5";
        column.innerText = this._text.ColumnStatusBookmarked();
        
        return column;
    },

    //-------------------------------------------------------------------------------
    //
    _CreateItem_Span_Status_PausedResume : function( item, showPause, showResume ) // : span
    {
        var createEl = document.createElement;

        var column = createEl( "div" );
        column.className = "mlibcolumn5";
        
        var perc = createEl( "div" );
        column.appendChild( perc );
        perc.innerText = item.PercentageDownloaded + "%";
        perc.id = "d1";
        
        var barCont = createEl( "div" );
        column.appendChild( barCont );
        barCont.id = "bar";
        
        var barBack = createEl( "<div style=\"background-color: #c3d1d1;\" />" );
        barCont.appendChild( barBack );
        barBack.id = "empty";
        
        var barProg = createEl( "<div style=\"width: " + item.PercentageDownloaded + "px;\" />" );
        barBack.appendChild( barProg );
        barProg.id = "d2";
        
        var buttons = createEl( "div" );
        column.appendChild( buttons );
        buttons.id = "barcontrol";
        
        var pauseDispMode;
        if( showPause )
            pauseDispMode = "inline";
        else
            pauseDispMode = "none";

        var pause = createEl( "<span style=\"display: " + pauseDispMode + ";\" />" );
        buttons.appendChild( pause );
        pause.id = "pause";
        
        var pauseA = createEl( "a" );
        pause.appendChild( pauseA );
        pauseA.innerText = this._text.ColumnStatusPause();

        var resumeDispMode;
        if( showResume )
            resumeDispMode = "inline";
        else
            resumeDispMode = "none";

        var resume = createEl( "<span style=\"display: " + resumeDispMode + ";\" />" );
        buttons.appendChild( resume );
        resume.id = "resume";

        var resumeA = createEl( "a" );
        resume.appendChild( resumeA );
        resumeA.innerText = this._text.ColumnStatusResume();
        
        
        var self = this;
        var moid = item.MediaContentId;
        resumeA.attachEvent( "onclick", function( e )
            {
                e.cancelBubble = true;
                self._DelegateActionButtonClick( e.srcElement, self.OnResumeClicked( moid ) );
            } );
        pauseA.attachEvent( "onclick", function( e )
            {
                e.cancelBubble = true;
                self._DelegateActionButtonClick( e.srcElement, self.OnPauseClicked( moid ) );
            } );
        
        return column;
    },

    //-------------------------------------------------------------------------------
    //
    _CreateItem_Span_Status_Downloading : function( item ) // : span
    {
        return this._CreateItem_Span_Status_Paused( item );
    },

    //-------------------------------------------------------------------------------
    //
    _CreateItem_Div_Status_Play : function( item ) // : span
    {
        var createEl = document.createElement;
        var column = createEl( "div" );
        column.className = "mlibcolumn5";
        column.id = "status-text";
        
        var anchor = createEl( "a" );
        column.appendChild( anchor );
        anchor.className = "play";
        //anchor.href = "#";
        
        var self = this;
        var callback = function( e )
        {
            e.cancelBubble = true;
            self._DelegateActionButtonClick( e.srcElement, self.OnPlayClicked );
        }
        anchor.attachEvent( "onclick", callback );

        //
        // the two images below have their inlie styles passed into CreateElement.  Without
        // this the styles seemed to have no effect.
        //
        
        var tick = createEl( "<img style=\"float: left; margin-top: -5px; height: 25px;\" />" );
        tick.title = "complete";
        tick.alt = "complete";
        tick.src = this._images.CompleteTick();
        anchor.appendChild( tick );
        
        var play = createEl( "<img style=\"float: left;\" />" );
        play.height = "16";
        play.width = "50";
        play.alt = "play";
        play.src = this._images.PlayButton();
        anchor.appendChild( play );

        return column;
    },

    //-------------------------------------------------------------------------------
    //
    _CreateItem_Span_LeftPad : function( item ) // : span
    {
        var createEl = document.createElement;
        var leftPad = createEl( "span" );
        leftPad.className = "mlibcolumn1";
        leftPad.innerText = "\u00a0";
        
        return leftPad;
    },

    //-------------------------------------------------------------------------------
    //
    _CreateItem_Span_DeleteButton : function( item ) // : span
    {
        var serverCancelRequired = ( item.PurchaseReference != null && item.PurchaseReference != '' && item.CompletionRequired );

        var createEl = document.createElement;
        var button = createEl( "span" );
        button.className = "mlibcolumn6";
        
        if( serverCancelRequired && !this._online )
        {
            // no cancel button
        }
        else
        {
            var anchor = createEl( "a" );
            button.appendChild( anchor );
            anchor.href = "#";
			anchor.attachEvent('onclick', function(ev) { ev.srcElement.blur(); });
            
            var self = this;
            
            if( serverCancelRequired && this._online )
            {
                var callback = function( e )
                {
                    e.cancelBubble = true;
                    try { show_waiting(); } catch(e){}
                    self.ClientController.serverCancelDownload( item.VideoId, item.PurchaseReference );
                }
                anchor.attachEvent( "onclick", callback );
            }
            else
            {
                var callback = function( e )
                {
                    e.cancelBubble = true;
                    try { show_waiting(); } catch(e){}
                    self._DelegateActionButtonClick( e.srcElement, self.OnDeleteClicked );
                }
                anchor.attachEvent( "onclick", callback );
            }

            var span1 = createEl( "span" );
            anchor.appendChild( span1 );
            span1.className = "cancel_text";

            var input = createEl( "input" );
            input.title = "delete";
            input.setAttribute( "type", "image" );
            input.alt = "delete";
            input.setAttribute( "src", this._images.DeleteButton() );
            span1.appendChild( input );

            var span2 = createEl( "span" );
            anchor.appendChild( span2 );

            }
            
        return button;
    },

    //-------------------------------------------------------------------------------
    //
    _CreateItem_Span_Title : function( item ) // : span
    {
        var createEl = document.createElement;
        var titleSpan = createEl( "span" );
        titleSpan.className = "mlibcolumn2";
     
	 	try {   
	        var title = item.Title;
	        var trimmed = CliMliTrimTitle( title );
	        
	        if( trimmed != null )
	        {
	            titleSpan.title = title;
	            titleSpan.innerText = trimmed;
	        }
	        else
	        {
	            titleSpan.title = "";
	            titleSpan.innerText = title;
	        }
		} catch(e) {
			CliDebug("error creating title column: " + e.message );
		}
        
        return titleSpan;
    },

    //-------------------------------------------------------------------------------
    // creates a div for the download availability of an item in the library
    //
    _CreateItem_Span_Availability : function( item ) // : span
    {
        var createEl = document.createElement;

        var container = createEl( "span" );
        container.className = "mlibcolumn4";
        
		try {
	        if( this.viewType == CliMliPageViewTypeBookmarks )
	            container.appendChild( Cli_FormatDownloadAvailabilityDaysForLibrary( item ) );
	        else if( this.viewType == CliMliPageViewTypeDownloads )
	            container.appendChild( Cli_FormatContentExpiryForLibrary( item ) );
		} catch (e) {
			CliDebug("error creating availability column: " + e.message);
		}   
		     
        return container;
    },
    
    //-------------------------------------------------------------------------------
    // handles calling an event for an action button
    //
    // clickedDiv: the div clicked
    // method: the event handler to call.  may be null.
    //
    _DelegateActionButtonClick : function( clickedDiv, method )
    {
        if( method != null )
        {            
            var moidDiv = this._LocateParentDivWithMoid( clickedDiv );
            
            if( moidDiv != null )
                method( moidDiv.moid );
        }
    },
    
    //-------------------------------------------------------------------------------
    // called when a click occurs on a library item row or child thereof
    //
    _LibraryItemRowClicked : function( divClicked )
    {
        if( this.OnLibraryItemClicked != null )
        {
            var moidDiv = this._LocateParentDivWithMoid( divClicked );
            
            if( moidDiv != null )
            {
                try { show_waiting(); } catch(e){}
                this.OnLibraryItemClicked( moidDiv.moid );
            }
        }
    },

    //-------------------------------------------------------------------------------
    //
    _LocateParentDivWithMoid : function( childDiv )
    {
        // locate the div with the moid on it by looking up the parent chain
        var moid = childDiv.moid;
        var candidateDiv = childDiv;
        
        while( moid == null && candidateDiv != null )
        {
            candidateDiv = candidateDiv.parentElement;
            if( candidateDiv != null )
                moid = candidateDiv.moid;
        }
        
        return candidateDiv;
    },

    _Class_ColumnHeader : function( channel )
    {
        return CliChannelPrefix( channel ) + "-header-text";
    },
    
    getDownloadTypesFilter : function() { return this.DownloadTypesFilter; },
    setDownloadTypesFilter : function( value ) { this.DownloadTypesFilter = value; }
}//end of prototype

//-------------------------------------------------------------------------------
// object to manage channel filter list for the library view
//
// filterClickedHandler: function to call when a filter row is clicked. Function
//          should be of the form x( channelName ).
//
function CliLibraryChannelFilters( defaultChannel, filterClickedHandler, clientController )
{
    this._OnFilterClicked = filterClickedHandler;
    this._clientController = clientController;
    
    this._filterText = new CliLibraryChannelFilterText();
    this._images = new CliLibraryImages();
    this._online = true;
    this._defaultChannel = defaultChannel;
}

CliLibraryChannelFilters.prototype =
{
    //-------------------------------------------------------------------------------
    // create the filter list.  A new div is created to hold the filters, using the
    // supplied id.  If a div with this id already exists, it is replaced.
    //
    // divId: id for the new div
    //
    CreateFilterButtons : function( divId )//: div containing filters
    {
        var createEl = document.createElement;
        
        var container = this._InitialiseRowsArea( divId );
        
        var form = createEl( '<form name="form1" action="" id="cli-channel-filters-container" />' );
        
        var rowDiv = this._CreateChannelFilterRow( this._filterText.AllDisplay(), this._filterText.AllTitle(), "all", null, false, CliMliChannelUrl( null ) );	        
        form.appendChild( rowDiv );

        var iterator = this._clientController.getChannels();
        
        if (!iterator.More)
        {
            return;
        }

        this._CreateFilterButtons( form );
        
        container.appendChild( form );
        return container;
    },

    //-------------------------------------------------------------------------------
    _CreateFilterButtons : function( form )
    {
        var iterator = this._clientController.getChannels();
        while( iterator.More )
        {
            var url = CliMliChannelUrl( iterator.Name );
            
            rowDiv = this._CreateChannelFilterRow( iterator.DisplayName, null,
                iterator.Name, iterator.Name, iterator.PinProtected, url );
                
            if( !this._online && iterator.PinProtected )
                rowDiv.disabled = true;

            form.appendChild( rowDiv );

            iterator.Next();
        }
    },
    
    //-------------------------------------------------------------------------------
	setOnline : function( online )
	{
	    if( online != null )
	        this._online = online;
	},

    //-------------------------------------------------------------------------------
    // searches the filter radio buttons to determine the current selection
    //
    // returns: channel id selected or null for all
    //
    GetCurrentChannelFilter : function()
    {
        var filterForm = document.getElementById( "cli-channel-filters-container" );
        
        if( filterForm != null )
        {
            var i = 0;
            var iMax = filterForm.children.length;
            for( ; i < iMax; i++ )
            {
                var p = filterForm.children[ i ];
                
                if( p != null && p.tagName == "P" )
                {
                    var input = p.children[ 0 ];
                    
                    if( input != null && input.tagName == "INPUT" )
                    {
                        if( input.checked )
                        {
                            if( input.id == "all" )
                                return null;
                            else
                                return input.id;
                        }
                    }
                }
            }
        }
        
        return null;
    },
    
    //-------------------------------------------------------------------------------
    //
    _InitialiseRowsArea : function( id )
    {
        var oldDiv = document.getElementById( id );

        var newDiv = document.createElement( "div" );
        newDiv.id = id;
        newDiv.className = "leftNavLinksContainerA";

        if( oldDiv != null )
            oldDiv.replaceNode( newDiv );
            
        return newDiv;
    },

    //-------------------------------------------------------------------------------
    // creates a div that represents a channel filter for redrawing the library for
    // one or more channels
    //
    // displayName: name to put in the row. probably a channel display name
    // filterId : id to use in the form
    // internalFilterId : filter id to pass around the code.  can be null;
    // url : url to visit in online mode. can be null
    //
    _CreateChannelFilterRow : function( displayName, title, filterId, internalFilterId, pinProtected, url )// : div
    {
        var createEl = document.createElement;

        var self = this;
        
        var row = createEl( "p" );
        if( this._online )
        {
            // if were online we do page redirection so server can cehck pin etc
            var redir = url;
            row.attachEvent( "onclick", function( e )
            {
                e.cancelBubble = true;
                self._FilterClicked( internalFilterId, redir );
            } );
        }
        else if( !this._online && pinProtected )
        {
            // if offline and pin protected channel, swallow click
            row.attachEvent( "onclick", function( e )
            {
                e.cancelBubble = true;
            } );
        }
        else
        {
            row.attachEvent( "onclick", function( e )
            {
                e.cancelBubble = true;
                self._FilterClicked( internalFilterId, null );
            } );
        }
        
        // this was the only reliable way to get radio buttons
        var chkString = '<input type="radio" name="search" value="" id="' + filterId;
        
        if( (internalFilterId == null && this._defaultChannel == null) || internalFilterId == this._defaultChannel )
        {
            chkString += '" checked >';
        }
        else
        {
            chkString += '" >';
        }
            
        var chk = document.createElement( chkString );
        row.appendChild( chk );

        // creating label with 'for' in the string make the for attribute work        
        var lbl = createEl( '<label for="' + filterId + '" />' );//"label" );
        lbl.innerText = displayName;
        
        if( title != null )
            lbl.title = title;
        
        if( pinProtected )
        {
            if( !this._online )
            {
                chk.disabled = true;
                lbl.disabled = true;
            }

            var img = createEl( "img" );
            img.title = "lock icon";
            img.alt = "lock icon";
            img.src = this._images.PinProtectedChannelIcon();
            lbl.appendChild( img );
        }
        
        row.appendChild( lbl );

        return row;
    },    
    
    //-------------------------------------------------------------------------------
    // called when a filter row is clicked.  expects to be called with 'this' refering
    // to an instance of CliLibraryChannelFilters
    //
    _FilterClicked : function( filterId, onlineUrl )
    {
        if( this._OnFilterClicked != null )
        {
            this._OnFilterClicked( filterId, onlineUrl );
        }
    }
}
