1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2004, 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) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 ZmCalDayView = function(parent, posStyle, controller, dropTgt, view, numDays, readonly, isInviteMessage, isRight) { 25 // Usage in ZmInviteMsgView requires a unique id - used in the conversation view, 26 // so multiple simultaneous instances 27 var id = isInviteMessage ? ZmId.getViewId(ZmId.VIEW_CAL_DAY, null, view) : ZmId.VIEW_CAL_DAY; 28 ZmCalColView.call(this, parent, posStyle, controller, dropTgt, id, 1, false, readonly, isInviteMessage, isRight); 29 this._compactMode = false; 30 }; 31 32 ZmCalDayView.prototype = new ZmCalColView; 33 ZmCalDayView.prototype.constructor = ZmCalDayView; 34 35 ZmCalDayView.prototype.toString = 36 function() { 37 return "ZmCalDayView"; 38 }; 39 40 ZmCalDayView.prototype.setCompactMode = 41 function(compactMode) { 42 this._compactMode = compactMode; 43 }; 44 45 ZmCalDayView.prototype.isCompactMode = 46 function() { 47 return this._compactMode; 48 }; 49 50 ZmCalDayView.prototype.fbStatusBarEnabled = 51 function(){ 52 return true; 53 }; 54 55 ZmCalDayView.prototype._layout = 56 function(refreshApptLayout) { 57 ZmCalColView.prototype._layout.call(this, refreshApptLayout); 58 59 if (this._compactMode && !this._closeButton) { 60 var btn = this._closeButton = new DwtButton({ 61 parent:this, 62 style: DwtLabel.ALIGN_RIGHT | DwtButton.ALWAYS_FLAT, 63 posStyle: DwtControl.ABSOLUTE_STYLE, 64 className:"DwtToolbarButton cal_day_expand" 65 }); 66 this._closeButton.setImage("Close"); 67 this._closeButton.setToolTipContent(ZmMsg.close); 68 this._closeButton.setSize(16,16); 69 var size= this.getSize(); 70 this._closeButton.setLocation(size.x-22, 0); // close button at top right corner for compact mode alone 71 this._closeButton.addSelectionListener(new AjxListener(this, this._closeDayViewListener)); 72 } 73 }; 74 75 ZmCalDayView.prototype._closeDayViewListener = 76 function() { 77 if (this._closeDayViewCallback) { 78 this._closeDayViewCallback.run(); 79 } 80 }; 81 82 ZmCalDayView.prototype.setCloseDayViewCallback = 83 function(callback) { 84 this._closeDayViewCallback = callback; 85 }; 86 87 ZmCalDayView.prototype.setSize = 88 function(width, height) { 89 ZmCalColView.prototype.setSize.call(this, width, height); 90 if (this._closeButton) { 91 this._closeButton.setLocation(width-22, 0); 92 } 93 }; 94 95 ZmCalDayView.prototype.layout = 96 function() { 97 this._layout(true); 98 } 99 100 ZmCalDayView.prototype._controlListener = 101 function(ev) { 102 if (!this._compactMode) { 103 ZmCalColView.prototype._controlListener.call(this, ev); 104 } 105 }; 106 107 108 ZmCalDayView.prototype._apptMouseDownAction = 109 function(ev, apptEl) { 110 appt = this.getItemFromElement(apptEl); 111 if (appt.isAllDayEvent()) { 112 return false; 113 } else { 114 return ZmCalBaseView.prototype._apptMouseDownAction.call(this, ev, apptEl, appt); 115 } 116 }; 117 118 ZmCalDayView.prototype._updateDays = 119 function() { 120 // When used from the ZmInviteMsgView, the day view requires a unique id - but 121 // underlying views count on the id being the standard ZmId.VIEW_CAL_DAY. Since 122 // the use from ZmInviteMsgView is read-only, _updateDays is the only superclass 123 // function that we have to fool by using the standard id. 124 var viewId = this.view; 125 this.view = ZmId.VIEW_CAL_DAY; 126 ZmCalColView.prototype._updateDays.call(this); 127 this.view = viewId; 128 } 129 130 ZmCalDayTabView = function(parent, posStyle, controller, dropTgt, view, numDays, readonly, isInviteMessage, isRight) { 131 //ZmCalColView.call(this, parent, posStyle, controller, dropTgt, ZmId.VIEW_CAL_DAY_TAB, 1, false, readonly, isInviteMessage, isRight); 132 ZmCalColView.call(this, parent, posStyle, controller, dropTgt, ZmId.VIEW_CAL_DAY, 1, true); 133 this._compactMode = false; 134 }; 135 136 ZmCalDayTabView.prototype = new ZmCalColView; 137 ZmCalDayTabView.prototype.constructor = ZmCalDayTabView; 138 139 ZmCalDayTabView._ALL_DAY_APPT_HEIGHT_PAD = 3; 140 ZmCalDayTabView._UNION_DIV_WIDTH = 0; 141 142 ZmCalDayTabView._TAB_BORDER_WIDTH = 1; 143 ZmCalDayTabView._TAB_BORDER_MARGIN = 4; 144 ZmCalDayTabView._TAB_SEP_WIDTH = 8; 145 ZmCalDayTabView._TAB_TITLE_MAX_LENGTH = 15; 146 147 ZmCalDayTabView.ATTR_CAL_ID = "_calid"; 148 149 ZmCalDayTabView.prototype.toString = 150 function() { 151 return "ZmCalDayTabView"; 152 }; 153 154 ZmCalDayTabView.prototype.fbStatusBarEnabled = 155 function(){ 156 return true; 157 }; 158 159 ZmCalDayTabView.prototype._createHtml = 160 function(abook) { 161 this._days = {}; 162 this._columns = []; 163 this._hours = {}; 164 this._layouts = []; 165 this._allDayAppts = []; 166 this._allDayRows = []; 167 168 this._headerYearId = Dwt.getNextId(); 169 this._yearHeadingDivId = Dwt.getNextId(); 170 this._yearAllDayDivId = Dwt.getNextId(); 171 this._yearAllDayTopBorderId = Dwt.getNextId(); 172 this._yearAllDayBottomBorderId = Dwt.getNextId(); 173 this._leftAllDaySepDivId = Dwt.getNextId(); 174 this._leftApptSepDivId = Dwt.getNextId(); 175 176 this._allDayScrollDivId = Dwt.getNextId(); 177 this._allDayHeadingDivId = Dwt.getNextId(); 178 this._allDayApptScrollDivId = Dwt.getNextId(); 179 this._allDayDivId = Dwt.getNextId(); 180 this._hoursScrollDivId = Dwt.getNextId(); 181 this._bodyHourDivId = Dwt.getNextId(); 182 this._allDaySepDivId = Dwt.getNextId(); 183 this._bodyDivId = Dwt.getNextId(); 184 this._apptBodyDivId = Dwt.getNextId(); 185 this._newApptDivId = Dwt.getNextId(); 186 this._newAllDayApptDivId = Dwt.getNextId(); 187 this._timeSelectionDivId = Dwt.getNextId(); 188 this._curTimeIndicatorHourDivId = Dwt.getNextId(); 189 this._curTimeIndicatorGridDivId = Dwt.getNextId(); 190 this._startLimitIndicatorDivId = Dwt.getNextId(); 191 this._endLimitIndicatorDivId = Dwt.getNextId(); 192 this._hourColDivId = Dwt.getNextId(); 193 194 this._unionHeadingDivId = Dwt.getNextId(); 195 this._unionAllDayDivId = Dwt.getNextId(); 196 this._unionHeadingSepDivId = Dwt.getNextId(); 197 this._unionGridScrollDivId = Dwt.getNextId(); 198 this._unionGridDivId = Dwt.getNextId(); 199 this._unionGridSepDivId = Dwt.getNextId(); 200 this._workingHrsFirstDivId = Dwt.getNextId(); 201 this._workingHrsSecondDivId = Dwt.getNextId(); 202 203 this._tabsContainerDivId = Dwt.getNextId(); 204 this._toggleBtnContainerId = Dwt.getNextId(); 205 206 this._borderLeftDivId = Dwt.getNextId(); 207 this._borderRightDivId = Dwt.getNextId(); 208 this._borderTopDivId = Dwt.getNextId(); 209 this._borderBottomDivId = Dwt.getNextId(); 210 this._startLimitIndicatorDivId = Dwt.getNextId(); 211 this._endLimitIndicatorDivId = Dwt.getNextId(); 212 213 var html = new AjxBuffer(), 214 // year heading 215 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 216 headerStyle = "position:absolute;" + inviteMessageHeaderStyle, 217 func, 218 ids, 219 types, 220 i; 221 222 // div under year 223 html.append("<div id='", this._yearAllDayDivId, "' name='_yearAllDayDivId' style='position:absolute;'>"); 224 html.append("<div id='", this._yearHeadingDivId, "' class='calendar_heading_day_tab' name='_yearHeadingDivId' style='width:100%;height:100%;'>"); 225 html.append("<div id='", this._headerYearId, 226 "' name='_headerYearId' class=calendar_heading_year_text style='position:absolute; width:", ZmCalColView._HOURS_DIV_WIDTH,"px;'></div>"); 227 html.append("</div>"); 228 html.append("</div>"); 229 230 // sep between year and headings 231 html.append("<div id='", this._leftAllDaySepDivId, "' name='_leftAllDaySepDivId' class='calendar_day_separator' style='position:absolute'></div>"); 232 233 if (this._scheduleMode) { 234 235 // div in all day space 236 html.append("<div id='", this._unionAllDayDivId, "' name='_unionAllDayDivId' style='position:absolute'>"); 237 html.append("<div id='", this._unionHeadingDivId, "' name='_unionHeadingDivId' class=calendar_heading style='position:absolute'>"); 238 html.append("<div class=calendar_heading_year_text style='position:absolute; width:", ZmCalDayTabView._UNION_DIV_WIDTH,"px;'>",ZmMsg.allDay,"</div>"); 239 html.append("</div>"); 240 html.append("</div>"); 241 242 // sep between year and headings 243 html.append("<div id='", this._unionHeadingSepDivId, "' name='_unionHeadingSepDivId' class='calendar_day_separator' style='position:absolute'></div>"); 244 } 245 246 // all day scroll ============= 247 html.append("<div id='", this._allDayScrollDivId, "' name='_allDayScrollDivId' style='position:absolute; overflow:hidden;'>"); 248 html.append("</div>"); 249 // end of all day scroll =========== 250 251 // div holding all day appts 252 html.append("<div id='", this._allDayApptScrollDivId, "' name='_allDayApptScrollDivId' class='calendar_allday_appt' style='position:absolute'>"); 253 html.append("<div id='", this._allDayDivId, "' name='_allDayDivId' style='position:absolute'>"); 254 html.append("<div id='", this._newAllDayApptDivId, "' name='_newAllDayApptDivId' class='appt-selected' style='position:absolute; display:none;'></div>"); 255 html.append("</div>"); 256 html.append("</div>"); 257 // end of div holding all day appts 258 259 // sep betwen all day and normal appts 260 html.append("<div id='", this._allDaySepDivId, "' name='_allDaySepDivId' style='overflow:hidden;position:absolute;'></div>"); 261 262 // div to hold hours 263 html.append("<div id='", this._hoursScrollDivId, "' name='_hoursScrollDivId' class=calendar_hour_scroll style='position:absolute;'>"); 264 this._createHoursHtml(html); 265 html.append("</div>"); 266 // end of div to hold hours 267 268 // sep between hours and grid 269 html.append("<div id='", this._leftApptSepDivId, "' name='_leftApptSepDivId' class='calendar_day_separator' style='position:absolute'></div>"); 270 271 // union grid 272 if (this._scheduleMode) { 273 html.append("<div id='", this._unionGridScrollDivId, "' name='_unionGridScrollDivId' class=calendar_union_scroll style='position:absolute;display:none;'>"); 274 html.append("<div id='", this._unionGridDivId, "' name='_unionGridDivId' class='ImgCalendarDayGrid' style='width:100%; height:1008px; position:absolute;'>"); 275 html.append("</div></div>"); 276 // sep between union grid and appt grid 277 html.append("<div id='", this._unionGridSepDivId, "' name='_unionGridSepDivId' class='calendar_day_separator' style='position:absolute;display:none;'></div>"); 278 } 279 280 // grid body 281 // Fix for bug: 66603. Removed horizontal scroll bar from grid body 282 html.append("<div id='", this._bodyDivId, "' name='_bodyDivId' class=calendar_body style='position:absolute; overflow-x:hidden;'>"); 283 html.append("<div id='", this._apptBodyDivId, "' name='_apptBodyDivId' class='ImgCalendarDayGrid' style='width:100%; height:1008px; position:absolute;background-color:#E3E3DC;'>"); 284 html.append("<div id='", this._timeSelectionDivId, "' name='_timeSelectionDivId' class='calendar_time_selection' style='position:absolute; display:none;z-index:10;'></div>"); 285 html.append("<div id='", this._newApptDivId, "' name='_newApptDivId' class='appt-selected' style='position:absolute; display:none;'></div>"); 286 287 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>"); 288 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>"); 289 290 html.append("<div id='", this._borderLeftDivId, "' name='_borderLeftDivId' class='ZmDayTabSeparator' style='background-color:#FFFFFF;position:absolute;'></div>"); 291 html.append("<div id='", this._borderRightDivId, "' name='_borderRightDivId' class='ZmDayTabSeparator' style='background-color:#FFFFFF;position:absolute;'></div>"); 292 html.append("<div id='", this._borderTopDivId, "' name='_borderTopDivId' class='ZmDayTabSeparator' style='background-color:#FFFFFF;position:absolute;'></div>"); 293 html.append("<div id='", this._borderBottomDivId, "' name='_borderBottomDivId' class='ZmDayTabSeparator' style='background-color:#FFFFFF;position:absolute;'></div>"); 294 html.append("</div>"); 295 // end of grid body 296 297 //Strip to indicate the current time 298 html.append("<div id='"+this._curTimeIndicatorGridDivId+"' name='_curTimeIndicatorGridDivId' class='calendar_cur_time_indicator_strip' style='position:absolute;background-color:#F16426; height: 1px;'></div>"); 299 //arrow to indicate the off-screen appointments 300 html.append("<div id='"+this._startLimitIndicatorDivId+"' class='calendar_start_limit_indicator'><div class='ImgArrowMoreUp'></div></div>"); 301 html.append("<div id='"+this._endLimitIndicatorDivId+"' class='calendar_end_limit_indicator'><div class='ImgArrowMoreDown'></div></div>"); 302 303 //html.append("<div id='"+this._curTimeIndicatorGridDivId+"' name='_curTimeIndicatorGridDivId' class='calendar_cur_time_indicator_strip' style='position:absolute;background-color:#F16426; height: 1px;'></div>"); 304 html.append("</div>"); 305 306 // all day headings 307 // Fix for bug: 66603. Separating merge/split button from tab container 308 html.append("<div id='", this._toggleBtnContainerId, "' name='_toggleBtnContainerId' style='position:absolute;bottom:0px;'></div>"); 309 // Fix for bug: 66603. Hide the overflow 310 html.append("<div id='", this._tabsContainerDivId, "' name='_tabsContainerDivId' style='position:absolute;height:45px;bottom:0px;overflow-y:hidden;'>"); 311 html.append("<div id='", this._allDayHeadingDivId, "' name='_allDayHeadingDivId' style='", headerStyle, "'></div>"); 312 html.append("</div>"); 313 // end of all day headings 314 315 this.getHtmlElement().innerHTML = html.toString(); 316 func = AjxCallback.simpleClosure(ZmCalColView.__onScroll, ZmCalColView, this); 317 document.getElementById(this._bodyDivId).onscroll = func; 318 document.getElementById(this._allDayApptScrollDivId).onscroll = func; 319 // Fix for bug: 66603. Attaching a scroll function. 320 document.getElementById(this._tabsContainerDivId).onscroll = func; 321 322 ids = [this._apptBodyDivId, this._bodyHourDivId, this._allDayDivId, this._allDaySepDivId]; 323 types = [ZmCalBaseView.TYPE_APPTS_DAYGRID, ZmCalBaseView.TYPE_HOURS_COL, ZmCalBaseView.TYPE_ALL_DAY, ZmCalBaseView.TYPE_DAY_SEP]; 324 for (i = 0; i < ids.length; i++) { 325 this.associateItemWithElement(null, document.getElementById(ids[i]), types[i], ids[i]); 326 } 327 this._scrollToTime(8); 328 }; 329 330 ZmCalDayTabView.prototype._layout = 331 function(refreshApptLayout) { 332 DBG.println(AjxDebug.DBG2, "ZmCalColView in layout!"); 333 this._updateDays(); 334 335 var numCols = this._columns.length, 336 sz = this.getSize(), 337 width = sz.x, 338 height = sz.y, 339 hoursWidth = ZmCalColView._HOURS_DIV_WIDTH-1, 340 bodyX = hoursWidth + this._daySepWidth, 341 bodyY, 342 unionX = ZmCalColView._HOURS_DIV_WIDTH, 343 needHorzScroll, 344 scrollFudge, 345 allDayHeadingDiv = document.getElementById(this._allDayHeadingDivId), 346 allDayHeadingDivHeight = Dwt.getSize(allDayHeadingDiv).y, 347 numRows = this._allDayApptsRowLayouts ? (this._allDayApptsRowLayouts.length) : 1, 348 percentageHeight, 349 nearestNoOfRows, 350 allDayScrollHeight, 351 unionSepX; 352 353 if (width == 0 || height == 0) { return; } 354 355 height -= 25; 356 this._needFirstLayout = false; 357 bodyX += this._daySepWidth + ZmCalDayTabView._TAB_SEP_WIDTH; 358 359 // compute height for hours/grid 360 this._bodyDivWidth = width - bodyX - ZmCalDayTabView._TAB_SEP_WIDTH; 361 362 // size appts divs 363 this._apptBodyDivHeight = ZmCalColView._DAY_HEIGHT + 1; // extra for midnight to show up 364 this._apptBodyDivWidth = Math.max(this._bodyDivWidth, this._calcMinBodyWidth(this._bodyDivWidth, numCols)); 365 needHorzScroll = this._apptBodyDivWidth > this._bodyDivWidth; 366 367 this._horizontalScrollbar(needHorzScroll); 368 369 if (needHorzScroll) this._apptBodyDivWidth -= 18; 370 scrollFudge = needHorzScroll ? 20 : 0; // need all day to be a little wider then grid 371 372 if(!this._toggleBtn) { 373 this._setBounds(this._toggleBtnContainerId, 0, Dwt.DEFAULT, hoursWidth+ZmCalDayTabView._UNION_DIV_WIDTH+this._daySepWidth, Dwt.DEFAULT); 374 this._toggleBtn = new DwtButton({parent:this, parentElement: this._toggleBtnContainerId, className: "ZButton ZPicker ZCalToggleBtn"}); 375 this._toggleBtn.setText(ZmMsg.calTabsMerge); 376 this._toggleBtn.addListener(DwtEvent.ONCLICK, new AjxListener(this, this._toggleView)); 377 } 378 379 // column headings 380 // Fix for bug: 66603. Position - X set to 0 to adjust the scrolling and Position - Y to 2px. 381 Dwt.setBounds(allDayHeadingDiv, 0, 2, this._apptBodyDivWidth, Dwt.DEFAULT); 382 // div for all day appts 383 if (this._allDayApptsList && this._allDayApptsList.length > 0) { 384 numRows++; 385 } 386 this._allDayFullDivHeight = (ZmCalColView._ALL_DAY_APPT_HEIGHT+ZmCalDayTabView._ALL_DAY_APPT_HEIGHT_PAD) * numRows + ZmCalDayTabView._ALL_DAY_APPT_HEIGHT_PAD; 387 388 percentageHeight = (this._allDayFullDivHeight/height)*100; 389 this._allDayDivHeight = this._allDayFullDivHeight; 390 391 // if height overflows more than 50% of full height set its height 392 // to nearest no of rows which occupies less than 50% of total height 393 if (percentageHeight > 50) { 394 nearestNoOfRows = Math.floor((0.50*height-ZmCalDayTabView._ALL_DAY_APPT_HEIGHT_PAD)/(ZmCalColView._ALL_DAY_APPT_HEIGHT+ZmCalDayTabView._ALL_DAY_APPT_HEIGHT_PAD)); 395 this._allDayDivHeight = (ZmCalColView._ALL_DAY_APPT_HEIGHT+ZmCalDayTabView._ALL_DAY_APPT_HEIGHT_PAD) * nearestNoOfRows + ZmCalDayTabView._ALL_DAY_APPT_HEIGHT_PAD; 396 } 397 398 this._setBounds(this._allDayApptScrollDivId, bodyX, allDayHeadingDivHeight+ZmCalDayTabView._TAB_BORDER_MARGIN, this._bodyDivWidth, this._allDayDivHeight+ZmCalDayTabView._TAB_BORDER_MARGIN); 399 this._setBounds(this._allDayDivId, 0, 0, this._apptBodyDivWidth + scrollFudge, this._allDayFullDivHeight+ZmCalDayTabView._TAB_BORDER_MARGIN); 400 401 // Fix for bug: 66603. Set the position-X, width and height for heading container. 402 this._setBounds(this._tabsContainerDivId, bodyX, Dwt.DEFAULT, this._bodyDivWidth, scrollFudge !== 0 ? 45 : 25); 403 404 this._allDayVerticalScrollbar(this._allDayDivHeight != this._allDayFullDivHeight); 405 406 // div under year 407 this._setBounds(this._yearAllDayDivId, 0, ZmCalDayTabView._TAB_BORDER_MARGIN, hoursWidth + ZmCalDayTabView._UNION_DIV_WIDTH + this._daySepWidth-1, this._allDayDivHeight); 408 //this._setBounds(this._yearHeadingDivId, 0, this._daySepWidth-1, hoursWidth + ZmCalDayTabView._UNION_DIV_WIDTH + this._daySepWidth, ZmCalColView._ALL_DAY_APPT_HEIGHT+ZmCalDayTabView._TAB_BORDER_MARGIN+1); 409 // all day scroll 410 allDayScrollHeight = this._allDayDivHeight; 411 this._setBounds(this._allDayScrollDivId, bodyX, 0, this._bodyDivWidth, allDayScrollHeight); 412 413 // horiz separator between all day appts and grid 414 this._setBounds(this._allDaySepDivId, 0, (this._hideAllDayAppt ? ZmCalColView._DAY_HEADING_HEIGHT : allDayScrollHeight)+2, width, ZmCalColView._ALL_DAY_SEP_HEIGHT); 415 416 bodyY = (this._hideAllDayAppt ? ZmCalColView._DAY_HEADING_HEIGHT : allDayScrollHeight) + ZmCalColView._ALL_DAY_SEP_HEIGHT + (AjxEnv.isIE ? 0 : 2); 417 418 // Fix for bug: 66603. Adjusts the height of grid body. 419 this._bodyDivHeight = height - bodyY - scrollFudge; 420 421 // hours 422 this._setBounds(this._hoursScrollDivId, 0, bodyY, hoursWidth, this._bodyDivHeight); 423 424 // vert sep between hours and grid 425 this._setBounds(this._leftApptSepDivId, hoursWidth, bodyY-ZmCalDayTabView._TAB_BORDER_WIDTH, this._daySepWidth, ZmCalColView._DAY_HEIGHT); 426 427 // div for scrolling grid 428 this._setBounds(this._bodyDivId, bodyX, bodyY, this._bodyDivWidth, this._bodyDivHeight); 429 430 this._setBounds(this._apptBodyDivId, 0, -1, this._apptBodyDivWidth, this._apptBodyDivHeight); 431 432 // sep in all day area 433 unionSepX = unionX + ZmCalDayTabView._UNION_DIV_WIDTH; 434 this._setBounds(this._unionHeadingSepDivId, unionSepX, ZmCalDayTabView._TAB_BORDER_MARGIN, this._daySepWidth-1, allDayScrollHeight+1); 435 436 // div for scrolling union 437 this._setBounds(this._unionGridScrollDivId, unionX, bodyY, ZmCalDayTabView._UNION_DIV_WIDTH, this._bodyDivHeight); 438 this._setBounds(this._unionGridDivId, 0, -1, ZmCalDayTabView._UNION_DIV_WIDTH, this._apptBodyDivHeight+ZmCalColView._HOUR_HEIGHT); 439 440 // sep in grid area 441 this._setBounds(this._unionGridSepDivId, unionSepX, bodyY-ZmCalDayTabView._TAB_BORDER_MARGIN, this._daySepWidth, this._apptBodyDivHeight); 442 443 this._bodyX = bodyX; 444 this.layoutWorkingHours(this.workingHours); 445 this._layoutAllDayAppts(); 446 447 this._apptBodyDivOffset = Dwt.toWindow(document.getElementById(this._apptBodyDivId), 0, 0, null, true); 448 this._apptAllDayDivOffset = Dwt.toWindow(document.getElementById(this._allDayDivId), 0, 0, null, true); 449 450 451 this._layoutAppts(); 452 this._layoutUnionData(); 453 454 }; 455 456 ZmCalDayTabView.prototype.layoutWorkingHours = 457 function(workingHours){ 458 if(!workingHours) { 459 workingHours = ZmCalBaseView.parseWorkingHours(ZmCalBaseView.getWorkingHours()); 460 this.workingHours = workingHours; 461 } 462 var numCols = this._columns.length; 463 var dayWidth = this._calcColWidth(this._apptBodyDivWidth - Dwt.SCROLLBAR_WIDTH, numCols); 464 465 var allDayHeadingDiv = document.getElementById(this._allDayHeadingDivId); 466 var allDayHeadingDivHeight = Dwt.getSize(allDayHeadingDiv).y; 467 468 var currentX = 0; 469 var topBorderYPos = AjxEnv.isIE ? ZmCalDayTabView._TAB_BORDER_WIDTH : ZmCalColView._ALL_DAY_SEP_HEIGHT-ZmCalDayTabView._TAB_BORDER_WIDTH; 470 471 for (var i = 0; i < numCols; i++) { 472 var col = this._columns[i]; 473 474 // position day heading 475 var day = this._days[col.dayIndex]; 476 // Fix for bug: 66603. Adjust position X & Y calendar title bubble 477 this._setBounds(col.titleId, currentX, Dwt.DEFAULT, dayWidth-ZmCalDayTabView._TAB_BORDER_MARGIN, ZmCalColView._DAY_HEADING_HEIGHT); 478 col.apptX = currentX + 2 ; //ZZZ 479 col.apptWidth = dayWidth - 3*this._daySepWidth - ZmCalDayTabView._TAB_SEP_WIDTH; //ZZZZ 480 col.allDayX = col.apptX; 481 col.allDayWidth = dayWidth - ZmCalDayTabView._TAB_SEP_WIDTH; // doesn't include sep 482 483 //split into half hrs sections 484 var dayIndex = day.date.getDay(), 485 workingHrs = this.workingHours[dayIndex], 486 pos = this.getPostionForWorkingHourDiv(dayIndex, 0); 487 488 if(this._scheduleMode && day.isWorkingDay) { 489 this.layoutWorkingHoursDiv(col.workingHrsFirstDivId, pos, currentX, dayWidth-ZmCalDayTabView._TAB_BORDER_MARGIN); 490 if( workingHrs.startTime.length >= 2 && 491 workingHrs.endTime.length >= 2) { 492 493 pos = this.getPostionForWorkingHourDiv(dayIndex, 1); 494 this.layoutWorkingHoursDiv(col.workingHrsSecondDivId, pos, currentX, dayWidth-ZmCalDayTabView._TAB_BORDER_MARGIN); 495 496 } 497 } 498 //set tab borders 499 this._setBounds(col.borderTopDivId, currentX+this._bodyX, topBorderYPos, dayWidth-ZmCalDayTabView._TAB_BORDER_MARGIN, Dwt.CLEAR); 500 // Fix for bug: 66603. Adjust position X of title border separator 501 this._setBounds(col.borderBottomDivId, currentX, 0, dayWidth-ZmCalDayTabView._TAB_BORDER_MARGIN, Dwt.CLEAR); 502 this._setBounds(col.borderLeftDivId, currentX, 0, ZmCalDayTabView._TAB_BORDER_WIDTH, this._apptBodyDivHeight); 503 this._setBounds(col.borderRightDivId, currentX+dayWidth-ZmCalDayTabView._TAB_BORDER_WIDTH-ZmCalDayTabView._TAB_BORDER_MARGIN, 0, ZmCalDayTabView._TAB_BORDER_WIDTH, this._apptBodyDivHeight); 504 505 this._setBounds(col.borderLeftAllDayDivId, currentX, 0, ZmCalDayTabView._TAB_BORDER_WIDTH, this._allDayDivHeight+ZmCalDayTabView._TAB_BORDER_WIDTH+1); 506 this._setBounds(col.borderTopAllDayDivId, currentX, 0, dayWidth-ZmCalDayTabView._TAB_BORDER_MARGIN, Dwt.CLEAR); 507 this._setBounds(col.borderRightAllDayDivId, currentX+dayWidth-ZmCalDayTabView._TAB_BORDER_WIDTH-ZmCalDayTabView._TAB_BORDER_MARGIN, 0, ZmCalDayTabView._TAB_BORDER_WIDTH, this._allDayDivHeight+ZmCalDayTabView._TAB_BORDER_WIDTH+1); 508 509 currentX += dayWidth; 510 511 if (i == numCols-1) { 512 //If the border div is last border div add 6 to the width 513 this._setBounds(col.daySepDivId, currentX-ZmCalDayTabView._TAB_BORDER_MARGIN, 0, ZmCalDayTabView._TAB_SEP_WIDTH+6, this._apptBodyDivHeight); 514 } 515 else { 516 this._setBounds(col.daySepDivId, currentX-ZmCalDayTabView._TAB_BORDER_MARGIN, 0, ZmCalDayTabView._TAB_SEP_WIDTH-1, this._apptBodyDivHeight); 517 } 518 519 currentX += this._daySepWidth; 520 } 521 }; 522 523 ZmCalDayTabView.prototype._toggleView = 524 function() { 525 if(!this._mergedView) { 526 this._mergedView = true; 527 this._toggleBtn.setText(ZmMsg.calTabsSplit); 528 } 529 else { 530 this._mergedView = false; 531 this._toggleBtn.setText(ZmMsg.calTabsMerge); 532 } 533 this.set(this._list, null, true); 534 }; 535 536 ZmCalDayTabView.prototype._resetCalendarData = 537 function() { 538 // TODO: optimize: if calendar list is same, skip! 539 var titleParentEl = document.getElementById(this._allDayHeadingDivId), 540 dayParentEl = document.getElementById(this._apptBodyDivId), 541 allDaySepEl = document.getElementById(this._allDaySepDivId), 542 allDayDivEl = document.getElementById(this._allDayDivId), 543 cal, 544 calColor, 545 mergedCal, 546 calMergerdTabColor, 547 col, 548 html, 549 calId, 550 div, 551 i, 552 k; 553 // remove existing 554 // TODO: optimize, add/remove depending on new calendar length 555 if (this._numCalendars > 0) { 556 for (i = 0; i < this._numCalendars; i++) { 557 col = this._columns[i]; 558 this._removeNode(col.titleId); 559 //this._removeNode(col.headingDaySepDivId); 560 this._removeNode(col.daySepDivId); 561 this._removeNode(col.borderBottomDivId); 562 this._removeNode(col.borderLeftDivId); 563 this._removeNode(col.borderRightDivId); 564 this._removeNode(col.borderTopDivId); 565 this._removeNode(col.borderLeftAllDayDivId); 566 this._removeNode(col.borderTopAllDayDivId); 567 this._removeNode(col.borderRightAllDayDivId); 568 this._removeNode(col.workingHrsFirstChildDivId); 569 this._removeNode(col.workingHrsSecondChildDivId); 570 this._removeNode(col.workingHrsFirstDivId); 571 this._removeNode(col.workingHrsSecondDivId); 572 } 573 } 574 575 this._calendars = this._controller.getCheckedCalendars(); 576 this._calendars.sort(ZmFolder.sortCompareNonMail); 577 this._folderIdToColIndex = {}; 578 this._columns = []; 579 this._numCalendars = this._mergedView ? 1 : this._calendars.length; 580 581 this._layoutMap = []; 582 this._unionBusyData = []; // 0-47, one slot per half hour, 48 all day 583 this._unionBusyDataToolTip = []; // tool tips 584 585 for (i = 0; i < this._numCalendars; i++) { 586 cal = this._calendars[i]; 587 calId = cal.id ? cal.id : ""; 588 col = this._columns[i] = { 589 index: i, 590 dayIndex: 0, 591 cal: cal, 592 titleId: Dwt.getNextId(), 593 headingDaySepDivId: Dwt.getNextId(), 594 daySepDivId: Dwt.getNextId(), 595 workingHrsFirstDivId: Dwt.getNextId(), 596 workingHrsSecondDivId: Dwt.getNextId(), 597 apptX: 0, // computed in layout 598 apptWidth: 0, // computed in layout 599 allDayX: 0, // computed in layout 600 allDayWidth: 0, // computed in layout 601 borderLeftDivId: Dwt.getNextId(), 602 borderRightDivId: Dwt.getNextId(), 603 borderTopDivId: Dwt.getNextId(), 604 borderBottomDivId: Dwt.getNextId(), 605 borderLeftAllDayDivId: Dwt.getNextId(), 606 borderTopAllDayDivId: Dwt.getNextId(), 607 borderRightAllDayDivId: Dwt.getNextId(), 608 workingHrsFirstChildDivId: Dwt.getNextId(), 609 workingHrsSecondChildDivId: Dwt.getNextId() 610 }; 611 calColor = this._mergedView ? "" : cal.rgb; 612 this._folderIdToColIndex[cal.id] = col; 613 if (cal.isRemote() && cal.rid && cal.zid) { 614 this._folderIdToColIndex[cal.zid + ":" + cal.rid] = col; 615 } 616 617 this._createDivForColumn(col.workingHrsFirstDivId, dayParentEl, "", "#FFFFFF"); 618 div = this._createDivForColumn(col.workingHrsFirstChildDivId, col.workingHrsFirstDivId, "ImgCalendarDayGrid"); 619 div.setAttribute(ZmCalDayTabView.ATTR_CAL_ID, calId); 620 this._createDivForColumn(col.workingHrsSecondDivId, dayParentEl, "", "#FFFFFF"); 621 div = this._createDivForColumn(col.workingHrsSecondChildDivId, col.workingHrsSecondDivId, "ImgCalendarDayGrid"); 622 div.setAttribute(ZmCalDayTabView.ATTR_CAL_ID, calId); 623 this._createDivForColumn(col.borderBottomDivId, titleParentEl, "ZmDayTabSeparator", calColor, calColor); 624 625 // Fix for bug: 66603. The class adjusts width of calendar title bubbles 626 div = this._createDivForColumn(col.titleId, titleParentEl, this._mergedView ? "" : "ZmCalDayTab ZmCalDayMerged", calColor, calColor); 627 div.style.top = ZmCalDayTabView._TAB_BORDER_WIDTH + 'px'; 628 629 // Fix for bug: 84268. Removed calendar titles from the merged view. 630 if (!this._mergedView) { 631 div.style.top = ZmCalDayTabView._TAB_BORDER_WIDTH + 'px'; 632 div.style.color = AjxUtil.getForegroundColor(calColor); 633 div.innerHTML = cal.getName(); 634 } 635 636 this._createDivForColumn(col.borderLeftAllDayDivId, allDayDivEl, "ZmDayTabSeparator", calColor, calColor); 637 this._createDivForColumn(col.borderTopAllDayDivId, allDayDivEl, "ZmDayTabSeparator", calColor, calColor); 638 this._createDivForColumn(col.borderRightAllDayDivId, allDayDivEl, "ZmDayTabSeparator", calColor, calColor); 639 this._createDivForColumn(col.daySepDivId, dayParentEl, "ZmDayTabMarginDiv"); 640 this._createDivForColumn(col.borderLeftDivId, dayParentEl, "ZmDayTabSeparator", calColor, calColor); 641 this._createDivForColumn(col.borderRightDivId, dayParentEl, "ZmDayTabSeparator", calColor, calColor); 642 this._createDivForColumn(col.borderTopDivId, allDaySepEl, "ZmDayTabSeparator", calColor, calColor); 643 644 } 645 }; 646 647 ZmCalDayTabView.prototype._createDivForColumn = 648 function(id, parentEl, className, bgColor, borderColor, position, isSpan, calId) { 649 var div = document.createElement(isSpan ? "span" : "div"); 650 div.className = className ? className : ""; 651 div.id = id; 652 div.style.position = position ? position : 'absolute'; 653 if(bgColor) { div.style.backgroundColor = bgColor; } 654 if(borderColor) { div.style.borderColor = borderColor; } 655 if(parentEl) { 656 parentEl = typeof parentEl === "string" ? document.getElementById(parentEl) : parentEl; 657 parentEl.appendChild(div); 658 } 659 return div; 660 }; 661 662 663 ZmCalDayTabView.prototype._layoutAllDayAppts = 664 function() { 665 var rows = this._allDayApptsRowLayouts; 666 if (!rows) { return; } 667 668 var rowY = ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD + 2; 669 for (var i=0; i < rows.length; i++) { 670 var row = rows[i]; 671 var num = this._mergedView ? 1 : this._numCalendars; 672 for (var j=0; j < num; j++) { 673 var slot = row[j]; 674 if (slot.data) { 675 var appt = slot.data.appt; 676 var div = document.getElementById(this._getItemId(appt)); 677 if(div) { 678 if (!this._mergedView) { 679 var cal = this._getColForFolderId(appt.folderId); 680 this._positionAppt(div, cal.allDayX+0, rowY); 681 this._sizeAppt(div, (cal.allDayWidth + this._daySepWidth) - this._daySepWidth - 1 - ZmCalDayTabView._TAB_SEP_WIDTH, 682 ZmCalColView._ALL_DAY_APPT_HEIGHT); 683 } else { 684 this._positionAppt(div, this._columns[j].allDayX+0, rowY); 685 this._sizeAppt(div, ((this._columns[j].allDayWidth + this._daySepWidth) * slot.data.numDays) - this._daySepWidth - 1 - ZmCalDayTabView._TAB_SEP_WIDTH, 686 ZmCalColView._ALL_DAY_APPT_HEIGHT); 687 } 688 } 689 } 690 } 691 rowY += ZmCalColView._ALL_DAY_APPT_HEIGHT + ZmCalColView._ALL_DAY_APPT_HEIGHT_PAD; 692 } 693 }; 694 695 ZmCalDayTabView.prototype._getBoundsForAppt = 696 function(appt) { 697 var sd = appt.startDate; 698 var endOfDay = new Date(sd); 699 endOfDay.setHours(23,59,59,999); 700 var et = Math.min(appt.getEndTime(), endOfDay.getTime()); 701 if (!this._mergedView) 702 return this._getBoundsForCalendar(sd, et - sd.getTime(), appt.folderId); 703 else 704 return this._getBoundsForDate(sd, et - sd.getTime()); 705 }; 706 707 ZmCalDayTabView.prototype._getBoundsForDate = 708 function(d, duration, col) { 709 var durationMinutes = duration / 1000 / 60; 710 durationMinutes = Math.max(durationMinutes, 22); 711 var h = d.getHours(); 712 var m = d.getMinutes(); 713 if (col == null) { 714 var day = this._getDayForDate(d); 715 col = day ? this._columns[day.index] : null; 716 } 717 if (col == null) return null; 718 return new DwtRectangle(col.apptX, ((h+m/60) * ZmCalColView._HOUR_HEIGHT), 719 col.apptWidth, (ZmCalColView._HOUR_HEIGHT / 60) * durationMinutes); 720 }; 721 722 ZmCalDayTabView.prototype._resetList = 723 function() { 724 var list = this.getList(); 725 var size = list ? list.size() : 0; 726 if (size == 0) return; 727 728 for (var i=0; i < size; i++) { 729 var ao = list.get(i); 730 var id = this._getItemId(ao); 731 var appt = document.getElementById(id); 732 if (appt) { 733 appt.parentNode.removeChild(appt); 734 this._data[id] = null; 735 } 736 } 737 //list.removeAll(); 738 this.removeAll(); 739 }; 740 741 ZmCalDayTabView.prototype._computeAllDayApptLayout = 742 function() { 743 var adlist = this._allDayApptsList; 744 adlist.sort(ZmCalBaseItem.compareByTimeAndDuration); 745 746 for (var i=0; i < adlist.length; i++) { 747 var appt = adlist[i]; 748 var data = this._allDayAppts[appt.getUniqueId()]; 749 if (data) { 750 var col = this._mergedView ? this._columns[0] : this._getColForFolderId(data.appt.folderId); 751 if (col) this._findAllDaySlot(col.index, data); 752 } 753 } 754 }; 755 756 ZmCalDayTabView.prototype._layoutUnionDataDiv = 757 function(gridEl, allDayEl, i, data, numCols) { 758 var enable = data instanceof Object; 759 var id = this._unionBusyDivIds[i]; 760 var divEl = null; 761 762 if (id == null) { 763 if (!enable) { return; } 764 id = this._unionBusyDivIds[i] = Dwt.getNextId(); 765 var divEl = document.createElement("div"); 766 divEl.style.position = 'absolute'; 767 divEl.className = "calendar_sched_union_div"; 768 this.associateItemWithElement(null, divEl, ZmCalBaseView.TYPE_SCHED_FREEBUSY, id, {index:i}); 769 770 Dwt.setOpacity(divEl, 40); 771 772 if (i == 48) { 773 //have to resize every layout, since all day div height might change 774 allDayEl.appendChild(divEl); 775 } else { 776 // position/size once right here! 777 Dwt.setBounds(divEl, 1, ZmCalColView._HALF_HOUR_HEIGHT*i+1, ZmCalDayTabView._UNION_DIV_WIDTH-2 , ZmCalColView._HALF_HOUR_HEIGHT-2); 778 gridEl.appendChild(divEl); 779 } 780 781 } else { 782 divEl = document.getElementById(id); 783 } 784 // have to relayout each time 785 //if (i == 48) Dwt.setBounds(divEl, 1, 1, ZmCalColView._UNION_DIV_WIDTH-2, this._allDayDivHeight-2); 786 787 var num = 0; 788 for (var key in data) num++; 789 790 Dwt.setOpacity(divEl, 20 + (60 * (num/numCols))); 791 Dwt.setVisibility(divEl, enable); 792 }; 793 794 /* 795 * compute appt layout for appts that aren't all day 796 */ 797 ZmCalDayTabView.prototype._computeApptLayout = 798 function() { 799 // DBG.println("_computeApptLayout"); 800 // DBG.timePt("_computeApptLayout: start", true); 801 var layouts = this._layouts = new Array(); 802 var layoutsDayMap = []; 803 var layoutsAllDay = []; 804 var list = this.getList(); 805 if (!list) return; 806 807 var size = list.size(); 808 if (size == 0) { return; } 809 810 var overlap = null; 811 var overlappingCol = null; 812 813 for (var i=0; i < size; i++) { 814 var ao = list.get(i); 815 816 if (ao.isAllDayEvent()) { 817 continue; 818 } 819 820 var newLayout = { appt: ao, col: 0, maxcol: -1}; 821 822 overlap = null; 823 overlappingCol = null; 824 825 var asd = ao.startDate; 826 var aed = ao.endDate; 827 828 var asdDate = asd.getDate(); 829 var aedDate = aed.getDate(); 830 831 var checkAllLayouts = (asdDate != aedDate); 832 var layoutCheck = []; 833 834 // if a appt starts n end in same day, it should be compared only with 835 // other appts on same day and with those which span multiple days 836 if (checkAllLayouts) { 837 layoutCheck.push(layouts); 838 } else { 839 layoutCheck.push(layoutsAllDay); 840 if (layoutsDayMap[asdDate]!=null) { 841 layoutCheck.push(layoutsDayMap[asdDate]); 842 } 843 } 844 845 // look for overlapping appts 846 for (var k = 0; k < layoutCheck.length; k++) { 847 for (var j=0; j < layoutCheck[k].length; j++) { 848 var layout = layoutCheck[k][j]; 849 if (ao.isOverlapping(layout.appt, !this._mergedView)) { 850 if (overlap == null) { 851 overlap = []; 852 overlappingCol = []; 853 } 854 overlap.push(layout); 855 overlappingCol[layout.col] = true; 856 // while we overlap, update our col 857 while (overlappingCol[newLayout.col]) { 858 newLayout.col++; 859 } 860 } 861 } 862 } 863 864 // figure out who is on our right 865 if (overlap != null) { 866 for (var c in overlap) { 867 var l = overlap[c]; 868 if (newLayout.col < l.col) { 869 if (!newLayout.right) newLayout.right = [l]; 870 else newLayout.right.push(l); 871 } else { 872 if (!l.right) l.right = [newLayout]; 873 else l.right.push(newLayout); 874 } 875 } 876 } 877 layouts.push(newLayout); 878 if (asdDate == aedDate) { 879 if(!layoutsDayMap[asdDate]) { 880 layoutsDayMap[asdDate] = []; 881 } 882 layoutsDayMap[asdDate].push(newLayout); 883 } else { 884 layoutsAllDay.push(newLayout); 885 } 886 } 887 888 // compute maxcols 889 for (var i=0; i < layouts.length; i++) { 890 this._computeMaxCols(layouts[i], -1); 891 this._layoutMap[this._getItemId(layouts[i].appt)] = layouts[i]; 892 // DBG.timePt("_computeApptLayout: computeMaxCol "+i, false); 893 } 894 895 delete layoutsAllDay; 896 delete layoutsDayMap; 897 delete layoutCheck; 898 //DBG.timePt("_computeApptLayout: end", false); 899 }; 900