var sUniqueCounter = 0;
var sSendCommands = true;      // if false, makes sendSyndicationCmd a no-op unless reloadArticles is set

function requestForSyndicationCmd( cmd, arg )
{
    sUniqueCounter++;
    if( ! arg ) arg = "";
    var url;
    var port = document.location.port;
    if ( port!=null )
        url = document.location.protocol+"//"+document.location.hostname+":"+port+"/__cmd__/"+cmd+"/"+arg+"/"+sUniqueCounter;
    else
        url = document.location.protocol+"//"+document.location.hostname+"/__cmd__/"+cmd+"/"+arg+"/"+sUniqueCounter;
    var post = new XMLHttpRequest();
    post.open("POST",url,true);
    post.setRequestHeader("Feed-URL",document.URL);
    return post;
}

function sendSyndicationCmd( cmd, arg, reloadArticles )
{
    if( reloadArticles == undefined ) reloadArticles = false;
    if( sSendCommands || reloadArticles ) {
        var post = requestForSyndicationCmd(cmd,arg);
        
        if( reloadArticles ) {
            post.setRequestHeader("Return-Articles","true");
            post.setRequestHeader("Filter-String", sIsPaginated && sFilterString ? sFilterString : "");
            post.onload = function( ) {reloadArticlesFromHTML(post.responseText);}
        }
        post.send(null);
        //console.log("sent cmd "+url);
    }
}


//// SELECTION:


var selArticles = new Array();
var clickedArticle = null;  // this is accessed by ArticleTextController.m when a link is clicked


function selectedArticles( )
{
    return String(selArticles);
}

function deselectArticles( )
{
    for( var i=0; i<selArticles.length; i++ ) {
        n = selArticles[i];
        if( n ) {
            document.getElementsByName(n).item(0).className = "read";
        }
    }
    selArticles = new Array();
}

function _deselectArticle( elem )
{
    elem.className = "read";
    var name = elem.getAttribute("name");
    for( var i=0; i<selArticles.length; i++ ) {
        if( selArticles[i] == name ) {
            delete selArticles[i];
            return;
        }
    }
}

function deselectArticle( elem ) {
    if( elem.className == "selected" )
        _deselectArticle(elem);
}

function articleClicked( elem )
{
    if( elem ) {
        clickedArticle = elem.name;
        var alt = (window.event.altKey || window.event.shiftKey || window.event.metaKey);

        if( elem.className == "unread" ) {
            sendSyndicationCmd("markread",elem.name);
            decrementUnreadCount();
        }
        
        if( false /*selection support*/ ) {
            if( elem.className != "selected" ) {
                if( ! alt ) {
                    deselectArticles();
                }
                elem.className = "selected";
                selArticles.push( elem.getAttribute("name") );
            } else if( alt ) {
                _deselectArticle(elem);
            }
        } else {
            elem.className = "read";
        }
    } else {
        deselectArticles();
    }
    
    window.event.cancelBubble = true; // Prevent <body> element from handling click and deselecting
    return true;
}


//// FILTERING


var sPrefiltered;                   // true if content has been prefiltered
var sFilterString;                  // current filter string. Do not set directly; call setFilterString
var sFilterWords;                   // WordMatcher for words in sFilterString.  Do not set directly; call setFilterString
var sFirstDate, sLastDate;          // date range currently being shown (these are STRINGS)
var sSourceFilter;                  // index of source being displayed, null for all (STRINGS)
var sSourceUUID;
var sMinFirstDate, sMaxLastDate;    // date range we have HTML to show (set in PageHeader.syntmpl)

function setFilterString( filterString ) {
    sFilterString = sFilterWords = null;
    if( filterString ) {
        filterString = filterString.toLocaleLowerCase();
        var words = (filterString+" ").match(/(\S+)(?=\s)/g);
        if( words ) {
            if( window.syndication )
                sFilterWords = window.syndication.createWordMatcher(words);
            else
                sFilterWords = words;               //FIX: Just for backward compatibility --2/3/2005
            sFilterString = filterString;
        }
    }
}

function setupFilter( )
{
    var filterField = document.getElementById("searchfield");
    filterField.focus();
    setFilterString(filterField.value);
    sPrefiltered = (sFilterString != null);
}

