/*******************************
* File: cbbg.js
* Desc: Script for blog gadgets.
* Auth: Kevin Machin.
*******************************/

/*jsl:import ../Scripts/cb.js       */
/*jsl:import ../Scripts/exlinks.js  */

// Loading
cbOnLoad (cbBgLoad);
var gsCbBgClass = "cbbg";

function cbBgLoad ()
{
    var iGadgets = 0, aoGadget = new Array;
    var sErrMsg = "ErrMsg";

    try
    {
        // Set up array of gadgets
        gadgetAdd ("CbBgWiP",     cbBgWiP);
        gadgetAdd ("CbBgGMatrix", cbBgGMatrix);
        gadgetAdd ("CbBgReading", cbBgReading);
        gadgetAdd ("CbBgExLinks", cbBgExLinks);
        
        // Load the gadgets
        gadgetsLoad ();

        // Load any sub-documents
        cbSubDocsLoad ();

        // Set up resizing for in-line frames
        /* FIXME: Chain? */
        //alert ("window.onresize = " + window.onresize);
        window.onresize = cbIFrameResize;
        cbIFrameResize (null);
    }
    catch (e)
    {
        cbErrMsg ("Failed to display gadget!", e, null, sErrMsg);
    }

    ///////////////////////////////////////////////////////
    // Priv: gadgetAdd
    // Desc: Add a gadget to the array.
    // Args: psId      -- Identifier for container element.
    //       pfnLoader -- Loader function.
    ///////////////////////////////////////////////////////

    function gadgetAdd (psId, pfnLoader)
    {
        var oGadget;

        // Create gadget and add it to the array
        oGadget = new gadget (psId, pfnLoader);
        aoGadget[iGadgets] = oGadget; iGadgets++;
    
        // Constructor
        function gadget (psId, pfnLoader)
        {
            this.sContainer = psId;
            this.oContainer = null;
            this.sMessage = sErrMsg = psId + "ErrMsg";
            this.oMessage = null;
            this.pfnLoader = pfnLoader;
        }
        // End of gadget (constructor)
    }
    // End of gadgetAdd

    //////////////////////////////////////////////////
    // Priv: gadgetsLoad
    // Desc: Attempt to load the gadgets in the array.
    //////////////////////////////////////////////////
    
    function gadgetsLoad ()
    {
        var iG, oG, oContainer, oMessage, pfnLoader, oGadget;
        
        // Iterate through gadgets
        for (iG = 0; iG < iGadgets; iG++)
        {
            // Consider the next gadget
            oG = aoGadget[iG];
            sErrMsg = oG.sMessage;
            oContainer = document.getElementById (oG.sContainer);
            if (oContainer !== null)
            {
                // Save container element
                oG.oContainer = oContainer;
                
                // Add "loading" message to container element
                oG.oMessage = oMessage = cbTag ("p", "Loading...");
                oMessage.className = gsCbBgClass + "error";
                oMessage.id = oG.sMessage;
                oContainer.appendChild (oMessage);
            }
        }
        
        // Second pass to load the gadgets
        for (iG = 0; iG < iGadgets; iG++)
        {
            oG = aoGadget[iG];
            sErrMsg = oG.sMessage;
            oContainer = oG.oContainer;
            if (oContainer !== null)
            {
                // Is there a loader
                pfnLoader = oG.pfnLoader;
                if (pfnLoader !== null)
                {
                    // Call the loader
                    try
                    {
                        oGadget = pfnLoader ();
                    }
                    catch (e)
                    {
                        oGadget = null;
                        cbErrMsg ("Failed to display gadget!", e, null, sErrMsg);
                    }
                    if (oGadget !== null)
                    {
                        // Attach the gadget to the container
                        oContainer.appendChild (oGadget);

                        // Remove "loading" message
                        oMessage = oG.oMessage;
                        if (oMessage !== null)
                        {
                            oContainer.removeChild (oMessage);
                        }
                    }
                }
            }
        }
    }
    // End of gadgetsLoad
}
// End of cbBgLoad

///////////////////////////////////
// Func: cbBgWiP
// Desc: Display works in progress.
// Retn: Gadget object reference.
///////////////////////////////////

