1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 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) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 /**
 24  * Appointment list view.
 25  */
 26 ZmApptListView = function(parent, posStyle, controller, dropTgt) {
 27     if (arguments.length == 0) return;
 28     var params = Dwt.getParams(arguments, ZmApptListView.PARAMS);
 29     params.headerList = this._getHeaderList();
 30     params.view = params.view || ZmId.VIEW_CAL_TRASH;
 31     ZmListView.call(this, params);
 32     this._bSortAsc = true;
 33     this._defaultSortField = ZmItem.F_DATE;
 34     this.setDragSource(params.controller._dragSrc);
 35 };
 36 ZmApptListView.prototype = new ZmListView;
 37 ZmApptListView.prototype.constructor = ZmApptListView;
 38 
 39 ZmApptListView.prototype.toString = function() {
 40     return "ZmApptListView";
 41 };
 42 
 43 ZmApptListView.PARAMS = ["parent","posStyle","controller","dropTgt"];
 44 
 45 //
 46 // Constants
 47 //
 48 
 49 ZmApptListView.COL_WIDTH_DATE			= ZmMsg.COLUMN_WIDTH_DATE_CAL;
 50 ZmApptListView.COL_WIDTH_LOCATION		= ZmMsg.COLUMN_WIDTH_LOCATION_CAL;
 51 ZmApptListView.COL_WIDTH_STATUS			= ZmMsg.COLUMN_WIDTH_STATUS_CAL;
 52 ZmApptListView.COL_WIDTH_FOLDER			= ZmMsg.COLUMN_WIDTH_FOLDER_CAL;
 53 
 54 //
 55 // Public methods
 56 //
 57 
 58 ZmApptListView.prototype.getApptList = function() {
 59     return this._apptList;
 60 };
 61 
 62 ZmApptListView.prototype.refresh = function() {
 63     if (this.needsRefresh()) {
 64         var selection = this.getSelection();
 65         this.set(this.getApptList());
 66         if (selection) {
 67             this.setSelectedItems(selection);
 68         }
 69         this.setNeedsRefresh(false);
 70     }
 71 };
 72 
 73 ZmApptListView.prototype.needsRefresh = function() {
 74     var controller = this._controller;
 75     return controller.getCurrentView().needsRefresh();
 76 };
 77 
 78 ZmApptListView.prototype.setNeedsRefresh = function(needsRefresh) {
 79     var controller = this._controller;
 80     if(controller.getCurrentView().setNeedsRefresh){
 81         return controller.getCurrentView().setNeedsRefresh(needsRefresh);
 82     }
 83     return null;
 84 };
 85 
 86 //to override
 87 ZmApptListView.prototype.getAtttendees = function() {
 88     return null;
 89 };
 90 
 91 ZmApptListView.prototype.updateTimeIndicator=function(force){
 92     //override
 93 };
 94 
 95 ZmApptListView.prototype.startIndicatorTimer=function(force){
 96     //override
 97 };
 98 
 99 ZmApptListView.prototype.checkIndicatorNeed=function(viewId,startDate){
100     //override
101 };
102 
103 //
104 // Protected methods
105 //
106 
107 ZmApptListView.prototype._getToolTip =
108 function(params) {
109 	var tooltip, field = params.field, item = params.item;
110 	if (field && (field == ZmItem.F_SELECTION || field == ZmItem.F_TAG)) {
111 		tooltip = ZmListView.prototype._getToolTip.apply(this, arguments);
112 	} else if (item.getToolTip) {
113 		tooltip = item.getToolTip(this._controller);
114 	}
115 	return tooltip;
116 };
117 
118 ZmApptListView.prototype._sortList = function(list, column) {
119 	ZmApptListView.sortByAsc = this._bSortAsc;
120 
121 	switch (column) {
122 		case ZmItem.F_SUBJECT:	list.sort(ZmApptListView._sortSubject); break;
123 		case ZmItem.F_STATUS:	list.sort(ZmApptListView._sortStatus); break;
124 		case ZmItem.F_FOLDER:	list.sort(ZmApptListView._sortFolder); break;
125 		case ZmItem.F_DATE:		list.sort(ZmApptListView._sortDate); break;
126 	}
127 };
128 
129 ZmApptListView.prototype._sortColumn = function(columnItem, bSortAsc) {
130 	this._defaultSortField = columnItem._field;
131 
132 	var list = this.getList();
133 	list = list && list.clone();
134 	if (list) {
135 		this._sortList(list, columnItem._field);
136 		this.set(list, null, true);
137 	}
138 };
139 
140 ZmApptListView.prototype._getHeaderToolTip = function(field, itemIdx) {
141 	switch (field) {
142 		case ZmItem.F_LOCATION: return ZmMsg.location;
143 		case ZmItem.F_FOLDER:	return ZmMsg.calendar;
144 		case ZmItem.F_DATE:		return ZmMsg.date;
145         case ZmItem.F_RECURRENCE:return ZmMsg.recurrence;       
146 	}
147 	return ZmListView.prototype._getHeaderToolTip.call(this, field, itemIdx);
148 };
149 
150 ZmApptListView.prototype._getHeaderList = function() {
151 	var hList = [];
152 
153 	if (appCtxt.get(ZmSetting.SHOW_SELECTION_CHECKBOX)) {
154 		hList.push(new DwtListHeaderItem({field:ZmItem.F_SELECTION, icon:"CheckboxUnchecked", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.selection}));
155 	}
156 	if (appCtxt.get(ZmSetting.TAGGING_ENABLED)) {
157 		hList.push(new DwtListHeaderItem({field:ZmItem.F_TAG, icon:"Tag", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.tag}));
158 	}
159 	hList.push(new DwtListHeaderItem({field:ZmItem.F_ATTACHMENT, icon:"Attachment", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.attachment}));
160 	hList.push(new DwtListHeaderItem({field:ZmItem.F_SUBJECT, text:ZmMsg.subject, noRemove:true, sortable:ZmItem.F_SUBJECT}));
161 	hList.push(new DwtListHeaderItem({field:ZmItem.F_LOCATION, text:ZmMsg.location, width:ZmApptListView.COL_WIDTH_LOCATION, resizeable:true}));
162 	hList.push(new DwtListHeaderItem({field:ZmItem.F_STATUS, text:ZmMsg.status, width:ZmApptListView.COL_WIDTH_STATUS, resizeable:true, sortable:ZmItem.F_STATUS}));
163 	hList.push(new DwtListHeaderItem({field:ZmItem.F_FOLDER, text:ZmMsg.calendar, width:ZmApptListView.COL_WIDTH_FOLDER, resizeable:true, sortable:ZmItem.F_FOLDER}));
164 	hList.push(new DwtListHeaderItem({field:ZmItem.F_RECURRENCE, icon:"ApptRecur", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.recurrence}));
165 	hList.push(new DwtListHeaderItem({field:ZmItem.F_DATE, text:ZmMsg.startDate, width:ZmApptListView.COL_WIDTH_DATE, sortable:ZmItem.F_DATE}));
166 
167 	return hList;
168 };
169 
170 //
171 // DwtListView methods
172 //
173 
174 ZmApptListView.prototype._itemClicked = function() {
175     ZmListView.prototype._itemClicked.apply(this, arguments);
176     this._controller.setCurrentListView(this);
177 };
178 
179 ZmApptListView.prototype.set = function(apptList, skipMiniCalUpdate, skipSort) {
180     this._apptList = apptList;
181 	if (!skipSort) {
182 		if ((this._defaultSortField != ZmItem.F_DATE) ||
183 			(this._defaultSortField == ZmItem.F_DATE && !this._bSortAsc))
184 		{
185 			this._sortList(apptList, this._defaultSortField);
186 		}
187 	}
188 	ZmListView.prototype.set.call(this, apptList, this._defaultSortField);
189     this._resetColWidth();
190     //Does not make sense but required to make the scrollbar appear
191     var size = this.getSize();
192     this._listDiv.style.height = (size.y - DwtListView.HEADERITEM_HEIGHT)+"px";
193 };
194 
195 ZmApptListView.prototype._getItemId = function(item) {
196 	var itemId = (item && item.id) ? item.getUniqueId(true) : Dwt.getNextId();
197 	return DwtId.getListViewItemId(DwtId.WIDGET_ITEM, this._view, itemId);
198 };
199 
200 ZmApptListView.prototype._getFieldId = function(item, field) {
201 	var itemId = (item && item.getUniqueId) ? item.getUniqueId(true) : item.id;
202 	return DwtId.getListViewItemId(DwtId.WIDGET_ITEM_FIELD, this._view, itemId, field);
203 };
204 
205 ZmApptListView.prototype._getCellId = function(item, field) {
206 	if (field == ZmItem.F_SUBJECT || field == ZmItem.F_DATE || field == ZmItem.F_LOCATION || field == ZmItem.F_STATUS || field == ZmItem.F_FOLDER) {
207 		return this._getFieldId(item, field);
208 	}
209 };
210 
211 ZmApptListView.prototype._getCellContents = function(htmlArr, idx, appt, field, colIdx, params) {
212 	if (field == ZmItem.F_RECURRENCE) {
213 		var icon;
214 		if (appt.isException) {
215 			icon = "ApptExceptionIndicator";
216 		}
217         else if (appt.isRecurring()) {
218 			icon = "ApptRecur";
219 		}
220 		idx = this._getImageHtml(htmlArr, idx, icon, this._getFieldId(appt, field));
221 
222 	}
223     else if (field == ZmItem.F_SUBJECT) {
224 		htmlArr[idx++] = AjxStringUtil.htmlEncode(appt.getName(), true);
225 		if (appCtxt.get(ZmSetting.SHOW_FRAGMENTS) && appt.fragment) {
226 			htmlArr[idx++] = this._getFragmentSpan(appt);
227 		}
228 
229 	}
230     else if (field == ZmItem.F_LOCATION) {
231 		htmlArr[idx++] = AjxStringUtil.htmlEncode(appt.getLocation(), true);
232 
233 	}
234     else if (field == ZmItem.F_STATUS) {
235 		if (appt.otherAttendees) {
236 			htmlArr[idx++] = appt.getParticipantStatusStr();
237 		}
238 
239 	}
240     else if (field == ZmItem.F_FOLDER) {
241 		var calendar = appt.getFolder();
242         var rgb = calendar.rgb || ZmOrganizer.COLOR_VALUES[calendar.color||ZmOrganizer.DEFAULT_COLOR[ZmOrganizer.CALENDAR]]; 
243 		var colors = ZmCalBaseView._getColors(rgb);
244 		var subs = {
245             folder: calendar,
246 			folderColor: colors.standard.header.bgcolor,
247 			folderName: calendar.getName(),
248             id: Dwt.getNextId()
249 		};
250 		htmlArr[idx++] = AjxTemplate.expand("calendar.Calendar#ListViewFolder", subs);
251 
252 	}
253     else if (field == ZmItem.F_DATE) {
254 		htmlArr[idx++] = (appt.isAllDayEvent())
255 			? AjxMessageFormat.format(ZmMsg.apptDateTimeAllDay, [appt.startDate])
256 			: AjxMessageFormat.format(ZmMsg.apptDateTime, [appt.startDate, appt.startDate]);
257 
258 	}
259     else {
260 		idx = ZmListView.prototype._getCellContents.apply(this, arguments);
261 	}
262 
263 	return idx;
264 };
265 
266 ZmApptListView.prototype._getLabelForField =
267 function(appt, field) {
268     switch (field) {
269     case ZmItem.F_RECURRENCE:
270         if (appt.isException) {
271             return ZmMsg.recurrenceException;
272         } else if (appt.isRecurring()) {
273             return ZmMsg.recurrence;
274         } else {
275             return '';
276         }
277 
278     case ZmItem.F_SUBJECT:
279         return appt.getName() || ZmMsg.noSubject;
280 
281     case ZmItem.F_LOCATION:
282         return appt.location || ZmMsg.noLocation;
283 
284     case ZmItem.F_STATUS:
285         return appt.otherAttendees && appt.getParticipantStatusStr();
286 
287     case ZmItem.F_FOLDER:
288         return appt.getFolder().getName();
289 
290     case ZmItem.F_ATTACHMENT:
291         return appt.hasAttach && ZmMsg.hasAttachment;
292 
293     case ZmItem.F_TAG:
294         if (appt.tags.length > 0) {
295             var tags = appt.tags.join(' & ');
296             return AjxMessageFormat.format(ZmMsg.taggedAs, [tags]);
297         }
298 
299         break;
300 
301     case ZmItem.F_DATE:
302         if (appt.isAllDayEvent()) {
303             return AjxMessageFormat.format(ZmMsg.apptDateTimeAllDay,
304                                            [appt.startDate]);
305         } else {
306             return AjxMessageFormat.format(ZmMsg.apptDateTime,
307                                            [appt.startDate, appt.startDate]);
308         }
309     }
310 
311     return ZmListView.prototype._getLabelForField.apply(this, arguments);
312 };
313 
314 //
315 // Private methods
316 //
317 
318 ZmApptListView._sortSubject = function(a, b) {
319     // Bug fix # 80458 - Convert the subject line to lower case and compare
320     var aVal = a.getName().toLowerCase();
321     var bVal = b.getName().toLowerCase();
322 
323 	if (aVal < bVal)		{ return ZmApptListView.sortByAsc ? -1 : 1; }
324 	else if (aVal > bVal)	{ return ZmApptListView.sortByAsc ? 1 : -1; }
325 	else 					{ return 0; }
326 };
327 
328 ZmApptListView._sortStatus = function(a, b) {
329 	if (!a.otherAttendees)	{ return ZmApptListView.sortByAsc ? -1 : 1; }
330 	if (!b.otherAttendees)	{ return ZmApptListView.sortByAsc ? 1 : -1; }
331 
332 	var aVal = a.getParticipantStatusStr();
333 	var bVal = b.getParticipantStatusStr();
334 
335 	if (aVal < bVal)		{ return ZmApptListView.sortByAsc ? -1 : 1; }
336 	else if (aVal > bVal)	{ return ZmApptListView.sortByAsc ? 1 : -1; }
337 	else 					{ return 0; }
338 };
339 
340 ZmApptListView._sortFolder = function(a, b) {
341 	var aVal = a.getFolder().getName();
342 	var bVal = b.getFolder().getName();
343 
344 	if (aVal < bVal)		{ return ZmApptListView.sortByAsc ? -1 : 1; }
345 	else if (aVal > bVal)	{ return ZmApptListView.sortByAsc ? 1 : -1; }
346 	else 					{ return 0; }
347 };
348 
349 ZmApptListView._sortDate = function(a, b) {
350 	var aVal = a.startDate.getTime();
351 	var bVal = b.startDate.getTime();
352 
353 	if (aVal < bVal)		{ return ZmApptListView.sortByAsc ? -1 : 1; }
354 	else if (aVal > bVal)	{ return ZmApptListView.sortByAsc ? 1 : -1; }
355 	else 					{ return 0; }
356 };
357