1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2011, 2012, 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) 2011, 2012, 2014, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 /**
 24  * Date group divides messages into the following sections:
 25  * Today
 26  * Yesterday
 27  * Day of week -- not today or yesterday, but still within this week
 28  * Last Week
 29  * Two Weeks Ago
 30  * Three Weeks Ago
 31  * Earlier this Month
 32  * Last Month
 33  * Older
 34  *
 35  */
 36 ZmMailListDateGroup = function(){
 37     this.id = ZmId.GROUPBY_DATE;
 38 	this.field = ZmItem.F_DATE;
 39 	this._weekStartDay = appCtxt.get(ZmSetting.CAL_FIRST_DAY_OF_WEEK) || 0;
 40 	var dayOfWeek = this._getToday().getDay();
 41 	this._keys = this._sortKeys(dayOfWeek, false);
 42     ZmMailListGroup.call(this);
 43 };
 44 
 45 ZmMailListDateGroup.prototype = new ZmMailListGroup;
 46 ZmMailListDateGroup.prototype.constructor =  ZmMailListDateGroup;
 47 
 48 ZmMailListDateGroup.MONDAY = "MONDAY";
 49 ZmMailListDateGroup.TUESDAY = "TUESDAY";
 50 ZmMailListDateGroup.WEDNESDAY = "WEDNESDAY";
 51 ZmMailListDateGroup.THURSDAY = "THURSDAY";
 52 ZmMailListDateGroup.FRIDAY = "FRIDAY";
 53 ZmMailListDateGroup.SATURDAY = "SATURDAY";
 54 ZmMailListDateGroup.SUNDAY = "SUNDAY";
 55 ZmMailListDateGroup.TODAY = "TODAY";
 56 ZmMailListDateGroup.YESTERDAY = "YESTERDAY";
 57 ZmMailListDateGroup.LAST_WEEK = "LAST_WEEK";
 58 ZmMailListDateGroup.TWO_WEEKS_AGO = "TWO_WEEKS_AGO";
 59 ZmMailListDateGroup.THREE_WEEKS_AGO = "THREE_WEEKS_AGO";
 60 ZmMailListDateGroup.EARLIER_THIS_MONTH = "EARLIER_THIS_MONTH";
 61 ZmMailListDateGroup.LAST_MONTH = "LAST_MONTH";
 62 ZmMailListDateGroup.OLDER = "OLDER";
 63 
 64 ZmMailListDateGroup.GROUP = [ZmMailListDateGroup.TODAY, ZmMailListDateGroup.YESTERDAY, ZmMailListDateGroup.SUNDAY, ZmMailListDateGroup.MONDAY,
 65 							 ZmMailListDateGroup.TUESDAY, ZmMailListDateGroup.WEDNESDAY, ZmMailListDateGroup.THURSDAY, ZmMailListDateGroup.FRIDAY,
 66 							 ZmMailListDateGroup.SATURDAY, ZmMailListDateGroup.LAST_WEEK, ZmMailListDateGroup.TWO_WEEKS_AGO, ZmMailListDateGroup.THREE_WEEKS_AGO,
 67 							 ZmMailListDateGroup.EARLIER_THIS_MONTH, ZmMailListDateGroup.LAST_MONTH, ZmMailListDateGroup.OLDER];
 68 
 69 ZmMailListDateGroup.WEEKDAYS = [ZmMailListDateGroup.SUNDAY, ZmMailListDateGroup.MONDAY, ZmMailListDateGroup.TUESDAY, ZmMailListDateGroup.WEDNESDAY,
 70 							    ZmMailListDateGroup.THURSDAY, ZmMailListDateGroup.FRIDAY, ZmMailListDateGroup.SATURDAY];
 71 
 72 ZmMailListDateGroup.SECTION_TITLE = {};
 73 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.TODAY] = ZmMsg.today;
 74 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.YESTERDAY] = ZmMsg.yesterday;
 75 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.MONDAY] = I18nMsg.weekdayMonLong;
 76 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.TUESDAY] = I18nMsg.weekdayTueLong;
 77 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.WEDNESDAY] = I18nMsg.weekdayWedLong;
 78 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.THURSDAY] = I18nMsg.weekdayThuLong;
 79 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.FRIDAY] = I18nMsg.weekdayFriLong;
 80 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.SATURDAY] = I18nMsg.weekdaySatLong;
 81 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.SUNDAY] = I18nMsg.weekdaySunLong;
 82 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.LAST_WEEK] = ZmMsg.lastWeek;
 83 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.TWO_WEEKS_AGO] = ZmMsg.twoWeeksAgo;
 84 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.THREE_WEEKS_AGO] = ZmMsg.threeWeeksAgo;
 85 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.EARLIER_THIS_MONTH] = ZmMsg.earlierThisMonth;
 86 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.LAST_MONTH] = ZmMsg.lastMonth;
 87 ZmMailListDateGroup.SECTION_TITLE[ZmMailListDateGroup.OLDER] = ZmMsg.older;
 88 
 89 ZmMailListDateGroup.TIME = {};
 90 ZmMailListDateGroup.TIME["YESTERDAY"] = AjxDateUtil.MSEC_PER_DAY;
 91 ZmMailListDateGroup.TIME["LAST_WEEK"] = AjxDateUtil.MSEC_PER_DAY * 7;
 92 ZmMailListDateGroup.TIME["TWO_WEEKS_AGO"] = AjxDateUtil.MSEC_PER_DAY * 14;
 93 ZmMailListDateGroup.TIME["THREE_WEEKS_AGO"] = AjxDateUtil.MSEC_PER_DAY * 21;
 94 
 95 
 96 /**
 97  *  returns HTML string for all sections.
 98  *  @param {boolean} sortAsc    true/false if sort ascending
 99  *  @return {String} HTML for all sections including section header
100  * @param sortAsc
101  */
102 ZmMailListDateGroup.prototype.getAllSections =
103 function(sortAsc) {
104     var htmlArr = [];
105 	var dayOfWeek = this._getToday().getDay();
106     var keys = sortAsc ? this._sortKeys(dayOfWeek, sortAsc) : this._keys; //keys have already been sorted desc
107     for (var i=0; i<keys.length; i++) {
108         if (this._section[keys[i]].length > 0) {
109             htmlArr.push(this.getSectionHeader(ZmMailListDateGroup.SECTION_TITLE[keys[i]]));
110             htmlArr.push(this._section[keys[i]].join(""));
111         }
112         else if (this._showEmptySectionHeader) {
113             htmlArr.push(this.getSectionHeader(ZmMailListDateGroup.SECTION_TITLE[keys[i]]));
114         }
115     }
116     return htmlArr.join("");
117 };
118 
119 /**
120  * Adds item to section
121  * @param {ZmMailMsg} msg   mail message
122  * @param {String} item  HTML to add to section
123  * @return {String} section returns section if successfully added, else returns null
124  */
125 ZmMailListDateGroup.prototype.addMsgToSection =
126 function(msg, item) {
127    for (var i=0; i<this._keys.length; i++) {
128        if (this.isMsgInSection(this._keys[i], msg)) {
129         this._section[this._keys[i]].push(item);
130         return this._keys[i];
131        }
132    }
133 };
134 
135 /**
136  * Determines if message is in group
137  * @param {String} section ID of section
138  * @param {ZmMailMsg} msg
139  * @return {boolean} true/false
140  */
141 ZmMailListDateGroup.prototype.isMsgInSection =
142 function(section, msg) {
143 
144    switch(section){
145        case ZmMailListDateGroup.TODAY:
146          return this._isToday(msg);
147 
148        case ZmMailListDateGroup.YESTERDAY:
149         return this._isYesterday(msg);
150 
151        case ZmMailListDateGroup.MONDAY:
152         return this._isDayOfWeek(msg, AjxDateUtil.MONDAY);
153 
154        case ZmMailListDateGroup.TUESDAY:
155         return this._isDayOfWeek(msg, AjxDateUtil.TUESDAY);
156 
157        case ZmMailListDateGroup.WEDNESDAY:
158         return this._isDayOfWeek(msg, AjxDateUtil.WEDNESDAY);
159 
160        case ZmMailListDateGroup.THURSDAY:
161         return this._isDayOfWeek(msg, AjxDateUtil.THURSDAY);
162 
163        case ZmMailListDateGroup.FRIDAY:
164         return this._isDayOfWeek(msg, AjxDateUtil.FRIDAY);
165 
166        case ZmMailListDateGroup.SATURDAY:
167         return this._isDayOfWeek(msg, AjxDateUtil.SATURDAY);
168 
169        case ZmMailListDateGroup.SUNDAY:
170         return this._isDayOfWeek(msg, AjxDateUtil.SUNDAY);
171 
172        case ZmMailListDateGroup.LAST_WEEK:
173         return this._isWeeksAgo(msg, ZmMailListDateGroup.LAST_WEEK, ZmMailListDateGroup.YESTERDAY);
174 
175        case ZmMailListDateGroup.TWO_WEEKS_AGO:
176         return this._isWeeksAgo(msg, ZmMailListDateGroup.TWO_WEEKS_AGO, ZmMailListDateGroup.LAST_WEEK);
177 
178        case ZmMailListDateGroup.THREE_WEEKS_AGO:
179         return this._isWeeksAgo(msg, ZmMailListDateGroup.THREE_WEEKS_AGO, ZmMailListDateGroup.TWO_WEEKS_AGO);
180 
181        case ZmMailListDateGroup.EARLIER_THIS_MONTH:
182         return this._isEarlierThisMonth(msg);
183 
184        case ZmMailListDateGroup.LAST_MONTH:
185         return this._isLastMonth(msg);
186 
187        case ZmMailListDateGroup.OLDER:
188         return this._isOlder(msg);
189 
190        default:
191         return false;
192    }
193 };
194 
195 /**
196  * Returns the sort by (ZmSearch.DATE_ASC or ZmSearch.DATE_DESC)
197  * @param {boolean} sortAsc
198  * @return {String} sortBy
199  */
200 ZmMailListDateGroup.prototype.getSortBy =
201 function(sortAsc) {
202     if (sortAsc) {
203         return ZmSearch.DATE_ASC;
204     }
205     return ZmSearch.DATE_DESC;
206 };
207 
208 ZmMailListDateGroup.prototype._init =
209 function(){
210   this._section = {};
211   this._section[ZmMailListDateGroup.TODAY] = [];
212   this._section[ZmMailListDateGroup.YESTERDAY] = [];
213   this._section[ZmMailListDateGroup.SUNDAY] = [];
214   this._section[ZmMailListDateGroup.MONDAY] = [];
215   this._section[ZmMailListDateGroup.TUESDAY] = [];
216   this._section[ZmMailListDateGroup.WEDNESDAY] = [];
217   this._section[ZmMailListDateGroup.THURSDAY] = [];
218   this._section[ZmMailListDateGroup.FRIDAY] = [];
219   this._section[ZmMailListDateGroup.SATURDAY] = [];
220   this._section[ZmMailListDateGroup.LAST_WEEK] = [];
221   this._section[ZmMailListDateGroup.TWO_WEEKS_AGO] = [];
222   this._section[ZmMailListDateGroup.THREE_WEEKS_AGO] = [];
223   this._section[ZmMailListDateGroup.EARLIER_THIS_MONTH] = [];
224   this._section[ZmMailListDateGroup.LAST_MONTH] = [];
225   this._section[ZmMailListDateGroup.OLDER] = [];
226 };
227 
228 /**
229  * determines if mail message was received today
230  * @param msg {ZmMailMsg} mail msg
231  * @return {boolean}
232  */
233 ZmMailListDateGroup.prototype._isToday =
234 function(msg){
235     if(msg){
236         var today = this._getToday();
237         var d = this._getDateFromMsg(msg, true);
238         if (d) {
239             return today.getTime() == d.getTime();
240         }
241     }
242     return false;
243 };
244 
245 /**
246  * determines if msg was received yesterday
247  * @param msg {ZmMailMsg} mail msg
248  * @return {boolean}
249  */
250 ZmMailListDateGroup.prototype._isYesterday =
251 function(msg) {
252     if (msg) {
253         var today = this._getToday();
254         var yesterday = new Date();
255         yesterday.setTime(today.getTime() - AjxDateUtil.MSEC_PER_DAY);
256         var d = this._getDateFromMsg(msg, true);
257         if (d) {
258             return yesterday.getTime() == d.getTime();
259         }
260     }
261 
262     return false;
263 };
264 
265 /**
266  * message is this week, but not today or yesterday
267  * @param msg {ZmMailMsg} mail msg
268  * @param dayOfWeek {integer} the day of the week to check against (e.g. Monday)
269  * @return {boolean}
270  */
271 ZmMailListDateGroup.prototype._isDayOfWeek =
272 function(msg, dayOfWeek) {
273     if (msg) {
274         var today = this._getToday();
275         var thisWeek = AjxDateUtil.getWeekNumber(today);
276         var thisYear = today.getYear();
277 
278         var d = this._getDateFromMsg(msg, true);
279         if (d) {
280             return d.getDay() == dayOfWeek && AjxDateUtil.getWeekNumber(d) == thisWeek &&
281                    !this._isYesterday(msg) && !this._isToday(msg) && thisYear == d.getYear();
282         }
283     }
284     return false;
285 };
286 
287 /**
288  * Determines if msg is from X number of weeks ago.
289  * @param {ZmMailMsg} msg the mail message to evaluate
290  * @param {String} minGroup Group oldest in date  (e.g. three weeks ago)
291  * @param {String} maxGroup Group newest in date  (e.g. two weeks ago)
292  */
293 ZmMailListDateGroup.prototype._isWeeksAgo =
294 function(msg, minGroup, maxGroup) {
295 	if (msg) {
296 		var today = this._getToday();
297 		var max = today.getTime() - ZmMailListDateGroup.TIME[maxGroup];
298 		var min = today.getTime() - ZmMailListDateGroup.TIME[minGroup];
299 		var d = this._getDateFromMsg(msg, true);
300 		if (d) {
301 			return d.getTime() >= min && d.getTime() < max;
302 		}
303 	}
304 
305 	return false;
306 };
307 
308 /**
309  * message is earlier this month and also more than 3 weeks ago
310  * @param msg {ZmMailMsg} mail msg
311  * @return {boolbean}
312  */
313 ZmMailListDateGroup.prototype._isEarlierThisMonth =
314 function(msg) {
315     if (msg) {
316         var today = this._getToday();
317         var threeWeeksAgo = today.getTime() - ZmMailListDateGroup.TIME[ZmMailListDateGroup.THREE_WEEKS_AGO];
318         var thisMonth = today.getMonth();
319         var thisYear = today.getYear();
320         var d = this._getDateFromMsg(msg, true);
321         if (d) {
322             return d.getTime() < threeWeeksAgo && (d.getYear() == thisYear && d.getMonth() == thisMonth);
323         }
324     }
325     return false;
326 };
327 
328 /**
329  * message is last month and also more than 3 weeks ago from today
330  * @param msg {ZmMailMsg} mail message
331  * @return {boolean}
332  */
333 ZmMailListDateGroup.prototype._isLastMonth =
334 function(msg) {
335     if (msg) {
336         var today = this._getToday();
337 	    var threeWeeksAgo = today.getTime() - ZmMailListDateGroup.TIME[ZmMailListDateGroup.THREE_WEEKS_AGO];
338         var thisMonth = today.getMonth();
339         var thisYear = today.getYear();
340         var lastMonth = this._calculateLastMonth(thisMonth);
341         var d = this._getDateFromMsg(msg, true);
342         if (d) {
343             if(d.getMonth() != thisMonth) {
344                 if (d.getYear() == thisYear) {
345                     return d.getMonth() == lastMonth && d.getTime() < threeWeeksAgo;
346                 }
347 				else if (d.getYear() == (thisYear -1) && thisMonth == 0 && lastMonth == 11) {
348 					//handle the january, december case
349 					return d.getTime() < threeWeeksAgo;	
350 				}
351             }
352         }
353 
354     }
355     return false;
356 };
357 
358 /**
359  * message is more than a month old
360  * @param msg {ZmMailMsg} mail msg
361  * @return {boolean}
362  */
363 ZmMailListDateGroup.prototype._isOlder =
364 function(msg) {
365     if (msg) {
366         var today = this._getToday();
367         var threeWeeksAgo = today.getTime() - ZmMailListDateGroup.TIME[ZmMailListDateGroup.THREE_WEEKS_AGO];
368         var d = this._getDateFromMsg(msg, true);
369         if (d) {
370             return d.getTime() < threeWeeksAgo && !this._isEarlierThisMonth(msg) && !this._isLastMonth(msg);
371         }
372     }
373     return false;
374 };
375 
376 ZmMailListDateGroup.prototype._getDateFromMsg =
377 function(msg, resetHours) {
378     if (msg) {
379         var d = msg.sentDate ? new Date(msg.sentDate) : new Date(msg.date);
380         if (d && resetHours) {
381             d.setHours(0, 0, 0, 0);
382         }
383         return d;
384     }
385     return null;
386 };
387 
388 /**
389  * Sorts sections (e.g. Today, Yesterday, Days, Last Week, etc) by ASC or DESC order.  dayOfWeek is used to sort the week days in ASC/DESC order
390  * @param dayOfWeek {integer} day value of today
391  * @param sortAsc  {boolean} true if sort ascending
392  * @return keys {array} sorted keys
393  */
394 ZmMailListDateGroup.prototype._sortKeys =
395 function(dayOfWeek, sortAsc) {
396     var keys = [];
397 	var sortedDays = this._sortThisWeek(dayOfWeek);
398 	sortedDays = sortedDays.slice(2); //account for today & yesterday
399 	keys = [ZmMailListDateGroup.TODAY, ZmMailListDateGroup.YESTERDAY];
400 	keys = keys.concat(sortedDays);
401 	keys = keys.concat([ZmMailListDateGroup.LAST_WEEK, ZmMailListDateGroup.TWO_WEEKS_AGO, ZmMailListDateGroup.THREE_WEEKS_AGO,
402 						ZmMailListDateGroup.EARLIER_THIS_MONTH, ZmMailListDateGroup.LAST_MONTH, ZmMailListDateGroup.OLDER]);
403     if (sortAsc) {
404         keys.reverse();
405     }
406     return keys;
407 };
408 
409 ZmMailListDateGroup.prototype._calculateLastMonth =
410 function(month) {
411     var lastMonth = month -1;
412     if (lastMonth == -1){
413         lastMonth = 11;
414     }
415     return lastMonth;
416 };
417 
418 ZmMailListDateGroup.prototype._getToday =
419 function() {
420   var today = new Date();
421   today.setHours(0,0,0,0);
422   return today;
423 };
424 
425 ZmMailListDateGroup.prototype._getSectionHeaderTitle =
426 function(section) {
427    if (ZmMailListDateGroup.SECTION_TITLE[section]) {
428        return ZmMailListDateGroup.SECTION_TITLE[section];
429    }
430 
431    return "";
432 };
433 
434 /**
435  * Sort days for this week.  If today is Monday & preferences is start week with Sunday, result will by [Monday, Sunday]
436  * @param firstDay {integer}  day value of today
437  * @return sorteDays {array} array of sorted days
438  */
439 ZmMailListDateGroup.prototype._sortThisWeek =
440 function(firstDay) {
441 	var sortedDays = [];
442 	var count = 0;
443 	var foundStart = false;
444 	while (count < 7 && !foundStart) {
445 		if (firstDay == this._weekStartDay) {
446 			foundStart = true;
447 		}
448 		sortedDays[count] = ZmMailListDateGroup.WEEKDAYS[firstDay];
449 		firstDay--;
450 		if (firstDay < 0) {
451 			firstDay = 6;
452 		}
453 		count++;
454 	}
455 
456 	return sortedDays;
457 };
458