function cbBgWiP ()
{
    var oTable, oCell, i, iRows = 0, oDB, oProj;

    // Create a table to hold the meters
    oTable = cbTable ();
    oTable.className = gsCbBgClass + "wip";
    oTable.row ();
    for (i = 0; i < 2; i++)
    {
        oCell = oTable.cell ("th", "Title");
        oCell.style.textAlign = "Left";
        if (i === 1)
        {
            oCell.style.borderLeft = "2px Solid #D3D3D3"; // LightGray
        }
        oTable.cell ("th", "Count");
        oTable.cell ("th", "Expected");
        oCell = oTable.cell ("th", "Progress");
        oCell.colSpan = 2;
        if (i === 0)
        {
            oCell.style.borderRight = "2px Solid Gray";
        }
    }

    // Data
    oDB = cbProjects ("Novel,Serial,Novella");
    for (i = 0; i < oDB.iCountProj; i++)
    {
        oProj = oDB.aoProj[i];
        if ((oProj.iWords > 0) && (oProj.iWords < oProj.iMax))
        {
            meterRow (oProj.sTitle, oProj.iWords, oProj.iMax);
        }
    }
    
    // Finish off
    if ((iRows % 2) !== 0)
    {
        oCell = oTable.cell ();
        oCell.colSpan = 5;
        oCell.style.borderLeft = "2px Solid #D3D3D3"; // LightGray
    }

    ////////////////////////////////////////////////
    // Priv: meterRow
    // Desc: Add a row to the table.
    // Args: psTitle -- Title of WiP.
    //       iWords  -- Words done so far.
    //       iMax    -- Expected maximum word count.
    ////////////////////////////////////////////////

    function meterRow (psTitle, iWords, iMax)
    {
        var bNewRow;
        
        // Start new row if necessary
        iRows++;
        bNewRow = ((iRows % 2) === 0) ? false : true;
        if (bNewRow)
        {
            oTable.row ();
        }
        
        // Title
        oCell = oTable.cell (psTitle);
        if (!bNewRow)
        {
            oCell.style.borderLeft = "2px Solid #D3D3D3"; // LightGray
        }
        
        // Count and maximum
        oCell = oTable.number (iWords);
        oCell.className = gsCbBgClass + "wii";
        oCell = oTable.number (iMax);
        oCell.className = gsCbBgClass + "wii";
        
        // Percentage
        oCell = oTable.cell (cbPercent (iWords, iMax) + "%");
        oCell.className = gsCbBgClass + "wii";
        oCell.style.borderRight = "0 None";
        
        // Bar
        oCell = oTable.bar (iWords, iMax);
        oCell.style.borderLeft = "0 None";
        if (bNewRow)
        {
            oCell.style.borderRight = "2px Solid Gray";
        }
    }
    // End of meterRow

    return (oTable);
}
// End of cbBgWiP

/////////////////////////////////////////////////////
// Func: cbBgReading
// Desc: Display the "currently reading" book covers.
// Retn: Gadget object reference.
/////////////////////////////////////////////////////

