﻿// XML to JavaScrpt ListView

var msnevs_XmlListViewErrors = 
{
	"initError":        "Could not create list view.",
	"settingErr":       "List view is not correctly set up.",
	"noData":           "Data not found.",
	"createTableErr":   "Could not create table object.",
	"findElementErr":   "Could not find element.",
	"xmlErr":           "Failed in retrieving the XML data."
}

msnevs_XmlListViewColumn = 
    function()
    {
        this.title = null;
        this.xPath = null;
        this.linkXPath = null;
        this.dataType = "string";
        this.formatString = null;
        this.headerType = null;     // text / default / custImage
        this.imgUrl = null;         // if text, ignore;  if default, it is a path; if else, it is the image without sorting
        this.imgUrlAsc = null;      // image url for ascending sorting 
        this.imgUrlDesc = null;     // image url for descending sorting 
        this.width = null;          // width of the column
        this.cssTHCell = null;      // css for Header tag (= cssPrefix + _THCell)
        this.cssTHCellText = null;  // css for the inner tag of Header cell (= cssPrefix + _THCellText)
        this.cssTDCell = null;      // css for TD tag (items) (= cssPrefix + _TDCell)
        this.cssTDCellText = null;  // css for the inner tag of TD cell (= cssPrefix + _TDCellText)
        this.tagTDCellText = null;  // if null, use span; 
        this.tabIndex = null;       // reserved
    }

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PROPERTIES
//  id: unique id for the table element
//  holder: contain of the table
//  xmlUrl: URL of xml feed
//  xmlFilterXPath: XPath to filter items
//  imageUrl: URL header of images (used to render table header)
//  width: width of the table object
//  cssPrefix: css class prefix, if define the css class seperately, set it as null
//  cssTable: css class of table (= cssPrefix + _Table)
//  cssTHeader: css class of table headers (= cssPrefix + _THeader)
//  cssTRow: css class of the table rows (= cssPrefix + _TRow)
//  cssTRowAlt: css class of the alternating rows (= cssPrefix + _TRowAlt)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

msnevs_XmlListView = 
    function(id, holder)
    {
        if (!id || !holder)
            throw new Error(msnevs_XmlListViewErrors.settingErr);
        
        var self = null;
        
        this.id = id;
        if (typeof holder == "string")
            this.holder = document.getElementById(holder);
        else
            this.holder = holder;
            
        this.columns = new Array();
        this.xmlUrl = null;
        this.xmlFilterXPath = null;
        this.imageUrl = null;
        this.width = null;
        this.maxRows = -1;
        this.cssPrefix = null;
        this.cssTable = null;
        this.cssTHeader = null;
        this.cssTRow = null;
        this.cssTRowAlt = null;
    }

msnevs_XmlListView.prototype.BindingXml =
    function(xmlUrl, xmlFilterXPath)
    {
        if (!xmlUrl)
            throw new Error(msnevs_XmlListViewErrors.noData);
        else
            this.xmlUrl = xmlUrl;
        
        if (!xmlFilterXPath)
            throw new Error(msnevs_XmlListViewErrors.settingErr);    
        this.xmlFilterXPath = xmlFilterXPath;
            
        var xmlDoc = msnevs_LoadXmlByHttpRequest(xmlUrl);
        this.Draw(xmlDoc);
    }

msnevs_XmlListView.prototype.BindingXmlString =
    function(xmlString, xmlFilterXPath)
    {
        if (!xmlString || typeof(xmlString) != "string")
            throw new Error(msnevs_XmlListViewErrors.noData);

        var parser = msnevs_GetDOMParser();
        if (parser) {
            var xmlDoc = parser.parseFromString(xmlString, "text/xml");
            if (xmlDoc) {
                if (!xmlFilterXPath)
                    throw new Error(msnevs_XmlListViewErrors.settingErr);    
                this.xmlFilterXPath = xmlFilterXPath;
                this.Draw(xmlDoc);
                return;
            }
        }
        throw new Error(msnevs_XmlListViewErrors.noData);
    }

