const schedule_left=12;
const schedule_top=28;
const schedule_action_none=0;
const schedule_action_add_new=1;
const schedule_action_adjust_right=2;
const schedule_action_adjust_left=3;
const schedule_action_drag=4;
class SchedulePicker{
constructor(name,json,callback){
this.cursorAction=0;
this.cursorObjectNum=-1;
this.selectedObjNum=-1;
this.dragStartedFlag=false;
this.dragInProgressFlag=false;
this.startDragTime=0;
this.dragObject=null;
this.objects=[];
this.container=document.getElementById('schedulePanel');
this.nameInput=document.getElementById('scheduleName');
this.startTime=document.getElementById('scheduleStart');
this.endTime=document.getElementById('scheduleEnd');
this.canvas=document.getElementById('scheduleCanvas');
this.nameInput.value=name?name:'Untitled Schedule';
this.startTime.oninput=(e)=>{this.startTimeChanged()};
this.endTime.oninput=(e)=>{this.endTimeChanged()};
this.canvas.onmousedown=(e)=>this.mousedown(e);
this.canvas.onmousemove=(e)=>this.mousemove(e);
this.canvas.onmouseup=(e)=>this.mouseup(e);
this.canvas.oncontextmenu=(e)=>this.rightclick(e);
this.canvas.onkeydown=(e)=>this.keydown(e);
document.getElementById('scheduleToggle').onclick=()=>{
this.toggle();
};
document.getElementById('scheduleCancel').onclick=()=>{
this.container.style.display='none';
};
document.getElementById('scheduleSave').onclick=()=>{
callback(this.nameInput.value?this.nameInput.value:Loc('Untitled Schedule'),this.json());
this.container.style.display='none';
};
document.getElementById('scheduleCode').onchange=()=>{
let str=document.getElementById('scheduleText').value;
str.replace(/[^0-9,\[\]]/g,'');
this.setJson(str);
}
this.setJson(json);
this.setTimeBoxes();
this.container.style.display='flex';
}
setJson(json){
let newObjects=JSON.parse(json);
if(newObjects){
this.objects=newObjects;
this.selectedObjNum=-1;
this.draw();
}
}
json(){
this.checkObjects();
return JSON.stringify(this.objects);
}
toggle(){
let button=document.getElementById('scheduleToggle');
if(button.getAttribute('aria-pressed')==='false'){
document.getElementById('scheduleText').value=this.json();
document.getElementById('scheduleCode').style.width=document.getElementById('scheduleGui').offsetWidth+'px';
document.getElementById('scheduleCode').style.height=document.getElementById('scheduleGui').offsetHeight+'px';
button.setAttribute('aria-pressed','true');
document.getElementById('scheduleGui').style.display='none';
document.getElementById('scheduleCode').style.display='flex';
}else{
button.setAttribute('aria-pressed','false');
document.getElementById('scheduleGui').style.display='block';
document.getElementById('scheduleCode').style.display='none';
}
}
mousedown(e){
if(this.objects===null)return;
this.mousemove(e);
if(this.cursorAction==schedule_action_none)return;
this.selectedObjNum=this.cursorObjectNum;
if(this.selectedObjNum>-1)this.selectedObjNum=this.moveObjectToEndOfList(this.selectedObjNum);
if(!this.dragStartedFlag){
this.dragStartedFlag=true;
this.startDragTime=this.timeFromEvent(e);
if(this.selectedObjNum>-1)this.dragObject=[this.objects[this.selectedObjNum][0],this.objects[this.selectedObjNum][1]];
if(this.cursorAction==schedule_action_add_new){
this.selectedObjNum=this.objects.length;
this.objects.push([this.startDragTime,this.startDragTime+10]);
}else if(this.cursorAction==schedule_action_drag){
this.canvas.style.cursor='grabbing';
}
}
this.draw();
}
mousemove(e){
if(this.objects===null)return;
if(this.dragStartedFlag && this.selectedObjNum>-1){
this.mousedrag(e);
return;
}
const x=e.offsetX,y=e.offsetY;
if(x>=0 && x<=720 &&((y>=0*33 && y<=0*33+18)||(y>=1*33 && y<=1*33+18)||(y>=2*33 && y<=2*33+18)||(y>=3*33 && y<=3*33+18)||(y>=4*33 && y<=4*33+18)||(y>=5*33 && y<=5*33+18)||(y>=6*33 && y<=6*33+18))){
let cursorTime=this.timeFromEvent(e);
for(let i in this.objects){
let obj=this.objects[i];
if(cursorTime>obj[0]-7 && cursorTime<obj[0]+4){
this.cursorAction=schedule_action_adjust_left;
this.cursorObjectNum=i;
this.canvas.style.cursor='w-resize';
return;
}
if(cursorTime>obj[1]-4 && cursorTime<obj[1]+7){
this.cursorAction=schedule_action_adjust_right;
this.cursorObjectNum=i;
this.canvas.style.cursor='e-resize';
return;
}
if(obj[1]>obj[0]){
if(cursorTime>obj[0] && cursorTime<obj[1]){
this.cursorAction=schedule_action_drag;
this.cursorObjectNum=i;
this.canvas.style.cursor='grab';
return;
}
}else if(cursorTime>obj[0] || cursorTime<obj[1]){
this.cursorAction=schedule_action_drag;
this.cursorObjectNum=i;
this.canvas.style.cursor='grab';
return;
}
}
this.cursorAction=schedule_action_add_new;
this.cursorObjectNum=-1;
this.canvas.style.cursor='copy';
}else{
this.cursorAction=schedule_action_none;
this.cursorObjectNum=-1;
this.canvas.style.cursor='default';
}
}
mousedrag(e){
if(this.objects===null)return;
let mouseTime=this.timeFromEvent(e);
if(this.dragInProgressFlag || mouseTime!=this.startDragTime){
let obj=this.objects[this.selectedObjNum];
if(this.cursorAction==schedule_action_adjust_left){
obj[0]=mouseTime;
if(obj[0]>obj[1])obj[0]-=10080;
if(obj[0]>obj[1]-10)obj[0]=obj[1]-10;
if(obj[0]<0)obj[0]+=10080;
}else if((this.cursorAction==schedule_action_adjust_right || this.cursorAction==schedule_action_add_new)){
obj[1]=mouseTime;
if(obj[1]<obj[0])obj[1]+=10080;
if(obj[1]<obj[0]+10)obj[1]=obj[0]+10;
if(obj[1]>10079)obj[1]-=10080;
}else if(this.cursorAction==schedule_action_drag){
let timeDifference=mouseTime-this.startDragTime;
obj[0]=this.dragObject[0]+timeDifference;
obj[1]=this.dragObject[1]+timeDifference;
if(obj[0]<0)obj[0]+=10080;
else if(obj[0]>10079)obj[0]-=10080;
if(obj[1]<0)obj[1]+=10080;
else if(obj[1]>10079)obj[1]-=10080;
}
this.dragInProgressFlag=true;
this.draw();
}
}
mouseup(e){
this.dragStartedFlag=false;
this.dragInProgressFlag=false;
this.draw();
}
rightclick(e){
e.preventDefault();
return false;
}
keydown(e){
if((this.selectedObjNum>-1)&&(e.key==='Delete' || e.key==='Backspace' || e.keyCode===46 || e.keyCode===8)){
this.objects.splice(this.selectedObjNum,1);
if(this.selectedObjNum>=this.objects.length)this.selectedObjNum--;
this.draw();
}
}
draw(){
const ctx=this.canvas.getContext('2d');
ctx.clearRect(0,0,720,716);
for(let i in this.objects){
let obj=[this.objects[i][0],this.objects[i][1]];
if(obj[0]==obj[1])continue;
if(i==this.selectedObjNum)ctx.fillStyle='rgba(240,30,30,1)';
else ctx.fillStyle='rgba(255,102,100,1)';
if(obj[0]<0 || obj[0]>10079)obj[0]=0;
if(obj[1]<0 || obj[1]>10079)obj[1]=0;
if(!obj[1])obj[1]=10080;
let startDay=Math.floor(obj[0]/1440);
let endDay=Math.floor((obj[1]-1)/1440);
if(startDay==endDay && obj[1]>=obj[0]){
const r=this.rectFromObject(obj);
ctx.fillRect(r.x,r.y,r.w,r.h);
}else{
let end=obj[1]
obj[1]=(startDay*1440)+1440;
const r=this.rectFromObject(obj);
ctx.fillRect(r.x,r.y,r.w,r.h);
for(let j=startDay+1;;j++){
if(j>6)j=0;
obj[0]=j*1440;
if(j==endDay)obj[1]=end;
else obj[1]=(j*1440)+1440;
if(!obj[1])obj[1]=10080;
const r=this.rectFromObject(obj);
ctx.fillRect(r.x,r.y,r.w,r.h);
if(j==endDay)break;
}
}
}
this.setTimeBoxes();
}
rectFromObject(obj){
let left=Math.round((obj[0]%1440)*720/1440);
let right=Math.round((obj[1]%1440)*720/1440);
if(right<=left)right=720;
return{x:left,y:Math.floor(obj[0]/1440)*33,w:right-left,h:18};
}
timeFromEvent(e){
let minute=Math.floor(e.offsetX*1440/720);
if(minute>1440)minute=1440;
else if(minute<0)minute=0;
let day=Math.floor(e.offsetY/33);
if(day>6)day=6;
else if(day<0)day=0;
let t=(day*1440)+minute;
if(t>10079)t=0;
if(!e.shiftKey)t=Math.round(t/10)*10;
return t;
}
moveObjectToEndOfList(i){
let obj=this.objects.splice(i,1)[0];
this.objects.push(obj);
return this.objects.length-1;
}
checkObjects(){
this.objects=this.objects.filter(obj=>obj[0]!==obj[1]);
this.objects.sort((a,b)=>a[0]-b[0]);
for(let removeJFlag=false,foundFlag=true;foundFlag;){
foundFlag=false;
for(let i=0;i<this.objects.length;i++){
for(let j=0;j<this.objects.length;j++){
if(i==j)continue;
removeJFlag=false;
if(this.objects[i][1]>this.objects[i][0]){
if(this.objects[j][0]>=this.objects[i][0] && this.objects[j][0]<=this.objects[i][1]){
if(this.objects[j][1]>this.objects[j][0]){
if(this.objects[j][1]>this.objects[i][1])this.objects[i][1]=this.objects[j][1];
removeJFlag=true;
}else{
this.objects[i][1]=this.objects[j][1];
removeJFlag=true;
}
}
}else if(this.objects[j][0]>=this.objects[i][0] || this.objects[j][0]<=this.objects[i][1]){
if(this.objects[j][1]>this.objects[j][0]){
if(this.objects[j][0]<this.objects[i][1] && this.objects[j][1]>this.objects[i][1])this.objects[i][1]=this.objects[j][1];
else if(this.objects[i][1]==0 && this.objects[j][0]==0)this.objects[i][1]=this.objects[j][1];
removeJFlag=true;
}else{
if(this.objects[j][1]>this.objects[i][1])this.objects[i][1]=this.objects[j][1];
removeJFlag=true;
}
}
if(removeJFlag){
this.objects.splice(j,1);
if(i>j)i--;
if(this.selectedObjNum==j)this.selectedObjNum=i;
else if(this.selectedObjNum>j)this.selectedObjNum--;
foundFlag=true;
}
}
}
}
}
setTimeBoxes(){
if(this.selectedObjNum>-1){
let dayMinutes=this.objects[this.selectedObjNum][0]%1440;
this.startTime.value=String(Math.floor(dayMinutes/60)).padStart(2,"0")+':'+String(dayMinutes%60).padStart(2,"0");
dayMinutes=this.objects[this.selectedObjNum][1]%1440;
this.endTime.value=String(Math.floor(dayMinutes/60)).padStart(2,"0")+':'+String(dayMinutes%60).padStart(2,"0");
this.startTime.style.pointerEvents='';
this.startTime.style.color='';
this.endTime.style.pointerEvents='';
this.endTime.style.color='';
}else{
this.startTime.value='';
this.startTime.style.pointerEvents='none';
this.startTime.style.color='grey';
this.endTime.value='';
this.endTime.style.pointerEvents='none';
this.endTime.style.color='grey';
}
}
startTimeChanged(){
if(this.selectedObjNum<0)return;
let midnight=Math.floor(this.objects[this.selectedObjNum][0]/1440)*1440;
let arr=this.startTime.value.split(':');
let h=parseInt(arr[0]),m=parseInt(arr[1]);
if(h>=0 && h<24 && m>=0 && m<60)this.objects[this.selectedObjNum][0]=midnight+h*60+m;
this.draw();
}
endTimeChanged(){
let midnight=Math.floor(this.objects[this.selectedObjNum][1]/1440)*1440;
let arr=this.endTime.value.split(':');
let h=parseInt(arr[0]),m=parseInt(arr[1]);
if(h>=0 && h<24 && m>=0 && m<60)this.objects[this.selectedObjNum][1]=midnight+h*60+m;
this.draw();
}
}