function cbBgReading ()
{
    var aoBook = new Array, iBooks = 0;
    var oTable, oBook, i, iC, iW, iRS, iCS;
    var sClass = gsCbBgClass + "cr";

    // Set up list of book covers (most recent first)
    bookAdd ("Matter",                       "2.bp.blogspot.com/_78Do_yAqJDI/SwP_-Rif6OI/AAAAAAAAAZo/u7MFuY7vjdQ");
    bookAdd ("Heaven_can_Wait",              "4.bp.blogspot.com/_78Do_yAqJDI/SugjorxrzXI/AAAAAAAAAZg/SPzlecpdhFg");
    bookAdd ("Secret_Shoppers_Revenge",      "2.bp.blogspot.com/_78Do_yAqJDI/SreZDQ-DGPI/AAAAAAAAAYk/2F2pto7O-Vs");
    bookAdd ("Your_Heart_Belongs_to_Me",     "3.bp.blogspot.com/_78Do_yAqJDI/SqdtTbKAYMI/AAAAAAAAAYc/cjPEp1Qf4qY");
    bookAdd ("Double_Cross",                 "1.bp.blogspot.com/_78Do_yAqJDI/SoxHZzBP_OI/AAAAAAAAAYU/JMuKSjwbz5Q");
    bookAdd ("First_Drop",                   "2.bp.blogspot.com/_78Do_yAqJDI/Snf17_BZIuI/AAAAAAAAAYM/kkunCFIbAl8");
    bookAdd ("HowNotToWriteANovel",          "2.bp.blogspot.com/_78Do_yAqJDI/Sm26EcUVGVI/AAAAAAAAAYE/7bUTUG5NTiU");
    bookAdd ("Mollys_Millions",              "4.bp.blogspot.com/_78Do_yAqJDI/SlzK5DzpkLI/AAAAAAAAAVE/UPrF0-BzmTg");
    bookAdd ("House_of_Thunder",             "1.bp.blogspot.com/_78Do_yAqJDI/Sk8BIIkjwSI/AAAAAAAAAU8/qye0klYLzm4");
    bookAdd ("Sleeping_Doll",                "1.bp.blogspot.com/_78Do_yAqJDI/SizaKt78LoI/AAAAAAAAAUk/34hMNT2pJQ0");
    bookAdd ("Nano_Flower",                  "1.bp.blogspot.com/_78Do_yAqJDI/Sf6jbpumAtI/AAAAAAAAATc/CwyP1rHHBJU");
    bookAdd ("Black_Boxes",                  "1.bp.blogspot.com/_78Do_yAqJDI/SeTo8oH4DyI/AAAAAAAAATU/5Mt7FNBZvq0");
    bookAdd ("Not_the_End_of_the_World",     "2.bp.blogspot.com/_78Do_yAqJDI/ScQDzCjX2jI/AAAAAAAAATM/wHRlsyApa0Y");
    bookAdd ("Dead_Mans_Footsteps",          "3.bp.blogspot.com/_78Do_yAqJDI/SbFW3MVPNLI/AAAAAAAAAS0/kuCJ121E76o");
    bookAdd ("Walking_on_Glass",             "1.bp.blogspot.com/_78Do_yAqJDI/SYdHO0Ew8kI/AAAAAAAAASs/q1p8LP_A5oU");
    bookAdd ("Temporal_Void",                "4.bp.blogspot.com/_78Do_yAqJDI/SVC_-M75IWI/AAAAAAAAAP4/B-Yw6MOeABM");
    bookAdd ("When_Will_There_Be_Good_News", "2.bp.blogspot.com/_78Do_yAqJDI/SWptpKpheaI/AAAAAAAAAQg/lE1glLnn2LI");
    bookAdd ("Over_You",                     "lh6.ggpht.com/_78Do_yAqJDI/SUqatJbrj-I/AAAAAAAAAPo/ovGLMVdhuWo");

    // Start table
    oTable = cbTable ();
    oTable.className = sClass;
    oTable.row ();

    // Iterate over the books
    for (i = iC = 0; i < iBooks; i++)
    {
        // Start new row if necessary
        if (iC >= 8)
        {
            if (i < 5)
            {
                iC = 4;
            }
            else
            {
                iC = 0;
            }
            oTable.row ();
        }

        // Decide on size of book image
        oBook = aoBook[i];
        iRS = iCS = 1;
        if (i === 0)
        {
            // First book biggest
            iW = 128;
            iC += 4;
            iRS = 2;
            iCS = 4;
        }
        else if (i < 5)
        {
            // Next four books medium sized
            iW = 64;
            iC += 2;
            iCS = 2;
        }
        else
        {
            // All other books small
            iW = 32;
            iC++;
        }

        // Render the book image
        imgWrite (oBook, iW, iRS, iCS);
    }

    // Complete the row
    for (iW = 32 ; iC < 8; iC++)
    {
        imgWrite (null, iW);
    }

    /////////////////////////////////////
    // Priv: bookAdd
    // Desc: Constructor for book object.
    // Args: psFile -- Image file name.
    //       psLocn -- File location.
    /////////////////////////////////////
    
    function bookAdd (psFile, psLocn)
    {
        var oBook;
        
        // Create new book and add it to the array
        oBook = new book (psFile, psLocn);
        aoBook[iBooks] = oBook; iBooks++;
        
        // Constructor
        function book (psFile, psLocn)
        {
            // Location
            if ((psLocn !== "") && (psLocn.indexOf (":") < 0))
            {
                psLocn = "http://" + psLocn;
            }
            this.sURL = psLocn;

            // File name
            if (psFile.indexOf (".") < 0)
            {
                psFile += ".jpg";
            }
            this.sImg = psFile;
        }
        // End of book (constructor)
    }
    // End of bookAdd

    //////////////////////////////////////////////////
    // Priv: imgWrite
    // Desc: Render an image.
    // Args: oBook -- Book object.
    //       iSize -- Size in pixels.
    //       iRows -- Number of table rows to span.
    //       iCols -- Number of table columns to span.
    //////////////////////////////////////////////////
    
    function imgWrite (oBook, iSize)
    {
        var sLarge, sSmall, sURL, sImg, oCell, oLink, oImg;
        
        // Get cell spanning parameters
        var iRows = (arguments.length > 2) ? arguments[2] : 1;
        var iCols = (arguments.length > 3) ? arguments[3] : 1;

        // Get book details
        if (oBook !== null)
        {
            sURL = oBook.sURL;
            sImg = oBook.sImg;
        }
        else
        {
            sURL = sImg = "";
        }

        // Directories for different sizes
        if (sURL.indexOf ("http://") >= 0)
        {
            sLarge = "/s1600-h/";
            sSmall = "/s400/";
        }
        else
        {
            sLarge = sSmall = "/";
        }

        // Render the table cell with the image
        oCell = oTable.cell ();
        oCell.rowSpan = iRows;
        oCell.colSpan = iCols;
        if (sURL === "")
        {
            oCell.innerHTML = (cbDebugMask ()) ? "x" : "&nbsp;";
        }
        else
        {
            oImg = cbTag ("img");
            oImg.className = sClass;
            oImg.style.maxWidth = oImg.style.width = iSize + "px";
            oImg.style.maxHeight = Math.floor ((iSize * 3) / 2) + "px";
            oImg.src = sURL + sSmall + sImg;
            oImg.alt = "Book cover";
            oLink = cbLink (oImg, sURL + sLarge + sImg, "title=Click for bigger picture...");
            oLink.className = sClass;
            oCell.appendChild (oLink);
        }
    }
    // End of imgWrite
    
    // Return gadget object
    return (oTable);
}
// End of cbBgReading

