﻿function __isIE() {
    return (navigator.userAgent.indexOf("compatible") > -1) && (navigator.userAgent.indexOf("MSIE") > -1); 
}

function __isSafari() {
    return navigator.userAgent.indexOf("AppleWebKit") > -1;
}

//////////////////////////////////////////////////////////////////////
//  type:       can be text, image, date, UTCDate
//  data:       data field, assign it to null for XPath
//  dataXpath:  data XPath, if data != null, it will be igored
//  dataFunc:   fix function
//  format:     format string to format data, only date type is supported in this version
//  css:        css for the Item cell
//  cssImgBox:  css for the box hosted the image 
//  link:       link URL, assign it to null for XPath
//  linkXpath:  link XPath, if link != null, it will be igored
//  linkFunc:   fix function
//  linkOpen:   new = open in a new window, not support in all browsers
//  tip:        tip field, assign it to null for XPath
//  tipXpath:   tip XPath, if tip != null, it iwll be igored
//  tipFunc:    fix function
//  xHelp:      XmlHelper instance
//  gIdx:       group index, start from 0. default has 1 group with index = 0;
//////////////////////////////////////////////////////////////////////

XmlListCell = function() {
    this.type = null;
    this.data = null;
    this.dataType = "string";
    this.dataXpath = null;
    this.dataFunc = null;
    this.format = null;
    this.css = null;
    this.cssImgBox = null;
    this.link = null;
    this.linkXpath = null;
    this.linkFunc = null;
    this.linkOpen = null;
    this.tip = null;
    this.tipXpath = null;
    this.tipFunc = null;
    this.xHelp = null;
    this.gIdx = 0;
}

XmlListCell.prototype = {
    checkData: function(field, data, xpath, index) {
        if (data) return data;
        if (!field || !xpath) return "";
        if (!this.xHelp) this.xHelp = new XmlHelper();
        if (!this.xHelp) return "";
        if (this.dataType.toLowerCase() != "json") {
            if (!index || index < 0)
                return this.xHelp.getXmlNodeValue(this.xHelp.selectSingleNode(field, xpath));
            else
                return this.xHelp.getXmlNodeValue(this.xHelp.selectNodes(field, xpath)[index]);
        }
        else {
            if (!index || index < 0)
                return this.xHelp.getJsonField(field, xpath);
            else
                return this.xHelp.getJsonField(field, xpath, index);
        }        
    },
    
    getHtml: function(field) {
        if (!field) return;
        
        var vData = this.checkData(field, this.data, this.dataXpath);
        if (this.dataFunc) vData = this.dataFunc(vData);
        var vLink = this.checkData(field, this.link, this.linkXpath);
        if (this.linkFunc) vLink = this.linkFunc(vLink);
        var vLinkOpen = this.linkOpen;
        var vTip = this.checkData(field, this.tip, this.tipXpath);
        if (this.tipFunc) vTip = this.tipFunc(vTip);
        var exp = /"/g;
        if (__isSafari())
            if (vTip != null) vTip = vTip.replace(exp, "&#39;");
        else
            if (vTip != null) vTip = vTip.replace(exp, "&quot;");
        
        _getText = function(vCss) {
            var str = "<div";
            if (vCss) str += " class='" + vCss + "'";
            if (vTip) str += " title=\"" + vTip + "\"";
            if (vLink) str += " style='cursor:pointer;'" + 
                              " onmouseout=\"this.style.textDecoration='none'\" " +
                              " onmouseover=\"this.style.textDecoration='underline'\" ";
            if (vLinkOpen && vLinkOpen.toLowerCase() == "new")
                str += "onclick=\"window.open('" + vLink + "')\"";
            else
                str += " onclick=\"parent.location.href='" + vLink + "'\"";
            if (vData) str += ">" + vData + "</div>";
            return str;            
        }
        
        _getImage = function(vCss, vCssImgBox) {
            var str = "<div";
            if (vCssImgBox) str +=" class='" + vCssImgBox + "'";
            str += ">";
            str += "<img";
            if (vCss) str += " class='" + vCss + "'";
            if (vData) str += " src='" + vData + "'";
            if (vTip) str += " title=\"" + vTip + "\"";
            if (vLink) str += " style='cursor:pointer;'";
            if (vLinkOpen && vLinkOpen.toLowerCase() == "new")
                str += "onclick=\"window.open('" + vLink + "')\"";
            else
                str += " onclick=\"parent.location.href='" + vLink + "'\"";
            str += " /></div>";
            return str;            
        }
        
        switch(this.type.toLowerCase()) {
            case "text": 
                return _getText(this.css);
            case "date":
                if (this.format) { 
                    var fHelp = new FormatHelper();
                    if (fHelp) vData = fHelp.format(fHelp.convert(vData, "date"), "date", this.format);
                    fHelp = null;
                }
                return _getText(this.css);
            case "utcdate":
                if (this.format) { 
                    var fHelp = new FormatHelper();
                    if (fHelp) vData = fHelp.format(fHelp.convert(vData, "utcdate"), "date", this.format);
                    fHelp = null;
                }
                return _getText(this.css);
            case "image": 
                return _getImage(this.css, this.cssImgBox);
            default:
                return "";
        }
    }
}


