// Converts Time class to milliseconds (UTC) /*function TimeToMSec(dt) { return Date.UTC(dt.adate.year, dt.adate.month-1, dt.adate.day, dt.atime.hh, dt.atime.mm, dt.atime.ss); }*/ /* NOTE: All internal variables for working with dates use either js-native Date type or numeric values (expressed in milliseconds, minutes, etc). UTC is used everywhere. Non-native types used only in server interactions. */ function CreateUTCDate(year, month, day, hour, minute, second) { var date=new Date(); // Не менять!!!! date.setUTCFullYear(year); date.setUTCMonth(0); date.setUTCDate(day); date.setUTCMonth(month); date.setUTCHours(hour); date.setUTCMinutes(minute); date.setUTCSeconds(second); return date; } var CalendarJob = Class.create({ dateID: null, id: null, start: null, duration: null, end: null, title: null, brief: null, keyType: 'int', attrs: {}, initialize: function(params, keyType) { //showObject(params); /* var start=params["Start"]; var sd=start.GetADate(); var st=start.GetATime(); // TODO: handle UTC stuff properly var dStart=CreateUTCDate(sd.year, sd.month-1, sd.day, st.hh, st.mm, st.ss); this.dateID=JobCalendar.makeDateID(dStart); this.start=dStart; // datetime this.duration=params["Duration"].Value(); // in minutes FIXME spike this.end=new Date(this.start.getTime()+this.duration*60*1000); this.title=JobCalendar.fromStr(params["Title"]); this.keyType=keyType; if (keyType=='int') this.id=JobCalendar.fromInt(params["ID"]); else if (keyType=='string') this.id=JobCalendar.fromStr(params["ID"]); else { alert("CalendarJob: Invalid key type: "+keyType); return; }*/ var dStart = params["Start"]; this.dateID=JobCalendar.makeDateID(dStart); this.start=dStart; // datetime this.duration=params["Duration"]; this.end=new Date(this.start.getTime()+this.duration*60*1000); this.title=params["Title"]; this.brief=params["BriefTitle"]; this.keyType=keyType; this.id = params["ID"]; if (params["Devided"] != undefined && params["Devided"]) { this.devided = true; this.position = params["Position"]; this.fullStart = params["FullStart"]; this.fullEnd = params["FullEnd"]; this.idOriginal = params["IDOriginal"]; } else this.devided = false; // assign attributes this.attrs=params["Attributes"] || {}; }, PrintAll: function() { // debug function LogE("--------- job ------"); LogL("dateID : "+this.dateID); LogL("id : "+this.id); LogL("start : "+this.start); LogL("duration: "+this.duration); LogL("end : "+this.end); LogL("=-=-=-=-=-=-=-=-=-=-=-"); }, AddID: function(args) { args["Date"]=JobCalendar.fromDateID(this.dateID); if (this.devided) args["ID"]=this.idOriginal; else args["ID"]=this.id; } }); CalendarJob.SortFunc = (function(a, b) { if (a.start < b.start) return -1; else if (a.start == b.start) { if (a.end < b.end) return -1; return 0; // equal } else return 1; }); var JobCalendar = Class.create({ SECOND_MS: 1000, MINUTE_MS: 60*1000, HOUR_MS: 3600*1000, DAY_MS: 24*3600*1000, WEEK_MS: 7*24*3600*1000, initialize: function(root) { root=$(root); this.id=root.getAttribute('id'); this.root=root; this.root.JSControl=this; this.mode=root.getAttribute('mode'); var kt=root.getAttribute('key-type'); this.keyType='int'; if (kt=='string') this.keyType=kt; this.jobListMethod=root.getAttribute('src'); this.startWeek = parseInt(root.getAttribute('start-week'), 10); if (this.mode == "month") { var ms = root.getAttribute('multi-select'); if (ms == "yes") this.multiselect = "yes"; } if (this.mode == "week" || this.mode == "day") { var tButton = this.my ("_xTooltipButton"); var tbv = root.getAttribute('tooltip'); if (tbv != "yes") tButton.setStyle({display: 'none'}); var tb = root.getAttribute('tooltip-button'); if (tb != undefined && tb != "") tButton.innerHTML = tb; } // Дата календаря var CurrDate = new Date(); this.CurrDate=new Date(CurrDate.getFullYear(), CurrDate.getMonth(), CurrDate.getDate()); this.Selected = Array(); this.ChooseDate = this.CurrDate; //LogE ("this.CurrDate: " + this.CurrDate); this.setCalendarDate (this.CurrDate); this.dayHourHeight=32; this.startHour=parseInt(root.getAttribute('start-hour'), 10); if (isNaN(this.startHour)) this.startHour=9; this.endHour=parseInt(root.getAttribute('end-hour'), 10); if (isNaN(this.endHour)) this.endHour=18; this.startCollapse=parseInt(root.getAttribute('start-collapse'), 10); this.endCollapse=parseInt(root.getAttribute('end-collapse'), 10); if (isNaN(this.startCollapse)) this.startCollapse=this.startHour; if (isNaN(this.endCollapse)) this.endCollapse=this.endHour; this.startHourOrig = this.startHour; this.endHourOrig = this.endHour; this.value=null; this.stopEventCallback=(function(evt){ evt.stop(); return false; }); this.jobDrag={}; // collection of job dragging stuff this.jobMove={}; // collection of job moving stuff // initial height setting var eleCont=this.my('_xContent'); eleCont.setStyle({ height: this.dayHourHeight*(this.endHour-this.startHour)+'px'/*hours*/ }); this.observe('click', '_xAddNew', this.OnAddNew.bind(this)); this.observe('click', '_xZoomIn', this.OnZoomIn.bind(this)); this.observe('click', '_xZoomOut', this.OnZoomOut.bind(this)); this.observe('click', '_xCurDec', this.OnCurDec.bind(this)); this.observe('click', '_xCurInc', this.OnCurInc.bind(this)); this.observe('click', '_xCurWeekDec', this.OnCurWeekDec.bind(this)); this.observe('click', '_xCurWeekInc', this.OnCurWeekInc.bind(this)); this.observe('click', '_xCurMonthDec', this.OnCurMonthDec.bind(this)); this.observe('click', '_xCurMonthInc', this.OnCurMonthInc.bind(this)); this.observe('click', '_xCollapse', this.OnCollapse.bind(this)); this.GetData(); //this.Redraw(); // Подписываемся на событие "Обновить" this.awindow = AControl.GetParentWindow(this.ID()); if (this.awindow != undefined) this.awindow.Subscribe("CALENDAR:REFRESH", this.onrefresh, this); //LogE ("end init" + this.mode); }, setCalendarDate: function (date) { this.CalendarMonth = date.getMonth(); this.CalendarYear = date.getFullYear(); // если на месяц, то берем первое число месяца if (this.mode == "month") { var startMonth=new Date(date.getFullYear(), date.getMonth(), 1); this.startDay = this.calcStartCalendar (startMonth); } else { this.startDay=new Date(date.getFullYear(), date.getMonth(), date.getDate()); // если календарь на неделю и указан день начала недели if (!isNaN(this.startWeek) && this.startWeek >=0 && this.startWeek <=6 && this.mode=="week") { var currDay = this.startDay.getDay(); this.startDay=new Date(this.startDay.getTime()-this.DAY_MS*Math.abs(this.startWeek - currDay)); } } }, onrefresh: function(aevent){ // проверяем, что это нужный нам элемент на странице if(aevent.Emitter().ID()+"_"+aevent.Data().id!=this.ID()) return; LogE ("onrefresh: " + this.mode); var data=aevent.Data(); var args = data.args; var CalendarDate = args["CalendarDate"]; if(CalendarDate != undefined) { var sd=CalendarDate.GetADate(); var dStart=CreateUTCDate(sd.year, sd.month-1, sd.day, 0, 0, 0); var cm = this.CalendarMonth; if (dStart != undefined) this.setCalendarDate(dStart); if (this.mode == 'month' && cm != this.CalendarMonth) { for (var day=0; day<42; ++day) { var eleDay=this.my('_wWeekDay'+day); var myStyles=new Array("AfterClick","AfterDblClick"); for (var q=0; q this.startWeek) delta = dw - this.startWeek; if (dw < this.startWeek) delta = 7 + dw - this.startWeek; return new Date(FirstDayOfMonth.getTime()-this.DAY_MS*delta); }, GetData: function () { //LogE ("GetData: " + this.mode + " " + this.jobListMethod); if (this.jobListMethod == undefined || this.jobListMethod == '') { var data; this.UpdateValue(data); return } var args={}; this.addFromTillToEvent(args); new ServerCall(this.jobListMethod, args, { onSuccess: this.JobListCallback, onException: this.JobListErrorCallback, thisObject: this }); }, observe: function(eventType, idSuffix, callback) { var e=this.my(idSuffix); if (!e) return; e.observe(eventType, callback); }, OnTooltipButton: function(dateID, id) { var job=this.findJob(dateID, id); var evData={}; var evArgs={}; job.AddID(evArgs); this.addFromTillToEvent(evArgs); evData["args"]=evArgs; new AEvent("JC:TOOLTIP-CLICK", evData, this); }, OnCollapse: function() { var CollIcon=this.my('_xCollapse'); if (this.Collapsed == "yes") { this.startHour = this.startHourOrig; this.endHour = this.endHourOrig; CollIcon.removeClassName("JCW_HeaderButtonExpand"); CollIcon.addClassName("JCW_HeaderButtonCollapse"); this.Collapsed = "no"; } else { this.startHour = this.startCollapse; this.endHour = this.endCollapse; CollIcon.removeClassName("JCW_HeaderButtonCollapse"); CollIcon.addClassName("JCW_HeaderButtonExpand"); this.Collapsed = "yes"; } var id=this.ID()+'_xContent'; var sel; if (this.mode=='day') { sel='div.JCD_HourCell'; } else if (this.mode=='week') { sel='div.JCW_HourCell'; } var all = this.endHourOrig - this.startHourOrig; var f = this.startCollapse - this.startHourOrig; var s = (this.endCollapse - this.startCollapse) + f - 1; var k = 0 var hrs=this.root.select(sel); for (var i=0; i s) e.addClassName("HourCellHide"); if (k >= all - 1) k = 0; else k++; } else e.removeClassName("HourCellHide"); } var eleCont=$(id); eleCont.setStyle({ height: this.dayHourHeight*(this.endHour-this.startHour)+'px'/*hours*/ }); this.Redraw(); }, OnZoomIn: function() { var id=this.ID()+'_xContent'; var sel; if (this.mode=='day') { sel='div.JCD_HourCell'; } else if (this.mode=='week') { sel='div.JCW_HourCell'; } if (this.dayHourHeight>=100) return; this.dayHourHeight+=4; var eleCont=$(id); // TODO: cache it eleCont.setStyle({ height: this.dayHourHeight*(this.endHour-this.startHour)+'px'/*hours*/ }); var hrs=this.root.select(sel); for (var i=0; i 11) { this.CalendarMonth = 0; this.CalendarYear ++; } var startMonth=new Date(this.CalendarYear, this.CalendarMonth, 1); this.startDay = this.calcStartCalendar (startMonth); this.GetData(); //this.Redraw(); }, JobListCallback: function(oPacket) { var data=oPacket.Data(); //LogL("JobListCallback!"); //alert ("JobListCallback"); this.UpdateValue(data); }, JobListErrorCallback: function(sMessage, nErrorCode) { LogE("JobListErrorCallback: ERROR"); LogL("message="+sMessage); LogL("code="+nErrorCode); alert("JobListErrorCallback ERROR"); }, CheckJobOnDevide: function (job) { var dStart=job["Start"]; var dDuration=job["Duration"]; var dEnd=new Date(dStart.getTime()+dDuration*60*1000); if (dStart.getFullYear() == dEnd.getFullYear() && dStart.getMonth() == dEnd.getMonth() && dStart.getDate() == dEnd.getDate()) return false; return true; }, DevideJob: function (dStart, dEnd) { var arr = Array(); var dStartUTC = new Date (); if (dStart.getFullYear() == dEnd.getFullYear() && dStart.getMonth() == dEnd.getMonth() && dStart.getDate() == dEnd.getDate()) { // задача умещается в один день var one = Array(); one["Start"] = dStart; one["Duration"] = (dEnd.getTime() - dStart.getTime()) / this.MINUTE_MS; arr.push (one); } else { // разбиваем var dDuration = (24 * 60) - dStart.getHours() * 60 - dStart.getMinutes(); var one = Array(); one["Start"] = dStart; one["Duration"] = dDuration - 1; arr.push (one); // dStart = new Date(dStart.getTime() + dDuration * this.MINUTE_MS); var next = this.DevideJob (dStart, dEnd); for (var i = 0; i < next.length; i++) arr.push (next[i]); } return arr; }, DecodeJob: function (job) { var newJob = Array(); newJob["Attributes"] = job["Attributes"]; if (job["Duration"].oValue_ != undefined) newJob["Duration"] = job["Duration"].oValue_.value_; else if (job["Duration"].value_ != undefined) newJob["Duration"] = job["Duration"].value_; if (this.keyType=='int') newJob["ID"] = JobCalendar.fromInt(job["ID"]); else newJob["ID"] = JobCalendar.fromStr(job["ID"]); var start=job["Start"]; var sd=start.GetADate(); var st=start.GetATime(); newJob["Start"] = CreateUTCDate(sd.year, sd.month-1, sd.day, st.hh, st.mm, st.ss); newJob["Title"] = JobCalendar.fromStr(job["Title"]); newJob["BriefTitle"] = JobCalendar.fromStr(job["BriefTitle"]); return newJob; }, UpdateValue: function(data) { //alert ("UpdateValue: " + data); //LogE("UpdateValue"); if (this.mode == 'day' || this.mode == 'week') { this.jobs=[]; if (data && data instanceof Array) { for (var k=0; k= br.left && newX <= br.right) s = wd; } } var eleContent=this.my('_xContent'); var br = eleContent.getBoundingClientRect(); // милисекунд всего var msec = (this.endHour - this.startHour) * this.HOUR_MS; var pixAll = br.bottom - br.top; var pix = newY - br.top; var my_msec = (msec * pix) / pixAll + this.startHour * this.HOUR_MS; // subtract current day s=new Date(s.getTime()-(s.getHours()*this.HOUR_MS+s.getMinutes()*this.MINUTE_MS+s.getSeconds()*this.SECOND_MS)+my_msec); // no redraw here, only event throwing var evData={}; var evArgs={}; evArgs["Date"]=new Time(new ADate(s.getUTCFullYear(), s.getUTCMonth()+1, s.getUTCDate()), new ATime(s.getUTCHours(), s.getUTCMinutes(), s.getUTCSeconds())); s.setHours(0,0,0); // возвращаем evData["args"]=evArgs; new AEvent("JC:NEW-ITEM", evData, this); }, OnJobDblClick: function(dateID, id) { var job=this.findJob(dateID, id); var evData={}; var evArgs={}; job.AddID(evArgs); this.addFromTillToEvent(evArgs); evData["args"]=evArgs; new AEvent("JC:JOB-DBL-CLICK", evData, this); }, OnMonthDayDblClick: function(date, count) { LogE ("OnMonthDayDblClick " + date); var evData={}; var evArgs={}; evArgs["Date"]=new Time(new ADate(date.getFullYear(), date.getMonth()+1, date.getDate()), new ATime(date.getHours(), date.getMinutes(), date.getSeconds())); // Красим! for (var day=0; day<42; ++day) { var eleDay=this.my('_wWeekDay'+day); eleDay.removeClassName('AfterDblClick'); eleDay.removeClassName('AfterClick'); if (day == count) eleDay.addClassName('AfterDblClick'); } evData["args"]=evArgs; new AEvent("JC:DAY-DBL-CLICK", evData, this); }, OnMonthDayClick: function(date, count, evt) { LogE ("OnMonthDayClick " + date + " - " + count); var evData={}; var evArgs={}; this.ChooseDate = new ADate(date.getFullYear(), date.getMonth()+1, date.getDate()); var CDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); evArgs["Date"]=new Time(new ADate(date.getFullYear(), date.getMonth()+1, date.getDate()), new ATime(date.getHours(), date.getMinutes(), date.getSeconds())); if (this.multiselect == undefined) this.Selected.clear(); // Ищем в Selected var exists = -1; for (var i = 0; i < this.Selected.length; i++) { if (CDate.getTime() == this.Selected[i].getTime()) exists = i; } if (exists < 0) this.Selected.push (CDate); else this.Selected.splice (exists, 1); // Красим! for (var day=0; day<42; ++day) { var eleDay=this.my('_wWeekDay'+day); eleDay.removeClassName('AfterDblClick'); if (this.multiselect == undefined) eleDay.removeClassName('AfterClick'); if (day == count) { if (exists < 0) eleDay.addClassName('AfterClick'); else eleDay.removeClassName('AfterClick'); } } evt.stop(); // отправляем событие evData["args"]=evArgs; new AEvent("JC:DAY-CLICK", evData, this); }, Redraw: function() { LogE ("Redraw" + this.mode); if (this.mode == 'day' || this.mode == 'week') { var curJobs=[]; if (this.jobs != undefined) { for (var ji=0; ji= endDay) return false; if (job.end <= this.startDay) return false; // Проверка времени var hourE = job.end.getHours(); var hourS = job.start.getHours(); if (hourE <= this.startHour) return false; if (hourS >= this.endHour) return false; return true; }, pad2: function(v) { if (v<10) return '0'+v; return v.toString(); }, toDate: function(d) { return d.getFullYear()+'-'+this.pad2(d.getMonth()+1)+'-'+this.pad2(d.getDate()); }, toReadableDate: function(d) { return this.pad2(d.getDate()) + ' ' + this.titleMonth(d.getMonth()) + ' ' + d.getFullYear(); }, toBriefDate: function(d) { return this.pad2(d.getDate()) + ' ' + this.shortMonth(d.getMonth()); }, toPeriodDate: function(d1, d2) { if (d1.getMonth() == d2.getMonth() && d1.getFullYear() == d2.getFullYear()) return this.pad2(d1.getDate()) + '-' + this.pad2(d2.getDate()) + ' ' + this.titleMonth(d1.getMonth()) + ' ' + d1.getFullYear(); return this.toReadableDate(d1) + ' - ' + this.toReadableDate(d2); }, toMonthDate: function(d1, d2) { return this.titleMonth(d1) + ' ' + d2; }, compareDates: function (d1, d2) { if (d1.getFullYear() == d2.getFullYear() && d1.getMonth() == d2.getMonth() && d1.getDay() == d2.getDay() && d1.getHours() == d2.getHours() && d1.getMinutes() == d2.getMinutes()) return true; return false; }, calculateDailyLayout: function(jobs) { //LogE("BEFORE SORT"); //for (var i=0; i 0) { var blockAdd=[]; for (var i=0; i size containing latest jobs indices by column var block=blocks[bi]; // iterate current block for (var cbi=0; cbi= headJob.end) { // no overlapping bPlaceFound=true; head[hi]=ji; //update head job.layoutColumn=hi; //LogL("new head item at position "+hi+": "+job.id); //printArray("new head now: ", jobs, head); break; } // job.start < headJob.end here (overlapping occurs) //LogL("job "+job.id+" overlaps with job "+headJob.id); } if (!bPlaceFound) { job.layoutColumn=cols; cols++; head.push(ji); //printArray("Place not found, head extended to ", jobs, head); } } } // block loop // store block column count blockColumnCount.push(cols); } // everything is calculated for (var bi=0; bi jd.maxDelta) { jobHeightDelta=jd.maxDelta; //LogE("height corr +"); } //LogE("hd.up="+jobHeightDelta); var job=this.findJob(jd.dateID, jd.id); // new in-day length in MS var inDayLength=this.startHour*this.HOUR_MS+ (this.endHour-this.startHour)*this.HOUR_MS*(jd.top+jd.h+jobHeightDelta)/jd.parentH; var e=job.end; // subtract current day var ne=new Date(e.getTime()- (e.getHours()*this.HOUR_MS+e.getMinutes()*this.MINUTE_MS+e.getSeconds()*this.SECOND_MS)+ inDayLength); job.end=ne; jd.started=false; var eleJob=this.my('_xJob-'+jd.id); eleJob.removeClassName('JCD_Dragged'); // Возвращаем задачу где росла // Чтобы в случае ошибки все было по старому eleJob.setStyle({height: (jd.startH)+'px'}); // no redraw here, only event throwing var evData={}; var evArgs={}; job.AddID(evArgs); evArgs["End"]=new Time(new ADate(ne.getUTCFullYear(), ne.getUTCMonth()+1, ne.getUTCDate()), new ATime(ne.getUTCHours(), ne.getUTCMinutes(), ne.getUTCSeconds())); this.addFromTillToEvent(evArgs); evData["args"]=evArgs; new AEvent("JC:JOB-DRAG", evData, this); } $(document).stopObserving('mousemove', this.mouseMoveHandler); $(document).stopObserving('mouseup', this.mouseUpHandler); $(document).stopObserving('dragstart', this.stopEventCallback); $(document).stopObserving('selectstart', this.stopEventCallback); $(document).stopObserving('contextmenu', this.stopEventCallback); this.mouseMoveHandler=null; this.mouseUpHandler=null; evt.stop(); }, OnJobMoveMouseDown: function(dateID, id, evt) { if (!evt.isLeftClick()) return; // FIXME dragging and moving cannot work at once var jm=this.jobMove; if (!jm.started) { this.mouseUpHandler=this.OnJobMoveMouseUp.bind(this); this.mouseMoveHandler=this.OnJobMoveMouseMove.bind(this); this.jobDblClickHandler=this.OnJobDblClick.bind(this, dateID, id); // regular dblclick on the job's div will not occur (will be forged by document's mousedown/mouseup handlers) $(document).observe('dblclick', this.jobDblClickHandler); $(document).observe('mouseup', this.mouseUpHandler); $(document).observe('mousemove', this.mouseMoveHandler); $(document).observe('dragstart', this.stopEventCallback); $(document).observe('selectstart', this.stopEventCallback); $(document).observe('contextmenu', this.stopEventCallback); //LogL('pointer='+evt.pointerY()); jm.Y=evt.pointerY(); var eleJob=this.my('_xJob-'+id); eleJob.addClassName('JCD_Dragged'); var h=eleJob.offsetHeight; var top=eleJob.offsetTop; jm.h=h; jm.parentNode = eleJob.parentNode; jm.parentH=eleJob.parentNode.offsetHeight; jm.top=top; jm.startTop = top; jm.minDelta=-top; jm.maxDelta=jm.parentH/*-h*/-top-2; // same as in dragging jm.dateID=dateID; jm.id=id; //LogL("h="+h+", top="+top+", parentH="+jm.parentH); // create ruler var eleRuler=new Element('div', {"class": 'JCD_MoveRuler'}); eleRuler.setStyle({top: top+'px'}); var eleCont=this.my('_xContent'); eleCont.appendChild(eleRuler); jm.ruler=eleRuler; // --- end of ruler jm.started=true; this.stopMouseDown(evt); } }, OnJobMoveMouseMove: function(evt) { var newY=evt.pointerY(); var newX=evt.pointerX(); var jm=this.jobMove; if (jm.started) { var eleJob=this.my('_xJob-'+jm.id); // Движение по вертикали var jobTopDelta=newY-jm.Y; if (jobTopDelta >= jm.minDelta && jobTopDelta <= jm.maxDelta) { //LogE("td="+jobTopDelta); var newTop=jm.top+jobTopDelta; eleJob.setStyle({top: newTop+'px'}); eleJob.setStyle({height: jm.h +'px'}); if (jm.parentH < jm.h + newTop) eleJob.setStyle({height: (jm.parentH - newTop) +'px'}); jm.ruler.setStyle({top: newTop+'px'}); } // движение по горизонтали if (this.mode == 'week') { for (var k = 0; k < 7; k++) // FIXME /*если не 7 дней*/ { var eleDay=this.my('_wDay' + k); var br = eleDay.getBoundingClientRect(); if (newX >= br.left && newX <= br.right) { eleJob.setStyle({left: 0+'%'}); eleJob.setStyle({right: 0+'%'}); eleDay.appendChild(eleJob); break; } } } } evt.stop(); }, OnJobMoveMouseUp: function(evt) { if (!evt.isLeftClick()) return; var jm=this.jobMove; if (jm.started) { var newY=evt.pointerY(); var newX=evt.pointerX(); var jobTopDelta=newY-jm.Y; // fit to constraints if (jobTopDelta < jm.minDelta) { //LogE("top corr -"); jobTopDelta=jm.minDelta; } if (jobTopDelta > jm.maxDelta) { jobTopDelta=jm.maxDelta; //LogE("top corr +"); } if (jobTopDelta!=0) { //LogE("td.up="+jobTopDelta); var job=this.findJob(jm.dateID, jm.id); // new start in MS var inDayStart=this.startHour*this.HOUR_MS+ (this.endHour-this.startHour)*this.HOUR_MS*(jm.top+jobTopDelta)/jm.parentH; var inDayEnd=this.startHour*this.HOUR_MS+ (this.endHour-this.startHour)*this.HOUR_MS*(jm.top+jobTopDelta+jm.h)/jm.parentH; var s=job.start; // Берем новую дату if (this.mode == 'week') { for (var day = 0; day < 7; day++) //FIXME если не 7 дней { var wd=new Date(this.startDay.getTime()+this.DAY_MS*day); var eleDay=this.my('_wDay' + day); var br = eleDay.getBoundingClientRect(); if (newX >= br.left && newX <= br.right) s = wd; } } // subtract current day s=new Date(s.getTime()- (s.getHours()*this.HOUR_MS+s.getMinutes()*this.MINUTE_MS+s.getSeconds()*this.SECOND_MS)+ inDayStart); var e=job.end; // subtract current day job.end=new Date(e.getTime()- (e.getHours()*this.HOUR_MS+e.getMinutes()*this.MINUTE_MS+e.getSeconds()*this.SECOND_MS)+ inDayEnd); // Возвращаем задачу где росла // Чтобы в случае ошибки все было по старому var eleJob=this.my('_xJob-'+jm.id); eleJob.setStyle({top: (jm.startTop)+'px'}); var eleDay = jm.parentNode; eleDay.appendChild(eleJob); // no redraw here, only event throwing var evData={}; var evArgs={}; job.AddID(evArgs); evArgs["Start"]=new Time(new ADate(s.getUTCFullYear(), s.getUTCMonth()+1, s.getUTCDate()), new ATime(s.getUTCHours(), s.getUTCMinutes(), s.getUTCSeconds())); evData["args"]=evArgs; new AEvent("JC:JOB-MOVE", evData, this); } var eleJob=this.my('_xJob-'+jm.id); eleJob.removeClassName('JCD_Dragged'); jm.ruler.remove(); jm.started=false; } $(document).stopObserving('dblclick', this.jobDblClickHandler); $(document).stopObserving('mousemove', this.mouseMoveHandler); $(document).stopObserving('mouseup', this.mouseUpHandler); $(document).stopObserving('dragstart', this.stopEventCallback); $(document).stopObserving('selectstart', this.stopEventCallback); $(document).stopObserving('contextmenu', this.stopEventCallback); this.jobDblClickHandler=null; this.mouseMoveHandler=null; this.mouseUpHandler=null; evt.stop(); }, dayOfWeek: function(day) { switch (day) { case 0: return '`Mon`'; case 1: return '`Tue`'; case 2: return '`Wed`'; case 3: return '`Thu`'; case 4: return '`Fri`'; case 5: return '`Sat`'; case 6: return '`Sun`'; } return 'UNK'; }, shortMonth: function(month) { switch (month) { case 0: return '`Jan`'; case 1: return '`Feb`'; case 2: return '`Mar`'; case 3: return '`Apr`'; case 4: return '`May`'; case 5: return '`Jun`'; case 6: return '`Jul`'; case 7: return '`Aug`'; case 8: return '`Sep`'; case 9: return '`Oct`'; case 10: return '`Nov`'; case 11: return '`Dec`'; } return 'UNK'; }, titleMonth: function(month) { switch (month) { case 0: return '`January`'; case 1: return '`February`'; case 2: return '`March`'; case 3: return '`April`'; case 4: return '`May`'; case 5: return '`June`'; case 6: return '`July`'; case 7: return '`August`'; case 8: return '`September`'; case 9: return '`October`'; case 10: return '`November`'; case 11: return '`December`'; } return 'UNK'; }, drawJobsWeek: function(jobs) { var endDay=new Date(this.startDay.getTime() + this.WEEK_MS-1); // TODO: common code, changed only when startDay is changed var eCur=this.my('_xCurrent'); eCur.innerHTML=this.toPeriodDate (this.startDay, endDay);//this.toDate(this.startDay)+' — '+this.toDate(endDay); // update day labels for (var day=0; day<7; ++day) { var eleDH=this.my('_wDayHeader'+day); var wd=new Date(this.startDay.getTime()+this.DAY_MS*day); var d=(wd.getDay()+6) % 7; eleDH.innerHTML=this.dayOfWeek(d) + ', ' + this.toBriefDate(wd); if (d>4) eleDH.addClassName('Weekend'); else eleDH.removeClassName('Weekend'); } // ---------------------------------------------- var eleCont=this.my('_xContent'); var aeleJobs=eleCont.select('div.JCD_Job'); for (var i=0; i4) eleDay.addClassName('Weekend'); else eleDay.removeClassName('Weekend'); // ------------------------------------------------------ var eleJobTemplate=this.root.down('div.JCD_Templates').down('div.JCD_Job').innerHTML; for (var ji=0; ji < curJobs.length; ++ji) { var job=curJobs[ji]; var st=job.start; var end=job.end; // FIXME: will behave bad if event spans to non-working hours or ever another day var inDayStart=st.getHours()*this.HOUR_MS+ st.getMinutes()*this.MINUTE_MS+ st.getSeconds()*this.SECOND_MS; var inDayEnd=job.end.getHours()*this.HOUR_MS+ end.getMinutes()*this.MINUTE_MS+ end.getSeconds()*this.SECOND_MS; var pDayStart=(inDayStart-this.startHour*this.HOUR_MS)/((this.endHour-this.startHour)*this.HOUR_MS)*100; var pDayEnd=(this.endHour*this.HOUR_MS-inDayEnd)/((this.endHour-this.startHour)*this.HOUR_MS)*100; var JobLonger = false; var DevidedTop = false; var DevidedBottom = false; if (pDayEnd < 0) { pDayEnd = 0; JobLonger = true; DevidedTop = true; } if (pDayStart < 0) { pDayStart = 0; DevidedBottom = true; } var pLeft=job.layoutLeft; // % var pRight=job.layoutRight; // % var attrs=job.attrs; var eleJob=new Element('div', {"class": 'JCD_Job', "id": this.ID()+'_xJob-'+job.id}); var style={ top: pDayStart+'%', bottom: pDayEnd+'%', left: pLeft+'%', right: (100-pRight)+'%' }; eleJob.setStyle(style); eleJob.innerHTML=eleJobTemplate; // insert template if (attrs["Style"]) eleJob.addClassName(attrs["Style"]); var startTime, endTime; var devPosition = "full"; if (!job.devided) { startTime=this.pad2(st.getHours())+':'+this.pad2(st.getMinutes()); endTime=this.pad2(end.getHours())+':'+this.pad2(end.getMinutes()); } else { var stOrig=job.fullStart; var endOrig=job.fullEnd; devPosition = job.position; startTime=this.pad2(stOrig.getHours())+':'+this.pad2(stOrig.getMinutes()); endTime=this.pad2(endOrig.getHours())+':'+this.pad2(endOrig.getMinutes()); } eleJob.down('div.JCD_JobTime').innerHTML=startTime + " - " + endTime; if (job.brief != undefined) eleJob.down('div.JCD_JobTime').innerHTML += "
" + job.brief; eleJob.down('div.JCD_JobTitle').innerHTML=AXML.EscapeEntities(job.title); eleDay.appendChild(eleJob); eleJob.observe('dblclick', this.OnJobDblClick.bind(this, job.dateID, job.id)); if (job.attrs["Movable"] != "no" && (!job.devided || devPosition == "head")) { eleJob.observe('mousedown', this.OnJobMoveMouseDown.bind(this, job.dateID, job.id)); } var eleDrag=eleJob.down('div.JCD_Drag'); // hide dragger if (job.attrs["Draggable"] == "no" || JobLonger == true || devPosition == "head" || devPosition == "middle") { eleDrag.setStyle({display: 'none'}); } else { eleDrag.observe('mousedown', this.OnJobDragMouseDown.bind(this, job.dateID, job.id)); } // дополнительные стили if (job.devided) eleJob.addClassName(devPosition + "Style"); if (DevidedTop) eleJob.addClassName("headStyle"); if (DevidedBottom) eleJob.addClassName("tailStyle"); // Кнопка var Tooltip=eleJob.down('div.JCW_Tooltip'); var button = Tooltip.down('button'); button.observe('click', this.OnTooltipButton.bind(this, job.dateID, job.id)); } } }, drawJobsMonth: function(days) { var eCur=this.my('_xCurrent'); eCur.innerHTML=this.toMonthDate (this.CalendarMonth, this.CalendarYear); // update day labels for (var day=0; day<7; ++day) { var eleDH=this.my('_wDayHeader'+day); var wd=new Date(this.startDay.getTime()+this.DAY_MS*day); var d=(wd.getDay()+6) % 7; eleDH.innerHTML=this.dayOfWeek(d); if (d>4) eleDH.addClassName('Weekend'); else eleDH.removeClassName('Weekend'); } for (var day=0; day<42; ++day) { /*FIXME 7*6 = 42*/ var eleDay=this.my('_wWeekDay'+day); eleDay.stopObserving('dblclick'); eleDay.stopObserving('click'); //Костыль! //Прибавляем еще 2 часа для вменяемого перехода на летнее-зимнее время var wd=new Date(this.startDay.getTime()+this.DAY_MS*day + this.HOUR_MS*2); var wdUTC=CreateUTCDate(wd.getFullYear(), wd.getMonth(), wd.getDate(), 0, 0, 0); // убираем все предыдущее стили var myStyles=new Array("NotMonth","Weekend","CurrDate", "Green", "Red", "Blue", "Marked", "low", "medium", "high", "critical", "AfterClick", "AfterDblClick"); for (var q=0; q