//////////////////////////////////
// Func: cbBgGMatrix
// Desc: Display the genre matrix.
// Retn: Gadget object reference.
//////////////////////////////////

function cbBgGMatrix ()
{
    var asGenre = new Array, aoAuth = new Array;
    var i, iA, iG, sL, sT, oT, oC, oE;

    var sClass = gsCbBgClass + "gm"; // Class name

    // Set up genre data
    i = 0;
    /*jsl:ignore*/
    asGenre[i++] = "Children";
    asGenre[i++] = "Crime";
    asGenre[i++] = "Horror";
    asGenre[i++] = "Humour";
    asGenre[i++] = "Poetry";
    asGenre[i++] = "Romance";
    asGenre[i++] = "Saga";
    asGenre[i++] = "SF/Fantasy";

    // Set up author data
    i = 0;
    aoAuth[i++] = new author ("Annie");
    aoAuth[i++] = new author ("Captain Black");
    aoAuth[i++] = new author ("Caroline");
    aoAuth[i++] = new author ("Denise");
    aoAuth[i++] = new author ("Fiona");
    aoAuth[i++] = new author ("Lane");
    aoAuth[i++] = new author ("Leatherdykeuk");
    aoAuth[i++] = new author ("Shaun");
    /*jsl:end*/

    // Fill in as we go...
    aoAuth[1].asGenre[0] = "2008/12/genre-matrix-children-captain";
    aoAuth[1].asGenre[1] = "2008/10/crime-captain";
    aoAuth[1].asGenre[2] = "2008/08/genre-matrix-horror-captain";
    aoAuth[1].asGenre[7] = "2008/12/story-about-toy-duck";
    aoAuth[4].asGenre[2] = "2008/08/spanish-house-edited-if-bloggers";
    aoAuth[5].asGenre[0] = "2008/08/genre-matrix-children-lane";
    aoAuth[5].asGenre[7] = "2008/11/dreamail-lane";
    aoAuth[6].asGenre[0] = "2008/08/childrens-story-kias-lunch-by-rachel";
    aoAuth[6].asGenre[1] = "2008/10/genre-matrix-crime-rachel";

    // Create for table
    oT = cbTable ();
    oT.className = sClass;
    oT.row ();
    oC = oT.cell ("th", "Author");
    oC.rowSpan = 2;
    oC.style.verticalAlign = "Bottom";
    oC = oT.cell ("th", "Genre");
    oC.colSpan = 8;
    oT.row ();
    for (iG = 0; iG < asGenre.length; iG++)
    {
        oC = oT.cell (asGenre[iG]);
        oC.style.fontStyle = "Italic";
    }

    // Iterate over authors
    for (iA = 0; iA < aoAuth.length; iA++)
    {
        oT.row ();
        oC = oT.cell (aoAuth[iA].sName);
        oC.style.textAlign = "Left";
        for (iG = 0; iG < asGenre.length; iG++)
        {
            sL = aoAuth[iA].asGenre[iG];
            if (sL === "")
            {
                oE = "&nbsp;";
            }
            else
            {
                sT =  "title=Click to read " + asGenre[iG].toLowerCase ();
                sT += " story by " + aoAuth[iA].sName + "...";
                oE = cbTag ("a", "X", sT);
                oE.href = "http://cloud-line.blogspot.com/" + sL + ".html";
                oE.className = sClass;
            }
            oC = oT.cell (oE);
        }
    }

    ////////////////////////////////////////
    // Priv: author
    // Desc: Constructor for author objects.
    // Args: psN -- Author's name.
    // Retn: Object.
    ////////////////////////////////////////

    function author (psN)
    {
        var iG;

        this.sName = psN;
        this.asGenre = new Array ();
        for (iG = 0; iG < asGenre.length; iG++)
        {
            if ((iG + 1) >= arguments.length)
            {
                this.asGenre[iG] = "";
            }
            else
            {
                this.asGenre[iG] = arguments[iG + 1];
            }
        }
    }
    // End of author
    
    return (oT);
}
// End of cbBgGMatrix