function setURLToBookmark( )
{
    var protocol = document.location.protocol.toLowerCase();
    var url = document.location.href;
    if( protocol == "feeds:" ) {
        var trim = url.indexOf("&filter:");
        if( trim >= 0 )
            url = url.substring(0,trim);
        if( sFilterString )
            url += "&filter:"+encodeURIComponent(sFilterString);
    } else if( protocol = "feed:" ) {
        // Add a filter to a feed: URL by converting it into a single-item feeds:
        if( sFilterString ) {
            url = url.substring(5);
            if( url.indexOf("//")==0 )
                url = url.substring(2);            
            url = "feeds:"+encodeURIComponent(document.title)+"&"
                + url.replace("&","%26")
                + "&filter:"+encodeURIComponent(sFilterString);
        }
    }
    
    // Shove this URL into an attr of a <meta id=AppleSyndicationInfo> tag:
    document.getElementById("AppleSyndicationInfo").setAttribute("URLToBookmark", url);
}

/** This is called by an event handler whenever the search field's content changes */
function setContentFilter( filter, bookmarkLinkTitle, searchLinkTitle )
{
    //var time = (new Date()).getTime();
    
    var oldFilter = sFilterString;
    setFilterString(filter);
    var reload = (sFilterString != oldFilter) && (sPrefiltered || sIsPaginated);

    setURLToBookmark();
    
    if( reload ) {
        // If articles were pre-filtered, we need to load all the articles:
        sPrefiltered = false;
        sResetPagination = true;
        sendSyndicationCmd("refilter",sFilterString,true);
    } else {
        refilterArticles();
    }
    
    // Show/hide the bookmark link, as appropriate
    var bookmarkLinkDiv = document.getElementById("bookmarkLink");
    var bookmarkLink = document.getElementById("bookmarkHref");   
    if( ! sFilterString ) {
        var bookmarkAttr = bookmarkLinkDiv.getAttribute("class");
        
        // If we've just cleared the filter, and we're already bookmarked, hide the link
        if (bookmarkAttr == "bookmarked") {
            bookmarkLinkDiv.style.display = "none";
        }
        // Otherwise, show Add Bookmark
        else {
            bookmarkLink.innerText = bookmarkLinkTitle;
            bookmarkLinkDiv.style.display = "block";
        }
    } else {
        // If we're adding chars to the filter, show the link with the text "Bookmark This Search"
        bookmarkLink.innerText = searchLinkTitle;
        bookmarkLinkDiv.style.display = "block";
    }
    
    //console.log("Filtering took "+((new Date()).getTime()-time)+" ms");
}


function subscribeInTunes()
{
    var href         = location.href;
    var indexOfColon = href.indexOf(":");

    location.href = "pcast" + href.substr(indexOfColon);    
}


function showTunesLink()
{
    var iTunesLink = document.getElementById("iTunesLink");
    iTunesLink.style.display = "block";
}


var sTimespanDivs;


function currentTimespan( )
{
    if( ! sTimespanDivs )
        sTimespanDivs = document.getElementById("timespans").getElementsByTagName("div");
    for( var i=0; i<sTimespanDivs.length; i++ ) {
        var div = sTimespanDivs[i];
        if( div.className == "current" )
            return parseInt(div.getAttribute("name"));
    }
    return -1;
}


/** This is called by an event handler when a date range is clicked */
function setDateFilter( index )
{
    var firstDate=null, lastDate=null;
    
    // Update the styles of the various date links:
    if( ! sTimespanDivs )
        sTimespanDivs = document.getElementById("timespans").getElementsByTagName("div");
    for( var i=0; i<sTimespanDivs.length; i++ ) {
        var div = sTimespanDivs[i];
        var divname = div.getAttribute("name");
        if( divname ) {
            if( parseInt(divname) == index ) {
                div.className = "current";
                firstDate = div.getAttribute("firstDate");
                lastDate  = div.getAttribute("lastDate");
            } else {
                div.className = null;
            }
        }
    }
    
    if( firstDate==null || lastDate==null ) {
        //alert("setDateFilter: couldn't get firstDate or lastDate");
        return; // means 'name' was invalid
    }
    if( firstDate=="" ) firstDate = null;
    if( lastDate=="" ) lastDate  = null;
    if( firstDate!=null ) firstDate = parseInt(firstDate);
    if( lastDate!=null ) lastDate = parseInt(lastDate);
    
    // If the articles are paginated or the new range is outside what we have in the HTML, we have to reload.
    // Remember that null means "no limit", which complicates the comparisons.
    var reload = (sIsPaginated || 
                 (sMinFirstDate!=null && (firstDate==null || firstDate < sMinFirstDate))
              || (sMaxLastDate!=null && (lastDate==null || lastDate > sMaxLastDate)));
    //if( reload ) console.log("Must reload HTML");
    sFirstDate = firstDate;
    sLastDate = lastDate;
    
    // Tell the back-end to update the prefs for this feed:
    if( sIsPaginated ) sResetPagination = true;
    sendSyndicationCmd("setTimespan",index,reload);
    
    if( reload ) {
        sMinFirstDate = firstDate;
        sMaxLastDate = lastDate;
    } else {
        // Now refilter, if we already have the HTML:
        refilterArticles();
    }
}