//////////////////////////////////////////////////////////////////////
//  data:       data field, assign it to null for XPath
//  dataNS:     data XML namespace
//  dataXpath:  data XPath, if data != null, it will be igored
//  filterXpath:filer XPath, for getting the field data
//  filterFunc: customized function to filter the data, return false = ignored
//  sortXpath:  data XPath for sorting
//  sortFunc:   fix function
//  sortArray:  data array for sorting, only the items with sort field data appears in this array can be displayed
//  sortDirect: can be "asc" or "desc", default is "asc". Will be igored when sortArray != null;
//  cells:      array of XmlListCell objects
//  css:        css for the Item
//  cssHover:   css for when mouse move in
//  cssGroups:  css for groups to group cells
//  displays:   array of indexes, for displayed items, overflow index will ignored
//  xHelp:      XmlHelper instance, for processing XML
//  gCount:     total group count
//////////////////////////////////////////////////////////////////////

XmlListItem = function() {
    this.data = null;
    this.dataNS = null;
    this.dataXpath = null;
    this.filterXpath = null;
    this.filterFunc = null;
    this.sortXpath = null;
    this.sortFunc = null;
    this.sortDirect = "asc";
    this.sortArray = null;
    this.cells = new Array();
    this.css = null;
    this.cssHover = null;
    this.cssGroups = new Array();
    this.displays = null;
    this.xHelp = null;
    this.gCount = 0;
}