msnevs_XmlListView.prototype.Draw =
    function(xmlDoc)
    {
        if (!this.id || !this.holder || !xmlDoc || !this.xmlFilterXPath)
            throw new Error(msnevs_XmlListViewErrors.settingErr);
        
        var items = msnevs_selectNodes(xmlDoc.documentElement, this.xmlFilterXPath);
        
        if (!items) 
            throw new Error(msnevs_XmlListViewErrors.noData);
        
        this.holder.innerHTML = "";
        
        var table = document.createElement("table");
        if (!table) 
            throw new Error(msnevs_XmlListViewErrors.createTableErr);
        
        table.id = this.id;
            
        if (this.cssTable)
            table.className = this.cssTable;
        else if (this.cssPrefix)
            table.className = this.cssPrefix + "_Table";

        if (this.cssTRow)
            table.cssTRow = this.cssTRow;
        else if (this.cssPrefix)
            table.cssTRow = this.Prefix + "_TRow";
        
        if (this.cssTRowAlt)
            table.cssTRowAlt = this.cssTRowAlt;
        else if (this.cssPrefix)
            table.cssTRowAlt = this.Prefix + "_TRowAlt";

        this.holder.appendChild(table);
        
        var thead = document.createElement("thead");
        if (!thead) 
            throw new Error(msnevs_XmlListViewErrors.createTableErr);
        table.appendChild(thead);
        this.DrawTableHeaders(items, thead);

        var tbody = document.createElement("tbody");
        if (!tbody) 
            throw new Error(msnevs_XmlListViewErrors.createTableErr);
        table.appendChild(tbody);
        
        this.DrawTableRows(items, tbody);
    }

msnevs_XmlListView.prototype.DrawTableHeaders =
    function(items, tbody)
    {
        if (!this.columns || this.columns.length < 1)
            throw new Error(msnevs_XmlListViewErrors.settingErr);
            
        var tr = document.createElement("tr");
        if (!tr) 
            throw new Error(msnevs_XmlListViewErrors.createTableErr);

        if (this.cssTHeader)
            tr.className = this.cssTHeader;
        else if (this.cssPrefix)
            tr.className = this.cssPrefix + "_THeader";

        tbody.appendChild(tr);        
            
        for (var i = 0; i < this.columns.length; i++)
        {
            var th = document.createElement("th");
            if (!th) 
                throw new Error(msnevs_XmlListViewErrors.createTableErr);
            tr.appendChild(th);
            
            var col = this.columns[i];

            if (col.title)
                th.title = col.title;

            if (col.cssTHCell)
                th.className = col.cssTHCell;
            else if (this.cssPrefix)
                th.className = this.cssPrefix + "_THCell";

            if (col.width != null && col.width > 0)
                th.width = col.width;

            if (!col.dataType)
                col.dataType = "string";

            if (col.cssTDCellText)
                th.cssTDCellText = col.cssTDCellText;
            else if (this.cssPrefix)
                th.cssTDCellText = this.cssPrefix + "_TDCellText";
            th.dataType = col.dataType;
            th.sortDir = "none";
            th.headerType = col.headerType;
            if (!col.tagTDCellText)
                col.tagTDCellText = "span";
            th.tagTDCellText = col.tagTDCellText;
                
            var htmlStr;
            if (!col.headerType || col.headerType == "text")
            {
                htmlStr = "<span ";
                if (col.cssTHCellText)
                {
                    htmlStr += "class='" + col.cssTHCellText + "' ";
                    th.cssTHCellText = col.cssTHCellText;
                }
                else if (this.cssPrefix)
                {
                    htmlStr += "class='" + this.cssPrefix + "THCellText" + "' ";
                    th.cssTHCellText = this.cssPrefix + "THCellText";
                }
                htmlStr += "style='cursor:pointer;' ";
                var onclickStr = "msnevs_SortXmlListView(\"" + this.id + "\"," +
                                                        "\"" + i.toString() + "\")";
                htmlStr += ("onclick='" + onclickStr + "'>");
                htmlStr += col.title;
                htmlStr += "</span>";
                th.innerHTML = htmlStr;
            }
            else
            {
                if (col.headerType == "custImage")
                {
                    th.imgUrl = col.imgUrl;
                    th.imgUrlAsc = col.imgUrlAsc;
                    th.imgUrlDesc = col.imgUrlDesc;
                }
                else if (col.headerType == "default" && this.imageUrl)
                {
                    var pos = this.imageUrl.lastIndexOf("/");
                    if (pos < 0)
                        pos = this.imageUrl.lastIndexOf("\\");
                    if (pos != this.imageUrl.length - 1)
                        this.imageUrl += "/";
                    th.imgUrl = this.imageUrl + this.id + "_" + i.toString() + ".gif";
                    th.imgUrlAsc = this.imageUrl + this.id + "_" + i.toString() + "asc.gif";
                    th.imgUrlDesc = this.imageUrl + this.id + "_" + i.toString() + "desc.gif"; 
                }
                htmlStr = "<img ";
                if (col.cssTHCellText)
                {
                    htmlStr += "class='" + col.cssTHCellText + "' ";
                    th.cssTHCellText = col.cssTHCellText;
                }
                else if (this.cssPrefix)
                {
                    htmlStr += "class='" + this.cssPrefix + "THCellText" + "' ";
                    th.cssTHCellText = this.cssPrefix + "THCellText";
                }
                htmlStr += "src='" + th.imgUrl + "' ";
                htmlStr += "style='cursor:pointer;' ";
                var onclickStr = "msnevs_SortXmlListView(\"" + this.id + "\"," +
                                                        "\"" + i.toString() + "\")";
                htmlStr += "onclick='" + onclickStr + "'>";
                htmlStr += "</img>";
                th.innerHTML = htmlStr;
            }
        }        
    }
    