var sSourceDivs;


function currentSourceFilter( ) // returns uuid
{
    if( ! sSourceDivs )
        sSourceDivs = document.getElementById("sourcelist").getElementsByTagName("div");
    for( var i=0; i<sSourceDivs.length; i++ ) {
        var div = sSourceDivs[i];
        if( div.className == "current" )
            return div.getAttribute("uuid");
    }
    return "";
}


var sOriginallySourceFiltered;


/** This is called by an event handler when a source is clicked */
function setSourceFilter( uuid )
{
    var which = "-1";
    
    // Update the styles of the various source links:
    if( ! sSourceDivs )
        sSourceDivs = document.getElementById("sourcelist").getElementsByTagName("div");
    for( var i=0; i<sSourceDivs.length; i++ ) {
        var div = sSourceDivs[i];
        var divuuid = div.getAttribute("uuid");
        if( divuuid != undefined ) {
            if( sOriginallySourceFiltered === undefined ) {
                if( div.className == "current" )
                    sOriginallySourceFiltered = (divuuid != "");
            }
            if( divuuid == uuid ) {
                div.className = "current";
                which = div.getAttribute("name");
            } else {
                div.className = null;
            }
        }
    }
    
    sSourceFilter = (which=="-1" ?null :which);
    if( sIsPaginated || sOriginallySourceFiltered ) {
        sSourceUUID = uuid; 
        sResetPagination = true;
    } else {
        sSourceUUID = "";
        refilterArticles();
    }
    sendSyndicationCmd("setSource",uuid,(sIsPaginated || sOriginallySourceFiltered));
}


var sCurrentArticleCount; // Updated in shouldShowArticle and refilterArticles
var sUnreadCount;

function shouldShowArticle( article )
{
    var articleDate = article.getAttribute("articlesortdate");
    if( articleDate ) {
        if( !sIsPaginated ) {
            if( sFirstDate && articleDate < sFirstDate )
                return false;
            if( sLastDate && articleDate >= sLastDate )
                return false;
            if( sSourceUUID == "" && sSourceFilter && sSourceFilter != article.getAttribute("sourceindex") )
                return false;
            if( sFilterWords && ! articleMatchesFilter(article) )
                return false;
        }
        sCurrentArticleCount++;
        if( article.className == "unread" )
            sUnreadCount++;
    }
    return true;
}

var sTotalCountSpan, sUnreadCountSpan, sUnreadCountWrapperSpan;

function updateNoArticlesMessage( )
{
    // Show the "No Articles" message if there are no visible articles:
    var noarticles = document.getElementById("noarticles");
    if( sCurrentArticleCount == 0 ) {
        // Look up message to display, from attr of selected timespan div:
        var message = null;
        var divs = document.getElementById("timespans").getElementsByTagName("div");
        for( var i=0; i<divs.length; i++ ) {
            var div = divs[i];
            if( div.className == "current" ) {
                message = div.getAttribute("noarticles");
                break;
            }
        }
        if( message )
            noarticles.innerText = message;
        noarticles.style.display = "block";
    } else {
        noarticles.style.display = "";
    }
}

var sCountsDiv;

function redisplayCounts( )
{
    if (sCountsDiv == undefined) {
        sCountsDiv = document.getElementById('counts');
        sCountsDiv.style.display = "block"; // now show counts
    }
    
    if( sTotalCountSpan == undefined )
        sTotalCountSpan = document.getElementById('totalcount');
    sTotalCountSpan.innerText = "" + (sCurrentArticleCount);  
    
    if( sUnreadCountWrapperSpan == undefined )
        sUnreadCountWrapperSpan = document.getElementById('unreadcountwrapper');
    sUnreadCountWrapperSpan.style.display = (sUnreadCount>0 ?"inline" :"none");
    
    if( sUnreadCountSpan == undefined )
        sUnreadCountSpan = document.getElementById('unreadcount');
    sUnreadCountSpan.innerText = "" + sUnreadCount;
    
    updateNoArticlesMessage();
}

