    function GColumn(){
        this.id         = null;
        this.label      = null;
        this.multi      = false;
        this.options    = {};
        this.values     = {};
    }
    function GColumns(){
        this.all        = {};
        this.deserialize= function(nodes){
            for (var i=0; i<nodes.length; i++){
                var node = nodes(i);
                var c = new GColumn();
                
                c.id = node.getAttribute("metaid");
                c.label = node.getAttribute("label").capitalize();
                
                this.all[c.id] = c;
                
                var opts = node.selectNodes("option");
                if (opts.length>0) c.multi = true;
                
                for (var j=0; j<opts.length; j++){
                    var opt = opts(j);
                    
                    var id = opt.getAttribute("id");
                    
                    var value = opt.selectSingleNode("value").text;
                    var label = opt.selectSingleNode("label").text;

                    c.options[id] = [value, label];
                    c.values[value] = label;
                }
            }
        }
        this.getLabelByValue = function(metaid, id){ 
            if (id.length==0) return null;
            
            //check if attribute was deleted
            if (this.all[metaid].values[id]){
                return this.all[metaid].values[id]; 
            }else{
                return null;
            }
        }
        this.getLabelById = function(metaid, id){ 
            if (id.length==0) return null;
            
            //check if attribute was deleted
            if (this.all[metaid].options[id]){
                return this.all[metaid].options[id][1]; 
            }else{
                return null;
            }
        }
    }

    function GList(){
        this.id             = null;
        this.label          = null;
        this.data           = null;
        this.style          = null;
        this.more           = null;
        this.preview        = false;
        this.moretext       = "More...";
        this.isfiltered     = false;
        this.pagesize       = 5;
        this.maxcount       = 100;
        this.start          = 0;
        this.handle         = null;  //todo;
        this.columns        = new GColumns();
        this.header         = new GListHeader(this);
        this.filter         = null;
        this.archive        = 0;
        this.isRendered     = false;
    }
    GList.prototype.load = function(isLast){
        this.data       = new XMLDom();
        this.data.async = false;

        if (this.preview==false){
            this.loadFile();
        }else{
            this.loadInline();
        }
        
        if (document.location.href.indexOf("localhost")!=-1 ||
            document.location.href.indexOf("10.94.192.102")!=-1){
            //this.data.load(this.id + ".xml");
        }
  //For IE
        if (window.ActiveXObject)
        {
            if (this.data.parseError!=0)
            {
                status = "Error loading list " + this.id + ". Reason: " + this.data.parseError.reason;
                return;
            }
        }
        else
        {
            //For Other Browser
            if (this.data.documentElement.nodeName=="parsererror" 
        			&& document.implementation 
        			&& document.implementation.createDocument )
      	    {
     	        status = "Error loading list " + this.data.documentElement.childNodes[0].nodeValue;
    	        return;
      	    }
      	}
        //onload event processing
        if (typeof(this.onload)=="function"){
            if (!this.onload(isLast)) return;
        }

        this.filter = new GListFilter(this);

        this.deserialize(this.data.selectSingleNode("//list"));

        this.columns.deserialize(this.data.selectNodes("//columns/column"));

        this.render();        

        this.isRendered = true;

        if (this.isfiltered && CMS_ListsCollection.length==0){
            var _t = this;
            _t.filter.load();
            //setTimeout(function(){ _t.filter.load(); }, 250);
        }
    }
    GList.prototype.render = function(){
        var s = "";
        
        var rows = this.data.selectNodes("//listitem");
                
        s += "<div class=cms_listcontainer" + this.style + ">";
        
        //do not render if there is no listitems
        if (rows.length>0){
            s += "<div class=cms_listtitle" + this.style + ">" + this.label + "</div>";
            
            //limit filtering to single lists 
            if (this.isfiltered && CMS_ListsCollection.length==1) s += this.filter.toString();
            
            //output header information if implemented
            s += this.header.toString();
        }
        
        
        
        s += "<div id=\"cms_listrows_" + this.id + "\">";
        s += this.toString();
        s += "</div>";
        
        document.getElementById("CMS_List_" + this.id).innerHTML = s;
    }
    GList.prototype.loadFile = function(){
        var tmp = Math.floor(new Number(this.id)/1000);
            tmp = "00000" + tmp;
		    tmp = "/includes/" + tmp.substring(tmp.length-5, tmp.length);
		    tmp = tmp + "/" + this.id + "/views/" + this.id + "-0";
		    
		    //load extract dataset off the start
		    //load extract dataset off the start
		    if (this.archive == 0) {
		        if (typeof (ListCustomization) != "undefined") {
		            if (ListCustomization.archive == 1) tmp += "";
		            else tmp += ".extract";
		        } else {
		            tmp += ".extract";
		        }
		    }
		    
		    tmp += ".xml";
		    
		    try
			{
				this.data.load(tmp);
            } 
            catch(e)
            { 
				var req = new XMLHttpRequest();
				req.open("GET", tmp, false);
				req.onload = function() {xmldata = handleResponse(req); }
				req.send(null);						
				var parser = new DOMParser();
				this.data = parser.parseFromString(xmldata, "text/xml");            
            }     
    }
    GList.prototype.loadInline = function()
    {
        if (window.ActiveXObject)
        { // code for IE
            this.data = document.getElementById("CMS_List_XML_" + this.id);
        } 
        else if (document.implementation && document.implementation.createDocument)
        { 
            var parser = new DOMParser();
            for (var i=0; i<document.getElementsByTagName("xml").length; i++)
            {
                if(document.getElementsByTagName("xml")[i].id == 'CMS_List_XML_' + this.id)
                {
                    var docValue = document.getElementsByTagName("xml")[i].innerHTML;
                    this.data = parser.parseFromString(document.getElementsByTagName("xml")[i].innerHTML, "text/xml");   
                }
            }
        }
        
    }
    GList.prototype.deserialize = function(node){
        this.label          = node.parentNode.getAttribute("label");
        
        var pagesizeVal = new Number(node.selectSingleNode("pagesize").text);
        
        this.pagesize       = (pagesizeVal==0 ? 1 : pagesizeVal);
        this.style          = node.selectSingleNode("style").text;
        this.maxcount       = node.getAttribute("size") || 100;
        this.more           = (node.selectSingleNode("more").text=="") ? null : node.selectSingleNode("more").text;
        this.isfiltered     = node.getAttribute("isfiltered")==1;
        
        if (typeof(ListCustomization)!="undefined"){
            try{
                this.maxcount   = ListCustomization.maxcount || this.data.selectNodes("//listitem").length;
                this.pagesize   = ListCustomization.pagesize || this.pagesize;
                this.moretext   = ListCustomization.moretext || this.moretext;
                this.isfiltered = ListCustomization.isfiltered || this.isfiltered;
                this.archive    = ListCustomization.archive || this.archive;
            }catch(e){}
        }
    }
    GList.prototype.toString = function(){
        var s = "";
        
        var rows = this.data.selectNodes("//listitem");   
    
        for (var i=this.start; i<this.start+this.pagesize; i++){
            if (rows[i]==null) break;
            
            var l = new GListItem();
            l.deserialize(rows[i], this);
            if (typeof(l.toString)=="function") 
            {
            s += l.toString(i);
            }
        }
        
        //default message
        if (rows.length==0 && this.isfiltered){
            s += "<div>No matches for your search criteria or list is empty.</div>";
        }
        
        s += "<div class=cms_listpages" + this.style + ">" + this._getPaging() + "</div>";
        
        return s;
    }
    GList.prototype.resetPaging = function(){ this.start = 0; }
    GList.prototype._getPaging = function(){
        var s = "";
        
        if (this.more!=null && CMS_ListsCollection.length>1){
            s += "<a class=cms_listpage href=" + this.more + ">" + this.moretext + "</a>";
            return s;
        }

        var rows = this.data.selectNodes("//listitem");
        
        var maxcount = Math.min(rows.length, this.maxcount);
                       
        //calculate total pages
        var n = maxcount / this.pagesize;
        if (n.toString().indexOf(".")!=-1){
            n = new Number(n.toString().substring(0, n.toString().indexOf(".")))+1;
        }
        
        //calculate current page
        var nn = 1;
        if (this.start==0) nn = 1;
        else{
            nn = this.start/this.pagesize+1;
        }
        
        if (nn>1){
            var jumpto = nn-2;
            s += "<a class=cms_listpage href=# onclick=CMS_ListsCollection[" + this.id + "].jump(" + jumpto + ")><< Previous</a>";
        }
        for (var i=1; i<=n; i++){
            if (n==1) continue;
            var jumpto = i-1;

            if (nn==i) s += " " + i + " ";
            else s += " <a class=cms_listpage href=# onclick=CMS_ListsCollection[" + this.id + "].jump(" + jumpto + ")>" + i + "</a> ";
        }
        if (nn<n){
            var jumpto = nn;
            s += "<a class=cms_listpage href=# onclick=CMS_ListsCollection[" + this.id + "].jump(" + jumpto + ")>Next >></a>";
        }
        
        //archive | current
        if (this.archive==0){
            s += "&#160;&#160;<a class=cms_listpage href=# onclick=CMS_ListsCollection[" + this.id + "].swap()>Archive</a>";
        }
        
        return s;
    }
    GList.prototype.swap = function(){
        //set archive bit
        this.archive = 1;
        
        //reload full dataset
        this.loadFile();
        
        //re-apply filters
        if (this.filter.filters.length>0){
            this.filter.alldata = this.data;
            this.filter.initialize(true);
            this.filter.apply();
        }else{
            this.write();
        }
    }
    GList.prototype.jump = function(i){
        this.start = i * this.pagesize;// + 1;
        if (this.start==1) this.start = 0;
        this.write();
    }
    GList.prototype.write = function(){
        document.getElementById("cms_listrows_" + this.id).innerHTML = this.toString();
    }

    function GAttributes(){
        this.all        = {};
        this.deserialize= function(nodes, columns){
            for (var i=0; i<nodes.length; i++){
                var a = new GAttribute();
                
                var metaid = nodes[i].getAttribute("metaid");
                
                a.id = metaid;
                
                this.all[metaid] = a;
                
                if (typeof(columns.all[metaid])!="undefined"){
                    var label = columns.all[metaid].label.replaceSpaces().toLowerCase();
                    this["_" + label] = a;
                }
                
                var opts = nodes[i].selectNodes("option");
                for (var j=0; j<opts.length; j++){
                    var value = opts[j].selectSingleNode("value").text;
                    
                    /* ************************************************************ */
                    /* start of ugly hack due to denormalization of attribute trees */
                    /* ************************************************************ */
                    if (value.indexOf("~")!=-1){
                        var tmp = value.split("~");
                        
                        var b = tmp[0].split("|");
                        var c = tmp[1].split("|");
                        
                        for (var k=1; k<b.length-1; k++){
                            a.values.push(c[k]);
                            a.textValues.push(columns.getLabelById(metaid, b[k]));
                        }
                        
                        continue;
                    }
                    /* **************** */
                    /* end of ugly hack */
                    /* **************** */
                                    
                    //000 parent values *** YET ANOTHER HACK ***              
                    //--- specified for attributes of attributes
                    //--- a parent value is the value of an attribute which implements another attribute
                    //--- the attribute of the attribute is the value that should be shown when a listitem
                    //--- with the specified attribute falls in scope.
                    //000 eg.
                    //--- Listitem (9999) flagged with Role (14) is marked as Urgent (5)
                    //--- when 9999 is filtered by 14, 5 can be shown as an attribute of 9999
               
                    var pValue = opts[j].getAttribute("parentValue");
                    if (pValue) a.parentValues.push(pValue);
                                       
                    a.values.push(value);
                    
                    if (columns.all[metaid]){
                        if (columns.all[metaid].multi)
                            a.textValues.push(columns.getLabelByValue(metaid, value));
                        else
                            a.textValues.push(value);
                    }
                }
            }
        }
    }

    function GAttribute(){
        this.id             = null;
        this.parentValues   = [];
        this.values         = [];
        this.textValues     = [];
    }
    
    function GListItem(){
        this.id         = null;
        this.data       = null;
        this.parent     = null;
        this.attributes = [];
    }

    GListItem.prototype.populateProperties = function(node){
        this.href           = node.getAttribute("url");
        
        //hack to fix domained URLs lacking leading slashes //
        var s = this.href.replace(".","");
        var i = this.href.length - s.length;
        //greater than one because a file/page link has a dot in it
        //more dots probably means a domain in the url
        if (i>1){
            this.href = "//" + this.href;
        }
        
        this.protocol       = node.getAttribute("protocol");
        this.target         = node.getAttribute("target");
        this.isFlag         = node.getAttribute("flag")==1;
        this.isPin          = node.getAttribute("pin")==1;
        this.type           = node.getAttribute("type");
        this.id             = node.getAttribute("id");
        this.label          = node.selectSingleNode("label").text;
        this.title          = node.getAttribute("title");
        this.extension      = null;
        this.size           = null;
        this.updatedon      = null;
        this.createdon      = null;
        this.updatedby      = null;
        this.createdby      = null;
        this.publishedon    = null;
        this.publishedby    = null;
        this.posteddate     = null;
        
        switch (this.type){
            case "file":
                var n = node.selectSingleNode("file");
                this.createdon      = node.getAttribute("createdon");
                this.updatedon      = n.getAttribute("updatedon");
                this.updatedby      = n.getAttribute("updatedby");
                this.extension      = n.getAttribute("ext");
                this.size           = n.getAttribute("size");
                this.publishedon     =node.getAttribute("updatedon")
                break;
            case "page":
                var n = node.selectSingleNode("page");
                this.updatedon      = node.getAttribute("updatedon");
                this.createdon      = node.getAttribute("createdon");
                this.publishedon    = n.selectSingleNode("properties/publisheddate").text;
                this.publishedby    = n.selectSingleNode("properties").getAttribute("publishedby");
                this.createdby      = n.selectSingleNode("properties").getAttribute("createdby");
                this.updatedby      = n.selectSingleNode("properties").getAttribute("updatedby");
                this.posteddate     = n.selectSingleNode("properties/posteddate").text;
                break;
            case "link":
                var n = node;
                this.updatedon      = node.getAttribute("updatedon");
                this.createdon      = node.getAttribute("createdon");
                this.createdby      = n.getAttribute("createdby");
                this.updatedby      = n.getAttribute("updatedby");
                break;
        } 
    }

    GListItem.prototype.deserialize = function(node, parent){
        this.parent = parent;
        
        this.populateProperties(node);
        
        var nodes = null;                
        switch (node.getAttribute("type")){
            case "page":
                nodes = node.selectNodes("page/attributes/values/attribute");
                break;
            case "file":
                nodes = node.selectNodes("file/attributes/values/attribute");
                break;
            case "link":
                nodes = node.selectNodes("attributes/values/attribute");
                break;
        }
        
        this.attributes = new GAttributes();
        this.attributes.deserialize(nodes, this.parent.columns);
    }
    GListItem.prototype.toString = function(){
        var s = "";

        switch (this.parent.style){
            default:
                //tabular
                s += "<div><span class=cms_listlabel>";
                s += "<a href=" + this.href + " target=" + this.target + ">" + this.label + "</a></span>";
                var date = null;
				if (this.updatedon)
				{
					date = new GCalendar(this.updatedon);
				}else{
					date = new GCalendar(this.createdon);	
				}
                if (date.isToday()) date = "Today at " + date.getTimeAsString();
                else date = date.toString("mmm-dd-yyyy");
                s += "<span class=cms_listdate>" + date + "</span></div>";
                break;
            case "1":
                //bulleted
                s += "<div><span class=cms_listbullet>";
                s += "<a href=" + this.href + " target=" + this.target + ">" + this.label + "</a></span><div>";
                break;
            case "2":
                //favorite bullets
                s += "<div><span class=cms_favorites>";
                s += "<a href=" + this.href + " target=" + this.target + ">" + this.label + "</a></span><div>";
                break;
            case "3":
                //attachments
                s += "<div><span class='cms_listattachmentlabel aw-image-ext_";
                s += this.extension + "'>";
                s += "<a href=" + this.href + " target=" + this.target + ">" + this.label + "</a></span>";
                var date = null;
				if (this.updatedon)
				{
					date = new GCalendar(this.updatedon);
				}else{
					date = new GCalendar(this.createdon);	
				}
                if (date.isToday()) date = "Today at " + date.getTimeAsString();
                else date = date.toString("mmm-dd-yyyy");
                s += "<span class=cms_listdate>" + date + "</span>";
                s += "<span class=cms_listsize>" + (this.size ? this.size : "") + "KB</span>";
                s += "</div>";
                break;
        }
        return s;
    }
    
    function GListHeader(parent){
        this.parent = parent;
    }
    GListHeader.prototype.toString = function(){
        var s = "<div class=cms_listheadercontainer>";
        
        switch (this.parent.style){
            default:
                //tabular
                s += "<span class=cms_listlabelheader>Name</span>";
                s += "<span class=cms_listdate>Date</span>";
                break;
            case "1":
                //bulleted
                break;
            case "2":
                //favorite bullets
                break;
            case "3":
                //attachments
                s += "<span class=cms_listattachmentlabel>Name</span>";
                s += "<span class=cms_listdate>Date</span>";
                s += "<span class=cms_listsize>Size</span>";
                break;
        }   
        
        s += "</div>";
        
        return s;
    }
    
    //base class - contains search/filter logic
    function GListFilterController(parent){
        this.label          = "Sorting and Filtering";
        this.expanded       = false;
        this.maxfilters     = 5;
        this.filters        = [];
        this.prefilters     = [];
        this.alldata        = parent.data;
        this.sorton         = null;
        this.sortdirection  = "ASC";
        this.load = function(){
            if (typeof(this.getPrefilters)=="function"){
                this.prefilters = this.getPrefilters();

                if (typeof(this.prefilters)=="undefined") this.prefilters = [];
                
                //validate
                if (!this.prefilters.length>0) this.prefilters = [];
            }
            
            //todo: load saved filters
            
            this.initialize();
        }
        this.apply = function(){
            //reset start
            this.parent.resetPaging();
        
            //clear filters
            this.filters = [];
            
            for (var i=0; i<this.all.length; i++){    
                if (this.all[i].isSelected()) this.filters.push(this.all[i].getFilterValue());
                else{
                    if (i!=this.all.length-1) this.remove(i);
                }
            }
            
            var gdf = new GDataFilter();
            
            //reset the data store
            this.parent.data = this.alldata;
            this.parent.data = gdf.filterList(this.parent);
            this.parent.write();
        }
        this.reset = function(){
            this.filters = [];
            this.parent.data = this.alldata;
            this.initialize(true);
            this.parent.write();
        }
        this.save = function(){}
    }
    
    //view class - contains all UI interaction
    function GListFilter(parent){
        this.base       = GListFilterController;
        this.base(parent);
        
        this.all        = [];
        this.parent     = parent;
        this.count      = 0;
        this.getElement =  function(){ return document.getElementById("cms_filters_" + this.parent.id); }
        this._handle = "CMS_ListsCollection[" + this.parent.id + "].filter";
    }
    GListFilter.prototype.initialize = function(bClear){
        if (bClear){
            this.all = [];
            this.count = 0;
            this.getElement().innerHTML = "";
        }
        
        //add prefilters and disable them
        if (this.prefilters.length>0){
            for (var i=0; i<this.prefilters.length; i++){
                var metaid = this.prefilters[i][0];
                var metavalue = this.prefilters[i][1];
                
                var f = this.append();
                f.populateValues(metaid, metavalue);
            }
            //prefilter
            this.apply();
        }

        //add edit row
        if (this.all.length>0){
            if (!this.all[this.all.length-1].isNew) this.add();
        }else{
            this.add();
        }
    }
    GListFilter.prototype.toString = function(){
        var s = "";

        //wireframe for filtering and sorting
        s += "<fieldset class=\"cms_listfiltergroup" + (this.expanded ? "" : " Collapsed") + "\">";
        s += "<legend>" + this.label + "</legend>";
        s += "<div style=\"min-width: 430px;\" id=\"cms_filters_" + this.parent.id + "\">";     
        s += "</div><hr /><div><table><tr>";
        s += "<td valign=\"top\" width=\"60\"><span>Sort by:</span></td>";
        s += "<td valign=\"top\" width=\"150\"><select title=\"Sorting choices\" onchange=\"" + this._handle + ".onsortchange();\">";
        s += this._buildFilters();
        s += "</select></td>";
        s += "<td width=\"16\"></td><td valign=\"top\" width=\"174\">";
        s += "<fieldset class=\"cms_filtersort\"><legend>Sort order</legend>";
        
        //sorting ASC/DESC radio buttons
        s += "<div><input id=\"cms_sortasc_" + this.parent.id + "\" name=\"cms_listradio\" type=\"radio\" onchange=\"" + this._handle + ".onsortdirchange();\" checked=\"true\"/>";
        s += "<label for=\"cms_sortasc_" + this.parent.id + "\">Ascending</label></div>";
        s += "<div><input id=\"cms_sortdesc_" + this.parent.id + "\" name=\"cms_listradio\" type=\"radio\" onchange=\"" + this._handle + ".onsortdirchange();\"/>";
        s += "<label for=\"cms_sortdesc_" + this.parent.id + "\">Descending</label></div>";
        s += "</fieldset></td></tr><tr><td colspan=\"4\" align=\"right\"><br />";
        
        //save filter selection button
        //s += "<button class=\"cms_listapply\" onclick=\"";
        //s += "CMS_ListsCollection[" + this.parent.id + "].filter.save();\">Save</button>&#160;&#160;";
        
        //clear filters | reset to defaults
        s += "<button class=\"cms_listapply\" onclick=\"";
        s += "CMS_ListsCollection[" + this.parent.id + "].filter.reset();\">Reset</button>&#160;&#160;";
        
        //apply filter selection button
        s += "<button class=\"cms_listapply\" onclick=\"";
        s += "CMS_ListsCollection[" + this.parent.id + "].filter.apply();\">Apply</button></td></tr>";
        s += "</table></div></fieldset>";
        
        return s;
    }

    GListFilter.prototype.onfilterchange = function(e, i){
        i = new Number(i);
        var val = e.options[e.selectedIndex].value;
        var e = this.all[i].getFilterOn();
        
        while (e.hasChildNodes()){
            e.removeChild(e.lastChild);
        }
        
        if (val!=-1){
            var o = this._buildOption(-1, "");
            e.appendChild(o);
            
            for (value in this.parent.columns.all[val].values){
                var o = this._buildOption(value, this.parent.columns.all[val].values[value]);
                e.appendChild(o);
            }
        }
        
        this.all[i].isNew = (val==-1);
        
        if (!this.all[this.all.length-1].isNew) this.add();
    }
    GListFilter.prototype.onfiltervaluechange = function(i){
        var e = this.all[i].getFilterOn();
        e.title = "Filter on " + e.options[e.selectedIndex].innerText;
    }
    GListFilter.prototype.onsortchange = function(){
        this.sorton = event.srcElement.options[event.srcElement.selectedIndex].value;
    }
    GListFilter.prototype.onsortdirchange = function(){
        this.sortdirection = (event.srcElement.id.indexOf("asc")!=-1) ? "ASC" : "DESC";
    }
    GListFilter.prototype._buildFilters = function(){
        var s = "<option value=\"-1\"></option>";
        s += "<option value=\"date\">Date</option>";
        for (col in this.parent.columns.all){
            if (this.parent.columns.all[col].multi)
                s += "<option value=\"" + col + "\">" + this.parent.columns.all[col].label + "</option>";
        }
        return s;
    }
    GListFilter.prototype._buildOption = function(value, label){
        var o = document.createElement("OPTION");
        o.value = value;
        o.innerText = label;
        return o;
    }
    GListFilter.prototype.add = function(i){
        if (i+1<this.all.length) this.insert(i);
        else this.append();
    }
    GListFilter.prototype.append = function(){
        if (this.all.length==this.maxfilters) return;
    
        var f = new GListFilterRow(this.all.length);
        f.parent = this;
        this.all.push(f);
        
        this.getElement().innerHTML += f.toString();
        
        this._toggleLastRow();
        
        return f;
    }
    GListFilter.prototype.remove = function(i){
        //remove from array
        var f = this.all.splice(i, 1);
        
        //remove respective html
        f[0].remove();
        
        //fix html if not at end
        for (var j=i; j<this.all.length; j++){
            this.all[j].index = j;
            this.getElement().childNodes(j).id = this.all[j].id + j;
        }
        
        this._toggleLastRow();
    }
    GListFilter.prototype.insert = function(i){
        if (this.all.length==this.maxfilters) return;
    
        //insert into array at pos=i (where i>=1)
        i++;
        var f = new GListFilterRow(i);
        f.parent = this;
        this.all.splice(i, 0, f);
        
        //insert new html
        this.all[i-1].getElement().insertAdjacentHTML("afterEnd", f.toString());
        
        //fix remaining html
        for (var j=i; j<this.all.length; j++){
            this.all[j].index = j
            this.getElement().childNodes(j).id = this.all[j].id + j;
        }
        
        this._toggleLastRow();
    }
    GListFilter.prototype._toggleLastRow = function(){
        for (var i=0; i<this.all.length; i++){
            if (i==this.all.length-1) this.all[i].disableRemove();
            else this.all[i].enableRemove();
        }
    }
    
    function GListFilterRow(i){
        this.id         = "cms_filters_row";
        this.index      = i;
        this.isNew      = true;
        this.parent     = null;
    }
    GListFilterRow.prototype.toString = function(){
        var s = "<div id=" + this.id + this.index + ">";
        s += "<span class=\"cms_listfilter_col0\"><span>";
        s += (this.index==0 ? "Filter by:" : "And:") + "</span></span>";
        s += "<span class=\"cms_listfilter_col1\">";
        s += "<select title=\"Filter choices\" ";
        s += "onchange=\"" + this.parent._handle + ".onfilterchange(this, this.parentElement.parentElement.id.replace('" + this.id + "', ''));\">";
        s += this.parent._buildFilters();
        s += "</select></span>";
        s += "<span class=\"cms_listfilter_col2\"><img src=\"../cms_images/16x16/arrow-right.gif\" /></span>";
        s += "<span class=\"cms_listfilter_col3\">";
        s += "<select title=\"Filter values\" ";
        s += "onchange=\"" + this.parent._handle + ".onfiltervaluechange(this.parentElement.parentElement.id.replace('" + this.id + "', ''));\"></select></span>";
        s += "<span class=\"cms_listfilter_col4\">";
        s += "<button onclick=\"" + this.parent._handle + ".remove(this.parentElement.parentElement.id.replace('" + this.id + "', ''));\" class=\"cms_listremfilter";
        s += "\" title=\"Delete this filter\"></button></span>";
        s += "</div>";
        return s;
    }
    GListFilterRow.prototype.getElement = function(){ return document.getElementById(this.id + this.index); }
    GListFilterRow.prototype.remove = function(){
        this.getElement().parentElement.removeChild(this.getElement());
    }
    GListFilterRow.prototype.disableRemove = function(){
        this.getElement().childNodes(4).style.display = "none";
    }
    GListFilterRow.prototype.enableRemove = function(){
        this.getElement().childNodes(4).style.display = "inline";
    }
    GListFilterRow.prototype.getFilterOn = function(s){
        return this.getElement().childNodes(3).firstChild;
    }
    GListFilterRow.prototype.getFilterBy = function(s){
        return this.getElement().childNodes(1).firstChild;
    }
    GListFilterRow.prototype.isSelected = function(){
        return this.getFilterOn().selectedIndex>0;
    }
    GListFilterRow.prototype.populateValues = function(metaid, metavalue){
        var e = this.getFilterBy();
        for (var k=0; k<e.options.length; k++){
            if (e.options[k].value==metaid){
                e.options[k].selected = true;
                break;
            }
        }
        
        //trigger filter on population
        e.onchange(e, metaid);
        
        e = this.getFilterOn();
        for (var k=0; k<e.options.length; k++){
            if (e.options[k].value==metavalue){
                e.options[k].selected = true;
                break;
            }
        }
        
        this.disable();        
    }
    GListFilterRow.prototype.getFilterValue = function(){
        var a = [
                 this.getFilterBy().options[this.getFilterBy().selectedIndex].value,
                 this.getFilterOn().options[this.getFilterOn().selectedIndex].value
                ]
        return a;
    }
    GListFilterRow.prototype.disable = function(){
        this.getFilterBy().disabled = true;
        this.getFilterOn().disabled = true;
        this.disableRemove();
    }
    
    // GDataFilter class to filter and sort CMS List 
    function GDataFilter(){
      this.xml = null;
      this.filters = [];
      this.sorton = null;
      this.sortdirection = 'ascending';
      this.xsl = new XMLDom(); 
      
      // Returns filtered and sorted xml from GList
      this.filterList = function(cmsList){
        this.filters = cmsList.filter.filters;
        this.xml = cmsList.data; 
        this.sorton = cmsList.filter.sorton;
        this.sortorder = (cmsList.filter.sortdirection=='DESC') ? 'descending' : 'ascending';
        
        this.buildXsl(); 
        var s = null;
        if (window.ActiveXObject){ // code for IE
          s = this.xml.transformNode(this.xsl);
        } else if (document.implementation && document.implementation.createDocument){ // other browsers
          xsltProcessor=new XSLTProcessor();
          xsltProcessor.importStylesheet(this.xsl);
          s = xsltProcessor.transformToFragment(this.xml,document); //transformToDocument(this.xml)
        }
        var resultXml = new XMLDom();
        resultXml.loadXML(s);
        return resultXml;
      }

      //private
      this.buildXsl = function(){
        var xmlDoc = null;
        var s = '<?xml version="1.0" encoding="utf-8"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">';
        s += '<xsl:template match="/"><message><data><xsl:apply-templates select="//listitem">';
        if(this.sorton!=null) s += '<xsl:sort select=".//attribute[@metaid=\'' + this.sorton + '\']/option/value" order="' + this.sortorder + '" />';
        s += '</xsl:apply-templates></data></message></xsl:template><xsl:template match="listitem">';

        for (var i=0; i<this.filters.length; i++){
          if(this.filters[i][0]=='date'){
            var d = this.filters[i][1];
            d = d.substr(6, 4) + '/' + d.substr(0, 2); //convert mm/dd/yyyy to yyyy/mm
            s += '<xsl:if test="substring(.//publisheddate,1,7)=\'' + d + '\'">';
          } else {
            s += '<xsl:if test=".//attribute[@metaid=\'';
            s += this.filters[i][0];
            s += '\']/option/value=\'';
            s += this.filters[i][1];
            s += '\' or contains(substring-after(.//attribute[@metaid=\'';
            s += this.filters[i][0];
            s += '\']/option/value,\'~\'),\'';
            s += '|' + this.filters[i][1] + '|';
            s += '\')">';
          }
        }
        s += '<xsl:copy-of select="."/>';
        for (var i=0; i<this.filters.length; i++){
          s += '</xsl:if>';
        }
        s += '</xsl:template></xsl:stylesheet> ';
        //alert(s);  
        this.xsl.loadXML(s);
      }            
    }
    // Hack for making insertAdjacentHTML work in firefox -- START
	if(typeof HTMLElement!="undefined" && !
HTMLElement.prototype.insertAdjacentElement){
	HTMLElement.prototype.insertAdjacentElement = function
(where,parsedNode)
	{
		switch (where){
		case 'beforeBegin':
			this.parentNode.insertBefore(parsedNode,this)
			break;
		case 'afterBegin':
			this.insertBefore(parsedNode,this.firstChild);
			break;
		case 'beforeEnd':
			this.appendChild(parsedNode);
			break;
		case 'afterEnd':
			if (this.nextSibling) 
this.parentNode.insertBefore(parsedNode,this.nextSibling);
			else this.parentNode.appendChild(parsedNode);
			break;
		}
	}

	HTMLElement.prototype.insertAdjacentHTML = function
(where,htmlStr)
	{
		var r = this.ownerDocument.createRange();
		r.setStartBefore(this);
		var parsedHTML = r.createContextualFragment(htmlStr);
		this.insertAdjacentElement(where,parsedHTML)
	}


	HTMLElement.prototype.insertAdjacentText = function
(where,txtStr)
	{
		var parsedText = document.createTextNode(txtStr)
		this.insertAdjacentElement(where,parsedText)
	}
}