msnevs_XmlListView.prototype.DrawTableRows =
    function(items, tbody)
    {
        if (items.length < 1)
            return;
        
        for (var i = 0; i < items.length; i++)
        {
            if (this.maxRows && this.maxRows != -1 && this.maxRows <= i)
                break;
                 
            var tr = document.createElement("tr");
            if (i % 2 == 0)
            {
                if (this.cssTRow)
                    tr.className = this.cssTRow;
            }
            else
            {
                if (this.cssTRowAlt)
                    tr.className = this.cssTRowAlt;
                else
                {
                    if (this.cssTRow)
                    tr.className = this.cssTRow;
                }
            }
            tbody.appendChild(tr);
            
            for (var j = 0; j < this.columns.length; j++)
            {
                var col = this.columns[j];
                var valueStr = msnevs_getXmlNodeValue(msnevs_selectSingleNode(items[i], col.xPath));
                var linkStr;
                if (col.linkXPath)
                    linkStr = msnevs_getXmlNodeValue(msnevs_selectSingleNode(items[i], col.linkXPath));
                else
                    linkStr = null;
                var value = msnevs_convert(valueStr, col.dataType);
                var displayStr;
                if (col.formatString)
                    displayStr = msnevs_format(value, col.dataType, col.formatString);
                else
                    displayStr = msnevs_format(value, col.dataType);
                
                var td = document.createElement("td");
                if (col.width)
                    td.width = col.width;
                if (col.cssTDCell)
                    td.className = col.cssTDCell;
                else if (this.cssPrefix)
                    td.className = this.cssPrefix + "_TDCell";
                
                var htmlStr;
                htmlStr = "<" + col.tagTDCellText + " ";
                if (col.cssTDCellText)
                    htmlStr += "class='" + col.cssTDCellText + "' ";
                else if (this.cssPrefix)
                    htmlStr += "class='" + this.cssPrefix + "TDCellText" + "' ";
                if (linkStr)
                {
                    htmlStr += "style='cursor:pointer;' ";
                    htmlStr += "onmouseout='this.style.textDecoration=\"none\"' ";
                    htmlStr += "onmouseover='this.style.textDecoration=\"underline\"' ";
                    htmlStr += "onclick='parent.location.href = \"" + linkStr + "\"' ";
                }
                htmlStr += ">" + displayStr + "</" + col.tagTDCellText + ">";
                td.innerHTML = htmlStr;
                tr.appendChild(td);
            }
        }    
    }
    