function refilterArticles( )
{
    sCurrentArticleCount = sUnreadCount = 0;
    var articles = document.getElementById("content").childNodes;
    var articleToKeepInView;
    for( var i=0; i<articles.length; i++ ) {
        var article = articles[i];
        if( article.nodeName == 'DIV' ) {
            shouldShow = shouldShowArticle(article);
            showHideArticle(article, shouldShow);
            if (shouldShow) {
                articleToKeepInView = article;
            }
        }
    }
    redisplayCounts();
    keepArticleInView(articleToKeepInView);    
}

function updateCounts(totalArticleCount, unreadCount) 
{
    sCurrentArticleCount = sTotalArticleCount = totalArticleCount;
    sUnreadCount = unreadCount;
    redisplayCounts();
}
    
function decrementUnreadCount( )
{
    if( sUnreadCount > 0 ) {
        --sUnreadCount;
        redisplayCounts();
    }
}


function reloadArticlesFromHTML( html )
{
    var content = document.getElementById("content");
    content.innerHTML = html;
    scroller.scrollTop = 0;    
    selArticles = new Array();      // no more selection
    if( sResetPagination ) sCurrentArticle = 0;
    if( sSourceFilter==null ) sOriginallySourceFiltered = false;     // now have all sources
    if( !sIsPaginated ) refilterArticles();
    setupSlider();
    if( sResetPagination ) {
        resetPagination();
        sResetPagination = false;
    }
}


function showHideArticle( article, show )
{
    if( show ) {
        article.style.display = "";
    } else {
        article.style.display = 'none';
        deselectArticle(article);
    }
}


var sSearchRange;

function articleMatchesFilter( article )
{
    //var text = article.innerText;     // <-doesn't work for hidden <div>s, unfortunately
    if( ! sSearchRange ) sSearchRange = document.createRange();
    sSearchRange.selectNodeContents(article);
    text = sSearchRange.toString();
    
    if( window.syndication ) {
        return sFilterWords.match(text);
    } else {
        /* Old pure-JS implementation -- keep around only till Safari is updated --jpa 2/1/2005 */
        text = text.toLocaleLowerCase();
        for( var i=sFilterWords.length-1; i>=0; i-- ) {
            var string = sFilterWords[i];
            var start = 0;
            while(true) {
                start = text.indexOf(string,start);
                if( start < 0 )
                    return false;                   // didn't find this string
                if( start==0 )
                    break;                          // found at start of text -- matches
                var prev = text.charAt(start-1);
                if( prev <= '~' ) {
                    if( prev<'0' || (prev>'9' && prev<'a') || prev>'z' )
                        break;                      // found at start of word -- matches
                } else if( prev >= '\u3000' && prev <= '\u303F' ) {
                    break;                          // CJK symbols and punctuation
                } else if( prev >= '\u5000' && prev <= '\u9FFF' ) {
                    break;                          // Chinese/Japanese characters (there are no word breaks)
                } else {
                    // JS regexps don't have a Unicode alphabet char class, only Unicode whitespace.
                    // So we simplistically assume any non-whitespace, with a few exceptions, is alphabetic.
                    if( prev.search(/[\s–—…“”‘’¿¡]/) == 0 )
                        break;                      // found at start of word -- matches
                }
                // This instance wasn't at the start of a word; search for next instance...
                start += string.length;
            }
        }        
        return true;
        /* End of old code */
    }
}


//// SORTING


var sSortDivs;


function currentSort( )
{
    if( ! sSortDivs )
        sSortDivs = document.getElementById("sorts").getElementsByTagName("div");
    for( var i=0; i<sSortDivs.length; i++ ) {
        var div = sSortDivs[i];
        if( div.className == "current" )
            return parseInt(div.getAttribute("name"));
    }
    return 0;
}


function sortArticlesBy( sortType )
{
    // Update the styles of the various sort links:
    var name = sortType.toString();
    if( ! sSortDivs )
        sSortDivs = document.getElementById("sorts").getElementsByTagName("div");
    for( var i=0; i<sSortDivs.length; i++ ) {
        var div = sSortDivs[i];
        var divname = div.getAttribute("name");
        if( divname ) {
            if( divname == name )
                div.className = "current";
            else
                div.className = null;
        }
    }
    if( sIsPaginated ) { // articles are paginated; need to sort all of them
        sResetPagination = true;
        sendSyndicationCmd("setSort",sortType,true);
    } else {
        reSortArticles(sortType);
        sendSyndicationCmd("setSort",sortType);
    }
}