/////////////////////////////////////////
// Func: cbBgExLinks
// Desc: Display lists of external links.
// Retn: Gadget object reference.
/////////////////////////////////////////

function cbBgExLinks ()
{
    var oGadget, oDB, aoExLink;
    var S_AUTH = 1, S_TYPE = 2, S_CAT = 4;

    // FIXME: Temporary testing code.
    //gbCbLegacyCode = false;

    // Create gadget container
    oGadget = cbTag ("div");
    oGadget.className = gsCbBgClass;
    oGadget.style.margin = "0";
    oGadget.style.border = "0 None";
    oGadget.style.padding = "0";
    
    // Initialise database
    oDB = new cbExLinks ();
    aoExLink = oDB.aoLink;

    // Create the lists
    exList ("Fiction",  "Blog",        "Fiction", S_AUTH);
    exList ("Blogs",    "Blog",        "Group",   S_CAT);
    exList ("",         "Blog",        "Personal",S_AUTH);
    exList ("Links",    "Web,Facebook","*",       S_TYPE);
    exList ("Resources","Resource",    "*",       S_CAT);

    /////////////////////////////////////////////////
    // Func: exList
    // Desc: Produce a list of links.
    // Args: psHeading -- List heading.
    //       psTypes   -- Comma separated types list.
    //       psCats    -- Categories list.
    //       iShow     -- Flags for display fields.
    /////////////////////////////////////////////////

    function exList (psHeading, psTypes, psCats, iShow)
    {
        var oTag, oList, oItem, iL, oL, sType, s, sTitle, oA;

        // Heading
        if (psHeading !== "")
        {
            oTag = cbTag ("h5", psHeading);
            oTag.className = gsCbBgClass;
            oGadget.appendChild (oTag);
        }

        // Start list
        oList = cbTag ("ul");
        oList.className = gsCbBgClass;
        oGadget.appendChild (oList);

        // Iterate through items
        for (iL = 0; iL < oDB.iCountLinks; iL++)
        {
            // Type we want to display?
            oL = aoExLink[iL];
            sType = oL.sType;
            if (psTypes.indexOf (sType) >= 0)
            {
                // Category we want?
                if ((psCats == "*") || (psCats.indexOf (oL.sCat) >= 0))
                {
                    // Create list item
                    oItem = cbTag ("li");
                    oItem.className = gsCbBgClass;
                    oList.appendChild (oItem);

                    // Author
                    if (iShow & S_AUTH)
                    {
                        s = oL.sAuthor + ": ";
                        oTag = document.createTextNode (s);
                        oItem.appendChild (oTag);
                    }

                    // Description (hover-over)
                    sTitle = oL.sDesc;
                    if (sTitle === "")
                    {
                        sTitle = "Click to visit...";
                    }

                    // Link
                    oA = cbTag ("a", oL.sName, "target=_blank");
                    oA.href = oL.sURL;
                    oA.title = sTitle;
                    oA.className = gsCbBgClass;
                    oItem.appendChild (oA);

                    // Type
                    s = "";
                    if (iShow & S_TYPE)
                    {
                        s += " (" + oL.sType + ")";
                    }

                    // Category
                    if (iShow & S_CAT)
                    {
                        s += " (" + oL.sCat + ")";
                    }

                    if (s !== "")
                    {
                        oTag = document.createTextNode (s);
                        oItem.appendChild (oTag);
                    }
                }
            }
        }
    }
    // End of exList
    
    return (oGadget);
}
// End of cbBgExLinks

////////////////////////////////////////////////
// Func: cbProjects
// Desc: Database of projects and their details.
// Args: psFilter -- Filter for project classes.
// Retn: Database object.
////////////////////////////////////////////////

