1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 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, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * Creates the calendar view manager. 26 * @class 27 * This class represents the calendar view manager. 28 * 29 * @param {DwtShell} parent the element that created this view 30 * @param {ZmController} controller the controller 31 * @param {DwtDropTarget} dropTgt the drop target 32 * 33 * @extends DwtComposite 34 */ 35 ZmCalViewMgr = function(parent, controller, dropTgt) { 36 37 DwtComposite.call(this, {parent:parent, className:"ZmCalViewMgr", posStyle:Dwt.ABSOLUTE_STYLE}); 38 this.addControlListener(new AjxListener(this, this._controlListener)); 39 40 this._controller = controller; 41 this._dropTgt = dropTgt; 42 this._showNewScheduleView = appCtxt.get(ZmSetting.FREE_BUSY_VIEW_ENABLED); 43 // View hash. Holds the various views e.g. day, month, week, etc... 44 this._views = {}; 45 this._date = new Date(); 46 this._viewFactory = {}; 47 this._viewFactory[ZmId.VIEW_CAL_DAY] = ZmCalDayTabView; 48 this._viewFactory[ZmId.VIEW_CAL_WORK_WEEK] = ZmCalWorkWeekView; 49 this._viewFactory[ZmId.VIEW_CAL_WEEK] = ZmCalWeekView; 50 this._viewFactory[ZmId.VIEW_CAL_MONTH] = ZmCalMonthView; 51 this._viewFactory[ZmId.VIEW_CAL_LIST] = ZmCalListView; 52 this._viewFactory[ZmId.VIEW_CAL_FB] = ZmCalNewScheduleView; 53 54 this._viewFactory[ZmId.VIEW_CAL_TRASH] = ZmApptListView; 55 }; 56 57 ZmCalViewMgr.prototype = new DwtComposite; 58 ZmCalViewMgr.prototype.constructor = ZmCalViewMgr; 59 60 ZmCalViewMgr._SEP = 5; 61 62 ZmCalViewMgr.MIN_CONTENT_SIZE = 100; 63 64 ZmCalViewMgr.prototype.toString = 65 function() { 66 return "ZmCalViewMgr"; 67 }; 68 69 ZmCalViewMgr.prototype.TEMPLATE = "calendar.Calendar#ZmCalViewMgr"; 70 71 ZmCalViewMgr.prototype._subContentShown = false; 72 ZmCalViewMgr.prototype._subContentInitialized = false; 73 74 ZmCalViewMgr.prototype.getController = 75 function() { 76 return this._controller; 77 }; 78 79 // sets need refresh on all views 80 ZmCalViewMgr.prototype.setNeedsRefresh = 81 function() { 82 for (var name in this._views) { 83 this._views[name].setNeedsRefresh(true); 84 } 85 }; 86 87 ZmCalViewMgr.prototype.layoutWorkingHours = 88 function() { 89 for (var name in this._views) { 90 if (name == ZmId.VIEW_CAL_DAY || 91 name == ZmId.VIEW_CAL_WORK_WEEK || 92 name == ZmId.VIEW_CAL_WEEK || 93 name == ZmId.VIEW_CAL_FB 94 ) 95 this._views[name].layoutWorkingHours(); 96 } 97 }; 98 99 ZmCalViewMgr.prototype.needsRefresh = 100 function(viewId) { 101 viewId = viewId || this._currentViewName; 102 var view = this._views[viewId]; 103 return view.needsRefresh ? view.needsRefresh() : false; 104 }; 105 106 ZmCalViewMgr.prototype.getCurrentView = 107 function() { 108 return this._views[this._currentViewName]; 109 }; 110 111 ZmCalViewMgr.prototype.getCurrentViewName = 112 function() { 113 return this._currentViewName; 114 }; 115 116 ZmCalViewMgr.prototype.getView = 117 function(viewName) { 118 return this._views[viewName]; 119 }; 120 121 ZmCalViewMgr.prototype.getTitle = 122 function() { 123 return this.getCurrentView().getTitle(); 124 }; 125 126 ZmCalViewMgr.prototype.getDate = 127 function() { 128 return this._date; 129 }; 130 131 ZmCalViewMgr.prototype.setDate = 132 function(date, duration, roll) { 133 this._date = new Date(date.getTime()); 134 this._duration = duration; 135 if (this._currentViewName) { 136 var view = this._views[this._currentViewName]; 137 view.setDate(date, duration, roll); 138 } 139 }; 140 141 ZmCalViewMgr.prototype.createView = 142 function(viewName) { 143 var view = new this._viewFactory[viewName](this, DwtControl.ABSOLUTE_STYLE, this._controller, this._dropTgt); 144 145 if (viewName != ZmId.VIEW_CAL_TRASH) { 146 view.addTimeSelectionListener(new AjxListener(this, this._viewTimeSelectionListener)); 147 view.addDateRangeListener(new AjxListener(this, this._viewDateRangeListener)); 148 view.addViewActionListener(new AjxListener(this, this._viewActionListener)); 149 } 150 this._views[viewName] = view; 151 if (viewName == ZmId.VIEW_CAL_TRASH) { 152 var controller = this._controller; 153 view.addSelectionListener(new AjxListener(controller, controller._listSelectionListener)); 154 view.addActionListener(new AjxListener(controller, controller._listActionListener)); 155 } 156 return view; 157 }; 158 159 ZmCalViewMgr.prototype.getSubContentView = function() { 160 return this._list || this._createSubContent(); 161 }; 162 163 ZmCalViewMgr.prototype.getSelTrashCount = function() { 164 165 var folders = this._controller.getCheckedCalendars(true); 166 this._multiAccTrashQuery = []; 167 for (var i=0; i< folders.length; i++) { 168 if (folders[i].nId == ZmOrganizer.ID_TRASH) { 169 this._multiAccTrashQuery.push(['inid:', '"', folders[i].getAccount().id, ':', ZmOrganizer.ID_TRASH, '"'].join("")); 170 } 171 } 172 return this._multiAccTrashQuery.length; 173 }; 174 175 ZmCalViewMgr.prototype.setSubContentVisible = function(visible) { 176 177 if (appCtxt.multiAccounts) { 178 var selCount = this.getSelTrashCount(); 179 // if no trash is checked 180 if (selCount < 1) { 181 this._subContentShown = false; 182 this._subContentInitialized = true; 183 this._controller.setCurrentListView(null); 184 } else { 185 // if more one or more trash is checked 186 this._subContentShown = true; 187 } 188 } else if (this._subContentShown != visible) { 189 this._subContentShown = visible; 190 if (!visible) { 191 this._controller.setCurrentListView(null); 192 } 193 } 194 this._layout(); 195 }; 196 197 ZmCalViewMgr.prototype._createSubContent = function() { 198 if (!this._subContentShown) return null; 199 if (this._subContentInitialized) return this._list; 200 201 this._subContentInitialized = true; 202 203 this._sash = new DwtSash({parent:this,posStyle:Dwt.ABSOLUTE_STYLE,style:DwtSash.VERTICAL_STYLE}); 204 this._sash.registerCallback(this._handleSashAdjustment, this); 205 this._list = this.createView(ZmId.VIEW_CAL_TRASH); 206 this._list.set(new AjxVector([])); 207 208 this._populateTrashListView(this._list); 209 return this._list; 210 }; 211 212 ZmCalViewMgr.prototype._handleSashAdjustment = function(delta) { 213 // sash moved too far up 214 var sashLocation = this._sash.getLocation(); 215 if (sashLocation.y + delta < ZmCalViewMgr.MIN_CONTENT_SIZE) { 216 delta = ZmCalViewMgr.MIN_CONTENT_SIZE - sashLocation.y; 217 } 218 219 // sash moved to0 far down 220 else { 221 var size = this.getSize(); 222 if (sashLocation.y + delta > size.y - ZmCalViewMgr.MIN_CONTENT_SIZE) { 223 delta = size.y - ZmCalViewMgr.MIN_CONTENT_SIZE - sashLocation.y; 224 } 225 } 226 227 // adjust sub-content 228 if (delta != 0) { 229 var listSize = this._list.getSize(); 230 this._list.setSize(listSize.x, listSize.y - delta); 231 this._layoutControls(true); 232 } 233 234 return delta; 235 }; 236 237 ZmCalViewMgr.prototype._populateTrashListView = function(listView) { 238 var params = { 239 searchFor:ZmItem.APPT, 240 limit:20, 241 types:AjxVector.fromArray([ZmId.ITEM_APPOINTMENT]), 242 forceSearch: true, 243 // noRender: true, 244 callback: new AjxCallback(this, this._populateTrashListViewResults, [listView]) 245 }; 246 247 if (appCtxt.multiAccounts) { 248 params.query = this._multiAccTrashQuery.join(" OR "); 249 params.account = appCtxt.accountList.mainAccount.name; 250 } else { 251 params.query = "inid:"+ZmOrganizer.ID_TRASH; 252 } 253 var search = new ZmSearch(params); 254 search.execute(params); 255 }; 256 257 ZmCalViewMgr.prototype._populateTrashListViewResults = function(listView, results) { 258 var data = results && results._data; 259 var apptList = data && data._results && data._results.APPT; 260 listView.set(apptList || new AjxVector([])); 261 }; 262 263 ZmCalViewMgr.prototype.addViewActionListener = 264 function(listener) { 265 this.addListener(ZmCalBaseView.VIEW_ACTION, listener); 266 }; 267 268 ZmCalViewMgr.prototype.removeViewActionListener = 269 function(listener) { 270 this.removeListener(ZmCalBaseView.VIEW_ACTION, listener); 271 }; 272 273 ZmCalViewMgr.prototype.addTimeSelectionListener = 274 function(listener) { 275 this.addListener(ZmCalBaseView.TIME_SELECTION, listener); 276 }; 277 278 ZmCalViewMgr.prototype.removeTimeSelectionListener = 279 function(listener) { 280 this.removeListener(ZmCalBaseView.TIME_SELECTION, listener); 281 }; 282 283 ZmCalViewMgr.prototype.addDateRangeListener = 284 function(listener) { 285 this.addListener(DwtEvent.DATE_RANGE, listener); 286 }; 287 288 ZmCalViewMgr.prototype.removeDateRangeListener = 289 function(listener) { 290 this.removeListener(DwtEvent.DATE_RANGE, listener); 291 }; 292 293 ZmCalViewMgr.prototype.setView = 294 function(viewName) { 295 if (viewName != this._currentViewName) { 296 if (this._currentViewName) { 297 this._views[this._currentViewName].setLocation(Dwt.LOC_NOWHERE, Dwt.LOC_NOWHERE); 298 } 299 var view = this._views[viewName]; 300 this._currentViewName = viewName; 301 302 var vd = view.getDate(); 303 if (vd == null || (view.getDate().getTime() != this._date.getTime())) { 304 view.setDate(this._date, this._duration, true); 305 } 306 this._layout(); 307 } 308 }; 309 310 ZmCalViewMgr.prototype._layout = 311 function() { 312 // create sub-content, if needed 313 var showSubContent = this._subContentShown; 314 if (showSubContent && !this._subContentInitialized) { 315 this._createSubContent(); 316 317 // NOTE: The list maintains its size so we can toggle back and forth 318 var size = this.getSize(); 319 this._list.setSize(null, size.y / 3); 320 } 321 // Always re-populate trash list for multi-accounts 322 if (appCtxt.multiAccounts && this._subContentInitialized) { 323 this._populateTrashListView(this._list); 324 } 325 326 // show sub-content 327 if (this._sash) { 328 this._sash.setVisible(showSubContent); 329 this._list.setVisible(showSubContent); 330 } 331 332 // layout the controls 333 this._layoutControls(); 334 }; 335 336 ZmCalViewMgr.prototype._layoutControls = function(skipSash) { 337 // size sub-content 338 var size = this.getSize(); 339 var contentHeight = size.y; 340 if (this._subContentShown) { 341 var listSize = this._list.getSize(); 342 var sashSize = this._sash.getSize(); 343 var subContentHeight = listSize.y + sashSize.y; 344 345 contentHeight -= subContentHeight; 346 347 if (!skipSash) { 348 this._sash.setBounds(0, contentHeight, size.x, sashSize.y); 349 } 350 this._list.setBounds(0, contentHeight+sashSize.y, size.x, listSize.y); 351 } 352 353 // size content 354 var view = this._views[this._currentViewName]; 355 view.setBounds(0, 0, size.x, contentHeight); 356 357 //need to reset layout for time view renderings 358 if (view instanceof ZmCalBaseView) view.layoutView(); 359 }; 360 361 ZmCalViewMgr.prototype._controlListener = 362 function(ev) { 363 if (ev.oldHeight != ev.newHeight || 364 ev.oldWidth != ev.newWidth) 365 { 366 this._layout(); 367 } 368 }; 369 370 ZmCalViewMgr.prototype._viewTimeSelectionListener = 371 function(ev) { 372 this.notifyListeners(ZmCalBaseView.TIME_SELECTION, ev); 373 }; 374 375 376 ZmCalViewMgr.prototype._viewActionListener = 377 function(ev) { 378 this.notifyListeners(ZmCalBaseView.VIEW_ACTION, ev); 379 }; 380 381 ZmCalViewMgr.prototype._viewSelectionListener = 382 function(ev) { 383 //this.notifyListeners(ZmCalBaseView.TIME_SELECTION, ev); 384 }; 385 386 ZmCalViewMgr.prototype._viewDateRangeListener = 387 function(ev) { 388 // Notify any listeners 389 if (this.isListenerRegistered(DwtEvent.DATE_RANGE)) { 390 this.notifyListeners(DwtEvent.DATE_RANGE, ev); 391 } 392 }; 393