function reSortArticles( sortType )
{
    // Make an array of the article nodes, removing other noise from the document:
    var parent = document.getElementById("content");
    var children = parent.childNodes;
    var articles = new Array();
    for( var i=children.length-1; i>=0; i-- ) {
        article = children[i];
        if( article.nodeType == 1 ) // element
            articles.push(article);
        else
            parent.removeChild(article);
    }

    // Sort the article array:
    var compareFunc;
    switch(sortType) {
        case 1: compareFunc = compareDates; break;
        case 2: compareFunc = compareUnread; break;
        case 3: compareFunc = compareTitles; break;
        case 4: compareFunc = compareSources; break;
        default:
            //alert("unknown sortType "+sortType);
            return;
    }
    articles.sort(compareFunc);
        
    // Now put them back:
    for( var i=0; i<articles.length; i++ ) {
        parent.appendChild(articles[i]);
        //articles[i].style.khtmlBoxOrdinalGroup = i;   //FIX: new, doesn't work right yet
    }
}


function compareDates( a, b )
{
    var adate = a.getAttribute("articlesortdate");
    var bdate = b.getAttribute("articlesortdate");
    if( adate < bdate )
        return 1;                       // We want them in _reverse_ order
    else if( adate == bdate )
        return 0;
    else
        return -1;
}


function compareTitles( a, b )
{
    var atitle = a.getAttribute("articlesorttitle");
    var btitle = b.getAttribute("articlesorttitle");
    if( atitle < btitle )
        return -1;
    else if( atitle > btitle )
        return 1;
    else
        return compareDates(a,b);
}


function compareSources( a, b )
{
    var asrc = a.getAttribute("articlesortsource");
    var bsrc = b.getAttribute("articlesortsource");
    if( asrc < bsrc )
        return -1;
    else if( asrc > bsrc )
        return 1;
    else
        return compareDates(a,b);
}


function compareUnread( a, b )
{
    var aUnread = a.className.indexOf("unread") >= 0;
    var bUnread = b.className.indexOf("unread") >= 0;
    if( aUnread ) {
        if( ! bUnread )
            return -1;
    } else if( bUnread ) {
        if( ! aUnread )
            return 1;
    }
    return compareDates(a,b);
}

//// PAGINATION:
var sCurrentArticle = 0;
var sArticlesPerPage;
var sTotalArticleCount;
var sTotalPageCount;
var sIsPaginated = false;
var sResetPagination = false;
var previousarticles, previousarticle, currentarticles, nextarticles, nextarticle, pagination;
var sNextImageLocation, sNextDisabledImageLocation, sPreviousImageLocation, sPreviousDisabledImageLocation;

function setupPagination(totalArticleCount, unreadCount, articlesPerPage) {
    pagination = document.getElementById('pagination');
    sArticlesPerPage = articlesPerPage;
    sCurrentArticleCount = sTotalArticleCount = totalArticleCount;
    sUnreadCount = unreadCount;

    redisplayCounts();

    sIsPaginated = (sTotalArticleCount > sArticlesPerPage);
    // build image page names based off of one that's resolved (can't access rsrcpath template expansion)
    nextarticle = document.getElementById('nextarticle');
    sNextImageLocation = nextarticle.src;
    var imageLocation = sNextImageLocation.replace(/\/[^\/]*$/, "/");
    sNextDisabledImageLocation = imageLocation + 'NextPage_Disabled.tif';
    sPreviousImageLocation = imageLocation + 'PreviousPage.tif';
    sPreviousDisabledImageLocation = imageLocation + 'PreviousPage_Disabled.tif';
    
    if (sIsPaginated) {
        resetPagination();
    } else {
        pagination.style.display = 'none';
    }
}

function resetPagination() {
    if( previousarticles == undefined ) {
        previousarticles = document.getElementById('previousarticles');
        previousarticle = document.getElementById('previousarticle');
        currentarticles = document.getElementById('currentarticles');
        nextarticles = document.getElementById('nextarticles');
        nextarticle = document.getElementById('nextarticle');    
    }
    var countElem = document.getElementById('totalarticlecount');
    sCurrentArticleCount = sTotalArticleCount = Number(countElem.getAttribute("total"));
    sUnreadCount = Number(countElem.getAttribute("unread"));
    redisplayCounts();
    sTotalPageCount = Math.ceil(sTotalArticleCount / sArticlesPerPage);
    if (sTotalArticleCount <= sArticlesPerPage) {
        pagination.style.display = 'none';
    } else {
        sIsPaginated = true;
        previousarticles.style.display = 'none';
        var pages = currentarticles.childNodes;
        var amountToShow = Math.min(sTotalPageCount, 10);
        for (i = 0; i < amountToShow; i++) {
            pages[i].style.display = 'inline';
            pages[i].innerText = i + 1 + "";
            pages[i].className = null;
        }
        for (; i < pages.length; i++) {
            pages[i].style.display = 'none';
            pages[i].innerText = i + 1 + "";            
            pages[i].className = null;            
        }
        if (sTotalPageCount > pages.length) {
            nextarticles.style.display = 'inline';
        } else {
            nextarticles.style.display = 'none';
        }
        pages[0].className = "selected";
        previousarticle.src = sPreviousDisabledImageLocation;
        nextarticle.src = sNextImageLocation;
        pagination.style.display = 'inline';
    }
}