function cbProjects ()
{
    var iCount = 0, iCountProj = 0, aoProj = new Array, aoFilt = new Array;
    var iP, oProj, sDesc, sJason;

    // Get parameter
    var psFilter = (arguments.length > 0) ? arguments[0] : "";

    // Set up the data
    add ("BotR","Blood on the Rooftops"    ,"Novel"  ,"Crime"   ,"Writing","Draft 1", 45620, 93000,"blood_sample");
    add ("TBAF","The Bridge Across Forever","Novel"  ,"SF"      ,"Dormant","Draft 1", 35191, 58000,"bridge_af");
    add ("Ins" ,"Insight"                  ,"Novel"  ,"SF"      ,"Shelved","Draft 2",126003,126003,"insight"); // 19/45 chap synopses
    add ("Dril","Driller"                  ,"Novel"  ,"SF"      ,"Shelved","Draft 1",  4301, 60000,"driller_story");
    add ("Ast" ,"Asteroid"                 ,"Serial" ,"SF"      ,"Dormant","Draft 1", 29191, 59000,"Asteroid/ast_nav");
    add ("KaM" ,"Kim &amp; Mark"           ,"Serial" ,"Crime"   ,"Dormant","Draft 1", 10712, 14000,"Kim_Mark/km_nav");
    add ("FiDM","Fugue in D Minus"         ,"Novella","Thriller","Dormant","Draft 2", 24447, 24447,"Fugue/fugue_nav");
    add ("TWOH","The Wild One's Hideout"   ,"Novella","SF"      ,"Dormant","Draft 2", 16649, 16649,"Twoh_Sample");
    add ("SS"  ,"Seeing Sharp"             ,"Novella","Thriller","Planned","None"   ,     0,     0,"");
    add ("Hood","Hoody"                    ,"Short"  ,"Thriller","Dormant","Submit" ,  1869,  1869,"");
    add ("Amb" ,"Ambush"                   ,"Short"  ,"Children","Dormant","Draft 1",   334,   334,"Shorts/ambush");
    add ("OtC" ,"On the Clock"             ,"Short"  ,""        ,"Dormant","Draft 1",   807,   807,"Shorts/on_the_clock");
    add ("TPtN","The Path to Nowhere"      ,"Short"  ,"SF"      ,"Dormant","Draft 1",  1617,  1617,"Shorts/path_to_nowhere");
    add ("PJ"  ,"Practical Joke"           ,"Short"  ,"SF"      ,"Dormant","Draft 1",  3058,  3058,"Shorts/practical_joke");
    add ("SP"  ,"Street Prejudice"         ,"Short"  ,"SF"      ,"Dormant","Draft 1",  3463,  3463,"Shorts/street_prejudice");
    add ("UfS" ,"Unlucky for Some"         ,"Short"  ,"Crime"   ,"Dormant","Draft 1",   824,   824,"Shorts/unlucky");
    add ("AoD" ,"Articles of Desire"       ,"Poem"   ,""        ,"Complete",""      ,   134,   134,"Poems/Articles");
    add ("Crus","Cruising"                 ,"Poem"   ,""        ,"Complete",""      ,   490,   490,"Poems/Cruising");
    add ("Help","I'm Here To Help You"     ,"Poem"   ,""        ,"Complete",""      ,   240,   240,"Poems/Help");
    add ("NMfW","Not Made From Wheat"      ,"Poem"   ,""        ,"Complete",""      ,     1,     1,"Poems/notwheat");
    add ("Torm","Tormato"                  ,"Poem"   ,""        ,"Complete",""      ,   100,   100,"Poems/Tormato");

    // Co-author's link
    sJason =  "<a title=\"See Jason's facebook profile...\" ";
    sJason += "href=\"http://www.facebook.com/profile.php?id=691450533\">Jason Harris</a>";

    // Add descriptions for novels
    sDesc =  "Life in a small town is shattered when a crazed bomber ";
    sDesc += "destroys life and buildings. The hunt is on...";
    desc ("BotR",sDesc);
    sDesc =  "A full length novel based on ";
    sDesc += "<a href=\"path_to_nowhere.htm\">The Path to Nowhere</a>.";
    desc ("TBAF",sDesc);
    desc ("Dril","Working title.");
    sDesc =  "A story inspired by the NEXUS environment. Co-authoring with ";
    sDesc += sJason + ".";
    desc ("Ins" ,sDesc);

    // Add descriptions for serials
    desc ("Ast" ,"Episodic blog story.");
    desc ("KaM" ,"A weekly prompt-driven crime story.");

    // Add descriptions for novellas
    sDesc =  "Kerry Mason is having problems with her memory. ";
    sDesc += "She keeps waking up in strange places, not knowing how she got there...";
    desc ("FiDM",sDesc);
    desc ("TWOH","What is Lark Island's ancient secret? Only the Wild One knows.");

    // Add descriptions for shorts
    desc ("Amb" ,"A brief war story on Cloud Line.");
    desc ("Hood","A micro-thriller set on a London commuter train.");
    desc ("OtC" ,"One of Jo's Weekly Workout exercises (week eight).");
    sDesc =  "Some kids from a school drama class are out hiking for the ";
    sDesc += "weekend with their teacher. On their way home they take a ";
    sDesc += "wrong turning that leads them to a dead end - an old disused ";
    sDesc += "quarry. Here they observe a strange St Elmo's fire in the rocks.<br /><br />";
    sDesc += "This 'teaser' is actually part of a larger story I'm working on. ";
    sDesc += "See <a href=\"bridge_af.htm\">The Bridge Across Forever</a>.";
    desc ("TPtN",sDesc);
    desc ("PJ"  ,"A story about a toy duck.");
    sDesc =  "A high street adventure. Co-authored with ";
    sDesc += sJason + ".";
    desc ("SP"  ,sDesc);
    desc ("UfS" ,"A Valentine's day special.");

    // Add descriptions for poems
    desc ("AoD", "A somewhat cryptic poem.");
    desc ("Crus","A short poem about a bird. Or is it?");
    desc ("Help","Can you escape the 'system'?");
    desc ("NMfW","...but made from something else entirely...");
    desc ("Torm","Tempting but tormenting.");

    // Apply filter
    for (iP = 0; iP < iCount; iP++)
    {
        oProj = aoFilt[iP];
        if ((psFilter === "") || (psFilter.indexOf (oProj.sClass) >= 0))
        {
            aoProj[iCountProj] = oProj; iCountProj++;
        }
    }

    // List the projects
    listProjects ();

    //////////////////////////////////////////////////////
    // Priv: add
    // Desc: Add a new project to the array.
    // Args: psCode   -- Abbreviation code for project.
    //       psTitle  -- Title of project.
    //       psClass  -- Class of project.
    //       psGenre  -- Genre of project.
    //       psStatus -- Project's status.
    //       psPhase  -- Project's phase.
    //       iWords   -- Current word count.
    //       iMax     -- Expected maximum word count.
    //       psFile   -- File name (for viewing/download).
    //////////////////////////////////////////////////////

    function add (psCode, psTitle, psClass, psGenre, psStatus, psPhase, iWords, iMax, psFile)
    {
        var oProj;

        // Create new object and add it to the array
        oProj = new project (psCode, psTitle, psClass, psGenre, psStatus, psPhase, iWords, iMax, psFile);
        aoFilt[iCount] = oProj; iCount++;

        // Constructor
        function project (psA, psT, psC, psG, psS, psP, iW, iM, psF)
        {
            this.sCode   = psA;
            this.sTitle  = psT;
            this.sClass  = psC;
            this.sGenre  = psG;
            this.sStatus = psS;
            this.sPhase  = psP;
            this.iWords  = iW;
            this.iMax    = iM;
            this.sDesc   = "No description available.";
            this.sFile   = psF;
        }
        // End of project (constructor)
    }
    // End of add

    ////////////////////////////////////////
    // Priv: desc
    // Desc: Add a description to a project.
    // Args: psCode -- Project code.
    //       psDesc -- Description.
    ////////////////////////////////////////

    function desc (psCode, psDesc)
    {
        // Locate the project
        for (iP = 0; iP < iCount; iP++)
        {
            oProj = aoFilt[iP];
            if (oProj.sCode === psCode)
            {
                oProj.sDesc = psDesc;
                break;
            }
        }
    }
    // End of desc

    //////////////////////////////////////
    // Func: listProjects
    // Desc: Display all writing projects.
    //////////////////////////////////////

    function listProjects ()
    {
        var oE, oTable, oCell, oTitle;

        // Fill in count
        oE = document.getElementById ("CountProj");
        if (oE !== null)
        {
            oE.innerHTML = iCountProj;
        }
        oE = document.getElementById ("Projects");
        if (oE !== null)
        {
            // Create table
            oTable = cbTable ("Title","Class","Genre","Status","Phase","Words","Expected");
            oCell = oTable.cell ("th", "Progress");
            oCell.colSpan = 2;
            oTable.cell ("th", "Description");

            // Iterate through projects
            for (iP = 0; iP < iCountProj; iP++)
            {
                oProj = aoProj[iP];
                if (oProj.sFile === "")
                {
                    oTitle = oProj.sTitle;
                }
                else
                {
                    oTitle = cbLink (oProj.sTitle, oProj.sFile);
                    if (oProj.sFile.indexOf ("_nav") > 0)
                    {
                        oTitle.target = "CbNav";
                    }
                }
                oTable.row (oTitle);
                oTable.cell (oProj.sClass);
                oTable.cell (oProj.sGenre);
                oTable.cell (oProj.sStatus);
                oTable.cell (oProj.sPhase);
                oTable.meter (oProj.iWords, oProj.iMax);
                oCell = oTable.cell ();
                oCell.innerHTML = oProj.sDesc;
            }

            // Place table on the page
            oE.appendChild (oTable);

            // Remove message
            cbErrMsg (null);
        }
    }
    // End of listProjects

    // Return results
    this.iCountProj = iCountProj;
    this.aoProj     = aoProj;
    return (this);
}
// End of cbProjects

