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 ZmMailListView = function(params) { 25 26 if (arguments.length == 0) { return; } 27 28 params.pageless = true; 29 ZmListView.call(this, params); 30 31 this._folderId = null; 32 this._selectAllEnabled = true; 33 34 this._isMultiColumn = this.isMultiColumn(); 35 if (!this._isMultiColumn) { 36 this._normalClass = ZmMailListView.ROW_DOUBLE_CLASS; 37 } 38 39 this._disallowSelection[ZmItem.F_READ] = true; 40 }; 41 42 ZmMailListView.prototype = new ZmListView; 43 ZmMailListView.prototype.constructor = ZmMailListView; 44 45 ZmMailListView.prototype.isZmMailListView = true; 46 ZmMailListView.prototype.toString = function() { return "ZmMailListView"; }; 47 48 // Consts 49 ZmMailListView.ROW_DOUBLE_CLASS = "RowDouble"; 50 51 ZmMailListView.FIRST_ITEM = -1; 52 ZmMailListView.LAST_ITEM = -2; 53 54 ZmMailListView.SINGLE_COLUMN_SORT = [ 55 {field:ZmItem.F_FROM, msg:"from" }, 56 {field:ZmItem.F_TO, msg:"to" }, 57 {field:ZmItem.F_SUBJECT,msg:"subject" }, 58 {field:ZmItem.F_SIZE, msg:"size" }, 59 {field:ZmItem.F_DATE, msg:"date" }, 60 {field:ZmItem.F_ATTACHMENT, msg:"attachment" }, 61 {field:ZmItem.F_FLAG, msg:"flag" }, 62 {field:ZmItem.F_PRIORITY, msg:"priority" }, 63 {field:ZmItem.F_READ, msg:"readUnread" } 64 ]; 65 66 ZmMailListView.SORTBY_HASH = []; 67 ZmMailListView.SORTBY_HASH[ZmSearch.NAME_ASC] = {field:ZmItem.F_FROM, msg:"from"}; 68 ZmMailListView.SORTBY_HASH[ZmSearch.NAME_DESC] = {field:ZmItem.F_FROM, msg:"from"}; 69 ZmMailListView.SORTBY_HASH[ZmSearch.SUBJ_ASC] = {field:ZmItem.F_SUBJECT, msg:"subject"}; 70 ZmMailListView.SORTBY_HASH[ZmSearch.SUBJ_DESC] = {field:ZmItem.F_SUBJECT, msg:"subject"}; 71 ZmMailListView.SORTBY_HASH[ZmSearch.SIZE_ASC] = {field:ZmItem.F_SIZE, msg:"size"}; 72 ZmMailListView.SORTBY_HASH[ZmSearch.SIZE_DESC] = {field:ZmItem.F_SIZE, msg:"size"}; 73 ZmMailListView.SORTBY_HASH[ZmSearch.DATE_ASC] = {field:ZmItem.F_DATE, msg:"date"}; 74 ZmMailListView.SORTBY_HASH[ZmSearch.DATE_DESC] = {field:ZmItem.F_DATE, msg:"date"}; 75 ZmMailListView.SORTBY_HASH[ZmSearch.ATTACH_ASC] = {field:ZmItem.F_ATTACHMENT, msg:"attachment"}; 76 ZmMailListView.SORTBY_HASH[ZmSearch.ATTACH_DESC] = {field:ZmItem.F_ATTACHMENT, msg:"attachment"}; 77 ZmMailListView.SORTBY_HASH[ZmSearch.FLAG_ASC] = {field:ZmItem.F_FLAG, msg:"flag"}; 78 ZmMailListView.SORTBY_HASH[ZmSearch.FLAG_DESC] = {field:ZmItem.F_FLAG, msg:"flag"}; 79 ZmMailListView.SORTBY_HASH[ZmSearch.MUTE_ASC] = {field:ZmItem.F_MUTE, msg:"mute"}; 80 ZmMailListView.SORTBY_HASH[ZmSearch.MUTE_DESC] = {field:ZmItem.F_MUTE, msg:"mute"}; 81 ZmMailListView.SORTBY_HASH[ZmSearch.READ_ASC] = {field:ZmItem.F_READ, msg:"readUnread"}; 82 ZmMailListView.SORTBY_HASH[ZmSearch.READ_DESC] = {field:ZmItem.F_READ, msg:"readUnread"}; 83 ZmMailListView.SORTBY_HASH[ZmSearch.PRIORITY_ASC] = {field:ZmItem.F_PRIORITY, msg:"priority"}; 84 ZmMailListView.SORTBY_HASH[ZmSearch.PRIORITY_DESC] = {field:ZmItem.F_PRIORITY, msg:"priority"}; 85 ZmMailListView.SORTBY_HASH[ZmSearch.RCPT_ASC] = {field:ZmItem.F_TO, msg:"to"}; 86 ZmMailListView.SORTBY_HASH[ZmSearch.RCPT_DESC] = {field:ZmItem.F_TO, msg:"to"}; 87 88 89 // Public methods 90 91 92 // Reset row style 93 ZmMailListView.prototype.markUIAsMute = 94 function(item) { 95 //Removed 96 }; 97 98 // Reset row style 99 ZmMailListView.prototype.markUIAsRead = 100 function(item, oldValue) { 101 this._setImage(item, ZmItem.F_READ, item.getReadIcon(), this._getClasses(ZmItem.F_READ)); 102 103 var newCssClass = this._getRowClass(item); 104 var oldCssClass = this._getRowClassValue(oldValue); 105 var oldCssClass = this._getRowClassValue(oldValue); 106 var row = this._getElement(item, ZmItem.F_ITEM_ROW); 107 if (row) { 108 if (oldCssClass) { 109 $(row).removeClass(oldCssClass); 110 } 111 if (newCssClass) { 112 $(row).addClass(newCssClass); 113 } 114 } 115 this._controller._checkKeepReading(); 116 }; 117 118 ZmMailListView.prototype.set = 119 function(list, sortField) { 120 121 var s = this._controller._activeSearch && this._controller._activeSearch.search; 122 this._folderId = s && s.folderId; 123 if (this._folderId) { 124 this._group = this.getGroup(this._folderId); 125 } 126 127 var sortBy = s && s.sortBy; 128 if (sortBy) { 129 var column; 130 if (sortBy == ZmSearch.SUBJ_DESC || sortBy == ZmSearch.SUBJ_ASC) { 131 column = ZmItem.F_SUBJECT; 132 } else if (sortBy == ZmSearch.DATE_DESC || sortBy == ZmSearch.DATE_ASC) { 133 column = ZmItem.F_DATE; 134 } else if (sortBy == ZmSearch.NAME_DESC || sortBy == ZmSearch.NAME_ASC) { 135 column = ZmItem.F_FROM; 136 } else if (sortBy == ZmSearch.SIZE_DESC || sortBy == ZmSearch.SIZE_ASC) { 137 column = ZmItem.F_SIZE; 138 } 139 if (column) { 140 var sortByAsc = (sortBy == ZmSearch.SUBJ_ASC || sortBy == ZmSearch.DATE_ASC || sortBy == ZmSearch.NAME_ASC || sortBy == ZmSearch.SIZE_ASC); 141 this.setSortByAsc(column, sortByAsc); 142 } 143 } 144 145 146 ZmListView.prototype.set.apply(this, arguments); 147 148 this.markDefaultSelection(list); 149 }; 150 151 152 ZmMailListView.prototype.markDefaultSelection = 153 function(list) { 154 if(window.defaultSelection) { 155 var sel = []; 156 var a = list.getArray(); 157 for (var i in a) { 158 if (window.defaultSelection[a[i].id]) { 159 sel.push(a[i]); 160 } 161 } 162 if (sel.length > 0) { 163 this.setSelectedItems(sel); 164 } 165 window.defaultSelection = null; 166 } 167 }; 168 169 ZmMailListView.prototype.handleKeyAction = 170 function(actionCode, ev) { 171 172 switch (actionCode) { 173 case DwtKeyMap.SELECT_ALL: 174 ZmListView.prototype.handleKeyAction.apply(this, arguments); 175 var ctlr = this._controller; 176 ctlr._resetOperations(ctlr.getCurrentToolbar(), this.getSelectionCount()); 177 return true; 178 179 case DwtKeyMap.SELECT_NEXT: 180 case DwtKeyMap.SELECT_PREV: 181 // Block widget shortcut for space since we want to handle it as app shortcut. 182 if (ev.charCode === 32) { 183 return false; 184 } 185 this._controller.lastListAction = actionCode; 186 187 default: 188 return ZmListView.prototype.handleKeyAction.apply(this, arguments); 189 } 190 }; 191 192 ZmMailListView.prototype.getTitle = 193 function() { 194 var search = this._controller._activeSearch ? this._controller._activeSearch.search : null; 195 return search ? search.getTitle() : ""; 196 }; 197 198 ZmMailListView.prototype.replenish = 199 function(list) { 200 DwtListView.prototype.replenish.call(this, list); 201 this._resetColWidth(); 202 }; 203 204 ZmMailListView.prototype.resetSize = 205 function(newWidth, newHeight) { 206 this.setSize(newWidth, newHeight); 207 var margins = this.getMargins(); 208 var listInsets = Dwt.getInsets(this._parentEl); 209 210 if (newWidth !== Dwt.DEFAULT) { 211 newWidth -= margins.left + margins.right; 212 newWidth -= listInsets.left + listInsets.right; 213 } 214 215 if (newHeight !== Dwt.DEFAULT) { 216 newHeight -= Dwt.getOuterSize(this._listColDiv).y; 217 newHeight -= margins.top + margins.bottom; 218 newHeight -= listInsets.top + listInsets.bottom; 219 } 220 221 Dwt.setSize(this._parentEl, newWidth, newHeight); 222 }; 223 224 ZmMailListView.prototype.calculateMaxEntries = 225 function() { 226 return (Math.floor(this._parentEl.clientHeight / (this._isMultiColumn ? 20 : 40)) + 5); 227 }; 228 229 /** 230 * Returns true if the reading pane is turned off or set to bottom. We use this 231 * call to tell the UI whether to re-render the listview with multiple columns 232 * or a single column (for right-pane). 233 */ 234 ZmMailListView.prototype.isMultiColumn = 235 function() { 236 return !this._controller.isReadingPaneOnRight(); 237 }; 238 239 240 ZmMailListView.prototype._getExtraStyle = 241 function(item,start,end) { 242 if (!appCtxt.get(ZmSetting.COLOR_MESSAGES)) { 243 return null; 244 } 245 var color = item.getColor && item.getColor(); 246 if (!color) { 247 return null; 248 } 249 start = start || 0.75; 250 end = end || 0.25; 251 252 return Dwt.createLinearGradientCss(AjxColor.lighten(color, start), AjxColor.lighten(color, end), "v"); 253 }; 254 255 256 ZmMailListView.prototype._getAbridgedContent = 257 function(item, colIdx) { 258 // override me 259 }; 260 261 ZmMailListView.prototype._getListFlagsWrapper = 262 function(htmlArr, idx, item) { 263 htmlArr[idx++] = "<div class='ZmListFlagsWrapper'"; 264 //compute the start and end of gradient based on height of this div and its position 265 var extraStyle = this._getExtraStyle(item,0.49,0.33); 266 if (extraStyle) { 267 htmlArr[idx++] = " style='" + extraStyle + ";'>"; 268 } else { 269 htmlArr[idx++] = ">"; 270 } 271 return idx; 272 }; 273 274 //apply colors to from and subject cells via zimlet 275 ZmMailListView.prototype._getStyleViaZimlet = 276 function(field, item) { 277 if (field != "fr" && field != "su" && field != "st") 278 return ""; 279 280 if (appCtxt.zimletsPresent() && this._ignoreProcessingGetMailCellStyle == undefined) { 281 if (!this._zimletMgr) { 282 this._zimletMgr = appCtxt.getZimletMgr();//cache zimletMgr 283 } 284 var style = this._zimletMgr.processARequest("getMailCellStyle", item, field); 285 if (style != undefined && style != null) { 286 return style;//set style 287 } else if (style == null && this._zimletMgr.isLoaded()) { 288 //zimlet not available or disabled, set _ignoreProcessingGetMailCellStyle to true 289 //to ignore this entire section for this session 290 this._ignoreProcessingGetMailCellStyle = true; 291 } 292 } 293 return ""; 294 }; 295 296 297 ZmMailListView.prototype._getAbridgedCell = 298 function(htmlArr, idx, item, field, colIdx, width, attr, classes) { 299 var params = {}; 300 classes = classes || []; 301 302 /* TODO: Find an alternate way for Zimlets to add styles to the field. 303 htmlArr[idx++] = this._getStyleViaZimlet(field, item); 304 */ 305 306 var className = this._getCellClass(item, field, params); 307 if (className) { 308 classes.push(className); 309 } 310 idx = this._getCellContents(htmlArr, idx, item, field, colIdx, params, classes); 311 312 return idx; 313 }; 314 315 ZmMailListView.prototype._getCellContents = 316 function(htmlArr, idx, item, field, colIdx, params, classes) { 317 if (field == ZmItem.F_ACCOUNT) { 318 idx = this._getImageHtml(htmlArr, idx, item.getAccount().getIcon(), this._getFieldId(item, field), classes); 319 } 320 else if (field == ZmItem.F_DATE) { 321 var date = AjxDateUtil.computeDateStr(params.now || new Date(), item.date); 322 htmlArr[idx++] = "<div id='"; 323 htmlArr[idx++] = this._getFieldId(item, field); 324 htmlArr[idx++] = "' "; 325 if (!this.isMultiColumn()) { 326 //compute the start and end of gradient based on height of this div and its position 327 var extraStyle = this._getExtraStyle(item,0.69,0.55); 328 if (extraStyle) { 329 htmlArr[idx++] = " style='" + extraStyle + "'"; 330 } 331 } 332 htmlArr[idx++] = AjxUtil.getClassAttr(classes); 333 htmlArr[idx++] = ">" + date + "</div>"; 334 } 335 else { 336 idx = ZmListView.prototype._getCellContents.apply(this, arguments); 337 } 338 339 return idx; 340 }; 341 342 /** 343 * Called by the controller whenever the reading pane preference changes 344 * 345 * @private 346 */ 347 ZmMailListView.prototype.reRenderListView = 348 function() { 349 var isMultiColumn = this.isMultiColumn(); 350 if (isMultiColumn != this._isMultiColumn) { 351 this._saveState({selection:true, focus:true, scroll:true, expansion:true}); 352 this._isMultiColumn = isMultiColumn; 353 this.headerColCreated = false; 354 this._headerList = this._getHeaderList(); 355 this._rowHeight = null; 356 this._normalClass = isMultiColumn ? DwtListView.ROW_CLASS : ZmMailListView.ROW_DOUBLE_CLASS; 357 var list = this.getList() || (new AjxVector()); 358 this.clearGroupSections(this._folderId); 359 this.set(list.clone()); 360 this._restoreState(); 361 this._resetFromColumnLabel(); 362 } 363 }; 364 365 // Private / protected methods 366 367 ZmMailListView.prototype._getLabelForField = 368 function(item, field) { 369 switch (field) { 370 case ZmItem.F_READ: 371 // usually included in the status tooltip 372 break; 373 374 case ZmItem.F_FLAG: 375 return item.isFlagged ? ZmMsg.flagged : ''; 376 377 case ZmItem.F_ATTACHMENT: 378 return item.hasAttach && ZmMsg.hasAttachment; 379 380 case ZmItem.F_STATUS: 381 return item.getStatusTooltip(); 382 383 case ZmItem.F_SUBJECT: 384 return item.subject || ZmMsg.noSubject; 385 386 case ZmItem.F_PRIORITY: 387 if (item.isLowPriority) { 388 return ZmMsg.priorityLow; 389 } else if (item.isHighPriority) { 390 return ZmMsg.priorityHigh; 391 } 392 393 break; 394 395 case ZmItem.F_TAG: 396 if (item.tags.length > 0) { 397 var tags = item.tags.join(' & '); 398 return AjxMessageFormat.format(ZmMsg.taggedAs, [tags]); 399 } 400 401 break; 402 403 case ZmItem.F_DATE: 404 return AjxDateUtil.computeWordyDateStr(new Date(), item.date); 405 406 case ZmItem.F_FROM: 407 var addrtype = this._isOutboundFolder() ? 408 AjxEmailAddress.TO : AjxEmailAddress.FROM; 409 var participants = item.getAddresses(addrtype) || item.participants || new AjxVector(); 410 var addrs = [] 411 412 if (participants.size() <= 0) { 413 return AjxStringUtil.stripTags(ZmMsg.noRecipients); 414 } 415 416 for (var i = 0; i < Math.min(participants.size(), 3); i++) { 417 addrs.push(participants.get(i).toString(true, true)); 418 } 419 420 return addrs.join(", "); 421 422 case ZmItem.F_SIZE: 423 if (item.size) { 424 return AjxUtil.formatSize(item.size); 425 } 426 427 break; 428 } 429 430 return ZmListView.prototype._getLabelForField.apply(this, arguments); 431 }; 432 433 ZmMailListView.prototype._initHeaders = 434 function() { 435 if (!this._headerInit) { 436 this._headerInit = {}; 437 this._headerInit[ZmItem.F_SELECTION] = {icon:"CheckboxUnchecked", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.selection, precondition:ZmSetting.SHOW_SELECTION_CHECKBOX, cssClass:"ZmMsgListColSelection"}; 438 this._headerInit[ZmItem.F_FLAG] = {icon:"FlagRed", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.flag, sortable:ZmItem.F_FLAG, noSortArrow:true, precondition:ZmSetting.FLAGGING_ENABLED, cssClass:"ZmMsgListColFlag"}; 439 this._headerInit[ZmItem.F_PRIORITY] = {icon:"PriorityHigh_list", width:ZmListView.COL_WIDTH_NARROW_ICON, name:ZmMsg.priority, sortable:ZmItem.F_PRIORITY, noSortArrow:true, precondition:ZmSetting.MAIL_PRIORITY_ENABLED, cssClass:"ZmMsgListColPriority"}; 440 this._headerInit[ZmItem.F_TAG] = {icon:"Tag", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.tag, precondition:ZmSetting.TAGGING_ENABLED, cssClass:"ZmMsgListColTag"}; 441 this._headerInit[ZmItem.F_ACCOUNT] = {icon:"AccountAll", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.account, noRemove:true, resizeable:true, cssClass:"ZmMsgListColAccount"}; 442 this._headerInit[ZmItem.F_STATUS] = {icon:"MsgStatus", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.status, cssClass:"ZmMsgListColStatus"}; 443 this._headerInit[ZmItem.F_MUTE] = {icon:"Mute", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.muteUnmute, sortable: false /*ZmItem.F_MUTE*/, noSortArrow:true, cssClass:"ZmMsgListColMute"}; //todo - once server supports readAsc/readDesc sort orders, uncomment the sortable 444 this._headerInit[ZmItem.F_READ] = {icon:"MsgUnread", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.readUnread, sortable: ZmItem.F_READ, noSortArrow:true, cssClass:"ZmMsgListColRead"}; 445 this._headerInit[ZmItem.F_FROM] = {text:ZmMsg.from, width:ZmMsg.COLUMN_WIDTH_FROM_MLV, resizeable:true, sortable:ZmItem.F_FROM, cssClass:"ZmMsgListColFrom"}; 446 this._headerInit[ZmItem.F_ATTACHMENT] = {icon:"Attachment", width:ZmListView.COL_WIDTH_ICON, name:ZmMsg.attachment, sortable:ZmItem.F_ATTACHMENT, noSortArrow:true, cssClass:"ZmMsgListColAttachment"}; 447 this._headerInit[ZmItem.F_SUBJECT] = {text:ZmMsg.subject, sortable:ZmItem.F_SUBJECT, noRemove:true, resizeable:true, cssClass:"ZmMsgListColSubject"}; 448 this._headerInit[ZmItem.F_FOLDER] = {text:ZmMsg.folder, width:ZmMsg.COLUMN_WIDTH_FOLDER, resizeable:true, cssClass:"ZmMsgListColFolder"}; 449 this._headerInit[ZmItem.F_SIZE] = {text:ZmMsg.size, width:ZmMsg.COLUMN_WIDTH_SIZE, sortable:ZmItem.F_SIZE, resizeable:true, cssClass:"ZmMsgListColSize"}; 450 this._headerInit[ZmItem.F_DATE] = {text:ZmMsg.received, width:ZmMsg.COLUMN_WIDTH_DATE, sortable:ZmItem.F_DATE, resizeable:true, cssClass:"ZmMsgListColDate"}; 451 this._headerInit[ZmItem.F_SORTED_BY] = {text:AjxMessageFormat.format(ZmMsg.arrangedBy, ZmMsg.date), sortable:ZmItem.F_SORTED_BY, resizeable:false}; 452 } 453 }; 454 455 ZmMailListView.prototype._getLabelFieldList = 456 function() { 457 var headers = []; 458 headers.push(ZmItem.F_SELECTION); 459 if (appCtxt.get(ZmSetting.FLAGGING_ENABLED)) { 460 headers.push(ZmItem.F_FLAG); 461 } 462 headers.push( 463 ZmItem.F_PRIORITY, 464 ZmItem.F_TAG, 465 ZmItem.F_READ, 466 ZmItem.F_STATUS, 467 ZmItem.F_FROM, 468 ZmItem.F_ATTACHMENT, 469 ZmItem.F_SUBJECT, 470 ZmItem.F_FOLDER, 471 ZmItem.F_SIZE 472 ); 473 if (appCtxt.accountList.size() > 2) { 474 headers.push(ZmItem.F_ACCOUNT); 475 } 476 headers.push(ZmItem.F_DATE); 477 478 return headers; 479 } 480 481 ZmMailListView.prototype._getHeaderList = 482 function() { 483 var headers; 484 if (this.isMultiColumn()) { 485 headers = this._getLabelFieldList(); 486 } 487 else { 488 headers = [ 489 ZmItem.F_SELECTION, 490 ZmItem.F_SORTED_BY 491 ]; 492 } 493 494 return this._getHeaders(this._mode, headers); 495 }; 496 497 ZmMailListView.prototype._getHeaders = 498 function(viewId, headerList) { 499 500 this._initHeaders(); 501 var hList = []; 502 503 this._defaultCols = headerList.join(ZmListView.COL_JOIN); 504 var isMultiColumn = !this._controller.isReadingPaneOnRight(); 505 var userHeaders = isMultiColumn && appCtxt.get(ZmSetting.LIST_VIEW_COLUMNS, viewId); 506 var headers = headerList; 507 if (userHeaders && isMultiColumn) { 508 headers = userHeaders.split(ZmListView.COL_JOIN); 509 //we have to do it regardless of the size of headers and headerList, as items could be added and removed, masking each other as far as length (previous code compared length) 510 headers = this._normalizeHeaders(headers, headerList); 511 } 512 // adding account header in _normalizeHeader method 513 // sometimes doesn't work since we check for array length which is bad. 514 515 // in ZD in case of All-Mailbox search always make sure account header is added to header array 516 if(appCtxt.isOffline && appCtxt.getSearchController().searchAllAccounts && isMultiColumn) { 517 var isAccHdrEnabled = false; 518 for (var k=0; k< headers.length; k++) { 519 if(headers[k] == ZmItem.F_ACCOUNT) { 520 isAccHdrEnabled = true; 521 } 522 } 523 if(!isAccHdrEnabled) { 524 headers.splice(headers.length - 1, 0, ZmId.FLD_ACCOUNT); 525 } 526 527 } 528 529 for (var i = 0, len = headers.length; i < len; i++) { 530 var header = headers[i]; 531 var field = header.substr(0, 2); 532 var hdrParams = this._headerInit[field]; 533 if (!hdrParams) { continue; } 534 var pre = hdrParams.precondition; 535 if (!pre || appCtxt.get(pre)) { 536 hdrParams.field = field; 537 // multi-account, account header is always initially invisible 538 // unless user is showing global inbox. Ugh. 539 if (appCtxt.multiAccounts && 540 appCtxt.accountList.size() > 2 && 541 appCtxt.get(ZmSetting.OFFLINE_SHOW_ALL_MAILBOXES) && 542 header.indexOf(ZmItem.F_ACCOUNT) != -1) 543 { 544 hdrParams.visible = true; 545 this._showingAccountColumn = true; 546 } else { 547 var visible = (appCtxt.multiAccounts && header == ZmItem.F_ACCOUNT && !userHeaders) 548 ? false : (header.indexOf("*") == -1); 549 if (!userHeaders && isMultiColumn) { 550 //this is the default header 551 if (typeof hdrParams.visible === "undefined") { 552 //if the visible header is not set than use the computed value 553 hdrParams.visible = visible; 554 } 555 } else { 556 hdrParams.visible = visible; 557 } 558 } 559 hList.push(new DwtListHeaderItem(hdrParams)); 560 } 561 } 562 563 return hList; 564 }; 565 566 /** 567 * Cleans up the list of columns in various ways: 568 * - Add new fields in penultimate position 569 * - Remove duplicate fields 570 * - Remove fields that aren't valid for the view 571 * 572 * @param userHeaders [Array] user-defined set of column headers 573 * @param headerList [Array] default set of column headers 574 */ 575 ZmMailListView.prototype._normalizeHeaders = 576 function(userHeaders, headerList) { 577 578 // strip duplicates and invalid headers 579 var allHeaders = AjxUtil.arrayAsHash(headerList); 580 var headers = [], used = {}, starred = {}; 581 for (var i = 0; i < userHeaders.length; i++) { 582 var hdr = userHeaders[i]; 583 var idx = hdr.indexOf("*"); 584 if (idx != -1) { 585 hdr = hdr.substr(0, idx); 586 starred[hdr] = true; 587 } 588 if (allHeaders[hdr] && !used[hdr]) { 589 headers.push(hdr); 590 used[hdr] = true; 591 } 592 } 593 594 // add columns this account doesn't know about 595 for (var j = 0; j < headerList.length; j++) { 596 var hdr = headerList[j]; 597 if (!used[hdr]) { 598 // if account field, add it but initially invisible 599 if (hdr == ZmId.FLD_ACCOUNT) { 600 starred[ZmItem.F_ACCOUNT] = true; 601 } 602 if (hdr == ZmId.FLD_SELECTION) { 603 //re-add selection checkbox at the beginning (no idea why the rest is added one before last item, but not gonna change it for now 604 headers.unshift(hdr); //unshift adds item at the beginning 605 } 606 else { 607 headers.splice(headers.length - 1, 0, hdr); 608 } 609 } 610 } 611 612 // rebuild the list, preserve invisibility 613 var list = AjxUtil.map(headers, function(hdr) { 614 return starred[hdr] ? hdr + "*" : hdr; }); 615 616 // save implicit pref with newly added column 617 appCtxt.set(ZmSetting.LIST_VIEW_COLUMNS, list.join(ZmListView.COL_JOIN), this.view); 618 return list; 619 }; 620 621 ZmMailListView.prototype.createHeaderHtml = 622 function(defaultColumnSort) { 623 624 var activeSortBy = this.getActiveSearchSortBy(); 625 // for multi-account, hide/show Account column header depending on whether 626 // user is search across all accounts or not. 627 if (appCtxt.multiAccounts) { 628 var searchAllAccounts = appCtxt.getSearchController().searchAllAccounts; 629 if (this._headerHash && 630 ((this._showingAccountColumn && !searchAllAccounts) || 631 (!this._showingAccountColumn && searchAllAccounts))) 632 { 633 var accountHeader = this._headerHash[ZmItem.F_ACCOUNT]; 634 if (accountHeader) { 635 accountHeader._visible = this._showingAccountColumn = searchAllAccounts; 636 this.headerColCreated = false; 637 } 638 } 639 } 640 641 if (this._headerList && !this.headerColCreated) { 642 var rpLoc = this._controller._getReadingPanePref(); 643 if (rpLoc == ZmSetting.RP_RIGHT && this._controller._itemCountText[rpLoc]) { 644 this._controller._itemCountText[rpLoc].dispose(); 645 } 646 647 if (activeSortBy && ZmMailListView.SORTBY_HASH[activeSortBy]) { 648 defaultColumnSort = ZmMailListView.SORTBY_HASH[activeSortBy].field; 649 } 650 DwtListView.prototype.createHeaderHtml.call(this, defaultColumnSort, this._isMultiColumn); 651 652 if (rpLoc == ZmSetting.RP_RIGHT) { 653 var td = document.getElementById(this._itemCountTextTdId); 654 if (td) { 655 var textId = DwtId.makeId(this.view, rpLoc, "text"); 656 var textDiv = document.getElementById(textId); 657 if (!textDiv) { 658 var text = this._controller._itemCountText[rpLoc] = 659 new DwtText({parent:this, className:"itemCountText", id:textId}); 660 td.appendChild(text.getHtmlElement()); 661 } 662 } 663 } 664 } 665 666 // Setting label on date column 667 this._resetFromColumnLabel(); 668 var col = Dwt.byId(this._currentColId); 669 var headerCol = this._isMultiColumn ? this._headerHash[ZmItem.F_DATE] : 670 (col && this.getItemFromElement(col)) || (this._headerHash && this._headerHash[ZmItem.F_SORTED_BY]) || null; 671 if (headerCol) { 672 var colLabel = ""; 673 var column; 674 if (this._isMultiColumn) { 675 // set the received column name based on search folder 676 colLabel = ZmMsg.received; 677 if (this._isOutboundFolder()) { 678 colLabel = (this._folderId == ZmFolder.ID_DRAFTS) ? ZmMsg.lastSaved : ZmMsg.sentAt; 679 colLabel = " " + colLabel; 680 } 681 } 682 else if (activeSortBy && ZmMailListView.SORTBY_HASH[activeSortBy]){ 683 var msg = ZmMailListView.SORTBY_HASH[activeSortBy].msg; 684 var field = ZmMailListView.SORTBY_HASH[activeSortBy].field; 685 if (msg) { 686 colLabel = AjxMessageFormat.format(ZmMsg.arrangedBy, ZmMsg[msg]); 687 } 688 if (field) { 689 headerCol._sortable = field; 690 } 691 } 692 693 //Set column label; for multi-column this changes the received text. For single column this sets to the sort by text 694 var colSpan = document.getElementById(DwtId.getListViewHdrId(DwtId.WIDGET_HDR_LABEL, this._view, headerCol._field)); 695 if (colSpan) { 696 colSpan.innerHTML = colLabel; 697 } 698 if (this._colHeaderActionMenu) { 699 if (!this._isMultiColumn) { 700 var mi = this._colHeaderActionMenu.getMenuItem(field); 701 if (mi) { 702 mi.setChecked(true, true); 703 } 704 } 705 } 706 707 // Now that we've created headers, we can update the View menu so that it doesn't get created before headers, 708 // since we want to know the header IDs when we create the Display submenu 709 var ctlr = this._controller, 710 viewType = ctlr.getCurrentViewType(); 711 ctlr._updateViewMenu(viewType); 712 ctlr._updateViewMenu(ctlr._getReadingPanePref()); 713 ctlr._updateViewMenu(appCtxt.get(ZmSetting.CONVERSATION_ORDER)); 714 } 715 }; 716 717 ZmMailListView.prototype._createHeader = 718 function(htmlArr, idx, headerCol, i, numCols, id, defaultColumnSort) { 719 720 if (headerCol._field == ZmItem.F_SORTED_BY) { 721 var field = headerCol._field; 722 var textTdId = this._itemCountTextTdId = DwtId.makeId(this.view, ZmSetting.RP_RIGHT, "td"); 723 htmlArr[idx++] = "<td id='"; 724 htmlArr[idx++] = id; 725 htmlArr[idx++] = "' class='"; 726 htmlArr[idx++] = (id == this._currentColId) ? "DwtListView-Column DwtListView-ColumnActive'" : 727 "DwtListView-Column'"; 728 htmlArr[idx++] = " width='auto'><table width='100%'><tr><td id='"; 729 htmlArr[idx++] = DwtId.getListViewHdrId(DwtId.WIDGET_HDR_LABEL, this._view, field); 730 htmlArr[idx++] = "' class='DwtListHeaderItem-label'>"; 731 htmlArr[idx++] = headerCol._label; 732 htmlArr[idx++] = "</td>"; 733 734 // sort icon 735 htmlArr[idx++] = "<td class='itemSortIcon' id='"; 736 htmlArr[idx++] = DwtId.getListViewHdrId(DwtId.WIDGET_HDR_ARROW, this._view, field); 737 htmlArr[idx++] = "'>"; 738 htmlArr[idx++] = AjxImg.getImageHtml(this._bSortAsc ? "ColumnUpArrow" : "ColumnDownArrow"); 739 htmlArr[idx++] = "</td>"; 740 741 // item count text 742 htmlArr[idx++] = "<td align=right class='itemCountText' id='"; 743 htmlArr[idx++] = textTdId; 744 htmlArr[idx++] = "'></td></tr></table></td>"; 745 746 return idx; 747 } else { 748 return DwtListView.prototype._createHeader.apply(this, arguments); 749 } 750 }; 751 752 ZmMailListView.prototype._resetColWidth = 753 function() { 754 755 if (!this.headerColCreated) { return; } 756 757 var lastColIdx = this._getLastColumnIndex(); 758 if (lastColIdx) { 759 var lastCol = this._headerList[lastColIdx]; 760 if (lastCol._field != ZmItem.F_SORTED_BY) { 761 DwtListView.prototype._resetColWidth.apply(this, arguments); 762 } 763 } 764 }; 765 766 ZmMailListView.prototype._mouseOverAction = 767 function(mouseEv, div) { 768 769 var type = this._getItemData(div, "type"); 770 if (type == DwtListView.TYPE_HEADER_ITEM){ 771 var hdr = this.getItemFromElement(div); 772 if (hdr && this.sortingEnabled && hdr._sortable && hdr._sortable == ZmItem.F_FROM) { 773 if (this._isOutboundFolder()) { 774 div.className = "DwtListView-Column DwtListView-ColumnHover"; 775 return true; 776 } 777 } 778 } 779 780 return ZmListView.prototype._mouseOverAction.apply(this, arguments); 781 }; 782 783 ZmMailListView.prototype._columnClicked = 784 function(clickedCol, ev) { 785 786 var hdr = this.getItemFromElement(clickedCol); 787 var group = this.getGroup(this._folderId); 788 if (group && hdr && hdr._sortable) { 789 var groupId = ZmMailListGroup.getGroupIdFromSortField(hdr._sortable, this.type); 790 if (groupId != group.id) { 791 this.setGroup(groupId); 792 } 793 } 794 795 ZmListView.prototype._columnClicked.call(this, clickedCol, ev); 796 }; 797 798 ZmMailListView.prototype._resetFromColumnLabel = 799 function() { 800 801 // set the from column name based on query string 802 var headerCol = this._headerHash[ZmItem.F_FROM]; 803 if (headerCol) { //this means viewing pane on bottom 804 var colLabel = this._isOutboundFolder() ? ZmMsg.to : ZmMsg.from; 805 //bug:1108 & 43789#c19 since sort-by-rcpt affects server performance avoid using in convList instead used in outbound folder 806 headerCol._sortable = this._isOutboundFolder() ? ZmItem.F_TO : ZmItem.F_FROM; 807 808 var fromColSpan = document.getElementById(DwtId.getListViewHdrId(DwtId.WIDGET_HDR_LABEL, this._view, headerCol._field)); 809 if (fromColSpan) { 810 fromColSpan.innerHTML = " " + colLabel; 811 } 812 var item = this._colHeaderActionMenu ? this._colHeaderActionMenu.getItem(headerCol._index) : null; 813 if (item) { 814 item.setText(colLabel); 815 } 816 } 817 }; 818 819 /** 820 * Returns true if the given folder is for outbound mail. 821 * 822 * @param folder 823 * 824 * @private 825 */ 826 ZmMailListView.prototype._isOutboundFolder = 827 function(folder) { 828 folder = folder || (this._folderId && appCtxt.getById(this._folderId)); 829 return (folder && folder.isOutbound()); 830 }; 831 832 /** 833 * Returns the current folder 834 * 835 */ 836 ZmMailListView.prototype.getFolder = 837 function() { 838 return this._folderId && appCtxt.getById(this._folderId); 839 }; 840 841 ZmMailListView.prototype.useListElement = 842 function() { 843 return true; 844 } 845 846 ZmMailListView.prototype._getRowClass = 847 function(item) { 848 var classes = this._isMultiColumn ? ["DwtMsgListMultiCol"]:["ZmRowDoubleHeader"]; 849 var value = this._getRowClassValue(item.isUnread && !item.isMute); 850 if (value) { 851 classes.push(value); 852 } 853 return classes.join(" "); 854 }; 855 856 ZmMailListView.prototype._getRowClassValue = 857 function(value) { 858 return value ? "Unread" : null; 859 }; 860 861 ZmMailListView.prototype._getCellId = 862 function(item, field) { 863 if (field == ZmItem.F_DATE) { 864 return null; 865 } 866 else if (field == ZmItem.F_SORTED_BY) { 867 return this._getFieldId(item, field); 868 } 869 else { 870 return ZmListView.prototype._getCellId.apply(this, arguments); 871 } 872 }; 873 874 ZmMailListView.prototype._getHeaderToolTip = 875 function(field, itemIdx) { 876 877 var isOutboundFolder = this._isOutboundFolder(); 878 if (field == ZmItem.F_FROM && isOutboundFolder) { 879 return ZmMsg.to; 880 } 881 if (field == ZmItem.F_STATUS) { 882 return ZmMsg.messageStatus; 883 } 884 if (field == ZmItem.F_MUTE) { 885 return ZmMsg.muteUnmute; 886 } 887 if (field == ZmItem.F_READ) { 888 return ZmMsg.readUnread; 889 } 890 891 return ZmListView.prototype._getHeaderToolTip.call(this, field, itemIdx, isOutboundFolder); 892 }; 893 894 ZmMailListView.prototype._getToolTip = 895 function(params) { 896 897 var tooltip, 898 field = params.field, 899 item = params.item, 900 matchIndex = params.match && params.match.participant || 0; 901 902 if (!item) { 903 return; 904 } 905 906 if (field === ZmItem.F_STATUS) { 907 tooltip = item.getStatusTooltip(); 908 } 909 else if (appCtxt.get(ZmSetting.CONTACTS_ENABLED) && (field === ZmItem.F_FROM || field === ZmItem.F_PARTICIPANT)) { 910 var addr; 911 if (!item.getAddress) { 912 return; 913 } 914 if (field === ZmItem.F_FROM) { 915 if (this._isOutboundFolder()) { 916 // this needs to be in sync with code that sets field IDs in ZmMailMsgListView::_getCellContents 917 var addrs = item.getAddresses(AjxEmailAddress.TO).getArray(); 918 addr = addrs[matchIndex]; 919 } 920 else { 921 addr = item.getAddress(AjxEmailAddress.FROM); 922 } 923 } 924 else if (field === ZmItem.F_PARTICIPANT) { 925 addr = item.participants && item.participants.get(matchIndex); 926 } 927 if (!addr) { 928 return; 929 } 930 931 var ttParams = { 932 address: addr, 933 ev: params.ev 934 } 935 var ttCallback = new AjxCallback(this, 936 function(callback) { 937 appCtxt.getToolTipMgr().getToolTip(ZmToolTipMgr.PERSON, ttParams, callback); 938 }); 939 tooltip = { callback:ttCallback }; 940 } 941 else if (field === ZmItem.F_SUBJECT || field === ZmItem.F_FRAGMENT) { 942 var invite = (item.type === ZmItem.MSG) && item.isInvite() && item.invite; 943 if (invite && item.needsRsvp()) { 944 tooltip = invite.getToolTip(); 945 } 946 else if (invite && !invite.isEmpty()) { 947 var bp = item.getBodyPart(); 948 tooltip = bp && ZmInviteMsgView.truncateBodyContent(bp.getContent(), true); 949 tooltip = AjxStringUtil.stripTags(tooltip); 950 } 951 else { 952 tooltip = AjxStringUtil.htmlEncode(item.fragment || (item.hasAttach ? "" : ZmMsg.fragmentIsEmpty)); 953 } 954 // Strip surrounding whitespace from the tooltip 955 tooltip = AjxStringUtil.trim(tooltip, false, "\\s"); 956 } 957 else if (field === ZmItem.F_FOLDER) { 958 var folder = appCtxt.getById(item.folderId); 959 if (folder && folder.parent) { 960 var path = folder.getPath(); 961 if (path !== folder.getName()) { 962 tooltip = path; 963 } 964 } 965 } 966 else if (field === ZmItem.F_ACCOUNT) { 967 tooltip = item.getAccount().getDisplayName(); 968 } 969 else { 970 tooltip = ZmListView.prototype._getToolTip.apply(this, arguments); 971 } 972 return tooltip; 973 }; 974 975 /** 976 * (override of ZmListView to add hooks to zimletMgr) 977 * Creates a TD and its content for a single field of the given item. Subclasses 978 * may override several dependent functions to customize the TD and its content. 979 * 980 * @param htmlArr [array] array that holds lines of HTML 981 * @param idx [int] current line of array 982 * @param item [object] item to render 983 * @param field [constant] column identifier 984 * @param colIdx [int] index of column (starts at 0) 985 * @param params [hash]* hash of optional params 986 * 987 * @private 988 */ 989 ZmMailListView.prototype._getCell = 990 function(htmlArr, idx, item, field, colIdx, params) { 991 var className = this._getCellClass(item, field, params, colIdx); 992 993 /* TODO Identify a way for Zimlets to add styles to fields. 994 var cellId = this._getCellId(item, field, params); 995 var idText = cellId ? [" id=", "'", cellId, "'"].join("") : ""; 996 var width = this._getCellWidth(colIdx, params); 997 var widthText = width ? ([" width=", width].join("")) : (" width='100%'"); 998 var classText = className ? [" class=", className].join("") : ""; 999 var alignValue = this._getCellAlign(colIdx, params); 1000 var alignText = alignValue ? [" align=", alignValue].join("") : ""; 1001 var otherText = (this._getCellAttrText(item, field, params)) || ""; 1002 var attrText = [idText, widthText, classText, alignText, otherText].join(" "); 1003 1004 htmlArr[idx++] = "<td"; 1005 htmlArr[idx++] = this._getStyleViaZimlet(field, item); 1006 htmlArr[idx++] = attrText ? (" " + attrText) : ""; 1007 htmlArr[idx++] = ">"; 1008 1009 idx = this._getCellContents(htmlArr, idx, item, field, colIdx, params); 1010 htmlArr[idx++] = "</td>";*/ 1011 1012 idx = this._getCellContents(htmlArr, idx, item, field, colIdx, params, [className || ""]); 1013 1014 return idx; 1015 }; 1016 1017 ZmMailListView.prototype._getCellClass = 1018 function(item, field, params, colIdx) { 1019 var classes = null; 1020 if (!this._isMultiColumn && field == ZmItem.F_SUBJECT) { 1021 classes = "SubjectDoubleRow "; 1022 } 1023 if (colIdx == null) { return classes; } 1024 var headerList = params.headerList || this._headerList; 1025 return classes ? classes + headerList[colIdx]._cssClass: headerList[colIdx]._cssClass; 1026 }; 1027 1028 ZmMailListView.prototype._getFlagIcon = 1029 function(isFlagged, isMouseover) { 1030 return (isFlagged || isMouseover) 1031 ? "FlagRed" 1032 : (this._isMultiColumn ? "Blank_16" : "FlagDis"); 1033 }; 1034 1035 1036 /** 1037 * Returns a list of the largest subset of the given participants that will fit within the 1038 * given width. The participants are assumed to be ordered oldest to most recent. We return 1039 * as many of the most recent as possible. 1040 * 1041 * @private 1042 * @param {array} participants list of AjxEmailAddress 1043 * @param {ZmMailItem} item item that contains the participants 1044 * @param {int} width available space in pixels 1045 * 1046 * @return list of participant objects with 'name' and 'index' fields 1047 */ 1048 ZmMailListView.prototype._fitParticipants = 1049 function(participants, item, availWidth) { 1050 1051 availWidth -= 15; // safety margin 1052 1053 var sepWidth = AjxStringUtil.getWidth(AjxStringUtil.LIST_SEP, item.isUnread); 1054 var ellWidth = AjxStringUtil.getWidth(AjxStringUtil.ELLIPSIS, item.isUnread); 1055 1056 // first see if we can fit everyone with their full names 1057 var list = []; 1058 var pLen = Math.min(20, participants.length); 1059 var width = 0; 1060 for (var i = 0; i < pLen; i++) { 1061 var p = participants[i]; 1062 var field = p.name || p.address || p.company || ""; 1063 width += AjxStringUtil.getWidth(AjxStringUtil.htmlEncode(field), item.isUnread); 1064 list.push({name:field, index:i}); 1065 } 1066 width += (pLen - 1) * sepWidth; 1067 if (width < availWidth) { 1068 return list; 1069 } 1070 1071 // now try with display (first) names; fit as many of the most recent as we can 1072 list = []; 1073 for (var i = 0; i < pLen; i++) { 1074 var p = participants[i]; 1075 var field = p.dispName || p.address || p.company || ""; 1076 list.push({name:field, index:i}); 1077 } 1078 while (list.length) { 1079 var width = 0; 1080 // total the width of the names 1081 for (var i = 0; i < list.length; i++) { 1082 width += AjxStringUtil.getWidth(AjxStringUtil.htmlEncode(list[i].name), item.isUnread); 1083 } 1084 // add the width of the separators 1085 width += (list.length - 1) * sepWidth; 1086 // add the width of the ellipsis if we've dropped anyone 1087 if (list.length < pLen) { 1088 width += ellWidth; 1089 } 1090 if (width < availWidth) { 1091 return list; 1092 } else { 1093 list.shift(); 1094 } 1095 } 1096 1097 // not enough room for even one participant, just return the last one 1098 var p = participants[pLen - 1]; 1099 var field = p.dispName || p.address || p.company || ""; 1100 return [{name:field, index:pLen - 1}]; 1101 }; 1102 1103 ZmMailListView.prototype._getActionMenuForColHeader = 1104 function(force) { 1105 1106 var menu; 1107 if (this.isMultiColumn()) { 1108 var doReset = (!this._colHeaderActionMenu || force); 1109 menu = ZmListView.prototype._getActionMenuForColHeader.call(this, force, this, "header"); 1110 if (doReset) { 1111 this._resetFromColumnLabel(); 1112 this._groupByActionMenu = this._getGroupByActionMenu(menu); 1113 } 1114 else if (this._groupByActionMenu) { 1115 this._setGroupByCheck(); 1116 } 1117 } 1118 else { 1119 if (!this._colHeaderActionMenu || force) { 1120 this._colHeaderActionMenu = this._setupSortMenu(null, true); 1121 } 1122 menu = this._colHeaderActionMenu; 1123 } 1124 1125 return menu; 1126 }; 1127 1128 1129 ZmMailListView.prototype._getSingleColumnSortFields = 1130 function() { 1131 return ZmMailListView.SINGLE_COLUMN_SORT; 1132 }; 1133 1134 1135 ZmMailListView.prototype._setupSortMenu = function(parent, includeGroupByMenu) { 1136 1137 var activeSortBy = this.getActiveSearchSortBy(); 1138 var defaultSort = activeSortBy && ZmMailListView.SORTBY_HASH[activeSortBy] ? 1139 ZmMailListView.SORTBY_HASH[activeSortBy].field : ZmItem.F_DATE; 1140 var sortMenu = this._getSortMenu(this._getSingleColumnSortFields(), defaultSort, parent); 1141 if (includeGroupByMenu) { 1142 this._groupByActionMenu = this._getGroupByActionMenu(sortMenu); 1143 this._setGroupByCheck(); 1144 } 1145 var mi = sortMenu.getMenuItem(ZmItem.F_FROM); 1146 if (mi) { 1147 mi.setVisible(!this._isOutboundFolder()); 1148 } 1149 mi = sortMenu.getMenuItem(ZmItem.F_TO); 1150 if (mi) { 1151 mi.setVisible(this._isOutboundFolder()); 1152 } 1153 1154 return sortMenu; 1155 }; 1156 1157 ZmMailListView.prototype._getNoResultsMessage = 1158 function() { 1159 if (appCtxt.isOffline && !appCtxt.getSearchController().searchAllAccounts) { 1160 // offline folders which are "syncable" but currently not syncing should 1161 // display a different message 1162 var fid = ZmOrganizer.getSystemId(this._controller._getSearchFolderId()); 1163 var folder = fid && appCtxt.getById(fid); 1164 if (folder) { 1165 if (folder.isOfflineSyncable && !folder.isOfflineSyncing) { 1166 var link = "ZmMailListView.toggleSync('" + folder.id + "', '" + this._htmlElId + "');"; 1167 return AjxMessageFormat.format(ZmMsg.notSyncing, link); 1168 } 1169 } 1170 } 1171 1172 return DwtListView.prototype._getNoResultsMessage.call(this); 1173 }; 1174 1175 ZmMailListView.toggleSync = 1176 function(folderId, htmlElementId) { 1177 var folder = appCtxt.getById(folderId); 1178 var htmlEl = folder ? document.getElementById(htmlElementId) : null; 1179 var listview = htmlEl ? DwtControl.fromElement(htmlEl) : null; 1180 if (listview) { 1181 var callback = new AjxCallback(listview, listview._handleToggleSync, [folder]); 1182 folder.toggleSyncOffline(callback); 1183 } 1184 }; 1185 1186 ZmMailListView.prototype._handleToggleSync = 1187 function(folder) { 1188 folder.getAccount().sync(); 1189 // bug fix #27846 - just clear the list view and let instant notify populate 1190 this.removeAll(true); 1191 }; 1192 1193 1194 // Listeners 1195 1196 ZmMailListView.prototype.handleUnmuteConv = 1197 function(items) { 1198 //overridden in ZmConvListView 1199 }; 1200 1201 ZmMailListView.prototype._changeListener = 1202 function(ev) { 1203 1204 var item = this._getItemFromEvent(ev); 1205 if (!item || ev.handled || !this._handleEventType[item.type]) { 1206 if (ev && ev.event == ZmEvent.E_CREATE) { 1207 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailListView: initial check failed"); 1208 } 1209 return; 1210 } 1211 1212 if ((!this.isMultiColumn() || appCtxt.get(ZmSetting.COLOR_MESSAGES)) 1213 && (ev.event == ZmEvent.E_TAGS || ev.event == ZmEvent.E_REMOVE_ALL)) { 1214 DBG.println(AjxDebug.DBG2, "ZmMailListView: TAG"); 1215 this.redrawItem(item); 1216 ZmListView.prototype._changeListener.call(this, ev); 1217 ev.handled = true; 1218 } 1219 1220 if (ev.event == ZmEvent.E_FLAGS) { // handle "unread" flag 1221 DBG.println(AjxDebug.DBG2, "ZmMailListView: FLAGS"); 1222 var flags = ev.getDetail("flags"); 1223 for (var j = 0; j < flags.length; j++) { 1224 var flag = flags[j]; 1225 if (flag == ZmItem.FLAG_MUTE) { 1226 var on = item[ZmItem.FLAG_PROP[flag]]; 1227 this.markUIAsMute(item, !on); 1228 } 1229 else if (flag == ZmItem.FLAG_UNREAD) { 1230 var on = item[ZmItem.FLAG_PROP[flag]]; 1231 this.markUIAsRead(item, !on); 1232 } 1233 } 1234 } 1235 1236 if (ev.event == ZmEvent.E_CREATE) { 1237 DBG.println(AjxDebug.DBG2, "ZmMailListView: CREATE"); 1238 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailListView: handle create " + item.id); 1239 1240 if (this._controller.actionedMsgId) { 1241 var newMsg = appCtxt.getById(this._controller.actionedMsgId); 1242 if (newMsg) { 1243 this._itemToSelect = this._controller.isZmConvListController ? appCtxt.getById(newMsg.cid) : newMsg; 1244 } 1245 this._controller.actionedMsgId = null; 1246 } 1247 1248 if (this._list && this._list.contains(item)) { 1249 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailListView: list already has item " + item.id); 1250 return; 1251 } 1252 if (!this._handleEventType[item.type]) { 1253 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailListView: list view of type " + this._mode + " does not handle " + item.type); 1254 return; 1255 } 1256 1257 // Check to see if ZmMailList::notifyCreate gave us an index for the item. 1258 // If not, we assume that the new conv/msg is the most recent one. The only case 1259 // we handle is where the user is on the first page. 1260 // 1261 // TODO: handle other sort orders, arbitrary insertion points 1262 //about the above - for now we insert new items on top (index would be 0 if not sorted by date). 1263 // I believe that way the users won't lose new messages if they are sorted by a different order. 1264 if (this._isPageless || this.offset == 0) { 1265 var sortIndex = ev.getDetail("sortIndex") || 0; 1266 AjxDebug.println(AjxDebug.NOTIFY, "ZmMailListView: adding item " + item.id + " at index " + sortIndex); 1267 this.addItem(item, sortIndex); 1268 1269 if (appCtxt.isOffline && appCtxt.getActiveAccount().isOfflineInitialSync()) { 1270 this._controller._app.numEntries++; 1271 } 1272 } 1273 ev.handled = true; 1274 } 1275 1276 if (!ev.handled) { 1277 ZmListView.prototype._changeListener.call(this, ev); 1278 if (ev.event == ZmEvent.E_MOVE || ev.event == ZmEvent.E_DELETE){ 1279 var cv = this._controller.getItemView(); 1280 var currentItem = cv && cv.getItem(); 1281 if (currentItem == item){ 1282 cv.set(null, true) 1283 } 1284 } 1285 } 1286 }; 1287 1288 /** 1289 * If we're showing content in the reading pane and there is exactly one item selected, 1290 * make sure the content is for that selected item. Otherwise, clear the content. 1291 */ 1292 ZmMailListView.prototype._itemClicked = 1293 function(clickedEl, ev) { 1294 1295 //bug:67455 request permission for desktop notifications 1296 if(window.webkitNotifications && appCtxt.get(ZmSetting.MAIL_NOTIFY_TOASTER)){ 1297 var perm = webkitNotifications.checkPermission(); 1298 if(perm == 1){ 1299 webkitNotifications.requestPermission(function(){}); 1300 } 1301 //else if(perm == 2) ){ /*ignore when browser has disabled notifications*/ } 1302 } 1303 1304 Dwt.setLoadingTime("ZmMailItem"); 1305 ZmListView.prototype._itemClicked.apply(this, arguments); 1306 1307 var ctlr = this._controller; 1308 if (ctlr.isReadingPaneOn()) { 1309 if (appCtxt.get(ZmSetting.SHOW_SELECTION_CHECKBOX) && ev.button == DwtMouseEvent.LEFT) { 1310 if (!ev.shiftKey && !ev.ctrlKey) { 1311 // get the field being clicked 1312 var target = this._getEventTarget(ev); 1313 var id = (target && target.id && target.id.indexOf("AjxImg") == -1) ? target.id : clickedEl.id; 1314 var m = id ? this._parseId(id) : null; 1315 if (m && m.field == ZmItem.F_SELECTION) { 1316 if (this.getSelectionCount() == 1) { 1317 var item = this.getSelection()[0]; 1318 var msg = (item instanceof ZmConv) ? item.getFirstHotMsg() : item; 1319 if (msg && ctlr._curItem && (msg.id != ctlr._curItem.id)) { 1320 ctlr.reset(); 1321 } 1322 } 1323 } 1324 } 1325 } 1326 } 1327 }; 1328 1329 ZmMailListView.prototype._setNextSelection = 1330 function() { 1331 1332 if (this._itemToSelect) { 1333 var item = this._getItemToSelect(); 1334 if (item) { 1335 DBG.println(AjxDebug.DBG1, "ZmMailListView._setNextSelection: select item with ID: " + item.id); 1336 this.setSelection(item, false); 1337 this._itemToSelect = null; 1338 } 1339 } 1340 }; 1341 1342 /** 1343 * Returns the next item to select, typically set by the controller. 1344 * 1345 * @private 1346 */ 1347 ZmMailListView.prototype._getItemToSelect = 1348 function() { 1349 var item = this._itemToSelect || (this._list && this._list.get(0)); 1350 var list = this.getList(true); 1351 if (item == ZmMailListView.FIRST_ITEM) { 1352 list = list && list.getArray(); 1353 item = list && list[0]; 1354 } else if (item == ZmMailListView.LAST_ITEM) { 1355 list = list && list.getArray(); 1356 item = list && list[list.length - 1]; 1357 } 1358 return item; 1359 }; 1360 1361 ZmMailListView.prototype._getSearchForSort = 1362 function(sortField, controller) { 1363 controller = controller || this._controller; 1364 var query = controller.getSearchString(); 1365 if (!query) { return ""; } 1366 if (sortField != ZmItem.F_READ) { 1367 return; //shouldn't happen. READ/Unread is the only current filter 1368 } 1369 1370 var str = "is:unread"; 1371 if (query.indexOf(str) != -1) { 1372 query = AjxStringUtil.trim(query.replace(str, "")); 1373 } else { 1374 query = query + " " + str; 1375 } 1376 return query; 1377 }; 1378 1379 ZmMailListView.prototype._columnHasCustomQuery = 1380 function(columnItem) { 1381 return columnItem._sortable == ZmItem.F_READ; 1382 }; 1383 1384 ZmMailListView.prototype._isDefaultSortAscending = 1385 function(colHeader) { 1386 // if date, flag, attachment or size fields, sort desc by default - otherwise ascending. 1387 var sortable = colHeader._sortable; 1388 var desc = sortable === ZmItem.F_DATE 1389 || sortable === ZmItem.F_FLAG 1390 || sortable === ZmItem.F_ATTACHMENT 1391 || sortable === ZmItem.F_SIZE; 1392 return !desc; 1393 }; 1394 1395 //GROUP SUPPORT 1396 ZmMailListView.prototype.reset = 1397 function() { 1398 this.clearGroupSections(this.getActiveSearchFolderId()); 1399 ZmListView.prototype.reset.call(this); 1400 }; 1401 1402 ZmMailListView.prototype.removeAll = 1403 function() { 1404 //similar to reset, but can't call reset since it sets _rendered to false and that prevents pagination from working. 1405 this.clearGroupSections(this.getActiveSearchFolderId()); 1406 ZmListView.prototype.removeAll.call(this); 1407 }; 1408 1409 1410 /** 1411 * Clear groups 1412 * @param {int} folderId folderId to get group 1413 */ 1414 ZmMailListView.prototype.clearGroupSections = 1415 function(folderId) { 1416 if (folderId) { 1417 var group = this.getGroup(folderId); 1418 if (group) { 1419 group.clearSections(); 1420 } 1421 } 1422 else if (this._group) { 1423 this._group.clearSections(); 1424 } 1425 }; 1426 1427 /** 1428 * Set the group 1429 * @param {String} groupId 1430 */ 1431 ZmMailListView.prototype.setGroup = 1432 function(groupId) { 1433 this._group = ZmMailListGroup.getGroup(groupId); 1434 if (this._folderId && !this._controller.isSearchResults) { 1435 appCtxt.set(ZmSetting.GROUPBY_LIST, groupId || ZmId.GROUPBY_NONE, this._folderId); //persist group Id 1436 appCtxt.set(ZmSetting.GROUPBY_HASH, this._group, this._folderId); //local cache for group object 1437 } 1438 }; 1439 1440 /** 1441 * get the group 1442 * @param {int} folderId 1443 * @return {ZmMailListGroup} group object or null 1444 */ 1445 ZmMailListView.prototype.getGroup = 1446 function(folderId) { 1447 if (folderId) { 1448 var group = appCtxt.get(ZmSetting.GROUPBY_HASH, folderId); 1449 if (!group) { 1450 var groupId = appCtxt.get(ZmSetting.GROUPBY_LIST, folderId); 1451 group = ZmMailListGroup.getGroup(groupId); 1452 appCtxt.set(ZmSetting.GROUPBY_HASH, group, folderId); 1453 } 1454 1455 var activeSortBy = this.getActiveSearchSortBy(); 1456 if (activeSortBy && ZmMailListView.SORTBY_HASH[activeSortBy] && group && group.field != ZmMailListView.SORTBY_HASH[activeSortBy].field) { 1457 //switching views can cause problems; make sure the group and sortBy match 1458 group = null; 1459 appCtxt.set(ZmSetting.GROUPBY_HASH, group, folderId); //clear cache 1460 appCtxt.set(ZmSetting.GROUPBY_LIST, ZmId.GROUPBY_NONE, folderId); //persist groupId 1461 } 1462 1463 1464 return group; 1465 } 1466 else { 1467 return this._group; 1468 } 1469 }; 1470 1471 ZmMailListView.prototype._getGroupByActionMenu = function(parent, noSeparator, menuItemIsParent) { 1472 1473 var list = [ ZmOperation.GROUPBY_NONE, ZmOperation.GROUPBY_DATE, ZmOperation.GROUPBY_FROM, ZmOperation.GROUPBY_SIZE ]; 1474 if (this._mode == ZmId.VIEW_CONVLIST || this._isOutboundFolder()) { 1475 AjxUtil.arrayRemove(list, ZmOperation.GROUPBY_FROM); 1476 } 1477 1478 var actionListener = this._groupByActionListener.bind(this); 1479 var sortActionListener = this._sortByActionListener.bind(this); 1480 1481 if (!noSeparator) { 1482 parent.createSeparator(); 1483 } 1484 1485 var menuItem = parent.createMenuItem(Dwt.getNextId("GROUP_BY_"), { text: ZmMsg.groupBy, style: DwtMenuItem.NO_STYLE }); 1486 var menu = new ZmPopupMenu(menuItemIsParent ? menuItem : parent); 1487 1488 var groupById = Dwt.getNextId("GroupByActionMenu_"); 1489 var sortById = Dwt.getNextId("SortByActionMenu_"); 1490 for (var i = 0; i < list.length; i++) { 1491 var mi = menu.createMenuItem(list[i], { 1492 text: ZmMsg[ZmOperation.getProp(list[i], "textKey")], 1493 style: DwtMenuItem.RADIO_STYLE, 1494 radioGroupId: groupById 1495 }); 1496 mi.addSelectionListener(actionListener); 1497 if (this._group && this._group.id === list[i]) { 1498 mi.setChecked(true, true); 1499 } 1500 else if (!this._group && list[i] === ZmOperation.GROUPBY_NONE) { 1501 mi.setChecked(true, true); 1502 } 1503 } 1504 1505 menu.createSeparator(); 1506 1507 var sortAsc = menu.createMenuItem(ZmOperation.SORT_ASC, { 1508 text: ZmMsg[ZmOperation.getProp(ZmOperation.SORT_ASC, "textKey")], 1509 style: DwtMenuItem.RADIO_STYLE, 1510 radioGroupId: sortById 1511 }); 1512 sortAsc.addSelectionListener(sortActionListener); 1513 1514 var sortDesc = menu.createMenuItem(ZmOperation.SORT_DESC, { 1515 text: ZmMsg[ZmOperation.getProp(ZmOperation.SORT_DESC, "textKey")], 1516 style: DwtMenuItem.RADIO_STYLE, 1517 radioGroupId: sortById 1518 }); 1519 sortDesc.addSelectionListener(sortActionListener); 1520 1521 if (this._bSortAsc) { 1522 sortAsc.setChecked(true, true); 1523 } 1524 else { 1525 sortDesc.setChecked(true, true); 1526 } 1527 menuItem.setMenu(menu); 1528 1529 return menu; 1530 }; 1531 1532 ZmMailListView.prototype._groupByActionListener = function(ev) { 1533 1534 var groupId = ev && ev.item && ev.item.getData(ZmOperation.MENUITEM_ID); 1535 //var oldGroup = this._group ? this._group : this.getGroup(this._folderId); 1536 var oldGroup = this.getGroup(this._folderId); 1537 var field = ZmMailListGroup.getHeaderField(groupId, this._isMultiColumn); 1538 var hdr = this._headerHash[field]; 1539 if (!hdr) { 1540 if (oldGroup) { 1541 field = ZmMailListGroup.getHeaderField(oldGroup.id, this._isMultiColumn); //groups turned off, keep sort the same 1542 } 1543 else { 1544 field = ZmId.FLD_DATE; 1545 } 1546 hdr = this._headerHash[field]; 1547 this.setGroup(null); 1548 } 1549 else { 1550 if (!oldGroup || (oldGroup.id != groupId)) { 1551 hdr._sortable = ZmMailListGroup.getHeaderField(groupId); 1552 this.setGroup(groupId); 1553 } 1554 } 1555 1556 if (!this._isMultiColumn) { 1557 //this sets the "Sort by: Field" for reading pane on right 1558 var column = ZmMailListGroup.getHeaderField(groupId); 1559 for (var i = 0; i < ZmMailListView.SINGLE_COLUMN_SORT.length; i++) { 1560 if (column == ZmMailListView.SINGLE_COLUMN_SORT[i].field) { 1561 if (this._colHeaderActionMenu) { 1562 var mi = this._colHeaderActionMenu.getMenuItem(column); 1563 if (mi) { 1564 mi.setChecked(true, true); 1565 var label = AjxMessageFormat.format(ZmMsg.arrangeBy, ZmMsg[ZmMailListView.SINGLE_COLUMN_SORT[i].msg]); 1566 column = this._headerHash[ZmItem.F_SORTED_BY]; 1567 var cell = document.getElementById(DwtId.getListViewHdrId(DwtId.WIDGET_HDR_LABEL, this._view, field)); 1568 if (cell) { 1569 cell.innerHTML = label; 1570 } 1571 break; 1572 } 1573 } 1574 } 1575 } 1576 } 1577 1578 if (ev && ev.item) { 1579 ev.item.setChecked(true, ev, true); 1580 } 1581 this._sortColumn(hdr, this._bSortAsc); 1582 //Hack: we don't re-fetch search results when list is size of 1; but if user changes their group let's re-render 1583 var list = this.getList(); 1584 if (list && list.size() == 1 && this._sortByString) { 1585 this._renderList(list); 1586 } 1587 1588 var ctlr = this._controller; 1589 ctlr._updateViewMenu(groupId, ctlr._groupByViewMenu); 1590 }; 1591 1592 ZmMailListView.prototype._sortByActionListener = function(ev) { 1593 1594 var data = ev && ev.item && ev.item.getData(ZmOperation.MENUITEM_ID); 1595 var sortAsc = (data === ZmId.OP_SORT_ASC); 1596 var oldSort = this._bSortAsc; 1597 if (oldSort != sortAsc) { 1598 this._bSortAsc = sortAsc; 1599 var col = Dwt.byId(this._currentColId); 1600 var hdr = (col && this.getItemFromElement(col)) || (this._headerHash && this._headerHash[ZmItem.F_SORTED_BY]) || null; 1601 if (hdr) { 1602 this.clearGroupSections(this._folderId); 1603 this._sortColumn(hdr, this._bSortAsc); 1604 if (!this._isMultiColumn) { 1605 this._setSortedColStyle(hdr._id); 1606 } 1607 } 1608 } 1609 1610 if (ev && ev.item) { 1611 ev.item.setChecked(true, ev, true); 1612 } 1613 1614 var ctlr = this._controller; 1615 ctlr._updateViewMenu(data, ctlr._groupByViewMenu); 1616 }; 1617 1618 ZmMailListView.prototype._sortMenuListener = 1619 function(ev) { 1620 1621 if (this._groupByActionMenu) { 1622 var mId = this._bSortAsc ? ZmOperation.OP_SORT_DESC : ZmOperation.OP_SORT_ASC; 1623 var mi = this._groupByActionMenu.getMenuItem(mId); 1624 if (mi) { 1625 mi.setChecked(true, true); 1626 } 1627 } 1628 var sortField = ev && ev.item && ev.item.getData(ZmOperation.MENUITEM_ID); 1629 if (this._group && sortField) { 1630 var groupId = ZmMailListGroup.getGroupIdFromSortField(sortField, this.type); 1631 this.setGroup(groupId); 1632 } 1633 this._setGroupByCheck(); 1634 ZmListView.prototype._sortMenuListener.call(this, ev); 1635 }; 1636 1637 ZmMailListView.prototype._sortColumn = function(columnItem, bSortAsc, callback) { 1638 1639 ZmListView.prototype._sortColumn.apply(this, arguments); 1640 1641 var ctlr = this._controller; 1642 ctlr._updateViewMenu(columnItem._sortable, ctlr._sortViewMenu); 1643 }; 1644 1645 ZmMailListView.prototype._setGroupByCheck = 1646 function() { 1647 1648 if (this._groupByActionMenu) { 1649 var mi = this._group && this._group.id ? this._groupByActionMenu.getMenuItem(this._group.id) : this._groupByActionMenu.getMenuItem(ZmOperation.GROUPBY_NONE); 1650 if (mi) { 1651 mi.setChecked(true, true); 1652 } 1653 1654 mi = this._bSortAsc ? this._groupByActionMenu.getMenuItem(ZmOperation.SORT_ASC) : this._groupByActionMenu.getMenuItem(ZmOperation.SORT_DESC); 1655 if (mi) { 1656 mi.setChecked(true, true); 1657 } 1658 } 1659 }; 1660 1661 /** 1662 * Adds a row for the given item to the list view. 1663 * Supports adding section header when group is set. 1664 * 1665 * @param {Object} item the data item 1666 * @param {number} index the index at which to add item to list and list view 1667 * @param {boolean} skipNotify if <code>true</code>, do not notify listeners 1668 * @param {number} itemIndex index at which to add item to list, if different 1669 * from the one for the list view 1670 */ 1671 ZmMailListView.prototype.addItem = 1672 function(item, index, skipNotify, itemIndex) { 1673 var group = this._group; 1674 if (!group) { 1675 return ZmListView.prototype.addItem.call(this, item, index, skipNotify, itemIndex); 1676 } 1677 1678 if (!this._list) { 1679 this._list = new AjxVector(); 1680 } 1681 1682 // clear the "no results" message before adding! 1683 if (this._list.size() == 0) { 1684 this._resetList(); 1685 } 1686 1687 var section; 1688 var headerDiv; 1689 1690 this._list.add(item, (itemIndex != null) ? itemIndex : index); 1691 var div = this._createItemHtml(item); 1692 if (div) { 1693 if (div instanceof Array) { 1694 for (var j = 0; j < div.length; j++) { 1695 section = group.addMsgToSection(item, div[j]); 1696 if (group.getSectionSize(section) == 1){ 1697 headerDiv = this._getSectionHeaderDiv(group, section); 1698 this._addRow(headerDiv); 1699 } 1700 this._addRow(div[j]); 1701 } 1702 } 1703 else { 1704 section = group.addMsgToSection(item, div); 1705 if (group.getSectionSize(section) == 1){ 1706 headerDiv = this._getSectionHeaderDiv(group, section); 1707 this._addRow(headerDiv, index); 1708 } 1709 index = parseInt(index) || 0; //check for NaN index 1710 this._addRow(div, index+1); //account for header 1711 1712 } 1713 } 1714 1715 if (!skipNotify && this._evtMgr.isListenerRegistered(DwtEvent.STATE_CHANGE)) { 1716 this._evtMgr.notifyListeners(DwtEvent.STATE_CHANGE, this._stateChangeEv); 1717 } 1718 }; 1719 1720 /** 1721 * return the active search sortby value 1722 * @return {String} sortby value or null 1723 */ 1724 ZmMailListView.prototype.getActiveSearchSortBy = 1725 function() { 1726 var sortBy = AjxUtil.get(this._controller, "_activeSearch", "search", "sortBy") || null; 1727 return sortBy; 1728 }; 1729 1730 /** 1731 * return folderId for the active search 1732 * @return {String} folderId or null 1733 */ 1734 ZmMailListView.prototype.getActiveSearchFolderId = 1735 function() { 1736 var folderId = AjxUtil.get(this._controller, "_activeSearch", "search", "folderId") || null; 1737 return folderId; 1738 }; 1739 1740 ZmMailListView.prototype._changeFolderName = 1741 function(msg, oldFolderId) { 1742 1743 var folder = appCtxt.getById(msg.folderId); 1744 1745 if (!this._controller.isReadingPaneOn() || !this._controller.isReadingPaneOnRight()) { 1746 var folderCell = folder ? this._getElement(msg, ZmItem.F_FOLDER) : null; 1747 if (folderCell) { 1748 folderCell.innerHTML = folder.getName(); 1749 } 1750 } 1751 1752 if (folder && (folder.nId == ZmFolder.ID_TRASH || oldFolderId == ZmFolder.ID_TRASH)) { 1753 this._changeTrashStatus(msg); 1754 } 1755 }; 1756 1757 ZmMailListView.prototype._changeTrashStatus = 1758 function(msg) { 1759 1760 var row = this._getElement(msg, ZmItem.F_ITEM_ROW); 1761 if (row) { 1762 if (msg.isUnread) { 1763 Dwt.addClass(row, "Unread"); 1764 } 1765 var folder = appCtxt.getById(msg.folderId); 1766 if (folder && folder.isInTrash()) { 1767 Dwt.addClass(row, "Trash"); 1768 } else { 1769 Dwt.delClass(row, "Trash"); 1770 } 1771 if (msg.isSent) { 1772 Dwt.addClass(row, "Sent"); 1773 } 1774 } 1775 }; 1776