Browse Source

change jsgantt to jsGanttImproved

Sergio Sanchis Climent 9 years ago
parent
commit
caf963351b

+ 242 - 53
htdocs/includes/jsgantt/jsgantt.css

@@ -1,53 +1,242 @@
-
-// These are the class/styles used by various objects in GanttChart.  However, Firefox has problems deciphering class style when DIVs are embedded in other DIVs.
-
-// GanttChart makes heavy use of embedded DIVS, thus the style are often embedded directly in the objects html.  If this could be resolved with Firefox, it would
-
-// make alot of the code look simpleer/cleaner without all the embedded styles
-
-
-
-..gantt { font-family:tahoma, arial, verdana; font-size:10px;}
-
-..gdatehead { BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; HEIGHT: 18px }
-
-..ghead { BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; WIDTH: 24px; HEIGHT: 20px }
-
-..gname { BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; WIDTH: 18px; HEIGHT: 18px }
-
-..ghead A { FONT-SIZE: 10px; COLOR: #000000; TEXT-DECORATION: none }
-
-..gheadwkend A { FONT-SIZE: 10px; COLOR: #000000; TEXT-DECORATION: none }
-
-..gheadwkend { BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; WIDTH: 24px; HEIGHT: 20px; background-color: #cfcfcf }
-
-..gfiller { BORDER-TOP: #efefef 1px solid; BORDER-LEFT: #efefef 1px solid; WIDTH: 18px; HEIGHT: 18px }
-
-..gfillerwkend { BORDER-LEFT: #efefef 1px solid; WIDTH: 18px; HEIGHT: 18px; BACKGROUND-COLOR: #cfcfcf }
-
-..gitem { BORDER-TOP: #cccccc 1px solid; WIDTH: 18px; HEIGHT: 18px }
-
-..gitemwkend { BORDER-TOP: #cccccc 1px solid; BORDER-LEFT: #cccccc 1px solid; WIDTH: 18px; HEIGHT: 18px }
-
-..gmilestone {	BORDER-TOP: #efefef 1px solid; FONT-SIZE: 14px; OVERFLOW: hidden; BORDER-LEFT: #efefef 1px solid; WIDTH: 18px; HEIGHT: 18px}
-
-..gmilestonewkend {	BORDER-TOP: #efefef 1px solid; BORDER-LEFT: #cccccc 1px solid; WIDTH: 18px; HEIGHT: 18px}
-
-..btn { BORDER-RIGHT: #ffffff; BORDER-TOP: #ffffff; FONT-WEIGHT: bold; FONT-SIZE: 10px; BORDER-LEFT: #ffffff; WIDTH: 12px; COLOR: #cccccc; BORDER-BOTTOM: #ffffff; BACKGROUND-COLOR: #ffffff }
-
-..hrcomplete { BORDER-RIGHT: #000000 2px solid; PADDING-RIGHT: 0px; BORDER-TOP: #000000 2px solid; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; BORDER-LEFT: #000000 2px solid; WIDTH: 20px; COLOR: #000000; PADDING-TOP: 0px; BORDER-BOTTOM: #000000 2px solid; HEIGHT: 4px }
-
-..hrhalfcomplete { BORDER-RIGHT: #000000 2px solid; BORDER-TOP: #000000 2px solid; BORDER-LEFT: #000000 2px solid; WIDTH: 9px; COLOR: #000000; BORDER-BOTTOM: #000000 2px solid; HEIGHT: 4px }
-
-..gweekend { font-family:tahoma, arial, verdana; font-size:11px; background-color:#EEEEEE; text-align:center; }
-
-..gtask { font-family:tahoma, arial, verdana; font-size:11px; background-color:#00FF00; text-align:center; }
-
-..gday { font-family:tahoma, arial, verdana; font-size:11px; text-align:center; }
-
-..gcomplete { background-color:black; height:5px; overflow: auto; margin-top:4px; }
-
-DIV.scroll { BORDER-RIGHT: #efefef 1px solid; PADDING-RIGHT: 0px; BORDER-TOP: #efefef 1px solid; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; OVERFLOW: hidden; BORDER-LEFT: #efefef 1px solid; WIDTH: 420px; PADDING-TOP: 0px; BORDER-BOTTOM: #efefef 1px solid; BACKGROUND-COLOR: #ffffff }
-
-DIV.scroll2 { position:relative; PADDING-RIGHT: 0px; overflow:auto ;overflow-x:scroll;overflow-y:hidden; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; WIDTH: 482px; PADDING-TOP: 0px; BACKGROUND-COLOR: #ffffff }
-
+/* Sample CSS for jsGanttImproved v1.7.3 */
+div.gantt		{ font-family:tahoma, arial, verdana, Sans-serif; font-size:10px; color: #2F2F2F; }
+
+.gantt table	{ border-collapse: collapse; }
+.gantt td { padding: 0px; }
+
+/* cell defaults */
+.gmajorheading,
+.gminorheading,
+.gminorheadingwkend,
+.gtaskcell,
+.gtaskcellwkend				{ height: 19px; font-size: 12px; border: #efefef 1px solid; text-align: center; cursor: default }
+.gtasklist					{ height: 19px; min-width: 5px; max-width: 5px; width: 5px; border: #efefef 1px solid; border-right: none; } /* all three width values set just to make sure - helps resizing code */
+
+/* Additional values for some cell elements */
+.gtaskheading,
+.gmajorheading,
+.gminorheading				{ background-color: #ffffff; font-weight: bold; font-size: 9px; white-space: nowrap; }
+.gtaskcellwkend,
+.gminorheadingwkend			{ background-color: #f7f7f7; font-weight: bold; font-size: 9px; white-space: nowrap; }
+td.gtaskcell				{ text-align: left }
+td.gspanning				{ border-left: none; border-right: none; }
+.gtaskcelldiv				{ position: relative; }
+
+/* Task list defaults */
+.gtaskheading,
+.gname,
+.gtaskname,
+.gresource,
+.gduration,
+.gpccomplete,
+.gstartdate,
+.genddate					{ height: 18px; white-space: nowrap; border: #efefef 1px solid; }
+
+/* Additional values for some task list elements */
+.gresource,
+.gduration,
+.gpccomplete,
+.gstartdate div, /* needed for IE8 */
+.gstartdate					{ text-align: center; min-width: 70px; max-width: 70px; width: 70px; font-size: 10px; }
+.genddate div, /* needed for IE8 */
+.genddate					{ text-align: center; min-width: 70px; max-width: 70px; width: 70px; font-size: 10px; }
+.gtaskheading				{ text-align: center; }
+.gtaskname div, /* needed for IE8 */
+.gtaskname					{ min-width: 170px; max-width: 170px; width: 170px;/* font-size: 9px;*/ border-left: none; }
+
+.gselector					{ text-align: left; white-space: nowrap; min-width: 170px; max-width: 170px; width: 170px; }
+
+.gformlabel					{ position:relative; top:0px; cursor:pointer; border: #ffffff 1px solid; margin-left: 2px; padding-left: 2px; padding-right: 2px; }
+span.gformlabel:hover,
+span.gselected				{ background-color: #dbecff; border: #cccccc 1px solid;}
+
+span.gfoldercollapse		{ color:#000000; cursor:pointer; font-weight:bold; font-size: 12px; font-family: Courier, "Courier New", monospace; }
+
+.gtasktableh,
+.gtasktable					{ border-right: #efefef 1px solid; }
+.gcharttable				{ border: #efefef 1px solid; }  /* for some reason firefox needs this */
+
+/* Differentiate Group, Milestone and Ordinary task items (applied to row) */
+.ggroupitem					{ background-color: #fbfbfb; font-weight: bold; }
+.gmileitem,
+.glineitem					{ background-color: #ffffff; }
+
+/* highlight row (applied to row) */
+.gitemhighlight	td			{ background-image: none; background-color: #fffaaa;}
+
+/* task bar caption text styles */
+.gmilecaption,
+.ggroupcaption,
+.gcaption					{ font-weight: normal; font-size: 9px; text-align: left; white-space: nowrap; top:1px; position: absolute; top:2px; }
+
+.ggroupcaption,
+.gcaption					{ right: -126px; }
+
+/* Task complete %age bar shared attributes */
+.gtaskcomplete				{ float:left; overflow: hidden; }
+
+/* Task complete %age bar */
+.gtaskcomplete				{ height:5px; background-color:#000000; margin-top:4px; opacity:0.4; filter: alpha(opacity=40); }
+
+/* Milestones */
+.gmilestone					{ font-size: 14px; position: absolute; top: -2px; }
+.gmdtop						{ top: 2px; overflow: hidden; width:0px; height:0px; border-bottom: 5px solid black; border-left: 5px solid transparent; border-top: 5px solid transparent; border-right: 5px solid transparent;}
+.gmdbottom					{ top: 2px; overflow: hidden; width:0px; height:0px; border-top: 5px solid black; border-left: 5px solid transparent; border-bottom: 5px solid transparent; border-right: 5px solid transparent;}
+
+/* Task bar shared attributes */
+.ggroupblack,
+.gtaskblue,
+.gtaskred,
+.gtaskgreen,
+.gtaskyellow,
+.gtaskpurple,
+.gtaskpink					{ height: 13px; filter: alpha(opacity=90); opacity:0.9; margin-top: 1px; }
+
+/* Task bars - ggroupblack is set as the default class on the task if it is undefined */
+.ggroupblack { height: 7px; background: #000000; margin-top: 2px; }
+.ggroupblackendpointleft	{ overflow: hidden; width:0px; height:0px; top: 2px; border-top: 4px solid black; border-left: 4px solid transparent; border-bottom: 4px solid transparent; border-right: 4px solid transparent; float: left; }
+.ggroupblackendpointright	{ overflow: hidden; width:0px; height:0px; top: 2px; border-top: 4px solid black; border-left: 4px solid transparent; border-bottom: 4px solid transparent; border-right: 4px solid transparent; float: right; }
+.ggroupblackcomplete		{ float:left; overflow: hidden; height:3px; filter: alpha(opacity=80); opacity:0.8; background-color:#777777; margin-top:2px; margin-bottom: 2px; }
+.gtaskblue {
+    background: rgb(58,132,195); /* Old browsers */
+    background: linear-gradient(to bottom,  rgba(58,132,195,1) 0%,rgba(65,154,214,1) 20%,rgba(75,184,240,1) 40%,rgba(58,139,194,1) 70%,rgba(38,85,139,1) 100%); /* W3C */
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4bb8f0', endColorstr='#3a84c3',GradientType=0 ); /* IE6-9 */
+}
+.gtaskred {
+    background: rgb(196,58,58); /* Old browsers */
+    background: linear-gradient(to bottom,  rgba(196,58,58,1) 0%,rgba(211,65,65,1) 20%,rgba(239,76,76,1) 40%,rgba(196,58,58,1) 70%,rgba(135,37,37,1) 100%); /* W3C */
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ef4c4c', endColorstr='#c43a3a',GradientType=0 ); /* IE6-9 */
+}
+.gtaskgreen {
+    background: rgb(80,193,58); /* Old browsers */
+    background: linear-gradient(to bottom,  rgba(80,193,58,1) 0%,rgba(88,209,64,1) 20%,rgba(102,237,75,1) 40%,rgba(80,193,58,1) 70%,rgba(53,132,37,1) 100%); /* W3C */
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#66ED4B', endColorstr='#50c13a',GradientType=0 ); /* IE6-9 */
+}
+.gtaskyellow {
+    background: rgb(247,228,56); /* Old browsers */
+    background: linear-gradient(to bottom,  rgba(247,228,56,1) 0%,rgba(239,239,55,1) 20%,rgba(255,255,58,1) 40%,rgba(242,236,55,1) 70%,rgba(241,218,54,1) 100%); /* W3C */
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffff3a', endColorstr='#f7e438',GradientType=0 ); /* IE6-9 */
+}
+.gtaskpurple {
+    background: rgb(193,58,193); /* Old browsers */
+    background: linear-gradient(to bottom,  rgba(193,58,193,1) 0%,rgba(211,65,211,1) 20%,rgba(239,76,239,1) 40%,rgba(193,58,193,1) 70%,rgba(137,38,137,1) 100%); /* W3C */
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ef4cef', endColorstr='#892689',GradientType=0 ); /* IE6-9 */
+}
+.gtaskpink {
+    background: rgb(249,177,245); /* Old browsers */
+    background: linear-gradient(to bottom,  rgba(249,177,245,1) 0%,rgba(247,192,243,1) 20%,rgba(247,202,244,1) 40%,rgba(249,192,246,1) 70%,rgba(252,174,247,1) 100%); /* W3C */
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f7caf4', endColorstr='#fcaef7',GradientType=0 ); /* IE6-9 */
+}
+.gtaskbluecomplete,
+.gtaskredcomplete,
+.gtaskgreencomplete,
+.gtaskyellowcomplete,
+.gtaskpurplecomplete,
+.gtaskpinkcomplete			{ float:left; overflow: hidden; height:5px; filter: alpha(opacity=40); opacity:0.4; background-color: #000000; margin-top:4px; }
+
+/* Printer friendly styles - we could use these all the time but they are not as pretty! */
+/* note that "@media print" is not supported in IE6 or 7.  Fully patched IE8 should be OK */
+@media print {
+    .ggroupblack { height:0px; border-top: 7px solid; border-color: #000000; }
+    .gtaskblue { height:0px; border-top: 13px solid; border-color: rgb(58,132,195); }
+    .gtaskred { height:0px; border-top: 13px solid; border-color: rgb(196,58,58); }
+    .gtaskgreen { height:0px; border-top: 13px solid; border-color: rgb(80,193,58); }
+    .gtaskyellow { height:0px; border-top: 13px solid; border-color: rgb(247,228,56); }
+    .gtaskpurple { height:0px; border-top: 13px solid; border-color: rgb(193,58,193); }
+    .gtaskpink { height:0px; border-top: 13px solid; border-color: rgb(249,177,245); }
+
+    .gtaskbluecomplete,
+    .gtaskredcomplete,
+    .gtaskgreencomplete,
+    .gtaskyellowcomplete,
+    .gtaskpurplecomplete,
+    .gtaskpinkcomplete { height:0px; filter: alpha(opacity=40); opacity:0.4; margin-top: -9px; border-top: 5px solid; border-color: #000000; }
+    .ggroupblackcomplete { height: 0px; filter: alpha(opacity=80); opacity:0.8; margin-top:-5px; border-top:3px solid; border-color:#777777; }
+}
+
+/*  END Task bar styles */
+.glinev { border-left: 1px solid; width: 0px; }
+.glineh { border-top: 1px solid; height: 0px; }
+
+.gDepFS,
+.gDepSS,
+.gDepSF,
+.gDepFF				{ border-color: #26558b; }
+.gDepFSArw,
+.gDepSSArw			{ overflow: hidden; width:0px; height:0px; border-bottom: 4px solid transparent; border-left: 4px solid #26558b; border-top: 4px solid transparent; border-right: 4px solid transparent;}
+.gDepFFArw,
+.gDepSFArw			{ overflow: hidden; width:0px; height:0px; border-bottom: 4px solid transparent; border-left: 4px solid transparent; border-top: 4px solid transparent; border-right: 4px solid #26558b;}
+.gCurDate			{ border-color: #0000ff; }
+
+
+div.gtaskbarcontainer		{ z-index: 1; position: absolute; top: 0px }
+
+.JSGanttToolTip		{position: absolute; display: block; z-index: 2;}
+.JSGanttToolTipcont	{font-family: tahoma, arial, verdana; font-size: 10px; display: block; background: #ffffff; color: #656565}
+.gTaskInfo			{background: #dbecff; width: 400px; border: #656565 1px solid; border-radius: 10px; padding: 4px 6px 4px 6px; float: left;}
+.gTtTitle			{display: block; font-size: 12px; font-weight: bold; color: #404040; margin-left: 4px; margin-bottom: 1em;}
+.gTaskLabel 		{font-size: 11px; font-weight: bold; color: #656565; margin-left: 4px;}
+.gTaskText	 		{position:absolute; left: 90px; padding-top: 1px; font-size: 10px; font-weight: normal; color: #656565;}
+.gTaskNotes 		{font-size: 11px; font-weight: normal; color: #323232; padding: 0 15px; display: block;}
+.gTIn	 			{padding-top: 10px;}
+
+.gantt				{ min-width: 1064px;	/* 2x LC width */ }
+.gchartcontainer	{ padding-left: 532px;	/* LC width */ }
+.gcontainercol		{ position: relative; float: left; } /* Add a max-height value here if wanted */
+.glistgrid			{ width: 532px; /* LC width */ margin-left: -100%; right: 532px; /* LC width */ padding-right: 0px; padding-left: 0px; padding-bottom: 0px; padding-top: 0px; background-color: #ffffff; overflow: hidden; }
+.glistlbl			{ width: 532px; /* LC width */ margin-left: -100%; right: 532px; /* LC width */ padding-right: 0px; padding-left: 0px; padding-bottom: 0px; padding-top: 0px; background-color: #ffffff; overflow: hidden; }
+.glabelfooter		{ clear: both; }
+.ggridfooter		{ clear: both; }
+
+/*.rhscrpad			{ width: 150px; position: absolute; top: 0px; height: 1px; }*/
+
+.gchartgrid			{ width: 100%; padding-right: 0px; padding-left: 0px; padding-bottom: 0px; padding-top: 0px; background-color: #ffffff; position: relative; overflow: auto; min-height: 0%; }
+.gchartlbl			{ width: 100%; padding-right: 0px; padding-left: 0px; padding-bottom: 0px; padding-top: 0px; background-color: #ffffff; position: relative; overflow: hidden; }
+
+/* Old Internet Explorer version hacks */
+.gantt						{ _height: 100% }	/* otherwise the chart disappears! */
+div .gantt					{ _width: 1064px; }	/* ie6 fixed width */
+div.gchartlbl,
+div.gchartgrid				{ _width: 532px; }	/* ie6 fixed width */
+div.glistlbl,
+div.glistgrid {
+    *right: 0px;			/* ie7 pulls the content too far left with the negative margin */
+    _right: 532px;			/* but ie6 fixed width needs this */
+    _margin-left: -532px;	/* ie6 fixed width */
+}
+div.gchartgrid { *padding-bottom: 20px; *overflow-y: hidden; } /* variable height design, no need for vertical scroll */
+td.gmajorheading div { *overflow: hidden; } /* stops resizing fixed width columns if the text is too wide */
+td.gspanning div { *overflow: hidden; } /* stops resizing fixed width columns if the text is too wide */
+
+/* border transparency tricks */
+.ggroupblackendpointleft { _border-top: 4px solid black; _border-left: 4px solid pink; _border-bottom: 4px solid pink; _border-right: 4px solid pink; _filter: chroma(color=pink); }
+.ggroupblackendpointright { _border-top: 4px solid black; _border-left: 4px solid pink; _border-bottom: 4px solid pink; _border-right: 4px solid pink;_filter: chroma(color=pink); }
+
+.gmdtop { _border-left: 5px solid pink; _border-top: 5px solid pink; _border-right: 5px solid pink; _filter: chroma(color=pink);}
+.gmdbottom { _border-left: 5px solid pink; _border-bottom: 5px solid pink; _border-right: 5px solid pink; _filter: chroma(color=pink);}
+
+.gDepFSArw,
+.gDepSSArw { _border-bottom: 4px solid pink; _border-top: 4px solid pink; _border-right: 4px solid pink; _filter: chroma(color=pink);}
+.gDepFFArw,
+.gDepSFArw { _border-bottom: 4px solid pink; _border-left: 4px solid pink; _border-top: 4px solid pink; _filter: chroma(color=pink);}
+
+/* Workaround for odd bug in old versions of Opera - no other browser needs this */
+.glinediv {position: absolute; top: 0px; left: 0px;}
+
+/* if using setUseSingleCell(1) the following is a suggested set of CSS3 styles to recreate the table grid - won't work on old browsers
+.ggrouphour td,
+.gmilehour td,
+.gitemhour td			{ background-size: 19px 1px; background-image: linear-gradient(to left, #efefef, transparent 1px, transparent 18px); width: 100%; height: 19px; }
+.ggroupday td,
+.gmileday td,
+.gitemday td			{ background-size: 19px 1px, 133px 1px; background-image: linear-gradient(to left, #efefef, transparent 1px, transparent 18px), linear-gradient(to left, #f7f7f7 39px, transparent 1px, transparent 92px); width: 100%; height: 19px; }
+.ggroupweek td,
+.gmileweek td,
+.gitemweek td			{ background-size: 37px 1px; background-image: linear-gradient(to left, #efefef, transparent 1px, transparent 36px); width: 100%; height: 19px; }
+.ggroupmonth td,
+.gmilemonth td,
+.gitemmonth td			{ background-size: 37px 1px; background-image: linear-gradient(to left, #efefef, transparent 1px, transparent 36px); width: 100%; height: 19px; }
+.ggroupquarter td,
+.gmilequarter td,
+.gitemquarter td		{ background-size: 19px 1px; background-image: linear-gradient(to left, #efefef, transparent 1px, transparent 18px); width: 100%; height: 19px; }
+*/

+ 2398 - 2138
htdocs/includes/jsgantt/jsgantt.js

@@ -1,2315 +1,2575 @@
 /*
-Copyright (c) 2009, Shlomy Gantz BlueBrick Inc. All rights reserved.
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions are met:
-*     * Redistributions of source code must retain the above copyright
-*       notice, this list of conditions and the following disclaimer.
-*     * Redistributions in binary form must reproduce the above copyright
-*       notice, this list of conditions and the following disclaimer in the
-*       documentation and/or other materials provided with the distribution.
-*     * Neither the name of Shlomy Gantz or BlueBrick Inc. nor the
-*       names of its contributors may be used to endorse or promote products
-*       derived from this software without specific prior written permission.
-*
-* THIS SOFTWARE IS PROVIDED BY SHLOMY GANTZ/BLUEBRICK INC. ''AS IS'' AND ANY
-* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-* DISCLAIMED. IN NO EVENT SHALL SHLOMY GANTZ/BLUEBRICK INC. BE LIABLE FOR ANY
-* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-* 
-* LDR Modified to replace hard coded values by i18[key]
-*/
-
-/**
-* JSGantt component is a UI control that displays gantt charts based by using CSS and HTML 
-* @module    jsgantt
-* @title    JSGantt
-*/
-
-var JSGantt; if (!JSGantt) JSGantt = {};
-
-var vTimeout = 0;
-var vBenchTime = new Date().getTime();
-
-/**
-* Creates a task (one row) in gantt object
-* @class TaskItem 
-* @namespace JSGantt
-* @constructor
-* @for JSGantt
-
-* @param pID {Number} Task unique numeric ID
-* @param pName {String} Task Name
-* @param pStart {Date} Task start date/time (not required for pGroup=1 )
-* @param pEnd {Date} Task end date/time, you can set the end time to 12:00 to indicate half-day (not required for pGroup=1 )
-* @param pColor {String} Task bar RGB value
-* @param pLink {String} Task URL, clicking on the task will redirect to this url. Leave empty if you do not with the Task also serve as a link
-* @param pMile {Boolean} Determines whether task is a milestone (1=Yes,0=No)
-* @param pRes {String} Resource to perform the task
-* @param pComp {Number} Percent complete (Number between 0 and 100)
-* @param pGroup {Boolean}
-* @param pParent {Number} ID of the parent task
-* @param pOpen {Boolean}
-* @param pDepend {String} Comma seperated list of IDs this task depends on
-* @param pCaption {String} Caption to be used instead of default caption (Resource). 
-* note : you should use setCaption("Caption") in order to display the caption
-* @return void
-*/
-JSGantt.TaskItem = function(pID, pName, pStart, pEnd, pColor, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption)
+ _   ___  _____   _  _____ ____   ____
+ (_) / _ \ \_   \ / ||___  | ___| |___ \
+ | |/ /_\/  / /\/ | |   / /|___ \   __) |
+ | / /_\\/\/ /_   | |_ / /_ ___) | / __/
+ _/ \____/\____/   |_(_)_/(_)____(_)_____|
+ |__/
+ jsGanttImproved 1.7.5.2
+ Copyright (c) 2013-2016, Paul Geldart All rights reserved.
+
+ The current version of this code can be found at https://code.google.com/p/jsgantt-improved/
+
+ * Copyright (c) 2013-2016, Paul Geldart.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Paul Geldart nor the names of its contributors
+ *       may be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PAUL GELDART. ''AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL PAUL GELDART BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ This project is based on jsGantt 1.2, (which can be obtained from
+ https://code.google.com/p/jsgantt/) and remains under the original BSD license.
+ The original project license follows:
+
+ Copyright (c) 2009, Shlomy Gantz BlueBrick Inc. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Shlomy Gantz or BlueBrick Inc. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SHLOMY GANTZ/BLUEBRICK INC. ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL SHLOMY GANTZ/BLUEBRICK INC. BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+var JSGantt; if (!JSGantt) JSGantt={};
+
+var vBenchTime=new Date().getTime();
+
+JSGantt.isIE=function ()
 {
-
-/**
-* The name of the attribute.
-* @property vID 
-* @type String 
-* @default pID
-* @private
-*/    
-var vID    = pID;
-
-/**
-* @property vName 
-* @type String 
-* @default pName
-* @private
-*/   
-var vName  = pName;
-
-/**
-* @property vStart 
-* @type Datetime 
-* @default new Date()
-* @private
-*/    
-var vStart = new Date();	
-
-/**
-* @property vEnd 
-* @type Datetime 
-* @default new Date()
-* @private
-*/    
-var vEnd   = new Date();
-
-/**
-* @property vColor 
-* @type String 
-* @default pColor
-* @private
-*/    
-var vColor = pColor;
-
-/**
-* @property vLink 
-* @type String 
-* @default pLink
-* @private
-*/    
-var vLink  = pLink;
-
-/**
-* @property vMile 
-* @type Boolean 
-* @default pMile
-* @private
-*/    
-var vMile  = pMile;
-
-/**
-* @property vRes 
-* @type String 
-* @default pRes
-* @private
-*/    
-var vRes   = pRes;
-
-/**
-* @property vComp 
-* @type Number 
-* @default pComp
-* @private
-*/    
-var vComp  = pComp;
-
-/**
-* @property vGroup 
-* @type Boolean 
-* @default pGroup
-* @private
-*/    
-var vGroup = pGroup;
-
-/**
-* @property vParent 
-* @type Number 
-* @default pParent
-* @private
-*/    
-var vParent = pParent;
-
-/**
-* @property vOpen 
-* @type Boolean 
-* @default pOpen
-* @private
-*/    
-var vOpen   = pOpen;
-
-/**
-* @property vDepend 
-* @type String 
-* @default pDepend
-* @private
-*/    
-var vDepend = pDepend;
-
-/**
-* @property vCaption 
-* @type String 
-* @default pCaption
-* @private
-*/    
-var vCaption = pCaption;
-
-/**
-* @property vDuration 
-* @type Number 
-* @default ''
-* @private
-*/    
-var vDuration = '';
-
-/**
-* @property vLevel 
-* @type Number 
-* @default 0
-* @private
-*/    
-var vLevel = 0;
-
-/**
-* @property vNumKid 
-* @type Number 
-* @default 0
-* @private
-*/   
-var vNumKid = 0;
-
-/**
-* @property vVisible 
-* @type Boolean 
-* @default 0
-* @private
-*/   
-var vVisible  = 1;
-      var x1, y1, x2, y2;
-
-
-      if (vGroup != 1)
-      {  
-         vStart = JSGantt.parseDateStr(pStart,g.getDateInputFormat());
-         vEnd   = JSGantt.parseDateStr(pEnd,g.getDateInputFormat());
-      }
-/**
-* Returns task ID
-* @method getID
-* @return {Number}
-*/
-      this.getID       = function(){ return vID };
-/**
-* Returns task name
-* @method getName
-* @return {String}
-*/
-      this.getName     = function(){ return vName };
-/**
-* Returns task start date
-* @method getStart
-* @return {Datetime}
-*/
-      this.getStart    = function(){ return vStart};
-/**
-* Returns task end date
-* @method getEnd
-* @return {Datetime}
-*/    this.getEnd      = function(){ return vEnd  };
-
-/**
-* Returns task bar color (i.e. 00FF00)
-* @method getColor
-* @return {String}
-*/    this.getColor    = function(){ return vColor};
-
-/**
-* Returns task URL (i.e. http://www.jsgantt.com)
-* @method getLink
-* @return {String}
-*/    this.getLink     = function(){ return vLink };
-
-/**
-* Returns whether task is a milestone (1=Yes,0=No)
-* @method getMile
-* @return {Boolean}
-*/    this.getMile     = function(){ return vMile };
-
-/**
-* Returns task dependencies as list of values (i.e. 123,122)
-* @method getDepend
-* @return {String}
-*/    this.getDepend   = function(){ if(vDepend) return vDepend; else return null };
-
-/**
-* Returns task caption (if it exists)
-* @method getCaption
-* @return {String}
-*/    this.getCaption  = function(){ if(vCaption) return vCaption; else return ''; };
-
-/**
-* Returns task resource name as string
-* @method getResource
-* @return {String}
-*/    this.getResource = function(){ if(vRes) return vRes; else return '&nbsp';  };
-
-/**
-* Returns task completion percent as numeric value
-* @method getCompVal
-* @return {Boolean}
-*/    this.getCompVal  = function(){ if(vComp) return vComp; else return 0; };
-
-/**
-* Returns task completion percent as formatted string (##%)
-* @method getCompStr
-* @return {String}
-*/    this.getCompStr  = function(){ if(vComp) return vComp+'%'; else return ''; };
-
-/**
-* Returns task duration as a fortmatted string based on the current selected format
-* @method getDuration
-* @param vFormat {String} selected format (minute,hour,day,week,month)
-* @return {String}
-*/ 	  this.getDuration = function(vFormat){ 
-         if (vMile) 
-            vDuration = '-';
-            else if (vFormat=='hour')
-            {
-                tmpPer =  Math.ceil((this.getEnd() - this.getStart()) /  ( 60 * 60 * 1000) );
-                if(tmpPer == 1)  
-                    vDuration = '1 '+i18n["sHour"];
-                else
-                    vDuration = tmpPer + ' '+i18n["sHours"];
-            }
-            
-            else if (vFormat=='minute')
-            {
-                tmpPer =  Math.ceil((this.getEnd() - this.getStart()) /  ( 60 * 1000) );
-                if(tmpPer == 1)  
-                    vDuration = '1 '+i18n["sMinute"];
-                else
-                    vDuration = tmpPer + ' '+i18n["sMinutes"];
-            }
-            
- 		   else { //if(vFormat == 'day') {
-            tmpPer =  Math.ceil((this.getEnd() - this.getStart()) /  (24 * 60 * 60 * 1000) + 1);
-            if(tmpPer == 1)  vDuration = '1 '+i18n["sDay"];
-            else             vDuration = tmpPer + ' '+i18n["sDays"];
-         }
-
-         //else if(vFormat == 'week') {
-         //   tmpPer =  ((this.getEnd() - this.getStart()) /  (24 * 60 * 60 * 1000) + 1)/7;
-         //   if(tmpPer == 1)  vDuration = '1 Week';
-         //   else             vDuration = tmpPer + ' Weeks'; 
-         //}
-
-         //else if(vFormat == 'month') {
-         //   tmpPer =  ((this.getEnd() - this.getStart()) /  (24 * 60 * 60 * 1000) + 1)/30;
-         //   if(tmpPer == 1) vDuration = '1 Month';
-         //   else            vDuration = tmpPer + ' Months'; 
-         //}
-
-         //else if(vFormat == 'quater') {
-         //   tmpPer =  ((this.getEnd() - this.getStart()) /  (24 * 60 * 60 * 1000) + 1)/120;
-         //   if(tmpPer == 1) vDuration = '1 Qtr';
-         //   else            vDuration = tmpPer + ' Qtrs'; 
-         //}
-         return( vDuration )
-      };
-
-/**
-* Returns task parent ID
-* @method getParent
-* @return {Number}
-*/      this.getParent   = function(){ return vParent };
-
-/**
-* Returns whether task is a group (1=Yes,0=No)
-* @method getGroup
-* @return {Number}
-*/    this.getGroup    = function(){ return vGroup };
-
-/**
-* Returns whether task is open (1=Yes,0=No)
-* @method getOpen
-* @return {Boolean}
-*/    this.getOpen     = function(){ return vOpen };
-
-/**
-* Returns task tree level (0,1,2,3...)
-* @method getLevel
-* @return {Boolean}
-*/    this.getLevel    = function(){ return vLevel };
-
-/**
-* Returns the number of child tasks
-* @method getNumKids
-* @return {Number}
-*/    this.getNumKids  = function(){ return vNumKid };
-  /**
-* Returns the X position of the left side of the task bar on the graph (right side)
-* @method getStartX
-* @return {Number}
-*/    this.getStartX   = function(){ return x1 };
-
-/**
-* Returns the Y position of the top of the task bar on the graph (right side)
-* @method getStartY
-* @return {Number}
-*/    this.getStartY   = function(){ return y1 };
-
-/**
-* Returns the X position of the right of the task bar on the graph (right side)
-* @method getEndX
-* @return {Int}
-*/    this.getEndX     = function(){ return x2 };
-
-/**
-* Returns the Y position of the bottom of the task bar on the graph (right side)
-* @method getEndY
-* @return {Number}
-*/    this.getEndY     = function(){ return y2 };
-
-/**
-* Returns whether task is visible  (1=Yes,0=No)
-* @method getVisible
-* @return {Boolean}
-*/    this.getVisible  = function(){ return vVisible };
-
-/**
-* Set task dependencies
-* @method setDepend
-* @param pDepend {String} A comma delimited list of task IDs the current task depends on.
-* @return {void}
-*/  this.setDepend   = function(pDepend){ vDepend = pDepend;};
-
-/**
-* Set task start date/time
-* @method setStart
-* @param pStart {Datetime} 
-* @return {void}
-*/    this.setStart    = function(pStart){ vStart = pStart;};
-
-/**
-* Set task end date/time
-* @method setEnd
-* @param pEnd {Datetime}
-* @return {void}
-*/    this.setEnd      = function(pEnd)  { vEnd   = pEnd;  };
-
-/**
-* Set task tree level (0,1,2,3...)
-* @method setLevel
-* @param pLevel {Number} 
-* @return {void}
-*/    this.setLevel    = function(pLevel){ vLevel = pLevel;};
-
-/**
-* Set Number of children for the task
-* @method setNumKid
-* @param pNumKid {Number}
-* @return {void}
-*/    this.setNumKid   = function(pNumKid){ vNumKid = pNumKid;};
-
-/**
-* Set task completion percentage
-* @method setCompVal
-* @param pCompVal {Number} 
-* @return {void}
-*/    this.setCompVal  = function(pCompVal){ vComp = pCompVal;};
-
-/**
-* Set a task bar starting position (left)
-* @method setStartX
-* @param pX {Number} 
-* @return {void}
-*/    this.setStartX   = function(pX) {x1 = pX; };
-
-/**
-* Set a task bar starting position (top)
-* @method setStartY
-* @param pY {Number} 
-* @return {String}
-*/    this.setStartY   = function(pY) {y1 = pY; };
-
-/**
-* Set a task bar starting position (right)
-* @method setEndX
-* @param pX {Number} 
-* @return {String}
-*/    this.setEndX     = function(pX) {x2 = pX; };
-
-/**
-* Set a task bar starting position (bottom)
-* @method setEndY
-* @param pY {Number} 
-* @return {String}
-*/    this.setEndY     = function(pY) {y2 = pY; };
-
-/**
-* Set task open/closed
-* @method setOpen
-* @param pOpen {Boolean} 
-* @return {void}
-*/    this.setOpen     = function(pOpen) {vOpen = pOpen; };
-
-/**
-* Set task visibility
-* @method setVisible
-* @param pVisible {Boolean} 
-* @return {void}
-*/    this.setVisible  = function(pVisible) {vVisible = pVisible; };
-
+    if(typeof document.all!='undefined')
+    {
+        if ('pageXOffset' in window) return false;	// give IE9 and above the benefit of the doubt!
+        else return true;
+    }
+    else return false;
 };
-	
-	
-/**
-* Creates the gant chart. for example:
-
-<p>var g = new JSGantt.GanttChart('g',document.getElementById('GanttChartDIV'), 'day');</p>
- 
-var g = new JSGantt.GanttChart( - assign the gantt chart to a javascript variable called 'g'
-'g' - the name of the variable that was just assigned (will be used later so that gantt object can reference itself)
-document.getElementById('GanttChartDIV') - reference to the DIV that will hold the gantt chart
-'day' - default format will be by day
-
-*
-* @class GanttChart 
-* @param pGanttVar {String} the name of the gantt chart variable
-* @param pDiv {String} reference to the DIV that will hold the gantt chart
-* @param pFormat {String} default format (minute,hour,day,week,month,quarter)
-* @return void
-*/
-
-JSGantt.GanttChart =  function(pGanttVar, pDiv, pFormat)
+
+JSGantt.TaskItem=function(pID, pName, pStart, pEnd, pClass, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGantt)
 {
-/**
-* The name of the gantt chart variable
-* @property vGanttVar 
-* @type String 
-* @default pGanttVar
-* @private
-*/ var vGanttVar = pGanttVar;
-/**
-* The name of the gantt chart DIV
-* @property vDiv 
-* @type String 
-* @default pDiv
-* @private
-*/  var vDiv      = pDiv;
-/**
-* Selected format (minute,hour,day,week,month)
-* @property vFormat 
-* @type String 
-* @default pFormat
-* @private
-*/ var vFormat   = pFormat;
-/**
-* Show resource column 
-* @property vShowRes 
-* @type Number 
-* @default 1
-* @private
-*/ var vShowRes  = 1;
-/**
-* Show duration column 
-* @property vShowDur 
-* @type Number 
-* @default 1
-* @private
-*/ var vShowDur  = 1;
-/**
-* Show percent complete column 
-* @property vShowComp 
-* @type Number 
-* @default 1
-* @private
-*/ var vShowComp = 1;
-/**
-* Show start date column 
-* @property vShowStartDate 
-* @type Number 
-* @default 1
-* @private
-*/ var vShowStartDate = 1;
-/**
-* Show end date column 
-* @property vShowEndDate 
-* @type Number 
-* @default 1
-* @private
-*/ var vShowEndDate = 1;
-/**
-* Date input format 
-* @property vDateInputFormat 
-* @type String 
-* @default "mm/dd/yyyy"
-* @private
-*/var vDateInputFormat = "mm/dd/yyyy";
-/**
-* Date display format 
-* @property vDateDisplayFormat 
-* @type String 
-* @default "mm/dd/yy"
-* @private
-*/var vDateDisplayFormat = "mm/dd/yy";
-
-	  var vNumUnits  = 0;
-      var vCaptionType;
-      var vDepId = 1;
-      var vTaskList     = new Array();	
-	  var vFormatArr	= new Array("day","week","month","quarter");
-      var vQuarterArr   = new Array(1,1,1,2,2,2,3,3,3,4,4,4);
-      var vMonthDaysArr = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
-      var vMonthArr     = new Array(i18n["January"],i18n["February"],i18n["March"],i18n["April"],i18n["May"],i18n["June"],i18n["July"],i18n["August"],i18n["September"],i18n["October"],i18n["November"],i18n["December"]);
-
-      
-/**
-* Set current display format (minute/hour/day/week/month/quarter)
-* Only the first 4 arguments are used, for example:
-* <code>
-* g.setFormatArr("day","week","month");
-* </code>
-* will show 3 formatting options (day/week/month) at the bottom right of the gantt chart
-* @method setFormatArr
-* @return {void}
-*/ this.setFormatArr = function() 	 {
-										  vFormatArr = new Array();
-										  for(var i = 0; i < arguments.length; i++) {vFormatArr[i] = arguments[i];}
-										  if(vFormatArr.length>4){vFormatArr.length=4;}
-										 };
-/**
-* Show/Hide resource column
-* @param pShow {Number} 1=Show,0=Hide
-* @method setShowRes
-* @return {void}
-*/ this.setShowRes  = function(pShow) { vShowRes  = pShow; };
-/**
-* Show/Hide duration column
-* @param pShow {Number} 1=Show,0=Hide
-* @method setShowDur
-* @return {void}
-*/ this.setShowDur  = function(pShow) { vShowDur  = pShow; };
-/**
-* Show/Hide completed column
-* @param pShow {Number} 1=Show,0=Hide
-* @method setShowComp
-* @return {void}
-*/ this.setShowComp = function(pShow) { vShowComp = pShow; };
-/**
-* Show/Hide start date column
-* @param pShow {Number} 1=Show,0=Hide
-* @method setShowStartDate
-* @return {void}
-*/ this.setShowStartDate = function(pShow) { vShowStartDate = pShow; };
-/**
-* Show/Hide end date column
-* @param pShow {Number} 1=Show,0=Hide
-* @method setShowEndDate
-* @return {void}
-*/ this.setShowEndDate = function(pShow) { vShowEndDate = pShow; };
-/**
-* Overall date input format 
-* @param pShow {String} (mm/dd/yyyy,dd/mm/yyyy,yyyy-mm-dd)
-* @method setDateInputFormat
-* @return {void}
-*/      this.setDateInputFormat = function(pShow) { vDateInputFormat = pShow; };
-/**
-* Overall date display format 
-* @param pShow {String} (mm/dd/yyyy,dd/mm/yyyy,yyyy-mm-dd)
-* @method setDateDisplayFormat
-* @return {void}
-*/      this.setDateDisplayFormat = function(pShow) { vDateDisplayFormat = pShow; };
-/**
-* Set gantt caption
-* @param pType {String} 
-<p>Caption-Displays a custom caption set in TaskItem<br>
-Resource-Displays task resource<br>
-Duration-Displays task duration<br>
-Complete-Displays task percent complete</p>
-* @method setCaptionType
-* @return {void}
-*/  this.setCaptionType = function(pType) { vCaptionType = pType };
-/**
-* Set current display format and redraw gantt chart (minute/hour/day/week/month/quarter)
-* @param pFormat {String} (mm/dd/yyyy,dd/mm/yyyy,yyyy-mm-dd)
-* @method setFormat
-* @return {void}
-*/ this.setFormat = function(pFormat){ 
-         vFormat = pFormat; 
-         this.Draw(); 
-      };
-/**
-* Returns whether resource column is shown
-* @method getShowRes
-* @return {Number}
-*/  this.getShowRes  = function(){ return vShowRes };
-/**
-* Returns whether duration column is shown
-* @method getShowDur
-* @return {Number}
-*/  this.getShowDur  = function(){ return vShowDur };
-/**
-* Returns whether percent complete column is shown
-* @method getShowComp
-* @return {Number}
-*/  this.getShowComp = function(){ return vShowComp };
-/**
-* Returns whether start date column is shown
-* @method getShowStartDate
-* @return {Number}
-*/  this.getShowStartDate = function(){ return vShowStartDate };
-/**
-* Returns whether end date column is shown
-* @method getShowEndDate
-* @return {Number}
-*/  this.getShowEndDate = function(){ return vShowEndDate };
-/**
-* Returns date input format 
-* @method getDateInputFormat
-* @return {String}
-*/  this.getDateInputFormat = function() { return vDateInputFormat };
-/**
-* Returns current display format
-* @method getDateDisplayFormat
-* @return {String}
-*/  this.getDateDisplayFormat = function() { return vDateDisplayFormat };
-/**
-* Returns current gantt caption type
-* @method getCaptionType
-* @return {String}
-*/  this.getCaptionType = function() { return vCaptionType };
-/**
-* Calculates X/Y coordinates of a task and sets the Start and End properties of the TaskItem
-* @method CalcTaskXY
-* @return {Void}
-*/  this.CalcTaskXY = function () 
-      {
-         var vList = this.getList();
-         var vTaskDiv;
-         var vParDiv;
-         var vLeft, vTop, vHeight, vWidth;
-
-         for(i = 0; i < vList.length; i++)
-         {
-            vID = vList[i].getID();
-            vTaskDiv = document.getElementById("taskbar_"+vID);
-            vBarDiv  = document.getElementById("bardiv_"+vID);
-            vParDiv  = document.getElementById("childgrid_"+vID);
-
-            if(vBarDiv) {
-               vList[i].setStartX( vBarDiv.offsetLeft );
-               vList[i].setStartY( vParDiv.offsetTop+vBarDiv.offsetTop+6 );
-               vList[i].setEndX( vBarDiv.offsetLeft + vBarDiv.offsetWidth );
-               vList[i].setEndY( vParDiv.offsetTop+vBarDiv.offsetTop+6 );
-            };
-         };
-      };
-
-/**
-* Adds a TaskItem to the Gantt object task list array
-* @method AddTaskItem
-* @return {Void}
-*/  this.AddTaskItem = function(value)
-      {
-         vTaskList.push(value);
-      };
-/**
-* Returns task list Array
-* @method getList
-* @return {Array}
-*/ this.getList   = function() { return vTaskList };
-
-/**
-* Clears dependency lines between tasks
-* @method clearDependencies
-* @return {Void}
-*/ this.clearDependencies = function()
-      {
-         var parent = document.getElementById('rightside');
-         var depLine;
-         var vMaxId = vDepId;
-         for ( i=1; i<vMaxId; i++ ) {
-            depLine = document.getElementById("line"+i);
-            if (depLine) { parent.removeChild(depLine); }
-         };
-         vDepId = 1;
-      };
-/**
-* Draw a straight line (colored one-pixel wide DIV), need to parameterize doc item
-* @method sLine
-* @return {Void}
-*/  this.sLine = function(x1,y1,x2,y2) {
-
-         vLeft = Math.min(x1,x2);
-         vTop  = Math.min(y1,y2);
-         vWid  = Math.abs(x2-x1) + 1;
-         vHgt  = Math.abs(y2-y1) + 1;
-
-         vDoc = document.getElementById('rightside');
-
-		 // retrieve DIV
-		 var oDiv = document.createElement('div');
-	
-		 oDiv.id = "line"+vDepId++;
-			 oDiv.style.position = "absolute";
-		 oDiv.style.margin = "0px";
-		 oDiv.style.padding = "0px";
-		 oDiv.style.overflow = "hidden";
-		 oDiv.style.border = "0px";
-
-		 // set attributes
-		 oDiv.style.zIndex = 0;
-		 oDiv.style.backgroundColor = "red";
-		
-		 oDiv.style.left = vLeft + "px";
-		 oDiv.style.top = vTop + "px";
-		 oDiv.style.width = vWid + "px";
-		 oDiv.style.height = vHgt + "px";
-	
-		 oDiv.style.visibility = "visible";
-		
-		 vDoc.appendChild(oDiv);
-
-      };
-
-/**
-* Draw a diaganol line (calc line x,y pairs and draw multiple one-by-one sLines)
-* @method dLine
-* @return {Void}
-*/  this.dLine = function(x1,y1,x2,y2) {
-
-         var dx = x2 - x1;
-         var dy = y2 - y1;
-         var x = x1;
-         var y = y1;
-
-         var n = Math.max(Math.abs(dx),Math.abs(dy));
-         dx = dx / n;
-         dy = dy / n;
-         for ( i = 0; i <= n; i++ )
-         {
-            vx = Math.round(x); 
-            vy = Math.round(y);
-            this.sLine(vx,vy,vx,vy);
-            x += dx;
-            y += dy;
-         };
-
-      };
-
-/**
-* Draw dependency line between two points (task 1 end -> task 2 start)
-* @method drawDependency
-* @return {Void}
-*/ this.drawDependency =function(x1,y1,x2,y2)
-      {
-         if(x1 + 10 < x2)
-         { 
-            this.sLine(x1,y1,x1+4,y1);
-            this.sLine(x1+4,y1,x1+4,y2);
-            this.sLine(x1+4,y2,x2,y2);
-            this.dLine(x2,y2,x2-3,y2-3);
-            this.dLine(x2,y2,x2-3,y2+3);
-            this.dLine(x2-1,y2,x2-3,y2-2);
-            this.dLine(x2-1,y2,x2-3,y2+2);
-         }
-         else
-         {
-            this.sLine(x1,y1,x1+4,y1);
-            this.sLine(x1+4,y1,x1+4,y2-10);
-            this.sLine(x1+4,y2-10,x2-8,y2-10);
-            this.sLine(x2-8,y2-10,x2-8,y2);
-            this.sLine(x2-8,y2,x2,y2);
-            this.dLine(x2,y2,x2-3,y2-3);
-            this.dLine(x2,y2,x2-3,y2+3);
-            this.dLine(x2-1,y2,x2-3,y2-2);
-            this.dLine(x2-1,y2,x2-3,y2+2);
-         }
-      };
-
-/**
-* Draw all task dependencies 
-* @method DrawDependencies
-* @return {Void}
-*/  this.DrawDependencies = function () {
-
-         //First recalculate the x,y
-         this.CalcTaskXY();
-
-         this.clearDependencies();
-
-         var vList = this.getList();
-         for(var i = 0; i < vList.length; i++)
-         {
-
-            vDepend = vList[i].getDepend();
-            if(vDepend) {
-         
-               var vDependStr = vDepend + '';
-               var vDepList = vDependStr.split(',');
-               var n = vDepList.length;
-
-               for(var k=0;k<n;k++) {
-                  var vTask = this.getArrayLocationByID(vDepList[k]);
-
-                  if(vList[vTask].getVisible()==1)
-                     this.drawDependency(vList[vTask].getEndX(),vList[vTask].getEndY(),vList[i].getStartX()-1,vList[i].getStartY())
-               }
-  	    }
-         }
-      };
-
-/**
-* Find location of TaskItem based on the task ID
-* @method getArrayLocationByID
-* @return {Void}
-*/  this.getArrayLocationByID = function(pId)  {
-
-         var vList = this.getList();
-         for(var i = 0; i < vList.length; i++)
-         {
-            if(vList[i].getID()==pId)
-               return i;
-         }
-      };
-
-/**
-* Draw gantt chart
-* @method Draw
-* @return {Void}
-*/ this.Draw = function()
-   {
-      var vMaxDate = new Date();
-      var vMinDate = new Date();	
-      var vTmpDate = new Date();
-      var vNxtDate = new Date();
-      var vCurrDate = new Date();
-      var vTaskLeft = 0;
-      var vTaskRight = 0;
-      var vNumCols = 0;
-      var vID = 0;
-      var vMainTable = "";
-      var vLeftTable = "";
-      var vRightTable = "";
-      var vDateRowStr = "";
-      var vItemRowStr = "";
-      var vColWidth = 0;
-      var vColUnit = 0;
-      var vChartWidth = 0;
-      var vNumDays = 0;
-      var vDayWidth = 0;
-      var vStr = "";
-      var vNameWidth = 220;	
-      var vStatusWidth = 70;
-      var vLeftWidth = 15 + 220 + 70 + 70 + 70 + 70 + 70;
-
-      if(vTaskList.length > 0)
-      {
-        
-		   // Process all tasks preset parent date and completion %
-         JSGantt.processRows(vTaskList, 0, -1, 1, 1);
-
-         // get overall min/max dates plus padding
-         vMinDate = JSGantt.getMinDate(vTaskList, vFormat);
-         vMaxDate = JSGantt.getMaxDate(vTaskList, vFormat);
-
-         // Calculate chart width variables.  vColWidth can be altered manually to change each column width
-         // May be smart to make this a parameter of GanttChart or set it based on existing pWidth parameter
-         if(vFormat == 'day') {
-            vColWidth = 18;
-            vColUnit = 1;
-         }
-         else if(vFormat == 'week') {
-            vColWidth = 37;
-            vColUnit = 7;
-         }
-         else if(vFormat == 'month') {
-            vColWidth = 37;
-            vColUnit = 30;
-         }
-         else if(vFormat == 'quarter') {
-            vColWidth = 60;
-            vColUnit = 90;
-         }
-         
-         else if(vFormat=='hour')
-         {
-            vColWidth = 18;
-            vColUnit = 1;
-         }
-         
-         else if(vFormat=='minute')
-         {
-            vColWidth = 18;
-            vColUnit = 1;
-         }
-         
-         vNumDays = (Date.parse(vMaxDate) - Date.parse(vMinDate)) / ( 24 * 60 * 60 * 1000);
-         vNumUnits = vNumDays / vColUnit;
-          
-         
-         vChartWidth = vNumUnits * vColWidth + 1;
-         vDayWidth = (vColWidth / vColUnit) + (1/vColUnit);
-
-         vMainTable =
-            '<TABLE id=theTable cellSpacing=0 cellPadding=0 border=0><TBODY><TR>' +
-            '<TD vAlign=top bgColor=#ffffff>';
-
-         if(vShowRes !=1) vNameWidth+=vStatusWidth;
-         if(vShowDur !=1) vNameWidth+=vStatusWidth;
-         if(vShowComp!=1) vNameWidth+=vStatusWidth;
-		   if(vShowStartDate!=1) vNameWidth+=vStatusWidth;
-		   if(vShowEndDate!=1) vNameWidth+=vStatusWidth;
-        
-		   // DRAW the Left-side of the chart (names, resources, comp%)
-         vLeftTable =
-            '<DIV class=scroll id=leftside style="width:' + vLeftWidth + 'px"><TABLE cellSpacing=0 cellPadding=0 border=0><TBODY>' +
-            '<TR style="HEIGHT: 17px">' +
-            '  <TD style="WIDTH: 15px; HEIGHT: 17px"></TD>' +
-            '  <TD style="WIDTH: ' + vNameWidth + 'px; HEIGHT: 17px"><NOBR></NOBR></TD>'; 
-
-         if(vShowRes ==1) vLeftTable += '  <TD style="WIDTH: ' + vStatusWidth + 'px; HEIGHT: 17px"></TD>' ;
-         if(vShowDur ==1) vLeftTable += '  <TD style="WIDTH: ' + vStatusWidth + 'px; HEIGHT: 17px"></TD>' ;
-         if(vShowComp==1) vLeftTable += '  <TD style="WIDTH: ' + vStatusWidth + 'px; HEIGHT: 17px"></TD>' ;
-			if(vShowStartDate==1) vLeftTable += '  <TD style="WIDTH: ' + vStatusWidth + 'px; HEIGHT: 17px"></TD>' ;
-			if(vShowEndDate==1) vLeftTable += '  <TD style="WIDTH: ' + vStatusWidth + 'px; HEIGHT: 17px"></TD>' ;
-
-         vLeftTable +=
-            '<TR style="HEIGHT: 20px">' +
-            '  <TD style="BORDER-TOP: #efefef 1px solid; WIDTH: 15px; HEIGHT: 20px"></TD>' +
-            '  <TD style="BORDER-TOP: #efefef 1px solid; WIDTH: ' + vNameWidth + 'px; HEIGHT: 20px"><NOBR></NOBR></TD>' ;
-
-         if(vShowRes ==1) vLeftTable += '  <TD style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; WIDTH: 60px; HEIGHT: 20px" align=center nowrap>'+i18n["Resource"]+'</TD>' ;
-         if(vShowDur ==1) vLeftTable += '  <TD style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; WIDTH: 60px; HEIGHT: 20px" align=center nowrap>'+i18n["Duration"]+'</TD>' ;
-         if(vShowComp==1) vLeftTable += '  <TD style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; WIDTH: 60px; HEIGHT: 20px" align=center nowrap>%</TD>' ;
-         if(vShowStartDate==1) vLeftTable += '  <TD style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; WIDTH: 60px; HEIGHT: 20px" align=center nowrap>'+i18n["Start_Date"]+'</TD>' ;
-         if(vShowEndDate==1) vLeftTable += '  <TD style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; WIDTH: 60px; HEIGHT: 20px" align=center nowrap>'+i18n["End_Date"]+'</TD>' ;
- 
-         vLeftTable += '</TR>';
-
-            for(i = 0; i < vTaskList.length; i++)
-            {
-               if( vTaskList[i].getGroup()) {
-                  vBGColor = "f3f3f3";
-                  vRowType = "group";
-               } else {
-                  vBGColor  = "ffffff";
-                  vRowType  = "row";
-               }
-               
-               vID = vTaskList[i].getID();
-
-  		         if(vTaskList[i].getVisible() == 0) 
-                  vLeftTable += '<TR id=child_' + vID + ' bgcolor=#' + vBGColor + ' style="display:none"  onMouseover=g.mouseOver(this,' + vID + ',"left","' + vRowType + '") onMouseout=g.mouseOut(this,' + vID + ',"left","' + vRowType + '")>' ;
-			      else
-                 vLeftTable += '<TR id=child_' + vID + ' bgcolor=#' + vBGColor + ' onMouseover=g.mouseOver(this,' + vID + ',"left","' + vRowType + '") onMouseout=g.mouseOut(this,' + vID + ',"left","' + vRowType + '")>' ;
-
-			      vLeftTable += 
-                  '  <TD class=gdatehead style="WIDTH: 15px; HEIGHT: 20px; BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;">&nbsp;</TD>' +
-                  '  <TD class=gname style="WIDTH: ' + vNameWidth + 'px; HEIGHT: 20px; BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px;" nowrap><NOBR><span style="color: #aaaaaa">';
-
-               for(j=1; j<vTaskList[i].getLevel(); j++) {
-                  vLeftTable += '&nbsp&nbsp&nbsp&nbsp';
-               }
-
-               vLeftTable += '</span>';
-
-               if( vTaskList[i].getGroup()) {
-                  if( vTaskList[i].getOpen() == 1) 
-                     vLeftTable += '<SPAN id="group_' + vID + '" style="color:#000000; cursor:pointer; font-weight:bold; FONT-SIZE: 12px;" onclick="JSGantt.folder(' + vID + ','+vGanttVar+');'+vGanttVar+'.DrawDependencies();">&ndash;</span><span style="color:#000000">&nbsp</SPAN>' ;
-                  else
-                     vLeftTable += '<SPAN id="group_' + vID + '" style="color:#000000; cursor:pointer; font-weight:bold; FONT-SIZE: 12px;" onclick="JSGantt.folder(' + vID + ','+vGanttVar+');'+vGanttVar+'.DrawDependencies();">+</span><span style="color:#000000">&nbsp</SPAN>' ;
-				 
-               } else {
-
-                  vLeftTable += '<span style="color: #000000; font-weight:bold; FONT-SIZE: 12px;">&nbsp&nbsp&nbsp</span>';
-               }
-
-               vLeftTable += 
-                  '<span onclick=JSGantt.taskLink("' + vTaskList[i].getLink() + '",300,200); style="cursor:pointer"> ' + vTaskList[i].getName() + '</span></NOBR></TD>' ;
-
-               if(vShowRes ==1) vLeftTable += '  <TD class=gname style="WIDTH: 60px; HEIGHT: 20px; TEXT-ALIGN: center; BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" align=center><NOBR>' + vTaskList[i].getResource() + '</NOBR></TD>' ;
-               if(vShowDur ==1) vLeftTable += '  <TD class=gname style="WIDTH: 60px; HEIGHT: 20px; TEXT-ALIGN: center; BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" align=center><NOBR>' + (vTaskList[i].getStart() > 0 ? vTaskList[i].getDuration(vFormat) : '') + '</NOBR></TD>' ;
-               if(vShowComp==1) vLeftTable += '  <TD class=gname style="WIDTH: 60px; HEIGHT: 20px; TEXT-ALIGN: center; BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" align=center><NOBR>' + vTaskList[i].getCompStr()  + '</NOBR></TD>' ;
-               if(vShowStartDate==1) vLeftTable += '  <TD class=gname style="WIDTH: 60px; HEIGHT: 20px; TEXT-ALIGN: center; BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" align=center><NOBR>' + (vTaskList[i].getStart() > 0 ? JSGantt.formatDateStr( vTaskList[i].getStart(), vDateDisplayFormat) : '')+ '</NOBR></TD>' ;
-               if(vShowEndDate==1) vLeftTable += '  <TD class=gname style="WIDTH: 60px; HEIGHT: 20px; TEXT-ALIGN: center; BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" align=center><NOBR>' + (vTaskList[i].getEnd() > 0 ? JSGantt.formatDateStr( vTaskList[i].getEnd(), vDateDisplayFormat) : '') + '</NOBR></TD>' ;
-
-               vLeftTable += '</TR>';
 
-            }
+    var vID=parseInt(document.createTextNode(pID).data);
+    var vName=document.createTextNode(pName).data;
+    var vStart=new Date(0);
+    var vEnd=new Date(0);
+    var vGroupMinStart=null;
+    var vGroupMinEnd=null;
+    var vClass=document.createTextNode(pClass).data;
+    var vLink=document.createTextNode(pLink).data;
+    var vMile=parseInt(document.createTextNode(pMile).data);
+    var vRes=document.createTextNode(pRes).data;
+    var vComp=parseFloat(document.createTextNode(pComp).data);
+    var vGroup=parseInt(document.createTextNode(pGroup).data);
+    var vParent=document.createTextNode(pParent).data;
+    var vOpen=(vGroup==2)?1:parseInt(document.createTextNode(pOpen).data);
+    var vDepend=new Array();
+    var vDependType=new Array();
+    var vCaption=document.createTextNode(pCaption).data;
+    var vDuration='';
+    var vLevel=0;
+    var vNumKid=0;
+    var vVisible=1;
+    var vSortIdx=0;
+    var vToDelete=false;
+    var x1, y1, x2, y2;
+    var vNotes;
+    var vParItem=null;
+    var vCellDiv=null;
+    var vGantt=(pGantt instanceof JSGantt.GanttChart)? pGantt : g; //hack for backwards compatibility
+    var vBarDiv=null;
+    var vTaskDiv=null;
+    var vListChildRow=null;
+    var vChildRow=null;
+    var vGroupSpan=null;
+
+    vNotes=document.createElement('span');
+    vNotes.className='gTaskNotes';
+    if (pNotes!=null)
+    {
+        vNotes.innerHTML=pNotes;
+        JSGantt.stripUnwanted(vNotes);
+    }
 
-            // DRAW the date format selector at bottom left.  Another potential GanttChart parameter to hide/show this selector
-            vLeftTable += '</TD></TR>' +
-            // LDR  '<TR><TD border=1 colspan=5 align=left style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 11px; BORDER-LEFT: #efefef 1px solid; height=18px">&nbsp;&nbsp;Powered by <a href=http://www.jsgantt.com>jsGantt</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Format:';
-            '<TR><TD border=1 colspan=5 align=left style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 11px; BORDER-LEFT: #efefef 1px solid; height=18px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; '+i18n["Period"]+': &nbsp; ';
-		
-			if (vFormatArr.join().indexOf("minute")!=-1) { 
-            if (vFormat=='minute') vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" VALUE="minute" checked>'+i18n["sMinute"];
-            else                vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" onclick=JSGantt.changeFormat("minute",'+vGanttVar+'); VALUE="minute">'+i18n["sMinute"];
-			}
-			
-			if (vFormatArr.join().indexOf("hour")!=-1) { 
-            if (vFormat=='hour') vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" VALUE="hour" checked>'+i18n["sHour"];
-            else                vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" onclick=JSGantt.changeFormat("hour",'+vGanttVar+'); VALUE="hour">'+i18n["sHour"];
-			}
-			
-			if (vFormatArr.join().indexOf("day")!=-1) { 
-            if (vFormat=='day') vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" VALUE="day" checked>'+i18n["sDay"];
-            else                vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" onclick=JSGantt.changeFormat("day",'+vGanttVar+'); VALUE="day">'+i18n["sDay"];
-			}
-			
-			if (vFormatArr.join().indexOf("week")!=-1) { 
-            if (vFormat=='week') vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" VALUE="week" checked>'+i18n["sWeek"];
-            else                vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" onclick=JSGantt.changeFormat("week",'+vGanttVar+') VALUE="week">'+i18n["sWeek"];
-			}
-			
-			if (vFormatArr.join().indexOf("month")!=-1) { 
-            if (vFormat=='month') vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" VALUE="month" checked>'+i18n["sMonth"];
-            else                vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" onclick=JSGantt.changeFormat("month",'+vGanttVar+') VALUE="month">'+i18n["sMonth"];
-			}
-			
-			if (vFormatArr.join().indexOf("quarter")!=-1) { 
-            if (vFormat=='quarter') vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" VALUE="quarter" checked>'+i18n["sQuarter"];
-            else                vLeftTable += '<INPUT TYPE=RADIO NAME="radFormat" onclick=JSGantt.changeFormat("quarter",'+vGanttVar+') VALUE="quarter">'+i18n["sQuarter"];
-			}
-			
-//            vLeftTable += '<INPUT TYPE=RADIO NAME="other" VALUE="other" style="display:none"> .';
-
-            vLeftTable += '</TD></TR></TBODY></TABLE></TD>';
-
-            vMainTable += vLeftTable;
+    if (pStart!=null && pStart!='')
+    {
+        vStart=(pStart instanceof Date)?pStart:JSGantt.parseDateStr(document.createTextNode(pStart).data,vGantt.getDateInputFormat());
+        vGroupMinStart=vStart;
+    }
 
-            // Draw the Chart Rows
-            vRightTable = 
-            '<TD style="width: ' + vChartWidth + 'px;" vAlign=top bgColor=#ffffff>' +
-            '<DIV class=scroll2 id=rightside>' +
-            '<TABLE style="width: ' + vChartWidth + 'px;" cellSpacing=0 cellPadding=0 border=0>' +
-            '<TBODY><TR style="HEIGHT: 18px">';
+    if (pEnd!=null && pEnd!='')
+    {
+        vEnd  =(pEnd instanceof Date)?pEnd:JSGantt.parseDateStr(document.createTextNode(pEnd).data,vGantt.getDateInputFormat());
+        vGroupMinEnd=vEnd;
+    }
 
-            vTmpDate.setFullYear(vMinDate.getFullYear(), vMinDate.getMonth(), vMinDate.getDate());
-            vTmpDate.setHours(0);
-            vTmpDate.setMinutes(0);
+    if (pDepend!=null)
+    {
+        var vDependStr=pDepend+'';
+        var vDepList=vDependStr.split(',');
+        var n=vDepList.length;
 
-         // Major Date Header
-         while(Date.parse(vTmpDate) <= Date.parse(vMaxDate))
-         {	
-            vStr = vTmpDate.getFullYear() + '';
-            vStr = vStr.substring(2,4);
-            
-            
-            if(vFormat == 'minute')
+        for(var k=0;k<n;k++)
+        {
+            if(vDepList[k].toUpperCase().indexOf('SS')!=-1)
             {
-                vRightTable += '<td class=gdatehead style="FONT-SIZE: 12px; HEIGHT: 19px;" align=center colspan=60>' ;
-                vRightTable += JSGantt.formatDateStr(vTmpDate, vDateDisplayFormat) + ' ' + vTmpDate.getHours() + ':00 -' + vTmpDate.getHours() + ':59 </td>';
-                vTmpDate.setHours(vTmpDate.getHours()+1);
+                vDepend[k]=vDepList[k].substring(0,vDepList[k].toUpperCase().indexOf('SS'));
+                vDependType[k]='SS';
             }
-            
-            if(vFormat == 'hour')
+            else if(vDepList[k].toUpperCase().indexOf('FF')!=-1)
             {
-                vRightTable += '<td class=gdatehead style="FONT-SIZE: 12px; HEIGHT: 19px;" align=center colspan=24>' ;
-                vRightTable += JSGantt.formatDateStr(vTmpDate, vDateDisplayFormat) + '</td>';
-                vTmpDate.setDate(vTmpDate.getDate()+1);
+                vDepend[k]=vDepList[k].substring(0,vDepList[k].toUpperCase().indexOf('FF'));
+                vDependType[k]='FF';
             }
-            
-  	         if(vFormat == 'day')
+            else if(vDepList[k].toUpperCase().indexOf('SF')!=-1)
             {
-			      vRightTable += '<td class=gdatehead style="FONT-SIZE: 12px; HEIGHT: 19px;" align=center colspan=7>' +
-			      JSGantt.formatDateStr(vTmpDate,vDateDisplayFormat.substring(0,5)) + ' - ';
-               vTmpDate.setDate(vTmpDate.getDate()+6);
-		         vRightTable += JSGantt.formatDateStr(vTmpDate, vDateDisplayFormat) + '</td>';
-               vTmpDate.setDate(vTmpDate.getDate()+1);
+                vDepend[k]=vDepList[k].substring(0,vDepList[k].toUpperCase().indexOf('SF'));
+                vDependType[k]='SF';
             }
-            else if(vFormat == 'week')
+            else if(vDepList[k].toUpperCase().indexOf('FS')!=-1)
             {
-  		         vRightTable += '<td class=gdatehead align=center style="FONT-SIZE: 12px; HEIGHT: 19px;" width='+vColWidth+'px>`'+ vStr + '</td>';
-               vTmpDate.setDate(vTmpDate.getDate()+7);
+                vDepend[k]=vDepList[k].substring(0,vDepList[k].toUpperCase().indexOf('FS'));
+                vDependType[k]='FS';
             }
-            else if(vFormat == 'month')
+            else
             {
-	            vRightTable += '<td class=gdatehead align=center style="FONT-SIZE: 12px; HEIGHT: 19px;" width='+vColWidth+'px>`'+ vStr + '</td>';
-               vTmpDate.setDate(vTmpDate.getDate() + 1);
-               while(vTmpDate.getDate() > 1)
-               {
-                 vTmpDate.setDate(vTmpDate.getDate() + 1);
-               }
+                vDepend[k]=vDepList[k];
+                vDependType[k]='FS';
             }
-            else if(vFormat == 'quarter')
+        }
+    }
+
+    this.getID=function(){return vID;};
+    this.getName=function(){return vName;};
+    this.getStart=function(){return vStart;};
+    this.getEnd=function(){return vEnd;};
+    this.getGroupMinStart=function(){return vGroupMinStart;};
+    this.getGroupMinEnd=function(){return vGroupMinEnd;};
+    this.getClass=function(){return vClass;};
+    this.getLink=function(){return vLink;};
+    this.getMile=function(){return vMile;};
+    this.getDepend=function(){if(vDepend) return vDepend; else return null;};
+    this.getDepType=function(){if(vDependType) return vDependType; else return null;};
+    this.getCaption=function(){if(vCaption) return vCaption; else return '';};
+    this.getResource=function(){if(vRes) return vRes; else return '\u00A0';};
+    this.getCompVal=function(){if(vComp) return vComp; else return 0;};
+    this.getCompStr=function(){if(vComp) return vComp+'%'; else return '';};
+    this.getNotes=function(){return vNotes;};
+    this.getSortIdx=function(){return vSortIdx;};
+    this.getToDelete=function(){return vToDelete;};
+
+    this.getDuration=function(pFormat, pLang)
+    {
+        if (vMile)
+        {
+            vDuration='-';
+        }
+        else
+        {
+            var vTaskEnd=new Date(this.getEnd().getTime());
+            var vUnits=null;
+            switch(pFormat)
             {
-	            vRightTable += '<td class=gdatehead align=center style="FONT-SIZE: 12px; HEIGHT: 19px;" width='+vColWidth+'px>`'+ vStr + '</td>';
-               vTmpDate.setDate(vTmpDate.getDate() + 81);
-               while(vTmpDate.getDate() > 1)
-               {
-                 vTmpDate.setDate(vTmpDate.getDate() + 1);
-               }
+                case 'week':  vUnits='day'; break;
+                case 'month':  vUnits='week'; break;
+                case 'quarter': vUnits='month'; break;
+                default: vUnits=pFormat; break;
             }
 
-         }
-
-         vRightTable += '</TR><TR>';
-
-         // Minor Date header and Cell Rows
-         vTmpDate.setFullYear(vMinDate.getFullYear(), vMinDate.getMonth(), vMinDate.getDate());
-         vNxtDate.setFullYear(vMinDate.getFullYear(), vMinDate.getMonth(), vMinDate.getDate());
-         vNumCols = 0;
- 
-         while(Date.parse(vTmpDate) <= Date.parse(vMaxDate))
-         {	
-            if (vFormat == 'minute')
+            if ((vTaskEnd.getTime()-(vTaskEnd.getTimezoneOffset()*60000))%(86400000)==0)
             {
-			
-			  if( vTmpDate.getMinutes() ==0 ) 
-                  vWeekdayColor = "ccccff";
-               else
-                  vWeekdayColor = "ffffff";
-				  
-				  
-                vDateRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; HEIGHT: 19px; BORDER-LEFT: #efefef 1px solid;"  bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">' + vTmpDate.getMinutes() + '</div></td>';
-                vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; cursor: default;"  bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-                vTmpDate.setMinutes(vTmpDate.getMinutes() + 1);
+                vTaskEnd=new Date(vTaskEnd.getFullYear(), vTaskEnd.getMonth(), vTaskEnd.getDate()+1, vTaskEnd.getHours(), vTaskEnd.getMinutes(), vTaskEnd.getSeconds());
             }
-          
-            else if (vFormat == 'hour')
+            var tmpPer=(JSGantt.getOffset(this.getStart(), vTaskEnd, 999, vUnits))/1000;
+            if(Math.floor(tmpPer)!=tmpPer) tmpPer=Math.round(tmpPer*10)/10;
+            switch(vUnits)
             {
-			
-			   if(  vTmpDate.getHours() ==0  ) 
-                  vWeekdayColor = "ccccff";
-               else
-                  vWeekdayColor = "ffffff";
-				  
-				  
-                vDateRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; HEIGHT: 19px; BORDER-LEFT: #efefef 1px solid;"  bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">' + vTmpDate.getHours() + '</div></td>';
-                vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; cursor: default;"  bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-                vTmpDate.setHours(vTmpDate.getHours() + 1);
+                case 'hour': vDuration=tmpPer+' '+((tmpPer!=1)?pLang['hrs']:pLang['hr']); break;
+                case 'day': vDuration=tmpPer+' '+((tmpPer!=1)?pLang['dys']:pLang['dy']); break;
+                case 'week': vDuration=tmpPer+' '+((tmpPer!=1)?pLang['wks']:pLang['wk']); break;
+                case 'month': vDuration=tmpPer+' '+((tmpPer!=1)?pLang['mths']:pLang['mth']); break;
+                case 'quarter': vDuration=tmpPer+' '+((tmpPer!=1)?pLang['qtrs']:pLang['qtr']); break;
             }
+        }
+        return vDuration;
+    };
+
+    this.getParent=function(){return vParent;};
+    this.getGroup=function(){return vGroup;};
+    this.getOpen=function(){return vOpen;};
+    this.getLevel=function(){return vLevel;};
+    this.getNumKids=function(){return vNumKid;};
+    this.getStartX=function(){return x1;};
+    this.getStartY=function(){return y1;};
+    this.getEndX=function(){return x2;};
+    this.getEndY=function(){return y2;};
+    this.getVisible=function(){return vVisible;};
+    this.getParItem=function(){return vParItem;};
+    this.getCellDiv=function(){return vCellDiv;};
+    this.getBarDiv=function(){return vBarDiv;};
+    this.getTaskDiv=function(){return vTaskDiv;};
+    this.getChildRow=function(){return vChildRow;};
+    this.getListChildRow=function(){return vListChildRow;};
+    this.getGroupSpan=function(){return vGroupSpan;};
+    this.setStart=function(pStart){if(pStart instanceof Date)vStart=pStart;};
+    this.setEnd=function(pEnd){if(pEnd instanceof Date)vEnd=pEnd;};
+    this.setGroupMinStart=function(pStart){if(pStart instanceof Date)vGroupMinStart=pStart;};
+    this.setGroupMinEnd=function(pEnd){if(pEnd instanceof Date)vGroupMinEnd=pEnd;};
+    this.setLevel=function(pLevel){vLevel=parseInt(document.createTextNode(pLevel).data);};
+    this.setNumKid=function(pNumKid){vNumKid=parseInt(document.createTextNode(pNumKid).data);};
+    this.setCompVal=function(pCompVal){vComp=parseFloat(document.createTextNode(pCompVal).data);};
+    this.setStartX=function(pX){x1=parseInt(document.createTextNode(pX).data);};
+    this.setStartY=function(pY){y1=parseInt(document.createTextNode(pY).data);};
+    this.setEndX=function(pX){x2=parseInt(document.createTextNode(pX).data);};
+    this.setEndY=function(pY){y2=parseInt(document.createTextNode(pY).data);};
+    this.setOpen=function(pOpen){vOpen=parseInt(document.createTextNode(pOpen).data);};
+    this.setVisible=function(pVisible){vVisible=parseInt(document.createTextNode(pVisible).data);};
+    this.setSortIdx=function(pSortIdx){vSortIdx=parseInt(document.createTextNode(pSortIdx).data);};
+    this.setToDelete=function(pToDelete){if (pToDelete) vToDelete=true; else vToDelete=false;};
+    this.setParItem=function(pParItem){if(pParItem instanceof JSGantt.TaskItem) vParItem=pParItem;};
+    this.setCellDiv=function(pCellDiv){if(typeof HTMLDivElement !== 'function' || pCellDiv instanceof HTMLDivElement) vCellDiv=pCellDiv;}; //"typeof HTMLDivElement !== 'function'" to play nice with ie6 and 7
+    this.setGroup=function(pGroup){vGroup=parseInt(document.createTextNode(pGroup).data);};
+    this.setBarDiv=function(pDiv){if(typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement)vBarDiv=pDiv;};
+    this.setTaskDiv=function(pDiv){if(typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement)vTaskDiv=pDiv;};
+    this.setChildRow=function(pRow){if(typeof HTMLTableRowElement !== 'function' || pRow instanceof HTMLTableRowElement)vChildRow=pRow;};
+    this.setListChildRow=function(pRow){if(typeof HTMLTableRowElement !== 'function' || pRow instanceof HTMLTableRowElement)vListChildRow=pRow;};
+    this.setGroupSpan=function(pSpan){if(typeof HTMLSpanElement !== 'function' || pSpan instanceof HTMLSpanElement)vGroupSpan=pSpan;};
+};
 
-	        else if(vFormat == 'day' )
-             {
-               if( JSGantt.formatDateStr(vCurrDate,'mm/dd/yyyy') == JSGantt.formatDateStr(vTmpDate,'mm/dd/yyyy')) {
-                  vWeekdayColor  = "ccccff";
-                  vWeekendColor  = "9999ff";
-                  vWeekdayGColor  = "bbbbff";
-                  vWeekendGColor = "8888ff";
-               } else {
-                  vWeekdayColor = "ffffff";
-                  vWeekendColor = "cfcfcf";
-                  vWeekdayGColor = "f3f3f3";
-                  vWeekendGColor = "c3c3c3";
-               }
-               
-               if(vTmpDate.getDay() % 6 == 0) {
-                  vDateRowStr  += '<td class="gheadwkend" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; HEIGHT: 19px; BORDER-LEFT: #efefef 1px solid;" bgcolor=#' + vWeekendColor + ' align=center><div style="width: '+vColWidth+'px">' + vTmpDate.getDate() + '</div></td>';
-                  vItemRowStr  += '<td class="gheadwkend" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; cursor: default;"  bgcolor=#' + vWeekendColor + ' align=center><div style="width: '+vColWidth+'px">&nbsp</div></td>';
-               }
-               else {
-                  vDateRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; HEIGHT: 19px; BORDER-LEFT: #efefef 1px solid;"  bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">' + vTmpDate.getDate() + '</div></td>';
-                  if( JSGantt.formatDateStr(vCurrDate,'mm/dd/yyyy') == JSGantt.formatDateStr(vTmpDate,'mm/dd/yyyy')) 
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; cursor: default;"  bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-                  else
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; cursor: default;"  align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-               }
-
-               vTmpDate.setDate(vTmpDate.getDate() + 1);
+// function that loads the main gantt chart properties and functions
+// pDiv: (required) this is a div object created in HTML
+// pFormat: (required) - used to indicate whether chart should be drawn in "hour", "day", "week", "month", or "quarter" format
+JSGantt.GanttChart=function(pDiv, pFormat)
+{
+    var vDiv=pDiv;
+    var vFormat=pFormat;
+    var vDivId=null;
+    var vUseFade=1;
+    var vUseMove=1;
+    var vUseRowHlt=1;
+    var vUseToolTip=1;
+    var vUseSort=1;
+    var vUseSingleCell=25000;
+    var vShowRes=1;
+    var vShowDur=1;
+    var vShowComp=1;
+    var vShowStartDate=1;
+    var vShowEndDate=1;
+    var vShowEndWeekDate=1;
+    var vShowTaskInfoRes=1;
+    var vShowTaskInfoDur=1;
+    var vShowTaskInfoComp=1;
+    var vShowTaskInfoStartDate=1;
+    var vShowTaskInfoEndDate=1;
+    var vShowTaskInfoNotes=1;
+    var vShowTaskInfoLink=1;
+    var vShowDeps=1;
+    var vShowSelector=new Array('top');
+    var vDateInputFormat='yyyy-mm-dd';
+    var vDateTaskTableDisplayFormat=JSGantt.parseDateFormatStr('dd/mm/yyyy');
+    var vDateTaskDisplayFormat=JSGantt.parseDateFormatStr('dd month yyyy');
+    var vHourMajorDateDisplayFormat=JSGantt.parseDateFormatStr('day dd month yyyy');
+    var vHourMinorDateDisplayFormat=JSGantt.parseDateFormatStr('HH');
+    var vDayMajorDateDisplayFormat=JSGantt.parseDateFormatStr('dd/mm/yyyy');
+    var vDayMinorDateDisplayFormat=JSGantt.parseDateFormatStr('dd');
+    var vWeekMajorDateDisplayFormat=JSGantt.parseDateFormatStr('yyyy');
+    var vWeekMinorDateDisplayFormat=JSGantt.parseDateFormatStr('dd/mm');
+    var vMonthMajorDateDisplayFormat=JSGantt.parseDateFormatStr('yyyy');
+    var vMonthMinorDateDisplayFormat=JSGantt.parseDateFormatStr('mon');
+    var vQuarterMajorDateDisplayFormat=JSGantt.parseDateFormatStr('yyyy');
+    var vQuarterMinorDateDisplayFormat=JSGantt.parseDateFormatStr('qq');
+    var vUseFullYear=JSGantt.parseDateFormatStr('dd/mm/yyyy');
+    var vCaptionType;
+    var vDepId=1;
+    var vTaskList=new Array();
+    var vFormatArr=new Array('hour','day','week','month','quarter');
+    var vMonthDaysArr=new Array(31,28,31,30,31,30,31,31,30,31,30,31);
+    var vProcessNeeded=true;
+    var vMinGpLen=8;
+    var vScrollTo='';
+    var vHourColWidth=18;
+    var vDayColWidth=18;
+    var vWeekColWidth=36;
+    var vMonthColWidth=36;
+    var vQuarterColWidth=18;
+    var vRowHeight=20;
+    var vTodayPx=-1;
+    /*var vLangs={'en':
+     {'format':'Format','hour':'Hour','day':'Day','week':'Week','month':'Month','quarter':'Quarter','hours':'Hours','days':'Days',
+     'weeks':'Weeks','months':'Months','quarters':'Quarters','hr':'Hr','dy':'Day','wk':'Wk','mth':'Mth','qtr':'Qtr','hrs':'Hrs',
+     'dys':'Days','wks':'Wks','mths':'Mths','qtrs':'Qtrs','resource':'Resource','duration':'Duration','comp':'% Comp.',
+     'completion':'Completion','startdate':'Start Date','enddate':'End Date','moreinfo':'More Information','notes':'Notes',
+     'january':'January','february':'February','march':'March','april':'April','maylong':'May','june':'June','july':'July',
+     'august':'August','september':'September','october':'October','november':'November','december':'December','jan':'Jan',
+     'feb':'Feb','mar':'Mar','apr':'Apr','may':'May','jun':'Jun','jul':'Jul','aug':'Aug','sep':'Sep','oct':'Oct','nov':'Nov',
+     'dec':'Dec','sunday':'Sunday','monday':'Monday','tuesday':'Tuesday','wednesday':'Wednesday','thursday':'Thursday',
+     'friday':'Friday','saturday':'Saturday','sun':'Sun','mon':'Mon','tue':'Tue','wed':'Wed','thu':'Thu','fri':'Fri','sat':'Sat'}
+     };
+     var vLang='en';*/
+    var vChartBody=null;
+    var vChartHead=null;
+    var vListBody=null;
+    var vChartTable=null;
+    var vLines=null;
+    var vTimer=20;
+
+    this.setUseFade=function(pVal){vUseFade=pVal;};
+    this.setUseMove=function(pVal){vUseMove=pVal;};
+    this.setUseRowHlt=function(pVal){vUseRowHlt=pVal;};
+    this.setUseToolTip=function(pVal){vUseToolTip=pVal;};
+    this.setUseSort=function(pVal){vUseSort=pVal;};
+    this.setUseSingleCell=function(pVal){vUseSingleCell=pVal*1;};
+    this.setFormatArr=function()
+    {
+        var vValidFormats='hour day week month quarter';
+        vFormatArr=new Array();
+        for(var i=0, j=0; i<arguments.length; i++)
+        {
+            if (vValidFormats.indexOf(arguments[i].toLowerCase())!=-1 && arguments[i].length>1)
+            {
+                vFormatArr[j++]=arguments[i].toLowerCase();
+                var vRegExp=new RegExp('(?:^|\s)'+arguments[i]+'(?!\S)', 'g');
+                vValidFormats=vValidFormats.replace(vRegExp, '');
+            }
+        }
+    };
+    this.setShowRes=function(pVal){vShowRes=pVal;};
+    this.setShowDur=function(pVal){vShowDur=pVal;};
+    this.setShowComp=function(pVal){vShowComp=pVal;};
+    this.setShowStartDate=function(pVal){vShowStartDate=pVal;};
+    this.setShowEndDate=function(pVal){vShowEndDate=pVal;};
+    this.setShowTaskInfoRes=function(pVal){vShowTaskInfoRes=pVal;};
+    this.setShowTaskInfoDur=function(pVal){vShowTaskInfoDur=pVal;};
+    this.setShowTaskInfoComp=function(pVal){vShowTaskInfoComp=pVal;};
+    this.setShowTaskInfoStartDate=function(pVal){vShowTaskInfoStartDate=pVal;};
+    this.setShowTaskInfoEndDate=function(pVal){vShowTaskInfoEndDate=pVal;};
+    this.setShowTaskInfoNotes=function(pVal){vShowTaskInfoNotes=pVal;};
+    this.setShowTaskInfoLink=function(pVal){vShowTaskInfoLink=pVal;};
+    this.setShowEndWeekDate=function(pVal){vShowEndWeekDate=pVal;};
+    this.setShowSelector=function()
+    {
+        var vValidSelectors='top bottom';
+        vShowSelector=new Array();
+        for(var i=0, j=0; i<arguments.length; i++)
+        {
+            if (vValidSelectors.indexOf(arguments[i].toLowerCase())!=-1 && arguments[i].length>1)
+            {
+                vShowSelector[j++]=arguments[i].toLowerCase();
+                var vRegExp=new RegExp('(?:^|\s)'+arguments[i]+'(?!\S)', 'g');
+                vValidSelectors=vValidSelectors.replace(vRegExp, '');
+            }
+        }
+    };
+    this.setShowDeps=function(pVal){vShowDeps=pVal;};
+    this.setDateInputFormat=function(pVal){vDateInputFormat=pVal;};
+    this.setDateTaskTableDisplayFormat=function(pVal){vDateTaskTableDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setDateTaskDisplayFormat=function(pVal){vDateTaskDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setHourMajorDateDisplayFormat=function(pVal){vHourMajorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setHourMinorDateDisplayFormat=function(pVal){vHourMinorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setDayMajorDateDisplayFormat=function(pVal){vDayMajorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setDayMinorDateDisplayFormat=function(pVal){vDayMinorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setWeekMajorDateDisplayFormat=function(pVal){vWeekMajorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setWeekMinorDateDisplayFormat=function(pVal){vWeekMinorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setMonthMajorDateDisplayFormat=function(pVal){vMonthMajorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setMonthMinorDateDisplayFormat=function(pVal){vMonthMinorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setQuarterMajorDateDisplayFormat=function(pVal){vQuarterMajorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setQuarterMinorDateDisplayFormat=function(pVal){vQuarterMinorDateDisplayFormat=JSGantt.parseDateFormatStr(pVal);};
+    this.setCaptionType=function(pType){vCaptionType=pType;};
+    this.setFormat=function(pFormat)
+    {
+        vFormat=pFormat;
+        this.Draw();
+    };
+    this.setMinGpLen=function(pMinGpLen){vMinGpLen=pMinGpLen;};
+    this.setScrollTo=function(pDate){vScrollTo=pDate;};
+    this.setHourColWidth=function(pWidth){vHourColWidth=pWidth;};
+    this.setDayColWidth=function(pWidth){vDayColWidth=pWidth;};
+    this.setWeekColWidth=function(pWidth){vWeekColWidth=pWidth;};
+    this.setMonthColWidth=function(pWidth){vMonthColWidth=pWidth;};
+    this.setQuarterColWidth=function(pWidth){vQuarterColWidth=pWidth;};
+    this.setRowHeight=function(pHeight){vRowHeight=pHeight;};
+    this.setLang=function(pLang){if(vLangs[pLang])vLang=pLang;};
+    this.setChartBody=function(pDiv){if(typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement)vChartBody=pDiv;};
+    this.setChartHead=function(pDiv){if(typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement)vChartHead=pDiv;};
+    this.setListBody=function(pDiv){if(typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement)vListBody=pDiv;};
+    this.setChartTable=function(pTable){if(typeof HTMLTableElement !== 'function' || pTable instanceof HTMLTableElement)vChartTable=pTable;};
+    this.setLines=function(pDiv){if(typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement)vLines=pDiv;};
+    this.setTimer=function(pVal){vTimer=pVal*1;};
+    this.addLang=function(pLang, pVals){
+        if(!vLangs[pLang])
+        {
+            vLangs[pLang]=new Object();
+            for(var vKey in vLangs['en'])vLangs[pLang][vKey]=(pVals[vKey])?document.createTextNode(pVals[vKey]).data:vLangs['en'][vKey];
+        }
+    };
+
+    this.getDivId=function(){return vDivId;};
+    this.getUseFade=function(){return vUseFade;};
+    this.getUseMove=function(){return vUseMove;};
+    this.getUseRowHlt=function(){return vUseRowHlt;};
+    this.getUseToolTip=function(){return vUseToolTip;};
+    this.getUseSort=function(){return vUseSort;};
+    this.getUseSingleCell=function(){return vUseSingleCell;};
+    this.getFormatArr=function(){return vFormatArr;};
+    this.getShowRes=function(){return vShowRes;};
+    this.getShowDur=function(){return vShowDur;};
+    this.getShowComp=function(){return vShowComp;};
+    this.getShowStartDate=function(){return vShowStartDate;};
+    this.getShowEndDate=function(){return vShowEndDate;};
+    this.getShowTaskInfoRes=function(){return vShowTaskInfoRes;};
+    this.getShowTaskInfoDur=function(){return vShowTaskInfoDur;};
+    this.getShowTaskInfoComp=function(){return vShowTaskInfoComp;};
+    this.getShowTaskInfoStartDate=function(){return vShowTaskInfoStartDate;};
+    this.getShowTaskInfoEndDate=function(){return vShowTaskInfoEndDate;};
+    this.getShowTaskInfoNotes=function(){return vShowTaskInfoNotes;};
+    this.getShowTaskInfoLink=function(){return vShowTaskInfoLink;};
+    this.getShowEndWeekDate=function(){return vShowEndWeekDate;};
+    this.getShowSelector=function(){return vShowSelector;};
+    this.getShowDeps=function(){return vShowDeps;};
+    this.getDateInputFormat=function(){return vDateInputFormat;};
+    this.getDateTaskTableDisplayFormat=function(){return vDateTaskTableDisplayFormat;};
+    this.getDateTaskDisplayFormat=function(){return vDateTaskDisplayFormat;};
+    this.getHourMajorDateDisplayFormat=function(){return vHourMajorDateDisplayFormat;};
+    this.getHourMinorDateDisplayFormat=function(){return vHourMinorDateDisplayFormat;};
+    this.getDayMajorDateDisplayFormat=function(){return vDayMajorDateDisplayFormat;};
+    this.getDayMinorDateDisplayFormat=function(){return vDayMinorDateDisplayFormat;};
+    this.getWeekMajorDateDisplayFormat=function(){return vWeekMajorDateDisplayFormat;};
+    this.getWeekMinorDateDisplayFormat=function(){return vWeekMinorDateDisplayFormat;};
+    this.getMonthMajorDateDisplayFormat=function(){return vMonthMajorDateDisplayFormat;};
+    this.getMonthMinorDateDisplayFormat=function(){return vMonthMinorDateDisplayFormat;};
+    this.getQuarterMajorDateDisplayFormat=function(){return vQuarterMajorDateDisplayFormat;};
+    this.getQuarterMinorDateDisplayFormat=function(){return vQuarterMinorDateDisplayFormat;};
+    this.getCaptionType=function(){return vCaptionType;};
+    this.getMinGpLen=function(){return vMinGpLen;};
+    this.getScrollTo=function(){return vScrollTo;};
+    this.getHourColWidth=function(){return vHourColWidth;};
+    this.getDayColWidth=function(){return vDayColWidth;};
+    this.getWeekColWidth=function(){return vWeekColWidth;};
+    this.getMonthColWidth=function(){return vMonthColWidth;};
+    this.getQuarterColWidth=function(){return vQuarterColWidth;};
+    this.getRowHeight=function(){return vRowHeight;};
+    this.getChartBody=function(){return vChartBody;};
+    this.getChartHead=function(){return vChartHead;};
+    this.getListBody=function(){return vListBody;};
+    this.getChartTable=function(){return vChartTable;};
+    this.getLines=function(){return vLines;};
+    this.getTimer=function(){return vTimer;};
+
+    this.CalcTaskXY=function()
+    {
+        var vID;
+        var vList=this.getList();
+        var vBarDiv;
+        var vTaskDiv;
+        var vParDiv;
+        var vLeft, vTop, vWidth;
+        var vHeight=Math.floor((this.getRowHeight()/2));
+
+        for(var i=0; i<vList.length; i++)
+        {
+            vID=vList[i].getID();
+            vBarDiv=vList[i].getBarDiv();
+            vTaskDiv=vList[i].getTaskDiv();
+            if((vList[i].getParItem() && vList[i].getParItem().getGroup()==2))
+            {
+                vParDiv=vList[i].getParItem().getChildRow();
+            }
+            else vParDiv=vList[i].getChildRow();
 
+            if(vBarDiv)
+            {
+                vList[i].setStartX(vBarDiv.offsetLeft+1);
+                vList[i].setStartY(vParDiv.offsetTop+vBarDiv.offsetTop+vHeight-1);
+                vList[i].setEndX(vBarDiv.offsetLeft+vBarDiv.offsetWidth+1);
+                vList[i].setEndY(vParDiv.offsetTop+vBarDiv.offsetTop+vHeight-1);
+            }
+        }
+    };
+
+    this.AddTaskItem=function(value)
+    {
+        var vExists=false;
+        for (var i=0; i<vTaskList.length; i++)
+        {
+            if (vTaskList[i].getID()==value.getID())
+            {
+                i=vTaskList.length;
+                vExists=true;
             }
+        }
+        if(!vExists)
+        {
+            vTaskList.push(value);
+            vProcessNeeded=true;
+        }
+    };
+
+    this.RemoveTaskItem=function(pID)
+    {
+        // simply mark the task for removal at this point - actually remove it next time we re-draw the chart
+        for (var i=0; i<vTaskList.length; i++)
+        {
+            if (vTaskList[i].getID()==pID) vTaskList[i].setToDelete(true);
+            else if (vTaskList[i].getParent()==pID) this.RemoveTaskItem(vTaskList[i].getID());
+        }
+        vProcessNeeded=true;
+    };
+
+    this.getList=function(){return vTaskList;};
+
+    this.clearDependencies=function()
+    {
+        var parent=this.getLines();
+        while(parent.hasChildNodes())parent.removeChild(parent.firstChild);
+        vDepId=1;
+    };
+
+
+    // sLine: Draw a straight line (colored one-pixel wide div)
+    this.sLine=function(x1,y1,x2,y2,pClass)
+    {
+        var vLeft=Math.min(x1,x2);
+        var vTop=Math.min(y1,y2);
+        var vWid=Math.abs(x2-x1)+1;
+        var vHgt=Math.abs(y2-y1)+1;
+
+        var vTmpDiv=document.createElement('div');
+        vTmpDiv.id=vDivId+'line'+vDepId++;
+        vTmpDiv.style.position='absolute';
+        vTmpDiv.style.overflow='hidden';
+        vTmpDiv.style.zIndex=0;
+        vTmpDiv.style.left=vLeft+'px';
+        vTmpDiv.style.top=vTop+'px';
+        vTmpDiv.style.width=vWid+'px';
+        vTmpDiv.style.height=vHgt+'px';
+
+        vTmpDiv.style.visibility='visible';
+
+        if (vWid==1) vTmpDiv.className='glinev';
+        else vTmpDiv.className='glineh';
+
+        if (pClass) vTmpDiv.className+=' '+pClass;
+
+        this.getLines().appendChild(vTmpDiv);
+
+        return vTmpDiv;
+    };
+
+    this.drawDependency =function(x1,y1,x2,y2,pType,pClass)
+    {
+        var vDir=1;
+        var vBend=false;
+        var vShort=4;
+        var vRow=Math.floor(this.getRowHeight()/2);
+
+        if(y2<y1) vRow*=-1;
+
+        switch(pType)
+        {
+            case 'SF':
+                vShort*=-1;
+                if(x1-10<=x2 && y1!=y2) vBend=true;
+                vDir=-1;
+                break;
+            case 'SS':
+                if (x1<x2) vShort*=-1;
+                else vShort=x2-x1-(2*vShort);
+                break;
+            case 'FF':
+                if (x1<=x2) vShort=x2-x1+(2*vShort);
+                vDir=-1;
+                break;
+            default:
+                if(x1+10>=x2 && y1!=y2) vBend=true;
+                break;
+        }
+
+        if (vBend)
+        {
+            this.sLine(x1,y1,x1+vShort,y1,pClass);
+            this.sLine(x1+vShort,y1,x1+vShort,y2-vRow,pClass);
+            this.sLine(x1+vShort,y2-vRow,x2-(vShort*2),y2-vRow,pClass);
+            this.sLine(x2-(vShort*2),y2-vRow,x2-(vShort*2),y2,pClass);
+            this.sLine(x2-(vShort*2),y2,x2-(1*vDir),y2,pClass);
+        }
+        else if (y1!=y2)
+        {
+            this.sLine(x1,y1,x1+vShort,y1,pClass);
+            this.sLine(x1+vShort,y1,x1+vShort,y2,pClass);
+            this.sLine(x1+vShort,y2,x2-(1*vDir),y2,pClass);
+        }
+        else this.sLine(x1,y1,x2-(1*vDir),y2,pClass);
+
+        var vTmpDiv=this.sLine(x2,y2,x2-3-((vDir<0)?1:0),y2-3-((vDir<0)?1:0),pClass+"Arw");
+        vTmpDiv.style.width='0px';
+        vTmpDiv.style.height='0px';
+    };
+
+    this.DrawDependencies=function()
+    {
+        if (this.getShowDeps()==1)
+        {
+            //First recalculate the x,y
+            this.CalcTaskXY();
+            this.clearDependencies();
+
+            var vList=this.getList();
+            for(var i=0; i<vList.length; i++)
+            {
+                var vDepend=vList[i].getDepend();
+                var vDependType=vList[i].getDepType();
+                var n=vDepend.length;
 
-	         else if(vFormat == 'week')
+                if(n>0 && vList[i].getVisible()==1)
+                {
+                    for(var k=0;k<n;k++)
+                    {
+                        var vTask=this.getArrayLocationByID(vDepend[k]);
+                        if (vTask>=0 && vList[vTask].getGroup()!=2)
+                        {
+                            if(vList[vTask].getVisible()==1)
+                            {
+                                if(vDependType[k]=='SS')this.drawDependency(vList[vTask].getStartX()-1,vList[vTask].getStartY(),vList[i].getStartX()-1,vList[i].getStartY(),'SS','gDepSS');
+                                else if(vDependType[k]=='FF')this.drawDependency(vList[vTask].getEndX(),vList[vTask].getEndY(),vList[i].getEndX(),vList[i].getEndY(),'FF','gDepFF');
+                                else if(vDependType[k]=='SF')this.drawDependency(vList[vTask].getStartX()-1,vList[vTask].getStartY(),vList[i].getEndX(),vList[i].getEndY(),'SF','gDepSF');
+                                else if(vDependType[k]=='FS')this.drawDependency(vList[vTask].getEndX(),vList[vTask].getEndY(),vList[i].getStartX()-1,vList[i].getStartY(),'FS','gDepFS');
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        // draw the current date line
+        if (vTodayPx>=0) this.sLine(vTodayPx, 0, vTodayPx, this.getChartTable().offsetHeight-1, 'gCurDate');
+    };
+
+    this.getArrayLocationByID=function(pId)
+    {
+        var vList=this.getList();
+        for(var i=0; i<vList.length; i++)
+        {
+            if(vList[i].getID()==pId)
+                return i;
+        }
+        return -1;
+    };
+
+    this.newNode=function(pParent, pNodeType, pId, pClass, pText, pWidth, pLeft, pDisplay, pColspan, pAttribs)
+    {
+        var vNewNode=pParent.appendChild(document.createElement(pNodeType));
+        if (pAttribs)
+        {
+            for (var i=0; i+1<pAttribs.length; i+=2)
+            {
+                vNewNode.setAttribute(pAttribs[i],pAttribs[i+1]);
+            }
+        }
+        // I wish I could do this with setAttribute but older IEs don't play nice
+        if (pId)vNewNode.id=pId;
+        if (pClass)vNewNode.className=pClass;
+        if (pWidth)vNewNode.style.width=(isNaN(pWidth*1))?pWidth:pWidth+'px';
+        if (pLeft)vNewNode.style.left=(isNaN(pLeft*1))?pLeft:pLeft+'px';
+        if (pText)vNewNode.appendChild(document.createTextNode(pText));
+        if (pDisplay)vNewNode.style.display=pDisplay;
+        if (pColspan)vNewNode.colSpan=pColspan;
+        return vNewNode;
+    };
+
+    this.Draw=function()
+    {
+        var vMaxDate=new Date();
+        var vMinDate=new Date();
+        var vTmpDate=new Date();
+        var vTaskLeftPx=0;
+        var vTaskRightPx=0;
+        var vTaskWidth=1;
+        var vNumCols=0;
+        var vNumRows=0;
+        var vSingleCell=false;
+        var vID=0;
+        var vMainTable='';
+        var vDateRow=null;
+        var vFirstCellItemRowStr='';
+        var vItemRowStr='';
+        var vColWidth=0;
+        var vColUnit=0;
+        var vChild;
+        var vGroup;
+        var vTaskDiv;
+        var vParDiv;
+
+        if(vTaskList.length>0)
+        {
+            // Process all tasks, reset parent date and completion % if task list has altered
+            if (vProcessNeeded)	JSGantt.processRows(vTaskList, 0, -1, 1, 1, this.getUseSort());
+            vProcessNeeded=false;
+
+            // get overall min/max dates plus padding
+            vMinDate=JSGantt.getMinDate(vTaskList, vFormat);
+            vMaxDate=JSGantt.getMaxDate(vTaskList, vFormat);
+
+            // Calculate chart width variables.
+            if(vFormat=='day') vColWidth=vDayColWidth;
+            else if(vFormat=='week') vColWidth=vWeekColWidth;
+            else if(vFormat=='month') vColWidth=vMonthColWidth;
+            else if(vFormat=='quarter') vColWidth=vQuarterColWidth;
+            else if(vFormat=='hour') vColWidth=vHourColWidth;
+
+            // DRAW the Left-side of the chart (names, resources, comp%)
+            var vLeftHeader=document.createDocumentFragment();
+
+            var vTmpDiv=this.newNode(vLeftHeader, 'div', vDivId+'glisthead', 'glistlbl gcontainercol');
+            var vTmpTab=this.newNode(vTmpDiv, 'table', null, 'gtasktableh');
+            var vTmpTBody=this.newNode(vTmpTab, 'tbody');
+            var vTmpRow=this.newNode(vTmpTBody, 'tr');
+            this.newNode(vTmpRow, 'td', null, 'gtasklist', '\u00A0');
+            var vTmpCell=this.newNode(vTmpRow, 'td', null, 'gspanning gtaskname');
+            vTmpCell.appendChild(this.drawSelector('top'));
+            if(vShowRes==1)this.newNode(vTmpRow, 'td', null, 'gspanning gresource', '\u00A0');
+            if(vShowDur==1)this.newNode(vTmpRow, 'td', null, 'gspanning gduration', '\u00A0');
+            if(vShowComp==1)this.newNode(vTmpRow, 'td', null, 'gspanning gpccomplete', '\u00A0');
+            if(vShowStartDate==1)this.newNode(vTmpRow, 'td', null, 'gspanning gstartdate', '\u00A0');
+            if(vShowEndDate==1)this.newNode(vTmpRow, 'td', null, 'gspanning genddate', '\u00A0');
+
+            vTmpRow=this.newNode(vTmpTBody, 'tr');
+            this.newNode(vTmpRow, 'td', null, 'gtasklist', '\u00A0');
+            this.newNode(vTmpRow, 'td', null, 'gtaskname', '\u00A0');
+            if(vShowRes==1)this.newNode(vTmpRow, 'td', null, 'gtaskheading gresource', vLangs[vLang]['resource']);
+            if(vShowDur==1)this.newNode(vTmpRow, 'td', null, 'gtaskheading gduration', vLangs[vLang]['duration']);
+            if(vShowComp==1)this.newNode(vTmpRow, 'td', null, 'gtaskheading gpccomplete', vLangs[vLang]['comp']);
+            if(vShowStartDate==1)this.newNode(vTmpRow, 'td', null, 'gtaskheading gstartdate', vLangs[vLang]['startdate']);
+            if(vShowEndDate==1)this.newNode(vTmpRow, 'td', null, 'gtaskheading genddate', vLangs[vLang]['enddate']);
+
+            vTmpDiv=this.newNode(vLeftHeader, 'div', null, 'glabelfooter');
+
+            var vLeftTable=document.createDocumentFragment();
+            var vTmpDiv2=this.newNode(vLeftTable, 'div', vDivId+'glistbody', 'glistgrid gcontainercol');
+            this.setListBody(vTmpDiv2);
+            vTmpTab=this.newNode(vTmpDiv2, 'table', null, 'gtasktable');
+            vTmpTBody=this.newNode(vTmpTab, 'tbody');
+
+            for(i=0; i<vTaskList.length; i++)
             {
+                if(vTaskList[i].getGroup()==1) var vBGColor='ggroupitem';
+                else vBGColor='glineitem';
 
-               vNxtDate.setDate(vNxtDate.getDate() + 7);
+                vID=vTaskList[i].getID();
 
-               if( vCurrDate >= vTmpDate && vCurrDate < vNxtDate ) 
-                  vWeekdayColor = "ccccff";
-               else
-                  vWeekdayColor = "ffffff";
+                if((!(vTaskList[i].getParItem() && vTaskList[i].getParItem().getGroup()==2)) || vTaskList[i].getGroup()==2)
+                {
+                    if(vTaskList[i].getVisible()==0) vTmpRow=this.newNode(vTmpTBody, 'tr', vDivId+'child_'+vID, 'gname '+vBGColor, null, null, null, 'none');
+                    else vTmpRow=this.newNode(vTmpTBody, 'tr', vDivId+'child_'+vID, 'gname '+vBGColor);
+                    vTaskList[i].setListChildRow(vTmpRow);
+                    this.newNode(vTmpRow, 'td', null, 'gtasklist', '\u00A0');
+                    vTmpCell=this.newNode(vTmpRow, 'td', null, 'gtaskname');
+
+                    var vCellContents ='';
+                    for(j=1; j<vTaskList[i].getLevel(); j++)
+                    {
+                        vCellContents+='\u00A0\u00A0\u00A0\u00A0';
+                    }
 
-               if(vNxtDate <= vMaxDate) {
-                  vDateRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; HEIGHT: 19px; BORDER-LEFT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center width:'+vColWidth+'px><div style="width: '+vColWidth+'px">' + (vTmpDate.getMonth()+1) + '/' + vTmpDate.getDate() + '</div></td>';
-                  if( vCurrDate >= vTmpDate && vCurrDate < vNxtDate ) 
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-                  else
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
+                    if(vTaskList[i].getGroup()==1)
+                    {
+                        vTmpDiv=this.newNode(vTmpCell, 'div', null, null, vCellContents);
+                        var vTmpSpan=this.newNode(vTmpDiv, 'span', vDivId+'group_'+vID, 'gfoldercollapse', (vTaskList[i].getOpen()==1)?'-':'+');
+                        vTaskList[i].setGroupSpan(vTmpSpan);
+                        JSGantt.addFolderListeners(this, vTmpSpan, vID);
+                        vTmpDiv.appendChild(document.createTextNode('\u00A0'+vTaskList[i].getName()));
+                    }
+                    else
+                    {
+                        vCellContents+='\u00A0\u00A0\u00A0\u00A0';
+                        vTmpDiv=this.newNode(vTmpCell, 'div', null, null, vCellContents+vTaskList[i].getName());
+                    }
 
-               } else {
-                  vDateRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; HEIGHT: 19px; BORDER-LEFT: #efefef 1px solid; bgcolor=#' + vWeekdayColor + ' BORDER-RIGHT: #efefef 1px solid;" align=center width:'+vColWidth+'px><div style="width: '+vColWidth+'px">' + (vTmpDate.getMonth()+1) + '/' + vTmpDate.getDate() + '</div></td>';
-                  if( vCurrDate >= vTmpDate && vCurrDate < vNxtDate ) 
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; BORDER-RIGHT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-                  else
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; BORDER-RIGHT: #efefef 1px solid;" align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
+                    if(vShowRes==1)
+                    {
+                        /*vTmpCell=this.newNode(vTmpRow, 'td', null, 'gresource');
+                         vTmpDiv=this.newNode(vTmpCell, 'div', null, null, vTaskList[i].getResource());*/
 
-               }
+                        vTmpCell=this.newNode(vTmpRow, 'td', null, 'gresource');
+                        var vTmpNode=this.newNode(vTmpCell, 'div', null, '');
+                        vTmpNode=this.newNode(vTmpNode, 'a', null, '', vLangs[vLang]['moreinfo']);
+                        vTmpNode.setAttribute('href',vTaskList[i].getLink());
+                    }
+                    if(vShowDur==1)
+                    {
+                        vTmpCell=this.newNode(vTmpRow, 'td', null, 'gduration');
+                        vTmpDiv=this.newNode(vTmpCell, 'div', null, null, vTaskList[i].getDuration(vFormat, vLangs[vLang]));
+                    }
+                    if(vShowComp==1)
+                    {
+                        vTmpCell=this.newNode(vTmpRow, 'td', null, 'gpccomplete');
+                        vTmpDiv=this.newNode(vTmpCell, 'div', null, null, vTaskList[i].getCompStr());
+                    }
+                    if(vShowStartDate==1)
+                    {
+                        vTmpCell=this.newNode(vTmpRow, 'td', null, 'gstartdate');
+                        vTmpDiv=this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTaskList[i].getStart(), vDateTaskTableDisplayFormat, vLangs[vLang]));
+                    }
+                    if(vShowEndDate==1)
+                    {
+                        vTmpCell=this.newNode(vTmpRow, 'td', null, 'genddate');
+                        vTmpDiv=this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTaskList[i].getEnd(), vDateTaskTableDisplayFormat, vLangs[vLang]));
+                    }
+                    vNumRows++;
+                }
+            }
 
-               vTmpDate.setDate(vTmpDate.getDate() + 7);
+            // DRAW the date format selector at bottom left.
+            vTmpRow=this.newNode(vTmpTBody, 'tr');
+            this.newNode(vTmpRow, 'td', null, 'gtasklist', '\u00A0');
+            vTmpCell=this.newNode(vTmpRow, 'td', null, 'gspanning gtaskname');
+            vTmpCell.appendChild(this.drawSelector('bottom'));
+            if(vShowRes==1)this.newNode(vTmpRow, 'td', null, 'gspanning gresource', '\u00A0');
+            if(vShowDur==1)this.newNode(vTmpRow, 'td', null, 'gspanning gduration', '\u00A0');
+            if(vShowComp==1)this.newNode(vTmpRow, 'td', null, 'gspanning gpccomplete', '\u00A0');
+            if(vShowStartDate==1)this.newNode(vTmpRow, 'td', null, 'gspanning gstartdate', '\u00A0');
+            if(vShowEndDate==1)this.newNode(vTmpRow, 'td', null, 'gspanning genddate', '\u00A0');
+            // Add some white space so the vertical scroll distance should always be greater
+            // than for the right pane (keep to a minimum as it is seen in unconstrained height designs)
+            this.newNode(vTmpDiv2, 'br');
+            this.newNode(vTmpDiv2, 'br');
 
-            }
+            // Draw the Chart Rows
+            var vRightHeader=document.createDocumentFragment();
+            vTmpDiv=this.newNode(vRightHeader, 'div', vDivId+'gcharthead', 'gchartlbl gcontainercol');
+            this.setChartHead(vTmpDiv);
+            vTmpTab=this.newNode(vTmpDiv, 'table', vDivId+'chartTableh', 'gcharttableh');
+            vTmpTBody=this.newNode(vTmpTab, 'tbody');
+            vTmpRow=this.newNode(vTmpTBody, 'tr');
+
+            vTmpDate.setFullYear(vMinDate.getFullYear(), vMinDate.getMonth(), vMinDate.getDate());
+            if(vFormat=='hour')vTmpDate.setHours(vMinDate.getHours());
+            else vTmpDate.setHours(0);
+            vTmpDate.setMinutes(0);
+            vTmpDate.setSeconds(0);
+            vTmpDate.setMilliseconds(0);
 
-	         else if(vFormat == 'month')
+            var vColSpan=1;
+            // Major Date Header
+            while(vTmpDate.getTime()<=vMaxDate.getTime())
             {
+                var vHeaderCellClass='gmajorheading';
+                vCellContents='';
 
-               vNxtDate.setFullYear(vTmpDate.getFullYear(), vTmpDate.getMonth(), vMonthDaysArr[vTmpDate.getMonth()]);
-               if( vCurrDate >= vTmpDate && vCurrDate < vNxtDate ) 
-                  vWeekdayColor = "ccccff";
-               else
-                  vWeekdayColor = "ffffff";
-
-               if(vNxtDate <= vMaxDate) {
-                  vDateRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; HEIGHT: 19px; BORDER-LEFT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center width:'+vColWidth+'px><div style="width: '+vColWidth+'px">' + vMonthArr[vTmpDate.getMonth()].substr(0,3) + '</div></td>';
-                  if( vCurrDate >= vTmpDate && vCurrDate < vNxtDate ) 
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-                  else
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-               } else {
-                  vDateRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; HEIGHT: 19px; BORDER-LEFT: #efefef 1px solid; BORDER-RIGHT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center width:'+vColWidth+'px><div style="width: '+vColWidth+'px">' + vMonthArr[vTmpDate.getMonth()].substr(0,3) + '</div></td>';
-                  if( vCurrDate >= vTmpDate && vCurrDate < vNxtDate ) 
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; BORDER-RIGHT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-                  else
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; BORDER-RIGHT: #efefef 1px solid;" align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-               }
-
-               vTmpDate.setDate(vTmpDate.getDate() + 1);
-
-               while(vTmpDate.getDate() > 1) 
-               {
-                  vTmpDate.setDate(vTmpDate.getDate() + 1);
-               }
+                if(vFormat=='day')
+                {
+                    vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass, null, null, null, null, 7);
+                    vCellContents+=JSGantt.formatDateStr(vTmpDate,vDayMajorDateDisplayFormat,vLangs[vLang]);
+                    vTmpDate.setDate(vTmpDate.getDate()+6);
 
+                    if (vShowEndWeekDate==1) vCellContents+=' - ' +JSGantt.formatDateStr(vTmpDate, vDayMajorDateDisplayFormat,vLangs[vLang]);
+
+                    this.newNode(vTmpCell, 'div', null, null, vCellContents, vColWidth*7);
+                    vTmpDate.setDate(vTmpDate.getDate()+1);
+                }
+                else if(vFormat=='week')
+                {
+                    vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass, null, vColWidth);
+                    this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vWeekMajorDateDisplayFormat,vLangs[vLang]), vColWidth);
+                    vTmpDate.setDate(vTmpDate.getDate()+7);
+                }
+                else if(vFormat=='month')
+                {
+                    vColSpan=(12-vTmpDate.getMonth());
+                    if (vTmpDate.getFullYear()==vMaxDate.getFullYear()) vColSpan-=(11-vMaxDate.getMonth());
+                    vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass, null, null, null, null, vColSpan);
+                    this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vMonthMajorDateDisplayFormat,vLangs[vLang]), vColWidth*vColSpan);
+                    vTmpDate.setFullYear(vTmpDate.getFullYear()+1,0,1);
+                }
+                else if(vFormat=='quarter')
+                {
+                    vColSpan=(4-Math.floor(vTmpDate.getMonth()/3));
+                    if (vTmpDate.getFullYear()==vMaxDate.getFullYear()) vColSpan-=(3-Math.floor(vMaxDate.getMonth()/3));
+                    vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass, null, null, null, null, vColSpan);
+                    this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vQuarterMajorDateDisplayFormat,vLangs[vLang]), vColWidth*vColSpan);
+                    vTmpDate.setFullYear(vTmpDate.getFullYear()+1,0,1);
+                }
+                else if(vFormat=='hour')
+                {
+                    vColSpan=(24-vTmpDate.getHours());
+                    if (vTmpDate.getFullYear()==vMaxDate.getFullYear() &&
+                        vTmpDate.getMonth()==vMaxDate.getMonth() &&
+                        vTmpDate.getDate()==vMaxDate.getDate()) vColSpan-=(23-vMaxDate.getHours());
+                    vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass, null, null, null, null, vColSpan);
+                    this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vHourMajorDateDisplayFormat,vLangs[vLang]), vColWidth*vColSpan);
+                    vTmpDate.setHours(0);
+                    vTmpDate.setDate(vTmpDate.getDate()+1);
+                }
             }
 
-	         else if(vFormat == 'quarter')
-            {
+            vTmpRow=this.newNode(vTmpTBody, 'tr');
 
-               vNxtDate.setDate(vNxtDate.getDate() + 122);
-               if( vTmpDate.getMonth()==0 || vTmpDate.getMonth()==1 || vTmpDate.getMonth()==2 )
-                  vNxtDate.setFullYear(vTmpDate.getFullYear(), 2, 31);
-               else if( vTmpDate.getMonth()==3 || vTmpDate.getMonth()==4 || vTmpDate.getMonth()==5 )
-                  vNxtDate.setFullYear(vTmpDate.getFullYear(), 5, 30);
-               else if( vTmpDate.getMonth()==6 || vTmpDate.getMonth()==7 || vTmpDate.getMonth()==8 )
-                  vNxtDate.setFullYear(vTmpDate.getFullYear(), 8, 30);
-               else if( vTmpDate.getMonth()==9 || vTmpDate.getMonth()==10 || vTmpDate.getMonth()==11 )
-                  vNxtDate.setFullYear(vTmpDate.getFullYear(), 11, 31);
-
-               if( vCurrDate >= vTmpDate && vCurrDate < vNxtDate ) 
-                  vWeekdayColor = "ccccff";
-               else
-                  vWeekdayColor = "ffffff";
-
-               if(vNxtDate <= vMaxDate) {
-                  vDateRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; HEIGHT: 19px; BORDER-LEFT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center width:'+vColWidth+'px><div style="width: '+vColWidth+'px">Qtr. ' + vQuarterArr[vTmpDate.getMonth()] + '</div></td>';
-                  if( vCurrDate >= vTmpDate && vCurrDate < vNxtDate ) 
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-                  else
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid;" align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-               } else {
-                  vDateRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; HEIGHT: 19px; BORDER-LEFT: #efefef 1px solid; BORDER-RIGHT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center width:'+vColWidth+'px><div style="width: '+vColWidth+'px">Qtr. ' + vQuarterArr[vTmpDate.getMonth()] + '</div></td>';
-                  if( vCurrDate >= vTmpDate && vCurrDate < vNxtDate ) 
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; BORDER-RIGHT: #efefef 1px solid;" bgcolor=#' + vWeekdayColor + ' align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-                  else 
-                     vItemRowStr += '<td class="ghead" style="BORDER-TOP: #efefef 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #efefef 1px solid; BORDER-RIGHT: #efefef 1px solid;" align=center><div style="width: '+vColWidth+'px">&nbsp&nbsp</div></td>';
-               }
-
-               vTmpDate.setDate(vTmpDate.getDate() + 81);
-
-               while(vTmpDate.getDate() > 1) 
-               {
-                  vTmpDate.setDate(vTmpDate.getDate() + 1);
-               }
+            // Minor Date header and Cell Rows
+            vTmpDate.setFullYear(vMinDate.getFullYear(), vMinDate.getMonth(), vMinDate.getDate(), vMinDate.getHours());
+            if(vFormat=='hour')vTmpDate.setHours(vMinDate.getHours());
+            vNumCols=0;
 
-            }
-         }
+            while(vTmpDate.getTime()<=vMaxDate.getTime())
+            {
+                vHeaderCellClass='gminorheading';
+                var vCellClass='gtaskcell';
 
-         vRightTable += vDateRowStr + '</TR>';
-         vRightTable += '</TBODY></TABLE>';
+                if(vFormat=='day')
+                {
+                    if(vTmpDate.getDay()%6==0)
+                    {
+                        vHeaderCellClass+='wkend';
+                        vCellClass+='wkend';
+                    }
 
-         // Draw each row
+                    if(vTmpDate<=vMaxDate)
+                    {
+                        vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass);
+                        this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vDayMinorDateDisplayFormat,vLangs[vLang]), vColWidth);
+                        vNumCols++;
+                    }
 
-         for(i = 0; i < vTaskList.length; i++)
+                    vTmpDate.setDate(vTmpDate.getDate()+1);
+                }
+                else if(vFormat=='week')
+                {
+                    if(vTmpDate<=vMaxDate)
+                    {
+                        vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass);
+                        this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vWeekMinorDateDisplayFormat,vLangs[vLang]), vColWidth);
+                        vNumCols++;
+                    }
 
-         {
+                    vTmpDate.setDate(vTmpDate.getDate()+7);
+                }
+                else if(vFormat=='month')
+                {
+                    if(vTmpDate<=vMaxDate)
+                    {
+                        vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass);
+                        this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vMonthMinorDateDisplayFormat,vLangs[vLang]), vColWidth);
+                        vNumCols++;
+                    }
 
-            vTmpDate.setFullYear(vMinDate.getFullYear(), vMinDate.getMonth(), vMinDate.getDate());
-            vTaskStart = vTaskList[i].getStart();
-            vTaskEnd   = vTaskList[i].getEnd();
-
-            vNumCols = 0;
-            vID = vTaskList[i].getID();
-
-           // vNumUnits = Math.ceil((vTaskList[i].getEnd() - vTaskList[i].getStart()) / (24 * 60 * 60 * 1000)) + 1;
-            vNumUnits = (vTaskList[i].getEnd() - vTaskList[i].getStart()) / (24 * 60 * 60 * 1000) + 1;
-	       if (vFormat=='hour')
-	       {
-                vNumUnits = (vTaskList[i].getEnd() - vTaskList[i].getStart()) / (  60 * 1000) + 1;
-	       }
-	       else if (vFormat=='minute')
-	       {
-                vNumUnits = (vTaskList[i].getEnd() - vTaskList[i].getStart()) / (  60 * 1000) + 1;
-	       }
-	       
-	         if(vTaskList[i].getVisible() == 0) 
-               vRightTable += '<DIV id=childgrid_' + vID + ' style="position:relative; display:none;">';
-            else
-		         vRightTable += '<DIV id=childgrid_' + vID + ' style="position:relative">';
-            
-            if( vTaskList[i].getMile()) {
+                    vTmpDate.setDate(vTmpDate.getDate()+1);
 
-               vRightTable += '<DIV><TABLE style="position:relative; top:0px; width: ' + vChartWidth + 'px;" cellSpacing=0 cellPadding=0 border=0>' +
-                  '<TR id=childrow_' + vID + ' class=yesdisplay style="HEIGHT: 20px" onMouseover=g.mouseOver(this,' + vID + ',"right","mile") onMouseout=g.mouseOut(this,' + vID + ',"right","mile")>' + vItemRowStr + '</TR></TABLE></DIV>';
+                    while(vTmpDate.getDate()>1)
+                    {
+                        vTmpDate.setDate(vTmpDate.getDate()+1);
+                    }
+                }
+                else if(vFormat=='quarter')
+                {
+                    if(vTmpDate<=vMaxDate)
+                    {
+                        vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass);
+                        this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vQuarterMinorDateDisplayFormat,vLangs[vLang]), vColWidth);
+                        vNumCols++;
+                    }
+
+                    vTmpDate.setDate(vTmpDate.getDate()+81);
 
-               // Build date string for Title
-               vDateRowStr = JSGantt.formatDateStr(vTaskStart,vDateDisplayFormat);
+                    while(vTmpDate.getDate()>1) vTmpDate.setDate(vTmpDate.getDate()+1);
+                }
+                else if(vFormat=='hour')
+                {
+                    for(i=vTmpDate.getHours();i<24;i++)
+                    {
+                        vTmpDate.setHours(i);//works around daylight savings but may look a little odd on days where the clock goes forward
+                        if(vTmpDate<=vMaxDate)
+                        {
+                            vTmpCell=this.newNode(vTmpRow, 'td', null, vHeaderCellClass);
+                            this.newNode(vTmpCell, 'div', null, null, JSGantt.formatDateStr(vTmpDate,vHourMinorDateDisplayFormat,vLangs[vLang]), vColWidth);
+                            vNumCols++;
+                        }
+                    }
+                    vTmpDate.setHours(0);
+                    vTmpDate.setDate(vTmpDate.getDate()+1);
+                }
+            }
+            vDateRow=vTmpRow;
 
-               vTaskLeft = (Date.parse(vTaskList[i].getStart()) - Date.parse(vMinDate)) / (24 * 60 * 60 * 1000);
-               vTaskRight = 1;
+            vTaskLeftPx=(vNumCols *(vColWidth+1))+1;
 
-  	            vRightTable +=
-                  '<div id=bardiv_' + vID + ' style="position:absolute; top:0px; left:' + Math.ceil((vTaskLeft * (vDayWidth) + 1)) + 'px; height: 18px; width:160px; overflow:hidden;">' +
-                  '  <div id=taskbar_' + vID + ' title="' + vTaskList[i].getName() + ': ' + vDateRowStr + '" style="height: 16px; width:12px; overflow:hidden; cursor: pointer;" onclick=JSGantt.taskLink("' + vTaskList[i].getLink() + '",300,200);>';
+            if(vUseSingleCell!=0 && vUseSingleCell<(vNumCols*vNumRows))vSingleCell=true;
 
-               if(vTaskList[i].getCompVal() < 100)
- 		            {vRightTable += '&loz;</div>' ;}
-               else
- 		           { vRightTable += '&diams;</div>' ;}
+            this.newNode(vTmpDiv, 'div', null, 'rhscrpad', null, null, vTaskLeftPx+1);
 
-                        if( g.getCaptionType() ) {
-                           vCaptionStr = '';
-                           switch( g.getCaptionType() ) {           
-                              case 'Caption':    vCaptionStr = vTaskList[i].getCaption();  break;
-                              case 'Resource':   vCaptionStr = vTaskList[i].getResource();  break;
-                              case 'Duration':   vCaptionStr = vTaskList[i].getDuration(vFormat);  break;
-                              case 'Complete':   vCaptionStr = vTaskList[i].getCompStr();  break;
-		                     }
-                           //vRightTable += '<div style="FONT-SIZE:12px; position:absolute; left: 6px; top:1px;">' + vCaptionStr + '</div>';
-                           vRightTable += '<div style="FONT-SIZE:12px; position:absolute; top:2px; width:120px; left:12px">' + vCaptionStr + '</div>';
-	                  };
+            var vRightTable=document.createDocumentFragment();
+            vTmpDiv=this.newNode(vRightTable, 'div', vDivId+'gchartbody', 'gchartgrid gcontainercol');
+            this.setChartBody(vTmpDiv);
+            vTmpTab=this.newNode(vTmpDiv, 'table', vDivId+'chartTable', 'gcharttable', null, vTaskLeftPx);
+            this.setChartTable(vTmpTab);
+            this.newNode(vTmpDiv, 'div', null, 'rhscrpad', null, null, vTaskLeftPx+1);
+            vTmpTBody=this.newNode(vTmpTab, 'tbody');
 
-  	            vRightTable += '</div>';
+            // Draw each row
 
+            var i=0;
+            var j=0;
+            for(i=0; i<vTaskList.length; i++)
+            {
+                var curTaskStart=vTaskList[i].getStart();
+                var curTaskEnd=vTaskList[i].getEnd();
+                if ((curTaskEnd.getTime()-(curTaskEnd.getTimezoneOffset()*60000))%(86400000)==0) curTaskEnd=new Date(curTaskEnd.getFullYear(), curTaskEnd.getMonth(), curTaskEnd.getDate()+1, curTaskEnd.getHours(), curTaskEnd.getMinutes(), curTaskEnd.getSeconds()); // add 1 day here to simplify calculations below
 
-            } else {
+                vTaskLeftPx=JSGantt.getOffset(vMinDate, curTaskStart, vColWidth, vFormat);
+                vTaskRightPx=JSGantt.getOffset(curTaskStart, curTaskEnd, vColWidth, vFormat);
 
-               // Build date string for Title
-               vDateRowStr = JSGantt.formatDateStr(vTaskStart,vDateDisplayFormat) + ' - ' + JSGantt.formatDateStr(vTaskEnd,vDateDisplayFormat);
+                vID=vTaskList[i].getID();
+                var vComb=(vTaskList[i].getParItem() && vTaskList[i].getParItem().getGroup()==2);
+                var vCellFormat='';
 
-                if (vFormat=='minute')
+                var vTmpItem=vTaskList[i];
+                var vCaptionStr='';
+                var vCaptClass=null;
+                if(vTaskList[i].getMile() && !vComb)
                 {
-                    vTaskRight = (Date.parse(vTaskList[i].getEnd()) - Date.parse(vTaskList[i].getStart())) / ( 60 * 1000) + 1/vColUnit;
-                    vTaskLeft = Math.ceil((Date.parse(vTaskList[i].getStart()) - Date.parse(vMinDate)) / ( 60 * 1000));
+                    vTmpRow=this.newNode(vTmpTBody, 'tr', vDivId+'childrow_'+vID, 'gmileitem gmile'+vFormat, null, null, null, ((vTaskList[i].getVisible()==0)? 'none' : null));
+                    vTaskList[i].setChildRow(vTmpRow);
+                    JSGantt.addThisRowListeners(this, vTaskList[i].getListChildRow(), vTmpRow);
+                    vTmpCell=this.newNode(vTmpRow, 'td', null, 'gtaskcell');
+                    vTmpDiv=this.newNode(vTmpCell, 'div', null, 'gtaskcelldiv', '\u00A0\u00A0');
+                    vTmpDiv=this.newNode(vTmpDiv, 'div', vDivId+'bardiv_'+vID, 'gtaskbarcontainer', null, 12, vTaskLeftPx-6);
+                    vTaskList[i].setBarDiv(vTmpDiv);
+                    vTmpDiv2=this.newNode(vTmpDiv, 'div', vDivId+'taskbar_'+vID, vTaskList[i].getClass(), null, 12);
+                    vTaskList[i].setTaskDiv(vTmpDiv2);
+
+                    if(vTaskList[i].getCompVal()<100)
+                        vTmpDiv2.appendChild(document.createTextNode('\u25CA'));
+                    else
+                    {
+                        vTmpDiv2=this.newNode(vTmpDiv2, 'div', null, 'gmilediamond');
+                        this.newNode(vTmpDiv2, 'div', null, 'gmdtop');
+                        this.newNode(vTmpDiv2, 'div', null, 'gmdbottom');
+                    }
+
+                    vCaptClass='gmilecaption';
+
+                    if(!vSingleCell && !vComb)
+                    {
+                        vCellFormat='';
+                        for(j=0; j<vNumCols-1; j++)
+                        {
+                            if(vFormat=='day'&&((j%7==4)||(j%7==5))) vCellFormat='gtaskcellwkend';
+                            else vCellFormat='gtaskcell';
+                            this.newNode(vTmpRow, 'td', null, vCellFormat, '\u00A0\u00A0');
+                        }
+                    }
                 }
-                else if (vFormat=='hour')
+                else
                 {
-                    vTaskRight = (Date.parse(vTaskList[i].getEnd()) - Date.parse(vTaskList[i].getStart())) / ( 60 * 60 * 1000) + 1/vColUnit;
-                    vTaskLeft = (Date.parse(vTaskList[i].getStart()) - Date.parse(vMinDate)) / ( 60 * 60 * 1000);
+                    vTaskWidth=vTaskRightPx-1;
+
+                    // Draw Group Bar which has outer div with inner group div and several small divs to left and right to create angled-end indicators
+                    if(vTaskList[i].getGroup())
+                    {
+                        vTaskWidth=(vTaskWidth>vMinGpLen && vTaskWidth<vMinGpLen*2)? vMinGpLen*2 : vTaskWidth; // Expand to show two end points
+                        vTaskWidth=(vTaskWidth<vMinGpLen)? vMinGpLen : vTaskWidth; // expand to show one end point
+
+                        vTmpRow=this.newNode(vTmpTBody, 'tr', vDivId+'childrow_'+vID, ((vTaskList[i].getGroup()==2)?'glineitem gitem':'ggroupitem ggroup')+vFormat, null, null, null, ((vTaskList[i].getVisible()==0)? 'none' : null));
+                        vTaskList[i].setChildRow(vTmpRow);
+                        JSGantt.addThisRowListeners(this, vTaskList[i].getListChildRow(), vTmpRow);
+                        vTmpCell=this.newNode(vTmpRow, 'td', null, 'gtaskcell');
+                        vTmpDiv=this.newNode(vTmpCell, 'div', null, 'gtaskcelldiv', '\u00A0\u00A0');
+                        vTaskList[i].setCellDiv(vTmpDiv);
+                        if(vTaskList[i].getGroup()==1)
+                        {
+                            vTmpDiv=this.newNode(vTmpDiv, 'div', vDivId+'bardiv_'+vID, 'gtaskbarcontainer', null, vTaskWidth, vTaskLeftPx);
+                            vTaskList[i].setBarDiv(vTmpDiv);
+                            vTmpDiv2=this.newNode(vTmpDiv, 'div', vDivId+'taskbar_'+vID, vTaskList[i].getClass(), null, vTaskWidth);
+                            vTaskList[i].setTaskDiv(vTmpDiv2);
+
+                            this.newNode(vTmpDiv2, 'div', vDivId+'complete_'+vID, vTaskList[i].getClass() +'complete', null, vTaskList[i].getCompStr());
+
+                            this.newNode(vTmpDiv, 'div', null, vTaskList[i].getClass() +'endpointleft');
+                            if (vTaskWidth>=vMinGpLen*2) this.newNode(vTmpDiv, 'div', null, vTaskList[i].getClass() +'endpointright');
+
+                            vCaptClass='ggroupcaption';
+                        }
+
+                        if(!vSingleCell && !vComb)
+                        {
+                            vCellFormat='';
+                            for(j=0; j<vNumCols-1; j++)
+                            {
+                                if(vFormat=='day'&&((j%7==4)||(j%7==5))) vCellFormat='gtaskcellwkend';
+                                else vCellFormat='gtaskcell';
+                                this.newNode(vTmpRow, 'td', null, vCellFormat, '\u00A0\u00A0');
+                            }
+                        }
+                    }
+                    else
+                    {
+                        vTaskWidth=(vTaskWidth<=0)? 1 : vTaskWidth;
+
+                        if(vComb)
+                        {
+                            vTmpDiv=vTaskList[i].getParItem().getCellDiv();
+                        }
+                        else
+                        {
+                            vTmpRow=this.newNode(vTmpTBody, 'tr', vDivId+'childrow_'+vID, 'glineitem gitem'+vFormat, null, null, null, ((vTaskList[i].getVisible()==0)? 'none' : null));
+                            vTaskList[i].setChildRow(vTmpRow);
+                            JSGantt.addThisRowListeners(this, vTaskList[i].getListChildRow(), vTmpRow);
+                            vTmpCell=this.newNode(vTmpRow, 'td', null, 'gtaskcell');
+                            vTmpDiv=this.newNode(vTmpCell, 'div', null, 'gtaskcelldiv', '\u00A0\u00A0');
+                        }
+                        // Draw Task Bar which has colored bar div, and opaque completion div
+                        vTmpDiv=this.newNode(vTmpDiv, 'div', vDivId+'bardiv_'+vID, 'gtaskbarcontainer', null, vTaskWidth, vTaskLeftPx);
+                        vTaskList[i].setBarDiv(vTmpDiv);
+                        vTmpDiv2=this.newNode(vTmpDiv, 'div', vDivId+'taskbar_'+vID, vTaskList[i].getClass(), null, vTaskWidth);
+                        vTaskList[i].setTaskDiv(vTmpDiv2);
+                        this.newNode(vTmpDiv2, 'div', vDivId+'complete_'+vID, vTaskList[i].getClass() +'complete', null, vTaskList[i].getCompStr());
+
+                        if(vComb)vTmpItem=vTaskList[i].getParItem();
+                        if(!vComb || (vComb && vTaskList[i].getParItem().getEnd()==vTaskList[i].getEnd())) vCaptClass='gcaption';
+
+                        if(!vSingleCell && !vComb)
+                        {
+                            vCellFormat='';
+                            for(j=0; j<vNumCols-1; j++)
+                            {
+                                if(vFormat=='day'&&((j%7==4)||(j%7==5))) vCellFormat='gtaskcellwkend';
+                                else vCellFormat='gtaskcell';
+                                this.newNode(vTmpRow, 'td', null, vCellFormat, '\u00A0\u00A0');
+                            }
+                        }
+                    }
                 }
-                else
+
+                if(this.getCaptionType() && vCaptClass!==null)
                 {
-                    vTaskRight = (Date.parse(vTaskList[i].getEnd()) - Date.parse(vTaskList[i].getStart())) / (24 * 60 * 60 * 1000) + 1/vColUnit;
-                    vTaskLeft = Math.ceil((Date.parse(vTaskList[i].getStart()) - Date.parse(vMinDate)) / (24 * 60 * 60 * 1000));
-                    if (vFormat='day')
+                    switch(this.getCaptionType())
                     {
-                        var tTime=new Date();
-                        tTime.setTime(Date.parse(vTaskList[i].getStart()));
-                        if (tTime.getMinutes() > 29)
-                            vTaskLeft+=.5;
+                        case 'Caption': var vCaptionStr=vTmpItem.getCaption(); break;
+                        case 'Resource': vCaptionStr=vTmpItem.getResource(); break;
+                        case 'Duration': vCaptionStr=vTmpItem.getDuration(vFormat, vLangs[vLang]); break;
+                        case 'Complete': vCaptionStr=vTmpItem.getCompStr(); break;
                     }
+                    this.newNode(vTmpDiv, 'div', null, vCaptClass, vCaptionStr, 120, (vCaptClass=='gmilecaption')?12:0);
                 }
 
-               // Draw Group Bar  which has outer div with inner group div and several small divs to left and right to create angled-end indicators
-               if( vTaskList[i].getGroup()) {
-                  vRightTable += '<DIV><TABLE style="position:relative; top:0px; width: ' + vChartWidth + 'px;" cellSpacing=0 cellPadding=0 border=0>' +
-                     '<TR id=childrow_' + vID + ' class=yesdisplay style="HEIGHT: 20px" bgColor=#f3f3f3 onMouseover=g.mouseOver(this,' + vID + ',"right","group") onMouseout=g.mouseOut(this,' + vID + ',"right","group")>' + vItemRowStr + '</TR></TABLE></DIV>';
-                  vRightTable +=
-                     '<div id=bardiv_' + vID + ' style="position:absolute; top:5px; left:' + Math.ceil(vTaskLeft * (vDayWidth) + 1) + 'px; height: 7px; width:' + Math.ceil((vTaskRight) * (vDayWidth) - 1) + 'px">' +
-                       '<div id=taskbar_' + vID + ' title="' + vTaskList[i].getName() + ': ' + vDateRowStr + '" class=gtask style="background-color:#000000; height: 7px; width:' + Math.ceil((vTaskRight) * (vDayWidth) -1) + 'px;  cursor: pointer;opacity:0.9;">' +
-                         '<div style="Z-INDEX: -4; float:left; background-color:#666666; height:3px; overflow: hidden; margin-top:1px; ' +
-                               'margin-left:1px; margin-right:1px; filter: alpha(opacity=80); opacity:0.8; width:' + vTaskList[i].getCompStr() + '; ' + 
-                               'cursor: pointer;" onclick=JSGantt.taskLink("' + vTaskList[i].getLink() + '",300,200);>' +
-                           '</div>' +
-                        '</div>' +
-                        '<div style="Z-INDEX: -4; float:left; background-color:#000000; height:4px; overflow: hidden; width:1px;"></div>' +
-                        '<div style="Z-INDEX: -4; float:right; background-color:#000000; height:4px; overflow: hidden; width:1px;"></div>' +
-                        '<div style="Z-INDEX: -4; float:left; background-color:#000000; height:3px; overflow: hidden; width:1px;"></div>' +
-                        '<div style="Z-INDEX: -4; float:right; background-color:#000000; height:3px; overflow: hidden; width:1px;"></div>' +
-                        '<div style="Z-INDEX: -4; float:left; background-color:#000000; height:2px; overflow: hidden; width:1px;"></div>' +
-                        '<div style="Z-INDEX: -4; float:right; background-color:#000000; height:2px; overflow: hidden; width:1px;"></div>' +
-                        '<div style="Z-INDEX: -4; float:left; background-color:#000000; height:1px; overflow: hidden; width:1px;"></div>' +
-                        '<div style="Z-INDEX: -4; float:right; background-color:#000000; height:1px; overflow: hidden; width:1px;"></div>' ;
-
-                        if( g.getCaptionType() ) {
-                           vCaptionStr = '';
-                           switch( g.getCaptionType() ) {           
-                              case 'Caption':    vCaptionStr = vTaskList[i].getCaption();  break;
-                              case 'Resource':   vCaptionStr = vTaskList[i].getResource();  break;
-                              case 'Duration':   vCaptionStr = vTaskList[i].getDuration(vFormat);  break;
-                              case 'Complete':   vCaptionStr = vTaskList[i].getCompStr();  break;
-		                     }
-                           //vRightTable += '<div style="FONT-SIZE:12px; position:absolute; left: 6px; top:1px;">' + vCaptionStr + '</div>';
-                           vRightTable += '<div style="FONT-SIZE:12px; position:absolute; top:-3px; width:120px; left:' + (Math.ceil((vTaskRight) * (vDayWidth) - 1) + 6) + 'px">' + vCaptionStr + '</div>';
-	                  };
-
-                  vRightTable += '</div>' ;
-
-               } else {
-
-                  vDivStr = '<DIV><TABLE style="position:relative; top:0px; width: ' + vChartWidth + 'px;" cellSpacing=0 cellPadding=0 border=0>' +
-                     '<TR id=childrow_' + vID + ' class=yesdisplay style="HEIGHT: 20px" bgColor=#ffffff onMouseover=g.mouseOver(this,' + vID + ',"right","row") onMouseout=g.mouseOut(this,' + vID + ',"right","row")>' + vItemRowStr + '</TR></TABLE></DIV>';
-                  vRightTable += vDivStr;
-                  
-                  // Draw Task Bar  which has outer DIV with enclosed colored bar div, and opaque completion div
-	            vRightTable +=
-                     '<div id=bardiv_' + vID + ' style="position:absolute; top:4px; left:' + Math.ceil(vTaskLeft * (vDayWidth) + 1) + 'px; height:18px; width:' + Math.ceil((vTaskRight) * (vDayWidth) - 1) + 'px">' +
-                        '<div id=taskbar_' + vID + ' title="' + vTaskList[i].getName() + ': ' + vDateRowStr + '" class=gtask style="background-color:#' + vTaskList[i].getColor() +'; height: 13px; width:' + Math.ceil((vTaskRight) * (vDayWidth) - 1) + 'px; cursor: pointer;opacity:0.9;" ' +
-                           'onclick=JSGantt.taskLink("' + vTaskList[i].getLink() + '",300,200); >' +
-                           '<div class=gcomplete style="Z-INDEX: -4; float:left; background-color:black; height:5px; overflow: auto; margin-top:4px; filter: alpha(opacity=40); opacity:0.4; width:' + vTaskList[i].getCompStr() + '; overflow:hidden">' +
-                           '</div>' +
-                        '</div>';
-
-                        if( g.getCaptionType() ) {
-                           vCaptionStr = '';
-                           switch( g.getCaptionType() ) {           
-                              case 'Caption':    vCaptionStr = vTaskList[i].getCaption();  break;
-                              case 'Resource':   vCaptionStr = vTaskList[i].getResource();  break;
-                              case 'Duration':   vCaptionStr = vTaskList[i].getDuration(vFormat);  break;
-                              case 'Complete':   vCaptionStr = vTaskList[i].getCompStr();  break;
-		                     }
-                           //vRightTable += '<div style="FONT-SIZE:12px; position:absolute; left: 6px; top:-3px;">' + vCaptionStr + '</div>';
-                           vRightTable += '<div style="FONT-SIZE:12px; position:absolute; top:-3px; width:120px; left:' + (Math.ceil((vTaskRight) * (vDayWidth) - 1) + 6) + 'px">' + vCaptionStr + '</div>';
-	                  }
-                  vRightTable += '</div>' ;
-
-                  
-
-               }
+                if (vTaskList[i].getTaskDiv() && vTmpDiv)
+                {
+                    // Add Task Info div for tooltip
+                    vTmpDiv2=this.newNode(vTmpDiv, 'div', vDivId+'tt'+vID, null, null, null, null, 'none');
+                    vTmpDiv2.appendChild(this.createTaskInfo(vTaskList[i]));
+                    JSGantt.addTooltipListeners(this, vTaskList[i].getTaskDiv(), vTmpDiv2);
+                }
             }
 
-            vRightTable += '</DIV>';
-
-         }
-
-         vMainTable += vRightTable + '</DIV></TD></TR></TBODY></TABLE></BODY></HTML>';
-
-		   vDiv.innerHTML = vMainTable;
-
-      }
-
-   }; //this.draw
-
-/**
-* Mouseover behaviour for gantt row
-* @method mouseOver
-* @return {Void}
-*/  this.mouseOver = function( pObj, pID, pPos, pType ) {
-      if( pPos == 'right' )  vID = 'child_' + pID;
-      else vID = 'childrow_' + pID;
-      
-      pObj.bgColor = "#ffffaa";
-      vRowObj = JSGantt.findObj(vID);
-      if (vRowObj) vRowObj.bgColor = "#ffffaa";
-   };
-
-/**
-* Mouseout behaviour for gantt row
-* @method mouseOut
-* @return {Void}
-*/  this.mouseOut = function( pObj, pID, pPos, pType ) {
-      if( pPos == 'right' )  vID = 'child_' + pID;
-      else vID = 'childrow_' + pID;
-      
-      pObj.bgColor = "#ffffff";
-      vRowObj = JSGantt.findObj(vID);
-      if (vRowObj) {
-         if( pType == "group") {
-            pObj.bgColor = "#f3f3f3";
-            vRowObj.bgColor = "#f3f3f3";
-         } else {
-            pObj.bgColor = "#ffffff";
-            vRowObj.bgColor = "#ffffff";
-         }
-      }
-   };
+            if(!vSingleCell) vTmpTBody.appendChild(vDateRow.cloneNode(true));
+
+            while(vDiv.hasChildNodes())vDiv.removeChild(vDiv.firstChild);
+            vTmpDiv=this.newNode(vDiv, 'div', null, 'gchartcontainer');
+            vTmpDiv.appendChild(vRightHeader);
+            vTmpDiv.appendChild(vLeftHeader);
+            vTmpDiv.appendChild(vRightTable);
+            vTmpDiv.appendChild(vLeftTable);
+            this.newNode(vTmpDiv, 'div', null, 'ggridfooter');
+            vTmpDiv2=this.newNode(this.getChartBody(), 'div', vDivId+'Lines', 'glinediv');
+            vTmpDiv2.style.visibility='hidden';
+            this.setLines(vTmpDiv2);
+
+            /* Quick hack to show the generated HTML on older browsers - add a '/' to the begining of this line to activate
+             var tmpGenSrc=document.createElement('textarea');
+             tmpGenSrc.appendChild(document.createTextNode(vTmpDiv.innerHTML));
+             vDiv.appendChild(tmpGenSrc);
+             //*/
+            // Now all the content exists, register scroll listeners
+            JSGantt.addScrollListeners(this);
+
+            // now check if we are actually scrolling the pane
+            if (vScrollTo!='')
+            {
+                var vScrollDate=new Date(vMinDate.getTime());
+                var vScrollPx=0;
 
-}; //GanttChart
+                if(vScrollTo.substr(0,2)=='px')
+                {
+                    vScrollPx=parseInt(vScrollTo.substr(2));
+                }
+                else
+                {
+                    vScrollDate=JSGantt.parseDateStr(vScrollTo, this.getDateInputFormat());
+                    if(vFormat=='hour')vScrollDate.setMinutes(0,0,0);
+                    else vScrollDate.setHours(0,0,0,0);
+                    vScrollPx=JSGantt.getOffset(vMinDate, vScrollDate, vColWidth, vFormat);
+                }
+                this.getChartBody().scrollLeft=vScrollPx;
+            }
 
+            if (vMinDate.getTime()<=(new Date()).getTime() && vMaxDate.getTime()>=(new Date()).getTime()) vTodayPx=JSGantt.getOffset(vMinDate, new Date(), vColWidth, vFormat);
+            else vTodayPx=-1;
+            this.DrawDependencies();
+        }
+    }; //this.draw
+
+    this.mouseOver=function(pObj1, pObj2)
+    {
+        if (this.getUseRowHlt())
+        {
+            pObj1.className+=' gitemhighlight';
+            pObj2.className+=' gitemhighlight';
+        }
+    };
+
+    this.mouseOut=function(pObj1, pObj2)
+    {
+        if (this.getUseRowHlt())
+        {
+            pObj1.className=pObj1.className.replace(/(?:^|\s)gitemhighlight(?!\S)/g, '');
+            pObj2.className=pObj2.className.replace(/(?:^|\s)gitemhighlight(?!\S)/g, '');
+        }
+    };
+
+    this.drawSelector=function(pPos)
+    {
+        var vOutput=document.createDocumentFragment();
+        var vDisplay=false;
+
+        for (var i=0; i<vShowSelector.length && !vDisplay; i++)
+        {
+            if (vShowSelector[i].toLowerCase()==pPos.toLowerCase()) vDisplay=true;
+        }
+
+        if (vDisplay)
+        {
+            var vTmpDiv=this.newNode(vOutput, 'div', null, 'gselector', vLangs[vLang]['format']+':');
+
+            if (vFormatArr.join().toLowerCase().indexOf('hour')!=-1)
+                JSGantt.addFormatListeners(this, 'hour', this.newNode(vTmpDiv, 'span', vDivId+'formathour'+pPos, 'gformlabel'+((vFormat=='hour')?' gselected':''), vLangs[vLang]['hour']));
+
+            if (vFormatArr.join().toLowerCase().indexOf('day')!=-1)
+                JSGantt.addFormatListeners(this, 'day', this.newNode(vTmpDiv, 'span', vDivId+'formatday'+pPos, 'gformlabel'+((vFormat=='day')?' gselected':''), vLangs[vLang]['day']));
+
+            if (vFormatArr.join().toLowerCase().indexOf('week')!=-1)
+                JSGantt.addFormatListeners(this, 'week', this.newNode(vTmpDiv, 'span', vDivId+'formatweek'+pPos, 'gformlabel'+((vFormat=='week')?' gselected':''), vLangs[vLang]['week']));
+
+            if (vFormatArr.join().toLowerCase().indexOf('month')!=-1)
+                JSGantt.addFormatListeners(this, 'month', this.newNode(vTmpDiv, 'span', vDivId+'formatmonth'+pPos, 'gformlabel'+((vFormat=='month')?' gselected':''), vLangs[vLang]['month']));
+
+            if (vFormatArr.join().toLowerCase().indexOf('quarter')!=-1)
+                JSGantt.addFormatListeners(this, 'quarter', this.newNode(vTmpDiv, 'span', vDivId+'formatquarter'+pPos, 'gformlabel'+((vFormat=='quarter')?' gselected':''), vLangs[vLang]['quarter']));
+        }
+        else
+        {
+            this.newNode(vOutput, 'div', null, 'gselector');
+        }
+        return vOutput;
+    };
+
+    this.createTaskInfo=function(pTask)
+    {
+        var vTmpDiv;
+        var vTaskInfoBox=document.createDocumentFragment();
+        var vTaskInfo=this.newNode(vTaskInfoBox, 'div', null, 'gTaskInfo');
+        this.newNode(vTaskInfo, 'span', null, 'gTtTitle', pTask.getName());
+        if(vShowTaskInfoStartDate==1){
+            vTmpDiv=this.newNode(vTaskInfo, 'div', null, 'gTILine gTIsd');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskLabel', vLangs[vLang]['startdate']+': ');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskText', JSGantt.formatDateStr(pTask.getStart(), vDateTaskDisplayFormat,vLangs[vLang]));
+        }
+        if(vShowTaskInfoEndDate==1){
+            vTmpDiv=this.newNode(vTaskInfo, 'div', null, 'gTILine gTIed');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskLabel', vLangs[vLang]['enddate']+': ');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskText', JSGantt.formatDateStr(pTask.getEnd(), vDateTaskDisplayFormat,vLangs[vLang]));
+        }
+        if(vShowTaskInfoDur==1 && !pTask.getMile()){
+            vTmpDiv=this.newNode(vTaskInfo, 'div', null, 'gTILine gTId');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskLabel', vLangs[vLang]['duration']+': ');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskText', pTask.getDuration(vFormat, vLangs[vLang]));
+        }
+        if(vShowTaskInfoComp==1){
+            vTmpDiv=this.newNode(vTaskInfo, 'div', null, 'gTILine gTIc');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskLabel', vLangs[vLang]['completion']+': ');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskText', pTask.getCompStr());
+        }
+        if(vShowTaskInfoRes==1){
+            vTmpDiv=this.newNode(vTaskInfo, 'div', null, 'gTILine gTIr');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskLabel', vLangs[vLang]['resource']+': ');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskText', pTask.getResource());
+        }
+        if(vShowTaskInfoLink==1 && pTask.getLink()!=''){
+            vTmpDiv=this.newNode(vTaskInfo, 'div', null, 'gTILine gTIl');
+            var vTmpNode=this.newNode(vTmpDiv, 'span', null, 'gTaskLabel');
+            vTmpNode=this.newNode(vTmpNode, 'a', null, 'gTaskText', vLangs[vLang]['moreinfo']);
+            vTmpNode.setAttribute('href',pTask.getLink());
+        }
+        if(vShowTaskInfoNotes==1){
+            vTmpDiv=this.newNode(vTaskInfo, 'div', null, 'gTILine gTIn');
+            this.newNode(vTmpDiv, 'span', null, 'gTaskLabel', vLangs[vLang]['notes']+': ');
+            if(pTask.getNotes())vTmpDiv.appendChild(pTask.getNotes());
+        }
+        return vTaskInfoBox;
+    };
+
+    this.getXMLProject=function()
+    {
+        var vProject='<?xml version="1.0" encoding="UTF-8" standalone="yes"?><project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
+        for (var i=0; i<vTaskList.length; i++)
+        {
+            vProject+=this.getXMLTask(i,true);
+        }
+        vProject+='</project>';
+        return vProject;
+    };
+
+    this.getXMLTask=function(pID,pIdx)
+    {
+        var i=0;
+        var vIdx=-1;
+        var vTask='';
+        var vOutFrmt=JSGantt.parseDateFormatStr(this.getDateInputFormat()+' HH:MI');
+        if (pIdx===true)vIdx=pID;
+        else
+        {
+            for (i=0; i<vTaskList.length; i++)
+            {
+                if (vTaskList[i].getID()==pID) {vIdx=i; break;}
+            }
+        }
+        if (vIdx>=0 && vIdx<vTaskList.length)
+        {
+            /* Simplest way to return case sensitive node names is to just build a string */
+            vTask='<task>';
+            vTask+='<pID>'+vTaskList[vIdx].getID()+'</pID>';
+            vTask+='<pName>'+vTaskList[vIdx].getName()+'</pName>';
+            vTask+='<pStart>'+JSGantt.formatDateStr(vTaskList[vIdx].getStart(),vOutFrmt,vLangs[vLang])+'</pStart>';
+            vTask+='<pEnd>'+JSGantt.formatDateStr(vTaskList[vIdx].getEnd(),vOutFrmt,vLangs[vLang])+'</pEnd>';
+            vTask+='<pClass>'+vTaskList[vIdx].getClass()+'</pClass>';
+            vTask+='<pLink>'+vTaskList[vIdx].getLink()+'</pLink>';
+            vTask+='<pMile>'+vTaskList[vIdx].getMile()+'</pMile>';
+            if(vTaskList[vIdx].getResource()!='\u00A0') vTask+='<pRes>'+vTaskList[vIdx].getResource()+'</pRes>';
+            vTask+='<pComp>'+vTaskList[vIdx].getCompVal()+'</pComp>';
+            vTask+='<pGroup>'+vTaskList[vIdx].getGroup()+'</pGroup>';
+            vTask+='<pParent>'+vTaskList[vIdx].getParent()+'</pParent>';
+            vTask+='<pOpen>'+vTaskList[vIdx].getOpen()+'</pOpen>';
+            vTask+='<pDepend>';
+            var vDepList=vTaskList[vIdx].getDepend();
+            for (i=0;i<vDepList.length;i++)
+            {
+                if(i>0)vTask+=',';
+                if(vDepList[i]>0)vTask+=vDepList[i]+vTaskList[vIdx].getDepType()[i];
+            }
+            vTask+='</pDepend>';
+            vTask+='<pCaption>'+vTaskList[vIdx].getCaption()+'</pCaption>';
+
+            var vTmpFrag=document.createDocumentFragment();
+            var vTmpDiv=this.newNode(vTmpFrag, 'div', null, null,vTaskList[vIdx].getNotes().innerHTML);
+            vTask+='<pNotes>'+vTmpDiv.innerHTML+'</pNotes>';
+            vTask+='</task>';
+        }
+        return vTask;
+    };
+    if (vDiv && vDiv.nodeName.toLowerCase()=='div') vDivId=vDiv.id;
+}; //GanttChart
 
-/**
-* 
-@class 
-*/
-
-/**
-* Checks whether browser is IE
-* 
-* @method isIE 
-*/
-JSGantt.isIE = function () {
-	
-	if(typeof document.all != 'undefined')
-		{return true;}
-	else
-		{return false;}
-};
-	
-/**
-* Recursively process task tree ... set min, max dates of parent tasks and identfy task level.
-*
-* @method processRows
-* @param pList {Array} - Array of TaskItem Objects
-* @param pID {Number} - task ID
-* @param pRow {Number} - Row in chart
-* @param pLevel {Number} - Current tree level
-* @param pOpen {Boolean}
-* @return void
-*/ 
-JSGantt.processRows = function(pList, pID, pRow, pLevel, pOpen)
-{
+JSGantt.updateFlyingObj=function (e, pGanttChartObj, pTimer) {
+    var vCurTopBuf=3;
+    var vCurLeftBuf=5;
+    var vCurBotBuf=3;
+    var vCurRightBuf=15;
+    var vMouseX=(e)?e.clientX:window.event.clientX;
+    var vMouseY=(e)?e.clientY:window.event.clientY;
+    var vViewportX=document.documentElement.clientWidth||document.getElementsByTagName('body')[0].clientWidth;
+    var vViewportY=document.documentElement.clientHeight||document.getElementsByTagName('body')[0].clientHeight;
+    var vNewX=vMouseX;
+    var vNewY=vMouseY;
+
+    if (navigator.appName.toLowerCase ()=='microsoft internet explorer') {
+        // the clientX and clientY properties include the left and top borders of the client area
+        vMouseX-=document.documentElement.clientLeft;
+        vMouseY-=document.documentElement.clientTop;
+
+        var vZoomFactor=JSGantt.getZoomFactor ();
+        if (vZoomFactor!=1) {// IE 7 at non-default zoom level
+            vMouseX=Math.round (vMouseX / vZoomFactor);
+            vMouseY=Math.round (vMouseY / vZoomFactor);
+        }
+    }
 
-   var vMinDate = new Date();
-   var vMaxDate = new Date();
-   var vMinSet  = 0;
-   var vMaxSet  = 0;
-   var vList    = pList;
-   var vLevel   = pLevel;
-   var i        = 0;
-   var vNumKid  = 0;
-   var vCompSum = 0;
-   var vVisible = pOpen;
-   
-   for(i = 0; i < pList.length; i++)
-   {
-      if(pList[i].getParent() == pID) {
-		 vVisible = pOpen;
-         pList[i].setVisible(vVisible);
-         if(vVisible==1 && pList[i].getOpen() == 0) 
-           {vVisible = 0;}
-            
-         pList[i].setLevel(vLevel);
-         vNumKid++;
-
-         if(pList[i].getGroup() == 1) {
-            JSGantt.processRows(vList, pList[i].getID(), i, vLevel+1, vVisible);
-         };
-
-         if( vMinSet==0 || pList[i].getStart() < vMinDate) {
-            vMinDate = pList[i].getStart();
-            vMinSet = 1;
-         };
-
-         if( vMaxSet==0 || pList[i].getEnd() > vMaxDate) {
-            vMaxDate = pList[i].getEnd();
-            vMaxSet = 1;
-         };
-
-         vCompSum += pList[i].getCompVal();
-
-      }
-   }
-
-   if(pRow >= 0) {
-      pList[pRow].setStart(vMinDate);
-      pList[pRow].setEnd(vMaxDate);
-      pList[pRow].setNumKid(vNumKid);
-      pList[pRow].setCompVal(Math.ceil(vCompSum/vNumKid));
-   }
+    var vScrollPos=JSGantt.getScrollPositions();
+
+    /* Code for positioned right of the mouse by default*/
+    /*
+     if (vMouseX+vCurRightBuf+pGanttChartObj.vTool.offsetWidth>vViewportX)
+     {
+     if (vMouseX-vCurLeftBuf-pGanttChartObj.vTool.offsetWidth<0) vNewX=vScrollPos.x;
+     else vNewX=vMouseX+vScrollPos.x-vCurLeftBuf-pGanttChartObj.vTool.offsetWidth;
+     }
+     else vNewX=vMouseX+vScrollPos.x+vCurRightBuf;
+     */
+
+    /* Code for positioned left of the mouse by default */
+    if (vMouseX-vCurLeftBuf-pGanttChartObj.vTool.offsetWidth<0)
+    {
+        if (vMouseX+vCurRightBuf+pGanttChartObj.vTool.offsetWidth>vViewportX) vNewX=vScrollPos.x;
+        else vNewX=vMouseX+vScrollPos.x+vCurRightBuf;
+    }
+    else vNewX=vMouseX+vScrollPos.x-vCurLeftBuf-pGanttChartObj.vTool.offsetWidth;
 
+    /* Code for positioned below the mouse by default */
+    if (vMouseY+vCurBotBuf+pGanttChartObj.vTool.offsetHeight>vViewportY)
+    {
+        if (vMouseY-vCurTopBuf-pGanttChartObj.vTool.offsetHeight<0) vNewY=vScrollPos.y;
+        else vNewY=vMouseY+vScrollPos.y-vCurTopBuf-pGanttChartObj.vTool.offsetHeight;
+    }
+    else vNewY=vMouseY+vScrollPos.y+vCurBotBuf;
+
+    /* Code for positioned above the mouse by default */
+    /*
+     if (vMouseY-vCurTopBuf-pGanttChartObj.vTool.offsetHeight<0)
+     {
+     if (vMouseY+vCurBotBuf+pGanttChartObj.vTool.offsetHeight>vViewportY) vNewY=vScrollPos.y;
+     else vNewY=vMouseY+vScrollPos.y+vCurBotBuf;
+     }
+     else vNewY=vMouseY+vScrollPos.y-vCurTopBuf-pGanttChartObj.vTool.offsetHeight;
+     */
+
+    if (pGanttChartObj.getUseMove())
+    {
+        clearInterval(pGanttChartObj.vTool.moveInterval);
+        pGanttChartObj.vTool.moveInterval=setInterval(function(){JSGantt.moveToolTip(vNewX, vNewY, pGanttChartObj.vTool, pTimer);},pTimer);
+    }
+    else
+    {
+        pGanttChartObj.vTool.style.left=vNewX +'px';
+        pGanttChartObj.vTool.style.top=vNewY +'px';
+    }
 };
 
-/**
-* Determine the minimum date of all tasks and set lower bound based on format
-*
-* @method getMinDate
-* @param pList {Array} - Array of TaskItem Objects
-* @param pFormat {String} - current format (minute,hour,day...)
-* @return {Datetime}
-*/
-JSGantt.getMinDate = function getMinDate(pList, pFormat)  
-      {
-
-         var vDate = new Date();
-
-         vDate.setFullYear(pList[0].getStart().getFullYear(), pList[0].getStart().getMonth(), pList[0].getStart().getDate());
-
-         // Parse all Task End dates to find min
-         for(i = 0; i < pList.length; i++)
-         {
-            if(Date.parse(pList[i].getStart()) < Date.parse(vDate))
-               vDate.setFullYear(pList[i].getStart().getFullYear(), pList[i].getStart().getMonth(), pList[i].getStart().getDate());
-         }
-
-         if ( pFormat== 'minute')
-         {
-            vDate.setHours(0);
-            vDate.setMinutes(0);
-         }
-		 else if (pFormat == 'hour' )
-         {
-            vDate.setHours(0);
-            vDate.setMinutes(0);
-         }
-         // Adjust min date to specific format boundaries (first of week or first of month)
-         else if (pFormat=='day')
-         {
-            vDate.setDate(vDate.getDate() - 1);
-            while(vDate.getDay() % 7 > 0)
+JSGantt.showToolTip=function(pGanttChartObj, e, pContents, pWidth, pTimer){
+    var vTtDivId=pGanttChartObj.getDivId()+'JSGanttToolTip';
+    var vMaxW=500;
+    var vMaxAlpha=100;
+    var vShowing=pContents.id;
+
+    if(pGanttChartObj.getUseToolTip())
+    {
+        if(pGanttChartObj.vTool==null){
+            pGanttChartObj.vTool=document.createElement('div');
+            pGanttChartObj.vTool.id=vTtDivId;
+            pGanttChartObj.vTool.className='JSGanttToolTip';
+            pGanttChartObj.vTool.vToolCont=document.createElement('div');
+            pGanttChartObj.vTool.vToolCont.id=vTtDivId+'cont';
+            pGanttChartObj.vTool.vToolCont.className='JSGanttToolTipcont';
+            pGanttChartObj.vTool.vToolCont.setAttribute('showing','');
+            pGanttChartObj.vTool.appendChild(pGanttChartObj.vTool.vToolCont);
+            document.body.appendChild(pGanttChartObj.vTool);
+            pGanttChartObj.vTool.style.opacity=0;
+            pGanttChartObj.vTool.setAttribute('currentOpacity',0);
+            pGanttChartObj.vTool.setAttribute('fadeIncrement',10);
+            pGanttChartObj.vTool.setAttribute('moveSpeed',10);
+            pGanttChartObj.vTool.style.filter='alpha(opacity=0)';
+            pGanttChartObj.vTool.style.visibility='hidden';
+            pGanttChartObj.vTool.style.left=Math.floor(((e)?e.clientX:window.event.clientX)/2)+'px';
+            pGanttChartObj.vTool.style.top=Math.floor(((e)?e.clientY:window.event.clientY)/2)+'px';
+            JSGantt.addListener('mouseover', function () {clearTimeout(pGanttChartObj.vTool.delayTimeout);}, pGanttChartObj.vTool);
+            JSGantt.addListener('mouseout', function () {JSGantt.delayedHide(pGanttChartObj, pGanttChartObj.vTool, pTimer);}, pGanttChartObj.vTool);
+        }
+        clearTimeout(pGanttChartObj.vTool.delayTimeout);
+        if(pGanttChartObj.vTool.vToolCont.getAttribute('showing')!=vShowing || pGanttChartObj.vTool.style.visibility!='visible')
+        {
+            if (pGanttChartObj.vTool.vToolCont.getAttribute('showing')!=vShowing)
             {
-                vDate.setDate(vDate.getDate() - 1);
-            }
+                pGanttChartObj.vTool.vToolCont.setAttribute('showing',vShowing);
 
-         }
+                pGanttChartObj.vTool.vToolCont.innerHTML=pContents.innerHTML;
+                // as we are allowing arbitrary HTML we should remove any tag ids to prevent duplication
+                JSGantt.stripIds(pGanttChartObj.vTool.vToolCont);
+            }
 
-         else if (pFormat=='week')
-         {
-            vDate.setDate(vDate.getDate() - 7);
-            while(vDate.getDay() % 7 > 0)
-            {
-                vDate.setDate(vDate.getDate() - 1);
+            pGanttChartObj.vTool.style.visibility='visible';
+            // Rather than follow the mouse just have it stay put
+            JSGantt.updateFlyingObj(e, pGanttChartObj, pTimer);
+            pGanttChartObj.vTool.style.width=(pWidth)? pWidth+'px' : 'auto';
+            if(!pWidth && JSGantt.isIE()){
+                pGanttChartObj.vTool.style.width=pGanttChartObj.vTool.offsetWidth;
             }
+            if(pGanttChartObj.vTool.offsetWidth>vMaxW){pGanttChartObj.vTool.style.width=vMaxW+'px';}
+        }
+
+        if (pGanttChartObj.getUseFade())
+        {
+            clearInterval(pGanttChartObj.vTool.fadeInterval);
+            pGanttChartObj.vTool.fadeInterval=setInterval(function(){JSGantt.fadeToolTip(1, pGanttChartObj.vTool, vMaxAlpha);},pTimer);
+        }
+        else
+        {
+            pGanttChartObj.vTool.style.opacity=vMaxAlpha * 0.01;
+            pGanttChartObj.vTool.style.filter='alpha(opacity='+vMaxAlpha+')';
+        }
+    }
+};
+
+JSGantt.stripIds=function(pNode){
+    for(var i=0; i<pNode.childNodes.length; i++)
+    {
+        if ('removeAttribute' in pNode.childNodes[i]) pNode.childNodes[i].removeAttribute('id');
+        if (pNode.childNodes[i].hasChildNodes()) JSGantt.stripIds(pNode.childNodes[i]);
+    }
+};
 
-         }
+JSGantt.stripUnwanted=function(pNode){
+    var vAllowedTags=new Array('#text','p','br','ul','ol','li','div','span','img');
+    for(var i=0; i<pNode.childNodes.length; i++)
+    {
+        /* versions of IE<9 don't support indexOf on arrays so add trailing comma to the joined array and lookup value to stop substring matches */
+        if ((vAllowedTags.join().toLowerCase()+',').indexOf(pNode.childNodes[i].nodeName.toLowerCase()+',')==-1)
+        {
+            pNode.replaceChild(document.createTextNode(pNode.childNodes[i].outerHTML), pNode.childNodes[i]);
+        }
+        if (pNode.childNodes[i].hasChildNodes()) JSGantt.stripUnwanted(pNode.childNodes[i]);
+    }
+};
 
-         else if (pFormat=='month')
-         {
-            while(vDate.getDate() > 1)
-            {
-                vDate.setDate(vDate.getDate() - 1);
-            }
-         }
+JSGantt.delayedHide=function(pGanttChartObj, pTool, pTimer){
+    var vDelay=1500;
+    if(pTool) pTool.delayTimeout=setTimeout(function(){JSGantt.hideToolTip(pGanttChartObj, pTool, pTimer);}, vDelay);
+};
 
-         else if (pFormat=='quarter')
-         {
-            if( vDate.getMonth()==0 || vDate.getMonth()==1 || vDate.getMonth()==2 )
-               {vDate.setFullYear(vDate.getFullYear(), 0, 1);}
-            else if( vDate.getMonth()==3 || vDate.getMonth()==4 || vDate.getMonth()==5 )
-               {vDate.setFullYear(vDate.getFullYear(), 3, 1);}
-            else if( vDate.getMonth()==6 || vDate.getMonth()==7 || vDate.getMonth()==8 )
-               {vDate.setFullYear(vDate.getFullYear(), 6, 1);}
-            else if( vDate.getMonth()==9 || vDate.getMonth()==10 || vDate.getMonth()==11 )
-               {vDate.setFullYear(vDate.getFullYear(), 9, 1);}
+JSGantt.hideToolTip=function(pGanttChartObj, pTool, pTimer){
+    if (pGanttChartObj.getUseFade())
+    {
+        clearInterval(pTool.fadeInterval);
+        pTool.fadeInterval=setInterval(function(){JSGantt.fadeToolTip(-1, pTool, 0);}, pTimer);
+    }
+    else
+    {
+        pTool.style.opacity=0;
+        pTool.style.filter='alpha(opacity=0)';
+        pTool.style.visibility='hidden';
+    }
+};
 
-         };
+JSGantt.fadeToolTip=function(pDirection, pTool, pMaxAlpha){
+    var vIncrement=parseInt(pTool.getAttribute('fadeIncrement'));
+    var vAlpha=pTool.getAttribute('currentOpacity');
+    var vCurAlpha=parseInt(vAlpha);
+    if((vCurAlpha!=pMaxAlpha && pDirection==1) || (vCurAlpha!=0 && pDirection==-1)){
+        var i=vIncrement;
+        if(pMaxAlpha-vCurAlpha<vIncrement && pDirection==1){
+            i=pMaxAlpha-vCurAlpha;
+        }else if(vAlpha<vIncrement && pDirection==-1){
+            i=vCurAlpha;
+        }
+        vAlpha=vCurAlpha+(i * pDirection);
+        pTool.style.opacity=vAlpha * 0.01;
+        pTool.style.filter='alpha(opacity='+vAlpha+')';
+        pTool.setAttribute('currentOpacity', vAlpha);
+    }else{
+        clearInterval(pTool.fadeInterval);
+        if(pDirection==-1){
+            pTool.style.opacity=0;
+            pTool.style.filter='alpha(opacity=0)';
+            pTool.style.visibility='hidden';
+        }
+    }
+};
 
-         return(vDate);
+JSGantt.moveToolTip=function(pNewX, pNewY, pTool){
+    var vSpeed=parseInt(pTool.getAttribute('moveSpeed'));
+    var vOldX=parseInt(pTool.style.left);
+    var vOldY=parseInt(pTool.style.top);
 
-      };
+    if (pTool.style.visibility!='visible')
+    {
+        pTool.style.left=pNewX +'px';
+        pTool.style.top=pNewY +'px';
+        clearInterval(pTool.moveInterval);
+    }
+    else
+    {
+        if(pNewX!=vOldX && pNewY!=vOldY)
+        {
+            vOldX+=Math.ceil((pNewX-vOldX)/vSpeed);
+            vOldY+=Math.ceil((pNewY-vOldY)/vSpeed);
+            pTool.style.left=vOldX +'px';
+            pTool.style.top=vOldY +'px';
+        }
+        else
+        {
+            clearInterval(pTool.moveInterval);
+        }
+    }
+};
 
+JSGantt.getZoomFactor=function() {
+    var vFactor=1;
+    if (document.body.getBoundingClientRect)
+    {
+        // rect is only in physical pixel size in IE before version 8
+        var vRect=document.body.getBoundingClientRect ();
+        var vPhysicalW=vRect.right-vRect.left;
+        var vLogicalW=document.body.offsetWidth;
+
+        // the zoom level is always an integer percent value
+        vFactor=Math.round ((vPhysicalW / vLogicalW) * 100) / 100;
+    }
+    return vFactor;
+};
 
+JSGantt.getScrollPositions=function() {
+    var vScrollLeft=window.pageXOffset;
+    var vScrollTop=window.pageYOffset;
+    if (!('pageXOffset' in window))	// Internet Explorer before version 9
+    {
+        var vZoomFactor=JSGantt.getZoomFactor ();
+        vScrollLeft=Math.round (document.documentElement.scrollLeft / vZoomFactor);
+        vScrollTop=Math.round (document.documentElement.scrollTop / vZoomFactor);
+    }
+    return {x : vScrollLeft, y : vScrollTop};
+};
 
+JSGantt.getOffset=function(pStartDate, pEndDate, pColWidth, pFormat)
+{
+    var vMonthDaysArr=new Array(31,28,31,30,31,30,31,31,30,31,30,31);
+    var curTaskStart=new Date(pStartDate.getTime());
+    var curTaskEnd=new Date(pEndDate.getTime());
+    var vTaskRightPx=0;
+    var tmpTaskStart=Date.UTC(curTaskStart.getFullYear(), curTaskStart.getMonth(), curTaskStart.getDate(), curTaskStart.getHours(), 0, 0);
+    var tmpTaskEnd=Date.UTC(curTaskEnd.getFullYear(), curTaskEnd.getMonth(), curTaskEnd.getDate(), curTaskEnd.getHours(), 0, 0);
+
+    var vTaskRight=(tmpTaskEnd-tmpTaskStart)/3600000; // Length of task in hours
+
+    if(pFormat=='day')
+    {
+        vTaskRightPx=Math.ceil((vTaskRight/24) * (pColWidth+1));
+    }
+    else if(pFormat=='week')
+    {
+        vTaskRightPx=Math.ceil(((vTaskRight/24) * (pColWidth+1))/7);
+    }
+    else if(pFormat=='month')
+    {
+        var vMonthsDiff=(12 * (curTaskEnd.getFullYear()-curTaskStart.getFullYear()))+(curTaskEnd.getMonth()-curTaskStart.getMonth());
+        var vPosTmpDate=new Date(curTaskEnd.getTime());
+        vPosTmpDate.setDate(curTaskStart.getDate());
+        var vDaysCrctn=(curTaskEnd.getTime()-vPosTmpDate.getTime())/ (86400000);
+
+        vTaskRightPx=Math.ceil((vMonthsDiff * (pColWidth+1))+(vDaysCrctn * (pColWidth/vMonthDaysArr[curTaskEnd.getMonth()])));
+    }
+    else if(pFormat=='quarter')
+    {
+        vMonthsDiff=(12 * (curTaskEnd.getFullYear()-curTaskStart.getFullYear()))+(curTaskEnd.getMonth()-curTaskStart.getMonth());
+        vPosTmpDate=new Date(curTaskEnd.getTime());
+        vPosTmpDate.setDate(curTaskStart.getDate());
+        vDaysCrctn=(curTaskEnd.getTime()-vPosTmpDate.getTime())/ (86400000);
+
+        vTaskRightPx=Math.ceil((vMonthsDiff * ((pColWidth+1)/3))+(vDaysCrctn * (pColWidth/90)));
+    }
+    else if(pFormat=='hour')
+    {
+        // can't just calculate sum because of daylight savings changes
+        vPosTmpDate=new Date(curTaskEnd.getTime());
+        vPosTmpDate.setMinutes(curTaskStart.getMinutes(), 0);
+        var vMinsCrctn=(curTaskEnd.getTime()-vPosTmpDate.getTime())/(3600000);
+
+        vTaskRightPx=Math.ceil((vTaskRight * (pColWidth+1))+(vMinsCrctn * (pColWidth)));
+    }
+    return vTaskRightPx;
+};
 
-/**
-* Used to determine the minimum date of all tasks and set lower bound based on format
-*
-* @method getMaxDate
-* @param pList {Array} - Array of TaskItem Objects
-* @param pFormat {String} - current format (minute,hour,day...)
-* @return {Datetime}
-*/
-JSGantt.getMaxDate = function (pList, pFormat)
+// Recursively process task tree ... set min, max dates of parent tasks and identfy task level.
+JSGantt.processRows=function(pList, pID, pRow, pLevel, pOpen, pUseSort)
 {
-   var vDate = new Date();
-
-         vDate.setFullYear(pList[0].getEnd().getFullYear(), pList[0].getEnd().getMonth(), pList[0].getEnd().getDate());
-         
-         
-                // Parse all Task End dates to find max
-         for(i = 0; i < pList.length; i++)
-         {
-            if(Date.parse(pList[i].getEnd()) > Date.parse(vDate))
+    var vMinDate=new Date();
+    var vMaxDate=new Date();
+    var vVisible=pOpen;
+    var vCurItem=null;
+    var vCompSum=0;
+    var vMinSet=0;
+    var vMaxSet=0;
+    var vNumKid=0;
+    var vLevel=pLevel;
+    var vList=pList;
+    var vComb=false;
+    var i=0;
+
+    for(i=0; i<pList.length; i++)
+    {
+        if (pList[i].getToDelete())
+        {
+            pList.splice(i,1);
+            i--;
+        }
+        if (i>=0 && pList[i].getID()==pID)vCurItem=pList[i];
+    }
+
+    for(i=0; i<pList.length; i++)
+    {
+        if(pList[i].getParent()==pID)
+        {
+            vVisible=pOpen;
+            pList[i].setParItem(vCurItem);
+            pList[i].setVisible(vVisible);
+            if(vVisible==1 && pList[i].getOpen()==0) vVisible=0;
+
+            if(pList[i].getMile() && pList[i].getParItem() && pList[i].getParItem().getGroup()==2)
+            {//remove milestones owned by combined groups
+                pList.splice(i,1);
+                i--;
+                continue;
+            }
+
+            pList[i].setLevel(vLevel);
+            vNumKid++;
+
+            if(pList[i].getGroup())
             {
-                 //vDate.setFullYear(pList[0].getEnd().getFullYear(), pList[0].getEnd().getMonth(), pList[0].getEnd().getDate());
-                 vDate.setTime(Date.parse(pList[i].getEnd()));
-			}	
-	     }
-	     
-	     if (pFormat == 'minute')
-         {
-            vDate.setHours(vDate.getHours() + 1);
-            vDate.setMinutes(59);
-         }	
-	     
-         if (pFormat == 'hour')
-         {
-            vDate.setHours(vDate.getHours() + 2);
-         }				
-				
-         // Adjust max date to specific format boundaries (end of week or end of month)
-         if (pFormat=='day')
-         {
-            vDate.setDate(vDate.getDate() + 1);
-
-            while(vDate.getDay() % 6 > 0)
+                if(pList[i].getParItem() && pList[i].getParItem().getGroup()==2)pList[i].setGroup(2);
+                JSGantt.processRows(vList, pList[i].getID(), i, vLevel+1, vVisible, 0);
+            }
+
+            if(vMinSet==0 || pList[i].getStart()<vMinDate)
+            {
+                vMinDate=pList[i].getStart();
+                vMinSet=1;
+            }
+
+            if(vMaxSet==0 || pList[i].getEnd()>vMaxDate)
             {
-                vDate.setDate(vDate.getDate() + 1);
+                vMaxDate=pList[i].getEnd();
+                vMaxSet=1;
             }
 
-         }
+            vCompSum+=pList[i].getCompVal();
+            pList[i].setSortIdx(i*pList.length);
+        }
+    }
 
-         if (pFormat=='week')
-         {
-            //For weeks, what is the last logical boundary?
-            vDate.setDate(vDate.getDate() + 11);
+    if(pRow>=0)
+    {
+        if(pList[pRow].getGroupMinStart()!=null && pList[pRow].getGroupMinStart()<vMinDate)
+        {
+            vMinDate=pList[pRow].getGroupMinStart();
+        }
+
+        if(pList[pRow].getGroupMinEnd()!=null && pList[pRow].getGroupMinEnd()>vMaxDate)
+        {
+            vMaxDate=pList[pRow].getGroupMinEnd();
+        }
+        pList[pRow].setStart(vMinDate);
+        pList[pRow].setEnd(vMaxDate);
+        pList[pRow].setNumKid(vNumKid);
+        pList[pRow].setCompVal(Math.ceil(vCompSum/vNumKid));
+    }
 
-            while(vDate.getDay() % 6 > 0)
+    if (pID==0 && pUseSort==1)
+    {
+        JSGantt.sortTasks(pList, 0, 0);
+        pList.sort(function(a,b){return a.getSortIdx()-b.getSortIdx();});
+    }
+    if (pID==0 && pUseSort!=1) // Need to sort combined tasks regardless
+    {
+        for(i=0; i<pList.length; i++)
+        {
+            if (pList[i].getGroup()==2)
             {
-                vDate.setDate(vDate.getDate() + 1);
+                vComb=true;
+                JSGantt.sortTasks(pList, pList[i].getID(), pList[i].getSortIdx()+1);
             }
+        }
+        if(vComb==true) pList.sort(function(a,b){return a.getSortIdx()-b.getSortIdx();});
+    }
+};
 
-         }
+JSGantt.sortTasks=function (pList, pID, pIdx)
+{
+    var sortIdx=pIdx;
+    var sortArr=new Array();
+
+    for(var i=0; i<pList.length; i++)
+    {
+        if(pList[i].getParent()==pID)sortArr.push(pList[i]);
+    }
+
+    if (sortArr.length>0)
+    {
+        sortArr.sort(function(a,b){ var i=a.getStart().getTime()-b.getStart().getTime();
+            if (i==0) i=a.getEnd().getTime()-b.getEnd().getTime();
+            if (i==0) return a.getID()-b.getID();
+            else return i; });
+    }
 
-         // Set to last day of current Month
-         if (pFormat=='month')
-         {
-            while(vDate.getDay() > 1)
+    for (var j=0; j<sortArr.length; j++)
+    {
+        for(i=0; i<pList.length; i++)
+        {
+            if(pList[i].getID()==sortArr[j].getID())
             {
-                vDate.setDate(vDate.getDate() + 1);
+                pList[i].setSortIdx(sortIdx++);
+                sortIdx=JSGantt.sortTasks(pList, pList[i].getID(), sortIdx);
             }
+        }
+    }
+    return sortIdx;
+};
 
-            vDate.setDate(vDate.getDate() - 1);
-         }
+// Used to determine the minimum date of all tasks and set lower bound based on format
+JSGantt.getMinDate=function getMinDate(pList, pFormat)
+{
+    var vDate=new Date();
+    vDate.setTime(pList[0].getStart().getTime());
 
-         // Set to last day of current Quarter
-         if (pFormat=='quarter')
-         {
-            if( vDate.getMonth()==0 || vDate.getMonth()==1 || vDate.getMonth()==2 )
-               vDate.setFullYear(vDate.getFullYear(), 2, 31);
-            else if( vDate.getMonth()==3 || vDate.getMonth()==4 || vDate.getMonth()==5 )
-               vDate.setFullYear(vDate.getFullYear(), 5, 30);
-            else if( vDate.getMonth()==6 || vDate.getMonth()==7 || vDate.getMonth()==8 )
-               vDate.setFullYear(vDate.getFullYear(), 8, 30);
-            else if( vDate.getMonth()==9 || vDate.getMonth()==10 || vDate.getMonth()==11 )
-               vDate.setFullYear(vDate.getFullYear(), 11, 31);
+    // Parse all Task End dates to find min
+    for(var i=0; i<pList.length; i++)
+    {
+        if(pList[i].getStart().getTime()<vDate.getTime()) vDate.setTime(pList[i].getStart().getTime());
+    }
 
-         }
+    // Adjust min date to specific format boundaries (first of week or first of month)
+    if (pFormat=='day')
+    {
+        vDate.setDate(vDate.getDate()-1);
+        while(vDate.getDay()%7!=1) vDate.setDate(vDate.getDate()-1);
+    }
+    else if (pFormat=='week')
+    {
+        vDate.setDate(vDate.getDate()-1);
+        while(vDate.getDay()%7!=1) vDate.setDate(vDate.getDate()-1);
+    }
+    else if (pFormat=='month')
+    {
+        vDate.setDate(vDate.getDate()-15);
+        while(vDate.getDate()>1) vDate.setDate(vDate.getDate()-1);
+    }
+    else if (pFormat=='quarter')
+    {
+        vDate.setDate(vDate.getDate()-31);
+        if(vDate.getMonth()==0 || vDate.getMonth()==1 || vDate.getMonth()==2)
+            vDate.setFullYear(vDate.getFullYear(), 0, 1);
+        else if(vDate.getMonth()==3 || vDate.getMonth()==4 || vDate.getMonth()==5)
+            vDate.setFullYear(vDate.getFullYear(), 3, 1);
+        else if(vDate.getMonth()==6 || vDate.getMonth()==7 || vDate.getMonth()==8)
+            vDate.setFullYear(vDate.getFullYear(), 6, 1);
+        else if(vDate.getMonth()==9 || vDate.getMonth()==10 || vDate.getMonth()==11)
+            vDate.setFullYear(vDate.getFullYear(), 9, 1);
+    }
+    else if (pFormat=='hour')
+    {
+        vDate.setHours(vDate.getHours()-1);
+        while(vDate.getHours()%6!=0) vDate.setHours(vDate.getHours()-1);
+    }
 
-         return(vDate);
+    if(pFormat=='hour')vDate.setMinutes(0,0);
+    else vDate.setHours(0,0,0);
+    return(vDate);
+};
 
-      };
+// Used to determine the maximum date of all tasks and set upper bound based on format
+JSGantt.getMaxDate=function (pList, pFormat)
+{
+    var vDate=new Date();
 
+    vDate.setTime(pList[0].getEnd().getTime());
 
-/**
-* Returns an object from the current DOM
-*
-* @method findObj
-* @param theObj {String} - Object name
-* @param theDoc {Document} - current document (DOM)
-* @return {Object}
-*/
-JSGantt.findObj = function (theObj, theDoc)
+    // Parse all Task End dates to find max
+    for(var i=0; i<pList.length; i++)
+    {
+        if(pList[i].getEnd().getTime()>vDate.getTime()) vDate.setTime(pList[i].getEnd().getTime());
+    }
 
-      {
+    // Adjust max date to specific format boundaries (end of week or end of month)
+    if (pFormat=='day')
+    {
+        vDate.setDate(vDate.getDate()+1);
 
-         var p, i, foundObj;
+        while(vDate.getDay()%7!=0) vDate.setDate(vDate.getDate()+1);
+    }
+    else if (pFormat=='week')
+    {
+        //For weeks, what is the last logical boundary?
+        vDate.setDate(vDate.getDate()+1);
 
-         if(!theDoc) {theDoc = document;}
+        while(vDate.getDay()%7!=0) vDate.setDate(vDate.getDate()+1);
+    }
+    else if (pFormat=='month')
+    {
+        // Set to last day of current Month
+        while(vDate.getDate()>1) vDate.setDate(vDate.getDate()+1);
+        vDate.setDate(vDate.getDate()-1);
+    }
+    else if (pFormat=='quarter')
+    {
+        // Set to last day of current Quarter
+        if(vDate.getMonth()==0 || vDate.getMonth()==1 || vDate.getMonth()==2)
+            vDate.setFullYear(vDate.getFullYear(), 2, 31);
+        else if(vDate.getMonth()==3 || vDate.getMonth()==4 || vDate.getMonth()==5)
+            vDate.setFullYear(vDate.getFullYear(), 5, 30);
+        else if(vDate.getMonth()==6 || vDate.getMonth()==7 || vDate.getMonth()==8)
+            vDate.setFullYear(vDate.getFullYear(), 8, 30);
+        else if(vDate.getMonth()==9 || vDate.getMonth()==10 || vDate.getMonth()==11)
+            vDate.setFullYear(vDate.getFullYear(), 11, 31);
+    }
+    else if (pFormat=='hour')
+    {
+        if(vDate.getHours()==0)vDate.setDate(vDate.getDate()+1);
+        vDate.setHours(vDate.getHours()+1);
 
-         if( (p = theObj.indexOf("?")) > 0 && parent.frames.length){
+        while(vDate.getHours()%6!=5) vDate.setHours(vDate.getHours()+1);
+    }
+    return(vDate);
+};
 
-            theDoc = parent.frames[theObj.substring(p+1)].document;
+// This function finds the document id of the specified object
+JSGantt.findObj=function (theObj, theDoc)
+{
+    var p, i, foundObj;
+    if(!theDoc) theDoc=document;
+    if(document.getElementById) foundObj=document.getElementById(theObj);
+    return foundObj;
+};
 
-            theObj = theObj.substring(0,p);
+JSGantt.changeFormat=function(pFormat,ganttObj)
+{
+    if(ganttObj) ganttObj.setFormat(pFormat);
+    else alert('Chart undefined');
+};
 
-         }
+// Function to open/close and hide/show children of specified task
+JSGantt.folder=function (pID,ganttObj)
+{
+    var vList=ganttObj.getList();
+    var vDivId=ganttObj.getDivId();
 
-         if(!(foundObj = theDoc[theObj]) && theDoc.all) 
+    ganttObj.clearDependencies(); // clear these first so slow rendering doesn't look odd
 
-            {foundObj = theDoc.all[theObj];}
+    for(var i=0; i<vList.length; i++)
+    {
+        if(vList[i].getID()==pID)
+        {
+            if(vList[i].getOpen()==1)
+            {
+                vList[i].setOpen(0);
+                JSGantt.hide(pID,ganttObj);
 
+                if (JSGantt.isIE())
+                    vList[i].getGroupSpan().innerText='+';
+                else
+                    vList[i].getGroupSpan().textContent='+';
+            }
+            else
+            {
+                vList[i].setOpen(1);
+                JSGantt.show(pID, 1, ganttObj);
 
+                if (JSGantt.isIE())
+                    vList[i].getGroupSpan().innerText='-';
+                else
+                    vList[i].getGroupSpan().textContent='-';
+            }
+        }
+    }
+    ganttObj.DrawDependencies();
+};
 
-         for (i=0; !foundObj && i < theDoc.forms.length; i++) 
+JSGantt.hide=function (pID,ganttObj)
+{
+    var vList=ganttObj.getList();
+    var vID=0;
+    var vDivId=ganttObj.getDivId();
+
+    for(var i=0; i<vList.length; i++)
+    {
+        if(vList[i].getParent()==pID)
+        {
+            vID=vList[i].getID();
+            // it's unlikely but if the task list has been updated since
+            // the chart was drawn some of the rows may not exist
+            if (vList[i].getListChildRow()) vList[i].getListChildRow().style.display='none';
+            if (vList[i].getChildRow()) vList[i].getChildRow().style.display='none';
+            vList[i].setVisible(0);
+            if(vList[i].getGroup()) JSGantt.hide(vID,ganttObj);
+        }
+    }
+};
 
-            {foundObj = theDoc.forms[i][theObj];}
+// Function to show children of specified task
+JSGantt.show=function (pID, pTop, ganttObj)
+{
+    var vList=ganttObj.getList();
+    var vID=0;
+    var vDivId=ganttObj.getDivId();
+    var vState='';
+
+    for(var i=0; i<vList.length; i++)
+    {
+        if(vList[i].getParent()==pID)
+        {
+            if (vList[i].getParItem().getGroupSpan())
+            {
+                if (JSGantt.isIE()) vState=vList[i].getParItem().getGroupSpan().innerText;
+                else vState=vList[i].getParItem().getGroupSpan().textContent;
+            }
+            i=vList.length;
+        }
+    }
 
+    for(i=0; i<vList.length; i++)
+    {
+        if(vList[i].getParent()==pID)
+        {
+            var vChgState=false;
+            vID=vList[i].getID();
 
+            if(pTop==1 && vState=='+')vChgState=true;
+            else if(vState=='-')vChgState=true;
+            else if(vList[i].getParItem() && vList[i].getParItem().getGroup()==2)vList[i].setVisible(1);
 
-         for(i=0; !foundObj && theDoc.layers && i < theDoc.layers.length; i++)
+            if(vChgState)
+            {
+                if (vList[i].getListChildRow()) vList[i].getListChildRow().style.display='';
+                if (vList[i].getChildRow()) vList[i].getChildRow().style.display='';
+                vList[i].setVisible(1);
+            }
+            if(vList[i].getGroup()) JSGantt.show(vID, 0,ganttObj);
+        }
+    }
+};
 
-            {foundObj = JSGantt.findObj(theObj,theDoc.layers[i].document);}
+// function to open window to display task link
+JSGantt.taskLink=function(pRef,pWidth,pHeight)
+{
 
+    if(pWidth) var vWidth =pWidth; else vWidth =400;
+    if(pHeight) var vHeight=pHeight; else vHeight=400;
 
+    var OpenWindow=window.open(pRef, 'newwin', 'height='+vHeight+',width='+vWidth);
+};
 
-         if(!foundObj && document.getElementById)
+JSGantt.parseDateStr=function(pDateStr,pFormatStr)
+{
+    var vDate=new Date();
+    var vDateParts=pDateStr.split(/[^0-9]/);
+    if (pDateStr.length>=10 && vDateParts.length>=3)
+    {
+        while(vDateParts.length<5)vDateParts.push(0);
+
+        switch(pFormatStr)
+        {
+            case 'mm/dd/yyyy':
+                vDate=new Date(vDateParts[2], vDateParts[0]-1, vDateParts[1], vDateParts[3], vDateParts[4]);
+                break;
+            case 'dd/mm/yyyy':
+                vDate=new Date(vDateParts[2], vDateParts[1]-1, vDateParts[0], vDateParts[3], vDateParts[4]);
+                break;
+            case 'yyyy-mm-dd':
+                vDate=new Date(vDateParts[0], vDateParts[1]-1, vDateParts[2], vDateParts[3], vDateParts[4]);
+                break;
+        }
+    }
+    return(vDate);
+};
 
-            {foundObj = document.getElementById(theObj);}
+JSGantt.formatDateStr=function(pDate, pDateFormatArr, pL)
+{
+    var vDateStr='';
+
+    var vYear2Str=pDate.getFullYear().toString().substring(2,4);
+    var vMonthStr=(pDate.getMonth()+1)+'';
+    var vMonthArr=new Array(pL['january'],pL['february'],pL['march'],pL['april'],pL['maylong'],pL['june'],pL['july'],pL['august'],pL['september'],pL['october'],pL['november'],pL['december']);
+    var vDayArr=new Array(pL['sunday'],pL['monday'],pL['tuesday'],pL['wednesday'],pL['thursday'],pL['friday'],pL['saturday']);
+    var vMthArr=new Array(pL['jan'],pL['feb'],pL['mar'],pL['apr'],pL['may'],pL['jun'],pL['jul'],pL['aug'],pL['sep'],pL['oct'],pL['nov'],pL['dec']);
+    var vDyArr=new Array(pL['sun'],pL['mon'],pL['tue'],pL['wed'],pL['thu'],pL['fri'],pL['sat']);
+
+
+
+    for (var i=0; i<pDateFormatArr.length; i++)
+    {
+        switch(pDateFormatArr[i])
+        {
+            case 'dd':
+                if (pDate.getDate()<10) vDateStr+='0'; // now fall through
+            case 'd':
+                vDateStr+=pDate.getDate();
+                break;
+            case 'day':
+                vDateStr+=vDyArr[pDate.getDay()];
+                break;
+            case 'DAY':
+                vDateStr+=vDayArr[pDate.getDay()];
+                break;
+            case 'mm':
+                if (vMonthStr<10) vDateStr+='0'; // now fall through
+            case 'm':
+                vDateStr+=vMonthStr;
+                break;
+            case 'mon':
+                vDateStr+=vMthArr[pDate.getMonth()];
+                break;
+            case 'month':
+                vDateStr+=vMonthArr[pDate.getMonth()];
+                break;
+            case 'yyyy':
+                vDateStr+=pDate.getFullYear();
+                break;
+            case 'yy':
+                vDateStr+=vYear2Str;
+                break;
+            case 'qq':
+                vDateStr+='Q'; // now fall through
+            case 'q':
+                vDateStr+=Math.floor(pDate.getMonth()/3)+1;
+                break;
+            case 'hh':
+                if ((((pDate.getHours()%12)==0)?12:pDate.getHours()%12)<10) vDateStr+='0'; // now fall through
+            case 'h':
+                vDateStr+=((pDate.getHours()%12)==0)?12:pDate.getHours()%12;
+                break;
+            case 'HH':
+                if ((pDate.getHours())<10) vDateStr+='0'; // now fall through
+            case 'H':
+                vDateStr+=(pDate.getHours());
+                break;
+            case 'MI':
+                if (pDate.getMinutes()<10) vDateStr+='0'; // now fall through
+            case 'mi':
+                vDateStr+=pDate.getMinutes();
+                break;
+            case 'pm':
+                vDateStr+=((pDate.getHours())<12)?'am':'pm';
+                break;
+            case 'PM':
+                vDateStr+=((pDate.getHours())<12)?'AM':'PM';
+                break;
+            case 'ww':
+                if (JSGantt.getIsoWeek(pDate)<10) vDateStr+='0'; // now fall through
+            case 'w':
+                vDateStr+=JSGantt.getIsoWeek(pDate);
+                break;
+            case 'week':
+                var vWeekNum=JSGantt.getIsoWeek(pDate);
+                var vYear=pDate.getFullYear();
+                var vDayOfWeek=(pDate.getDay()==0)? 7 : pDate.getDay();
+                if (vWeekNum>=52 && vMonthStr==1) vYear--;
+                if (vWeekNum==1 && vMonthStr==12) vYear++;
+                if (vWeekNum<10) vWeekNum='0'+vWeekNum;
+
+                vDateStr+=vYear+'-W'+vWeekNum+'-'+vDayOfWeek;
+                break;
+            default:
+                if (pL[pDateFormatArr[i].toLowerCase()]) vDateStr+=pL[pDateFormatArr[i].toLowerCase()];
+                else vDateStr+=pDateFormatArr[i];
+                break;
+        }
+    }
+    return vDateStr;
+};
 
+JSGantt.parseDateFormatStr=function(pFormatStr)
+{
+    var vDateStr='';
+    var vComponantStr='';
+    var vCurrChar='';
+    var vSeparators=new RegExp('[\/\\ -.,\'":]');
+    var vDateFormatArray=new Array();
+
+    for (var i=0; i<pFormatStr.length; i++)
+    {
+        vCurrChar=pFormatStr.charAt(i);
+        if ((vCurrChar.match(vSeparators)) || (i+1==pFormatStr.length)) // separator or end of string
+        {
+            if ((i+1==pFormatStr.length) && (!(vCurrChar.match(vSeparators)))) // at end of string add any non-separator chars to the current component
+            {
+                vComponantStr+=vCurrChar;
+            }
+            vDateFormatArray.push(vComponantStr);
+            if (vCurrChar.match(vSeparators)) vDateFormatArray.push(vCurrChar);
+            vComponantStr='';
+        }
+        else
+        {
+            vComponantStr+=vCurrChar;
+        }
 
+    }
+    return vDateFormatArray;
+};
 
-         return foundObj;
+JSGantt.parseXML=function(pFile,pGanttVar)
+{
+    if (window.XMLHttpRequest) {
+        var xhttp=new XMLHttpRequest();
+    } else {	// IE 5/6
+        xhttp=new ActiveXObject('Microsoft.XMLHTTP');
+    }
 
-      };
+    xhttp.open('GET', pFile, false);
+    xhttp.send(null);
+    var xmlDoc=xhttp.responseXML;
 
+    JSGantt.AddXMLTask(pGanttVar, xmlDoc);
+};
 
-/**
-* Change display format of current gantt chart
-*
-* @method changeFormat
-* @param pFormat {String} - Current format (minute,hour,day...)
-* @param ganttObj {GanttChart} - The gantt object
-* @return {void}
-*/
-JSGantt.changeFormat =      function(pFormat,ganttObj) {
+JSGantt.parseXMLString=function(pStr,pGanttVar)
+{
+    if (typeof window.DOMParser!='undefined') {
+        var xmlDoc =(new window.DOMParser()).parseFromString(pStr, 'text/xml');
+    } else if (typeof window.ActiveXObject!='undefined' &&
+        new window.ActiveXObject('Microsoft.XMLDOM')) {
+        xmlDoc=new window.ActiveXObject('Microsoft.XMLDOM');
+        xmlDoc.async='false';
+        xmlDoc.loadXML(pStr);
+    }
 
-        if(ganttObj) 
-		{
-		ganttObj.setFormat(pFormat);
-		ganttObj.DrawDependencies();
-		}
-        else
-        {alert('Chart undefined');};
-      };
+    JSGantt.AddXMLTask(pGanttVar, xmlDoc);
+};
 
 
-/**
-* Open/Close and hide/show children of specified task
-*
-* @method folder
-* @param pID {Number} - Task ID
-* @param ganttObj {GanttChart} - The gantt object
-* @return {void}
-*/
-JSGantt.folder= function (pID,ganttObj) {
+JSGantt.findXMLNode=function(pRoot,pNodeName)
+{
+    var vRetValue;
 
-   var vList = ganttObj.getList();
+    try {vRetValue=pRoot.getElementsByTagName(pNodeName);
+    } catch (error)	{ ; } // do nothing, we'll return undefined
 
-   for(i = 0; i < vList.length; i++)
-   {
-      if(vList[i].getID() == pID) {
+    return vRetValue;
+};
 
-         if( vList[i].getOpen() == 1 ) {
-            vList[i].setOpen(0);
-            JSGantt.hide(pID,ganttObj);
+// pType can be 1=numeric, 2=String, all other values just return raw data
+JSGantt.getXMLNodeValue=function(pRoot,pNodeName,pType,pDefault)
+{
+    var vRetValue;
 
-            if (JSGantt.isIE()) 
-               {JSGantt.findObj('group_'+pID).innerText = '+';}
-            else
-               {JSGantt.findObj('group_'+pID).textContent = '+';}
-				
-         } else {
+    try {vRetValue=pRoot.getElementsByTagName(pNodeName)[0].childNodes[0].nodeValue;
+    } catch (error)
+    {
+        if (typeof pDefault!='undefined')vRetValue=pDefault;
+    }
 
-            vList[i].setOpen(1);
+    if (typeof vRetValue!='undefined' && vRetValue!=null)
+    {
+        if (pType==1)vRetValue*=1;
+        else if (pType==2)vRetValue=vRetValue.toString();
+    }
+    return vRetValue;
+};
 
-            JSGantt.show(pID, 1, ganttObj);
+JSGantt.AddXMLTask=function(pGanttVar, pXmlDoc)
+{
+    var project='';
+    var vMSP=false;
+    var Task;
+    var n=0;
+    var m=0;
+    var i=0;
+    var j=0;
+    var k=0;
+    var maxPID=0;
+    var ass=new Array();
+    var assRes=new Array();
+    var res=new Array();
+    var pars=new Array();
+
+    var projNode=JSGantt.findXMLNode(pXmlDoc,'Project');
+    if (typeof projNode!='undefined' && projNode.length>0) project=projNode[0].getAttribute('xmlns');
+
+    if(project=='http://schemas.microsoft.com/project')
+    {
+        vMSP=true;
+        pGanttVar.setDateInputFormat('yyyy-mm-dd');
+        Task=JSGantt.findXMLNode(pXmlDoc,'Task');
+        if (typeof Task=='undefined')n=0;
+        else n=Task.length;
+
+        var resources=JSGantt.findXMLNode(pXmlDoc,'Resource');
+        if (typeof resources=='undefined'){n=0; m=0;}
+        else m=resources.length;
+
+        for(i=0;i<m;i++)
+        {
+            var resname=JSGantt.getXMLNodeValue(resources[i],'Name',2,'');
+            var uid=JSGantt.getXMLNodeValue(resources[i],'UID',1,-1);
+
+            if (resname.length>0 && uid>0) res[uid]=resname;
+        }
+
+        var assignments=JSGantt.findXMLNode(pXmlDoc,'Assignment');
+        if (typeof assignments=='undefined') j=0;
+        else j=assignments.length;
+
+        for(i=0;i<j;i++)
+        {
+            var resUID=JSGantt.getXMLNodeValue(assignments[i],'ResourceUID',1,-1);
+            uid=JSGantt.getXMLNodeValue(assignments[i],'TaskUID',1,-1);
+
+            if (uid>0)
+            {
+                if (resUID>0) assRes[uid]=res[resUID];
+                ass[uid]=assignments[i];
+            }
+        }
 
-               if (JSGantt.isIE()) 
-                  {JSGantt.findObj('group_'+pID).innerText = '�';}
-               else
-                  {JSGantt.findObj('group_'+pID).textContent = '�';}
+        // Store information about parent UIDs in an easily searchable form
+        for(i=0;i<n;i++)
+        {
+            uid=JSGantt.getXMLNodeValue(Task[i],'UID',1,0);
 
-         }
+            if(uid!=0) var vOutlineNumber=JSGantt.getXMLNodeValue(Task[i],'OutlineNumber',2,'0');
+            if (uid>0) pars[vOutlineNumber]=uid;
+            if (uid>maxPID)maxPID=uid;
+        }
 
-      }
-   }
-};
+        for(i=0;i<n;i++)
+        {
+            // optional parameters may not have an entry
+            // Task ID must NOT be zero otherwise it will be skipped
+            var pID=JSGantt.getXMLNodeValue(Task[i],'UID',1,0);
 
-/**
-* Hide children of a task
-*
-* @method hide
-* @param pID {Number} - Task ID
-* @param ganttObj {GanttChart} - The gantt object
-* @return {void}
-*/
-JSGantt.hide=     function (pID,ganttObj) {
-   var vList = ganttObj.getList();
-   var vID   = 0;
-
-   for(var i = 0; i < vList.length; i++)
-   {
-      if(vList[i].getParent() == pID) {
-         vID = vList[i].getID();
-         JSGantt.findObj('child_' + vID).style.display = "none";
-         JSGantt.findObj('childgrid_' + vID).style.display = "none";
-         vList[i].setVisible(0);
-         if(vList[i].getGroup() == 1) 
-            {JSGantt.hide(vID,ganttObj);}
-      }
-
-   }
-};
+            if(pID!=0)
+            {
+                var pName=JSGantt.getXMLNodeValue(Task[i],'Name',2,'No Task Name');
+                var pStart=JSGantt.getXMLNodeValue(Task[i],'Start',2,'');
+                var pEnd=JSGantt.getXMLNodeValue(Task[i],'Finish',2,'');
+                var pLink=JSGantt.getXMLNodeValue(Task[i],'HyperlinkAddress',2,'');
+                var pMile=JSGantt.getXMLNodeValue(Task[i],'Milestone',1,0);
+                var pComp=JSGantt.getXMLNodeValue(Task[i],'PercentWorkComplete',1,0);
+                var pGroup=JSGantt.getXMLNodeValue(Task[i],'Summary',1,0);
+
+                var pParent=0;
+
+                var vOutlineLevel=JSGantt.getXMLNodeValue(Task[i],'OutlineLevel',1,0);
+                if (vOutlineLevel>1)
+                {
+                    vOutlineNumber=JSGantt.getXMLNodeValue(Task[i],'OutlineNumber',2,'0');
+                    pParent=pars[vOutlineNumber.substr(0, vOutlineNumber.lastIndexOf('.'))];
+                }
 
-/**
-* Show children of a task
-*
-* @method show
-* @param pID {Number} - Task ID
-* @param ganttObj {GanttChart} - The gantt object
-* @return {void}
-*/
-JSGantt.show =  function (pID, pTop, ganttObj) {
-   var vList = ganttObj.getList();
-   var vID   = 0;
-
-   for(var i = 0; i < vList.length; i++)
-   {
-      if(vList[i].getParent() == pID) {
-         vID = vList[i].getID();
-         if(pTop == 1) {
-            if (JSGantt.isIE()) { // IE;
-
-               if( JSGantt.findObj('group_'+pID).innerText == '+') {
-                  JSGantt.findObj('child_'+vID).style.display = "";
-                  JSGantt.findObj('childgrid_'+vID).style.display = "";
-                  vList[i].setVisible(1);
-               }
-
-            } else {
- 
-               if( JSGantt.findObj('group_'+pID).textContent == '+') {
-                  JSGantt.findObj('child_'+vID).style.display = "";
-                  JSGantt.findObj('childgrid_'+vID).style.display = "";
-                  vList[i].setVisible(1);
-               }
+                try {var pNotes=Task[i].getElementsByTagName('Notes')[0].childNodes[1].nodeValue; //this should be a CDATA node
+                } catch (error)
+                {pNotes ='';}
 
-            }
+                if(typeof assRes[pID]!='undefined') var pRes=assRes[pID];
+                else pRes='';
+
+                var predecessors=JSGantt.findXMLNode(Task[i],'PredecessorLink');
+                if (typeof predecessors=='undefined') j=0;
+                else j=predecessors.length;
+                var pDepend='';
+
+                for(k=0;k<j;k++)
+                {
+                    var depUID=JSGantt.getXMLNodeValue(predecessors[k],'PredecessorUID',1,-1);
+                    var depType=JSGantt.getXMLNodeValue(predecessors[k],'Type',1,1);
+
+                    if (depUID>0)
+                    {
+                        if (pDepend.length>0)pDepend+=',';
+                        switch(depType)
+                        {
+                            case 0:  pDepend+=depUID+'FF'; break;
+                            case 1:  pDepend+=depUID+'FS'; break;
+                            case 2:  pDepend+=depUID+'SF'; break;
+                            case 3:  pDepend+=depUID+'SS'; break;
+                            default: pDepend+=depUID+'FS'; break;
+                        }
+                    }
+                }
 
-         } else {
+                var pOpen=1;
+                var pCaption='';
 
-            if (JSGantt.isIE()) { // IE;
-               if( JSGantt.findObj('group_'+pID).innerText == '�') {
-                  JSGantt.findObj('child_'+vID).style.display = "";
-                  JSGantt.findObj('childgrid_'+vID).style.display = "";
-                  vList[i].setVisible(1);
-               }
+                if(pGroup>0) var pClass ='ggroupblack';
+                else if(pMile>0) pClass ='gmilestone';
+                else pClass ='gtaskblue';
 
-            } else {
+                // check for split tasks
 
-               if( JSGantt.findObj('group_'+pID).textContent == '�') {
-                  JSGantt.findObj('child_'+vID).style.display = "";
-                  JSGantt.findObj('childgrid_'+vID).style.display = "";
-                  vList[i].setVisible(1);
-               }
-            }
-         }
+                var splits=JSGantt.findXMLNode(ass[pID],'TimephasedData');
+                if (typeof splits=='undefined') j=0;
+                else j=splits.length;
 
-         if(vList[i].getGroup() == 1) 
-            {JSGantt.show(vID, 0,ganttObj);}
+                var vSplitStart=pStart;
+                var vSplitEnd=pEnd;
+                var vSubCreated=false;
+                var vDepend=pDepend.replace(/,*[0-9]+[FS]F/g,'');
 
-      }
-   }
-};
-/**
-* Handles click events on task name, currently opens a new window
-*
-* @method taskLink
-* @param pRef {String} - URL for window
-* @param pWidth {Number} - Width of window
-* @param pHeight {Number} - Height of window
-* @return {void}
-*/
-JSGantt.taskLink = function(pRef,pWidth,pHeight) 
-
-  {
-
-    if(pWidth)  {vWidth =pWidth;}  else {vWidth =400;}
-    if(pHeight) {vHeight=pHeight;} else {vHeight=400;}
-
-    // LDR To open in same window
-    //var OpenWindow=window.open(pRef, "newwin", "height="+vHeight+",width="+vWidth);
-    window.location.href=pRef
-
-  };
-
-/**
-* Parse dates based on gantt date format setting as defined in JSGantt.GanttChart.setDateInputFormat()
-*
-* @method parseDateStr
-* @param pDateStr {String} - A string that contains the date (i.e. "01/01/09")
-* @param pFormatStr {String} - The date format (mm/dd/yyyy,dd/mm/yyyy,yyyy-mm-dd)
-* @return {Datetime}
-*/
-JSGantt.parseDateStr = function(pDateStr,pFormatStr) {
-   var vDate =new Date();	
-   vDate.setTime( Date.parse(pDateStr));
-
-   switch(pFormatStr) 
-   {
-	  case 'mm/dd/yyyy':
-	     var vDateParts = pDateStr.split('/');
-         vDate.setFullYear(parseInt(vDateParts[2], 10), parseInt(vDateParts[0], 10) - 1, parseInt(vDateParts[1], 10));
-         break;
-	  case 'dd/mm/yyyy':
-	     var vDateParts = pDateStr.split('/');
-         vDate.setFullYear(parseInt(vDateParts[2], 10), parseInt(vDateParts[1], 10) - 1, parseInt(vDateParts[0], 10));
-         break;
-	  case 'yyyy-mm-dd':
-	     var vDateParts = pDateStr.split('-');
-         vDate.setFullYear(parseInt(vDateParts[0], 10), parseInt(vDateParts[1], 10) - 1, parseInt(vDateParts[1], 10));
-         break;
+                for(k=0;k<j;k++)
+                {
+                    var vDuration=JSGantt.getXMLNodeValue(splits[k],'Value',2,'0');
+                    //remove all text
+                    vDuration='0'+vDuration.replace(/\D/g,'');
+                    vDuration*=1;
+                    if ((vDuration==0 && !vSubCreated)|| (k+1==j && pGroup==2))
+                    {
+                        // No time booked in the given period (or last entry)
+                        // Make sure the parent task is set as a combined group
+                        pGroup=2;
+                        // Handle last loop
+                        if (k+1==j)vDepend=pDepend.replace(/,*[0-9]+[FS]S/g,'');
+                        // Now create a subtask
+                        maxPID++;
+                        vSplitEnd=JSGantt.getXMLNodeValue(splits[k],(k+1==j)?'Finish':'Start',2,'');
+                        pGanttVar.AddTaskItem(new JSGantt.TaskItem(maxPID, pName, vSplitStart, vSplitEnd, 'gtaskblue', pLink, pMile, pRes, pComp, 0, pID, pOpen, vDepend, pCaption, pNotes, pGanttVar));
+                        vSubCreated=true;
+                        vDepend='';
+                    }
+                    else if (vDuration!=0 && vSubCreated)
+                    {
+                        vSplitStart=JSGantt.getXMLNodeValue(splits[k],'Start',2,'');
+                        vSubCreated=false;
+                    }
+                }
+                if (vSubCreated)pDepend='';
+
+                // Finally add the task
+                pGanttVar.AddTaskItem(new JSGantt.TaskItem(pID, pName, pStart, pEnd, pClass, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGanttVar));
+            }
+        }
+    }
+    else
+    {
+        Task=pXmlDoc.getElementsByTagName('task');
+        n=Task.length;
+
+        for(i=0;i<n;i++)
+        {
+            // optional parameters may not have an entry
+            // Task ID must NOT be zero otherwise it will be skipped
+            pID=JSGantt.getXMLNodeValue(Task[i],'pID',1,0);
+
+            if(pID!=0)
+            {
+                pName=JSGantt.getXMLNodeValue(Task[i],'pName',2,'No Task Name');
+                pStart=JSGantt.getXMLNodeValue(Task[i],'pStart',2,'');
+                pEnd=JSGantt.getXMLNodeValue(Task[i],'pEnd',2,'');
+                pLink=JSGantt.getXMLNodeValue(Task[i],'pLink',2,'');
+                pMile=JSGantt.getXMLNodeValue(Task[i],'pMile',1,0);
+                pComp=JSGantt.getXMLNodeValue(Task[i],'pComp',1,0);
+                pGroup=JSGantt.getXMLNodeValue(Task[i],'pGroup',1,0);
+                pParent=JSGantt.getXMLNodeValue(Task[i],'pParent',1,0);
+                pRes=JSGantt.getXMLNodeValue(Task[i],'pRes',2,'');
+                pOpen=JSGantt.getXMLNodeValue(Task[i],'pOpen',1,1);
+                pDepend=JSGantt.getXMLNodeValue(Task[i],'pDepend',2,'');
+                pCaption=JSGantt.getXMLNodeValue(Task[i],'pCaption',2,'');
+                pNotes=JSGantt.getXMLNodeValue(Task[i],'pNotes',2,'');
+                pClass=JSGantt.getXMLNodeValue(Task[i],'pClass',2);
+                if (typeof pClass=='undefined')
+                {
+                    if(pGroup>0) pClass ='ggroupblack';
+                    else if(pMile>0) pClass ='gmilestone';
+                    else pClass ='gtaskblue';
+                }
+
+                // Finally add the task
+                pGanttVar.AddTaskItem(new JSGantt.TaskItem(pID, pName, pStart, pEnd, pClass, pLink, pMile, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGanttVar));
+            }
+        }
     }
+};
 
-    return(vDate);
-    
+
+JSGantt.benchMark=function(pItem)
+{
+    var vEndTime=new Date().getTime();
+    alert(pItem+': Elapsed time: '+((vEndTime-vBenchTime)/1000)+' seconds.');
+    vBenchTime=new Date().getTime();
 };
 
-/**
-* Display a formatted date based on gantt date format setting as defined in JSGantt.GanttChart.setDateDisplayFormat()
-*
-* @method formatDateStr
-* @param pDate {Date} - A javascript date object
-* @param pFormatStr {String} - The date format (mm/dd/yyyy,dd/mm/yyyy,yyyy-mm-dd...)
-* @return {String}
-*/
-JSGantt.formatDateStr = function(pDate,pFormatStr) {
-       vYear4Str = pDate.getFullYear() + '';
- 	   vYear2Str = vYear4Str.substring(2,4);
-       vMonthStr = (pDate.getMonth()+1) + '';
-       vDayStr   = pDate.getDate() + '';
-
-      var vDateStr = "";	
-
-      switch(pFormatStr) {
-	        case 'mm/dd/yyyy':
-               return( vMonthStr + '/' + vDayStr + '/' + vYear4Str );
-	        case 'dd/mm/yyyy':
-               return( vDayStr + '/' + vMonthStr + '/' + vYear4Str );
-	        case 'yyyy-mm-dd':
-               return( vYear4Str + '-' + vMonthStr + '-' + vDayStr );
-	        case 'mm/dd/yy':
-               return( vMonthStr + '/' + vDayStr + '/' + vYear2Str );
-	        case 'dd/mm/yy':
-               return( vDayStr + '/' + vMonthStr + '/' + vYear2Str );
-	        case 'yy-mm-dd':
-               return( vYear2Str + '-' + vMonthStr + '-' + vDayStr );
-	        case 'mm/dd':
-               return( vMonthStr + '/' + vDayStr );
-	        case 'dd/mm':
-               return( vDayStr + '/' + vMonthStr );
-      }		 
-	  
+JSGantt.getIsoWeek=function(pDate){
+    // We have to compare against the monday of the first week of the year containing 04 jan *not* 01/01
+    // 60*60*24*1000=86400000
+    var dayMiliseconds=86400000;
+    var keyDay=new Date(pDate.getFullYear(),0,4,0,0,0);
+    var keyDayOfWeek=(keyDay.getDay()==0) ? 6 : keyDay.getDay()-1; // define monday as 0
+    var firstMondayYearTime=keyDay.getTime()-(keyDayOfWeek * dayMiliseconds);
+    var thisDate=new Date(pDate.getFullYear(), pDate.getMonth(),pDate.getDate(),0,0,0); // This at 00:00:00
+    var thisTime=thisDate.getTime();
+    var daysFromFirstMonday=Math.round(((thisTime-firstMondayYearTime) / dayMiliseconds));
+    var lastWeek=99;
+    var thisWeek=99;
+
+    var firstMondayYear=new Date(firstMondayYearTime);
+
+    thisWeek=Math.ceil((daysFromFirstMonday+1)/7);
+
+    if (thisWeek<=0) thisWeek=JSGantt.getIsoWeek(new Date(pDate.getFullYear()-1,11,31,0,0,0));
+    else if (thisWeek==53 && (new Date(pDate.getFullYear(),0,1,0,0,0)).getDay()!=4 && (new Date(pDate.getFullYear(),11,31,0,0,0)).getDay()!=4) thisWeek=1;
+    return thisWeek;
 };
 
-/**
-* Parse an external XML file containing task items.
-*
-* @method parseXML
-* @param ThisFile {String} - URL to XML file
-* @param pGanttVar {Gantt} - Gantt object
-* @return {void}
-*/
-JSGantt.parseXML = function(ThisFile,pGanttVar){
-	var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1;   // Is this Chrome 
-	
-	try { //Internet Explorer  
-		xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
-		}
-	catch(e) {
-		try { //Firefox, Mozilla, Opera, Chrome etc. 
-			if (is_chrome==false) {  xmlDoc=document.implementation.createDocument("","",null); }
-		}
-		catch(e) {
-			alert(e.message);
-			return;
-		}
-	}
-
-	if (is_chrome==false) { 	// can't use xmlDoc.load in chrome at the moment
-		xmlDoc.async=false;
-		xmlDoc.load(ThisFile);		// we can use  loadxml
-		JSGantt.AddXMLTask(pGanttVar);
-		xmlDoc=null;			// a little tidying
-		Task = null;
-	}
-	else {
-		JSGantt.ChromeLoadXML(ThisFile,pGanttVar);	
-		ta=null;	// a little tidying	
-	}
+JSGantt.addListener=function (eventName, handler, control)
+{
+    // Check if control is a string
+    if (control===String(control)) control=JSGantt.findObj(control);
+
+    if(control.addEventListener) //Standard W3C
+    {
+        return control.addEventListener(eventName, handler, false);
+    }
+    else if (control.attachEvent) //IExplore
+    {
+        return control.attachEvent('on'+eventName, handler);
+    }
+    else
+    {
+        return false;
+    }
 };
 
-/**
-* Add a task based on parsed XML doc
-*
-* @method AddXMLTask
-* @param pGanttVar {Gantt} - Gantt object
-* @return {void}
-*/
-JSGantt.AddXMLTask = function(pGanttVar){
-
-	Task=xmlDoc.getElementsByTagName("task");
-	
-	var n = xmlDoc.documentElement.childNodes.length;	// the number of tasks. IE gets this right, but mozilla add extra ones (Whitespace)
-	
-	for(var i=0;i<n;i++) {
-	
-		// optional parameters may not have an entry (Whitespace from mozilla also returns an error )
-		// Task ID must NOT be zero other wise it will be skipped
-		try { pID = Task[i].getElementsByTagName("pID")[0].childNodes[0].nodeValue;
-		} catch (error) {pID =0;}
-		pID *= 1;	// make sure that these are numbers rather than strings in order to make jsgantt.js behave as expected.
-
-		if(pID!=0){
-	 		try { pName = Task[i].getElementsByTagName("pName")[0].childNodes[0].nodeValue;
-			} catch (error) {pName ="No Task Name";}			// If there is no corresponding entry in the XML file the set a default.
-		
-			try { pColor = Task[i].getElementsByTagName("pColor")[0].childNodes[0].nodeValue;
-			} catch (error) {pColor ="0000ff";}
-			
-			try { pParent = Task[i].getElementsByTagName("pParent")[0].childNodes[0].nodeValue;
-			} catch (error) {pParent =0;}
-			pParent *= 1;
-	
-			try { pStart = Task[i].getElementsByTagName("pStart")[0].childNodes[0].nodeValue;
-			} catch (error) {pStart ="";}
-
-			try { pEnd = Task[i].getElementsByTagName("pEnd")[0].childNodes[0].nodeValue;
-			} catch (error) { pEnd ="";}
-
-			try { pLink = Task[i].getElementsByTagName("pLink")[0].childNodes[0].nodeValue;
-			} catch (error) { pLink ="";}
-	
-			try { pMile = Task[i].getElementsByTagName("pMile")[0].childNodes[0].nodeValue;
-			} catch (error) { pMile=0;}
-			pMile *= 1;
-
-			try { pRes = Task[i].getElementsByTagName("pRes")[0].childNodes[0].nodeValue;
-			} catch (error) { pRes ="";}
-
-			try { pComp = Task[i].getElementsByTagName("pComp")[0].childNodes[0].nodeValue;
-			} catch (error) {pComp =0;}
-			pComp *= 1;
-
-			try { pGroup = Task[i].getElementsByTagName("pGroup")[0].childNodes[0].nodeValue;
-			} catch (error) {pGroup =0;}
-			pGroup *= 1;
-
-			try { pOpen = Task[i].getElementsByTagName("pOpen")[0].childNodes[0].nodeValue;
-			} catch (error) { pOpen =1;}
-			pOpen *= 1;
-
-			try { pDepend = Task[i].getElementsByTagName("pDepend")[0].childNodes[0].nodeValue;
-			} catch (error) { pDepend =0;}
-			//pDepend *= 1;
-			if (pDepend.length==0){pDepend=''} // need this to draw the dependency lines
-			
-			try { pCaption = Task[i].getElementsByTagName("pCaption")[0].childNodes[0].nodeValue;
-			} catch (error) { pCaption ="";}
-			
-			
-			// Finally add the task
-			pGanttVar.AddTaskItem(new JSGantt.TaskItem(pID , pName, pStart, pEnd, pColor,  pLink, pMile, pRes,  pComp, pGroup, pParent, pOpen, pDepend,pCaption));
-		}
-	}
+JSGantt.addTooltipListeners=function(pGanttChart, pObj1, pObj2)
+{
+    JSGantt.addListener('mouseover', function (e) {JSGantt.showToolTip(pGanttChart, e, pObj2, null, pGanttChart.getTimer());}, pObj1);
+    JSGantt.addListener('mouseout', function (e) {JSGantt.delayedHide(pGanttChart, pGanttChart.vTool, pGanttChart.getTimer());}, pObj1);
 };
 
-/**
-* Load an XML document in Chrome
-*
-* @method ChromeLoadXML
-* @param ThisFile {String} - URL to XML file
-* @param pGanttVar {Gantt} - Gantt object
-* @return {void}
-*/
-JSGantt.ChromeLoadXML = function(ThisFile,pGanttVar){
-// Thanks to vodobas at mindlence,com for the initial pointers here.
-	XMLLoader = new XMLHttpRequest();
-	XMLLoader.onreadystatechange= function(){
-    JSGantt.ChromeXMLParse(pGanttVar);
-	};
-	XMLLoader.open("GET", ThisFile, false);
-	XMLLoader.send(null);
+JSGantt.addThisRowListeners=function(pGanttChart, pObj1, pObj2)
+{
+    JSGantt.addListener('mouseover', function () {pGanttChart.mouseOver(pObj1, pObj2);}, pObj1);
+    JSGantt.addListener('mouseover', function () {pGanttChart.mouseOver(pObj1, pObj2);}, pObj2);
+    JSGantt.addListener('mouseout', function () {pGanttChart.mouseOut(pObj1, pObj2);}, pObj1);
+    JSGantt.addListener('mouseout', function () {pGanttChart.mouseOut(pObj1, pObj2);}, pObj2);
 };
 
-/**
-* Parse XML document in Chrome
-*
-* @method ChromeXMLParse
-* @param pGanttVar {Gantt} - Gantt object
-* @return {void}
-*/
-
-JSGantt.ChromeXMLParse = function (pGanttVar){
-// Manually parse the file as it is loads quicker
-	if (XMLLoader.readyState == 4) {
-		var ta=XMLLoader.responseText.split(/<task>/gi);
-
-		var n = ta.length;	// the number of tasks. 
-		for(var i=1;i<n;i++) {
-			Task = ta[i].replace(/<[/]p/g, '<p');	
-			var te = Task.split(/<pid>/i);
-	
-			if(te.length> 2){var pID=te[1];} else {var pID = 0;}
-			pID *= 1;
-	
-			var te = Task.split(/<pName>/i);
-			if(te.length> 2){var pName=te[1];} else {var pName = "No Task Name";}
-	
-			var te = Task.split(/<pstart>/i);
-			if(te.length> 2){var pStart=te[1];} else {var pStart = "";}
-	
-			var te = Task.split(/<pEnd>/i);
-			if(te.length> 2){var pEnd=te[1];} else {var pEnd = "";}
-	
-			var te = Task.split(/<pColor>/i);
-			if(te.length> 2){var pColor=te[1];} else {var pColor = '0000ff';}
-
-			var te = Task.split(/<pLink>/i);
-			if(te.length> 2){var pLink=te[1];} else {var pLink = "";}
-	
-			var te = Task.split(/<pMile>/i);
-			if(te.length> 2){var pMile=te[1];} else {var pMile = 0;}
-			pMile  *= 1;
-	
-			var te = Task.split(/<pRes>/i);
-			if(te.length> 2){var pRes=te[1];} else {var pRes = "";}	
-	
-			var te = Task.split(/<pComp>/i);
-			if(te.length> 2){var pComp=te[1];} else {var pComp = 0;}	
-			pComp  *= 1;
-	
-			var te = Task.split(/<pGroup>/i);
-			if(te.length> 2){var pGroup=te[1];} else {var pGroup = 0;}	
-			pGroup *= 1;
-
-			var te = Task.split(/<pParent>/i);
-			if(te.length> 2){var pParent=te[1];} else {var pParent = 0;}	
-			pParent *= 1;
-	
-			var te = Task.split(/<pOpen>/i);
-			if(te.length> 2){var pOpen=te[1];} else {var pOpen = 1;}
-			pOpen *= 1;
-	
-			var te = Task.split(/<pDepend>/i);
-			if(te.length> 2){var pDepend=te[1];} else {var pDepend = "";}	
-			//pDepend *= 1;
-			if (pDepend.length==0){pDepend=''} // need this to draw the dependency lines
-			
-			var te = Task.split(/<pCaption>/i);
-			if(te.length> 2){var pCaption=te[1];} else {var pCaption = "";}
-			
-			// Finally add the task
-			pGanttVar.AddTaskItem(new JSGantt.TaskItem(pID , pName, pStart, pEnd, pColor,  pLink, pMile, pRes,  pComp, pGroup, pParent, pOpen, pDepend,pCaption 	));
-		};
-	};
+JSGantt.addFolderListeners=function(pGanttChart, pObj, pID)
+{
+    JSGantt.addListener('click', function () {JSGantt.folder(pID, pGanttChart);}, pObj);
 };
-/**
-* Used for benchmarking performace
-*
-* @method benchMark
-* @param pItem {TaskItem} - TaskItem object
-* @return {void}
-*/
-JSGantt.benchMark = function(pItem){
-   var vEndTime=new Date().getTime();
-   alert(pItem + ': Elapsed time: '+((vEndTime-vBenchTime)/1000)+' seconds.');
-   vBenchTime=new Date().getTime();
+
+JSGantt.addFormatListeners=function(pGanttChart, pFormat, pObj)
+{
+    JSGantt.addListener('click', function () {JSGantt.changeFormat(pFormat, pGanttChart);}, pObj);
 };
 
+JSGantt.addScrollListeners=function(pGanttChart)
+{
+    JSGantt.addListener('scroll', function () {pGanttChart.getChartBody().scrollTop=pGanttChart.getListBody().scrollTop;}, pGanttChart.getListBody());
+    JSGantt.addListener('scroll', function () {pGanttChart.getListBody().scrollTop=pGanttChart.getChartBody().scrollTop;}, pGanttChart.getChartBody());
+    JSGantt.addListener('scroll', function () {pGanttChart.getChartHead().scrollLeft=pGanttChart.getChartBody().scrollLeft;}, pGanttChart.getChartBody());
+    JSGantt.addListener('scroll', function () {pGanttChart.getChartBody().scrollLeft=pGanttChart.getChartHead().scrollLeft;}, pGanttChart.getChartHead());
+    JSGantt.addListener('resize', function () {pGanttChart.getChartHead().scrollLeft=pGanttChart.getChartBody().scrollLeft;}, window);
+    JSGantt.addListener('resize', function () {pGanttChart.getListBody().scrollTop=pGanttChart.getChartBody().scrollTop;}, window);
+};

File diff suppressed because it is too large
+ 0 - 0
htdocs/includes/jsgantt/jsgantt.min.js


+ 16 - 16
htdocs/projet/ganttchart.inc.php

@@ -78,11 +78,11 @@ function reloadGraph() {
 }
 
 
-var g = new JSGantt.GanttChart('g', document.getElementById('GanttChartDIV'), 'day');
-/* For JSGanttImproved var g = new JSGantt.GanttChart(document.getElementById('GanttChartDIV'), 'day'); */
+//var g = new JSGantt.GanttChart('g', document.getElementById('GanttChartDIV'), 'day');
+var g = new JSGantt.GanttChart(document.getElementById('GanttChartDIV'), 'day');
 
-/* For JSGanttImproved if (g.getDivId() != null) */
-if (g)
+if (g.getDivId() != null)
+//if (g)
 {
 	var booShowRessources = 1;
 	var booShowDurations = 1;
@@ -91,9 +91,9 @@ if (g)
 	var graphFormat = "day";
 
 	g.setDateInputFormat('mm/dd/yyyy');  // Set format of input dates ('mm/dd/yyyy', 'dd/mm/yyyy', does not work with 'yyyy-mm-dd')
-	g.setDateDisplayFormat('<?php echo $dateformat; ?>');
-	/* For JSGanttImproved g.setDateTaskDisplayFormat('<?php echo $datehourformat; ?>'); */
-	/* For JSGanttImproved g.setDayMajorDateDisplayFormat('dd mon'); */
+	//g.setDateDisplayFormat('<?php echo $dateformat; ?>');
+	g.setDateTaskDisplayFormat('<?php echo $datehourformat; ?>');
+	g.setDayMajorDateDisplayFormat('dd mon');
 	g.setShowRes(1); 		// Show/Hide Responsible (0/1)
 	g.setShowDur(1); 		// Show/Hide Duration (0/1)
 	g.setShowComp(1); 		// Show/Hide % Complete(0/1)
@@ -132,7 +132,7 @@ else
  * Add a gant chart line
  *
  * @param 	string	$tarr					tarr
- * @param	Task	$task					Task object
+ * @param	array	$task					Task object
  * @param 	Project	$project_dependencies	Project object
  * @param 	int		$level					Level
  * @param 	int		$project_id				Id of project
@@ -148,7 +148,7 @@ function constructGanttLine($tarr,$task,$project_dependencies,$level=0,$project_
     // Resources
     $resources = $task["task_resources"];
     // Define depend (ex: "", "4,13", ...)
-    $depend = "\"";
+    $depend = '';
     $count = 0;
     foreach ($project_dependencies as $value) {
         // Not yet used project_dependencies = array(array(0=>idtask,1=>idtasktofinishfisrt))
@@ -157,7 +157,7 @@ function constructGanttLine($tarr,$task,$project_dependencies,$level=0,$project_
             $count ++;
         }
     }
-    $depend .= "\"";
+   // $depend .= "\"";
     // Define parent
     if ($project_id && $level < 0)
     $parent = 'p'.$project_id;
@@ -170,9 +170,9 @@ function constructGanttLine($tarr,$task,$project_dependencies,$level=0,$project_
 
     // Name
     $name=$task['task_name'];
-    for($i=0; $i < $level; $i++) {
-        $name=' &nbsp; &nbsp; '.$name;
-    }
+    /*for($i=0; $i < $level; $i++) {
+        $name=' - '.$name;
+    }*/
     // Add line to gantt
     /*
 	g.AddTaskItem(new JSGantt.TaskItem(1, 'Define Chart API','',          '',          'ggroupblack','', 0, 'Brian', 0,  1,0,1,'','','Some Notes text',g));
@@ -199,12 +199,12 @@ function constructGanttLine($tarr,$task,$project_dependencies,$level=0,$project_
 	<dt>pGantt</dt><dd>(required) javascript JSGantt.GanttChart object from which to take settings.  Defaults to &quot;g&quot; for backwards compatibility</dd>
     */
 
-    $note="";
+    //$note="";
 
     $s = "\n// Add taks id=".$task["task_id"]." level = ".$level."\n";
-    $s.= "g.AddTaskItem(new JSGantt.TaskItem(".$task['task_id'].",'".dol_escape_js($name)."','".$start_date."', '".$end_date."', '".$task['task_color']."', '".$link."', ".$task['task_milestone'].", '".$resources."', ".($percent >= 0 ? $percent : 0).", ".($task["task_is_group"]>0?1:0).", '".$parent."', 1, '".($depend?$depend:"")."', '".$note."'));";
+   // $s.= "g.AddTaskItem(new JSGantt.TaskItem(".$task['task_id'].",'".dol_escape_js($name)."','".$start_date."', '".$end_date."', '".$task['task_color']."', '".$link."', ".$task['task_milestone'].", '".$resources."', ".($percent >= 0 ? $percent : 0).", ".($task["task_is_group"]>0?1:0).", '".$parent."', 1, '".($depend?$depend:"")."', '".$note."'));";
     // For JSGanttImproved
-    //$s.= "g.AddTaskItem(new JSGantt.TaskItem(".$task['task_id'].",'".dol_escape_js($name)."','".$start_date."', '".$end_date."', 'gtaskblue', '".$link."', ".$task['task_milestone'].", '".$resources."', ".($percent >= 0 ? $percent : 0).", ".($task["task_is_group"]>0?1:0).", '".$parent."', 1, '".($depend?$depend:"")."', '".$note."'));";
+    $s.= "g.AddTaskItem(new JSGantt.TaskItem(".$task['task_id'].",'".dol_escape_js(trim($name))."','".$start_date."', '".$end_date."', '".$task['task_css']."', '".$link."', ".$task['task_milestone'].", '".$resources."', ".($percent >= 0 ? $percent : 0).", ".($task["task_is_group"]).", '".$parent."', 1, '".($depend?$depend:$parent."SS")."', '".($percent >= 0 ? $percent.'%' : '0%')."','".$task['note']."'));";
     echo $s;
 
 

+ 22 - 9
htdocs/projet/ganttview.php

@@ -206,8 +206,18 @@ if (count($tasksarray)>0)
 		$task->fetch($val->id);
 		$tasks[$taskcursor]['task_id']=$val->id;
 		$tasks[$taskcursor]['task_parent']=$val->fk_parent;
-		$tasks[$taskcursor]['task_is_group']=0;
-		$tasks[$taskcursor]['task_milestone']=0;
+        $tasks[$taskcursor]['task_is_group'] = 0;
+        $tasks[$taskcursor]['task_css'] = 'gtaskblue';
+
+        if($val->fk_parent > 0 && $task->hasChildren()> 0){
+            $tasks[$taskcursor]['task_is_group']=1;
+            $tasks[$taskcursor]['task_css'] = 'gtaskred';
+        }
+        elseif($task->hasChildren()> 0) {
+            $tasks[$taskcursor]['task_is_group'] = 1;
+            $tasks[$taskcursor]['task_css'] = 'gtaskgreen';
+        }
+		$tasks[$taskcursor]['task_milestone']='0';
 		$tasks[$taskcursor]['task_percent_complete']=$val->progress;
 		//$tasks[$taskcursor]['task_name']=$task->getNomUrl(1);
 		//print dol_print_date($val->date_start).dol_print_date($val->date_end).'<br>'."\n";
@@ -217,7 +227,7 @@ if (count($tasksarray)>0)
 		$tasks[$taskcursor]['task_color']='b4d1ea';
 		$idofusers=$task->getListContactId('internal');
 		$idofthirdparty=$task->getListContactId('external');
-		$s='';
+  		$s='';
 		if (count($idofusers)>0)
 		{
 			$s.=$langs->trans("Internals").': ';
@@ -225,12 +235,12 @@ if (count($tasksarray)>0)
 			foreach($idofusers as $valid)
 			{
 				$userstatic->fetch($valid);
-				if ($i) $s.=',';
+				if ($i) $s.=', ';
 				$s.=$userstatic->login;
 				$i++;
 			}
 		}
-		if (count($idofusers)>0 && (count($idofthirdparty)>0)) $s.=' - ';
+		//if (count($idofusers)>0 && (count($idofthirdparty)>0)) $s.=' - ';
 		if (count($idofthirdparty)>0)
 		{
 			if ($s) $s.=' - ';
@@ -244,18 +254,21 @@ if (count($tasksarray)>0)
 				$i++;
 			}
 		}
-		if ($s) $tasks[$taskcursor]['task_resources']='<a href="'.DOL_URL_ROOT.'/projet/tasks/contact.php?id='.$val->id.'&withproject=1" title="'.dol_escape_htmltag($s).'">'.$langs->trans("List").'</a>';
-		/* For JSGanttImproved if ($s) $tasks[$taskcursor]['task_resources']=join(',',$idofusers); */
+		//if ($s) $tasks[$taskcursor]['task_resources']='<a href="'.DOL_URL_ROOT.'/projet/tasks/contact.php?id='.$val->id.'&withproject=1" title="'.dol_escape_htmltag($s).'">'.$langs->trans("List").'</a>';
+		/* For JSGanttImproved */
+		//if ($s) $tasks[$taskcursor]['task_resources']=implode(',',$idofusers);
+        $tasks[$taskcursor]['task_resources'] = $s;
 		//print "xxx".$val->id.$tasks[$taskcursor]['task_resources'];
+        $tasks[$taskcursor]['note']=$task->note_public;
 		$taskcursor++;
 	}
 
 	print "\n";
 
-	if (! empty($conf->use_javascript_ajax))
+ 	if (! empty($conf->use_javascript_ajax))
 	{
 	    //var_dump($_SESSION);
-		print '<div id="tabs" class="ganttcontainer" style="border: 1px solid #ACACAC;">'."\n";
+  		print '<div id="tabs" class="gantt" style="width: 80vw;border: 1px solid #ACACAC;">'."\n";
 		include_once DOL_DOCUMENT_ROOT.'/projet/ganttchart.inc.php';
 		print '</div>'."\n";
 	}

+ 17 - 32
htdocs/projet/jsgantt_language.js.php

@@ -32,44 +32,29 @@ if (! defined('NOREQUIREHTML'))   define('NOREQUIREHTML',1);
 if (! defined('NOREQUIREAJAX'))   define('NOREQUIREAJAX','1');
 
 
-require_once '../main.inc.php';
+require_once __DIR__.'/../main.inc.php';
 
 // Define css type
 header('Content-type: text/javascript');
 
 global $langs;
 ?>
-var i18n = new Array();
-i18n["sMinute"]= "<?php echo $langs->transnoentities("Minute") ?>";
-i18n["sMinutes"]= "<?php echo $langs->transnoentities("Minutes") ?>";
-i18n["sHour"]= "<?php echo $langs->transnoentities("Hour") ?>";
-i18n["sHours"]= "<?php echo $langs->transnoentities("Hours") ?>";
-i18n["sDay"]= "<?php echo $langs->transnoentities("Day") ?>";
-i18n["sDays"]= "<?php echo $langs->transnoentities("Days") ?>";
-i18n["sWeek"] = "<?php echo $langs->transnoentities("Week") ?>";
-i18n["sMonth"] = "<?php echo $langs->transnoentities("Month") ?>";
-i18n["sQuarter"] = "<?php echo $langs->transnoentities("Quadri") ?>";
-i18n["View"] = "<?php echo $langs->transnoentities("View") ?>";
-i18n["Resource"] = "<?php echo $langs->transnoentities("Resources") ?>";
-i18n["Duration"] = "<?php echo $langs->transnoentities("Duration") ?>";
-i18n["Start_Date"] = "<?php echo $langs->transnoentities("DateStart") ?>";
-i18n["End_Date"] = "<?php echo $langs->transnoentities("DateEnd") ?>";
-i18n["Date_Format"] = "<?php echo $langs->transnoentities("Format") ?>";
-i18n["January"] = "<?php echo $langs->transnoentities("January") ?>";
-i18n["February"] = "<?php echo $langs->transnoentities("February") ?>";
-i18n["March"] = "<?php echo $langs->transnoentities("March") ?>";
-i18n["April"] = "<?php echo $langs->transnoentities("April") ?>";
-i18n["May"] = "<?php echo $langs->transnoentities("May") ?>";
-i18n["June"] = "<?php echo $langs->transnoentities("June") ?>";
-i18n["July"] = "<?php echo $langs->transnoentities("July") ?>";
-i18n["August"] = "<?php echo $langs->transnoentities("August") ?>";
-i18n["September"] = "<?php echo $langs->transnoentities("Septembre") ?>";
-i18n["October"] = "<?php echo $langs->transnoentities("October") ?>";
-i18n["November"] = "<?php echo $langs->transnoentities("November") ?>";
-i18n["December"] = "<?php echo $langs->transnoentities("December") ?>";
-i18n["Quarter"] = "<?php echo $langs->transnoentities("Quarter") ?>";
-i18n["Period"] = "<?php echo $langs->transnoentities("Period") ?>";
-
 
+var vLangs={'<?php print $langs->getDefaultLang(1);?>':
+{'format':'<?php print $langs->transnoentities('Periodo');?>','hour':'"<?php print $langs->transnoentities('Hour'); ?>','day':'<?php print $langs->transnoentities('Day'); ?>',
+    'week':'<?php print $langs->transnoentities('Week'); ?>','month':'<?php print $langs->transnoentities('Month'); ?>','quarter':'<?php print $langs->transnoentities('Quadri'); ?>',
+    'hours':'<?php print $langs->transnoentities('Hours'); ?>','days':'<?php print $langs->transnoentities('Days'); ?>','weeks':'<?php print $langs->transnoentities('Weeks');?>',
+    'months':'<?php print $langs->transnoentities('Months'); ?>','quarters':'<?php print $langs->transnoentities('Quadri'); ?>','hr':'Hr','dy':'<?php print $langs->transnoentities('Day'); ?>','wk':'<?php print $langs->transnoentities('Week'); ?>','mth':'<?php print $langs->transnoentities('Month'); ?>','qtr':'<?php print $langs->transnoentities('Quadri'); ?>','hrs':'<?php print $langs->transnoentities('Hours'); ?>',
+    'dys':'<?php print $langs->transnoentities('Days'); ?>','wks':'<?php print $langs->transnoentities('Weeks'); ?>','mths':'<?php print $langs->transnoentities('Months'); ?>','qtrs':'<?php print $langs->transnoentities('Quadri'); ?>','resource':'<?php print $langs->transnoentities('Resources'); ?>','duration':'<?php print $langs->transnoentities('Duration'); ?>','comp':'% <?php print $langs->transnoentities('Total'); ?>',
+    'completion':'<?php print $langs->transnoentities('Total'); ?>','startdate':'<?php print $langs->transnoentities('DateStart'); ?>','enddate':'<?php print $langs->transnoentities('DateEnd'); ?>','moreinfo':'<?php print $langs->transnoentities('MoreInformation'); ?>',
+    'notes':'<?php print $langs->transnoentities('NotePublic'); ?>',
+    'january':'<?php print $langs->transnoentities('January'); ?>','february':'<?php print $langs->transnoentities('February'); ?>','march':'<?php print $langs->transnoentities('March'); ?>','april':'<?php print $langs->transnoentities('April'); ?>','maylong':'<?php print $langs->transnoentities('May'); ?>','june':'<?php print $langs->transnoentities('June'); ?>','july':'<?php print $langs->transnoentities('July'); ?>',
+    'august':'<?php print $langs->transnoentities('August'); ?>','september':'<?php print $langs->transnoentities('September'); ?>','october':'<?php print $langs->transnoentities('October'); ?>','november':'<?php print $langs->transnoentities('November'); ?>','december':'<?php print $langs->transnoentities('December'); ?>',
+    'jan':'<?php print $langs->transnoentities('JanuaryMin'); ?>','feb':'<?php print $langs->transnoentities('FebruaryMin'); ?>','mar':'<?php print $langs->transnoentities('MarchMin'); ?>','apr':'<?php print $langs->transnoentities('AprilMin'); ?>','may':'<?php print $langs->transnoentities('MayMin'); ?>','jun':'<?php print $langs->transnoentities('JuneMin'); ?>','jul':'<?php print $langs->transnoentities('JulyMin'); ?>',
+    'aug':'<?php print $langs->transnoentities('AugustMin'); ?>','sep':'<?php print $langs->transnoentities('SeptemberMin'); ?>','oct':'<?php print $langs->transnoentities('OctoberMin'); ?>','nov':'<?php print $langs->transnoentities('NovemberMin'); ?>','dec':'<?php print $langs->transnoentities('DecemberMin'); ?>',
+    'sunday':'<?php print $langs->transnoentities('Sunday'); ?>','monday':'<?php print $langs->transnoentities('Monday'); ?>','tuesday':'<?php print $langs->transnoentities('Tuesday'); ?>','wednesday':'<?php print $langs->transnoentities('Wednesday'); ?>','thursday':'<?php print $langs->transnoentities('Thursday'); ?>','friday':'<?php print $langs->transnoentities('Friday'); ?>','saturday':'<?php print $langs->transnoentities('Saturday'); ?>',
+    'sun':'<?php print $langs->transnoentities('SundayMin'); ?>','mon':'<?php print $langs->transnoentities('MondayMin'); ?>','tue':'<?php print $langs->transnoentities('TuesdayMin'); ?>','wed':'<?php print $langs->transnoentities('WednesdayMin'); ?>','thu':'<?php print $langs->transnoentities('ThursdayMin'); ?>','fri':'<?php print $langs->transnoentities('FridayMin'); ?>','sat':'<?php print $langs->transnoentities('SaturdayMin'); ?>'}
+};
+var vLang='<?php print $langs->getDefaultLang(1);?>';
 <?php
 if (is_object($db)) $db->close();

Some files were not shown because too many files changed in this diff