Fixed issue with RenderHtml not correctly outputting table to contain sub-reports that fall within a table cell.

--- src/ReportingCloud.Engine/Render/RenderHtml.cs
+++ src/ReportingCloud.Engine/Render/RenderHtml.cs
@@ -19,128 +19,885 @@
 */

 using System;
-using ReportingCloud.Engine;
+using System.Collections;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Globalization;
 using System.IO;
-using System.Collections;
-using System.Drawing.Imaging;
 using System.Text;
 using System.Xml;
-using System.Globalization;
-using System.Drawing;

 namespace ReportingCloud.Engine
-{
-	///
-	/// Renders a report to HTML.   This handles some page formating but does not do true page formatting.
-	///
-	internal class RenderHtml: IPresent
-	{
-		Report r;					// report
-		StringWriter tw;			// temporary location where the output is going
-		IStreamGen _sg;				// stream generater
-		Hashtable _styles;			// hash table of styles we've generated
-		int cssId=1;				// ID for css when names are usable or available
-		bool bScriptToggle=false;	// need to generate toggle javascript in header
-		bool bScriptTableSort=false; // need to generate table sort javascript in header
-		Bitmap _bm=null;			// bm and
-		Graphics _g=null;			//		  g are needed when calculating string heights
-		bool _Asp=false;			// denotes ASP.NET compatible HTML; e.g. no ,
-									//    separate JavaScript and CSS
-		string _Prefix="";			// prefix to generating all HTML names (e.g. css, ...
-		string _CSS;				// when ASP we put the CSS into a string
-		string _JavaScript;			//     as well as any required javascript
-		int _SkipMatrixCols=0;		// # of matrix columns to skip
-
-		public RenderHtml(Report rep, IStreamGen sg)
-		{
-			r = rep;
-			_sg = sg;					// We need this in future
-
-			tw = new StringWriter();	// will hold the bulk of the HTML until we generate
-			// final file
-			_styles = new Hashtable();
-		}
-		~RenderHtml()
-		{
-			// These should already be cleaned up; but in case of an unexpected error
-			//   these still need to be disposed of
-			if (_bm != null)
-				_bm.Dispose();
-			if (_g != null)
-				_g.Dispose();
-		}
-
-		public Report Report()
-		{
-			return r;
-		}
-
-		public bool Asp
-		{
-			get {return _Asp;}
-			set {_Asp = value;}
-		}
-
-		public string JavaScript
-		{
-			get {return _JavaScript;}
-		}
-
-		public string CSS
-		{
-			get {return _CSS;}
-		}
-
-		public string Prefix
-		{
-			get {return _Prefix;}
-			set
-			{
-				_Prefix = value==null? "": value;
-				if (_Prefix.Length > 0 &&
-					_Prefix[0] == '_')
-					_Prefix = "a" + _Prefix;	// not perfect but underscores as first letter don't work
-			}
-		}
-
-		public bool IsPagingNeeded()
-		{
-			return false;
-		}
-
-		public void Start()
-		{
-			return;
-		}
-
-		string FixupRelativeName(string relativeName)
-		{
-			if (_sg is OneFileStreamGen)
-			{
-				if (relativeName[0] == Path.DirectorySeparatorChar || relativeName[0] == Path.AltDirectorySeparatorChar)
-					relativeName = relativeName.Substring(1);
-			}
+{
+    ///
+    ///     Renders a report to HTML.   This handles some page formating but does not do true page formatting.
+    ///
+    internal class RenderHtml : IPresent
+    {
+        readonly IStreamGen _sg; // stream generater
+        readonly Hashtable _styles; // hash table of styles we've generated
+        readonly Report r; // report
+        readonly StringWriter tw; // temporary location where the output is going
+        bool _Asp; // denotes ASP.NET compatible HTML; e.g. no ,
+        //    separate JavaScript and CSS
+        string _CSS; // when ASP we put the CSS into a string
+        string _JavaScript; //     as well as any required javascript
+        string _Prefix = ""; // prefix to generating all HTML names (e.g. css, ...
+        int _SkipMatrixCols; // # of matrix columns to skip
+        Bitmap _bm; // bm and
+        Graphics _g; //		  g are needed when calculating string heights
+        bool bScriptTableSort; // need to generate table sort javascript in header
+        bool bScriptToggle; // need to generate toggle javascript in header
+        int cssId = 1; // ID for css when names are usable or available
+
+        public RenderHtml(Report rep, IStreamGen sg)
+        {
+            r = rep;
+            _sg = sg; // We need this in future
+
+            tw = new StringWriter(); // will hold the bulk of the HTML until we generate
+            // final file
+            _styles = new Hashtable();
+        }
+
+        public bool Asp
+        {
+            get { return _Asp; }
+            set { _Asp = value; }
+        }
+
+        public string JavaScript
+        {
+            get { return _JavaScript; }
+        }
+
+        public string CSS
+        {
+            get { return _CSS; }
+        }
+
+        public string Prefix
+        {
+            get { return _Prefix; }
+            set
+            {
+                _Prefix = value == null ? "" : value;
+                if (_Prefix.Length > 0 &&
+                    _Prefix[0] == '_')
+                    _Prefix = "a" + _Prefix; // not perfect but underscores as first letter don't work
+            }
+        }
+
+        Graphics GetGraphics
+        {
+            get
+            {
+                if (_g == null)
+                {
+                    _bm = new Bitmap(10, 10);
+                    _g = Graphics.FromImage(_bm);
+                }
+                return _g;
+            }
+        }
+
+        public Report Report()
+        {
+            return r;
+        }
+
+        public bool IsPagingNeeded()
+        {
+            return false;
+        }
+
+        public void Start()
+        {
+            return;
+        }
+
+        public void End()
+        {
+            string bodyCssId;
+            if (r.ReportDefinition.Body != null)
+                bodyCssId = CssAdd(r.ReportDefinition.Body.Style, r.ReportDefinition.Body, null); // add the style for the body
+            else
+                bodyCssId = null;
+
+            TextWriter ftw = _sg.GetTextWriter(); // the final text writer location
+
+            if (_Asp)
+            {
+                // do any required JavaScript
+                var sw = new StringWriter();
+                ScriptGenerate(sw);
+                _JavaScript = sw.ToString();
+                sw.Close();
+                // do any required CSS
+                sw = new StringWriter();
+                CssGenerate(sw);
+                _CSS = sw.ToString();
+                sw.Close();
+            }
+            else
+            {
+                ftw.WriteLine("");
+
+                // handle the : description, javascript and CSS goes here
+                ftw.WriteLine("");
+
+                ScriptGenerate(ftw);
+                CssGenerate(ftw);
+
+                if (r.Description != null) // Use description as title if provided
+                    ftw.WriteLine(string.Format(@"<title>{0}</title>", XmlUtil.XmlAnsi(r.Description)));
+
+                ftw.WriteLine(@"");
+            }
+
+            // Always want an HTML body - even if report doesn't have a body stmt
+            if (_Asp)
+            {
+                ftw.WriteLine("
");
+            }
+            else if (bodyCssId != null)
+                ftw.WriteLine(@"
", bodyCssId);
+            else
+                ftw.WriteLine("
");
+
+            ftw.Write(tw.ToString());
+
+            if (_Asp)
+                ftw.WriteLine(@"
");
+            else
+                ftw.WriteLine(@"
");
+
+            if (_g != null)
+            {
+                _g.Dispose();
+                _g = null;
+            }
+            if (_bm != null)
+            {
+                _bm.Dispose();
+                _bm = null;
+            }
+            return;
+        }
+
+        // Body: main container for the report
+        public void BodyStart(Body b)
+        {
+            if (b.ReportItems != null && b.ReportItems.Items.Count > 0)
+                tw.WriteLine("

");
+        }
+
+        public void BodyEnd(Body b)
+        {
+            if (b.ReportItems != null && b.ReportItems.Items.Count > 0)
+                tw.WriteLine("

");
+        }
+
+        public void PageHeaderStart(PageHeader ph)
+        {
+            if (ph.ReportItems != null && ph.ReportItems.Items.Count > 0)
+                tw.WriteLine("

", ph.Height.CSS);
+        }
+
+        public void PageHeaderEnd(PageHeader ph)
+        {
+            if (ph.ReportItems != null && ph.ReportItems.Items.Count > 0)
+                tw.WriteLine("

");
+        }
+
+        public void PageFooterStart(PageFooter pf)
+        {
+            if (pf.ReportItems != null && pf.ReportItems.Items.Count > 0)
+                tw.WriteLine("

", pf.Height.CSS);
+        }
+
+        public void PageFooterEnd(PageFooter pf)
+        {
+            if (pf.ReportItems != null && pf.ReportItems.Items.Count > 0)
+                tw.WriteLine("

");
+        }
+
+        public void Textbox(Textbox tb, string t, Row row)
+        {
+            if (tb.IsHtml(r, row)) // we leave the text as is (except to handle unicode) when request is to treat as html
+            {
+                //   this can screw up the generated HTML if not properly formed HTML
+                t = XmlUtil.HtmlAnsi(t);
+            }
+            else
+            {
+                // make all the characters browser readable
+                t = XmlUtil.XmlAnsi(t);
+
+                // handle any specified bookmark
+                t = Bookmark(tb.BookmarkValue(r, row), t);
+
+                // handle any specified actions
+                t = Action(tb.Action, row, t, tb.ToolTipValue(r, row));
+            }
+            // determine if we're in a tablecell
+            Type tp = tb.Parent.Parent.GetType();
+            bool bCell;
+            if (tp == typeof (TableCell) ||
+                tp == typeof (Corner) ||
+                tp == typeof (DynamicColumns) ||
+                tp == typeof (DynamicRows) ||
+                tp == typeof (StaticRow) ||
+                tp == typeof (StaticColumn) ||
+                tp == typeof (Subtotal) ||
+                tp == typeof (MatrixCell))
+                bCell = true;
+            else
+                bCell = false;
+
+            if (tp == typeof (Rectangle))
+                tw.Write("");
+
+            if (bCell)
+            {
+                // The cell has the formatting for this text
+                if (t == "")
+                    tw.Write("
"); // must have something in cell for formating
+                else
+                    tw.Write(t);
+            }
+            else
+            {
+                // Formatting must be specified
+                string cssName = CssAdd(tb.Style, tb, row); // get the style name for this item
+
+                tw.Write("
{1}
", cssName, t);
+            }
+
+            if (tp == typeof (Rectangle))
+                tw.Write("	");
+        }
+
+        public void DataRegionNoRows(DataRegion d, string noRowsMsg) // no rows in table
+        {
+            if (noRowsMsg == null)
+                noRowsMsg = "";
+
+            bool bTableCell = d.Parent.Parent.GetType() == typeof (TableCell);
+
+            if (bTableCell)
+            {
+                if (noRowsMsg == "")
+                    tw.Write("
");
+                else
+                    tw.Write(noRowsMsg);
+            }
+            else
+            {
+                string cssName = CssAdd(d.Style, d, null); // get the style name for this item
+                tw.Write("
{1}
", cssName, noRowsMsg);
+            }
+        }
+
+        // Lists
+        public bool ListStart(List l, Row r)
+        {
+            // identifiy reportitem it if necessary
+            string bookmark = l.BookmarkValue(this.r, r);
+            if (bookmark != null) //
+                tw.WriteLine("
", bookmark); // can't use the table id since we're using for css style
+            return true;
+        }
+
+        public void ListEnd(List l, Row r)
+        {
+            string bookmark = l.BookmarkValue(this.r, r);
+            if (bookmark != null)
+                tw.WriteLine("
");
+        }
+
+        public void ListEntryBegin(List l, Row r)
+        {
+            string cssName = CssAdd(l.Style, l, r, true); // get the style name for this item; force to be relative
+            tw.WriteLine();
+            tw.WriteLine("
", cssName);
+        }
+
+        public void ListEntryEnd(List l, Row r)
+        {
+            tw.WriteLine();
+            tw.WriteLine("
");
+        }
+
+        // Tables					// Report item table
+        public bool TableStart(Table t, Row row)
+        {
+            string cssName = CssAdd(t.Style, t, row); // get the style name for this item
+
+            // Determine if report custom defn want this table to be sortable
+            if (IsTableSortable(t))
+            {
+                bScriptTableSort = true;
+            }
+
+            string bookmark = t.BookmarkValue(r, row);
+            if (bookmark != null)
+                tw.WriteLine("
", bookmark); // can't use the table id since we're using for css style
+
+            // Calculate the width of all the columns
+            int width = t.WidthInPixels(r, row);
+            if (width <= 0)
+                tw.WriteLine("
", cssName);
+            else
+                tw.WriteLine("
", cssName, width);
+
+            return true;
+        }
+
+        public void TableEnd(Table t, Row row)
+        {
+            string bookmark = t.BookmarkValue(r, row);
+            if (bookmark != null)
+                tw.WriteLine("
");
+            tw.WriteLine("
");
+            return;
+        }
+
+        public void TableBodyStart(Table t, Row row)
+        {
+            tw.WriteLine("
");
+        }
+
+        public void TableBodyEnd(Table t, Row row)
+        {
+            tw.WriteLine("
");
+        }
+
+        public void TableFooterStart(Footer f, Row row)
+        {
+            tw.WriteLine("
");
+        }
+
+        public void TableFooterEnd(Footer f, Row row)
+        {
+            tw.WriteLine("
");
+        }
+
+        public void TableHeaderStart(Header h, Row row)
+        {
+            tw.WriteLine("
");
+        }
+
+        public void TableHeaderEnd(Header h, Row row)
+        {
+            tw.WriteLine("
");
+        }
+
+        public void TableRowStart(TableRow tr, Row row)
+        {
+            tw.Write("\t<tr");
+            ReportLink rl = tr.Parent.Parent;
+            Visibility v = null;
+            Textbox togText = null; // holds the toggle text box if any
+            if (rl is Details)
+            {
+                var d = (Details) rl;
+                v = d.Visibility;
+                togText = d.ToggleTextbox;
+            }
+            else if (rl.Parent is TableGroup)
+            {
+                var tg = (TableGroup) rl.Parent;
+                v = tg.Visibility;
+                togText = tg.ToggleTextbox;
+            }
+
+            if (v != null &&
+                v.Hidden != null)
+            {
+                bool bHide = v.Hidden.EvaluateBoolean(r, row);
+                if (bHide)
+                    tw.Write(" style=\"display:none;\"");
+            }
+
+            if (togText != null && togText.Name != null)
+            {
+                string name = togText.Name.Nm + "_" + togText.RunCount(r).ToString();
+                tw.Write(" id='{0}'", name);
+            }
+
+            tw.Write(">");
+        }
+
+        public void TableRowEnd(TableRow tr, Row row)
+        {
+            tw.WriteLine("
");
+        }
+
+        public void TableCellStart(TableCell t, Row row)
+        {
+            string cellType = t.InTableHeader ? "th" : "td";
+
+            ReportItem r = t.ReportItems.Items[0];
+
+            string cssName = CssAdd(r.Style, r, row); // get the style name for this item
+
+            tw.Write("<{0} id='{1}'", cellType, cssName);
+
+            // calculate width of column
+            if (t.InTableHeader && t.OwnerTable.TableColumns != null)
+            {
+                // Calculate the width across all the spanned columns
+                int width = 0;
+                for (int ci = t.ColIndex; ci < t.ColIndex + t.ColSpan; ci++)
+                {
+                    TableColumn tc = t.OwnerTable.TableColumns.Items[ci];
+                    if (tc != null && tc.Width != null)
+                        width += tc.Width.PixelsX;
+                }
+                if (width > 0)
+                    tw.Write(" width={0}", width);
+            }
+
+            if (t.ColSpan > 1)
+                tw.Write(" colspan={0}", t.ColSpan);
+
+            var tb = r as Textbox;
+            if (tb != null && // have textbox
+                tb.IsToggle && //   and its a toggle
+                tb.Name != null) //   and need name as well
+            {
+                int groupNestCount = t.OwnerTable.GetGroupNestCount(this.r);
+                if (groupNestCount > 0) // anything to toggle?
+                {
+                    string name = tb.Name.Nm + "_" + (tb.RunCount(this.r) + 1).ToString();
+                    bScriptToggle = true;
+
+                    // need both hand and pointer because IE and Firefox use different names
+                    tw.Write(" onClick=\"hideShow(this, {0}, '{1}')\" onMouseOver=\"style.cursor ='hand';style.cursor ='pointer'\">", groupNestCount, name);
+                    tw.Write("");
+                }
+                else
+                    tw.Write("");
+            }
+            else
+                tw.Write(">");
+
+            if (t.InTableHeader)
+            {
+                // put the second half of the sort tags for the column; if needed
+                // first half ----
+                // next half follows text  ----
+
+                string sortcmp = SortType(t, tb); // obtain the sort type
+                if (sortcmp != null) // null if sort not needed
+                {
+                    int headerRows, footerRows;
+                    headerRows = t.OwnerTable.Header.TableRows.Items.Count; // since we're in header we know we have some rows
+                    if (t.OwnerTable.Footer != null &&
+                        t.OwnerTable.Footer.TableRows != null)
+                        footerRows = t.OwnerTable.Footer.TableRows.Items.Count;
+                    else
+                        footerRows = 0;
+                    tw.Write("", sortcmp, headerRows, footerRows);
+                }
+            }
+
+            return;
+        }
+
+        public void TableCellEnd(TableCell t, Row row)
+        {
+            string cellType = t.InTableHeader ? "th" : "td";
+            var tb = t.ReportItems.Items[0] as Textbox;
+            if (cellType == "th" && SortType(t, tb) != null)
+            {
+                // put the second half of the sort tags for the column
+                // first half ----
+                // next half follows text  ----
+                tw.Write("   ");
+            }
+
+            tw.Write("</{0}>", cellType);
+            return;
+        }
+
+        public bool MatrixStart(Matrix m, MatrixCellEntry[,] matrix, Row r, int headerRows, int maxRows, int maxCols) // called first
+        {
+            string bookmark = m.BookmarkValue(this.r, r);
+            if (bookmark != null)
+                tw.WriteLine("
", bookmark); // can't use the table id since we're using for css style
+
+            // output some of the table styles
+            string cssName = CssAdd(m.Style, m, r); // get the style name for this item
+
+            tw.WriteLine("
", cssName);
+            return true;
+        }
+
+        public void MatrixColumns(Matrix m, MatrixColumns mc) // called just after MatrixStart
+        {
+        }
+
+        public void MatrixCellStart(Matrix m, ReportItem ri, int row, int column, Row r, float h, float w, int colSpan)
+        {
+            if (ri == null) // Empty cell?
+            {
+                if (_SkipMatrixCols == 0)
+                    tw.Write("");
+                return;
+            }
+
+            string cssName = CssAdd(ri.Style, ri, r, false, h, w); // get the style name for this item
+
+            tw.Write("<td id='{0}'", cssName);
+            if (colSpan != 1)
+            {
+                tw.Write(" colspan={0}", colSpan);
+                _SkipMatrixCols = -(colSpan - 1); // start it as negative as indicator that we need this
+            }
+            else
+                _SkipMatrixCols = 0;
+            if (ri is Textbox)
+            {
+                var tb = (Textbox) ri;
+                if (tb.IsToggle && tb.Name != null) // name is required for this
+                {
+                    string name = tb.Name.Nm + "_" + (tb.RunCount(this.r) + 1).ToString();
+
+                    bScriptToggle = true; // we need to generate JavaScript in header
+                    // TODO -- need to calculate the hide count correctly
+                    tw.Write(" onClick=\"hideShow(this, {0}, '{1}')\" onMouseOver=\"style.cursor ='hand'\"", 0, name);
+                }
+            }
+
+            tw.Write(">");
+        }
+
+        public void MatrixCellEnd(Matrix m, ReportItem ri, int row, int column, Row r)
+        {
+            if (_SkipMatrixCols == 0)
+                tw.Write("	");
+            else if (_SkipMatrixCols < 0)
+            {
+                tw.Write("	");
+                _SkipMatrixCols = -_SkipMatrixCols;
+            }
+            else
+                _SkipMatrixCols--;
+            return;
+        }
+
+        public void MatrixRowStart(Matrix m, int row, Row r)
+        {
+            tw.Write("\t
");
+        }
+
+        public void MatrixRowEnd(Matrix m, int row, Row r)
+        {
+            tw.WriteLine("
");
+        }
+
+        public void MatrixEnd(Matrix m, Row r) // called last
+        {
+            tw.Write("
");
+
+            string bookmark = m.BookmarkValue(this.r, r);
+            if (bookmark != null)
+                tw.WriteLine("
");
+            return;
+        }
+
+        public void Chart(Chart c, Row r, ChartBase cb)
+        {
+            string relativeName;
+
+            Stream io = _sg.GetIOStream(out relativeName, "png");
+            try
+            {
+                cb.Save(this.r, io, ImageFormat.Png);
+            }
+            finally
+            {
+                io.Flush();
+                io.Close();
+            }
+
+            relativeName = FixupRelativeName(relativeName);
+
+            // Create syntax in a string buffer
+            var sw = new StringWriter();
+
+            string bookmark = c.BookmarkValue(this.r, r);
+            if (bookmark != null)
+                sw.WriteLine("
", bookmark); // can't use the table id since we're using for css style
+
+            string cssName = CssAdd(c.Style, c, null); // get the style name for this item
+
+            sw.Write("");
+            if (bookmark != null)
+                sw.Write("
");
+
+            tw.Write(Action(c.Action, r, sw.ToString(), tooltip));
+
+            return;
+        }
+
+        public void Image(Image i, Row r, string mimeType, Stream ioin)
+        {
+            string relativeName;
+            string suffix;
+
+            switch (mimeType)
+            {
+                case "image/bmp":
+                    suffix = "bmp";
+                    break;
+                case "image/jpeg":
+                    suffix = "jpeg";
+                    break;
+                case "image/gif":
+                    suffix = "gif";
+                    break;
+                case "image/png":
+                case "image/x-png":
+                    suffix = "png";
+                    break;
+                default:
+                    suffix = "unk";
+                    break;
+            }
+            Stream io = _sg.GetIOStream(out relativeName, suffix);
+            try
+            {
+                if (ioin.CanSeek) // ioin.Length requires Seek support
+                {
+                    var ba = new byte[ioin.Length];
+                    ioin.Read(ba, 0, ba.Length);
+                    io.Write(ba, 0, ba.Length);
+                }
+                else
+                {
+                    var ba = new byte[1000]; // read a 1000 bytes at a time
+                    while (true)
+                    {
+                        int length = ioin.Read(ba, 0, ba.Length);
+                        if (length <= 0)
+                            break;
+                        io.Write(ba, 0, length);
+                    }
+                }
+            }
+            finally
+            {
+                io.Flush();
+                io.Close();
+            }
+
+            relativeName = FixupRelativeName(relativeName);
+
+            // Create syntax in a string buffer
+            var sw = new StringWriter();
+
+            string bookmark = i.BookmarkValue(this.r, r);
+            if (bookmark != null)
+                sw.WriteLine("
", bookmark); // we're using for css style
+
+            string cssName = CssAdd(i.Style, i, null); // get the style name for this item
+
+            sw.Write("<img src=\"{0}\" class='{1}'", relativeName, cssName);
+
+            string tooltip = i.ToolTipValue(this.r, r);
+            if (tooltip != null)
+                sw.Write(" alt=\"{0}\"", tooltip);
+            int h = i.Height == null ? -1 : i.Height.PixelsY;
+            int w = i.Width == null ? -1 : i.Width.PixelsX;
+            switch (i.Sizing)
+            {
+                case ImageSizingEnum.AutoSize:
+                    break; // this is right
+                case ImageSizingEnum.Clip:
+                    break; // not sure how to clip it
+                case ImageSizingEnum.Fit:
+                    if (h > 0)
+                        sw.Write(" height=\"{0}\"", h.ToString());
+                    if (w > 0)
+                        sw.Write(" width=\"{0}\"", w.ToString());
+                    break;
+                case ImageSizingEnum.FitProportional:
+                    break; // would have to create an image to handle this
+            }
+
+            sw.Write("/>");
+
+            if (bookmark != null)
+                sw.Write("
");
+
+            tw.Write(Action(i.Action, r, sw.ToString(), tooltip));
+            return;
+        }
+
+        public void Line(Line l, Row r)
+        {
+            bool bVertical;
+            string t;
+            if (l.Height == null || l.Height.PixelsY > 0) // only handle horizontal rule
+            {
+                if (l.Width == null || l.Width.PixelsX > 0) //    and vertical rules
+                    return;
+                bVertical = true;
+                t =
+                    "

";
+            }
+            else
+            {
+                bVertical = false;
+                t =
+                    "

";
+            }
+
+            string width, left, top, height, color;
+            Style s = l.Style;
+
+            left = l.Left == null ? "0px" : l.Left.CSS;
+            top = l.Top == null ? "0px" : l.Top.CSS;
+
+            if (bVertical)
+            {
+                height = l.Height == null ? "0px" : l.Height.CSS;
+                // width comes from the BorderWidth
+                if (s != null && s.BorderWidth != null && s.BorderWidth.Default != null)
+                    width = s.BorderWidth.Default.EvaluateString(this.r, r);
+                else
+                    width = "1px";
+            }
+            else
+            {
+                width = l.Width == null ? "0px" : l.Width.CSS;
+                // height comes from the BorderWidth
+                if (s != null && s.BorderWidth != null && s.BorderWidth.Default != null)
+                    height = s.BorderWidth.Default.EvaluateString(this.r, r);
+                else
+                    height = "1px";
+            }
+
+            if (s != null && s.BorderColor != null && s.BorderColor.Default != null)
+                color = s.BorderColor.Default.EvaluateString(this.r, r);
+            else
+                color = "black";
+
+            tw.WriteLine(t, width, left, top, height, color);
+            return;
+        }
+
+        public bool RectangleStart(Rectangle rect, Row r)
+        {
+            string cssName = CssAdd(rect.Style, rect, r); // get the style name for this item
+
+            string bookmark = rect.BookmarkValue(this.r, r);
+            if (bookmark != null)
+                tw.WriteLine("
", bookmark); // can't use the table id since we're using for css style
+
+            // Calculate the width of all the columns
+            int width = rect.Width == null ? -1 : rect.Width.PixelsX;
+            if (width < 0)
+                tw.WriteLine("

", cssName);
+            else
+                tw.WriteLine("

", cssName, width);
+
+            return true;
+        }
+
+        public void RectangleEnd(Rectangle rect, Row r)
+        {
+            tw.WriteLine("

");
+            string bookmark = rect.BookmarkValue(this.r, r);
+            if (bookmark != null)
+                tw.WriteLine("
");
+            return;
+        }
+
+        // Subreport:
+        public void Subreport(Subreport s, Row r)
+        {
+            string cssName = CssAdd(s.Style, s, r); // get the style name for this item
+
+            tw.WriteLine("

", cssName);
+
+            s.ReportDefn.Run(this);
+
+            tw.WriteLine("

");
+        }
+
+        public void GroupingStart(Grouping g) // called at start of grouping
+        {
+        }
+
+        public void GroupingInstanceStart(Grouping g) // called at start for each grouping instance
+        {
+        }
+
+        public void GroupingInstanceEnd(Grouping g) // called at start for each grouping instance
+        {
+        }
+
+        public void GroupingEnd(Grouping g) // called at end of grouping
+        {
+        }
+
+        public void RunPages(Pages pgs) // we don't have paging turned on for html
+        {
+        }
+
+        ~RenderHtml()
+        {
+            // These should already be cleaned up; but in case of an unexpected error
+            //   these still need to be disposed of
+            if (_bm != null)
+                _bm.Dispose();
+            if (_g != null)
+                _g.Dispose();
+        }
+
+        string FixupRelativeName(string relativeName)
+        {
+            if (_sg is OneFileStreamGen)
+            {
+                if (relativeName[0] == Path.DirectorySeparatorChar || relativeName[0] == Path.AltDirectorySeparatorChar)
+                    relativeName = relativeName.Substring(1);
+            }
             else if (!(relativeName[0] == Path.DirectorySeparatorChar || relativeName[0] == Path.AltDirectorySeparatorChar))
             {
                 if (_Asp)
-                    relativeName = "/" + relativeName;      // Hack for Firefox
+                    relativeName = "/" + relativeName; // Hack for Firefox
                 else
                     relativeName = Path.DirectorySeparatorChar + relativeName;
             }
-			return relativeName;
-		}
-		// puts the JavaScript into the header
-		private void ScriptGenerate(TextWriter ftw)
-		{
-			if (bScriptToggle || bScriptTableSort)
-			{
-				ftw.WriteLine("");
-			}
-
-			return;
-		}
-
-		// handle the Action tag
-		private string Action(Action a, Row r, string t, string tooltip)
-		{
-			if (a == null)
-				return t;
-
-			string result = t;
-			if (a.Hyperlink != null)
-			{	// Handle a hyperlink
-				string url = a.HyperLinkValue(this.r, r);
-				if (tooltip == null)
-					result = String.Format("{1}", url, t);
-				else
-					result = String.Format("{2}", url, tooltip, t);
-			}
-			else if (a.Drill != null)
-			{	// Handle a drill through
-				StringBuilder args= new StringBuilder("<a target=\"_top\" href=\"");
-				if (_Asp)	// for ASP we go thru the default page and pass it as an argument
-					args.Append("Default.aspx?rs:url=");
-				args.Append(a.Drill.ReportName);
-				args.Append(".rdl");
-				if (a.Drill.DrillthroughParameters != null)
-				{
-					bool bFirst = !_Asp;		// ASP already have an argument
-					foreach (DrillthroughParameter dtp in a.Drill.DrillthroughParameters.Items)
-					{
-						if (!dtp.OmitValue(this.r, r))
-						{
-							if (bFirst)
-							{	// First parameter - prefixed by '?'
-								args.Append('?');
-								bFirst = false;
-							}
-							else
-							{	// Subsequant parameters - prefixed by '&'
-								args.Append('&');
-							}
-							args.Append(dtp.Name.Nm);
-							args.Append('=');
-							args.Append(dtp.ValueValue(this.r, r));
-						}
-					}
-				}
-				args.Append('"');
-				if (tooltip != null)
-					args.Append(String.Format(" title=\"{0}\"", tooltip));
-				args.Append(">");
-				args.Append(t);
-				args.Append("");
-				result = args.ToString();
-			}
-			else if (a.BookmarkLink != null)
-			{	// Handle a bookmark
-				string bm = a.BookmarkLinkValue(this.r, r);
-				if (tooltip == null)
-					result = String.Format("{1}", bm, t);
-				else
-					result = String.Format("{2}", bm, tooltip, t);
-			}
-
-			return result;
-		}
-
-		private string Bookmark(string bm, string t)
-		{
-			if (bm == null)
-				return t;
-
-			return String.Format("
{1}
", bm, t);
-		}
-
-		// Generate the CSS styles and put them in the header
-		private void CssGenerate(TextWriter ftw)
-		{
-			if (_styles.Count <= 0)
-				return;
-
-			if (!_Asp)
-				ftw.WriteLine("");
-		}
-
-		private string CssAdd(Style s, ReportLink rl, Row row)
-		{
-			return CssAdd(s, rl, row, false, float.MinValue, float.MinValue);
-		}
-
-		private string CssAdd(Style s, ReportLink rl, Row row, bool bForceRelative)
-		{
-			return CssAdd(s, rl, row, bForceRelative, float.MinValue, float.MinValue);
-		}
-		private string CssAdd(Style s, ReportLink rl, Row row, bool bForceRelative, float h, float w)
-		{
-			string css;
-			string prefix = CssPrefix(s, rl);
-			if (_Asp && prefix == "table#")
-				bForceRelative = true;
-
-			if (s != null)
-				css = prefix + "{" + CssPosition(rl, row, bForceRelative, h, w) + s.GetCSS(this.r, row, true) + "}";
-			else if (rl is Table || rl is Matrix)
-				css = prefix + "{" + CssPosition(rl, row, bForceRelative, h, w) + "border-collapse:collapse;}";
-			else
-				css = prefix + "{" + CssPosition(rl, row, bForceRelative, h, w) + "}";
-
-			CssCacheEntry cce = (CssCacheEntry) _styles[css];
-			if (cce == null)
-			{
-				string name = prefix + this.Prefix + "css" + cssId++.ToString();
-				cce = new CssCacheEntry(css, name);
-				_styles.Add(cce.Css, cce);
-			}
-
-			int i = cce.Name.IndexOf('#');
-			if (i > 0)
-				return cce.Name.Substring(i+1);
-			else
-				return cce.Name;
-		}
-
-		private string CssPosition(ReportLink rl,Row row, bool bForceRelative, float h, float w)
-		{
-			if (!(rl is ReportItem))		// if not a report item then no position
-				return "";
-
-			// no positioning within a table
-			for (ReportLink p=rl.Parent; p != null; p=p.Parent)
-			{
-				if (p is TableCell)
-					return "";
-				if (p is RowGrouping ||
-					p is MatrixCell ||
-					p is ColumnGrouping ||
-					p is Corner)
-				{
-					StringBuilder sb2 = new StringBuilder();
-					if (h != float.MinValue)
-						sb2.AppendFormat(NumberFormatInfo.InvariantInfo, "height: {0}pt; ", h);
-					if (w != float.MinValue)
-						sb2.AppendFormat(NumberFormatInfo.InvariantInfo, "width: {0}pt; ", w);
-					return sb2.ToString();
-				}
-			}
-
-			// TODO: optimize by putting this into ReportItem and caching result???
-			ReportItem ri = (ReportItem) rl;
-
-			StringBuilder sb = new StringBuilder();
-
-			if (ri.Left != null && ri.Left.Size > 0)
-			{
-				sb.AppendFormat(NumberFormatInfo.InvariantInfo, "left: {0}; ", ri.Left.CSS);
-			}
+            }
+
+            if (bScriptTableSort)
+            {
+                ftw.WriteLine("var SORT_INDEX;");
+                ftw.WriteLine("var SORT_DIR;");
+
+                ftw.WriteLine("function sort_getInnerText(element) {");
+                ftw.WriteLine("	if (typeof element == 'string') return element;");
+                ftw.WriteLine("	if (typeof element == 'undefined') return element;");
+                ftw.WriteLine("	if (element.innerText) return element.innerText;");
+                ftw.WriteLine("	var s = '';");
+
+                ftw.WriteLine("	var cn = element.childNodes;");
+                ftw.WriteLine("	for (var i = 0; i < cn.length; i++) {");
+                ftw.WriteLine("		switch (cn[i].nodeType) {");
+                ftw.WriteLine("			case 1:"); // element node
+                ftw.WriteLine("				s += sort_getInnerText(cn[i]);");
+                ftw.WriteLine("				break;");
+                ftw.WriteLine("			case 3:"); // text node
+                ftw.WriteLine("				s += cn[i].nodeValue;");
+                ftw.WriteLine("				break;");
+                ftw.WriteLine("		}");
+                ftw.WriteLine("	}");
+                ftw.WriteLine("	return s;");
+                ftw.WriteLine("}");
+
+                ftw.WriteLine("function sort_table(node, sortfn, header_rows, footer_rows) {");
+                ftw.WriteLine("    var arrowNode;"); // arrow node
+                ftw.WriteLine("    for (var ci=0;ci<node.childNodes.length;ci++) {");
+                ftw.WriteLine("        if (node.childNodes[ci].tagName && node.childNodes[ci].tagName.toLowerCase() == 'span') arrowNode = node.childNodes[ci];");
+                ftw.WriteLine("    }");
+
+                ftw.WriteLine("    var td = node.parentNode;");
+                ftw.WriteLine("    SORT_INDEX = td.cellIndex;"); // need to remember SORT_INDEX in compare function
+                ftw.WriteLine("    var table = sort_getTable(td);");
+
+                ftw.WriteLine("    var sortnext;");
+                ftw.WriteLine("    if (arrowNode.getAttribute('sortdir') == 'down') {");
+                ftw.WriteLine("        arrow = '  ↑';");
+                ftw.WriteLine("        SORT_DIR = -1;"); // descending SORT_DIR in compare function
+                ftw.WriteLine("        sortnext = 'up';");
+                ftw.WriteLine("    } else {");
+                ftw.WriteLine("        arrow = '  ↓';");
+                ftw.WriteLine("        SORT_DIR = 1;"); // ascending SORT_DIR in compare function
+                ftw.WriteLine("        sortnext = 'down';");
+                ftw.WriteLine("    }");
+
+                ftw.WriteLine("    var newRows = new Array();");
+                ftw.WriteLine("    for (j=header_rows;j<table.rows.length-footer_rows;j++) { newRows[j-header_rows] = table.rows[j]; }");
+
+                ftw.WriteLine("    newRows.sort(sortfn);");
+
+                // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
+                ftw.WriteLine("    for (i=0;i<newRows.length;i++) {table.tBodies[0].appendChild(newRows[i]);}");
+
+                // Reset all arrows and directions for next time
+                ftw.WriteLine("    var spans = document.getElementsByTagName('span');");
+                ftw.WriteLine("    for (var ci=0;ci<spans.length;ci++) {");
+                ftw.WriteLine("        if (spans[ci].className == 'sortarrow') {");
+                // in the same table as us?
+                ftw.WriteLine("            if (sort_getTable(spans[ci]) == sort_getTable(node)) {");
+                ftw.WriteLine("                spans[ci].innerHTML = '   ';");
+                ftw.WriteLine("                spans[ci].setAttribute('sortdir','up');");
+                ftw.WriteLine("            }");
+                ftw.WriteLine("        }");
+                ftw.WriteLine("    }");
+
+                ftw.WriteLine("    arrowNode.innerHTML = arrow;");
+                ftw.WriteLine("    arrowNode.setAttribute('sortdir',sortnext);");
+                ftw.WriteLine("}");
+
+                ftw.WriteLine("function sort_getTable(el) {");
+                ftw.WriteLine("	if (el == null) return null;");
+                ftw.WriteLine("	else if (el.nodeType == 1 && el.tagName.toLowerCase() == 'table')");
+                ftw.WriteLine("		return el;");
+                ftw.WriteLine("	else");
+                ftw.WriteLine("		return sort_getTable(el.parentNode);");
+                ftw.WriteLine("}");
+
+                ftw.WriteLine("function sort_cmp_date(c1,c2) {");
+                ftw.WriteLine("    t1 = sort_getInnerText(c1.cells[SORT_INDEX]);");
+                ftw.WriteLine("    t2 = sort_getInnerText(c2.cells[SORT_INDEX]);");
+                ftw.WriteLine("    dt1 = new Date(t1);");
+                ftw.WriteLine("    dt2 = new Date(t2);");
+                ftw.WriteLine("    if (dt1==dt2) return 0;");
+                ftw.WriteLine("    if (dt1<dt2) return -SORT_DIR;");
+                ftw.WriteLine("    return SORT_DIR;");
+                ftw.WriteLine("}");
+
+                // numeric - removes any extraneous formating characters before parsing
+                ftw.WriteLine("function sort_cmp_number(c1,c2) {");
+                ftw.WriteLine("    t1 = sort_getInnerText(c1.cells[SORT_INDEX]).replace(/[^0-9.]/g,'');");
+                ftw.WriteLine("    t2 = sort_getInnerText(c2.cells[SORT_INDEX]).replace(/[^0-9.]/g,'');");
+                ftw.WriteLine("    n1 = parseFloat(t1);");
+                ftw.WriteLine("    n2 = parseFloat(t2);");
+                ftw.WriteLine("    if (isNaN(n1)) n1 = Number.MAX_VALUE");
+                ftw.WriteLine("    if (isNaN(n2)) n2 = Number.MAX_VALUE");
+                ftw.WriteLine("    return (n1 - n2)*SORT_DIR;");
+                ftw.WriteLine("}");
+
+                // For string we first do a case insensitive comparison;
+                //   when equal we then do a case sensitive comparison
+                ftw.WriteLine("function sort_cmp_string(c1,c2) {");
+                ftw.WriteLine("    t1 = sort_getInnerText(c1.cells[SORT_INDEX]).toLowerCase();");
+                ftw.WriteLine("    t2 = sort_getInnerText(c2.cells[SORT_INDEX]).toLowerCase();");
+                ftw.WriteLine("    if (t1==t2) return sort_cmp_casesensitive(c1,c2);");
+                ftw.WriteLine("    if (t1<t2) return -SORT_DIR;");
+                ftw.WriteLine("    return SORT_DIR;");
+                ftw.WriteLine("}");
+
+                ftw.WriteLine("function sort_cmp_casesensitive(c1,c2) {");
+                ftw.WriteLine("    t1 = sort_getInnerText(c1.cells[SORT_INDEX]);");
+                ftw.WriteLine("    t2 = sort_getInnerText(c2.cells[SORT_INDEX]);");
+                ftw.WriteLine("    if (t1==t2) return 0;");
+                ftw.WriteLine("    if (t2<t2) return -SORT_DIR;");
+                ftw.WriteLine("    return SORT_DIR;");
+                ftw.WriteLine("}");
+            }
+
+            if (bScriptToggle || bScriptTableSort)
+            {
+                ftw.WriteLine("</script>");
+            }
+
+            return;
+        }
+
+        // handle the Action tag
+        string Action(Action a, Row r, string t, string tooltip)
+        {
+            if (a == null)
+                return t;
+
+            string result = t;
+            if (a.Hyperlink != null)
+            {
+                // Handle a hyperlink
+                string url = a.HyperLinkValue(this.r, r);
+                if (tooltip == null)
+                    result = String.Format("{1}", url, t);
+                else
+                    result = String.Format("{2}", url, tooltip, t);
+            }
+            else if (a.Drill != null)
+            {
+                // Handle a drill through
+                var args = new StringBuilder("<a target=\"_top\" href=\"");
+                if (_Asp) // for ASP we go thru the default page and pass it as an argument
+                    args.Append("Default.aspx?rs:url=");
+                args.Append(a.Drill.ReportName);
+                args.Append(".rdl");
+                if (a.Drill.DrillthroughParameters != null)
+                {
+                    bool bFirst = !_Asp; // ASP already have an argument
+                    foreach (DrillthroughParameter dtp in a.Drill.DrillthroughParameters.Items)
+                    {
+                        if (!dtp.OmitValue(this.r, r))
+                        {
+                            if (bFirst)
+                            {
+                                // First parameter - prefixed by '?'
+                                args.Append('?');
+                                bFirst = false;
+                            }
+                            else
+                            {
+                                // Subsequant parameters - prefixed by '&'
+                                args.Append('&');
+                            }
+                            args.Append(dtp.Name.Nm);
+                            args.Append('=');
+                            args.Append(dtp.ValueValue(this.r, r));
+                        }
+                    }
+                }
+                args.Append('"');
+                if (tooltip != null)
+                    args.Append(String.Format(" title=\"{0}\"", tooltip));
+                args.Append(">");
+                args.Append(t);
+                args.Append("");
+                result = args.ToString();
+            }
+            else if (a.BookmarkLink != null)
+            {
+                // Handle a bookmark
+                string bm = a.BookmarkLinkValue(this.r, r);
+                if (tooltip == null)
+                    result = String.Format("{1}", bm, t);
+                else
+                    result = String.Format("{2}", bm, tooltip, t);
+            }
+
+            return result;
+        }
+
+        string Bookmark(string bm, string t)
+        {
+            if (bm == null)
+                return t;
+
+            return String.Format("
{1}
", bm, t);
+        }
+
+        // Generate the CSS styles and put them in the header
+        void CssGenerate(TextWriter ftw)
+        {
+            if (_styles.Count <= 0)
+                return;
+
+            if (!_Asp)
+                ftw.WriteLine("");
+        }
+
+        string CssAdd(Style s, ReportLink rl, Row row)
+        {
+            return CssAdd(s, rl, row, false, float.MinValue, float.MinValue);
+        }
+
+        string CssAdd(Style s, ReportLink rl, Row row, bool bForceRelative)
+        {
+            return CssAdd(s, rl, row, bForceRelative, float.MinValue, float.MinValue);
+        }
+
+        string CssAdd(Style s, ReportLink rl, Row row, bool bForceRelative, float h, float w)
+        {
+            string css;
+            string prefix = CssPrefix(s, rl);
+            if (_Asp && prefix == "table#")
+                bForceRelative = true;
+
+            if (s != null)
+                css = prefix + "{" + CssPosition(rl, row, bForceRelative, h, w) + s.GetCSS(r, row, true) + "}";
+            else if (rl is Table || rl is Matrix)
+                css = prefix + "{" + CssPosition(rl, row, bForceRelative, h, w) + "border-collapse:collapse;}";
+            else
+                css = prefix + "{" + CssPosition(rl, row, bForceRelative, h, w) + "}";
+
+            var cce = (CssCacheEntry) _styles[css];
+            if (cce == null)
+            {
+                string name = prefix + Prefix + "css" + cssId++.ToString();
+                cce = new CssCacheEntry(css, name);
+                _styles.Add(cce.Css, cce);
+            }
+
+            int i = cce.Name.IndexOf('#');
+            if (i > 0)
+                return cce.Name.Substring(i + 1);
+            else
+                return cce.Name;
+        }
+
+        string CssPosition(ReportLink rl, Row row, bool bForceRelative, float h, float w)
+        {
+            if (!(rl is ReportItem)) // if not a report item then no position
+                return "";
+
+            // no positioning within a table
+            for (ReportLink p = rl.Parent; p != null; p = p.Parent)
+            {
+                if (p is TableCell)
+                    return "";
+                if (p is RowGrouping ||
+                    p is MatrixCell ||
+                    p is ColumnGrouping ||
+                    p is Corner)
+                {
+                    var sb2 = new StringBuilder();
+                    if (h != float.MinValue)
+                        sb2.AppendFormat(NumberFormatInfo.InvariantInfo, "height: {0}pt; ", h);
+                    if (w != float.MinValue)
+                        sb2.AppendFormat(NumberFormatInfo.InvariantInfo, "width: {0}pt; ", w);
+                    return sb2.ToString();
+                }
+            }
+
+            // TODO: optimize by putting this into ReportItem and caching result???
+            var ri = (ReportItem) rl;
+
+            var sb = new StringBuilder();
+
+            if (ri.Left != null && ri.Left.Size > 0)
+            {
+                sb.AppendFormat(NumberFormatInfo.InvariantInfo, "left: {0}; ", ri.Left.CSS);
+            }
             if (ri is Matrix || ri is Image || ri is Chart)
-            { }
-            else
-            {
-				if (ri.Width != null)
-					sb.AppendFormat(NumberFormatInfo.InvariantInfo, "width: {0}; ", ri.Width.CSS);
-			}
+            {
+            }
+            else
+            {
+                if (ri.Width != null)
+                    sb.AppendFormat(NumberFormatInfo.InvariantInfo, "width: {0}; ", ri.Width.CSS);
+            }
             if (ri.Top != null && ri.Top.Size > 0)
-			{
-				sb.AppendFormat(NumberFormatInfo.InvariantInfo, "top: {0}pt; ", ri.Gap(this.r));
-			}
-			if (ri is List)
-			{
-				List l = ri as List;
-				sb.AppendFormat(NumberFormatInfo.InvariantInfo, "height: {0}pt; ", l.HeightOfList(this.r, GetGraphics,row));
-			}
-			else if (ri is Matrix || ri is Table || ri is Image || ri is Chart)
-			{}
-			else if (ri.Height != null)
-				sb.AppendFormat(NumberFormatInfo.InvariantInfo, "height: {0}; ", ri.Height.CSS);
-
-			if (sb.Length > 0)
-			{
-				if (bForceRelative || ri.YParents != null)
-					sb.Insert(0, "position: relative; ");
-				else
-					sb.Insert(0, "position: absolute; ");
-			}
-
-			return sb.ToString();
-		}
-
-		private Graphics GetGraphics
-		{
-			get
-			{
-				if (_g == null)
-				{
-					_bm = new Bitmap(10, 10);
-					_g = Graphics.FromImage(_bm);
-				}
-				return _g;
-			}
-		}
-
-		private string CssPrefix(Style s, ReportLink rl)
-		{
-			string cssPrefix=null;
-			ReportLink p;
-
-			if (rl is Table || rl is Matrix || rl is Rectangle)
-			{
-				cssPrefix = "table#";
-			}
-			else if (rl is Body)
-			{
-				cssPrefix = "body#";
-			}
-			else if (rl is Line)
-			{
-				cssPrefix = "table#";
-			}
-			else if (rl is List)
-			{
-				cssPrefix = "";
-			}
-			else if (rl is Subreport)
-			{
-				cssPrefix = "";
-			}
-			else if (rl is Chart)
-			{
-				cssPrefix = "";
-			}
-			if (cssPrefix != null)
-				return cssPrefix;
-
-			// now find what the style applies to
-			for (p=rl.Parent; p != null; p=p.Parent)
-			{
-				if (p is TableCell)
-				{
-					bool bHead = false;
-					ReportLink p2;
-					for (p2=p.Parent; p2 != null; p2=p2.Parent)
-					{
-						Type t2 = p2.GetType();
-						if (t2 == typeof(Header))
-						{
-							if (p2.Parent is Table)
-								bHead=true;
-							break;
-						}
-					}
-					if (bHead)
-						cssPrefix = "th#";
-					else
-						cssPrefix = "td#";
-					break;
-				}
-				else if (p is RowGrouping ||
-					p is MatrixCell ||
-					p is ColumnGrouping ||
-					p is Corner)
-				{
-					cssPrefix = "td#";
-					break;
-				}
-			}
-
-			return cssPrefix == null? "": cssPrefix;
-		}
-
-		public void End()
-		{
-			string bodyCssId;
-			if (r.ReportDefinition.Body != null)
-				bodyCssId = CssAdd(r.ReportDefinition.Body.Style, r.ReportDefinition.Body, null);		// add the style for the body
-			else
-				bodyCssId = null;
-
-			TextWriter ftw = _sg.GetTextWriter();	// the final text writer location
-
-			if (_Asp)
-			{
-				// do any required JavaScript
-				StringWriter sw = new StringWriter();
-				ScriptGenerate(sw);
-				_JavaScript = sw.ToString();
-				sw.Close();
-				// do any required CSS
-				sw = new StringWriter();
-				CssGenerate(sw);
-				_CSS = sw.ToString();
-				sw.Close();
-			}
-			else
-			{
-				ftw.WriteLine("");
-
-				// handle the : description, javascript and CSS goes here
-				ftw.WriteLine("");
-
-				ScriptGenerate(ftw);
-				CssGenerate(ftw);
-
-				if (r.Description != null)	// Use description as title if provided
-					ftw.WriteLine(string.Format(@"<title>{0}</title>", XmlUtil.XmlAnsi(r.Description)));
-
-				ftw.WriteLine(@"");
-			}
-
-			// Always want an HTML body - even if report doesn't have a body stmt
-			if (this._Asp)
-			{
-				ftw.WriteLine("
");
-			}
-			else if (bodyCssId != null)
-				ftw.WriteLine(@"
", bodyCssId);
-			else
-				ftw.WriteLine("
");
-
-			ftw.Write(tw.ToString());
-
-			if (this._Asp)
-				ftw.WriteLine(@"
");
-			else
-				ftw.WriteLine(@"
");
-
-			if (_g != null)
-			{
-				_g.Dispose();
-				_g = null;
-			}
-			if (_bm != null)
-			{
-				_bm.Dispose();
-				_bm = null;
-			}
-			return;
-		}
-
-		// Body: main container for the report
-		public void BodyStart(Body b)
-		{
-			if (b.ReportItems != null && b.ReportItems.Items.Count > 0)
-				tw.WriteLine("

");
-		}
-
-		public void BodyEnd(Body b)
-		{
-			if (b.ReportItems != null && b.ReportItems.Items.Count > 0)
-				tw.WriteLine("

");
-		}
-
-		public void PageHeaderStart(PageHeader ph)
-		{
-			if (ph.ReportItems != null && ph.ReportItems.Items.Count > 0)
-				tw.WriteLine("

", ph.Height.CSS);
-		}
-
-		public void PageHeaderEnd(PageHeader ph)
-		{
-			if (ph.ReportItems != null && ph.ReportItems.Items.Count > 0)
-				tw.WriteLine("

");
-		}
-
-		public void PageFooterStart(PageFooter pf)
-		{
-			if (pf.ReportItems != null && pf.ReportItems.Items.Count > 0)
-				tw.WriteLine("

", pf.Height.CSS);
-		}
-
-		public void PageFooterEnd(PageFooter pf)
-		{
-			if (pf.ReportItems != null && pf.ReportItems.Items.Count > 0)
-				tw.WriteLine("

");
-		}
-
-		public void Textbox(Textbox tb, string t, Row row)
-		{
-            if (tb.IsHtml(this.r, row))		// we leave the text as is (except to handle unicode) when request is to treat as html
-            {                               //   this can screw up the generated HTML if not properly formed HTML
-                t = XmlUtil.HtmlAnsi(t);
-            }
-            else
-            {
-				// make all the characters browser readable
-				t = XmlUtil.XmlAnsi(t);
-
-				// handle any specified bookmark
-				t = Bookmark(tb.BookmarkValue(this.r, row), t);
-
-				// handle any specified actions
-				t = Action(tb.Action, row, t, tb.ToolTipValue(this.r, row));
-			}
-			// determine if we're in a tablecell
-			Type tp = tb.Parent.Parent.GetType();
-			bool bCell;
-			if (tp == typeof(TableCell) ||
-				tp == typeof(Corner) ||
-				tp == typeof(DynamicColumns) ||
-				tp == typeof(DynamicRows) ||
-				tp == typeof(StaticRow) ||
-				tp == typeof(StaticColumn) ||
-				tp == typeof(Subtotal) ||
-				tp == typeof(MatrixCell))
-				bCell = true;
-			else
-				bCell = false;
-
-			if (tp == typeof(Rectangle))
-				tw.Write("");
-
-			if (bCell)
-			{	// The cell has the formatting for this text
-				if (t == "")
-					tw.Write("
");		// must have something in cell for formating
-				else
-					tw.Write(t);
-			}
-			else
-			{	// Formatting must be specified
-				string cssName = CssAdd(tb.Style, tb, row);	// get the style name for this item
-
-				tw.Write("
{1}
", cssName, t);
-			}
-
-			if (tp == typeof(Rectangle))
-				tw.Write("	");
-		}
-
-		public void DataRegionNoRows(DataRegion d, string noRowsMsg)			// no rows in table
-		{
-			if (noRowsMsg == null)
-				noRowsMsg = "";
-
-			bool bTableCell = d.Parent.Parent.GetType() == typeof(TableCell);
-
-			if (bTableCell)
-			{
-				if (noRowsMsg == "")
-					tw.Write("
");
-				else
-					tw.Write(noRowsMsg);
-			}
-			else
-			{
-				string cssName = CssAdd(d.Style, d, null);	// get the style name for this item
-				tw.Write("
{1}
", cssName, noRowsMsg);
-			}
-		}
-
-		// Lists
-		public bool ListStart(List l, Row r)
-		{
-			// identifiy reportitem it if necessary
-			string bookmark = l.BookmarkValue(this.r, r);
-			if (bookmark != null)	//
-				tw.WriteLine("
", bookmark);		// can't use the table id since we're using for css style
-			return true;
-		}
-
-		public void ListEnd(List l, Row r)
-		{
-			string bookmark = l.BookmarkValue(this.r, r);
-			if (bookmark != null)
-				tw.WriteLine("
");
-		}
-
-		public void ListEntryBegin(List l, Row r)
-		{
-			string cssName = CssAdd(l.Style, l, r, true);	// get the style name for this item; force to be relative
-			tw.WriteLine();
-			tw.WriteLine("
", cssName);
-		}
-
-		public void ListEntryEnd(List l, Row r)
-		{
-			tw.WriteLine();
-			tw.WriteLine("
");
-		}
-
-		// Tables					// Report item table
-		public bool TableStart(Table t, Row row)
-		{
-			string cssName = CssAdd(t.Style, t, row);	// get the style name for this item
-
-			// Determine if report custom defn want this table to be sortable
-			if (IsTableSortable(t))
-			{
-				this.bScriptTableSort = true;
-			}
-
-			string bookmark = t.BookmarkValue(this.r, row);
-			if (bookmark != null)
-				tw.WriteLine("
", bookmark);		// can't use the table id since we're using for css style
-
-			// Calculate the width of all the columns
-			int width = t.WidthInPixels(this.r, row);
-			if (width <= 0)
-				tw.WriteLine("
", cssName);
-			else
-				tw.WriteLine("
", cssName, width);
-
-			return true;
-		}
-
-		public bool IsTableSortable(Table t)
-		{
-			if (t.TableGroups != null || t.Details == null ||
-				t.Details.TableRows == null || t.Details.TableRows.Items.Count != 1)
-				return false;	// can't have tableGroups; must have 1 detail row
-
-			// Determine if report custom defn want this table to be sortable
-			bool bReturn = false;
-			if (t.Custom != null)
-			{
-				// Loop thru all the child nodes
-				foreach(XmlNode xNodeLoop in t.Custom.CustomXmlNode.ChildNodes)
-				{
-					if (xNodeLoop.Name == "HTML")
-					{
-						if (xNodeLoop.LastChild.InnerText.ToLower() == "true")
-						{
-							bReturn = true;
-						}
-						break;
-					}
-				}
-			}
-			return bReturn;
-		}
-
-		public void TableEnd(Table t, Row row)
-		{
-			string bookmark = t.BookmarkValue(this.r, row);
-			if (bookmark != null)
-				tw.WriteLine("
");
-			tw.WriteLine("
");
-			return;
-		}
-
-		public void TableBodyStart(Table t, Row row)
-		{
-			tw.WriteLine("
");
-		}
-
-		public void TableBodyEnd(Table t, Row row)
-		{
-			tw.WriteLine("
");
-		}
-
-		public void TableFooterStart(Footer f, Row row)
-		{
-			tw.WriteLine("
");
-		}
-
-		public void TableFooterEnd(Footer f, Row row)
-		{
-			tw.WriteLine("
");
-		}
-
-		public void TableHeaderStart(Header h, Row row)
-		{
-			tw.WriteLine("
");
-		}
-
-		public void TableHeaderEnd(Header h, Row row)
-		{
-			tw.WriteLine("
");
-		}
-
-		public void TableRowStart(TableRow tr, Row row)
-		{
-			tw.Write("\t<tr");
-			ReportLink rl = tr.Parent.Parent;
-			Visibility v=null;
-			Textbox togText=null;		// holds the toggle text box if any
-			if (rl is Details)
-			{
-				Details d = (Details) rl;
-				v = d.Visibility;
-				togText = d.ToggleTextbox;
-			}
-			else if (rl.Parent is TableGroup)
-			{
-				TableGroup tg = (TableGroup) rl.Parent;
-				v = tg.Visibility;
-				togText = tg.ToggleTextbox;
-			}
-
-			if (v != null &&
-				v.Hidden != null)
-			{
-				bool bHide = v.Hidden.EvaluateBoolean(this.r, row);
-				if (bHide)
-					tw.Write(" style=\"display:none;\"");
-			}
-
-			if (togText != null && togText.Name != null)
-			{
-				string name = togText.Name.Nm + "_" + togText.RunCount(this.r).ToString();
-				tw.Write(" id='{0}'", name);
-			}
-
-			tw.Write(">");
-		}
-
-		public void TableRowEnd(TableRow tr, Row row)
-		{
-			tw.WriteLine("
");
-		}
-
-		public void TableCellStart(TableCell t, Row row)
-		{
-			string cellType = t.InTableHeader? "th": "td";
-
-			ReportItem r = t.ReportItems.Items[0];
-
-			string cssName = CssAdd(r.Style, r, row);	// get the style name for this item
-
-			tw.Write("<{0} id='{1}'", cellType, cssName);
-
-			// calculate width of column
-			if (t.InTableHeader && t.OwnerTable.TableColumns != null)
-			{
-				// Calculate the width across all the spanned columns
-				int width = 0;
-				for (int ci=t.ColIndex; ci < t.ColIndex + t.ColSpan; ci++)
-				{
-					TableColumn tc = t.OwnerTable.TableColumns.Items[ci] as TableColumn;
-					if (tc != null && tc.Width != null)
-						width += tc.Width.PixelsX;
-				}
-				if (width > 0)
-					tw.Write(" width={0}", width);
-			}
-
-			if (t.ColSpan > 1)
-				tw.Write(" colspan={0}", t.ColSpan);
-
-			Textbox tb = r as Textbox;
-			if (tb != null &&				// have textbox
-				tb.IsToggle &&				//   and its a toggle
-				tb.Name != null)			//   and need name as well
-			{
-				int groupNestCount = t.OwnerTable.GetGroupNestCount(this.r);
-				if (groupNestCount > 0) // anything to toggle?
-				{
-					string name = tb.Name.Nm + "_" + (tb.RunCount(this.r)+1).ToString();
-					bScriptToggle = true;
-
-					// need both hand and pointer because IE and Firefox use different names
-					tw.Write(" onClick=\"hideShow(this, {0}, '{1}')\" onMouseOver=\"style.cursor ='hand';style.cursor ='pointer'\">", groupNestCount, name);
-                    tw.Write("");
-				}
-				else
-                    tw.Write("");
-			}
-			else
-				tw.Write(">");
-
-			if (t.InTableHeader)
-			{
-				// put the second half of the sort tags for the column; if needed
-				// first half ----
-				// next half follows text  ----
-
-				string sortcmp = SortType(t, tb);	// obtain the sort type
-				if (sortcmp != null)				// null if sort not needed
-				{
-					int headerRows, footerRows;
-					headerRows = t.OwnerTable.Header.TableRows.Items.Count;	// since we're in header we know we have some rows
-					if (t.OwnerTable.Footer != null &&
-						t.OwnerTable.Footer.TableRows != null)
-						footerRows = t.OwnerTable.Footer.TableRows.Items.Count;
-					else
-						footerRows = 0;
-					tw.Write("",sortcmp, headerRows, footerRows);
-				}
-			}
-
-			return;
-		}
-
-		private string SortType(TableCell tc, Textbox tb)
-		{
-			// return of null means don't sort
-			if (tb == null || !IsTableSortable(tc.OwnerTable))
-				return null;
-
-			// default is true if table is sortable;
-			//   but user may place override on Textbox custom tag
-			if (tb.Custom != null)
-			{
-				// Loop thru all the child nodes
-				foreach(XmlNode xNodeLoop in tb.Custom.CustomXmlNode.ChildNodes)
-				{
-					if (xNodeLoop.Name == "HTML")
-					{
-						if (xNodeLoop.LastChild.InnerText.ToLower() == "false")
-						{
-							return null;
-						}
-						break;
-					}
-				}
-			}
-
-			// Must find out the type of the detail column
-			Details d = tc.OwnerTable.Details;
-			if (d == null)
-				return null;
-			TableRow tr = d.TableRows.Items[0] as TableRow;
-			if (tr == null)
-				return null;
-			TableCell dtc = tr.TableCells.Items[tc.ColIndex] as TableCell;
-			if (dtc == null)
-				return null;
-			Textbox dtb = dtc.ReportItems.Items[0] as Textbox;
-			if (dtb == null)
-				return null;
-
-			string sortcmp;
-			switch (dtb.Value.Type)
-			{
-				case TypeCode.DateTime:
-					sortcmp = "sort_cmp_date";
-					break;
-				case TypeCode.Int16:
-				case TypeCode.UInt16:
-				case TypeCode.Int32:
-				case TypeCode.UInt32:
-				case TypeCode.Int64:
-				case TypeCode.UInt64:
-				case TypeCode.Decimal:
-				case TypeCode.Single:
-				case TypeCode.Double:
-					sortcmp = "sort_cmp_number";
-					break;
-				case TypeCode.String:
-					sortcmp = "sort_cmp_string";
-					break;
-				case TypeCode.Empty:	// Not a type we know how to sort
-				default:
-					sortcmp = null;
-					break;
-			}
-
-			return sortcmp;
-		}
-
-		public void TableCellEnd(TableCell t, Row row)
-		{
-			string cellType = t.InTableHeader? "th": "td";
-			Textbox tb = t.ReportItems.Items[0] as Textbox;
-			if (cellType == "th" && SortType(t, tb) != null)
-			{	// put the second half of the sort tags for the column
-				// first half ----
-				// next half follows text  ----
-				tw.Write("   ");
-			}
-
-			tw.Write("</{0}>", cellType);
-			return;
-		}
-
-        public bool MatrixStart(Matrix m, MatrixCellEntry[,] matrix, Row r, int headerRows, int maxRows, int maxCols)				// called first
-		{
-			string bookmark = m.BookmarkValue(this.r, r);
-			if (bookmark != null)
-				tw.WriteLine("
", bookmark);		// can't use the table id since we're using for css style
-
-			// output some of the table styles
-			string cssName = CssAdd(m.Style, m, r);	// get the style name for this item
-
-			tw.WriteLine("
", cssName);
-			return true;
-		}
-
-		public void MatrixColumns(Matrix m, MatrixColumns mc)	// called just after MatrixStart
-		{
-		}
-
-		public void MatrixCellStart(Matrix m, ReportItem ri, int row, int column, Row r, float h, float w, int colSpan)
-		{
-			if (ri == null)			// Empty cell?
-			{
-				if (_SkipMatrixCols == 0)
-					tw.Write("");
-				return;
-			}
-
-			string cssName = CssAdd(ri.Style, ri, r, false, h, w);	// get the style name for this item
-
-			tw.Write("<td id='{0}'", cssName);
-			if (colSpan != 1)
-			{
-				tw.Write(" colspan={0}", colSpan);
-				_SkipMatrixCols=-(colSpan-1);	// start it as negative as indicator that we need this
-			}
-			else
-				_SkipMatrixCols=0;
-			if (ri is Textbox)
-			{
-				Textbox tb = (Textbox) ri;
-				if (tb.IsToggle && tb.Name != null)		// name is required for this
-				{
-					string name = tb.Name.Nm + "_" + (tb.RunCount(this.r)+1).ToString();
-
-					bScriptToggle = true;	// we need to generate JavaScript in header
-					// TODO -- need to calculate the hide count correctly
-					tw.Write(" onClick=\"hideShow(this, {0}, '{1}')\" onMouseOver=\"style.cursor ='hand'\"", 0, name);
-				}
-			}
-
-			tw.Write(">");
-		}
-
-		public void MatrixCellEnd(Matrix m, ReportItem ri, int row, int column, Row r)
-		{
-			if (_SkipMatrixCols == 0)
-				tw.Write("	");
-			else if (_SkipMatrixCols < 0)
-			{
-				tw.Write("	");
-				_SkipMatrixCols = -_SkipMatrixCols;
-			}
-			else
-				_SkipMatrixCols--;
-			return;
-		}
-
-		public void MatrixRowStart(Matrix m, int row, Row r)
-		{
-			tw.Write("\t
");
-		}
-
-		public void MatrixRowEnd(Matrix m, int row, Row r)
-		{
-			tw.WriteLine("
");
-		}
-
-		public void MatrixEnd(Matrix m, Row r)				// called last
-		{
-			tw.Write("
");
-
-			string bookmark = m.BookmarkValue(this.r, r);
-			if (bookmark != null)
-				tw.WriteLine("
");
-			return;
-		}
-
-		public void Chart(Chart c, Row r, ChartBase cb)
-		{
-			string relativeName;
-
-			Stream io = _sg.GetIOStream(out relativeName, "png");
-			try
-			{
-				cb.Save(this.r, io, ImageFormat.Png);
-			}
-			finally
-			{
-				io.Flush();
-				io.Close();
-			}
-
-			relativeName = FixupRelativeName(relativeName);
-
-			// Create syntax in a string buffer
-			StringWriter sw = new StringWriter();
-
-			string bookmark = c.BookmarkValue(this.r, r);
-			if (bookmark != null)
-				sw.WriteLine("
", bookmark);		// can't use the table id since we're using for css style
-
-			string cssName = CssAdd(c.Style, c, null);	// get the style name for this item
-
-			sw.Write("");
-			if (bookmark != null)
-				sw.Write("
");
-
-			tw.Write(Action(c.Action, r, sw.ToString(), tooltip));
-
-			return;
-		}
-
-		public void Image(Image i, Row r, string mimeType, Stream ioin)
-		{
-			string relativeName;
-			string suffix;
-
-			switch (mimeType)
-			{
-				case "image/bmp":
-					suffix = "bmp";
-					break;
-				case "image/jpeg":
-					suffix = "jpeg";
-					break;
-				case "image/gif":
-					suffix = "gif";
-					break;
-				case "image/png":
-				case "image/x-png":
-					suffix = "png";
-					break;
-				default:
-					suffix = "unk";
-					break;
-			}
-			Stream io = _sg.GetIOStream(out relativeName, suffix);
-			try
-			{
-				if (ioin.CanSeek)		// ioin.Length requires Seek support
-				{
-					byte[] ba = new byte[ioin.Length];
-					ioin.Read(ba, 0, ba.Length);
-					io.Write(ba, 0, ba.Length);
-				}
-				else
-				{
-					byte[] ba = new byte[1000];		// read a 1000 bytes at a time
-					while (true)
-					{
-						int length = ioin.Read(ba, 0, ba.Length);
-						if (length <= 0)
-							break;
-						io.Write(ba, 0, length);
-					}
-				}
-			}
-			finally
-			{
-				io.Flush();
-				io.Close();
-			}
-
-			relativeName = FixupRelativeName(relativeName);
-
-			// Create syntax in a string buffer
-			StringWriter sw = new StringWriter();
-
-			string bookmark = i.BookmarkValue(this.r, r);
-			if (bookmark != null)
-				sw.WriteLine("
", bookmark);		// we're using for css style
-
-			string cssName = CssAdd(i.Style, i, null);	// get the style name for this item
-
-			sw.Write("<img src=\"{0}\" class='{1}'", relativeName, cssName);
-
-			string tooltip = i.ToolTipValue(this.r, r);
-			if (tooltip != null)
-				sw.Write(" alt=\"{0}\"", tooltip);
-            int h = i.Height == null? -1: i.Height.PixelsY;
-            int w = i.Width == null ? -1 : i.Width.PixelsX;
-            switch (i.Sizing)
-            {
-                case ImageSizingEnum.AutoSize:
-                    break;          // this is right
-                case ImageSizingEnum.Clip:
-                    break;          // not sure how to clip it
-                case ImageSizingEnum.Fit:
-                    if (h > 0)
-                        sw.Write(" height=\"{0}\"", h.ToString());
-                    if (w > 0)
-                        sw.Write(" width=\"{0}\"", w.ToString());
+            {
+                sb.AppendFormat(NumberFormatInfo.InvariantInfo, "top: {0}pt; ", ri.Gap(r));
+            }
+            if (ri is List)
+            {
+                var l = ri as List;
+                sb.AppendFormat(NumberFormatInfo.InvariantInfo, "height: {0}pt; ", l.HeightOfList(r, GetGraphics, row));
+            }
+            else if (ri is Matrix || ri is Table || ri is Image || ri is Chart)
+            {
+            }
+            else if (ri.Height != null)
+                sb.AppendFormat(NumberFormatInfo.InvariantInfo, "height: {0}; ", ri.Height.CSS);
+
+            if (sb.Length > 0)
+            {
+                if (bForceRelative || ri.YParents != null)
+                    sb.Insert(0, "position: relative; ");
+                else
+                    sb.Insert(0, "position: absolute; ");
+            }
+
+            return sb.ToString();
+        }
+
+        string CssPrefix(Style s, ReportLink rl)
+        {
+            string cssPrefix = null;
+            ReportLink p;
+
+            if (rl is Table || rl is Matrix || rl is Rectangle)
+            {
+                cssPrefix = "table#";
+            }
+            else if (rl is Body)
+            {
+                cssPrefix = "body#";
+            }
+            else if (rl is Line)
+            {
+                cssPrefix = "table#";
+            }
+            else if (rl is List)
+            {
+                cssPrefix = "";
+            }
+            else if (rl is Subreport)
+            {
+                cssPrefix = "";
+            }
+            else if (rl is Chart)
+            {
+                cssPrefix = "";
+            }
+            if (cssPrefix != null)
+                return cssPrefix;
+
+            // now find what the style applies to
+            for (p = rl.Parent; p != null; p = p.Parent)
+            {
+                if (p is TableCell)
+                {
+                    bool bHead = false;
+                    ReportLink p2;
+                    for (p2 = p.Parent; p2 != null; p2 = p2.Parent)
+                    {
+                        Type t2 = p2.GetType();
+                        if (t2 == typeof (Header))
+                        {
+                            if (p2.Parent is Table)
+                                bHead = true;
+                            break;
+                        }
+                    }
+                    if (bHead)
+                        cssPrefix = "th#";
+                    else
+                        cssPrefix = "td#";
                     break;
-                case ImageSizingEnum.FitProportional:
-                    break;          // would have to create an image to handle this
-            }
-
-			sw.Write("/>");
-
-			if (bookmark != null)
-				sw.Write("
");
-
-			tw.Write(Action(i.Action, r, sw.ToString(), tooltip));
-			return;
-		}
-
-		public void Line(Line l, Row r)
-		{
-			bool bVertical;
-			string t;
-			if (l.Height == null || l.Height.PixelsY > 0)	// only handle horizontal rule
-			{
-				if (l.Width == null || l.Width.PixelsX > 0)	//    and vertical rules
-					return;
-				bVertical = true;
-				t = "

";
-			}
-			else
-			{
-				bVertical = false;
-				t = "

";
-			}
-
-			string width, left, top, height, color;
-			Style s = l.Style;
-
-			left = l.Left == null? "0px": l.Left.CSS;
-			top = l.Top == null? "0px": l.Top.CSS;
-
-			if (bVertical)
-			{
-				height = l.Height == null? "0px": l.Height.CSS;
-				// width comes from the BorderWidth
-				if (s != null && s.BorderWidth != null && s.BorderWidth.Default != null)
-					width = s.BorderWidth.Default.EvaluateString(this.r, r);
-				else
-					width = "1px";
-			}
-			else
-			{
-				width = l.Width == null? "0px": l.Width.CSS;
-				// height comes from the BorderWidth
-				if (s != null && s.BorderWidth != null && s.BorderWidth.Default != null)
-					height = s.BorderWidth.Default.EvaluateString(this.r, r);
-				else
-					height = "1px";
-			}
-
-			if (s != null && s.BorderColor != null && s.BorderColor.Default != null)
-				color = s.BorderColor.Default.EvaluateString(this.r, r);
-			else
-				color = "black";
-
-			tw.WriteLine(t, width, left, top, height, color);
-			return;
-		}
-
-        public bool RectangleStart(Engine.Rectangle rect, Row r)
-		{
-			string cssName = CssAdd(rect.Style, rect, r);	// get the style name for this item
-
-			string bookmark = rect.BookmarkValue(this.r, r);
-			if (bookmark != null)
-				tw.WriteLine("
", bookmark);		// can't use the table id since we're using for css style
-
-			// Calculate the width of all the columns
-			int width = rect.Width == null? -1: rect.Width.PixelsX;
-			if (width < 0)
-				tw.WriteLine("

", cssName);
-			else
-				tw.WriteLine("

", cssName, width);
-
-			return true;
-		}
-
-        public void RectangleEnd(Engine.Rectangle rect, Row r)
-		{
-			tw.WriteLine("

");
-			string bookmark = rect.BookmarkValue(this.r, r);
-			if (bookmark != null)
-				tw.WriteLine("
");
-			return;
-		}
-
-		// Subreport:
-		public void Subreport(Subreport s, Row r)
-		{
-			string cssName = CssAdd(s.Style, s, r);	// get the style name for this item
-
-			tw.WriteLine("
", cssName);
-
-			s.ReportDefn.Run(this);
-
-			tw.WriteLine("
");
-		}
-		public void GroupingStart(Grouping g)			// called at start of grouping
-		{
-		}
-		public void GroupingInstanceStart(Grouping g)	// called at start for each grouping instance
-		{
-		}
-		public void GroupingInstanceEnd(Grouping g)	// called at start for each grouping instance
-		{
-		}
-		public void GroupingEnd(Grouping g)			// called at end of grouping
-		{
-		}
-		public void RunPages(Pages pgs)	// we don't have paging turned on for html
-		{
-		}
-	}
-
-
-	class CssCacheEntry
-	{
-		string _Css;					// css
-		string _Name;					// name of entry
-
-		public CssCacheEntry(string css, string name)
-		{
-			_Css = css;
-			_Name = name;
-		}
-
-		public string Css
-		{
-			get { return  _Css; }
-			set { _Css = value; }
-		}
-
-		public string Name
-		{
-			get { return  _Name; }
-			set { _Name = value; }
-		}
-	}
-}
+                }
+                else if (p is RowGrouping ||
+                         p is MatrixCell ||
+                         p is ColumnGrouping ||
+                         p is Corner)
+                {
+                    cssPrefix = "td#";
+                    break;
+                }
+            }
+
+            return cssPrefix == null ? "" : cssPrefix;
+        }
+
+        public bool IsTableSortable(Table t)
+        {
+            if (t.TableGroups != null || t.Details == null ||
+                t.Details.TableRows == null || t.Details.TableRows.Items.Count != 1)
+                return false; // can't have tableGroups; must have 1 detail row
+
+            // Determine if report custom defn want this table to be sortable
+            bool bReturn = false;
+            if (t.Custom != null)
+            {
+                // Loop thru all the child nodes
+                foreach (XmlNode xNodeLoop in t.Custom.CustomXmlNode.ChildNodes)
+                {
+                    if (xNodeLoop.Name == "HTML")
+                    {
+                        if (xNodeLoop.LastChild.InnerText.ToLower() == "true")
+                        {
+                            bReturn = true;
+                        }
+                        break;
+                    }
+                }
+            }
+            return bReturn;
+        }
+
+        string SortType(TableCell tc, Textbox tb)
+        {
+            // return of null means don't sort
+            if (tb == null || !IsTableSortable(tc.OwnerTable))
+                return null;
+
+            // default is true if table is sortable;
+            //   but user may place override on Textbox custom tag
+            if (tb.Custom != null)
+            {
+                // Loop thru all the child nodes
+                foreach (XmlNode xNodeLoop in tb.Custom.CustomXmlNode.ChildNodes)
+                {
+                    if (xNodeLoop.Name == "HTML")
+                    {
+                        if (xNodeLoop.LastChild.InnerText.ToLower() == "false")
+                        {
+                            return null;
+                        }
+                        break;
+                    }
+                }
+            }
+
+            // Must find out the type of the detail column
+            Details d = tc.OwnerTable.Details;
+            if (d == null)
+                return null;
+            TableRow tr = d.TableRows.Items[0];
+            if (tr == null)
+                return null;
+            TableCell dtc = tr.TableCells.Items[tc.ColIndex];
+            if (dtc == null)
+                return null;
+            var dtb = dtc.ReportItems.Items[0] as Textbox;
+            if (dtb == null)
+                return null;
+
+            string sortcmp;
+            switch (dtb.Value.Type)
+            {
+                case TypeCode.DateTime:
+                    sortcmp = "sort_cmp_date";
+                    break;
+                case TypeCode.Int16:
+                case TypeCode.UInt16:
+                case TypeCode.Int32:
+                case TypeCode.UInt32:
+                case TypeCode.Int64:
+                case TypeCode.UInt64:
+                case TypeCode.Decimal:
+                case TypeCode.Single:
+                case TypeCode.Double:
+                    sortcmp = "sort_cmp_number";
+                    break;
+                case TypeCode.String:
+                    sortcmp = "sort_cmp_string";
+                    break;
+                case TypeCode.Empty: // Not a type we know how to sort
+                default:
+                    sortcmp = null;
+                    break;
+            }
+
+            return sortcmp;
+        }
+    }
+
+
+    internal class CssCacheEntry
+    {
+        public CssCacheEntry(string css, string name)
+        {
+            Css = css;
+            Name = name;
+        }
+
+        public string Css { get; set; }
+
+        public string Name { get; set; }
+    }
+}


Use a different assembly name each run (fixes mocking libraries that use cached Castle proxies)

--- src/RoslynPad.Roslyn/Scripting/ScriptRunner.cs
+++ src/RoslynPad.Roslyn/Scripting/ScriptRunner.cs
@@ -20,7 +20,8 @@
     /// </summary>
     public sealed class ScriptRunner
     {
-        private static readonly string _globalAssemblyNamePrefix = "\u211B\u2118*" + Guid.NewGuid();
+        private static readonly string _globalAssemblyNamePrefix = "\u211B\u2118*" + Guid.NewGuid() + "-";
+        private static int _assemblyNumber;

         private readonly InteractiveAssemblyLoader _assemblyLoader;
         private Func<object[], Task<object>> _lazyExecutor;
@@ -275,18 +276,20 @@
                 assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default
             );
             //.WithTopLevelBinderFlags(BinderFlags.IgnoreCorLibraryDuplicatedTypes),
-
+
+            var assemblyNumber = Interlocked.Increment(ref _assemblyNumber);
+
             if (OutputKind == OutputKind.ConsoleApplication || OutputKind == OutputKind.WindowsApplication)
             {
                 return CSharpCompilation.Create(
-                 _globalAssemblyNamePrefix,
+                 _globalAssemblyNamePrefix + assemblyNumber,
                  new[] { tree },
                  references,
                  compilationOptions);
             }

             return CSharpCompilation.CreateScriptCompilation(
-                    _globalAssemblyNamePrefix,
+                    _globalAssemblyNamePrefix + assemblyNumber,
                     tree,
                     references,
                     compilationOptions,


* DeserializeObjecTests for WinRT

* DeserializeObjecTests for WinRT

--- src/SimpleJson.Tests/DeserializeObjectTests.cs
+++ src/SimpleJson.Tests/DeserializeObjectTests.cs
@@ -438,7 +438,7 @@
             var json = "{\"PropTypeKnown\":\"str\",\"PropTypeUnknown\":{\"unknown\":\"property\"}}";
             var result = SimpleJson.DeserializeObject<ObjProp>(json);

-            Assert.NotNull(result);
+            Assert.IsNotNull(result);
             Assert.AreEqual("str", result.PropTypeKnown);

 #if SIMPLE_JSON_WINRT
@@ -449,7 +449,7 @@
             Assert.IsInstanceOf<JsonObject>(result.PropTypeUnknown);
 #endif
             var dict = (IDictionary<string, object>)result.PropTypeUnknown;
-            Assert.NotNull(dict);
+            Assert.IsNotNull(dict);
             Assert.AreEqual(1, dict.Count);
             Assert.AreEqual("property", dict["unknown"]);
         }


Delete LoziAnimationClip.cs

Delete LoziAnimationClip.cs

--- LoziExporter/Exporter/Animation/LoziAnimationClip.cs
+++ LoziExporter/Exporter/Animation/LoziAnimationClip.cs
@@ -1,250 +0,0 @@
-using UnityEngine;
-using UnityEditor;
-using System.Collections;
-using System.Collections.Generic;
-using Lozi.baseClasses;
-using Lozi.helpers;
-
-namespace Lozi
-{
-	public class LoziAnimationClip : HierarchyManager<HierarchyObject>
-	{
-		private string         clipName;
-		private string         wrapMode;
-		private AnimationClip  clip;
-		private float fps;
-		private float length;
-		private List<LoziAnimationKeyPoints> keys;
-
-		public LoziAnimationClip (AnimationClip clip, Transform target):base()
-		{
-			setTarget(target);
-
-			keys = new List<LoziAnimationKeyPoints>();
-
-			clipName = clip.name;
-			wrapMode = clip.wrapMode.ToString();
-			fps      = clip.frameRate;
-			length   = clip.length;
-
-			AnimationClipCurveData[] curvesArray = AnimationUtility.GetAllCurves (clip, true);
-
-			for(int num1 = 0; num1 < curvesArray.Length; num1++)
-			{
-				addToKeys(curvesArray[num1]);
-
-				Dictionary<string, object> curve = new Dictionary<string, object>();
-				curve["name"] = curvesArray[num1].propertyName;
-				curve["type"] = curvesArray[num1].type.Name;
-			}
-			mergeTimes();
-			getAnimationTargets();
-			sortObjectsByHierarchyIndex(rootObject);
-		}
-
-		public void getAnimationTargets()
-		{
-			unsortedObjects = new List<HierarchyObject>();
-			for(int num1 = 0; num1 < keys.Count; num1++)
-			{
-				string  path 		 = keys[num1].path;
-				string[] pathTargets = path.Split('/');
-
-				path = "";
-				for(int num2 = 0; num2 < pathTargets.Length; num2++)
-				{
-					path += "/"+pathTargets[num2];
-
-					if(path[0]=='/')
-					{
-						path = path.Substring(1);
-					}
-
-					Transform targetObj = baseObj.FindChild(path);
-
-					if(targetObj!=null)
-					{
-						if(!hasInUnsortedArray(targetObj))
-						{
-							HierarchyObject animated = new HierarchyObject();
-							animated.isRoot  		 = (path.Contains("/")) ? false : true;
-							animated.path            = path;
-							animated.setObject(targetObj);
-							unsortedObjects.Add(animated);
-						}
-					}
-				}
-			}
-		}
-
-		private void parsePoint(string vec, LoziAnimationKeyPoint points, Keyframe[] keys)
-		{
-			if(points.time==null)
-			{
-				points.size  = keys.Length;
-				points.time  = new float  [keys.Length];
-				points.point = new Vector4[keys.Length];
-			}
-
-			for(int num = 0; num < keys.Length; num++)
-			{
-				if(points.point[num]==null){points.point[num] = new Vector4();}
-
-				Vector4 temp = points.point[num];
-				switch(vec)
-				{
-					case "x":{points.point[num] = new Vector4(keys[num].value,temp.y,temp.z,temp.w); break;}
-					case "y":{points.point[num] = new Vector4(temp.x,temp.y,keys[num].value,temp.w); break;}
-					case "z":{points.point[num] = new Vector4(temp.x,keys[num].value,temp.z,temp.w); break;}
-					case "w":{points.point[num] = new Vector4(temp.x,temp.y,temp.z,keys[num].value); break;}
-				}
-				points.time[num] = keys[num].time;
-			}
-		}
-
-		private LoziAnimationKeyPoints getKeyPoint(string path)
-		{
-			LoziAnimationKeyPoints point = null;
-			for(int num = 0; num < keys.Count; num++)
-			{
-				if(keys[num].path == path)
-				{
-					point = keys[num];
-					break;
-				}
-			}
-			if(point==null)
-			{
-				point        = new LoziAnimationKeyPoints();
-				point.pos    = new LoziAnimationKeyPoint();
-				point.rot    = new LoziAnimationKeyPoint();
-				point.scl    = new LoziAnimationKeyPoint();
-				point.path   = path;
-				keys.Add(point);
-			}
-			return point;
-		}
-
-		private void addToKeys(AnimationClipCurveData curveData)
-		{
-			string prop  = curveData.propertyName.Split('.')[0];
-			string vec   = curveData.propertyName.Split('.')[1];
-			LoziAnimationKeyPoints point = getKeyPoint(curveData.path);
-
-			switch(prop)
-			{
-				case "m_LocalPosition":{parsePoint(vec,point.pos,curveData.curve.keys); break;}
-				case "m_LocalRotation":{parsePoint(vec,point.rot,curveData.curve.keys); break;}
-				case "m_LocalScale"	  :{parsePoint(vec,point.scl,curveData.curve.keys); break;}
-			}
-		}
-
-		private void mergeTimes()
-		{
-			for(int num1 = 0; num1 < keys.Count; num1++)
-			{
-				LoziAnimationKeyPoints point = keys[num1];
-				point.times = new List<float>();
-				if(point.pos.time!=null){for(int num2 = 0; num2 < point.pos.time.Length; num2++){if(!point.times.Contains(point.pos.time[num2])){point.times.Add(point.pos.time[num2]);}}}
-				if(point.rot.time!=null){for(int num2 = 0; num2 < point.rot.time.Length; num2++){if(!point.times.Contains(point.rot.time[num2])){point.times.Add(point.rot.time[num2]);}}}
-				if(point.scl.time!=null){for(int num2 = 0; num2 < point.scl.time.Length; num2++){if(!point.times.Contains(point.scl.time[num2])){point.times.Add(point.scl.time[num2]);}}}
-				point.times.Sort();
-			}
-		}
-
-		private float[] getPointArrayByTime(LoziAnimationKeyPoint keyPoint, float time,int length)
-		{
-			float[] arr = new float[length];
-
-			if(keyPoint.time!=null)
-			{
-				for(int num = 0; num < keyPoint.time.Length; num++)
-				{
-					if(time==keyPoint.time[num])
-					{
-						Vector4 point = keyPoint.point[num];
-
-						arr[0] = point.x;
-						arr[2] = point.y;
-						arr[1] = point.z;
-
-						if(length>3)
-						{
-							arr[3] = point.w;
-						}
-						return arr;
-					}
-				}
-			}
-			return null;
-		}
-
-		public List<Dictionary<string,object>> keysDictionary
-		{
-			get
-			{
-				List<Dictionary<string,object>> keysArr = new List<Dictionary<string, object>>();
-				for(int num1 = 0; num1 < keys.Count; num1++)
-				{
-					List<Dictionary<string,object>> timesArr = new List<Dictionary<string, object>>();
-					Dictionary<string,object> dict 			 = new Dictionary<string, object>();
-
-					Vector3 oldPos = Vector3.zero;
-					Vector3 oldScl = Vector3.one;
-					for(int num2 = 0; num2 < keys[num1].times.Count; num2++)
-					{
-						Dictionary<string,object> keyDict = new Dictionary<string, object>();
-						float[] pos						  = getPointArrayByTime(keys[num1].pos,keys[num1].times[num2],3);
-						float[] rot						  = getPointArrayByTime(keys[num1].rot,keys[num1].times[num2],4);
-						float[] scl						  = getPointArrayByTime(keys[num1].scl,keys[num1].times[num2],3);
-
-						keyDict["time"] = keys[num1].times[num2];
-						if(pos!=null){keyDict["pos"] = pos;}
-						if(rot!=null){keyDict["rot"] = rot;}
-						if(scl!=null){keyDict["scl"] = scl;}
-
-						timesArr.Add(keyDict);
-					}
-					dict["path"  ] = keys[num1].path;
-
-					dict["index" ] = getObjectIndexByPath(keys[num1].path);
-					dict["parent"] = getObjectParentIndexByPath(keys[num1].path);
-					dict["keys"  ] = timesArr;
-					keysArr.Add(dict);
-				}
-				return keysArr;
-			}
-		}
-
-		public Dictionary<string,object> clipDictionary
-		{
-			get
-			{
-				Dictionary<string,object> dict = new Dictionary<string, object>();
-				dict["name"  	] = clipName;
-				dict["fps"   	] = fps;
-				dict["length"	] = length;
-				dict["hierarchy"] = keysDictionary;
-
-				return dict;
-			}
-		}
-
-		override public void Dispose()
-		{
-			base.Dispose();
-
-			for(int num = 0; num < keys.Count; num++)
-			{
-				keys[num].Dispose();
-			}
-			keys.Clear();
-
-			keys     = null;
-			clipName = null;
-			wrapMode = null;
-			clip     = null;
-		}
-	}
-}
-


Refactoring - typo

Refactoring - typo

--- src/StatisticsRomania/StatisticsRomania/App.cs
+++ src/StatisticsRomania/StatisticsRomania/App.cs
@@ -17,7 +17,7 @@
     //  - maybe group indicators by category
     //  [cele de mai jos testate cu Nexus 5]
     //  - adaugă un "help" unde să ai explicații pentru toți termenii sau abrevierile folosite (eventual poți avea un buton de info "i" în colțul dreapta-sus al termenilor respectivi).
-    //  - adaugă search la lsita județelor, e destul de greu să scoll-ezi în 41 de județe.
+    //  - adaugă search la lista județelor, e destul de greu să scoll-ezi în 41 de județe.
     //  - zoom-in și zoom-out la grafice. O difentă între 155.000 și 158.000 pare imensă când axa Y pleacă de la 150.000 și se termină la 160.000.

     public class App : Application


:pig_nose: Remove double char case

:pig_nose: Remove double char case

--- src/StrangerData.SqlServer/SqlServerDialect.cs
+++ src/StrangerData.SqlServer/SqlServerDialect.cs
@@ -115,7 +115,6 @@
                         case "TEXT":
                         case "NTEXT":
                         case "VARCHAR":
-                        case "CHAR":
                             tableColumnInfo.ColumnType = ColumnType.String;
                             break;
                         case "ROWVERSION":


Fixed an exception being thrown when closing the application while a stream was running.

--- MarsRover/MarsRoverClient/Content/CameraViewModel.cs
+++ MarsRover/MarsRoverClient/Content/CameraViewModel.cs
@@ -148,7 +148,8 @@

         private void HandleFinishedPlaying(object sender, ReasonToFinishPlaying reason)
         {
-            Image = null;
+            if(reason == ReasonToFinishPlaying.StoppedByUser)
+                Image = null;
         }

         protected void OnPropertyChanged(string iPropertyName)


Changed angle values to speed values for camera panning / fixed on/offing cameras

--- MarsRover/RoverOperator/Gamepad/GamepadController.cs
+++ MarsRover/RoverOperator/Gamepad/GamepadController.cs
@@ -16,6 +16,7 @@
         private const int POLLING_RATE = 200; //milliseconds
         private const int STATE_CHECK_INTERVAL = 200;
         private const int BUTTON_PRESS_SAFE_INTERVAL = 1000;
+        Dictionary<int, CameraState> cameraStates;

         protected class CameraState
         {
@@ -29,6 +30,16 @@
             //TODO: Detect new controllers after start ?
             var controllers = new[] { new Controller(UserIndex.One), new Controller(UserIndex.Two) };
             StartPollingAndSendingCommands(controllers);
+
+            cameraStates = new Dictionary<int, CameraState>(5);
+            for (int i = 0; i < 5; i++)
+            {
+                CameraState cs = new CameraState();
+                cs.Active = false;
+                cs.Pan = 0;
+                cs.Tilt = 0;
+                cameraStates[i] = cs;
+            }
         }

         public void StartPollingAndSendingCommands(Controller[] controllers)
@@ -45,8 +56,8 @@
             {
                 logger.Debug("Found a XInput controller available");
                 var commandsQueue = new BlockingCollection<string>();
-                Thread t = new Thread(() => PollAndSendCameraCommands(controllers[1], commandsQueue));
-                Thread commandsSender = new Thread(() => ProcessCommandQueue(commandsQueue));
+                Thread t = new Thread(() => PollAndSendCameraCommands(controllers[1], commandsQueue, cameraStates));
+                Thread commandsSender = new Thread(() => ProcessCommandQueue(commandsQueue, cameraStates));
                 commandsSender.Start();
                 t.IsBackground = true;
                 t.Start();
@@ -117,22 +128,13 @@
             }
         }

-        private void PollAndSendCameraCommands(Controller controller, BlockingCollection<string> commands)
+        private void PollAndSendCameraCommands(Controller controller, BlockingCollection<string> commands, Dictionary<int, CameraState> cameraStates)
         {

             var previousState = controller.GetState();
             int selectedCamera = 0; //Default camera is broom
             int angleIncrement = 5;
-            Dictionary<int, CameraState> cameraStates = new Dictionary<int, CameraState>(5);
-            for (int i = 0; i < 5; i++) {
-                CameraState cs = new CameraState();
-                cs.Active = false;
-                cs.Pan = 0;
-                cs.Tilt = 0;
-                cameraStates[i] = cs;
-            }
-
-
+
             while (controller.IsConnected)
             {
                 var state = controller.GetState();
@@ -224,29 +226,12 @@
                 //Actually panning/tilting
                 if (state.Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadLeft)) //Left Pan
                 {
-                    if (selectedCamera == 0) //Broom camera - allows an angle of 000-359
-                    {
-                        if (cameraStates[selectedCamera].Pan >= 359 - angleIncrement)
-                            cameraStates[selectedCamera].Pan = 359;
-                        else
-                            cameraStates[selectedCamera].Pan += angleIncrement;
-                    }
-                    else //Any other camera - allows an angle of 000-180
-                    {
-                        if (cameraStates[selectedCamera].Pan >= 180 - angleIncrement)
-                            cameraStates[selectedCamera].Pan = 180;
-                        else
-                            cameraStates[selectedCamera].Pan += angleIncrement;
-                    }
-
+                    cameraStates[selectedCamera].Pan = 0;
                 }

                 else if (state.Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadRight)) //Right Pan
                 {
-                    if (cameraStates[selectedCamera].Pan <= angleIncrement)
-                        cameraStates[selectedCamera].Pan = 0;
-                    else
-                        cameraStates[selectedCamera].Pan -= angleIncrement;
+                    cameraStates[selectedCamera].Pan = 200;
                 }

                 else if (state.Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadUp)) //Up Tilt
@@ -270,14 +255,12 @@
                 {
                     string command = "<P" + selectedCamera + getPaddedInt(cameraStates[selectedCamera].Pan) + ">";
                     commands.Add(command.ToString());
-                    //logger.Debug(command);
                 }

                 else if (state.Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadUp) || state.Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadDown))
                 {
                     string command = "<T" + selectedCamera + getPaddedInt(cameraStates[selectedCamera].Tilt) + ">";
                     commands.Add(command.ToString());
-                    //logger.Debug(command);
                 }

                 previousState = state;
@@ -285,7 +268,7 @@
             }
         }

-        private void ProcessCommandQueue(BlockingCollection<string> commands)
+        private void ProcessCommandQueue(BlockingCollection<string> commands, Dictionary<int, CameraState> cameraStates)
         {
             var previousSelectedCamera = -1;
             var timePreviousCameraSelect = DateTime.Now;
@@ -297,6 +280,7 @@
                     var cameraNumber = getCameraNumberFromToggleCommand(command);
                     if (previousSelectedCamera == cameraNumber && (DateTime.Now - timePreviousCameraSelect).TotalMilliseconds < BUTTON_PRESS_SAFE_INTERVAL)
                     {
+                        cameraStates[cameraNumber].Active = !cameraStates[cameraNumber].Active;
                         logger.Debug("Ignored command " + command);
                         continue;
                     }
@@ -305,14 +289,15 @@
                         previousSelectedCamera = cameraNumber;
                         timePreviousCameraSelect = DateTime.Now;
                         CommandSender.Instance.UpdateCommand(command.ToString());
+                        Thread.Sleep(STATE_CHECK_INTERVAL);
                         logger.Debug(command.ToString());
-                        Thread.Sleep(STATE_CHECK_INTERVAL);
                     }
                 }
                 else
                 {
                     CommandSender.Instance.UpdateCommand(command.ToString());
                     Thread.Sleep(STATE_CHECK_INTERVAL);
+                    logger.Debug(command.ToString());
                 }
             }


Simplified some logic in SQLServerCertificateDatabase

--- MimeKit/Cryptography/SQLServerCertificateDatabase.cs
+++ MimeKit/Cryptography/SQLServerCertificateDatabase.cs
@@ -272,10 +272,10 @@
 				}

 				var value = GetValue (record, columns[i].ColumnName);
-				if (value.GetType () == typeof (DateTime))
-					value = ((DateTime) value < DateUtils.UnixEpoch) ? DateUtils.UnixEpoch : (DateTime) value;
-
-				if (columns[i].ColumnName == "PRIVATEKEY" && value.GetType () == typeof (DBNull))
+				if (value is DateTime dateTime && dateTime < DateUtils.UnixEpoch)
+					value = DateUtils.UnixEpoch;
+
+				if (columns[i].ColumnName == "PRIVATEKEY" && value is DBNull)
 					value = new byte[0];

 				var variable = "@" + columns[i];