// Hack for making insertAdjacentHTML work in firefox --  END
   // Hack for making selectNodes, selectSingleNode work in firefox Starts
    //START
if( document.implementation.hasFeature("XPath", "3.0") )
{
	if( typeof XMLDocument == "undefined" )
	{ 
	    XMLDocument = Document; 
	}
	
    XMLDocument.prototype.selectNodes = function(cXPathString, xNode)
    {
        if( !xNode ) 
        { 
            xNode = this; 
        } 
		var oNSResolver = this.createNSResolver(this.documentElement)
		var aItems = this.evaluate(cXPathString, xNode, oNSResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
		var aResult = [];
		for( var i = 0; i < aItems.snapshotLength; i++)
		{
		    aResult[i] =  aItems.snapshotItem(i);	
		}
		return aResult;
	}
	
	XMLDocument.prototype.selectSingleNode = function(cXPathString, xNode)
	{
		if( !xNode ) 
		{ 
		    xNode = this; 
		} 
		var xItems = this.selectNodes(cXPathString, xNode);
		if( xItems.length > 0 )
		{
		    return xItems[0];	
		}
		else
		{
		    return null;	
		}
	}
	
	Element.prototype.selectNodes = function(cXPathString)
	{
		if(this.ownerDocument.selectNodes)
		{	
		    return this.ownerDocument.selectNodes(cXPathString, this);
		}
		else
		{
		    throw "For XML Elements Only";
		}
	}
	
	Element.prototype.selectSingleNode = function(cXPathString)
	{	
		if(this.ownerDocument.selectSingleNode)
		{
		    return this.ownerDocument.selectSingleNode(cXPathString, this);	
		}
		else{throw "For XML Elements Only";}
	}
}



	
 	if (window.XPathEvaluator) { 
    (function(){ 
        if (window.__defineGetter__) { 

            var stylesheet = CSSStyleSheet.prototype; 
            delete stylesheet.rules; 
            stylesheet.__proto__ = {__proto__: stylesheet.__proto__}; 
            stylesheet.__proto__.__defineGetter__("rules", function(){ 
                return this.cssRules; 
            }); 

            var text = Text.prototype; 
            text.__proto__ = {__proto__: text.__proto__}; 
            text.__proto__.__defineGetter__("text", function(){ 
                return this.nodeValue; 
            }); 

            var attr = Attr.prototype; 
            delete attr.text; 
            attr.__proto__ = {__proto__: attr.__proto__}; 
            attr.__proto__.__defineGetter__("text", function(){ 
                return this.nodeValue; 
            }); 

            var element = Element.prototype;
            delete element.text;
            element.__proto__ = {__proto__: element.__proto__};
            element.__proto__.__defineGetter__("text", function(){
                var i, a=[], nodes = this.childNodes, length = nodes.length;
                for (i=0; i<length; i++){
                    a[i] = nodes[i].text;
                }
                return a.join("");
            });
        }
    })();
}
// Hack for making selectNodes, selectSingleNode work in firefox Ends
    function handleResponse(req) 
    {    
		return req.responseText;	 
	}
//END

