1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 ZmCalColView = function(parent, posStyle, controller, dropTgt, view, numDays, scheduleMode, readonly, isInviteMessage, isRight) { 25 if (arguments.length == 0) { return; } 26 27 view = view || ZmId.VIEW_CAL_DAY; 28 // set before call to parent 29 this._scheduleMode = scheduleMode; 30 var workingHours = ZmCalBaseView.parseWorkingHours(ZmCalBaseView.getWorkingHours()); 31 if (!numDays && view === ZmId.VIEW_CAL_WORK_WEEK) { 32 // Edge Case: Work week is selected but all the days are configured as non working days, 33 // Fall back to week view by faking all the days are working days with same start and end time 34 for (var i=0; i<workingHours.length; i++) { 35 if (!workingHours[i].isWorkingDay) { 36 workingHours[i].isWorkingDay = true; 37 } 38 } 39 numDays = 7; 40 var msgDlg = appCtxt.getMsgDialog(); 41 msgDlg.setMessage(ZmMsg.emptyWorkingHoursWarning, DwtMessageDialog.WARNING_STYLE); 42 msgDlg.popup(); 43 var listener = msgDlg.popdown.bind(msgDlg); 44 msgDlg.setButtonListener(DwtDialog.OK_BUTTON, listener); 45 } 46 this.workingHours = workingHours; 47 48 this.numDays = numDays || 1; 49 this._daySepWidth = 2; // width of separator between days 50 this._columns = []; 51 this._layoutMap = []; 52 this._unionBusyDivIds = []; // div ids for layingout union 53 this._fbBarEnabled = this.fbStatusBarEnabled(); 54 55 //we need special alignment for this case. 56 this._isInviteMessage = isInviteMessage; 57 this._isRight = isRight; 58 59 ZmCalBaseView.call(this, parent, "calendar_view", posStyle, controller, view, readonly); 60 var element = this.getHtmlElement(); 61 // clear the onClick event handler. Otherwise accessibility code will 62 // generate spurious mouse up/down events 63 this._setEventHdlrs([DwtEvent.ONCLICK], true, element); 64 65 this.setDropTarget(dropTgt); 66 this.setScrollStyle(DwtControl.CLIP); 67 this._needFirstLayout = true; 68 69 this._isValidIndicatorDuration = true; 70 }; 71 72 ZmCalColView.prototype = new ZmCalBaseView; 73 ZmCalColView.prototype.constructor = ZmCalColView; 74 75 ZmCalColView.DRAG_THRESHOLD = 4; 76 77 // min width before we'll turn on horizontal scrollbars 78 ZmCalColView.MIN_COLUMN_WIDTH = 120; 79 // max number of all day appts before we turn on vertical scrollbars 80 ZmCalColView.MAX_ALLDAY_APPTS = 4; 81 82 ZmCalColView.HALF_HOUR_HEIGHT = 21; 83 84 ZmCalColView._OPACITY_APPT_NORMAL = 100; 85 ZmCalColView._OPACITY_APPT_DECLINED = 20; 86 ZmCalColView._OPACITY_APPT_TENTATIVE = 60; 87 ZmCalColView._OPACITY_APPT_DND = 70; 88 89 ZmCalColView._OPACITY_APPT_FREE = 40; 90 ZmCalColView._OPACITY_APPT_BUSY = 100; 91 ZmCalColView._OPACITY_APPT_TENTATIVE = 60; 92 93 ZmCalColView._HOURS_DIV_WIDTH = parseInt(ZmMsg.COLUMN_WIDTH_HOURS); // width of div holding hours text (1:00am, etc); defaults to 55 94 ZmCalColView._UNION_DIV_WIDTH = 40; // width of div holding union in sched view 95 ZmCalColView._FBBAR_DIV_WIDTH = 10; 96 97 ZmCalColView._ALL_DAY_SEP_HEIGHT = 5; // height of separator between all day appts and body 98 99 ZmCalColView._SCROLL_PRESSURE_FUDGE = 10; // pixels for scroll pressure around top/bottom 100 101 ZmCalColView._DAY_HEADING_HEIGHT = 20; 102 ZmCalColView._ALL_DAY_APPT_HEIGHT = 20; 103 ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD = 3; // space between all day appt rows 104 ZmCalColView._APPT_X_FUDGE = 0; // due to border stuff 105 ZmCalColView._APPT_Y_FUDGE = -1; // ditto 106 ZmCalColView._APPT_WIDTH_FUDGE = (AjxEnv.isIE ? 0 : 0); // due to border stuff 107 ZmCalColView._APPT_HEIGHT_FUDGE = (AjxEnv.isIE ? 0 : 0); // ditto 108 109 ZmCalColView._HOUR_HEIGHT = 42; 110 ZmCalColView._HALF_HOUR_HEIGHT = ZmCalColView._HOUR_HEIGHT/2; 111 ZmCalColView._15_MINUTE_HEIGHT = ZmCalColView._HOUR_HEIGHT/4; 112 ZmCalColView._DAY_HEIGHT = ZmCalColView._HOUR_HEIGHT*24; 113 114 ZmCalColView._STATUS_FREE = "F"; 115 ZmCalColView._STATUS_TENTATIVE = "T"; 116 ZmCalColView._STATUS_BUSY = "B"; 117 ZmCalColView._STATUS_OOO = "O"; 118 119 ZmCalColView.prototype.toString = 120 function() { 121 return "ZmCalColView"; 122 }; 123 124 ZmCalColView.prototype.fbStatusBarEnabled = 125 function(){ 126 return false; 127 }; 128 129 ZmCalColView.prototype.getRollField = 130 function() { 131 switch(this.view) { 132 case ZmId.VIEW_CAL_WORK_WEEK: 133 case ZmId.VIEW_CAL_WEEK: 134 return AjxDateUtil.WEEK; 135 break; 136 case ZmId.VIEW_CAL_DAY: 137 default: 138 return AjxDateUtil.DAY; 139 break; 140 } 141 }; 142 143 ZmCalColView.prototype.dragSelect = 144 function(div) { 145 // do nothing 146 }; 147 148 ZmCalColView.prototype.dragDeselect = 149 function(div) { 150 // do nothing 151 }; 152 153 ZmCalColView.prototype.setIsRight = 154 function(isRight) { 155 this._isRight = isRight; 156 }; 157 158 ZmCalColView.prototype._dateUpdate = 159 function(rangeChanged) { 160 this._selectDay(this._date); 161 this._clearSelectedTime(); 162 this._updateSelectedTime(); 163 }; 164 165 ZmCalColView.prototype._selectDay = 166 function(date) { 167 if (this._numDays == 1 || this._scheduleMode) return; 168 var day = this._getDayForDate(date); 169 if (day != null) { 170 var col = this._columns[day.index]; 171 if (this._selectedDay) { 172 var te = document.getElementById(this._selectedCol.titleId); 173 te.className = this._selectedDay.isToday ? 'calendar_heading_day_today' : 'calendar_heading_day'; 174 } 175 this._selectedDay = day; 176 this._selectedCol = col; 177 var te = document.getElementById(col.titleId); 178 te.className = day.isToday ? 'calendar_heading_day_today-selected' : 'calendar_heading_day-selected'; 179 } 180 }; 181 182 ZmCalColView.prototype._clearSelectedTime = 183 function() { 184 var e = document.getElementById(this._timeSelectionDivId); 185 if (e) Dwt.setVisible(e, false); 186 }; 187 188 ZmCalColView.prototype._updateSelectedTime = 189 function() { 190 var t = this._date.getTime(); 191 if (t < this._timeRangeStart || t >= this._timeRangeEnd) 192 return; 193 194 var e = document.getElementById(this._timeSelectionDivId); 195 if (!e) return; 196 197 var bounds = this._getBoundsForDate(this._date, AjxDateUtil.MSEC_PER_HALF_HOUR); 198 if (bounds == null) return; 199 var snap = this._snapXY(bounds.x, bounds.y, 30); 200 if (snap == null) return; 201 202 Dwt.setLocation(e, snap.x, snap.y); 203 Dwt.setSize(e, bounds.width, bounds.height); 204 Dwt.setOpacity(e, 40); 205 Dwt.setVisible(e, true); 206 }; 207 208 ZmCalColView.prototype._removeNode = 209 function(id) { 210 var node = document.getElementById(id); 211 if (node) node.parentNode.removeChild(node); 212 }; 213 214 ZmCalColView.prototype._updateUnionDataHash = 215 function(index, folderId) { 216 var hash = this._unionBusyData[index]; 217 if (!hash) hash = this._unionBusyData[index] = {}; 218 hash[folderId] = 1; 219 }; 220 221 ZmCalColView.prototype._updateUnionData = 222 function(appt) { 223 if (appt.isAllDayEvent()) { 224 this._updateUnionDataHash(48, appt.folderId); 225 } else { 226 var em = appt.endDate.getMinutes(); 227 var eh = appt.endDate.getHours(); 228 var startIndex = (appt.startDate.getHours()*2) + (appt.startDate.getMinutes() < 30 ? 0 : 1); 229 var endIndex = ((eh ? eh : 24) *2) + (em == 0 ? 0 : (em <= 30 ? 1 : 2)); 230 if (startIndex == endIndex) endIndex++; 231 for (var i=startIndex; i < endIndex; i++) { 232 this._updateUnionDataHash(i, appt.folderId); 233 } 234 } 235 }; 236 237 ZmCalColView.prototype.addAppt = 238 function(appt) { 239 ZmCalBaseView.prototype.addAppt.call(this, appt); 240 if (this._scheduleMode) { 241 this._updateUnionData(appt); 242 } 243 }; 244 245 ZmCalColView.prototype._resetCalendarData = 246 function() { 247 // TODO: optimize: if calendar list is same, skip! 248 249 // remove existing 250 // TODO: optimize, add/remove depending on new calendar length 251 if (this._numCalendars > 0) { 252 for (var i = 0; i < this._numCalendars; i++) { 253 var col = this._columns[i]; 254 this._removeNode(col.titleId); 255 this._removeNode(col.headingDaySepDivId); 256 this._removeNode(col.daySepDivId); 257 } 258 } 259 260 this._calendars = this._controller.getCheckedCalendars(); 261 this._calendars.sort(ZmFolder.sortCompareNonMail); 262 this._folderIdToColIndex = {}; 263 this._columns = []; 264 this._numCalendars = this._calendars.length; 265 266 this._layoutMap = []; 267 this._unionBusyData = []; // 0-47, one slot per half hour, 48 all day 268 this._unionBusyDataToolTip = []; // tool tips 269 270 var titleParentEl = document.getElementById(this._allDayHeadingDivId); 271 var headingParentEl = document.getElementById(this._allDayScrollDivId); 272 var dayParentEl = document.getElementById(this._apptBodyDivId); 273 274 for (var i = 0; i < this._numCalendars; i++) { 275 var col = this._columns[i] = { 276 index: i, 277 dayIndex: 0, 278 cal: this._calendars[i], 279 titleId: Dwt.getNextId(), 280 headingDaySepDivId: Dwt.getNextId(), 281 daySepDivId: Dwt.getNextId(), 282 workingHrsFirstDivId: Dwt.getNextId(), 283 workingHrsSecondDivId: Dwt.getNextId(), 284 apptX: 0, // computed in layout 285 apptWidth: 0, // computed in layout 286 allDayX: 0, // computed in layout 287 allDayWidth: 0 // computed in layout 288 }; 289 var cal = this._calendars[i]; 290 this._folderIdToColIndex[cal.id] = col; 291 if (cal.isRemote() && cal.rid && cal.zid) { 292 this._folderIdToColIndex[cal.zid + ":" + cal.rid] = col; 293 } 294 295 var div = document.createElement("div"); 296 div.style.position = 'absolute'; 297 div.className = "calendar_heading_day"; 298 div.id = col.titleId; 299 var calName = AjxStringUtil.htmlEncode(cal.getName()); 300 if (appCtxt.multiAccounts) { 301 var acct = cal.getAccount(); 302 div.innerHTML = [ 303 "<center><table border=0><tr><td>", 304 calName, 305 "</td><td>[", 306 "<td>", 307 AjxImg.getImageSpanHtml(acct.getIcon(), "width:18px"), 308 "</td><td>", 309 AjxStringUtil.htmlEncode(acct.getDisplayName()), 310 "]</td></tr></table></center>" 311 ].join(""); 312 } else { 313 div.innerHTML = calName; 314 } 315 if(titleParentEl) { 316 titleParentEl.appendChild(div); 317 } 318 div = document.createElement("div"); 319 div.className = "calendar_day_separator"; 320 div.style.position = 'absolute'; 321 div.id = col.headingDaySepDivId; 322 if(headingParentEl) { 323 headingParentEl.appendChild(div); 324 } 325 326 div = document.createElement("div"); 327 div.className = "calendar_day_separator"; 328 div.style.position = 'absolute'; 329 div.id = col.daySepDivId; 330 if(dayParentEl) { 331 dayParentEl.appendChild(div); 332 } 333 } 334 }; 335 336 ZmCalColView.prototype._preSet = 337 function() { 338 if (this._scheduleMode) { 339 this._resetCalendarData(); // cal must be first 340 } 341 this._layoutMap = []; 342 this._resetAllDayData(); 343 }; 344 345 ZmCalColView.prototype._postSet = 346 function() { 347 this._computeApptLayout(); 348 this._computeAllDayApptLayout(); 349 if (!this._needFirstLayout) { 350 this._layoutAppts(); 351 } 352 this._layout(); 353 this._scrollToTime(8); 354 355 if(this._list && this._list.size() > 0) { 356 AjxDebug.println(AjxDebug.CALENDAR, " ---------------- ZmCalColView::set - calendar is blank"); 357 AjxDebug.println(AjxDebug.CALENDAR, " list size :" + this._list.size()); 358 } 359 360 if(this._fbBarEnabled){ 361 this._layoutFBBar(); 362 } 363 364 this._checkForOffscreenAppt(); 365 Dwt.setLoadedTime("ZmCalItemView"); 366 }; 367 368 ZmCalColView._inSyncScroll = false; 369 370 ZmCalColView.prototype._syncScroll = 371 function(resetLeft) { 372 if (ZmCalColView._inSyncScroll) { return; } 373 374 ZmCalColView._inSyncScroll = true; 375 try { 376 var bodyElement = document.getElementById(this._bodyDivId), 377 hourElement = document.getElementById(this._hoursScrollDivId), 378 alldayElement = document.getElementById(this._allDayScrollDivId), 379 unionGridScrollElement = document.getElementById(this._unionGridScrollDivId), 380 alldayApptElement = document.getElementById(this._allDayApptScrollDivId); 381 382 hourElement.scrollTop = bodyElement.scrollTop; 383 hourElement.scrollLeft = bodyElement.scrollLeft; 384 if (resetLeft) bodyElement.scrollLeft = 0; 385 alldayElement.scrollLeft = bodyElement.scrollLeft; 386 alldayApptElement.scrollLeft = bodyElement.scrollLeft; 387 if (unionGridScrollElement) unionGridScrollElement.scrollTop = bodyElement.scrollTop; 388 this._checkForOffscreenAppt(bodyElement); 389 } catch (ex) { 390 ZmController.handleScriptError(ex, true); 391 } finally { 392 ZmCalColView._inSyncScroll = false; 393 } 394 }; 395 396 ZmCalColView.prototype._horizontalScrollbar = 397 function(enable) { 398 var bodyElement = document.getElementById(this._bodyDivId); 399 bodyElement.className = enable ? "calendar_body_hscroll" : "calendar_body"; 400 if (enable != this._horzEnabled) { 401 this._horzEnabled = enable; 402 this._syncScroll(true); 403 } 404 }; 405 406 ZmCalColView.prototype._allDayVerticalScrollbar = 407 function(enable) { 408 var el = document.getElementById(this._allDayApptScrollDivId); 409 el.className = enable ? "calendar_allday_appt_vert" : "calendar_allday_appt"; 410 if (enable != this._vertEnabled) { 411 this._vertEnabled = enable; 412 this._syncScroll(true); 413 } 414 }; 415 416 ZmCalColView.prototype._allDayScrollToBottom = 417 function() { 418 var el = document.getElementById(this._allDayApptScrollDivId); 419 el.scrollTop = this._allDayFullDivHeight; 420 }; 421 422 ZmCalColView.prototype._scrollToTime = 423 function(hour) { 424 hour = hour || 8; // default to 8am 425 426 if (!this._autoScrollDisabled) { 427 var bodyElement = document.getElementById(this._bodyDivId); 428 if (!bodyElement) { return; } 429 bodyElement.scrollTop = ZmCalColView._HOUR_HEIGHT*hour - 10; 430 this._syncScroll(); 431 } else { 432 this._autoScrollDisabled = false; 433 } 434 }; 435 436 ZmCalColView.prototype._updateTitle = 437 function() { 438 var dayFormatter = DwtCalendar.getDayFormatter(); 439 440 if (this.numDays == 1) { 441 var colFormatter = DwtCalendar.getDateFormatter(); 442 var date = this._date; 443 this._title = this._scheduleMode 444 ? colFormatter.format(date) 445 : dayFormatter.format(date); 446 } else { 447 var first = this._days[0].date; 448 var last = this._days[this.numDays-1].date; 449 this._title = [ 450 dayFormatter.format(first), " - ", dayFormatter.format(last) 451 ].join(""); 452 } 453 }; 454 455 ZmCalColView.prototype._dayTitle = 456 function(date) { 457 var formatter = this.numDays == 1 458 ? DwtCalendar.getDateLongFormatter() 459 : DwtCalendar.getDateFormatter(); 460 return formatter.format(date); 461 }; 462 463 ZmCalColView.prototype._updateDays = 464 function() { 465 var d = new Date(this._date.getTime()); 466 d.setHours(0,0,0,0); 467 468 //counter to track DST adjustments 469 var daylightAdjustment = false; 470 471 //handle daylight shifting the day e.g. Santiago Oct 10, 2010 00:00 shifted to Oct 9 2010 23:00 472 if(d.getHours() != 0) { 473 AjxDateUtil.rollToNextDay(d); 474 daylightAdjustment = true; 475 } 476 477 var dow; 478 479 switch(this.view) { 480 case ZmId.VIEW_CAL_WORK_WEEK: 481 /*dow = d.getDay(); 482 if (dow == 0) 483 d.setDate(d.getDate()+1); 484 else if (dow != 1) 485 d.setDate(d.getDate()-(dow-1)); 486 break; */ 487 case ZmId.VIEW_CAL_WEEK: 488 var fdow = this.firstDayOfWeek(); 489 dow = d.getDay(); 490 if (dow != fdow) { 491 d.setDate(d.getDate()-((dow+(7-fdow))%7)); 492 } 493 break; 494 case ZmId.VIEW_CAL_DAY: 495 default: 496 /* nothing */ 497 break; 498 } 499 500 //handling the case where start day of week shifted due to DST 501 if(d.getHours() != 0 && !daylightAdjustment) { 502 AjxDateUtil.rollToNextDay(d); 503 daylightAdjustment = true; 504 } 505 506 this._dateToDayIndex = new Object(); 507 508 var today = new Date(); 509 today.setHours(0,0,0,0); 510 511 var lastDay = this.numDays - 1; 512 var j = 0; 513 for (var i=0; i < 7; i++) { 514 var wHrs = this.workingHours[d.getDay()]; 515 var isWorkingDay = wHrs && wHrs.isWorkingDay ? wHrs.isWorkingDay : false; 516 if (this.view === ZmId.VIEW_CAL_WEEK || 517 this.view === ZmId.VIEW_CAL_DAY || 518 this._scheduleMode === true || 519 isWorkingDay === true ) { 520 521 var day = this._days[j] = {}; 522 day.index = j; 523 day.date = new Date(d); 524 day.endDate = new Date(d); 525 day.endDate.setHours(23,59,59,999); 526 day.isToday = day.date.getTime() == today.getTime(); 527 day.isWorkingDay = isWorkingDay; 528 this._dateToDayIndex[this._dayKey(day.date)] = day; 529 if (!this._scheduleMode && this._columns[j]) { 530 var id = this._columns[j].titleId; 531 this._calendarTodayHeaderDivId=day.isToday?id:this._calendarTodayHeaderDivId; 532 var te = document.getElementById(id); 533 if (te) { 534 te.innerHTML = this._dayTitle(d); 535 this.associateItemWithElement(null, te, ZmCalBaseView.TYPE_DAY_HEADER, id, {dayIndex:j}); 536 te.className = day.isToday ? 'calendar_heading_day_today' : 'calendar_heading_day'; 537 } 538 } 539 j++; 540 } 541 var oldDate = d.getDate(); 542 d.setDate(d.getDate() + 1); 543 if (oldDate == d.getDate()) { 544 // daylight saving problem 545 d.setHours(0,0,0,0); 546 d.setTime(d.getTime() + AjxDateUtil.MSEC_PER_DAY); 547 } 548 549 //handling the case where first day got shifted due to DST 550 if(daylightAdjustment) { 551 d.setHours(0,0,0,0); 552 daylightAdjustment = false; 553 } 554 } 555 var te = document.getElementById(this._headerYearId); 556 if(te) { 557 te.innerHTML = this._days[0].date.getFullYear(); 558 } 559 }; 560 561 ZmCalColView.prototype._resetAllDayData = 562 function() { 563 this._allDayAppts = {}; 564 this._allDayApptsList = []; 565 this._allDayApptsRowLayouts = []; 566 this._addAllDayApptRowLayout(); 567 }; 568 569 /** 570 * we don't want allday appts that span days to be fanned out 571 */ 572 ZmCalColView.prototype._fanoutAllDay = 573 function(appt) { 574 return false; 575 }; 576 577 ZmCalColView.prototype._getDivForAppt = 578 function(appt) { 579 return document.getElementById(appt.isAllDayEvent() ? this._allDayDivId : this._apptBodyDivId); 580 }; 581 582 583 584 // for the new appt when drag selecting time grid 585 ZmCalColView.prototype._populateNewApptHtml = 586 function(div, allDay, folderId) { 587 if (folderId == null) { 588 folderId = this._controller.getDefaultCalendarFolderId(); 589 } 590 var color = ZmCalendarApp.COLORS[this._controller.getCalendarColor(folderId)]; 591 var prop = allDay ? "_newAllDayApptColor" : "_newApptColor"; 592 if (this[prop] && this[prop] == color) { 593 return div; 594 } 595 596 this[prop] = color; 597 div.style.position = 'absolute'; 598 Dwt.setSize(div, 10, 10);// will be resized 599 div.className = this._getStyle(null, true); 600 Dwt.setOpacity(div, ZmCalColView._OPACITY_APPT_DND); 601 var calendar = appCtxt.getById(folderId); 602 //var headerColor = calendar.rgb ? AjxColor.deepen(AjxColor.darken(calendar.rgb,ZmCalBaseView.headerColorDelta)) : ""; 603 var bodyColor = calendar.rgb ? AjxColor.deepen(AjxColor.lighten(calendar.rgb,ZmCalBaseView.bodyColorDelta)) : ""; 604 var subs = { 605 id: div.id, 606 newState: "", 607 headerColor: calendar.rgb ? "" : (color + "Light"), 608 bodyColor: calendar.rgb ? "" : (color + "Bg"), 609 headerStyle: calendar.rgb ? "background-color: "+bodyColor+";" : "", 610 name: AjxStringUtil.htmlEncode(ZmMsg.newAppt), 611 starttime: "", 612 endtime: "", 613 location: "", 614 status: "" 615 }; 616 var template; 617 var gradient = Dwt.createLinearGradientCss("#FFFFFF", bodyColor, "v"); 618 if (allDay) { 619 template = "calendar_appt_allday"; 620 if (gradient) { 621 subs.headerStyle = gradient; 622 } 623 } else { 624 template = "calendar_appt"; 625 if (gradient) { 626 subs.bodyStyle = gradient; 627 subs.headerStyle = null; 628 } 629 } 630 div.innerHTML = AjxTemplate.expand("calendar.Calendar#"+template, subs); 631 return div; 632 }; 633 634 ZmCalColView.prototype._createItemHtml = function(appt) { 635 636 if (this.view === ZmId.VIEW_CAL_WORK_WEEK) { 637 var availableStartTime = this.getAvailableStartTime(appt); 638 if (!availableStartTime) { 639 return; 640 } 641 } 642 643 var isAllDay = appt.isAllDayEvent(); 644 if (isAllDay) { 645 var dataId = appt.getUniqueId(); 646 var startTime = availableStartTime || Math.max(appt.getStartTime(), this._timeRangeStart); 647 648 this._allDayAppts[dataId] = { 649 appt: appt, 650 startTime: startTime 651 }; 652 this._allDayApptsList.push(appt); 653 } 654 655 var apptWidth = 10, 656 apptHeight = 10, 657 apptX = 0, 658 apptY = 0, 659 layout = this._layoutMap[this._getItemId(appt)]; 660 661 // set up DIV 662 var div = document.createElement("div"); 663 664 Dwt.setPosition(div, Dwt.ABSOLUTE_STYLE); 665 Dwt.setCursor(div, 'default'); 666 Dwt.setSize(div, apptWidth, apptHeight); 667 if (layout) { 668 div.style.left = apptX + 'px'; 669 div.style.top = apptY + 'px'; 670 } 671 div.className = this._getStyle(); 672 if (this.view === ZmId.VIEW_CAL_FB) { 673 Dwt.setScrollStyle(div, Dwt.CLIP); 674 } 675 676 this.associateItemWithElement(appt, div, ZmCalBaseView.TYPE_APPT); 677 678 var isNew = (appt.ptst === ZmCalBaseItem.PSTATUS_NEEDS_ACTION), 679 id = this._getItemId(appt), 680 calendar = appCtxt.getById(appt.folderId), 681 isRemote = Boolean(calendar.url), 682 is30 = appt._orig.getDuration() <= AjxDateUtil.MSEC_PER_HALF_HOUR, 683 is60 = appt._orig.getDuration() <= AjxDateUtil.MSEC_PER_HOUR, 684 apptName = AjxStringUtil.htmlEncode(appt.getName()); 685 686 // normalize location 687 var location = appt.getLocation(); 688 location = location && location.length && !is60 ? "<div class='appt_location'>" + AjxStringUtil.htmlEncode(appt.getLocation()) + "</div>" : null; 689 690 if ((is30 || isAllDay) && this.view !== ZmId.VIEW_CAL_DAY) { 691 // fit as much of appt name as we can in one row, use ... if we have to truncate 692 apptName = isAllDay ? apptName : appt.getDurationText(true, true) + " - " + apptName; 693 var apptBounds = this._getBoundsForAppt(appt), 694 apptWidth = apptBounds && apptBounds.width; 695 696 if (apptWidth > 30) { 697 apptName = AjxStringUtil.fitString(apptName, apptWidth - 15); 698 } 699 } 700 701 var tagNames = appt.getVisibleTags(), 702 tagIcon = appt.getTagImageFromNames(tagNames); 703 704 // If the tag icon is returned blank image reset the tag icon 705 if (tagIcon === "Blank_16") { 706 tagIcon = ""; 707 } 708 709 var colors = ZmApptViewHelper.getApptColor(isNew, calendar, tagNames, "body"), 710 bodyStyle = ZmCalBaseView._toColorsCss(colors.appt), 711 fba = isNew ? ZmCalBaseItem.PSTATUS_NEEDS_ACTION : appt.fba; 712 713 var subs = { 714 id: id, 715 newState: isNew ? "_new" : "", 716 headerStyle: bodyStyle, 717 name: apptName, 718 starttime: appt.getDurationText(true, true), 719 endtime: !appt._fanoutLast && (appt._fanoutFirst || appt._fanoutNum > 0) ? "" : ZmCalBaseItem._getTTHour(appt.endDate), 720 location: location, 721 status: appt.isOrganizer() ? "" : appt.getParticipantStatusStr(), 722 icon: appt.isPrivate() ? "ReadOnly" : null, 723 tagIcon: tagIcon, 724 hideTime: is60, 725 showAsColor : ZmApptViewHelper._getShowAsColorFromId(fba), 726 boxBorder: ZmApptViewHelper.getBoxBorderFromId(fba), 727 isDraft: appt.isDraft, 728 otherAttendees: appt.otherAttendees, 729 isException: appt.isException, 730 isRecurring: appt.isRecurring() 731 }; 732 733 var template, 734 colorParam, 735 clearParam, 736 bs; 737 738 if (appt.isAllDayEvent()) { 739 colorParam = "headerStyle"; 740 template = "calendar_appt_allday"; 741 if (!this.isStartInView(appt._orig)) { 742 bs = "border-left:none;"; 743 } 744 if (!this.isEndInView(appt._orig)) { 745 bs += "border-right:none;"; 746 } 747 if (bs) { 748 subs.bodyStyle = bs; 749 } 750 } 751 else if (this.view == ZmId.VIEW_CAL_FB) { 752 template = "calendar_fb_appt"; 753 } 754 else if (is30) { 755 colorParam = "headerStyle"; 756 template = "calendar_appt_30"; 757 } 758 else if (appt._fanoutNum > 0) { 759 colorParam = "bodyStyle"; 760 template = "calendar_appt_bottom_only"; 761 } 762 else { 763 colorParam = "bodyStyle"; 764 clearParam = "headerStyle"; 765 template = "calendar_appt"; 766 } 767 // Currently header/bodyStyles are only used for coloring. Replace with a gradient 768 // if supported by the browser 769 ZmApptViewHelper.setupCalendarColor(true, colors, tagNames, subs, colorParam, clearParam, 1, 1); 770 771 div.innerHTML = AjxTemplate.expand("calendar.Calendar#" + template, subs); 772 773 // Set opacity on the table element that is colored with the gradient. Needed for IE 774 var tableEl = Dwt.getDescendant(div, id + "_tableBody"), 775 opacity = ZmCalBaseView.getApptOpacity(appt); 776 if (tableEl) { 777 Dwt.setOpacity(tableEl, opacity); 778 } 779 else { 780 Dwt.setOpacity(div, opacity); 781 } 782 783 // if (we can edit this appt) then create sash.... 784 if (!appt.isReadOnly() && !appt.isAllDayEvent() && !isRemote && this.view !== ZmId.VIEW_CAL_FB) { 785 if (appt._fanoutLast || (!appt._fanoutFirst && (!appt._fanoutNum))) { 786 var bottom = document.createElement("div"); 787 this.associateItemWithElement(null, bottom, ZmCalBaseView.TYPE_APPT_BOTTOM_SASH, appt.id + "-bs"); 788 bottom.className = 'appt_bottom_sash'; 789 div.appendChild(bottom); 790 } 791 792 if (appt._fanoutFirst || (!appt._fanoutLast && (!appt._fanoutNum))) { 793 var top = document.createElement("div"); 794 this.associateItemWithElement(null, top, ZmCalBaseView.TYPE_APPT_TOP_SASH, appt.id + "-ts"); 795 top.className = 'appt_top_sash'; 796 div.appendChild(top); 797 } 798 } 799 800 return div; 801 }; 802 803 804 // TODO: i18n 805 ZmCalColView.prototype._createHoursHtml = 806 function(html) { 807 808 html.append("<div style='position:absolute; top:-8; width:", ZmCalColView._HOURS_DIV_WIDTH, "px;' id='", this._bodyHourDivId, "'>"); 809 810 var formatter = DwtCalendar.getHourFormatter(); 811 var curDate = new Date(); 812 var date = new Date(); 813 date.setHours(0, 0, 0, 0); 814 var timeTDWidth = ZmCalColView._HOURS_DIV_WIDTH - (this._fbBarEnabled ? ZmCalColView._FBBAR_DIV_WIDTH : 0 ); 815 html.append("<table class=calendar_grid_day_table>"); 816 for (var h=0; h < 25; h++) { 817 html.append("<tr><td class=calendar_grid_body_time_td style='height:", 818 ZmCalColView._HOUR_HEIGHT ,"px; width:", timeTDWidth, "px'><div id='"+this._hourColDivId+"_"+h+"' class=calendar_grid_body_time_text>"); 819 date.setHours(h); 820 html.append(h > 0 && h < 24 ? AjxStringUtil.htmlEncode(formatter.format([h, date])) : " "); 821 html.append("</div>"); 822 html.append("</td>"); 823 if(this._fbBarEnabled){ 824 html.append("<td class=calendar_grid_body_fbbar_td style='height:",ZmCalColView._HOUR_HEIGHT ,"px; width:", ZmCalColView._FBBAR_DIV_WIDTH,"px; border-left:1px solid #A7A194;'> </td>"); 825 } 826 html.append("</tr>"); 827 } 828 html.append("</table>"); 829 html.append("<div id='"+this._curTimeIndicatorHourDivId+"' class='calendar_cur_time_indicator_arr'><div class='calendar_hour_arrow_indicator'>→</div></div>"); 830 html.append( "</div>"); 831 }; 832 833 834 ZmCalColView.prototype._createHtml = 835 function(abook) { 836 this._days = {}; 837 this._columns = []; 838 this._hours = {}; 839 this._layouts = []; 840 this._allDayAppts = []; 841 842 var html = new AjxBuffer(); 843 844 this._headerYearId = Dwt.getNextId(); 845 this._yearHeadingDivId = Dwt.getNextId(); 846 this._yearAllDayDivId = Dwt.getNextId(); 847 this._leftAllDaySepDivId = Dwt.getNextId(); 848 this._leftApptSepDivId = Dwt.getNextId(); 849 850 this._allDayScrollDivId = Dwt.getNextId(); 851 this._allDayHeadingDivId = Dwt.getNextId(); 852 this._allDayApptScrollDivId = Dwt.getNextId(); 853 this._allDayDivId = Dwt.getNextId(); 854 this._hoursScrollDivId = Dwt.getNextId(); 855 this._bodyHourDivId = Dwt.getNextId(); 856 this._allDaySepDivId = Dwt.getNextId(); 857 this._allDaySepSashDivId = Dwt.getNextId(); 858 this._bodyDivId = Dwt.getNextId(); 859 this._apptBodyDivId = Dwt.getNextId(); 860 this._newApptDivId = Dwt.getNextId(); 861 this._newAllDayApptDivId = Dwt.getNextId(); 862 this._timeSelectionDivId = Dwt.getNextId(); 863 this._curTimeIndicatorHourDivId = Dwt.getNextId(); 864 this._curTimeIndicatorGridDivId = Dwt.getNextId(); 865 this._hourColDivId = Dwt.getNextId(); 866 this._startLimitIndicatorDivId = Dwt.getNextId(); 867 this._endLimitIndicatorDivId = Dwt.getNextId(); 868 // Fix for bug: 66603. Reference to parent container of _allDayHeadingDivId 869 this._tabsContainerDivId = Dwt.getNextId(); 870 871 872 if (this._scheduleMode) { 873 this._unionHeadingDivId = Dwt.getNextId(); 874 this._unionAllDayDivId = Dwt.getNextId(); 875 this._unionHeadingSepDivId = Dwt.getNextId(); 876 this._unionGridScrollDivId = Dwt.getNextId(); 877 this._unionGridDivId = Dwt.getNextId(); 878 this._unionGridSepDivId = Dwt.getNextId(); 879 this._workingHrsFirstDivId = Dwt.getNextId(); 880 this._workingHrsFirstChildDivId = Dwt.getNextId(); 881 this._workingHrsSecondDivId = Dwt.getNextId(); 882 this._workingHrsSecondChildDivId = Dwt.getNextId(); 883 } 884 885 this._allDayRows = []; 886 887 if (!this._scheduleMode) { 888 for (var i =0; i < this.numDays; i++) { 889 this._columns[i] = { 890 index: i, 891 dayIndex: i, 892 titleId: Dwt.getNextId(), 893 headingDaySepDivId: Dwt.getNextId(), 894 daySepDivId: Dwt.getNextId(), 895 workingHrsFirstDivId: Dwt.getNextId(), 896 workingHrsFirstChildDivId: Dwt.getNextId(), 897 workingHrsSecondDivId: Dwt.getNextId(), 898 workingHrsSecondChildDivId: Dwt.getNextId(), 899 apptX: 0, // computed in layout 900 apptWidth: 0,// computed in layout 901 allDayX: 0, // computed in layout 902 allDayWidth: 0// computed in layout 903 }; 904 } 905 } 906 907 // year heading 908 var inviteMessageHeaderStyle = (this._isInviteMessage && !this._isRight ? "height:26px;" : ""); //override class css in this case, so the header height aligns with the message view on the left 909 var headerStyle = "position:absolute;" + inviteMessageHeaderStyle; 910 911 html.append("<div id='", this._yearHeadingDivId, "' class='calendar_heading' style='", headerStyle, "'>"); 912 html.append("<div id='", this._headerYearId, 913 "' class=calendar_heading_year_text style='position:absolute; width:", ZmCalColView._HOURS_DIV_WIDTH,"px;'></div>"); 914 html.append("</div>"); 915 916 // div under year 917 html.append("<div id='", this._yearAllDayDivId, "' style='position:absolute'></div>"); 918 919 // sep between year and headings 920 html.append("<div id='", this._leftAllDaySepDivId, "' class='calendar_day_separator' style='position:absolute'></div>"); 921 922 if (this._scheduleMode) { 923 // "All" heading 924 html.append("<div id='", this._unionHeadingDivId, "' class=calendar_heading style='position:absolute'>"); 925 html.append("<div class=calendar_heading_year_text style='position:absolute; width:", ZmCalColView._UNION_DIV_WIDTH,"px;'>",ZmMsg.all,"</div>"); 926 html.append("</div>"); 927 928 // div in all day space 929 html.append("<div id='", this._unionAllDayDivId, "' style='position:absolute'></div>"); 930 931 // sep between year and headings 932 html.append("<div id='", this._unionHeadingSepDivId, "' class='calendar_day_separator' style='position:absolute'></div>"); 933 } 934 935 // all day scroll ============= 936 html.append("<div id='", this._allDayScrollDivId, "' style='position:absolute; overflow:hidden;'>"); 937 938 // all day headings 939 // Fix for bug: 66603. Adding a container to calendar headings 940 html.append("<div id='", this._tabsContainerDivId, "' name='_tabsContainerDivId' style='position:absolute;height:25px;bottom:0px;top:0px'>"); 941 html.append("<div id='", this._allDayHeadingDivId, "' class='calendar_heading' style='", headerStyle, "'>"); 942 if (!this._scheduleMode) { 943 for (var i =0; i < this.numDays; i++) { 944 html.append("<div id='", this._columns[i].titleId, "' class='calendar_heading_day' style='position:absolute;'></div>"); 945 } 946 } 947 html.append("</div>"); 948 // Fix for bug: 66603 949 html.append("</div>"); 950 951 // divs to separate day headings 952 if (!this._scheduleMode) { 953 for (var i =0; i < this.numDays; i++) { 954 html.append("<div id='", this._columns[i].headingDaySepDivId, "' class='calendar_day_separator' style='position:absolute'></div>"); 955 } 956 } 957 html.append("</div>"); 958 // end of all day scroll =========== 959 960 // div holding all day appts 961 html.append("<div id='", this._allDayApptScrollDivId, "' class='calendar_allday_appt' style='position:absolute'>"); 962 html.append("<div id='", this._allDayDivId, "' style='position:absolute'>"); 963 html.append("<div id='", this._newAllDayApptDivId, "' class='appt-selected' style='position:absolute; display:none;'></div>"); 964 html.append("</div>"); 965 html.append("</div>"); 966 967 // sep betwen all day and normal appts 968 html.append("<div id='", this._allDaySepDivId, "' class=calendar_header_allday_separator style='overflow:hidden;position:absolute;'><div id='", this._allDaySepSashDivId, "' class='calendar_header_allday_separator_sash open'></div></div>"); 969 970 // div to hold hours 971 html.append("<div id='", this._hoursScrollDivId, "' class=calendar_hour_scroll style='position:absolute;'>"); 972 this._createHoursHtml(html); 973 html.append("</div>"); 974 975 // sep between hours and grid 976 html.append("<div id='", this._leftApptSepDivId, "' class='calendar_day_separator' style='position:absolute'></div>"); 977 978 // union grid 979 if (this._scheduleMode) { 980 html.append("<div id='", this._unionGridScrollDivId, "' class=calendar_union_scroll style='position:absolute'>"); 981 html.append("<div id='", this._unionGridDivId, "' class='ImgCalendarDayGrid' style='width:100%; height:1008px; position:absolute;'>"); 982 html.append("</div></div>"); 983 // sep between union grid and appt grid 984 html.append("<div id='", this._unionGridSepDivId, "' class='calendar_day_separator' style='position:absolute'></div>"); 985 } 986 987 // grid body 988 html.append("<div id='", this._bodyDivId, "' class=calendar_body style='position:absolute'>"); 989 html.append("<div id='", this._apptBodyDivId, "' class='ImgCalendarDayGrid' style='width:100%; height:1008px; position:absolute;background-color:#E3E3DC;'>"); 990 html.append("<div id='", this._timeSelectionDivId, "' class='calendar_time_selection' style='position:absolute; display:none;z-index:10;'></div>"); 991 html.append("<div id='", this._newApptDivId, "' class='appt-selected' style='position:absolute; display:none;'></div>"); 992 if (!this._scheduleMode) { 993 for (var i =0; i < this.numDays; i++) { 994 html.append("<div id='", this._columns[i].daySepDivId, "' class='calendar_day_separator' style='position:absolute'></div>"); 995 html.append("<div id='", this._columns[i].workingHrsFirstDivId, "' style='position:absolute;background-color:#FFFFFF;'><div id='", this._columns[i].workingHrsFirstChildDivId, "' class='ImgCalendarDayGrid' style='position:absolute;top:0px;left:0px;overflow:hidden;'></div></div>"); 996 html.append("<div id='", this._columns[i].workingHrsSecondDivId, "' style='position:absolute;background-color:#FFFFFF;'><div id='", this._columns[i].workingHrsSecondChildDivId, "' class='ImgCalendarDayGrid' style='position:absolute;top:0px;left:0px;overflow:hidden;'></div></div>"); 997 } 998 } 999 else { 1000 html.append("<div id='", this._workingHrsFirstDivId, "' style='position:absolute;background-color:#FFFFFF;'><div class='ImgCalendarDayGrid' id='", this._workingHrsFirstChildDivId, "' style='position:absolute;top:0px;left:0px;overflow:hidden;'></div></div>"); 1001 html.append("<div id='", this._workingHrsSecondDivId, "' style='position:absolute;background-color:#FFFFFF;'><div class='ImgCalendarDayGrid' id='", this._workingHrsSecondChildDivId, "' style='position:absolute;top:0px;left:0px;overflow:hidden;'></div></div>"); 1002 } 1003 1004 1005 html.append("</div>"); 1006 //Strip to indicate the current time 1007 html.append("<div id='"+this._curTimeIndicatorGridDivId+"' class='calendar_cur_time_indicator_container'><div class='calendar_cur_time_indicator_strip'></div></div>"); 1008 html.append("<div id='"+this._startLimitIndicatorDivId+"' class='calendar_start_limit_indicator'><div class='ImgArrowMoreUp'></div></div>"); 1009 html.append("<div id='"+this._endLimitIndicatorDivId+"' class='calendar_end_limit_indicator'><div class='ImgArrowMoreDown'></div></div>"); 1010 html.append("</div>"); 1011 1012 this.getHtmlElement().innerHTML = html.toString(); 1013 1014 var func = AjxCallback.simpleClosure(ZmCalColView.__onScroll, ZmCalColView, this); 1015 document.getElementById(this._bodyDivId).onscroll = func; 1016 document.getElementById(this._allDayApptScrollDivId).onscroll = func; 1017 // Fix for bug: 66603. Adding a handler to enable scrolling. 1018 document.getElementById(this._tabsContainerDivId).onscroll = func; 1019 1020 var ids = [this._apptBodyDivId, this._bodyHourDivId, this._allDayDivId, this._allDaySepDivId]; 1021 var types = [ZmCalBaseView.TYPE_APPTS_DAYGRID, ZmCalBaseView.TYPE_HOURS_COL, ZmCalBaseView.TYPE_ALL_DAY, ZmCalBaseView.TYPE_DAY_SEP]; 1022 for (var i = 0; i < ids.length; i++) { 1023 this.associateItemWithElement(null, document.getElementById(ids[i]), types[i], ids[i]); 1024 } 1025 this._scrollToTime(8); 1026 }; 1027 1028 ZmCalColView.prototype.updateTimeIndicator = function(force) { 1029 this._updateTimeIndicator(force); 1030 return this.setTimer(1); 1031 } 1032 1033 1034 ZmCalColView.prototype._updateTimeIndicator = function(force) { 1035 var curDate = new Date(); 1036 var hr = curDate.getHours(); 1037 var min = curDate.getMinutes(); 1038 var curHourDiv = document.getElementById(this._hourColDivId + "_" + hr); 1039 if (!curHourDiv) { 1040 return; 1041 } 1042 1043 var curTimeHourIndicator = document.getElementById(this._curTimeIndicatorHourDivId); 1044 var currentTopPosition = Math.round((ZmCalColView._HOUR_HEIGHT/60)*min)+parseInt(curHourDiv.offsetParent.offsetTop); 1045 Dwt.setLocation(curTimeHourIndicator, curHourDiv.offsetParent.offsetLeft, currentTopPosition - 5); 1046 var calendarStrip = document.getElementById(this._curTimeIndicatorGridDivId); 1047 Dwt.setVisibility(calendarStrip,true); 1048 var todayColDiv = document.getElementById(this._calendarTodayHeaderDivId); 1049 if (todayColDiv && (force || this._isValidIndicatorDuration)) { 1050 Dwt.setBounds(calendarStrip, todayColDiv.offsetLeft, currentTopPosition, todayColDiv.offsetWidth, null); 1051 } else { 1052 Dwt.setVisibility(calendarStrip,false); 1053 } 1054 }; 1055 1056 1057 ZmCalColView.prototype.startIndicatorTimer=function(force){ 1058 if(force || !this._indicatorTimer){ 1059 this._indicatorTimer = this.updateTimeIndicator(force); 1060 } 1061 }; 1062 1063 ZmCalColView.prototype.checkIndicatorNeed=function(viewId,startDate){ 1064 var isValidView = (viewId == ZmId.VIEW_CAL_WORK_WEEK || viewId == ZmId.VIEW_CAL_WEEK || viewId == ZmId.VIEW_CAL_DAY); 1065 if(startDate!=null && isValidView){ 1066 var today = new Date(); 1067 var todayTime = today.getTime(); 1068 startDate.setHours(0,0,0,0); 1069 var sTime = startDate.getTime(); 1070 var endDate = AjxDateUtil.roll(startDate,AjxDateUtil.DAY,1); 1071 endDate.setHours(23,59,59,999); 1072 var endTime = endDate.getTime(); 1073 if(!(todayTime>=sTime && todayTime<=endTime)){ 1074 this._isValidIndicatorDuration = false; 1075 var calendarStrip = document.getElementById(this._curTimeIndicatorGridDivId); 1076 Dwt.setVisibility(calendarStrip,false); 1077 }else{ 1078 this._isValidIndicatorDuration = true; 1079 this.updateTimeIndicator(); 1080 } 1081 }else{ 1082 this._isValidIndicatorDuration = true; 1083 } 1084 }; 1085 1086 /* 1087 * Checks whether any offscreen appointment exists, and indicates according to the direction it gets hidden. 1088 */ 1089 ZmCalColView.prototype._checkForOffscreenAppt=function(bodyElement){ 1090 var topExceeds = false; 1091 var bottomExceeds = false; 1092 if(!bodyElement){bodyElement = document.getElementById(this._bodyDivId);} 1093 if(!bodyElement) { return; } 1094 var height = bodyElement.offsetHeight; 1095 var top = bodyElement.scrollTop; 1096 var appt; 1097 1098 if(this._list && this._list.size()>0){ 1099 var apptArray = this._list.getArray(); 1100 for(var i=0;i<apptArray.length;i++){ 1101 appt = apptArray[i]; 1102 if (!appt) { continue; } 1103 var layoutParams = apptArray[i].getLayoutInfo(); 1104 if(!topExceeds){topExceeds=(layoutParams && layoutParams.y<(top));} 1105 if(!bottomExceeds){bottomExceeds=(layoutParams && layoutParams.y>(height+top));} 1106 if(topExceeds && bottomExceeds){break;} 1107 } 1108 } 1109 1110 var topIndicator = document.getElementById(this._startLimitIndicatorDivId); 1111 Dwt.setVisibility(topIndicator,topExceeds); 1112 var bottomIndicator = document.getElementById(this._endLimitIndicatorDivId); 1113 Dwt.setVisibility(bottomIndicator,bottomExceeds); 1114 1115 if(topExceeds){ 1116 topIndicator.style.top=bodyElement.scrollTop+"px"; 1117 } 1118 1119 if(bottomExceeds){ 1120 bottomIndicator.style.top = ((bodyElement.offsetHeight+bodyElement.scrollTop+8)-(bottomIndicator.offsetHeight))+"px"; 1121 } 1122 }; 1123 1124 ZmCalColView.__onScroll = 1125 function(myView) { 1126 if(this.__scrollActionId) { // Fix for Bug 84928 1127 AjxTimedAction.cancelAction(this.__scrollActionId); 1128 delete this.__scrollActionId; 1129 } 1130 this.__scrollActionId = AjxTimedAction.scheduleAction(new AjxTimedAction(myView,myView._syncScroll), 30); 1131 }; 1132 1133 ZmCalColView.prototype._computeMaxCols = 1134 function(layout, max) { 1135 //DBG.println("compute max cols for "+layout.appt.id+" col="+layout.col); 1136 if (layout.maxDone) return layout.maxcol; 1137 layout.maxcol = Math.max(layout.col, layout.maxcol, max); 1138 if (layout.right) { 1139 for (var r = 0; r < layout.right.length; r++) { 1140 layout.maxcol = Math.max(layout.col, this._computeMaxCols(layout.right[r], layout.maxcol)); 1141 } 1142 } 1143 //DBG.println("max cols for "+layout.appt.id+" was: "+layout.maxcol); 1144 layout.maxDone = true; 1145 return layout.maxcol; 1146 }; 1147 1148 /* 1149 * compute appt layout for appts that aren't all day 1150 */ 1151 ZmCalColView.prototype._computeApptLayout = 1152 function() { 1153 // DBG.println("_computeApptLayout"); 1154 // DBG.timePt("_computeApptLayout: start", true); 1155 var layouts = this._layouts = new Array(); 1156 var layoutsDayMap = []; 1157 var layoutsAllDay = []; 1158 var list = this.getList(); 1159 if (!list) return; 1160 1161 var size = list.size(); 1162 if (size == 0) { return; } 1163 1164 var overlap = null; 1165 var overlappingCol = null; 1166 1167 for (var i=0; i < size; i++) { 1168 var ao = list.get(i); 1169 1170 if (!ao || ao.isAllDayEvent()) { 1171 continue; 1172 } 1173 1174 var newLayout = { appt: ao, col: 0, maxcol: -1}; 1175 1176 overlap = null; 1177 overlappingCol = null; 1178 1179 var asd = ao.startDate; 1180 var aed = ao.endDate; 1181 1182 var asdDate = asd.getDate(); 1183 var aedDate = aed.getDate(); 1184 1185 var checkAllLayouts = (asdDate != aedDate); 1186 var layoutCheck = []; 1187 1188 // if a appt starts n end in same day, it should be compared only with 1189 // other appts on same day and with those which span multiple days 1190 if (checkAllLayouts) { 1191 layoutCheck.push(layouts); 1192 } else { 1193 layoutCheck.push(layoutsAllDay); 1194 if (layoutsDayMap[asdDate]!=null) { 1195 layoutCheck.push(layoutsDayMap[asdDate]); 1196 } 1197 } 1198 1199 // look for overlapping appts 1200 for (var k = 0; k < layoutCheck.length; k++) { 1201 for (var j=0; j < layoutCheck[k].length; j++) { 1202 var layout = layoutCheck[k][j]; 1203 if (ao.isOverlapping(layout.appt, this._scheduleMode)) { 1204 if (overlap == null) { 1205 overlap = []; 1206 overlappingCol = []; 1207 } 1208 overlap.push(layout); 1209 overlappingCol[layout.col] = true; 1210 // while we overlap, update our col 1211 while (overlappingCol[newLayout.col]) { 1212 newLayout.col++; 1213 } 1214 } 1215 } 1216 } 1217 1218 // figure out who is on our right 1219 if (overlap != null) { 1220 for (var c = 0; c < overlap.length; c++) { 1221 var l = overlap[c]; 1222 if (newLayout.col < l.col) { 1223 if (!newLayout.right) newLayout.right = [l]; 1224 else newLayout.right.push(l); 1225 } else { 1226 if (!l.right) l.right = [newLayout]; 1227 else l.right.push(newLayout); 1228 } 1229 } 1230 } 1231 layouts.push(newLayout); 1232 if (asdDate == aedDate) { 1233 if(!layoutsDayMap[asdDate]) { 1234 layoutsDayMap[asdDate] = []; 1235 } 1236 layoutsDayMap[asdDate].push(newLayout); 1237 } else { 1238 layoutsAllDay.push(newLayout); 1239 } 1240 } 1241 1242 // compute maxcols 1243 for (var i=0; i < layouts.length; i++) { 1244 this._computeMaxCols(layouts[i], -1); 1245 this._layoutMap[this._getItemId(layouts[i].appt)] = layouts[i]; 1246 // DBG.timePt("_computeApptLayout: computeMaxCol "+i, false); 1247 } 1248 1249 delete layoutsAllDay; 1250 delete layoutsDayMap; 1251 delete layoutCheck; 1252 //DBG.timePt("_computeApptLayout: end", false); 1253 }; 1254 1255 /* 1256 * add a new all day appt row layout slot and return it 1257 */ 1258 ZmCalColView.prototype._addAllDayApptRowLayout = 1259 function() { 1260 var data = []; 1261 var num = this._columns.length; 1262 for (var i=0; i < num; i++) { 1263 // free is set to true if slot is available, false otherwise 1264 // appt is set to the _allDayAppts data in the first slot only (if appt spans days) 1265 data[i] = { free: true, data: null }; 1266 } 1267 this._allDayApptsRowLayouts.push(data); 1268 return data; 1269 }; 1270 1271 /** 1272 * take the appt data in reserve the slots 1273 */ 1274 ZmCalColView.prototype._fillAllDaySlot = 1275 function(row, colIndex, data) { 1276 for (var j=0; j < data.numDays; j++) { 1277 var col = colIndex + j; 1278 if (col == row.length) break; 1279 row[col].data = j==0 ? data : null; 1280 row[col].free = false; 1281 } 1282 }; 1283 1284 /** 1285 * find a slot and fill it in, adding new rows if needed 1286 */ 1287 ZmCalColView.prototype._findAllDaySlot = 1288 function(colIndex, data) { 1289 if (data.appt) { 1290 var appt = data.appt; 1291 var startTime = appt.getStartTime(); 1292 var endTime = appt.getEndTime(); 1293 data.numDays = 1; 1294 if (startTime != endTime) { 1295 data.numDays = this._calcNumDays(startTime, endTime); 1296 } 1297 if (startTime < data.startTime) { 1298 data.numDays -= this._calcNumDays(startTime, data.startTime); 1299 } 1300 } 1301 var rows = this._allDayApptsRowLayouts; 1302 var row = null; 1303 for (var i=0; i < rows.length; i++) { 1304 row = rows[i]; 1305 for (var j=0; j < data.numDays; j++) { 1306 var col = colIndex + j; 1307 if (col == row.length) break; 1308 if (!row[col].free) { 1309 row = null; 1310 break; 1311 } 1312 } 1313 if (row != null) break; 1314 } 1315 if (row == null) { 1316 row = this._addAllDayApptRowLayout(); 1317 } 1318 1319 this._fillAllDaySlot(row, colIndex, data); 1320 }; 1321 1322 ZmCalColView.prototype._calcNumDays = 1323 function(startTime, endTime) { 1324 return Math.round((endTime-startTime) / AjxDateUtil.MSEC_PER_DAY); 1325 } 1326 // Calculate the offset in days from the 0th column date. Used for 1327 // multi-day appt dragging. 1328 ZmCalColView.prototype._calcOffsetFromZeroColumn = 1329 function(time) { 1330 var dayIndex = this._columns[0].dayIndex; 1331 var day = this._days[dayIndex]; 1332 return Math.round((time-day.date.getTime()) / AjxDateUtil.MSEC_PER_DAY); 1333 } 1334 1335 /* 1336 * compute layout info for all day appts 1337 */ 1338 ZmCalColView.prototype._computeAllDayApptLayout = 1339 function() { 1340 var adlist = this._allDayApptsList; 1341 adlist.sort(ZmCalBaseItem.compareByTimeAndDuration); 1342 1343 for (var i=0; i < adlist.length; i++) { 1344 var appt = adlist[i]; 1345 var data = this._allDayAppts[appt.getUniqueId()]; 1346 if (data) { 1347 var col = this._scheduleMode ? this._getColForFolderId(data.appt.folderId) : this._getDayForDate(new Date(data.startTime)); 1348 if (col) this._findAllDaySlot(col.index, data); 1349 } 1350 } 1351 }; 1352 1353 ZmCalColView.prototype._layoutAllDayAppts = 1354 function() { 1355 var rows = this._allDayApptsRowLayouts; 1356 if (!rows) { return; } 1357 1358 var rowY = ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD + 2; 1359 for (var i=0; i < rows.length; i++) { 1360 var row = rows[i]; 1361 var num = this._scheduleMode ? this._numCalendars : this.numDays; 1362 for (var j=0; j < num; j++) { 1363 var slot = row[j]; 1364 if (slot.data) { 1365 var appt = slot.data.appt; 1366 var div = document.getElementById(this._getItemId(appt)); 1367 if(div) { 1368 if (this._scheduleMode) { 1369 var cal = this._getColForFolderId(appt.folderId); 1370 this._positionAppt(div, cal.allDayX+0, rowY); 1371 this._sizeAppt(div, ((cal.allDayWidth + this._daySepWidth) * slot.data.numDays) - this._daySepWidth - 1, 1372 ZmCalColView._ALL_DAY_APPT_HEIGHT); 1373 } else { 1374 this._positionAppt(div, this._columns[j].allDayX+0, rowY); 1375 this._sizeAppt(div, ((this._columns[j].allDayWidth + this._daySepWidth) * slot.data.numDays) - this._daySepWidth - 1, 1376 ZmCalColView._ALL_DAY_APPT_HEIGHT); 1377 } 1378 } 1379 } 1380 } 1381 rowY += ZmCalColView._ALL_DAY_APPT_HEIGHT + ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD; 1382 } 1383 }; 1384 1385 1386 ZmCalColView._getApptWidthPercent = 1387 function(numCols) { 1388 switch(numCols) { 1389 case 1: return 1; 1390 case 2: return 0.8; 1391 case 3: return 0.6; 1392 case 4: return 0.4; 1393 default: return 0.4; 1394 } 1395 }; 1396 1397 ZmCalColView.prototype._positionAppt = 1398 function(apptDiv, x, y) { 1399 if(!apptDiv) { return; } 1400 // position overall div 1401 Dwt.setLocation(apptDiv, x + ZmCalColView._APPT_X_FUDGE, y + ZmCalColView._APPT_Y_FUDGE); 1402 }; 1403 1404 ZmCalColView.prototype._sizeAppt = 1405 function(apptDiv, w, h) { 1406 if(!apptDiv) { return; } 1407 // set outer as well as inner 1408 var fw = w + ZmCalColView._APPT_WIDTH_FUDGE; // no fudge for you 1409 var fh = h; 1410 Dwt.setSize(apptDiv, fw >= 0 ? fw : 0, fh >= 0 ? fh : 0); 1411 1412 // get the inner div that should be sized and set its width/height 1413 var apptBodyDiv = document.getElementById(apptDiv.id + "_body"); 1414 if (apptBodyDiv != null) { 1415 fw = w + ZmCalColView._APPT_WIDTH_FUDGE; 1416 fh = h + ZmCalColView._APPT_HEIGHT_FUDGE; 1417 Dwt.setSize( apptBodyDiv, fw >= 0 ? fw : 0, fh >= 0 ? fh : 0); 1418 } 1419 }; 1420 1421 ZmCalColView.prototype._layoutAppt = 1422 function(ao, apptDiv, x, y, w, h) { 1423 // record to restore after dnd/sash 1424 if (ao) ao._layout = {x: x, y: y, w: w, h: h}; 1425 this._positionAppt(apptDiv, x, y); 1426 this._sizeAppt(apptDiv, w, h); 1427 }; 1428 1429 ZmCalColView.prototype._layoutAppts = 1430 function() { 1431 // for starting x and width 1432 var data = this._hours[0]; 1433 1434 for (var i=0; i < this._layouts.length; i++) { 1435 var layout = this._layouts[i]; 1436 var apptDiv = document.getElementById(this._getItemId(layout.appt)); 1437 if (apptDiv) { 1438 layout.bounds = this._getBoundsForAppt(layout.appt); 1439 if (!layout.bounds) { continue; } 1440 var w = Math.floor(layout.bounds.width*ZmCalColView._getApptWidthPercent(layout.maxcol+1)); 1441 var xinc = layout.maxcol ? ((layout.bounds.width - w) / layout.maxcol) : 0; // n-1 1442 var x = xinc * layout.col + (layout.bounds.x); 1443 this._layoutAppt(layout.appt, apptDiv, x, layout.bounds.y, w, layout.bounds.height); 1444 } 1445 } 1446 }; 1447 1448 ZmCalColView.prototype._getDayForDate = 1449 function(d) { 1450 return this._dateToDayIndex[this._dayKey(d)]; 1451 }; 1452 1453 ZmCalColView.prototype._getColForFolderId = 1454 function(folderId) { 1455 return this._folderIdToColIndex[folderId]; 1456 }; 1457 1458 ZmCalColView.prototype._getColFromX = 1459 function(x) { 1460 var num = this._columns.length; 1461 for (var i =0; i < num; i++) { 1462 var col = this._columns[i]; 1463 if (x >= col.apptX && x <= col.apptX+col.apptWidth) return col; 1464 } 1465 return null; 1466 }; 1467 1468 ZmCalColView.prototype._getLocationForDate = 1469 function(d) { 1470 var h = d.getHours(); 1471 var m = d.getMinutes(); 1472 var day = this._getDayForDate(d); 1473 if (day == null) return null; 1474 return new DwtPoint(day.apptX, Math.floor(((h+m/60) * ZmCalColView._HOUR_HEIGHT))+1); 1475 }; 1476 1477 ZmCalColView.prototype._getBoundsForAppt = 1478 function(appt) { 1479 var sd = appt.startDate; 1480 var endOfDay = new Date(sd); 1481 endOfDay.setHours(23,59,59,999); 1482 var endDate = new Date(appt.endDate); 1483 endDate.setHours(0,0,0,0); 1484 var endTime = appt.getEndTime(); 1485 if(appt.startDate.getTime()==endDate.getTime()){ 1486 var diffOffset = appt.checkDSTChangeOnEndDate(); 1487 endTime = endTime + (diffOffset*60*1000); 1488 } 1489 var et = Math.min(endTime, endOfDay.getTime()); 1490 1491 if (this._scheduleMode) 1492 return this._getBoundsForCalendar(sd, et - sd.getTime(), appt.folderId); 1493 else 1494 return this._getBoundsForDate(sd, et - sd.getTime()); 1495 }; 1496 1497 ZmCalColView.prototype._getBoundsForDate = 1498 function(d, duration, col) { 1499 var durationMinutes = duration / 1000 / 60; 1500 durationMinutes = Math.max(durationMinutes, 22); 1501 var h = d.getHours(); 1502 var m = d.getMinutes(); 1503 if (col == null && !this._scheduleMode) { 1504 var day = this._getDayForDate(d); 1505 col = day ? this._columns[day.index] : null; 1506 } 1507 if (col == null) return null; 1508 return new DwtRectangle(col.apptX, ((h+m/60) * ZmCalColView._HOUR_HEIGHT), 1509 col.apptWidth, (ZmCalColView._HOUR_HEIGHT / 60) * durationMinutes); 1510 }; 1511 1512 ZmCalColView.prototype._getBoundsForCalendar = 1513 function(d, duration, folderId) { 1514 var durationMinutes = duration / 1000 / 60; 1515 durationMinutes = Math.max(durationMinutes, 22); 1516 var h = d.getHours(); 1517 var m = d.getMinutes(); 1518 var col= this._getColForFolderId(folderId); 1519 if (col == null) return null; 1520 return new DwtRectangle(col.apptX, ((h+m/60) * ZmCalColView._HOUR_HEIGHT), 1521 col.apptWidth, (ZmCalColView._HOUR_HEIGHT / 60) * durationMinutes); 1522 }; 1523 1524 ZmCalColView.prototype._getBoundsForAllDayDate = 1525 function(startSnap, endSnap, useYPadding) { 1526 if (startSnap == null || endSnap == null) return null; 1527 var yOffset = useYPadding ? ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD + 2 : 0; 1528 return new DwtRectangle(startSnap.col.allDayX, yOffset, 1529 (endSnap.col.allDayX + endSnap.col.allDayWidth) - startSnap.col.allDayX - this._daySepWidth-1, 1530 ZmCalColView._ALL_DAY_APPT_HEIGHT); 1531 }; 1532 1533 // snapXY coord to specified minute boundary (15,30) 1534 // return x, y, col 1535 ZmCalColView.prototype._snapXY = 1536 function(x, y, snapMinutes, roundUp) { 1537 // snap it to grid 1538 var col = this._getColFromX(x); 1539 if (col == null) return null; 1540 x = col.apptX; 1541 var height = (snapMinutes/60) * ZmCalColView._HOUR_HEIGHT; 1542 y = Math.floor(y/height) * height; 1543 if (roundUp) y += height; 1544 return {x:x, y:y, col:col}; 1545 }; 1546 1547 // snapXY coord to specified minute boundary (15,30) 1548 // return x, y, col 1549 ZmCalColView.prototype._snapAllDayXY = 1550 function(x, y) { 1551 // snap it to grid 1552 var col = this._getColFromX(x); 1553 if (col == null) return null; 1554 x = col.allDayX; 1555 return {x:x, y:0, col:col}; 1556 }; 1557 1558 ZmCalColView.prototype._snapAllDayOutsideGrid = 1559 function(x) { 1560 var colWidth = this._columns[0].allDayWidth + this._daySepWidth; 1561 var colIndex = Math.floor(x/colWidth); 1562 var colX = (colIndex * colWidth) + 2; 1563 return {x:colX, y:0, col:{index:colIndex}}; 1564 } 1565 1566 // Generate a date (time hour/min/sec == 0) from an arbitrary index 1567 // i.e. an index that may not have a col object 1568 ZmCalColView.prototype._createAllDayDateFromIndex = 1569 function(colIndex) { 1570 var dayIndex = this._columns[0].dayIndex; 1571 var day = this._days[dayIndex]; 1572 return new Date(day.date.getTime() + (AjxDateUtil.MSEC_PER_DAY * colIndex)); 1573 } 1574 1575 ZmCalColView.prototype._getDateFromXY = 1576 function(x, y, snapMinutes, roundUp) { 1577 var col = this._getColFromX(x); 1578 if (col == null) return null; 1579 var minutes = Math.floor((y / ZmCalColView._HOUR_HEIGHT) * 60); 1580 if (snapMinutes != null && snapMinutes > 1) { 1581 minutes = Math.floor(minutes/snapMinutes) * snapMinutes; 1582 if (roundUp) minutes += snapMinutes; 1583 } 1584 var day = this._days[col.dayIndex]; 1585 if (day == null) return null; 1586 return new Date(day.date.getTime() + (minutes * 60 * 1000)); 1587 }; 1588 1589 ZmCalColView.prototype._getAllDayDateFromXY = 1590 function(x, y) { 1591 var col = this._getColFromX(x); 1592 if (col == null) return null; 1593 var day = this._days[col.dayIndex]; 1594 if (day == null) return null; 1595 return new Date(day.date.getTime()); 1596 }; 1597 1598 // helper function to minimize code and catch errors 1599 ZmCalColView.prototype._setBounds = 1600 function(id, x, y, w, h) { 1601 var el = typeof id === 'string' ? document.getElementById(id) : id; 1602 if (el == null) { 1603 DBG.println("ZmCalColView._setBounds null element for id: "+id); 1604 } else { 1605 Dwt.setBounds(el, x, y, w, h); 1606 } 1607 }; 1608 1609 ZmCalColView.prototype._calcColWidth = 1610 function(bodyWidth, numCols, horzScroll) { 1611 // var sbwfudge = (AjxEnv.isIE ? 1 : 0) + (horzScroll ? 0 : Dwt.SCROLLBAR_WIDTH); 1612 var sbwfudge = 0; 1613 return dayWidth = Math.floor((bodyWidth-sbwfudge)/numCols) - (this._daySepWidth == 1 ? 0 : 1); 1614 }; 1615 1616 ZmCalColView.prototype._calcMinBodyWidth = 1617 function(width, numCols) { 1618 //return minWidth = (ZmCalColView.MIN_COLUMN_WIDTH * numCols) + (this._daySepWidth == 1 ? 0 : 1); 1619 return minWidth = (ZmCalColView.MIN_COLUMN_WIDTH + (this._daySepWidth == 1 ? 0 : 1)) * numCols; 1620 }; 1621 1622 ZmCalColView.prototype._layout = 1623 function(refreshApptLayout) { 1624 DBG.println(AjxDebug.DBG2, "ZmCalColView in layout!"); 1625 this._updateDays(); 1626 1627 var numCols = this._columns.length; 1628 1629 var sz = this.getSize(true); //get the size from the style - it's more accurate as it's exactly what it was set for 1630 if (!sz) { 1631 return; 1632 } 1633 1634 var width = sz.x + (this._isRight ? -2 : 0); // -2 is an adjustment due to some problem I can't figure out exactly. bug 75115 1635 var height = sz.y; 1636 1637 if (width == 0 || height == 0) { return; } 1638 1639 this._needFirstLayout = false; 1640 1641 var hoursWidth = ZmCalColView._HOURS_DIV_WIDTH; 1642 1643 var bodyX = hoursWidth + this._daySepWidth; 1644 var unionX = bodyX; 1645 if (this._scheduleMode) { 1646 bodyX += ZmCalColView._UNION_DIV_WIDTH + this._daySepWidth; 1647 } 1648 1649 // compute height for hours/grid 1650 this._bodyDivWidth = width - bodyX; 1651 1652 // size appts divs 1653 this._apptBodyDivHeight = ZmCalColView._DAY_HEIGHT + 1; // extra for midnight to show up 1654 this._apptBodyDivWidth = Math.max(this._bodyDivWidth, this._calcMinBodyWidth(this._bodyDivWidth, numCols)); 1655 var needHorzScroll = this._apptBodyDivWidth > this._bodyDivWidth; 1656 1657 1658 this._horizontalScrollbar(needHorzScroll); 1659 var sbwfudge = AjxEnv.isIE ? 1 : 0; 1660 var dayWidth = this._calcColWidth(this._apptBodyDivWidth - Dwt.SCROLLBAR_WIDTH, numCols); 1661 1662 if (needHorzScroll) this._apptBodyDivWidth -= 18; 1663 var scrollFudge = needHorzScroll ? 20 : 0; // need all day to be a little wider then grid 1664 1665 // year heading 1666 this._setBounds(this._yearHeadingDivId, 0, 0, hoursWidth, Dwt.DEFAULT); 1667 1668 // column headings 1669 var allDayHeadingDiv = document.getElementById(this._allDayHeadingDivId); 1670 Dwt.setBounds(allDayHeadingDiv, 0, 0, this._apptBodyDivWidth + scrollFudge, Dwt.DEFAULT); 1671 var allDayHeadingDivHeight = Dwt.getSize(allDayHeadingDiv).y; 1672 1673 // div for all day appts 1674 var numRows = this._allDayApptsRowLayouts ? (this._allDayApptsRowLayouts.length) : 1; 1675 if (this._allDayApptsList && this._allDayApptsList.length > 0) numRows++; 1676 this._allDayFullDivHeight = (ZmCalColView._ALL_DAY_APPT_HEIGHT+ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD) * numRows + ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD; 1677 1678 var percentageHeight = (this._allDayFullDivHeight/height)*100; 1679 this._allDayDivHeight = this._allDayFullDivHeight; 1680 1681 // if height overflows more than 50% of full height set its height 1682 // to nearest no of rows which occupies less than 50% of total height 1683 if (percentageHeight > 50) { 1684 var nearestNoOfRows = Math.floor((0.50*height-ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD)/(ZmCalColView._ALL_DAY_APPT_HEIGHT+ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD)); 1685 this._allDayDivHeight = (ZmCalColView._ALL_DAY_APPT_HEIGHT+ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD) * nearestNoOfRows + ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD; 1686 } 1687 1688 this._setBounds(this._allDayApptScrollDivId, bodyX, allDayHeadingDivHeight, this._bodyDivWidth, this._allDayDivHeight); 1689 this._setBounds(this._allDayDivId, 0, 0, this._apptBodyDivWidth + scrollFudge, this._allDayFullDivHeight); 1690 1691 this._allDayVerticalScrollbar(this._allDayDivHeight != this._allDayFullDivHeight); 1692 1693 // div under year 1694 this._setBounds(this._yearAllDayDivId, 0, allDayHeadingDivHeight, hoursWidth, this._allDayDivHeight); 1695 1696 // all day scroll 1697 var allDayScrollHeight = allDayHeadingDivHeight + this._allDayDivHeight; 1698 this._setBounds(this._allDayScrollDivId, bodyX, 0, this._bodyDivWidth, allDayScrollHeight); 1699 1700 // vert sep between year and all day headings 1701 this._setBounds(this._leftAllDaySepDivId, hoursWidth, 0, this._daySepWidth, allDayScrollHeight); 1702 1703 // horiz separator between all day appts and grid 1704 this._setBounds(this._allDaySepDivId, 0, (this._hideAllDayAppt ? ZmCalColView._DAY_HEADING_HEIGHT : allDayScrollHeight), width, ZmCalColView._ALL_DAY_SEP_HEIGHT); 1705 1706 var bodyY = (this._hideAllDayAppt ? ZmCalColView._DAY_HEADING_HEIGHT : allDayScrollHeight) + ZmCalColView._ALL_DAY_SEP_HEIGHT + (AjxEnv.isIE ? 0 : 2); 1707 1708 this._bodyDivHeight = height - bodyY; 1709 1710 // hours 1711 this._setBounds(this._hoursScrollDivId, 0, bodyY, hoursWidth, this._bodyDivHeight); 1712 1713 // vert sep between hours and grid 1714 this._setBounds(this._leftApptSepDivId, hoursWidth, bodyY, this._daySepWidth, ZmCalColView._DAY_HEIGHT); 1715 1716 // div for scrolling grid 1717 this._setBounds(this._bodyDivId, bodyX, bodyY, this._bodyDivWidth, this._bodyDivHeight); 1718 1719 this._setBounds(this._apptBodyDivId, 0, -1, this._apptBodyDivWidth, this._apptBodyDivHeight); 1720 1721 if (this._scheduleMode) { 1722 //heading 1723 this._setBounds(this._unionHeadingDivId, unionX, 0, ZmCalColView._UNION_DIV_WIDTH, Dwt.DEFAULT); 1724 1725 //div under heading 1726 this._setBounds(this._unionAllDayDivId, unionX, allDayHeadingDivHeight, ZmCalColView._UNION_DIV_WIDTH, this._allDayDivHeight); 1727 1728 // sep in all day area 1729 var unionSepX = unionX + ZmCalColView._UNION_DIV_WIDTH; 1730 this._setBounds(this._unionHeadingSepDivId, unionSepX, 0, this._daySepWidth, allDayScrollHeight); 1731 1732 // div for scrolling union 1733 this._setBounds(this._unionGridScrollDivId, unionX, bodyY, ZmCalColView._UNION_DIV_WIDTH, this._bodyDivHeight); 1734 this._setBounds(this._unionGridDivId, 0, -1, ZmCalColView._UNION_DIV_WIDTH, this._apptBodyDivHeight+ZmCalColView._HOUR_HEIGHT); 1735 1736 // sep in grid area 1737 this._setBounds(this._unionGridSepDivId, unionSepX, bodyY, this._daySepWidth, this._apptBodyDivHeight); 1738 } 1739 1740 this.layoutWorkingHours(this.workingHours); 1741 this._layoutAllDayAppts(); 1742 1743 this._apptBodyDivOffset = Dwt.toWindow(document.getElementById(this._apptBodyDivId), 0, 0, null, true); 1744 this._apptAllDayDivOffset = Dwt.toWindow(document.getElementById(this._allDayDivId), 0, 0, null, true); 1745 1746 if (this._scheduleMode || refreshApptLayout) { 1747 this._layoutAppts(); 1748 this._checkForOffscreenAppt(document.getElementById(this._bodyDivId)); 1749 if (this._scheduleMode) { 1750 this._layoutUnionData(); 1751 } 1752 } 1753 }; 1754 1755 ZmCalColView.prototype.getPostionForWorkingHourDiv = 1756 function(dayIndex, workingHourIndex){ 1757 dayIndex = dayIndex || 0; 1758 workingHourIndex = workingHourIndex || 0; 1759 var workingHrs = this.workingHours[dayIndex], 1760 startTime = workingHrs.startTime[workingHourIndex], 1761 endTime = workingHrs.endTime[workingHourIndex], 1762 startMin = (startTime%100)/15, 1763 endMin = (endTime%100)/15, 1764 startWorkingHour = 2 * Math.floor(startTime/100), 1765 endWorkingHour = 2 * Math.floor(endTime/100), 1766 fifteenMinHeight = ZmCalColView.HALF_HOUR_HEIGHT/2, 1767 topPosition = startWorkingHour*ZmCalColView.HALF_HOUR_HEIGHT, 1768 bottomPosition = endWorkingHour*ZmCalColView.HALF_HOUR_HEIGHT, 1769 workingDivHeight = bottomPosition - topPosition;//duration*halfHourHeight; 1770 return { 1771 topPosition : topPosition, 1772 workingDivHeight: workingDivHeight, 1773 startMinAdjust : startMin * fifteenMinHeight, 1774 endMinAdjust : endMin * fifteenMinHeight 1775 }; 1776 }; 1777 1778 ZmCalColView.prototype.layoutWorkingHoursDiv = 1779 function(divId, pos, currentX, dayWidth){ 1780 this._setBounds(divId, currentX, pos.topPosition+pos.startMinAdjust, dayWidth, pos.workingDivHeight+pos.endMinAdjust-pos.startMinAdjust); 1781 this._setBounds(document.getElementById(divId).firstChild, 0, -pos.startMinAdjust, dayWidth, pos.workingDivHeight+pos.endMinAdjust); 1782 }; 1783 1784 ZmCalColView.prototype.layoutWorkingHours = 1785 function(workingHours){ 1786 if(!workingHours) { 1787 workingHours = ZmCalBaseView.parseWorkingHours(ZmCalBaseView.getWorkingHours()); 1788 this.workingHours = workingHours; 1789 } 1790 var numCols = this._columns.length; 1791 var dayWidth = this._calcColWidth(this._apptBodyDivWidth - Dwt.SCROLLBAR_WIDTH, numCols); 1792 1793 var allDayHeadingDiv = document.getElementById(this._allDayHeadingDivId); 1794 var allDayHeadingDivHeight = Dwt.getSize(allDayHeadingDiv).y; 1795 1796 var currentX = 0; 1797 1798 for (var i = 0; i < numCols; i++) { 1799 var col = this._columns[i]; 1800 1801 // position day heading 1802 var day = this._days[col.dayIndex]; 1803 this._setBounds(col.titleId, currentX+1, Dwt.DEFAULT, dayWidth, ZmCalColView._DAY_HEADING_HEIGHT); 1804 col.apptX = currentX + 2 ; //ZZZ 1805 col.apptWidth = dayWidth - this._daySepWidth - 3; //ZZZZ 1806 col.allDayX = col.apptX; 1807 col.allDayWidth = dayWidth; // doesn't include sep 1808 1809 //split into half hrs sections 1810 var dayIndex = day.date.getDay(), 1811 workingHrs = this.workingHours[dayIndex], 1812 pos = this.getPostionForWorkingHourDiv(dayIndex, 0); 1813 1814 if(day.isWorkingDay) { 1815 if(!this._scheduleMode) { 1816 this.layoutWorkingHoursDiv(col.workingHrsFirstDivId, pos, currentX, dayWidth); 1817 1818 if( workingHrs.startTime.length >= 2 && 1819 workingHrs.endTime.length >= 2) { 1820 1821 pos = this.getPostionForWorkingHourDiv(dayIndex, 1); 1822 this.layoutWorkingHoursDiv(col.workingHrsSecondDivId, pos, currentX, dayWidth); 1823 } 1824 } 1825 if(this._scheduleMode) { 1826 this.layoutWorkingHoursDiv(this._workingHrsFirstDivId, pos, 0, dayWidth); 1827 1828 if( workingHrs.startTime.length >= 2 && 1829 workingHrs.endTime.length >= 2) { 1830 1831 pos = this.getPostionForWorkingHourDiv(dayIndex, 1); 1832 this.layoutWorkingHoursDiv(this._workingHrsSecondDivId, pos, 0, dayWidth); 1833 1834 } 1835 } 1836 } 1837 currentX += dayWidth; 1838 1839 this._setBounds(col.headingDaySepDivId, currentX, 0, this._daySepWidth, allDayHeadingDivHeight + this._allDayDivHeight); 1840 this._setBounds(col.daySepDivId, currentX, 0, this._daySepWidth, this._apptBodyDivHeight); 1841 currentX += this._daySepWidth; 1842 } 1843 }; 1844 1845 // Must remain in sync with layoutWorkingHours 1846 ZmCalColView.prototype._calculateColumnApptLeft = 1847 function(index, dayWidth, numDays) { 1848 if (index < 0) { 1849 numDays = 0; 1850 } else { 1851 numDays -= 1; 1852 } 1853 return (dayWidth * index) + (this._daySepWidth * numDays) + 2; 1854 } 1855 1856 1857 //Free Busy Bar 1858 1859 ZmCalColView.prototype._layoutFBBar = 1860 function(){ 1861 //Fetch FB Data from GetFreeBusyRequest 1862 var date = this._getDayForDate(this._date); 1863 var startDate = date ? date.date : this._date; 1864 var endDate = date ? date.endDate : null; 1865 this.getFreeBusyInfo(startDate, endDate, new AjxCallback(this, this._handleFBResponse)); 1866 }; 1867 1868 ZmCalColView.prototype._handleFBResponse = 1869 function(result){ 1870 var statusSlots = result.getResponse().GetFreeBusyResponse.usr; 1871 statusSlots = statusSlots[0]; // 1 User for Calendar View 1872 1873 1874 //Prepare UI 1875 var hoursDiv = document.getElementById(this._hoursScrollDivId); 1876 if(!this._fbBarSlots){ 1877 var div = document.createElement("DIV"); 1878 //div.style.backgroundColor = "#EFE7D4"; 1879 if (hoursDiv) { 1880 hoursDiv.appendChild(div); 1881 Dwt.setPosition(div, Dwt.ABSOLUTE_STYLE); 1882 this._fbBarSlots = div; 1883 this._fbBarSlotsId = div.id = Dwt.getNextId(); 1884 } 1885 } 1886 1887 //Calculate X, Y 1888 if (hoursDiv) { 1889 var hourScrollDivLoc = Dwt.getLocation(hoursDiv); 1890 var x = hourScrollDivLoc.x; 1891 x = x + (ZmCalColView._HOURS_DIV_WIDTH - ZmCalColView._FBBAR_DIV_WIDTH + 1); 1892 Dwt.setLocation(this._fbBarSlots, x, 0); 1893 1894 //Set Ht./ Width 1895 var calBodyHt = document.getElementById(this._bodyDivId).scrollHeight; 1896 Dwt.setSize(this._fbBarSlots, ZmCalColView._FBBAR_DIV_WIDTH - 2, calBodyHt); 1897 1898 //Cleanup Existing Slots 1899 this._fbBarSlots.innerHTML = ""; 1900 1901 //Handle Slots 1902 if(statusSlots.t) this._drawSlots(ZmCalColView._STATUS_TENTATIVE, statusSlots.t); 1903 if(statusSlots.b) this._drawSlots(ZmCalColView._STATUS_BUSY, statusSlots.b); 1904 if(statusSlots.o) this._drawSlots(ZmCalColView._STATUS_OOO, statusSlots.o); 1905 if(statusSlots.u) this._drawSlots(ZmCalColView._STATUS_OOO, statusSlots.u); 1906 //non tentative/busy/ooo are all free, dont handle them 1907 //if(statusSlots.f) this._drawSlots(ZmCalColView._STATUS_FREE, statusSlots.f); 1908 } 1909 }; 1910 1911 ZmCalColView.prototype._drawSlots = 1912 function(status, slots){ 1913 1914 //Slots 1915 var currDate = this._timeRangeStart; 1916 var calBodyHt = document.getElementById(this._bodyDivId).scrollHeight; 1917 1918 for(var i=0; i<slots.length; i++){ 1919 var slot = slots[i]; 1920 var start = slot.s; 1921 var end = slot.e; 1922 if(end > currDate + AjxDateUtil.MSEC_PER_DAY){ 1923 end = currDate + AjxDateUtil.MSEC_PER_DAY; 1924 } 1925 if(start < currDate){ 1926 start = currDate; 1927 } 1928 1929 start = new Date(start); 1930 end = new Date(end); 1931 1932 start = start.getHours()*60 + start.getMinutes(); 1933 end = end.getHours()*60 + end.getMinutes(); 1934 1935 var startPx = Math.floor(start * (calBodyHt / ( 24 * 60))); 1936 var endPx = Math.floor(end * ( calBodyHt / (24 * 60))); 1937 1938 var div = document.createElement("DIV"); 1939 div.className = this._getFBBarSlotColor(status); 1940 Dwt.setPosition(div, Dwt.ABSOLUTE_STYLE); 1941 this._fbBarSlots.appendChild(div); 1942 div.style.top = ( startPx - 2 ) + "px"; 1943 div.style.height = ( endPx - startPx) + "px"; 1944 div.style.width = ZmCalColView._FBBAR_DIV_WIDTH - 2; 1945 } 1946 1947 }; 1948 1949 ZmCalColView.prototype._getFBBarSlotColor = 1950 function(status){ 1951 switch(status){ 1952 case ZmCalColView._STATUS_FREE: return "ZmFBBar-free"; 1953 case ZmCalColView._STATUS_TENTATIVE: return "ZmFBBar-tentative"; 1954 case ZmCalColView._STATUS_BUSY: return "ZmFBBar-busy"; 1955 case ZmCalColView._STATUS_OOO: return "ZmFBBar-ooo"; 1956 } 1957 return "ZmFBBar-busy"; 1958 }; 1959 1960 ZmCalColView.prototype.getFreeBusyInfo = 1961 function(startTime, endTime , callback, errorCallback) { 1962 1963 if(startTime instanceof Date) 1964 startTime = startTime.getTime(); 1965 1966 if(endTime instanceof Date) 1967 endTime = endTime.getTime(); 1968 1969 endTime = endTime || (startTime + AjxDateUtil.MSEC_PER_DAY ); 1970 var email = appCtxt.getActiveAccount().getEmail(); 1971 1972 var soapDoc = AjxSoapDoc.create("GetFreeBusyRequest", "urn:zimbraMail"); 1973 soapDoc.setMethodAttribute("s", startTime); 1974 soapDoc.setMethodAttribute("e", endTime); 1975 soapDoc.setMethodAttribute("uid", email); 1976 1977 return appCtxt.getAppController().sendRequest({ 1978 soapDoc: soapDoc, 1979 asyncMode: true, 1980 callback: callback, 1981 errorCallback: errorCallback, 1982 noBusyOverlay: true 1983 }); 1984 }; 1985 1986 ZmCalColView.prototype._isFBBarDiv = 1987 function(ev){ 1988 var target = DwtUiEvent.getTargetWithProp(ev, "id"); 1989 if(target.id == this._fbBarSlotsId){ 1990 return true; 1991 } 1992 return false; 1993 }; 1994 1995 ZmCalColView.prototype._getFBBarToolTipContent = 1996 function(ev){ 1997 var target = DwtUiEvent.getTarget(ev); 1998 var className = target.className; 1999 if(/-busy$/.test(className)) 2000 return ZmMsg.busy; 2001 if(/-tentative$/.test(className)) 2002 return ZmMsg.tentative; 2003 if(/-ooo$/.test(className)) 2004 return ZmMsg.outOfOffice; 2005 return ZmMsg.free; 2006 }; 2007 2008 ZmCalColView.prototype._getUnionToolTip = 2009 function(i) { 2010 // cache it... 2011 var tooltip = this._unionBusyDataToolTip[i]; 2012 if (tooltip) { return tooltip; } 2013 2014 var data = this._unionBusyData[i]; 2015 if (!data instanceof Object) return null; 2016 2017 var html = new AjxBuffer(); 2018 html.append("<table cellpadding=2 cellspacing=0 border=0>"); 2019 var checkedCals = this._controller.getCheckedCalendarFolderIds(); 2020 for (var i = 0; i < checkedCals.length; i++) { 2021 var fid = checkedCals[i]; 2022 if (data[fid]) { 2023 var cal = this._controller.getCalendar(fid); 2024 if (cal) { 2025 var color = ZmCalendarApp.COLORS[cal.color]; 2026 html.append("<tr valign='center' class='", color, "Bg'><td>", AjxImg.getImageHtml(cal.getIcon()), "</td>"); 2027 html.append("<td>", AjxStringUtil.htmlEncode(cal.getName()), "</td></tr>"); 2028 } 2029 } 2030 } 2031 html.append("</table>"); 2032 tooltip = this._unionBusyDataToolTip[i] = html.toString(); 2033 return tooltip; 2034 }; 2035 2036 ZmCalColView.prototype._layoutUnionDataDiv = 2037 function(gridEl, allDayEl, i, data, numCols) { 2038 var enable = data instanceof Object; 2039 var id = this._unionBusyDivIds[i]; 2040 var divEl = null; 2041 2042 if (id == null) { 2043 if (!enable) { return; } 2044 id = this._unionBusyDivIds[i] = Dwt.getNextId(); 2045 var divEl = document.createElement("div"); 2046 divEl.style.position = 'absolute'; 2047 divEl.className = "calendar_sched_union_div"; 2048 this.associateItemWithElement(null, divEl, ZmCalBaseView.TYPE_SCHED_FREEBUSY, id, {index:i}); 2049 2050 Dwt.setOpacity(divEl, 40); 2051 2052 if (i == 48) { 2053 //have to resize every layout, since all day div height might change 2054 allDayEl.appendChild(divEl); 2055 } else { 2056 // position/size once right here! 2057 Dwt.setBounds(divEl, 2, ZmCalColView._HALF_HOUR_HEIGHT*i+1, ZmCalColView._UNION_DIV_WIDTH-4 , ZmCalColView._HALF_HOUR_HEIGHT-2); 2058 gridEl.appendChild(divEl); 2059 } 2060 2061 } else { 2062 divEl = document.getElementById(id); 2063 } 2064 // have to relayout each time 2065 if (i == 48) Dwt.setBounds(divEl, 1, 1, ZmCalColView._UNION_DIV_WIDTH-2, this._allDayDivHeight-2); 2066 2067 var num = 0; 2068 for (var key in data) num++; 2069 2070 Dwt.setOpacity(divEl, 20 + (60 * (num/numCols))); 2071 Dwt.setVisibility(divEl, enable); 2072 }; 2073 2074 ZmCalColView.prototype._layoutUnionData = 2075 function() { 2076 if (!this._unionBusyData) { return; } 2077 2078 var gridEl = document.getElementById(this._unionGridDivId); 2079 var allDayEl = document.getElementById(this._unionAllDayDivId); 2080 var numCols = this._columns.length; 2081 for (var i=0; i < 49; i++) { 2082 this._layoutUnionDataDiv(gridEl, allDayEl, i, this._unionBusyData[i], numCols); 2083 } 2084 }; 2085 2086 ZmCalColView.prototype._handleApptScrollRegion = 2087 function(docX, docY, incr, data) { 2088 var offset = 0; 2089 var upper = docY < this._apptBodyDivOffset.y; 2090 // Trigger scroll when scroll is within 8 px of the bottom 2091 var lower = docY > this._apptBodyDivOffset.y+this._bodyDivHeight - 8; 2092 if (upper || lower) { 2093 var div = document.getElementById(this._bodyDivId); 2094 var sTop = div.scrollTop; 2095 if (upper && sTop > 0) { 2096 offset = -(sTop > incr ? incr : sTop); 2097 } else if (lower) { 2098 var sVisibleTop = this._apptBodyDivHeight - this._bodyDivHeight; 2099 if (sTop < sVisibleTop) { 2100 var spaceLeft = sVisibleTop - sTop; 2101 offset = spaceLeft > incr ?incr : spaceLeft; 2102 } 2103 } 2104 if (offset != 0) { 2105 div.scrollTop += offset; 2106 this._syncScroll(); 2107 } 2108 if (data) { 2109 data.docY -= offset; 2110 } 2111 } 2112 return offset; 2113 }; 2114 2115 ZmCalColView.prototype._controlListener = 2116 function(ev) { 2117 if (ev.newWidth == Dwt.DEFAULT && ev.newHeight == Dwt.DEFAULT) return; 2118 try { 2119 if ((ev.oldWidth != ev.newWidth) || (ev.oldHeight != ev.newHeight)) { 2120 this._layout(true); 2121 this._updateTimeIndicator(); 2122 } 2123 } catch(ex) { 2124 DBG.dumpObj(ex); 2125 } 2126 }; 2127 2128 ZmCalColView.prototype._apptSelected = 2129 function() { 2130 // 2131 }; 2132 2133 ZmCalColView._ondblclickHandler = 2134 function (ev){ 2135 ev = DwtUiEvent.getEvent(ev); 2136 ev._isDblClick = true; 2137 ZmCalColView._onclickHandler(ev); 2138 }; 2139 2140 ZmCalColView.prototype._mouseOverAction = 2141 function(ev, div) { 2142 var type = this._getItemData(div, "type"); 2143 if (type == ZmCalBaseView.TYPE_DAY_HEADER) { 2144 div.style.textDecoration = "underline"; 2145 } 2146 }; 2147 2148 ZmCalColView.prototype.getToolTipContent = 2149 function(ev) { 2150 if(this._fbBarEnabled && this._isFBBarDiv(ev)){ 2151 return this._getFBBarToolTipContent(ev); 2152 } 2153 var div = this.getTargetItemDiv(ev); 2154 var type = this._getItemData(div, "type"); 2155 if (type == ZmCalBaseView.TYPE_SCHED_FREEBUSY) { 2156 var index = this._getItemData(div, "index"); 2157 return this._getUnionToolTip(index); 2158 } 2159 return ZmCalBaseView.prototype.getToolTipContent.apply(this, arguments); 2160 }; 2161 2162 ZmCalColView.prototype._mouseOutAction = 2163 function(ev, div) { 2164 ZmCalBaseView.prototype._mouseOutAction.call(this, ev, div); 2165 var type = this._getItemData(div, "type"); 2166 if (type == ZmCalBaseView.TYPE_DAY_HEADER) { 2167 div.style.textDecoration = "none"; 2168 } else if (type == ZmCalBaseView.TYPE_SCHED_FREEBUSY) { 2169 this.setToolTipContent(null); 2170 } 2171 }; 2172 2173 ZmCalColView.prototype._mouseUpAction = 2174 function(ev, div) { 2175 2176 if (Dwt.ffScrollbarCheck(ev)) { return false; } 2177 2178 var type = this._getItemData(div, "type"); 2179 if (type == ZmCalBaseView.TYPE_DAY_HEADER && !this._scheduleMode && ! this._isInviteMessage) { 2180 var dayIndex = this._getItemData(div, "dayIndex"); 2181 var date = this._days[dayIndex].date; 2182 var cc = appCtxt.getCurrentController(); 2183 2184 if (this.numDays > 1) { 2185 cc.setDate(date); 2186 cc.show(ZmId.VIEW_CAL_DAY); 2187 } else { 2188 // TODO: use pref for work week 2189 if (date.getDay() > 0 && date.getDay() < 6) 2190 cc.show(ZmId.VIEW_CAL_WORK_WEEK); 2191 else 2192 cc.show(ZmId.VIEW_CAL_WEEK); 2193 } 2194 } else if (type == ZmCalBaseView.TYPE_DAY_SEP) { 2195 this.toggleAllDayAppt(!this._hideAllDayAppt); 2196 } 2197 }; 2198 2199 ZmCalColView.prototype._doubleClickAction = 2200 function(ev, div) { 2201 ZmCalBaseView.prototype._doubleClickAction.call(this, ev, div); 2202 var type = this._getItemData(div, "type"); 2203 if (type == ZmCalBaseView.TYPE_APPTS_DAYGRID || 2204 type == ZmCalBaseView.TYPE_ALL_DAY) 2205 { 2206 this._timeSelectionAction(ev, div, true); 2207 } 2208 }; 2209 2210 ZmCalColView.prototype._timeSelectionAction = 2211 function(ev, div, dblclick) { 2212 var date; 2213 var duration = AjxDateUtil.MSEC_PER_HALF_HOUR; 2214 var isAllDay = false; 2215 var gridLoc; 2216 var type = this._getItemData(div, "type"); 2217 switch (type) { 2218 case ZmCalBaseView.TYPE_APPTS_DAYGRID: 2219 gridLoc = Dwt.toWindow(ev.target, ev.elementX, ev.elementY, div, true); 2220 date = this._getDateFromXY(gridLoc.x, gridLoc.y, 30); 2221 break; 2222 case ZmCalBaseView.TYPE_ALL_DAY: 2223 gridLoc = Dwt.toWindow(ev.target, ev.elementX, ev.elementY, div, true); 2224 date = this._getAllDayDateFromXY(gridLoc.x, gridLoc.y); 2225 isAllDay = true; 2226 break; 2227 default: 2228 return; 2229 } 2230 2231 if (date == null) { return false; } 2232 var col = this._getColFromX(gridLoc.x); 2233 var folderId = col ? (col.cal ? col.cal.id : null) : null; 2234 2235 this._timeSelectionEvent(date, duration, dblclick, isAllDay, folderId, ev.shiftKey); 2236 }; 2237 2238 ZmCalColView.prototype._mouseDownAction = 2239 function(ev, div) { 2240 2241 //ZmCalBaseView.prototype._mouseDownAction.call(this, ev, div); 2242 //bug: 57755 - avoid scroll check hack for appt related mouse events 2243 //todo: disable ffScrollbarCheck for 3.6.4+ versions of firefox ( bug 55342 ) 2244 var type = this._getItemData(div, "type"); 2245 if (type != ZmCalBaseView.TYPE_APPT && Dwt.ffScrollbarCheck(ev)) { return false; } 2246 2247 switch (type) { 2248 case ZmCalBaseView.TYPE_APPT_BOTTOM_SASH: 2249 case ZmCalBaseView.TYPE_APPT_TOP_SASH: 2250 this.setToolTipContent(null); 2251 return this._sashMouseDownAction(ev, div); 2252 break; 2253 case ZmCalBaseView.TYPE_APPT: 2254 this.setToolTipContent(null); 2255 return this._apptMouseDownAction(ev, div); 2256 break; 2257 case ZmCalBaseView.TYPE_HOURS_COL: 2258 if (ev.button == DwtMouseEvent.LEFT) { 2259 var gridLoc = AjxEnv.isIE ? Dwt.toWindow(ev.target, ev.elementX, ev.elementY, div, true) : {x: ev.elementX, y: ev.elementY}; 2260 var fakeLoc = this._getLocationForDate(this.getDate()); 2261 if (fakeLoc) { 2262 gridLoc.x = fakeLoc.x; 2263 var gridDiv = document.getElementById(this._apptBodyDivId); 2264 return this._gridMouseDownAction(ev, gridDiv, gridLoc); 2265 } 2266 } else if (ev.button == DwtMouseEvent.RIGHT) { 2267 DwtUiEvent.copy(this._actionEv, ev); 2268 this._actionEv.item = this; 2269 this._evtMgr.notifyListeners(ZmCalBaseView.VIEW_ACTION, this._actionEv); 2270 } 2271 break; 2272 case ZmCalBaseView.TYPE_APPTS_DAYGRID: 2273 if (!appCtxt.isWebClientOffline()) { 2274 this._timeSelectionAction(ev, div, false); 2275 if (ev.button == DwtMouseEvent.LEFT) { 2276 // save grid location here, since timeSelection might move the time selection div 2277 var gridLoc = Dwt.toWindow(ev.target, ev.elementX, ev.elementY, div, true); 2278 return this._gridMouseDownAction(ev, div, gridLoc); 2279 } else if (ev.button == DwtMouseEvent.RIGHT) { 2280 DwtUiEvent.copy(this._actionEv, ev); 2281 this._actionEv.item = this; 2282 this._evtMgr.notifyListeners(ZmCalBaseView.VIEW_ACTION, this._actionEv); 2283 } 2284 } 2285 break; 2286 case ZmCalBaseView.TYPE_ALL_DAY: 2287 this._timeSelectionAction(ev, div, false); 2288 if (ev.button == DwtMouseEvent.LEFT) { 2289 var gridLoc = Dwt.toWindow(ev.target, ev.elementX, ev.elementY, div, true); 2290 return this._gridMouseDownAction(ev, div, gridLoc, true); 2291 } else if (ev.button == DwtMouseEvent.RIGHT) { 2292 DwtUiEvent.copy(this._actionEv, ev); 2293 this._actionEv.item = this; 2294 this._evtMgr.notifyListeners(ZmCalBaseView.VIEW_ACTION, this._actionEv); 2295 } 2296 break; 2297 } 2298 return false; 2299 }; 2300 2301 // BEGIN APPT ACTION HANDLERS 2302 2303 2304 // called when DND is confirmed after threshold 2305 ZmCalColView.prototype._apptDndBegin = 2306 function(data) { 2307 var loc = Dwt.getLocation(data.apptEl); 2308 data.dndObj = {}; 2309 data.apptX = loc.x; 2310 data.apptY = loc.y; 2311 2312 data.apptsDiv = document.getElementById(this._apptBodyDivId); 2313 data.bodyDivEl = document.getElementById(this._bodyDivId); 2314 data.apptBodyEl = document.getElementById(data.apptEl.id + "_body"); 2315 2316 data.startDate = new Date(data.appt.getStartTime()); 2317 data.startTimeEl = document.getElementById(data.apptEl.id +"_st"); 2318 data.endTimeEl = document.getElementById(data.apptEl.id +"_et"); 2319 2320 if (data.appt.isAllDayEvent()) { 2321 data.saveHTML = data.apptEl.innerHTML; 2322 data.saveLoc = loc; 2323 2324 // Adjust apptOffset.x to be the offset from the clicked on column. Then create the 2325 // start snap using this offset (so that start column of a multi-day is tracked). 2326 var leftSnap = this._snapXY(data.apptX, data.apptY, 15); 2327 var colSnap = this._snapXY(data.apptX + data.apptOffset.x, data.apptY, 15); 2328 data.apptOffset.x = data.apptOffset.x - colSnap.x + leftSnap.x; 2329 2330 // Multi day appt may have its start off the grid. It's will be truncated 2331 // by the layout code, so calculate the true start 2332 var dayOffset = this._calcOffsetFromZeroColumn(data.appt.getStartTime()); 2333 // All columns should be the same width. Choose the 0th 2334 var colWidth = this._columns[0].allDayWidth + this._daySepWidth; 2335 2336 var endTime = data.appt.getEndTime(); 2337 var numDays = this._calcNumDays(data.startDate, endTime); 2338 data.apptX = this._calculateColumnApptLeft(dayOffset, colWidth, numDays); 2339 data.snap = this._snapAllDayOutsideGrid(data.apptX + data.apptOffset.x); 2340 data.apptWidth = (colWidth * numDays) - this._daySepWidth - 1; 2341 2342 // Offset the y fudge that is applied in _layout, _positionAppt 2343 data.apptY -= ZmCalColView._APPT_Y_FUDGE; 2344 var bounds = new DwtRectangle(data.apptX, data.apptY, 2345 data.apptWidth, ZmCalColView._ALL_DAY_APPT_HEIGHT); 2346 2347 this._layoutAppt(data.appt, data.apptEl, bounds.x, bounds.y, bounds.width, bounds.height); 2348 2349 data.disableScroll = true; 2350 } else { 2351 data.snap = this._snapXY(data.apptX + data.apptOffset.x, data.apptY, 15); // get orig grid snap 2352 } 2353 2354 if (data.snap == null) return false; 2355 2356 this.deselectAll(); 2357 this.setSelection(data.appt); 2358 Dwt.addClass(data.apptBodyEl, DwtCssStyle.DROPPABLE); 2359 Dwt.setOpacity(data.apptEl, ZmCalColView._OPACITY_APPT_DND); 2360 data.dndStarted = true; 2361 return true; 2362 }; 2363 2364 2365 ZmCalColView.prototype._restoreApptLoc = 2366 function(data) { 2367 if (data && data.appt) { 2368 //Appt move by drag cancelled 2369 var lo = data.appt._layout; 2370 data.view._layoutAppt(null, data.apptEl, lo.x, lo.y, lo.w, lo.h); 2371 if (data.startTimeEl) { 2372 data.startTimeEl.innerHTML = ZmCalBaseItem._getTTHour(data.appt.startDate); 2373 } 2374 if (data.endTimeEl) { 2375 data.endTimeEl.innerHTML = ZmCalBaseItem._getTTHour(data.appt.endDate); 2376 } 2377 ZmCalBaseView._setApptOpacity(data.appt, data.apptEl); 2378 } 2379 else if (data.newApptDivEl) { 2380 // ESC key is pressed while dragging the mouse 2381 // Undo the drag event and hide the new appt div 2382 data.gridEl.style.cursor = 'auto'; 2383 var col = data.view._getColFromX(data.gridX); 2384 data.folderId = col ? (col.cal ? col.cal.id : null) : null; 2385 Dwt.setVisible(data.newApptDivEl, false); 2386 } 2387 }; 2388 2389 2390 ZmCalColView.prototype._restoreAppt = 2391 function(data) { 2392 if (data.appt.isAllDayEvent()) { 2393 Dwt.setLocation(data.apptEl, data.saveLoc.x, data.saveLoc.y); 2394 data.apptEl.innerHTML = data.saveHTML; 2395 } 2396 }; 2397 2398 2399 ZmCalColView.prototype._createContainerRect = 2400 function(data) { 2401 this._containerRect = null; 2402 if (data.appt.isAllDayEvent()) { 2403 this._containerRect = new DwtRectangle(this._apptAllDayDivOffset.x, 2404 this._apptAllDayDivOffset.y, 2405 this._bodyDivWidth, 2406 this._allDayDivHeight + this._bodyDivHeight + ZmCalColView._SCROLL_PRESSURE_FUDGE); 2407 } else { 2408 this._containerRect = new DwtRectangle(this._apptAllDayDivOffset.x, 2409 this._apptBodyDivOffset.y - ZmCalColView._SCROLL_PRESSURE_FUDGE, 2410 this._bodyDivWidth, 2411 this._bodyDivHeight + ZmCalColView._SCROLL_PRESSURE_FUDGE); 2412 } 2413 } 2414 2415 ZmCalColView.prototype._clearSnap = 2416 function(snap) { 2417 snap.x = null; 2418 snap.y = null; 2419 } 2420 2421 2422 ZmCalColView.prototype._restoreHighlight = 2423 function(data) { 2424 Dwt.setOpacity(data.apptEl, ZmCalColView._OPACITY_APPT_DND); 2425 Dwt.addClass(data.apptBodyEl, DwtCssStyle.DROPPABLE); 2426 } 2427 2428 ZmCalColView.prototype._doApptMove = 2429 function(data, deltaX, deltaY) { 2430 // snap new location to grid 2431 var newDate = null; 2432 var snap = data.view._snapXY(data.apptX + data.apptOffset.x + deltaX, data.apptY + deltaY, 15); 2433 if (snap == null) { 2434 if (data.appt.isAllDayEvent()) { 2435 // For a multi day appt , the start snap may have started or be pushed off the grid. 2436 // Create a snap with a pseudo column. 2437 snap = data.view._snapAllDayOutsideGrid(data.apptX + data.apptOffset.x + deltaX); 2438 newDate = data.view._createAllDayDateFromIndex(snap.col.index); 2439 } 2440 } else { 2441 newDate = data.view._getDateFromXY(snap.x, snap.y, 15); 2442 } 2443 2444 //DBG.println("mouseMove new snap: "+snap.x+","+snap.y+ " data snap: "+data.snap.x+","+data.snap.y); 2445 if (snap != null && ((snap.x != data.snap.x || snap.y != data.snap.y))) { 2446 if (newDate != null && 2447 (!(data.view._scheduleMode && snap.col != data.snap.col)) && // don't allow col moves in sched view 2448 (newDate.getTime() != data.startDate.getTime())) 2449 { 2450 var bounds = null; 2451 if (data.appt.isAllDayEvent()) { 2452 // Not using snapXY and GeBoundsForAllDayDate - snap requires that a date 2453 // fall within one of its columns, which may not be so for a multi day appt. 2454 var bounds = new DwtRectangle(snap.x, data.apptY, 2455 data.apptWidth, ZmCalColView._ALL_DAY_APPT_HEIGHT); 2456 } else { 2457 bounds = data.view._getBoundsForDate(newDate, data.appt._orig.getDuration(), snap.col); 2458 } 2459 data.view._layoutAppt(null, data.apptEl, bounds.x, bounds.y, bounds.width, bounds.height); 2460 data.startDate = newDate; 2461 data.snap = snap; 2462 if (data.startTimeEl) data.startTimeEl.innerHTML = ZmCalBaseItem._getTTHour(data.startDate); 2463 if (data.endTimeEl) data.endTimeEl.innerHTML = ZmCalBaseItem._getTTHour(new Date(data.startDate.getTime()+data.appt.getDuration())); 2464 } 2465 } 2466 } 2467 2468 2469 2470 ZmCalColView.prototype._deselectDnDHighlight = 2471 function(data) { 2472 Dwt.delClass(data.apptBodyEl, DwtCssStyle.DROPPABLE); 2473 ZmCalBaseView._setApptOpacity(data.appt, data.apptEl); 2474 } 2475 2476 // END APPT ACTION HANDLERS 2477 2478 // BEGIN SASH ACTION HANDLERS 2479 2480 ZmCalColView.prototype._sashMouseDownAction = 2481 function(ev, sash) { 2482 // DBG.println("ZmCalColView._sashMouseDownHdlr"); 2483 if (ev.button != DwtMouseEvent.LEFT) { 2484 return false; 2485 } 2486 2487 var apptEl = sash.parentNode; 2488 var apptBodyEl = document.getElementById(apptEl.id + "_body"); 2489 2490 var appt = this.getItemFromElement(apptEl); 2491 var origHeight = Dwt.getSize(apptBodyEl).y; 2492 var origLoc = Dwt.getLocation(apptEl); 2493 var parentOrigHeight = Dwt.getSize(apptEl).y; 2494 var type = this._getItemData(sash, "type"); 2495 var isTop = (type == ZmCalBaseView.TYPE_APPT_TOP_SASH); 2496 var data = { 2497 sash: sash, 2498 isTop: isTop, 2499 appt:appt, 2500 view:this, 2501 apptEl: apptEl, 2502 endTimeEl: document.getElementById(apptEl.id +"_et"), 2503 startTimeEl: document.getElementById(apptEl.id +"_st"), 2504 apptBodyEl: apptBodyEl, 2505 origHeight: origHeight, 2506 apptX: origLoc.x, 2507 apptY: origLoc.y, 2508 parentOrigHeight: parentOrigHeight, 2509 startY: ev.docY 2510 }; 2511 2512 if (isTop) { 2513 data.startDate = new Date(appt.getStartTime()); 2514 } else { 2515 data.endDate = new Date(appt.getEndTime()); 2516 } 2517 2518 //TODO: only create one of these and change data each time... 2519 var capture = new DwtMouseEventCapture({ 2520 targetObj:data, 2521 mouseOverHdlr:ZmCalColView._emptyHdlr, 2522 mouseDownHdlr:ZmCalColView._emptyHdlr, // mouse down (already handled by action) 2523 mouseMoveHdlr:ZmCalColView._sashMouseMoveHdlr, 2524 mouseUpHdlr:ZmCalColView._sashMouseUpHdlr, 2525 mouseOutHdlr:ZmCalColView._emptyHdlr 2526 }); 2527 capture.capture(); 2528 this.deselectAll(); 2529 this.setSelection(data.appt); 2530 Dwt.setOpacity(apptEl, ZmCalColView._OPACITY_APPT_DND); 2531 return false; 2532 }; 2533 2534 ZmCalColView._sashMouseMoveHdlr = 2535 function(ev) { 2536 // DBG.println("ZmCalColView._sashMouseMoveHdlr"); 2537 var mouseEv = DwtShell.mouseEvent; 2538 mouseEv.setFromDhtmlEvent(ev); 2539 var delta = 0; 2540 var data = DwtMouseEventCapture.getTargetObj(); 2541 2542 if (mouseEv.docY > 0 && mouseEv.docY != data.startY) { 2543 delta = mouseEv.docY - data.startY; 2544 } 2545 2546 var draggedOut = data.view._apptDraggedOut(mouseEv.docX, mouseEv.docY); 2547 2548 if (draggedOut) { 2549 if (!data._lastDraggedOut) { 2550 data._lastDraggedOut = true; 2551 data.view._restoreApptLoc(data); 2552 } 2553 } else { 2554 if (data._lastDraggedOut) { 2555 data._lastDraggedOut = false; 2556 data.lastDelta = 0; 2557 Dwt.setOpacity(data.apptEl, ZmCalColView._OPACITY_APPT_DND); 2558 } 2559 var scrollOffset = data.view._handleApptScrollRegion(mouseEv.docX, mouseEv.docY, ZmCalColView._HOUR_HEIGHT, null); 2560 if (scrollOffset != 0) { 2561 data.startY -= scrollOffset; 2562 } 2563 2564 var delta15 = Math.floor(delta/ZmCalColView._15_MINUTE_HEIGHT); 2565 delta = delta15 * ZmCalColView._15_MINUTE_HEIGHT; 2566 2567 if (delta != data.lastDelta) { 2568 if (data.isTop) { 2569 var newY = data.apptY + delta; 2570 var newHeight = data.origHeight - delta; 2571 if (newHeight >= ZmCalColView._15_MINUTE_HEIGHT) { 2572 Dwt.setLocation(data.apptEl, Dwt.DEFAULT, newY); 2573 Dwt.setSize(data.apptEl, Dwt.DEFAULT, data.parentOrigHeight - delta); 2574 Dwt.setSize(data.apptBodyEl, Dwt.DEFAULT, Math.floor(newHeight)); 2575 data.lastDelta = delta; 2576 data.startDate.setTime(data.appt.getStartTime() + (delta15 * AjxDateUtil.MSEC_PER_FIFTEEN_MINUTES)); // num msecs in 15 minutes 2577 if (data.startTimeEl) data.startTimeEl.innerHTML = ZmCalBaseItem._getTTHour(data.startDate); 2578 } 2579 } else { 2580 var newHeight = data.origHeight + delta; 2581 if (newHeight >= ZmCalColView._15_MINUTE_HEIGHT) { 2582 var parentNewHeight = data.parentOrigHeight + delta; 2583 //DBG.println("delta = " + delta); 2584 Dwt.setSize(data.apptEl, Dwt.DEFAULT, parentNewHeight); 2585 Dwt.setSize(data.apptBodyEl, Dwt.DEFAULT, newHeight + ZmCalColView._APPT_HEIGHT_FUDGE); 2586 2587 data.lastDelta = delta; 2588 data.endDate.setTime(data.appt.getEndTime() + (delta15 * AjxDateUtil.MSEC_PER_FIFTEEN_MINUTES)); // num msecs in 15 minutes 2589 if (data.endTimeEl) data.endTimeEl.innerHTML = ZmCalBaseItem._getTTHour(data.endDate); 2590 } 2591 } 2592 } 2593 } 2594 2595 mouseEv._stopPropagation = true; 2596 mouseEv._returnValue = false; 2597 mouseEv.setToDhtmlEvent(ev); 2598 return false; 2599 }; 2600 2601 ZmCalColView._sashMouseUpHdlr = 2602 function(ev) { 2603 // DBG.println("ZmCalColView._sashMouseUpHdlr"); 2604 var data = DwtMouseEventCapture.getTargetObj(); 2605 ZmCalBaseView._setApptOpacity(data.appt, data.apptEl); 2606 var mouseEv = DwtShell.mouseEvent; 2607 mouseEv.setFromDhtmlEvent(ev); 2608 if (mouseEv.button != DwtMouseEvent.LEFT) { 2609 DwtUiEvent.setBehaviour(ev, true, false); 2610 return false; 2611 } 2612 2613 DwtMouseEventCapture.getCaptureObj().release(); 2614 2615 mouseEv._stopPropagation = true; 2616 mouseEv._returnValue = false; 2617 mouseEv.setToDhtmlEvent(ev); 2618 2619 var draggedOut = data.view._apptDraggedOut(mouseEv.docX, mouseEv.docY); 2620 if (draggedOut) { 2621 data.view._restoreApptLoc(data); 2622 return false; 2623 } 2624 2625 var needUpdate = false; 2626 var startDate = null, endDate = null; 2627 if (data.isTop && data.startDate.getTime() != data.appt.getStartTime()) { 2628 needUpdate = true; 2629 startDate = data.startDate; 2630 } else if (!data.isTop && data.endDate.getTime() != data.appt.getEndTime()) { 2631 needUpdate = true; 2632 endDate = data.endDate; 2633 } 2634 if (needUpdate) { 2635 data.view._autoScrollDisabled = true; 2636 var cc = data.view.getController(); 2637 var errorCallback = new AjxCallback(null, ZmCalColView._handleDnDError, data); 2638 var sdOffset = startDate ? (startDate.getTime() - data.appt.getStartTime()) : null; 2639 var edOffset = endDate ? (endDate.getTime() - data.appt.getEndTime()) : null; 2640 cc.dndUpdateApptDate(data.appt._orig, sdOffset, edOffset, null, errorCallback, mouseEv); 2641 } 2642 2643 return false; 2644 }; 2645 2646 // END SASH ACTION HANDLERS 2647 2648 2649 // BEGIN GRID ACTION HANDLERS 2650 2651 ZmCalColView.prototype._gridMouseDownAction = 2652 function(ev, gridEl, gridLoc, isAllDay) { 2653 if (ev.button != DwtMouseEvent.LEFT) { return false; } 2654 2655 if(ZmCalViewController._contextMenuOpened){ 2656 ZmCalViewController._contextMenuOpened = false; 2657 return false; 2658 } 2659 2660 var data = { 2661 dndStarted: false, 2662 view: this, 2663 gridEl: gridEl, 2664 gridX: gridLoc.x, // ev.elementX, 2665 gridY: gridLoc.y, //ev.elementY, 2666 docX: ev.docX, 2667 docY: ev.docY, 2668 isAllDay: isAllDay 2669 }; 2670 2671 var capture = new DwtMouseEventCapture({ 2672 targetObj:data, 2673 mouseOverHdlr:ZmCalColView._emptyHdlr, 2674 mouseDownHdlr:ZmCalColView._emptyHdlr, // mouse down (already handled by action) 2675 mouseMoveHdlr: isAllDay ? ZmCalColView._gridAllDayMouseMoveHdlr : ZmCalColView._gridMouseMoveHdlr, 2676 mouseUpHdlr:ZmCalColView._gridMouseUpHdlr, 2677 mouseOutHdlr:ZmCalColView._emptyHdlr 2678 }); 2679 capture.capture(); 2680 return false; 2681 }; 2682 2683 // called when DND is confirmed after threshold 2684 ZmCalColView.prototype._gridDndBegin = 2685 function(data) { 2686 if(appCtxt.isExternalAccount()) { return false; } 2687 var col = data.view._getColFromX(data.gridX); 2688 data.folderId = col ? (col.cal ? col.cal.id : null) : null; 2689 if (data.isAllDay) { 2690 data.gridEl.style.cursor = 'e-resize'; 2691 data.newApptDivEl = document.getElementById(data.view._newAllDayApptDivId); 2692 data.view._populateNewApptHtml(data.newApptDivEl, true, data.folderId); 2693 data.apptBodyEl = document.getElementById(data.newApptDivEl.id + "_body"); 2694 data.view._allDayScrollToBottom(); 2695 //zzzzz 2696 } else { 2697 data.gridEl.style.cursor = 's-resize'; 2698 data.newApptDivEl = document.getElementById(data.view._newApptDivId); 2699 data.view._populateNewApptHtml(data.newApptDivEl, false, data.folderId); 2700 data.apptBodyEl = document.getElementById(data.newApptDivEl.id + "_body"); 2701 data.endTimeEl = document.getElementById(data.newApptDivEl.id +"_et"); 2702 data.startTimeEl = document.getElementById(data.newApptDivEl.id +"_st"); 2703 } 2704 this.deselectAll(); 2705 return true; 2706 }; 2707 2708 /* 2709 * Initializes the vertical scrollbar of the body element to 8AM. 2710 */ 2711 ZmCalColView.prototype.initializeTimeScroll = function(){ 2712 this._scrollToTime(8); 2713 } 2714 2715 ZmCalColView._gridMouseMoveHdlr = 2716 function(ev) { 2717 2718 var mouseEv = DwtShell.mouseEvent; 2719 mouseEv.setFromDhtmlEvent(ev); 2720 var data = DwtMouseEventCapture.getTargetObj(); 2721 2722 var deltaX = mouseEv.docX - data.docX; 2723 var deltaY = mouseEv.docY - data.docY; 2724 2725 if (!data.dndStarted) { 2726 var withinThreshold = (Math.abs(deltaX) < ZmCalColView.DRAG_THRESHOLD && Math.abs(deltaY) < ZmCalColView.DRAG_THRESHOLD); 2727 if (withinThreshold || !data.view._gridDndBegin(data)) { 2728 mouseEv._stopPropagation = true; 2729 mouseEv._returnValue = false; 2730 mouseEv.setToDhtmlEvent(ev); 2731 return false; 2732 } 2733 } 2734 2735 var scrollOffset = data.view._handleApptScrollRegion(mouseEv.docX, mouseEv.docY, ZmCalColView._HOUR_HEIGHT, null); 2736 if (scrollOffset != 0) { 2737 data.docY -= scrollOffset; 2738 deltaY += scrollOffset; 2739 } 2740 2741 // snap new location to grid 2742 var snap = data.view._snapXY(data.gridX + deltaX, data.gridY + deltaY, 30); 2743 if (snap == null) return false; 2744 2745 var newStart, newEnd; 2746 2747 if (deltaY >= 0) { // dragging down 2748 newStart = data.view._snapXY(data.gridX, data.gridY, 30); 2749 newEnd = data.view._snapXY(data.gridX, data.gridY + deltaY, 30, true); 2750 } else { // dragging up 2751 newEnd = data.view._snapXY(data.gridX, data.gridY, 30); 2752 newStart = data.view._snapXY(data.gridX, data.gridY + deltaY, 30); 2753 } 2754 2755 if (newStart == null || newEnd == null) return false; 2756 2757 if ((data.start == null) || (data.start.y != newStart.y) || (data.end.y != newEnd.y)) { 2758 2759 if (!data.dndStarted) data.dndStarted = true; 2760 2761 data.start = newStart; 2762 data.end = newEnd; 2763 2764 data.startDate = data.view._getDateFromXY(data.start.x, data.start.y, 30, false); 2765 data.endDate = data.view._getDateFromXY(data.end.x, data.end.y, 30, false); 2766 2767 var e = data.newApptDivEl; 2768 if (!e) return; 2769 var duration = (data.endDate.getTime() - data.startDate.getTime()); 2770 if (duration < AjxDateUtil.MSEC_PER_HALF_HOUR) duration = AjxDateUtil.MSEC_PER_HALF_HOUR; 2771 2772 var bounds = data.view._getBoundsForDate(data.startDate, duration, newStart.col); 2773 if (bounds == null) return false; 2774 data.view._layoutAppt(null, e, newStart.x, newStart.y, bounds.width, bounds.height); 2775 Dwt.setVisible(e, true); 2776 if (data.startTimeEl) data.startTimeEl.innerHTML = ZmCalBaseItem._getTTHour(data.startDate); 2777 if (data.endTimeEl) data.endTimeEl.innerHTML = ZmCalBaseItem._getTTHour(data.endDate); 2778 } 2779 mouseEv._stopPropagation = true; 2780 mouseEv._returnValue = false; 2781 mouseEv.setToDhtmlEvent(ev); 2782 return false; 2783 }; 2784 2785 ZmCalColView._gridMouseUpHdlr = 2786 function(ev) { 2787 var data = DwtMouseEventCapture.getTargetObj(); 2788 var mouseEv = DwtShell.mouseEvent; 2789 mouseEv.setFromDhtmlEvent(ev); 2790 2791 DwtMouseEventCapture.getCaptureObj().release(); 2792 2793 if (!data.dndStarted && appCtxt.get(ZmSetting.CAL_USE_QUICK_ADD)) { 2794 var newStart, newEnd; 2795 var deltaY = mouseEv.docY - data.docY; 2796 2797 if (deltaY >= 0) { // dragging down 2798 newStart = data.view._snapXY(data.gridX, data.gridY, 30); 2799 newEnd = data.view._snapXY(data.gridX, data.gridY + deltaY, 30, true); 2800 } else { // dragging up 2801 newEnd = data.view._snapXY(data.gridX, data.gridY, 30); 2802 newStart = data.view._snapXY(data.gridX, data.gridY + deltaY, 30); 2803 } 2804 2805 if (newStart == null || newEnd == null) return false; 2806 2807 if ((data.start == null) || (data.start.y != newStart.y) || (data.end.y != newEnd.y)) { 2808 2809 if (!data.dndStarted){ 2810 data.dndStarted = true; 2811 } 2812 2813 data.start = newStart; 2814 data.end = newEnd; 2815 2816 data.startDate = data.view._getDateFromXY(data.start.x, data.start.y, 30, false); 2817 data.endDate = data.view._getDateFromXY(data.end.x, data.end.y, 30, false); 2818 } 2819 2820 if (data.isAllDay) { 2821 data.newApptDivEl = document.getElementById(data.view._newAllDayApptDivId); 2822 } else { 2823 data.newApptDivEl = document.getElementById(data.view._newApptDivId); 2824 } 2825 } 2826 2827 if (data.dndStarted) { 2828 data.gridEl.style.cursor = 'auto'; 2829 var col = data.view._getColFromX(data.gridX); 2830 data.folderId = col ? (col.cal ? col.cal.id : null) : null; 2831 Dwt.setVisible(data.newApptDivEl, false); 2832 if (data.isAllDay) { 2833 appCtxt.getCurrentController().newAllDayAppointmentHelper(data.startDate, data.endDate, data.folderId, mouseEv.shiftKey); 2834 } else { 2835 var duration = (data.endDate.getTime() - data.startDate.getTime()); 2836 if (duration < AjxDateUtil.MSEC_PER_HALF_HOUR) duration = AjxDateUtil.MSEC_PER_HALF_HOUR; 2837 appCtxt.getCurrentController().newAppointmentHelper(data.startDate, duration, data.folderId, mouseEv.shiftKey); 2838 } 2839 } 2840 2841 mouseEv._stopPropagation = true; 2842 mouseEv._returnValue = false; 2843 mouseEv.setToDhtmlEvent(ev); 2844 2845 return false; 2846 }; 2847 2848 // END GRID ACTION HANDLERS 2849 2850 // BEGIN ALLDAY GRID ACTION HANDLERS 2851 2852 ZmCalColView._gridAllDayMouseMoveHdlr = 2853 function(ev) { 2854 2855 var mouseEv = DwtShell.mouseEvent; 2856 mouseEv.setFromDhtmlEvent(ev); 2857 var data = DwtMouseEventCapture.getTargetObj(); 2858 2859 var deltaX = mouseEv.docX - data.docX; 2860 var deltaY = mouseEv.docY - data.docY; 2861 2862 if (!data.dndStarted) { 2863 var withinThreshold = (Math.abs(deltaX) < ZmCalColView.DRAG_THRESHOLD && Math.abs(deltaY) < ZmCalColView.DRAG_THRESHOLD); 2864 if (withinThreshold || !data.view._gridDndBegin(data)) { 2865 mouseEv._stopPropagation = true; 2866 mouseEv._returnValue = false; 2867 mouseEv.setToDhtmlEvent(ev); 2868 return false; 2869 } 2870 } 2871 2872 // snap new location to grid 2873 var snap = data.view._snapXY(data.gridX + deltaX, data.gridY + deltaY, 30); 2874 if (snap == null) return false; 2875 2876 var newStart, newEnd; 2877 2878 if (deltaX >= 0) { // dragging right 2879 newStart = data.view._snapAllDayXY(data.gridX, data.gridY); 2880 newEnd = data.view._snapAllDayXY(data.gridX + deltaX, data.gridY); 2881 } else { // dragging left 2882 newEnd = data.view._snapAllDayXY(data.gridX, data.gridY); 2883 newStart = data.view._snapAllDayXY(data.gridX + deltaX, data.gridY); 2884 } 2885 2886 if (newStart == null || newEnd == null) return false; 2887 2888 if ((data.start == null) || (!data.view._scheduleMode && ((data.start.x != newStart.x) || (data.end.x != newEnd.x)))) { 2889 2890 if (!data.dndStarted) data.dndStarted = true; 2891 2892 data.start = newStart; 2893 data.end = newEnd; 2894 2895 data.startDate = data.view._getAllDayDateFromXY(data.start.x, data.start.y); 2896 data.endDate = data.view._getAllDayDateFromXY(data.end.x, data.end.y); 2897 2898 var e = data.newApptDivEl; 2899 if (!e) return; 2900 2901 var bounds = data.view._getBoundsForAllDayDate(data.start, data.end); 2902 if (bounds == null) return false; 2903 // blank row at the bottom 2904 var y = data.view._allDayFullDivHeight - (ZmCalColView._ALL_DAY_APPT_HEIGHT+ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD); 2905 Dwt.setLocation(e, newStart.x, y); 2906 Dwt.setSize(e, bounds.width, bounds.height); 2907 Dwt.setSize(data.apptBodyEl, bounds.width, bounds.height); 2908 Dwt.setVisible(e, true); 2909 } 2910 mouseEv._stopPropagation = true; 2911 mouseEv._returnValue = false; 2912 mouseEv.setToDhtmlEvent(ev); 2913 return false; 2914 }; 2915 2916 // END ALLDAY GRID ACTION HANDLERS 2917 2918 ZmCalColView._emptyHdlr = 2919 function(ev) { 2920 var mouseEv = DwtShell.mouseEvent; 2921 mouseEv.setFromDhtmlEvent(ev); 2922 mouseEv._stopPropagation = true; 2923 mouseEv._returnValue = false; 2924 mouseEv.setToDhtmlEvent(ev); 2925 return false; 2926 }; 2927 2928 ZmCalColView._handleError = 2929 function(data) { 2930 data.view.getController()._refreshAction(true); 2931 return false; 2932 }; 2933 2934 ZmCalColView._handleDnDError = 2935 function(data) { 2936 // Redraw the grid to reposition whatever DnD failed 2937 data.view._layout(true); 2938 return false; 2939 }; 2940 2941 2942 ZmCalColView.prototype.toggleAllDayAppt = 2943 function(hide) { 2944 var apptScroll = document.getElementById(this._allDayApptScrollDivId); 2945 Dwt.setVisible(apptScroll, !hide); 2946 var sash = document.getElementById(this._allDaySepSashDivId); 2947 if(hide) { 2948 Dwt.addClass(sash, 'closed'); 2949 } 2950 else { 2951 Dwt.delClass(sash, 'closed'); 2952 } 2953 if (this._scheduleMode) { 2954 var unionAllDayDiv = document.getElementById(this._unionAllDayDivId); 2955 Dwt.setVisible(unionAllDayDiv, !hide); 2956 } 2957 2958 this._hideAllDayAppt = ! this._hideAllDayAppt; 2959 this._layout(); 2960 }; 2961 2962 ZmCalColView.prototype._postApptCreate = 2963 function(appt,div) { 2964 var layout = this._layoutMap[this._getItemId(appt)]; 2965 if (layout){ 2966 layout.bounds = this._getBoundsForAppt(layout.appt); 2967 if (!layout.bounds) { return; } 2968 2969 apptWidthPercent = ZmCalColView._getApptWidthPercent(layout.maxcol+1); 2970 var w = Math.floor(layout.bounds.width*apptWidthPercent); 2971 var xinc = layout.maxcol ? ((layout.bounds.width - w) / layout.maxcol) : 0; // n-1 2972 var x = xinc * layout.col + (layout.bounds.x); 2973 if (appt) appt._layout = {x: x, y: layout.bounds.y, w: w, h: layout.bounds.height}; 2974 var apptHeight = layout.bounds.height; 2975 var apptY = layout.bounds.y; 2976 var apptDiv = document.getElementById(this._getItemId(layout.appt)); 2977 this._layoutAppt(layout.appt, apptDiv, x, apptY, w, apptHeight); 2978 } 2979 }; 2980