XmlListItem.prototype = {
    getField: function(type, data, xpath, xHelp) {
        if (!xHelp) {
            if (!this.xHelp)
                xHelp = new XmlHelper();
            else
                xHelp = this.xHelp;
        }
        return (type.toLowerCase() == "json") ?
            xHelp.getJsonField(data, xpath) : xHelp.selectNodes(data, xpath);
    },
    
    fixSortDirect: function() {
        if (this.sortDirect && this.sortDirect.toLowerCase() == "desc")
            this.sortDirect = "desc";
        else
            this.sortDirect = "asc";    
    },

    getData: function(data, dataType) {
        var vNodes;
        if (this.data)
            vNodes = this.data;
        else {
            if (!data || !this.dataXpath) return null;
            if (!this.xHelp) this.xHelp = new XmlHelper();
            if (dataType.toLowerCase() == "json")
                vNodes = this.xHelp.getJsonField(data, this.dataXpath);
            else
                vNodes = this.xHelp.selectNodes(data, this.dataXpath, this.dataNS);
        }
        if (vNodes && vNodes.length > 0 && this.filterXpath && this.filterFunc) {
            var vNodes2 = new Array();
            for (var i = 0; i < vNodes.length; i++) {
                var vFilter = (dataType.toLowerCase() == "json") ?
                    this.xHelp.getJsonField(vNodes[i], this.filterXpath) : this.xHelp.selectNodes(vNodes[i], this.filterXpath);
                if (this.filterFunc(vFilter)) vNodes2.push(vNodes[i]);
            }
            return vNodes2;
        }
        else
            return vNodes;
    },
    
    sortData: function(vNodes, dataType) {
        if (!vNodes || vNodes.length <= 0 || !this.sortXpath) {
            this.displays = null;
            return;
        }
        
        if (this.sortArray && this.sortArray.length && this.sortArray.length > 0) {
            this.displays = new Array();
            for (var j = 0; j < this.sortArray.length; j++) {
                for (var i = 0; i < vNodes.length; i++) {
                    var val = (dataType.toLowerCase() == "json") ?
                        this.xHelp.getJsonField(vNodes[i], this.sortXpath) : this.xHelp.selectNodes(vNodes[i], this.sortXpath);
                    if (val.toLowerCase() == this.sortArray[j].toLowerCase()) {
                        this.displays.push(i);
                        break;
                    }
                }
            }
        }
        else {
            this.fixSortDirect();
            this.displays = new Array();
            for (var i = 0; i < vNodes.length; i++) this.displays.push(i);
            var _sortXpath = this.sortXpath;
            var _sortFunc = this.sortFunc;
            var _getSortField = this.getField;
            var _xHelp = this.xHelp;
            
            _compare = function(i1, i2) {
                var v1 = _getSortField(dataType, vNodes[i1], _sortXpath, _xHelp);
                var v2 = _getSortField(dataType, vNodes[i2], _sortXpath, _xHelp);
                if (_sortFunc) {
                    v1 = _sortFunc(v1);
                    v2 = _sortFunc(v2);
                }
                if (v1 == v2) 
                    return 0;
                else
                    return ((v1 > v2)? 1 : -1);
            }
            
            _decompare = function(i1, i2) { return 0 - _compare(i1, i2); }
    
            if (this.sortDirect != "desc")
                this.displays.sort(_compare);
            else
                this.displays.sort(_decompare);
        }
    },
    
    writeHtml: function(holder, xmlNode, start, length) {
        if (!holder) return;
        var fHelp = new FormatHelper();
        var iStart = (start && start >= 0) ? start : 0;
        var iLen = (length && length > 0) ? length : 999999;

        var vNodes;
        if (this.data)
            vNodes = this.data;
        else 
            vNodes = this.getData(xmlNode, "xml");
        if (!vNodes || vNodes.length <= 0) return null;
        
        if (this.sortXpath)
            this.sortData(vNodes, "xml");
            
        if (this.displays) {
            if (i < iLen) {
                for (var i = 0; i < this.displays.length; i++)
                    if (this.displays[i] >= 0 && this.displays[i] < vNodes.length)
                        this.writeHtmlAt(holder, this.displays[i], vNodes);
            }
        }
        else {
            for (var i = 0; i < iLen; i++) {
                if (iStart + i >= vNodes.length) break;
                this.writeHtmlAt(holder, i, vNodes);
            }
        }
    },
    
    writeHtmlByJson: function(holder, data, start, length) {
        if (!holder) return;
        var fHelp = new FormatHelper();
        var iStart = (start && start >= 0) ? start : 0;
        var iLen = (length && length > 0) ? length : 999999;

        var vNodes;
        if (this.data)
            vNodes = this.data;
        else 
            vNodes = this.getData(data, "json");
        if (!vNodes || vNodes.length <= 0) return null;

        if (this.sortXpath)
            this.sortData(vNodes, "json");
            
        if (this.displays) {
            for (var i = 0; i < this.displays.length; i++) {
                if (i < iLen && this.displays[i] >= 0 && this.displays[i] < vNodes.length)
                    this.writeHtmlAt(holder, this.displays[i], vNodes);
            }
        }
        else {
            for (var i = 0; i < iLen; i++) {
                if (iStart + i >= vNodes.length) break;
                this.writeHtmlAt(holder, i, vNodes);
            }
        }
    },    

    writeHtmlAt: function(holder, i, vNodes){
        var strs = new Array();
        if (this.gCount > 0) {
            for (var k = 0; k < this.gCount; k++) {
                var str = "<div";
                if (this.cssGroups && k < this.cssGroups.length && this.cssGroups[k]) 
                    str += " class='" + this.cssGroups[k] + "'";
                str += ">";
                strs.push(str);
            }
        }
        else 
            strs.push("");

        for (var j = 0; j < this.cells.length; j++) {
            if (this.cells[j].gIdx >= 0 && this.cells[j].gIdx < this.cells.length) {
                if (!this.cells[j].xHelp) this.cells[j].xHelp = this.xHelp;
                strs[this.cells[j].gIdx] += this.cells[j].getHtml(vNodes[i]);
            }
        }
        
        var sHtml = "<div";
        if (this.css) sHtml += " class='" + this.css + "'";
        if (this.cssHover)
            sHtml += " onmouseout=\"this.className='" + this.css + "';\" " +
                     " onmouseover=\"this.className='" + this.cssHover + "';\" ";
        sHtml += ">";
        if (this.gCount > 0)
            for (var k = 0; k < this.gCount; k++) sHtml += strs[k] + "</div>";
        else
            sHtml = sHtml + strs[0] + "</div>";
        holder.innerHTML += sHtml;
    }
}


XmlList = function() {}

XmlList.prototype = {
    create: function(holder, datatype, data, listItem, start, length) {
        if (!holder || !datatype || !data || !listItem) return;
        var xHelp = new XmlHelper();
        if (!xHelp) return;
        if (listItem.cells && listItem.cells.length > 0)
            for(var i = 0; i < listItem.cells.length; i++)
                listItem.cells[i].dataType = datatype.toLowerCase();
                
        switch(datatype.toLowerCase()) {
            case "string":
            case "url":
                var xmlDoc = xHelp.getXmlDoc(datatype, data);
                listItem.xHelp = xHelp;
                listItem.writeHtml(holder, xmlDoc.documentElement, start, length);
                break;
            case "json":
                listItem.xHelp = xHelp;
                listItem.writeHtmlByJson(holder, data, start, length);
                break;
        }
    }
}