function goToPage(page) {
    var newCurrentArticle, currentPage;
    var previousPage = Math.ceil((sCurrentArticle+1) / sArticlesPerPage);
    var pages = currentarticles.childNodes;    
    switch(page) {
        case -4: // previous page
            if (sCurrentArticle != 0) {
                newCurrentArticle = sCurrentArticle - sArticlesPerPage;
                currentPage = previousPage - 1;
                // currentPage = 10, 20, 30...
                if( currentPage % 10 == 0 ) {
                    renumberPages(currentPage - 9, false);
                }
            }
            break;
        case -3: // last of previous 10 pages
            // 21 -> 20; 30 -> 20
            currentPage = Math.floor((previousPage - 11) / 10) * 10 + 10;
            newCurrentArticle = (currentPage - 1) * sArticlesPerPage;
            renumberPages(currentPage - 9, false);
            break;
        case -2: // first to next 10 pages
            // 11 -> 21; 20 -> 21
            currentPage = Math.floor((previousPage + 9) / 10) * 10 + 1;
            newCurrentArticle = (currentPage - 1) * sArticlesPerPage;
            renumberPages(currentPage, true);
            break;
        case -1: // next page
            if (sCurrentArticle + sArticlesPerPage < sTotalArticleCount) {
                newCurrentArticle = sCurrentArticle + sArticlesPerPage;
                currentPage = previousPage + 1;
                // currentPage = 1, 11, 21...
                if( currentPage % 10 == 1) {
                    renumberPages(currentPage, true);
                }                
            }
            break;
        default: // clicked on a particular number
            currentPage = Number(pages[page - 1].innerText);
            newCurrentArticle = (currentPage - 1) * sArticlesPerPage;
            break;
    }
    
    // Change the enabled/disabled state of the forward/back arrows
    switch (page) {
        // moving backwards
        case -4:
        case -3:
            // was on the last page, now we're not
            if (previousPage == sTotalPageCount) {
                nextarticle.src = sNextImageLocation;
            }
            if (currentPage == 1) {
                previousarticle.src = sPreviousDisabledImageLocation;
            }
            break;        
        // moving forwards
        case -2:
        case -1:
            // was on the first page, now we're not
            if (previousPage == 1) {
                previousarticle.src = sPreviousImageLocation;
            }
            if (currentPage == sTotalPageCount) {
                nextarticle.src = sNextDisabledImageLocation;
            }
            break;
        default:
            // could be going forwards or backwards
            if (currentPage == sTotalPageCount) {
                nextarticle.src = sNextDisabledImageLocation;
                previousarticle.src = sPreviousImageLocation;
            } else if (currentPage == 1) {
                nextarticle.src = sNextImageLocation;
                previousarticle.src = sPreviousDisabledImageLocation;            
            } else {
                nextarticle.src = sNextImageLocation;
                previousarticle.src = sPreviousImageLocation;            
            }        
            break;
    }
    if (newCurrentArticle != sCurrentArticle && newCurrentArticle >= 0) {
        pages[((previousPage - 1) % 10)].className = null;
        pages[((currentPage - 1) % 10)].className = "selected";
        sCurrentArticle = newCurrentArticle;    
        sendSyndicationCmd("setOffset",sCurrentArticle,true);		
    }
}