////////////////////////////////////////////////////////////
// Func: cbSubDocsLoad
// Desc: Look for sub-documents and load them into the page.
////////////////////////////////////////////////////////////

function cbSubDocsLoad ()
{
    var sSubId = "";

    try
    {
        subDocsLoad ();
    }
    catch (e)
    {
        cbErrMsg ("Failed to load sub-document!", e, null, sSubId);
    }

    /////////////////////////////////////////
    // Priv: subDocsLoad
    // Desc: Scan for and load sub-documents.
    /////////////////////////////////////////

    function subDocsLoad ()
    {
        var i, oSub, aoLink, oLink, oFrame, oText;

        // Find all sub-document hyperlinks
        aoLink = document.getElementsByTagName ("a");
        for (i = 0; i < aoLink.length; i++)
        {
            // Filter out all but the class we're interested in
            oLink = aoLink[i];
            if (oLink.className.indexOf ("subdoc") >= 0)
            {
                // Is there a section for the subdocument?
                oSub = oLink.parentNode;
                if ((oSub.nodeName !== "DIV") || (oSub.className.indexOf ("subdoc") < 0))
                {
                    var oPage = oSub;
                    oSub = cbTag ("div");
                    oSub.className = gsCbBgClass + "subdoc";
                    oPage.insertBefore (oSub, oLink);
                }

                // Set up identifier for messages
                sSubId = oSub.id;
                if (sSubId === "")
                {
                    sSubId = oSub.id = "CbBgSubDoc_" + i;
                }

                // Create in-line frame
                oFrame = cbTag ("iframe", "frameborder=0", "scrolling=auto");
                oFrame.className = gsCbBgClass + "subdoc";
                oFrame.src = oLink.href;

                // Put instructions inside, in case i-frames's are not supported
                oText = document.createTextNode ("Your browser does not support in-line frames, or has this feature disabled. Please ");
                //frameInner (oFrame, oText);
                frameInner (oFrame, oLink);
                oText = document.createTextNode (" to read the document.");
                //frameInner (oFrame, oText);

                // Insert frame in section
                oSub.innerHTML = "";
                oSub.appendChild (oFrame);

                // Reset message identifier
                sSubId = "";
            }
        }

        /////////////////////////////////////////////////
        // Priv: frameInner
        // Desc: Append to the inner contents of a frame.
        // Args: oIFrame -- I-frame to change.
        //       oInner  -- Inner content to append.
        /////////////////////////////////////////////////

        function frameInner (oIFrame, oInner)
        {
            try
            {
                oIFrame.appendChild (oInner);
            }
            catch (eF)
            {
                oInner.innerHTML = "";
            }
        }
        // End of frameInner
    }
    // End of subDocsLoad
}
// End of cbSubDocsLoad

