rays-javascript-extension/build/ryzproj.bundle.min.js

25 lines
10 KiB
JavaScript

/**
* Ray's Projects (Bundled with TaskGrid and GanttChart)
* @version v0.1.0.104 (2023/12/27 1051)
*/
class RyzGanttChart{constructor(b,a){this.Canvas=new Canvas(b);this.Options=Object.assign(this.DefaultOptions,a);this.Debug=!1;this.StartDate=this.Project=null}get DefaultOptions(){return{DayWidth:24,HeaderRow:{Height:[21,21]},Row:{Height:28,Task:{Height:13,PaddingTop:6,BorderColour:"#555555",FillColour:"#9CC2E6"},CollatedTask:{Height:3,PaddingTop:11,BorderColour:"#555555",FillColour:"#555555"}},Line:{Margin:5,Colour:"#555555",Width:1,ArrowSize:5},DateFont:"7pt sans-serif",DateForeColour:"#636363",
BorderWidth:1,BorderColour:"#B8B8B8",BorderDashPattern:[1,3]}}get HeaderHeight(){return this.Options.HeaderRow.Height[0]+this.Options.HeaderRow.Height[1]}Invalidate(){this.Canvas.Clear();if(null!=this.Project){var b=this.Project.ExportTasks(),a=(this.Project.Duration+2)*this.Options.DayWidth,c=b.length*this.Options.Row.Height+this.HeaderHeight;this.AutoSize=!1;this.ClientWidth=a;this.ClientHeight=c;this.Canvas.Invalidate();this.drawChartLabel(this.Project);this.drawTasks(b);this.drawLines(b)}}Load(b){this.Canvas.Clear();
null!=b&&(this.Project=b,this.StartDate=new Date(b.StartDate),this.Invalidate())}drawChartLabel(b){var a=this.Canvas.ClientWidth;const c=this.Canvas.ClientHeight,e=b.Duration+2;let d=new Date(this.StartDate);d.addDays(-1);for(var f=1;f<e;f++)this.Canvas.DrawVerticalLine(this.Options.DayWidth*f,this.Options.HeaderRow.Height[0]+this.Options.BorderWidth,this.Options.HeaderRow.Height[1]-2*this.Options.BorderWidth,this.Options.BorderColour,{});this.Canvas.DrawHorizontalLine(0,this.Options.HeaderRow.Height[0],
a,this.Options.BorderColour);this.Canvas.DrawHorizontalLine(0,this.HeaderHeight-this.Options.BorderWidth,a,this.Options.BorderColour);for(a=0;a<e;a++){f=Date.addDays(d,a);const h=this.Options.DayWidth*a;if(1==f.getDate()){var k=this.Canvas.MeasureText(this.Options.DateFont,"#"),g=h+2;k=Math.half(this.Options.HeaderRow.Height[0]-k.H);this.Canvas.DrawText(g,k,f.toCString("MMMM"),this.Options.DateFont,this.Options.DateForeColour)}g={X:h,Y:this.Options.HeaderRow.Height[1],W:this.Options.DayWidth-this.Options.BorderWidth,
H:this.Options.HeaderRow.Height[1]-2*this.Options.BorderWidth};this.Canvas.FillText(g,f.getDate(),this.Options.DateFont,this.Options.DateForeColour);this.Debug&&this.Canvas.DrawRectangle(g,"red",{});g=c-this.Options.BorderWidth;b.Project.StartOfWeek==f.getDay()?this.Canvas.DrawVerticalLine(h,this.HeaderHeight,g,this.Options.BorderColour,{}):this.Canvas.DrawVerticalLine(h,this.HeaderHeight,g,this.Options.BorderColour,{LineDash:this.Options.BorderDashPattern})}}drawTasks(b){for(let a=0;a<b.length;a++){const c=
1==b[a].IsCollated?this.Options.Row.CollatedTask:this.Options.Row.Task,e=this.getTaskRectangle(b[a],c);0>=b[a].Duration?this.Canvas.DrawDiamond(e,c.BorderColour,{FillColour:c.FillColour}):this.Canvas.DrawRectangle(e,c.BorderColour,{FillColour:c.FillColour})}}drawLines(b){for(let e=0;e<b.length;e++){if(null==b[e].PredecessorTaskID)continue;if(null==b[e].PredecessorTaskNo)continue;var a=this.getTaskRectangle(b[e],1==b[e].IsCollated?this.Options.Row.CollatedTask:this.Options.Row.Task),c=b.first("Order",
b[e].PredecessorTaskNo);if(null==c)continue;c=this.getTaskRectangle(c,1==c.IsCollated?this.Options.Row.CollatedTask:this.Options.Row.Task);let d=[];d.push({X:c.X+c.W,Y:c.Y+Math.half(c.H)});d.push({X:a.X+this.Options.Line.Margin,Y:c.Y+Math.half(c.H)});d.push({X:a.X+this.Options.Line.Margin,Y:a.Y-this.Options.Line.Margin});d.push({X:a.X+this.Options.Line.Margin,Y:a.Y});this.Canvas.DrawLines(d,this.Options.Line.Colour,{LineWidth:this.Options.Line.Width});a={X:a.X+this.Options.Line.Margin-Math.half(this.Options.Line.ArrowSize),
Y:a.Y-this.Options.Line.ArrowSize,W:this.Options.Line.ArrowSize,H:this.Options.Line.ArrowSize};this.Canvas.DrawArrowS(a,this.Options.Line.Colour,{FillColour:this.Options.Line.Colour})}}getTaskRectangle(b,a){const c=b.Order-1;0>=b.Duration?(b={X:this.Options.DayWidth*(Date.diffDays(this.StartDate,b.StartDate)+1),Y:this.HeaderHeight+this.Options.Row.Height*c+a.PaddingTop,W:a.Height,H:a.Height},b.X-=Math.half(b.W)):b={X:this.Options.DayWidth*(Date.diffDays(this.StartDate,b.StartDate)+1),Y:this.HeaderHeight+
this.Options.Row.Height*c+a.PaddingTop,W:this.Options.DayWidth*b.Duration,H:a.Height};return b}};class RyzProject{constructor(b){this.Project=Object.assign(this.newProject,b);this.Tasks=[];this.initialiseComponents()}initialiseComponents(){}get NewTask(){return{ID:null,Name:"",Description:"",Tag:null,StartDelay:0,Duration:1,PredecessorTaskID:null,IsCollated:!1,CollatedTaskID:null,ActuWorkHours:null,Progress:0,Resources:[]}}get StartDate(){return new Date(this.Project.StartDate)}get FinishDate(){let b=new Date(this.Project.StartDate);this.ExportTasks().forEach(a=>{null!=a.FinishDate&&(b=Date.max(b,
a.FinishDate))});return b}get Duration(){return Date.diffDays(this.StartDate,this.FinishDate)}get newProject(){return{Name:"",Description:"",StartDate:Date.today(),Tag:null,StartOfWeek:1,WorkHours:[0,7.5,7.5,7.5,7.5,7.5,0]}}get newTaskNode(){return{Order:null,ID:null,Name:"",Description:"",Tag:null,StartDate:null,FinishDate:null,StartDelay:0,Duration:1,PredecessorTaskID:null,IsCollated:!1,CollatedTaskID:null,CalcWorkHours:0,ActuWorkHours:null,Progress:0,Resources:[],Level:0,PredecessorTaskNo:null,
Tasks:[]}}AddTask(b){b=Object.assign(this.NewTask,b);b=Object.assign(this.newTaskNode,b);if(null==b.PredecessorTaskID&&null==b.CollatedTaskID)this.Tasks.push(b);else if(null!=b.PredecessorTaskID){var a=this.FindTask(b.PredecessorTaskID);null!=a?a.Tasks.push(b):this.log("Task not found ("+b.PredecessorTaskID+")")}else null!=b.CollatedTaskID?(a=this.FindTask(b.CollatedTaskID),null!=a?a.Tasks.push(b):this.log("Task not found ("+b.CollatedTaskID+")")):this.log("Task not found ("+b.ID+")")}ClearTasks(){this.Tasks=
[]}ExportTasks(){let b=this.Tasks.copy().flatten("Tasks");for(var a=0;a<b.length;a++)b[a].Order=a+1,delete b[a].Tasks;return b}FindTask(b){let a=null;this.Tasks.forEachTree("Tasks",function(c){return c.ID==b?(a=c,!1):!0});return a}GetNodeType(b){return null==b?0:null==b.PredecessorTaskID&&null==b.CollatedTaskID&&0==b.IsCollated?1:null==b.PredecessorTaskID&&null==b.CollatedTaskID&&1==b.IsCollated?2:null!=b.PredecessorTaskID&&null==b.CollatedTaskID&&0==b.IsCollated?3:null!=b.PredecessorTaskID&&null==
b.CollatedTaskID&&1==b.IsCollated?4:null!=b.CollatedTaskID&&0==b.IsCollated?5:null!=b.CollatedTaskID&&1==b.IsCollated?6:0}Invalidate(){let b=!1;this.Recalculate()||(b=!1);return b}Recalculate(){let b=!1;this.Sort();let a=this.Tasks.flatten("Tasks");for(var c=0;c<a.length;c++)a[c].Order=c+1,a[c].StartDate=null,a[c].FinishDate=null,a[c].CalcWorkHours=null;for(var e=0;128>e;e++){if(0>=a.copy().countMany({propName:"StartDate",value:null},{propName:"FinishDate",value:null})){b=!0;break}this.log("Round "+
(e+1));for(c=0;c<a.length;c++)switch(this.GetNodeType(a[c])){case 1:a[c].StartDate=Date.addDays(this.Project.StartDate,a[c].StartDelay);a[c].FinishDate=Date.addDays(a[c].StartDate,a[c].Duration);break;case 2:a[c].StartDate=Date.addDays(this.Project.StartDate,a[c].StartDelay);a[c].Progress=0;this.recalculateCollatedTask(a,c);break;case 3:var d=a.first("ID",a[c].PredecessorTaskID);null!=d&&null!=d.FinishDate&&(a[c].StartDate=Date.addDays(d.FinishDate,a[c].StartDelay),a[c].FinishDate=Date.addDays(a[c].StartDate,
a[c].Duration));break;case 4:d=a.first("ID",a[c].PredecessorTaskID);null!=d&&null!=d.FinishDate&&(a[c].StartDate=Date.addDays(d.FinishDate,a[c].StartDelay),a[c].Progress=0,this.recalculateCollatedTask(a,c));break;case 5:d=a.first("ID",a[c].CollatedTaskID);null!=d&&null!=d.StartDate&&(a[c].StartDate=Date.addDays(d.StartDate,a[c].StartDelay),a[c].FinishDate=Date.addDays(a[c].StartDate,a[c].Duration),a[c].Level=d.Level+1);break;case 6:d=a.first("ID",a[c].CollatedTaskID),null!=d&&null!=d.StartDate&&(a[c].StartDate=
Date.addDays(d.StartDate,a[c].StartDelay),a[c].Progress=0,a[c].Level=d.Level+1,this.recalculateCollatedTask(a,c))}}this.recalculateWorkHours(a);this.recalculatePredecessorTaskNo(a);return b}Sort(){this.Tasks.sortTree("Tasks","StartDelay")}log(b){console.log(b)}recalculateWorkHours(b){for(var a=0;a<b.length;a++){if(null==b[a].StartDate)continue;if(null==b[a].FinishDate)continue;if(0>=b[a].Duration)continue;let c=0,e=new Date(b[a].StartDate);for(let d=0;d<b[a].Duration;d++){const f=e.getDay();c+=this.Project.WorkHours[f]}b[a].CalcWorkHours=
c}}recalculatePredecessorTaskNo(b){for(var a=0;a<b.length;a++){if(null==b[a].PredecessorTaskID)continue;const c=this.FindTask(b[a].PredecessorTaskID);null!=c&&(b[a].PredecessorTaskNo=c.Order)}}recalculateCollatedTask(b,a){let c=b.select("CollatedTaskID",b[a].ID);if(0>=c.length)return b[a].Duration=0,new Date(b[a].StartDate);if(c.any("FinishDate",null))return b[a].Duration=0,null;let e=new Date(b[a].StartDate);c.forEach(d=>{d.FinishDate>e&&(e=d.FinishDate)});b[a].Progress=Math.average(c.toList("Progress"));
b[a].FinishDate=new Date(e);b[a].Duration=Date.diffDays(b[a].StartDate,b[a].FinishDate)}};class ProjectTaskGrid{constructor(b){this.Container=b;this.Columns=[null,null,"Task Name","Duration","Start","Finish","Predecessor","Resource Names",null];this.initialiseComponents()}initialiseComponents(){let b;b="<table class='ryz-project'>"+this.renderTHead();b=b+"<tbody>"+this.renderPlaceholder();this.Container.innerHTML=b+"</tbody></table>"}Render(b){const a=this;let c="";c+="<table class='ryz-project'>";c+=a.renderTHead();c+="<tbody>";b.forEach(e=>{c+=a.renderRow(e)});c+="</tbody>";c+="</table>";
a.Container.innerHTML=c}renderTHead(){let b="";b+="<thead>";b+="<tr>";this.Columns.forEach(a=>{b+="<th>"+(a??"")+"</th>"});b+="</tr>";return b+="</thead>"}renderPlaceholder(){return"<tr><td></td><td colspan='"+(this.Columns.length-1+"' class='c'>Loading...</td></tr>")}renderRow(b){let a="";a=1==b.IsCollated?a+"<tr class='b'>":a+"<tr>";a+="<td class='c'>"+b.Order+"</td>";a+="<td></td><td>";for(let c=0;c<b.Level;c++)a+="<span class='i'></span>";a+=b.Name;a=a+"</td><td>"+(b.Duration+" day"+(1==parseInt(b.Duration)?
"":"s")+"</td>");a+="<td class='c'>"+(new Date(b.StartDate)).toLocaleDateString()+"</td>";a+="<td class='c'>"+(new Date(b.FinishDate)).toLocaleDateString()+"</td>";a+="<td class='c'>"+(b.PredecessorTaskNo??"")+"</td>";return a+"<td></td><td></td></tr>"}};