function renumberPages(startCount, countUp) {
    var pages = currentarticles.childNodes;
    for (i = 0; i < pages.length; i++) {
        pages[i].innerText = startCount + i + "";
    }
    // last page eg: 25 pages total, would show range 21 - 30, but need to clip it
    if (countUp && (startCount + 9) > sTotalPageCount) {
        for (i = (sTotalPageCount % 10); i < pages.length; i++) {
            pages[i].style.display = 'none';
        }
        // don't show "more"
        nextarticles.style.display = 'none';
    // next to last page eg: 25 pages total, need to show full range of 11-20; 11+19=30 > 25
    } else if (!countUp && (startCount + 19) > sTotalPageCount) {
        for (i = 0; i < pages.length; i++) {
            pages[i].style.display = 'inline';
        }    
        // show "more"
        nextarticles.style.display = 'inline';        
    }
    if (startCount == 1) {
        // first page; don't show "previous"
        previousarticles.style.display = 'none';
    } else {
        previousarticles.style.display = 'inline';    
    }
}

//// SLIDER:

var slider;
var scroller;

var pinnedElement;
var offsetFromTop;
var content;
var contentElements;
var scaling = false;
var pinnedSidebarTop;

function setupSlider() {
    if( slider === undefined ) {
        slider = document.getElementById("slider");
        scroller = document.getElementById("scroller");    
        // Initial value of appleLineClamp in the HTML doesn't take effect [3762017] so we have to change it:
        if( slider.value != 100 )
            document.getElementById('content').style.appleLineClamp = slider.value + "%";
    }
}

function startScale()
{
    scaling = true;
    if( scroller == undefined ) scroller = document.getElementById('scroller');
    var top = scroller.scrollTop;
    if( sidebar === undefined )
        sidebar = document.getElementById("sidebar");
    pinnedSidebarTop = top - sidebar.offsetTop;    
    if( contentElements == undefined ) {
        if ( content == undefined ) content = document.getElementById('content');
        contentElements = new Array();
        var children = content.childNodes;
        var bottom = top + scroller.clientHeight;
        var found = 0;
        offsetFromTop = 0;
        var numItems = 0;
        var child;
        for (var i = 0; i < children.length; i++) {
            child = children[i];
            if (child.nodeType == 1) { // element
                contentElements.push(child);
                if (!found) {
                    var elementTop = child.offsetTop;
                    if (elementTop > top) {
                        // Item contents span entire window
                        if (elementTop > bottom && i > 0) {
                            pinnedElement = contentElements[numItems-1];
                        } else {
                            pinnedElement = child;
                        }
                        found = 1;
                    }
                }
                numItems++;
            }
        }
    } else if (contentElements.length > 0) {
        var bottom = top + scroller.clientHeight;
        var found = 0;
        offsetFromTop = 0;
        for (i = 0; !found && i < contentElements.length; i++) {
            var elementTop = contentElements[i].offsetTop;
            if (elementTop > top) {
                // Item contents span entire window
                if (elementTop > bottom && i > 0) {
                    pinnedElement = contentElements[i-1];
                } else {
                    pinnedElement = contentElements[i];
                }
                found = 1;
            }
        }
    }
    offsetFromTop = pinnedElement.offsetTop - top;
}

function endScale()
{
    scaling = false;
    slipDiffScroll( scroller );
    
    // Tell the back-end to update the prefs for this feed:
    sendSyndicationCmd("setSlider",""+slider.value);
}

function scaleArticles( value )
{
    if ( content == undefined ) content = document.getElementById('content');
    content.style.appleLineClamp = value + "%";
    if (pinnedElement != undefined) {
        scroller.scrollTop = pinnedElement.offsetTop - offsetFromTop;
    }
}

function scaleTo( value ) {
    if( value != slider.value ) {
        startScale();
        slider.value = value;
        scaleArticles( value ); // Need to call this because events aren't called when the value is programmatically called
        //console.log("slider="+slider+", slider.value="+slider.value+", should be "+value+" which is "+typeof(value));
        endScale();
    }
}

function scaleToMin() {
    scaleTo( slider.min );
}

function scaleToMax() {
    scaleTo( slider.max );    
}


//// SCROLLING:


function handleScrollKeys( event )
{
    if ( scroller == undefined ) scroller = document.getElementById('scroller');
    var keyIdentifier = event.keyIdentifier;
    switch(keyIdentifier) {
        case "Up":
            scroller.scrollByLines(-1);
            break;
        case "Down": 
            scroller.scrollByLines(1); 
            break;
        case "PageUp": 
            scroller.scrollByPages(-1); 
            break;
        case "PageDown":
            scroller.scrollByPages(1);
            break;
        case "Home":
            scroller.scrollLeft = 0;
            scroller.scrollTop = 0;
            break;
        case "End":
            scroller.scrollLeft = 0;
            scroller.scrollTop = scroller.scrollHeight;
            break;
        default:
            return;
    }
    event.preventDefault();
}

var sidebar;