msnevs_SortXmlListView =
    function(id, iCol)
    {
        var table = document.getElementById(id);
        if (!table)
            throw new Error(msnevs_XmlListViewErrors.findElementErr);
        var cssTRow = msnevs_getDynamicalAttribute(table, "cssTRow");
        var cssTRowAlt = msnevs_getDynamicalAttribute(table, "cssTRowAlt");        

        var THs;
        if (table.tHead)
            THs = table.tHead.rows[0];
        else
            THs = table.rows[0];
        
        _getThAttribute = function(i, attributeName, defaultValue) {
            var value = msnevs_getDynamicalAttribute(THs.cells[i], attributeName);
            if (value)
                return value;
            else {
                if (defaultValue == null)
                    return defaultValue; 
                else
                    return null;
            }
        }

        var columns = new Array();
        for (var i = 0; i < THs.cells.length; i++)
        {
            var col = new Object();
            col.title = _getThAttribute(i, "title", null);
            col.dataType = _getThAttribute(i, "dataType", "string");
            col.sortDir = _getThAttribute(i, "sortDir", "none");
            if (i == iCol) {
                if (col.sortDir == "asc") {
                    col.sortDir = "desc";
                    col.imgUrlDesc = _getThAttribute(i, "imgUrlDesc", null);
                }
                else {
                    col.sortDir = "asc";
                    col.imgUrlAsc = _getThAttribute(i, "imgUrlAsc", null);
                }
            }
            else {
                col.sortDir = "none";
                col.imgUrl = _getThAttribute(i, "imgUrl", null);
            }
            THs.cells[i].sortDir = col.sortDir;
            col.headerType = _getThAttribute(i, "headerType", "default");
            col.tagTDCellText = _getThAttribute(i, "tagTDCellText", "span");
            col.cssTHCellText = _getThAttribute(i, "cssTHCellText", null);
            col.cssTDCellText = _getThAttribute(i, "cssTDCellText", null);
            columns.push(col);
            
            var htmlStr;
            if (!col.headerType || col.headerType == "text") {
                htmlStr = "<span ";
                if (col.cssTHCellText)
                    htmlStr += "class='" + cssTHCellText + "' ";
                htmlStr += "style='cursor:pointer;' ";
                var onclickStr = "msnevs_SortXmlListView(\"" + id + "\"," +
                                                        "\"" + i.toString() + "\")";
                htmlStr += ("onclick='" + onclickStr + "'>");
                htmlStr += col.title;
                htmlStr += "</span>";
                THs.cells[i].innerHTML = htmlStr;
            }
            else {
                htmlStr = "<img ";
                if (col.cssTHCellText)
                    htmlStr += "class='" + cssTHCellText + "' ";
                switch(col.sortDir)
                {
                    case "asc":
                        htmlStr += "src='" + THs.cells[i].imgUrlAsc + "' "; 
                        break;
                    case "desc":
                        htmlStr += "src='" + THs.cells[i].imgUrlDesc + "' "; 
                        break;
                    default:
                        htmlStr += "src='" + THs.cells[i].imgUrl + "' "; 
                }                    
                htmlStr += "style='cursor:pointer;' ";
                var onclickStr = "msnevs_SortXmlListView(\"" + id + "\"," +
                                                        "\"" + i.toString() + "\")";
                htmlStr += "onclick='" + onclickStr + "'>";
                htmlStr += "</img>";
                THs.cells[i].innerHTML = htmlStr;
            }
        }

        if (table.tBodies[0].rows.length >= 2)
        {
            var TRs = new Array();
            for (var i = 0; i < table.tBodies[0].rows.length; i++) {
                TRs[i] = table.tBodies[0].rows[i];
            }
            
            _getDataFromTR = function(tr) {
                var val = null;
                if (!columns[iCol].tagTDCellText) {
                    val = msnevs_convert(msnevs_getXmlNodeValue(tr.cells[iCol]));
                }
                else {
                    var dataElements = tr.cells[iCol].getElementsByTagName(columns[iCol].tagTDCellText);
                    if (dataElements.length > 0)
                        val = msnevs_getXmlNodeValue(dataElements[0]);
                }
                return val;
            }
            
            _compareTRs = function(tr1, tr2) {
                var v1 = _getDataFromTR(tr1);
                var v2 = _getDataFromTR(tr2);
                if (v1 == v2)
                    return 0;
                else
                    return ((v1 > v2)? 1 : -1);
            }
            
            _decompareTRs = function(tr1, tr2) {
                return (0 - _compareTRs(tr1, tr2));
            }    
            
            if (columns[iCol].sortDir == "asc")
                TRs.sort(_compareTRs);
            else
                TRs.sort(_decompareTRs);
            
            if (cssTRow || cssTRowAlt) {
                for (var i = 0; i < TRs.length; i++) {
                    if (i % 2 == 0) {
                        if (cssTRow)
                            TRs[i].className = cssTRow;
                    }
                    else {
                        if (cssTRowAlt)
                            TRs[i].className = cssTRowAlt;
                        else if (cssTRow)
                            TRs[i].className = cssTRow;
                    }
                }
            }
        }
        else {
            return;
        } // end of sort TRs
        
        var fragHead = document.createDocumentFragment();
        var fragBody = document.createDocumentFragment();
        fragHead.appendChild(THs);
        for (var i = 0; i < TRs.length; i++) {
            fragBody.appendChild(TRs[i]);
        }
        
        try
        {
            table.innerHTML = "";
            var thead = table.createTHead();
            thead.appendChild(fragHead);
            table.appendChild(thead);
            var tbody = document.createElement("tbody");
            tbody.appendChild(fragBody);
            table.appendChild(tbody);
        }
        catch(e)
        {
            table.tHead.appendChild(fragHead);
            table.tBodies[0].appendChild(fragBody);
        }
    }