///////////////////////////////////////////
// Func: cbIFrameResize
// Desc: Adjust the size of in-line frames.
// Args: oEvent -- Event details.
///////////////////////////////////////////

function cbIFrameResize (oEvent)
{
    try
    {
        resize ();
    }
    catch (e)
    {
        // Benign error - Comment out next line unless debugging
        //cbErrMsg ("Failed to resize sub-document!", e);
    }

    //////////////////////////////////////////
    // Priv: resize
    // Desc: Resize all sub-document i-frames.
    //////////////////////////////////////////

    function resize ()
    {
        var i, oIF, aoIF, iHeight;

        // Get required height
        iHeight = document.documentElement.clientHeight;
        if ((iHeight == undefined) || (iHeight <= 0))
        {
            iHeight = document.body.clientHeight;
        }
        if ((iHeight == undefined) || (iHeight <= 0))
        {
            iHeight = window.innerHeight;
        }
        if ((iHeight == undefined) || (iHeight <= 0))
        {
            throw ("Window height could not be determined!");
        }

        // Make height 90 percent of window height
        iHeight = Math.floor (iHeight * 0.9);

        // Iterate through list of all i-frames
        aoIF = document.getElementsByTagName ("iframe");
        for (i = 0; i < aoIF.length; i++)
        {
            // Sift out all but the one's we're interested in
            oIF = aoIF[i];
            if (oIF.className.indexOf ("subdoc") >= 0)
            {
                // Adjust the size
                oIF.style.height = iHeight + "px";
            }
        }
    }
    // End of resize
}
// End of cbIFrameSize
/* End of file cbbg.js */