function slipDiffScroll(scroller)
{
    if( sidebar === undefined )
        sidebar = document.getElementById("sidebar");
    if (scaling) {
        sidebar.style.top = scroller.scrollTop - pinnedSidebarTop;
    } else {
        //console.log("scroller="+scroller+", height="+scroller.clientHeight+", scrollY="+scroller.scrollTop);
        var windowBottom = scroller.clientHeight + scroller.scrollTop;
        var viewportTop = scroller.scrollTop+8;
        
        var sidebarTop = windowBottom - 8 - sidebar.offsetHeight;
        //console.log("windowBottom="+windowBottom+", viewportTop="+viewportTop+", sidebarTop="+sidebarTop);
        if (sidebarTop < 8)
            sidebarTop = 8;                                         // Don't let top of sidebar fall off the top
        else if (sidebarTop > viewportTop)
            sidebarTop = viewportTop;                               // Don't let top of sidebar go below top of visible area
        sidebar.style.top = sidebarTop - 8;
        //console.log("sidebar.style.top = "+(sidebarTop-8));
    }
}

function keepArticleInView(articleToKeepInView)
{
    if ( scroller == undefined ) scroller = document.getElementById('scroller');
    if ( articleToKeepInView == undefined ) {
        scroller.scrollTop = 0;
    } else {
        if( content == undefined ) content = document.getElementById('content');
        var contentTop = content.offsetTop;
        var childBottom = contentTop + articleToKeepInView.offsetTop + articleToKeepInView.clientHeight + 3;
        var top = scroller.scrollTop;
        var bottom = top + scroller.clientHeight;
        // the last child's bottom and top is above the visible window
        if( contentTop < top && childBottom < bottom ) {
            scroller.scrollTop = childBottom - scroller.clientHeight;        
        }
    }
}

//// FOAF:


var FOAFRequest;

function loadFOAF( )
{
    FOAFRequest = requestForSyndicationCmd("getFOAF","");
    FOAFRequest.onload = receivedFOAF;
    FOAFRequest.send(null);
}


function replaceElementWithHTML( element, html )
{
    if( html ) {
        var node = document.createElement("DIV");
        node.innerHTML = html;
        // Find the actual HTML node, skipping any preceding whitespace:
        for( var i=0; i<node.childNodes.length; i++ ) {
            var child = node.childNodes[i];
            if( child.nodeType == Node.ELEMENT_NODE ) {
                element.parentNode.replaceChild(node,element);
                return;
            }
        }
    }
    element.parentNode.removeChild(element);
}


function receivedFOAF()
{
    var placeholder = document.getElementById("FOAFPlaceholder");
    replaceElementWithHTML(placeholder,FOAFRequest.responseText);
}


function swapOut( outID, inID )
{
    document.getElementById(outID).style.display = "none";
    document.getElementById(inID) .style.display = "block";
}


//// TOOLS:


function bookmarkFeed()
{
    sendSyndicationCmd( "bookmark","");
}


//// SETUP:


function updateSettingsIfAfter( timeCreated )
{
    // If the page is being loaded more than 5 seconds after it was created, fetch the current
    // sidebar settings and update if they've changed:
    if( (new Date().getTime())-timeCreated > 5000 ) {
        var post = requestForSyndicationCmd("getSettings","");
        post.onload = function() {receivedSettings(post.responseText);};
        post.send(null);
    }
}

function receivedSettings( text ) {
    var settings = text.split("\n");
    
    sSendCommands = false;      // Don't tell the back-end about "changes" to settings
    
    var sort = parseInt(settings[0]);
    if( sort != currentSort() )
        sortArticlesBy(sort);

    var timespan = parseInt(settings[1]);
    if( timespan != currentTimespan() )
        setDateFilter(timespan);
     
    setupSlider();
    var newSlider = parseInt(settings[2]);
    scaleTo(newSlider);
    
    var source = settings[3];
    if( source != currentSourceFilter() )
        setSourceFilter(source);
    
    sSendCommands = true;
}


function setupSourceNames( /*varargs*/ )
{
    var sourceLinks = document.getElementById("sourcelist").getElementsByTagName("a");
    for( var i=0; arguments[i]; i++ )
        sourceLinks[i].innerText = arguments[i];
}


function setupTitle( title )
{
    document.title = title;
    document.getElementById("titleText").innerText = title;
    document.getElementById("sourceNameText").innerText = title;
}


/* This is called via an onLoad handler when the page finishes loading. */
function setup( )
{
    setupFilter();
    setupSlider();
    setURLToBookmark();
}
