1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 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) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 /** 24 * Creates a new calendar item edit view. 25 * @constructor 26 * @class 27 * This is the main screen for creating/editing a calendar item. It provides 28 * inputs for the various appointment/task details. 29 * 30 * @author Parag Shah 31 * 32 * @param {DwtControl} parent the container 33 * @param {Hash} attendees the attendees/locations/equipment 34 * @param {ZmController} controller the compose controller for this view 35 * @param {Object} dateInfo a hash of date info 36 * @param {static|relative|absolute} posStyle the position style 37 * @param {string} className Class name 38 * 39 * @extends DwtComposite 40 * 41 * @private 42 */ 43 ZmCalItemEditView = function(parent, attendees, controller, dateInfo, posStyle, className, uid) { 44 if (arguments.length == 0) { return; } 45 46 DwtComposite.call(this, {parent:parent, posStyle:posStyle, className:className, id:uid}); 47 48 this.uid = uid; 49 this._attendees = attendees; 50 this._controller = controller; 51 this._dateInfo = dateInfo; 52 53 this.setScrollStyle(DwtControl.SCROLL); 54 this._rendered = false; 55 56 var bComposeEnabled = appCtxt.get(ZmSetting.HTML_COMPOSE_ENABLED); 57 var composeFormat = appCtxt.get(ZmSetting.COMPOSE_AS_FORMAT); 58 this._composeMode = bComposeEnabled && composeFormat == ZmSetting.COMPOSE_HTML 59 ? Dwt.HTML : Dwt.TEXT; 60 61 this._repeatSelectDisabled = false; 62 this._attachCount = 0; 63 this._calendarOrgs = {}; 64 65 this._kbMgr = appCtxt.getKeyboardMgr(); 66 this._isForward = false; 67 this._isProposeTime = false; 68 69 this._customRecurDialogCallback = null; 70 this._enableCustomRecurCallback = true; 71 72 this.addControlListener(this._controlListener.bind(this)); 73 }; 74 75 ZmCalItemEditView.prototype = new DwtComposite; 76 ZmCalItemEditView.prototype.constructor = ZmCalItemEditView; 77 78 ZmCalItemEditView.prototype.toString = 79 function() { 80 return "ZmCalItemEditView"; 81 }; 82 83 // Consts 84 85 ZmCalItemEditView.UPLOAD_FIELD_NAME = "__calAttUpload__"; 86 ZmCalItemEditView.SHOW_MAX_ATTACHMENTS = AjxEnv.is800x600orLower ? 2 : 3; 87 88 ZmCalItemEditView._REPEAT_CHANGE = "REPEAT_CHANGE"; 89 90 // Public 91 92 ZmCalItemEditView.prototype.show = 93 function() { 94 this.resize(); 95 }; 96 97 ZmCalItemEditView.prototype.isRendered = 98 function() { 99 return this._rendered; 100 }; 101 102 /** 103 * Gets the calendar item. 104 * 105 * @return {ZmCalItem} the item 106 */ 107 ZmCalItemEditView.prototype.getCalItem = 108 function(attId) { 109 // attempt to submit attachments first! 110 if (!attId && this._gotAttachments()) { 111 this._submitAttachments(); 112 return null; 113 } 114 115 return this._populateForSave(this._getClone()); 116 }; 117 118 ZmCalItemEditView.prototype.initialize = 119 function(calItem, mode, isDirty, apptComposeMode) { 120 121 this._calItem = calItem; 122 this._isDirty = isDirty; 123 124 var firstTime = !this._rendered; 125 this.createHtml(); 126 127 this._mode = (mode == ZmCalItem.MODE_NEW_FROM_QUICKADD || !mode) ? ZmCalItem.MODE_NEW : mode; 128 this._reset(calItem, mode || ZmCalItem.MODE_NEW, firstTime); 129 }; 130 131 ZmCalItemEditView.prototype.cleanup = 132 function() { 133 if (this._recurDialog) { 134 this._recurDialog.clearState(); 135 this._recurDialogRepeatValue = null; 136 } 137 138 delete this._calItem; 139 this._calItem = null; 140 141 // clear out all input fields 142 this._subjectField.setValue(""); 143 this._notesHtmlEditor.clear(); 144 145 if(this._hasRepeatSupport) { 146 this._repeatDescField.innerHTML = ""; 147 // reinit non-time sensitive selects option values 148 this._repeatSelect.setSelectedValue(ZmApptViewHelper.REPEAT_OPTIONS[0].value); 149 } 150 151 // remove attachments if any were added 152 this._removeAllAttachments(); 153 154 // disable all input fields 155 this.enableInputs(false); 156 }; 157 158 ZmCalItemEditView.prototype.addRepeatChangeListener = 159 function(listener) { 160 this.addListener(ZmCalItemEditView._REPEAT_CHANGE, listener); 161 }; 162 163 // Acceptable hack needed to prevent cursor from bleeding thru higher z-index'd views 164 ZmCalItemEditView.prototype.enableInputs = 165 function(bEnableInputs) { 166 this._subjectField.setEnabled(bEnableInputs); 167 this._startDateField.disabled = !(bEnableInputs || this._isProposeTime); 168 this._endDateField.disabled = !(bEnableInputs || this._isProposeTime); 169 }; 170 171 ZmCalItemEditView.prototype.enableSubjectField = 172 function(bEnableInputs) { 173 this._subjectField.setEnabled(bEnableInputs); 174 }; 175 176 /** 177 * Checks to see if the recurring (repeat custom - CUS) changes dialog was edited. 178 * 179 */ 180 ZmCalItemEditView.prototype.areRecurringChangesDirty = 181 function() { 182 if (this._recurDialog) 183 return this._recurDialog.isDirty(); 184 else 185 return false; 186 }; 187 188 /** 189 * Checks for dirty fields. 190 * 191 * @param {Boolean} excludeAttendees if <code>true</code> check for dirty fields excluding the attendees field 192 */ 193 ZmCalItemEditView.prototype.isDirty = 194 function(excludeAttendees) { 195 if(this._controller.inactive) { 196 return false; 197 } 198 var formValue = excludeAttendees && this._origFormValueMinusAttendees 199 ? this._origFormValueMinusAttendees 200 : this._origFormValue; 201 202 return (this._gotAttachments() || this._removedAttachments()) || 203 this._isDirty || 204 (this._formValue(excludeAttendees) != formValue); 205 }; 206 207 /** 208 * Checks if reminder only is changed. 209 * 210 * @return {Boolean} <code>true</code> if reminder only changed 211 */ 212 ZmCalItemEditView.prototype.isReminderOnlyChanged = 213 function() { 214 215 if (!this._hasReminderSupport) { return false; } 216 217 var formValue = this._origFormValueMinusReminder; 218 219 var isDirty = (this._gotAttachments() || this._removedAttachments()) || 220 this._isDirty || 221 (this._formValue(false, true) != formValue); 222 223 var isReminderChanged = this._reminderSelectInput && (this._origReminderValue != this._reminderSelectInput.getValue()); 224 225 return isReminderChanged && !isDirty; 226 }; 227 228 ZmCalItemEditView.prototype.isValid = 229 function() { 230 // override 231 }; 232 233 ZmCalItemEditView.prototype.getComposeMode = 234 function() { 235 return this._composeMode; 236 }; 237 238 ZmCalItemEditView.prototype.setComposeMode = 239 function(composeMode) { 240 this._composeMode = composeMode || this._composeMode; 241 this._notesHtmlModeFirstTime = !this._notesHtmlEditor.isHtmlModeInited(); 242 this._notesHtmlEditor.setMode(this._composeMode, true); 243 this.resize(); 244 }; 245 246 ZmCalItemEditView.prototype.reEnableDesignMode = 247 function() { 248 if (this._composeMode == Dwt.HTML) 249 this._notesHtmlEditor.reEnableDesignMode(); 250 }; 251 252 ZmCalItemEditView.prototype.createHtml = 253 function() { 254 if (!this._rendered) { 255 var width = AjxEnv.is800x600orLower ? "150" : "250"; 256 257 this._createHTML(); 258 this._createWidgets(width); 259 this._cacheFields(); 260 this._addEventHandlers(); 261 this._rendered = true; 262 } 263 }; 264 265 /** 266 * Adds an attachment (file input field) to the appointment view. If none 267 * already exist, creates the attachments container. If <code>attach</code> parameters is 268 * provided, user is opening an existing appointment w/ an attachment and therefore 269 * display differently. 270 * 271 * @param {ZmCalItem} calItem the calendar item 272 * @param {Object} attach the attachment 273 * 274 * @private 275 */ 276 ZmCalItemEditView.prototype.addAttachmentField = 277 function(calItem, attach) { 278 if (this._attachCount == 0) { 279 this._initAttachContainer(); 280 } 281 282 this._attachCount++; 283 284 // add file input field 285 var div = document.createElement("div"); 286 var id = this._htmlElId; 287 var attachRemoveId = id + "_att_" + Dwt.getNextId(); 288 var attachInputId = id + "_att_" + Dwt.getNextId(); 289 var sizeContId = id + "_att_" + Dwt.getNextId(); 290 291 if (attach) { 292 div.innerHTML = ZmApptViewHelper.getAttachListHtml(calItem, attach, true); 293 } else { 294 var subs = { 295 id: id, 296 attachInputId: attachInputId, 297 attachRemoveId: attachRemoveId, 298 sizeId: sizeContId, 299 uploadFieldName: ZmCalItemEditView.UPLOAD_FIELD_NAME 300 }; 301 div.innerHTML = AjxTemplate.expand("calendar.Appointment#AttachAdd", subs); 302 } 303 304 if (this._attachDiv == null) { 305 this._attachDiv = document.getElementById(this._attachDivId); 306 } 307 this._attachDiv.appendChild(div); 308 309 if (attach == null) { 310 // add event handlers as necessary 311 var tvpId = AjxCore.assignId(this); 312 var attachRemoveSpan = document.getElementById(attachRemoveId); 313 attachRemoveSpan._editViewId = tvpId; 314 attachRemoveSpan._parentDiv = div; 315 Dwt.setHandler(attachRemoveSpan, DwtEvent.ONCLICK, ZmCalItemEditView._onClick); 316 317 var attachInputEl = document.getElementById(attachInputId); 318 // trap key presses in IE for input field so we can ignore ENTER key (bug 961) 319 if (AjxEnv.isIE) { 320 //var attachInputEl = document.getElementById(attachInputId); 321 attachInputEl._editViewId = tvpId; 322 Dwt.setHandler(attachInputEl, DwtEvent.ONKEYDOWN, ZmCalItemEditView._onKeyDown); 323 } 324 325 //HTML5 326 if(AjxEnv.supportsHTML5File){ 327 var sizeEl = document.getElementById(sizeContId); 328 Dwt.setHandler(attachInputEl, "onchange", AjxCallback.simpleClosure(this._handleFileSize, this, attachInputEl, sizeEl)); 329 } 330 } 331 332 this.resize(); 333 }; 334 335 ZmCalItemEditView.prototype._handleFileSize = 336 function(inputEl, sizeEl){ 337 338 var files = inputEl.files; 339 if(!files) return; 340 341 var sizeStr = [], className, totalSize =0; 342 for(var i=0; i<files.length;i++){ 343 var file = files[i]; 344 var size = file.size || file.fileSize /*Safari*/ || 0; 345 if ((-1 /* means unlimited */ != appCtxt.get(ZmSetting.MESSAGE_SIZE_LIMIT)) && 346 (size > appCtxt.get(ZmSetting.MESSAGE_SIZE_LIMIT))) { 347 className = "RedC"; 348 } 349 totalSize += size; 350 } 351 352 if(sizeEl) { 353 sizeEl.innerHTML = " ("+AjxUtil.formatSize(totalSize, true)+")"; 354 if(className) 355 Dwt.addClass(sizeEl, "RedC"); 356 else 357 Dwt.delClass(sizeEl, "RedC"); 358 } 359 }; 360 361 ZmCalItemEditView.prototype.resize = 362 function() { 363 if (!this._rendered) { return; } 364 365 this._resizeNotes(); 366 367 var subjectContainer = this._subjectField.getHtmlElement().parentNode; 368 this._subjectField.setSize(0, Dwt.DEFAULT); 369 var containerBounds = Dwt.getInsetBounds(subjectContainer); 370 this._subjectField.setSize(containerBounds.width - 20, Dwt.DEFAULT); 371 }; 372 373 ZmCalItemEditView.prototype.getHtmlEditor = 374 function() { 375 return this._notesHtmlEditor; 376 }; 377 378 ZmCalItemEditView.prototype.getOrganizer = 379 function() { 380 var folderId = this._folderSelect.getValue(); 381 var organizer = new ZmContact(null); 382 var acct = appCtxt.multiAccounts && appCtxt.getById(folderId).getAccount(); 383 organizer.initFromEmail(ZmApptViewHelper.getOrganizerEmail(this._calendarOrgs[folderId], acct), true); 384 385 return organizer; 386 }; 387 388 389 // Private / protected methods 390 391 ZmCalItemEditView.prototype._addTabGroupMembers = 392 function(tabGroup) { 393 // override 394 }; 395 396 ZmCalItemEditView.prototype._reset = 397 function(calItem, mode, firstTime) { 398 this._calendarOrgs = {}; 399 ZmApptViewHelper.populateFolderSelect(this._folderSelect, this._folderRow, this._calendarOrgs, calItem); 400 401 this.enableInputs(true); 402 403 var enableTimeSelection = !this._isForward; 404 405 // lets always attempt to populate even if we're dealing w/ a "new" calItem 406 this._populateForEdit(calItem, mode); 407 408 // disable the recurrence select object for editing single instance 409 var enableRepeat = ((mode != ZmCalItem.MODE_EDIT_SINGLE_INSTANCE) && enableTimeSelection && !this._isProposeTime); 410 var repeatOptions = document.getElementById(this._htmlElId + "_repeat_options"); 411 if(repeatOptions) this._enableRepeat(enableRepeat); 412 413 //show 'to' fields for forward action 414 var forwardOptions = document.getElementById(this._htmlElId + "_forward_options"); 415 if(forwardOptions) Dwt.setVisible(forwardOptions, this._isForward || this._isProposeTime); 416 417 this._resetReminders(); 418 419 // Delay of 500ms to call the finishReset 420 // It should be called only when all the items are loaded properly including the scheduler 421 var ta = new AjxTimedAction(this, this._finishReset); 422 AjxTimedAction.scheduleAction(ta, 500); 423 }; 424 425 ZmCalItemEditView.prototype._resetReminders = function() { 426 if (!this._hasReminderSupport) return; 427 428 var reminderOptions = document.getElementById(this._htmlElId + "_reminder_options"); 429 if(reminderOptions) { 430 var enableReminder = !this._isForward && !this._isProposeTime; 431 this._reminderSelectInput.setEnabled(enableReminder); 432 this._reminderButton.setEnabled(enableReminder); 433 } 434 }; 435 436 ZmCalItemEditView.prototype._finishReset = 437 function() { 438 // save the original form data in its initialized state 439 this._origFormValue = this._formValue(false); 440 }; 441 442 ZmCalItemEditView.prototype._getClone = 443 function() { 444 // override 445 }; 446 447 ZmCalItemEditView.prototype._populateForSave = 448 function(calItem) { 449 // create a copy of the appointment so we don't muck w/ the original 450 calItem.setViewMode(this._mode); 451 452 // bug fix #5617 - check if there are any existing attachments that were unchecked 453 var attCheckboxes = document.getElementsByName(ZmCalItem.ATTACHMENT_CHECKBOX_NAME); 454 if (attCheckboxes && attCheckboxes.length > 0) { 455 for (var i = 0; i < attCheckboxes.length; i++) { 456 if (!attCheckboxes[i].checked) 457 calItem.removeAttachment(attCheckboxes[i].value); 458 } 459 } 460 461 // save field values of this view w/in given appt 462 calItem.setName(this._subjectField.getValue()); 463 464 var folderId = this._folderSelect.getValue(); 465 if (this._mode != ZmCalItem.MODE_NEW && this._calItem.folderId != folderId) { 466 // if moving existing calitem across mail boxes, cache the new folderId 467 // so we can save it as a separate request 468 var origFolder = appCtxt.getById(this._calItem.folderId); 469 var newFolder = appCtxt.getById(folderId); 470 if (origFolder.isRemote() || newFolder.isRemote()) { 471 calItem.__newFolderId = folderId; 472 folderId = this._calItem.folderId; 473 } 474 } 475 476 calItem.setFolderId(folderId); 477 calItem.setOrganizer(this._calItem.organizer || this._calendarOrgs[folderId]); 478 479 // set the notes parts (always add text part) 480 var top = new ZmMimePart(); 481 if (this._composeMode == Dwt.HTML) { 482 top.setContentType(ZmMimeTable.MULTI_ALT); 483 484 // create two more mp's for text and html content types 485 var textPart = new ZmMimePart(); 486 textPart.setContentType(ZmMimeTable.TEXT_PLAIN); 487 textPart.setContent(this._notesHtmlEditor.getTextVersion()); 488 top.children.add(textPart); 489 490 var htmlPart = new ZmMimePart(); 491 htmlPart.setContentType(ZmMimeTable.TEXT_HTML); 492 htmlPart.setContent(this._notesHtmlEditor.getContent(true, true)); 493 top.children.add(htmlPart); 494 } else { 495 top.setContentType(ZmMimeTable.TEXT_PLAIN); 496 top.setContent(this._notesHtmlEditor.getContent()); 497 } 498 499 calItem.notesTopPart = top; 500 501 //set the reminder time for alarm 502 if (this._hasReminderSupport) { 503 //calItem.setReminderMinutes(this._reminderSelect.getValue()); 504 var reminderString = this._reminderSelectInput && this._reminderSelectInput.getValue(); 505 if (!reminderString || reminderString == ZmMsg.apptRemindNever) { 506 calItem.setReminderMinutes(-1); 507 } else { 508 var reminderInfo = ZmCalendarApp.parseReminderString(reminderString); 509 var reminders = [ 510 { control: this._reminderEmailCheckbox, action: ZmCalItem.ALARM_EMAIL }, 511 { control: this._reminderDeviceEmailCheckbox, action: ZmCalItem.ALARM_DEVICE_EMAIL } 512 ]; 513 for (var i = 0; i < reminders.length; i++) { 514 var reminder = reminders[i]; 515 if (reminder.control.getEnabled() && reminder.control.isSelected()) { 516 calItem.addReminderAction(reminder.action); 517 } 518 else { 519 calItem.removeReminderAction(reminder.action); 520 } 521 } 522 calItem.setReminderUnits(reminderInfo.reminderValue, reminderInfo.reminderUnits); 523 } 524 } 525 return calItem; 526 }; 527 528 ZmCalItemEditView.prototype._populateForEdit = 529 function(calItem, mode) { 530 // set subject 531 var subject = calItem.getName(), 532 buttonText; 533 534 this._subjectField.setValue(subject); 535 if(subject) { 536 buttonText = subject.substr(0, ZmAppViewMgr.TAB_BUTTON_MAX_TEXT); 537 appCtxt.getAppViewMgr().setTabTitle(this._controller.getCurrentViewId(), buttonText); 538 } 539 if (this._hasRepeatSupport) { 540 this._repeatSelect.setSelectedValue(calItem.isCustomRecurrence() ? "CUS" : calItem.getRecurType()); 541 this._initRecurDialog(calItem.getRecurType()); 542 // recurrence string 543 this._setRepeatDesc(calItem); 544 } 545 546 if (this._hasReminderSupport) { 547 this._setEmailReminderControls(); 548 } 549 550 // attachments 551 this._attachDiv = document.getElementById(this._attachDivId); 552 if (this._attachDiv) { 553 // Bug 19993: clear out the attachments to prevent duplicates in the display. 554 this._attachDiv.innerHTML = ""; 555 } 556 var attachList = calItem.getAttachments(); 557 if (attachList) { 558 for (var i = 0; i < attachList.length; i++) 559 this.addAttachmentField(calItem, attachList[i]); 560 } 561 562 this._setContent(calItem, mode); 563 if (this._hasReminderSupport) { 564 this.adjustReminderValue(calItem); 565 var actions = calItem.alarmActions; 566 this._reminderEmailCheckbox.setSelected(actions.contains(ZmCalItem.ALARM_EMAIL)); 567 this._reminderDeviceEmailCheckbox.setSelected(actions.contains(ZmCalItem.ALARM_DEVICE_EMAIL)); 568 } 569 }; 570 571 ZmCalItemEditView.prototype.adjustReminderValue = 572 function(calItem) { 573 this._reminderSelectInput.setValue(ZmCalendarApp.getReminderSummary(calItem._reminderMinutes)); 574 }; 575 576 ZmCalItemEditView.prototype._setRepeatDesc = 577 function(calItem) { 578 if (calItem.isCustomRecurrence()) { 579 //Bug fix # 58493 - Set the classname if for the first time directly custom weekly/monthly/yearly repetition is selected 580 this._repeatDescField.className = "FakeAnchor"; 581 this._repeatDescField.innerHTML = calItem.getRecurBlurb(); 582 } else { 583 this._repeatDescField.innerHTML = (calItem.getRecurType() != "NON") 584 ? AjxStringUtil.htmlEncode(ZmMsg.customize) : ""; 585 } 586 }; 587 588 ZmCalItemEditView.prototype._setContent = 589 function(calItem, mode) { 590 591 var isSavedinHTML = false, 592 notesHtmlPart = calItem.getNotesPart(ZmMimeTable.TEXT_HTML), 593 notesPart; 594 595 if (calItem.notesTopPart) { //Already existing appointment 596 var pattern = /<([A-Z][A-Z0-9]*)\b[^>]*>(.*?)<\/\1>/ig; // improved regex to parse html tags 597 if (notesHtmlPart && notesHtmlPart.match(pattern)) { 598 isSavedinHTML = true; 599 } 600 } 601 else if (appCtxt.get(ZmSetting.HTML_COMPOSE_ENABLED) && (appCtxt.get(ZmSetting.COMPOSE_AS_FORMAT) === ZmSetting.COMPOSE_HTML)) { 602 isSavedinHTML = true; 603 } 604 605 if( !isSavedinHTML ){ 606 notesPart = calItem.getNotesPart(ZmMimeTable.TEXT_PLAIN); 607 } 608 609 this._controller.setFormatBtnItem(true, isSavedinHTML ? ZmMimeTable.TEXT_HTML : ZmMimeTable.TEXT_PLAIN); 610 this.setComposeMode(isSavedinHTML ? Dwt.HTML : Dwt.TEXT); 611 612 if(this._isForward /* && !calItem.isOrganizer() */) { 613 var preface = [ZmMsg.DASHES, " ", ZmMsg.originalAppointment, " ", ZmMsg.DASHES].join(""); 614 if(isSavedinHTML) { 615 var crlf2 = "<br><br>"; 616 var crlf = "<br>"; 617 notesHtmlPart = crlf2 + preface + crlf + calItem.getInviteDescription(true); 618 notesHtmlPart = this.formatContent(notesHtmlPart, true); 619 } else { 620 var crlf2 = AjxStringUtil.CRLF2; 621 var crlf = AjxStringUtil.CRLF; 622 notesPart = crlf2 + preface + crlf + calItem.getInviteDescription(false); 623 notesPart = this.formatContent(notesPart, false); 624 } 625 } 626 if (isSavedinHTML && notesHtmlPart) notesHtmlPart = AjxStringUtil.defangHtmlContent(notesHtmlPart); 627 628 this._notesHtmlEditor.setContent(isSavedinHTML ? notesHtmlPart : notesPart); 629 }; 630 631 ZmCalItemEditView.prototype.formatContent = 632 function(body, composingHtml) { 633 634 var includePref = appCtxt.get(ZmSetting.FORWARD_INCLUDE_ORIG); 635 if (includePref == ZmSetting.INCLUDE_PREFIX || includePref == ZmSetting.INCLUDE_PREFIX_FULL) { 636 var preface = (composingHtml ? '<br>' : '\n'); 637 var wrapParams = { 638 text: body, 639 htmlMode: composingHtml, 640 preserveReturns: true 641 } 642 body = preface + AjxStringUtil.wordWrap(wrapParams); 643 } 644 return body; 645 }; 646 647 ZmCalItemEditView.prototype.getRepeatType = 648 function() { 649 return this._repeatSelectDisabled ? "NON" : this._repeatSelect.getValue(); 650 } 651 652 /** 653 * sets any recurrence rules w/in given ZmCalItem object 654 */ 655 ZmCalItemEditView.prototype._getRecurrence = 656 function(calItem) { 657 var repeatType = this._repeatSelect.getValue(); 658 659 if (this._recurDialog && repeatType == "CUS") { 660 calItem.setRecurType(this._recurDialog.getSelectedRepeatValue()); 661 662 switch (calItem.getRecurType()) { 663 case "DAI": this._recurDialog.setCustomDailyValues(calItem); break; 664 case "WEE": this._recurDialog.setCustomWeeklyValues(calItem); break; 665 case "MON": this._recurDialog.setCustomMonthlyValues(calItem); break; 666 case "YEA": this._recurDialog.setCustomYearlyValues(calItem); break; 667 } 668 669 // set the end recur values 670 this._recurDialog.setRepeatEndValues(calItem); 671 } else { 672 calItem.setRecurType(repeatType != "CUS" ? repeatType : "NON"); 673 this._resetRecurrence(calItem); 674 } 675 }; 676 677 ZmCalItemEditView.prototype._enableRepeat = 678 function(enable) { 679 if (enable) { 680 this._repeatSelect.enable(); 681 this._repeatDescField.className = (this._repeatSelect.getValue() == "NON") ? "DisabledText" : "FakeAnchor"; 682 } else { 683 this._repeatSelect.disable(); 684 this._repeatDescField.className = "DisabledText"; 685 } 686 this._repeatSelectDisabled = !enable; 687 this._repeatSelect.setAlign(DwtLabel.ALIGN_LEFT); // XXX: hack b/c bug w/ DwtSelect 688 }; 689 690 ZmCalItemEditView.prototype._createHTML = 691 function() { 692 // override 693 }; 694 695 ZmCalItemEditView.prototype._createWidgets = 696 function(width) { 697 // subject DwtInputField 698 var params = { 699 parent: this, 700 parentElement: (this._htmlElId + "_subject"), 701 inputId: this._htmlElId + "_subject_input", 702 type: DwtInputField.STRING, 703 label: ZmMsg.subject, 704 errorIconStyle: DwtInputField.ERROR_ICON_NONE, 705 validationStyle: DwtInputField.CONTINUAL_VALIDATION 706 }; 707 this._subjectField = new DwtInputField(params); 708 Dwt.setSize(this._subjectField.getInputElement(), "100%", "2rem"); 709 710 // CalItem folder DwtSelect 711 this._folderSelect = new DwtSelect({parent:this, parentElement:(this._htmlElId + "_folderSelect")}); 712 this._folderSelect.setAttribute('aria-label', ZmMsg.folder); 713 714 this._hasRepeatSupport = Boolean(Dwt.byId(this._htmlElId + "_repeatSelect") != null); 715 716 if(this._hasRepeatSupport) { 717 // recurrence DwtSelect 718 this._repeatSelect = new DwtSelect({parent:this, parentElement:(this._htmlElId + "_repeatSelect")}); 719 this._repeatSelect.setAttribute('aria-label', ZmMsg.repeat); 720 this._repeatSelect.addChangeListener(new AjxListener(this, this._repeatChangeListener)); 721 for (var i = 0; i < ZmApptViewHelper.REPEAT_OPTIONS.length; i++) { 722 var option = ZmApptViewHelper.REPEAT_OPTIONS[i]; 723 this._repeatSelect.addOption(option.label, option.selected, option.value); 724 } 725 } 726 727 this._hasReminderSupport = Dwt.byId(this._htmlElId + "_reminderSelect") != null; 728 729 // start/end date DwtButton's 730 var dateButtonListener = new AjxListener(this, this._dateButtonListener); 731 var dateCalSelectionListener = new AjxListener(this, this._dateCalSelectionListener); 732 733 // start/end date DwtCalendar's 734 this._startDateButton = ZmCalendarApp.createMiniCalButton(this, this._htmlElId + "_startMiniCalBtn", dateButtonListener, dateCalSelectionListener, ZmMsg.startDate); 735 this._endDateButton = ZmCalendarApp.createMiniCalButton(this, this._htmlElId + "_endMiniCalBtn", dateButtonListener, dateCalSelectionListener, ZmMsg.endDate); 736 this._startDateButton.setSize("20"); 737 this._startDateButton.setAttribute('aria-label', ZmMsg.startDate); 738 this._endDateButton.setSize("20"); 739 this._endDateButton.setAttribute('aria-label', ZmMsg.endDate); 740 741 if (this._hasReminderSupport) { 742 var params = { 743 parent: this, 744 parentElement: (this._htmlElId + "_reminderSelectInput"), 745 type: DwtInputField.STRING, 746 label: ZmMsg.reminder, 747 errorIconStyle: DwtInputField.ERROR_ICON_NONE, 748 validationStyle: DwtInputField.CONTINUAL_VALIDATION, 749 className: "DwtInputField ReminderInput" 750 }; 751 this._reminderSelectInput = new DwtInputField(params); 752 var reminderInputEl = this._reminderSelectInput.getInputElement(); 753 // Fix for bug: 83100. Fix adapted from ZmReminderDialog::_createButtons 754 Dwt.setSize(reminderInputEl, "120px", "2rem"); 755 reminderInputEl.onblur = AjxCallback.simpleClosure(this._handleReminderOnBlur, this, reminderInputEl); 756 757 var reminderButtonListener = new AjxListener(this, this._reminderButtonListener); 758 var reminderSelectionListener = new AjxListener(this, this._reminderSelectionListener); 759 this._reminderButton = ZmCalendarApp.createReminderButton(this, this._htmlElId + "_reminderSelect", reminderButtonListener, reminderSelectionListener); 760 this._reminderButton.setSize("20"); 761 this._reminderButton.setAttribute('aria-label', ZmMsg.reminder); 762 this._reminderEmailCheckbox = new DwtCheckbox({parent: this}); 763 this._reminderEmailCheckbox.replaceElement(document.getElementById(this._htmlElId + "_reminderEmailCheckbox")); 764 this._reminderEmailCheckbox.setText(ZmMsg.email); 765 this._reminderDeviceEmailCheckbox = new DwtCheckbox({parent: this}); 766 this._reminderDeviceEmailCheckbox.replaceElement(document.getElementById(this._htmlElId + "_reminderDeviceEmailCheckbox")); 767 this._reminderDeviceEmailCheckbox.setText(ZmMsg.deviceEmail); 768 this._reminderConfigure = new DwtText({parent:this,className:"FakeAnchor"}); 769 this._reminderConfigure.setText(ZmMsg.remindersConfigure); 770 // NOTE: We can't query the section name based on the pref id 771 // NOTE: because that info won't be available until the first time 772 // NOTE: prefs app is launched. 773 this._reminderConfigure.getHtmlElement().onclick = AjxCallback.simpleClosure(skin.gotoPrefs, skin, "NOTIFICATIONS"); 774 this._reminderConfigure.replaceElement(document.getElementById(this._htmlElId+"_reminderConfigure")); 775 this._setEmailReminderControls(); 776 var settings = appCtxt.getSettings(); 777 var listener = new AjxListener(this, this._settingChangeListener); 778 settings.getSetting(ZmSetting.CAL_EMAIL_REMINDERS_ADDRESS).addChangeListener(listener); 779 settings.getSetting(ZmSetting.CAL_DEVICE_EMAIL_REMINDERS_ADDRESS).addChangeListener(listener); 780 } 781 782 this._notesContainer = document.getElementById(this._htmlElId + "_notes"); 783 this._topContainer = document.getElementById(this._htmlElId + "_top"); 784 785 this._notesHtmlEditor = new ZmHtmlEditor(this, null, null, this._composeMode, null, this._htmlElId + "_notes"); 786 this._notesHtmlEditor.addOnContentInitializedListener(new AjxCallback(this,this.resize)); 787 }; 788 789 ZmCalItemEditView.prototype._handleReminderOnBlur = 790 function(inputEl) { 791 var reminderString = inputEl.value; 792 793 if (!reminderString) { 794 inputEl.value = ZmMsg.apptRemindNever; 795 return; 796 } 797 798 var reminderInfo = ZmCalendarApp.parseReminderString(reminderString); 799 var reminderMinutes = ZmCalendarApp.convertReminderUnits(reminderInfo.reminderValue, reminderInfo.reminderUnits); 800 inputEl.value = ZmCalendarApp.getReminderSummary(reminderMinutes); 801 }; 802 803 ZmCalItemEditView.prototype._addEventHandlers = 804 function() { 805 // override 806 }; 807 808 // cache all input fields so we dont waste time traversing DOM each time 809 ZmCalItemEditView.prototype._cacheFields = 810 function() { 811 this._folderRow = document.getElementById(this._htmlElId + "_folderRow"); 812 this._startDateField = document.getElementById(this._htmlElId + "_startDateField"); 813 this._endDateField = document.getElementById(this._htmlElId + "_endDateField"); 814 this._repeatDescField = document.getElementById(this._repeatDescId); // dont delete! 815 }; 816 817 ZmCalItemEditView.prototype._initAttachContainer = 818 function() { 819 // create new table row which will contain parent fieldset 820 var table = document.getElementById(this._htmlElId + "_table"); 821 this._attachmentRow = document.getElementById(this._htmlElId + "_attachment_container"); 822 if (!this._attachmentRow){ 823 this._attachmentRow = table.insertRow(-1); 824 this._attachmentRow.id = this._htmlElId + "_attachment_container"; 825 } 826 var cell = this._attachmentRow.insertCell(-1); 827 cell.colSpan = 2; 828 829 this._uploadFormId = Dwt.getNextId(); 830 this._attachDivId = Dwt.getNextId(); 831 832 var subs = { 833 uploadFormId: this._uploadFormId, 834 attachDivId: this._attachDivId, 835 url: appCtxt.get(ZmSetting.CSFE_UPLOAD_URI)+"&fmt=extended" 836 }; 837 838 cell.innerHTML = AjxTemplate.expand("calendar.Appointment#AttachContainer", subs); 839 }; 840 841 // Returns true if any of the attachment fields are populated 842 ZmCalItemEditView.prototype._gotAttachments = 843 function() { 844 var id = this._htmlElId; 845 if(!this._attachCount || !this._attachDiv) { 846 return false; 847 } 848 var atts = document.getElementsByName(ZmCalItemEditView.UPLOAD_FIELD_NAME); 849 850 for (var i = 0; i < atts.length; i++) { 851 if (atts[i].id.indexOf(id) === 0 && atts[i].value.length) 852 return true; 853 } 854 855 return false; 856 }; 857 858 ZmCalItemEditView.prototype.gotNewAttachments = 859 function() { 860 return this._gotAttachments(); 861 }; 862 863 ZmCalItemEditView.prototype._removedAttachments = 864 function(){ 865 var attCheckboxes = document.getElementsByName(ZmCalItem.ATTACHMENT_CHECKBOX_NAME); 866 if (attCheckboxes && attCheckboxes.length > 0) { 867 for (var i = 0; i < attCheckboxes.length; i++) { 868 if (!attCheckboxes[i].checked) { 869 return true; 870 } 871 } 872 } 873 return false; 874 }; 875 876 ZmCalItemEditView.prototype._removeAttachment = 877 function(removeId) { 878 // get document of attachment's iframe 879 var removeSpan = document.getElementById(removeId); 880 if (removeSpan) { 881 // have my parent kill me 882 removeSpan._parentDiv.parentNode.removeChild(removeSpan._parentDiv); 883 if ((this._attachCount-1) == 0) { 884 this._removeAllAttachments(); 885 } else { 886 this._attachCount--; 887 } 888 if (this._attachCount == ZmCalItemEditView.SHOW_MAX_ATTACHMENTS) { 889 this._attachDiv.style.height = ""; 890 } 891 892 this.resize(); 893 } 894 }; 895 896 ZmCalItemEditView.prototype._removeAllAttachments = 897 function() { 898 if (this._attachCount == 0) { return; } 899 var attachRow = document.getElementById(this._htmlElId + "_attachment_container"); 900 if (attachRow) Dwt.removeChildren(attachRow); 901 902 // let's be paranoid and really cleanup 903 delete this._uploadFormId; 904 delete this._attachDivId; 905 delete this._attachRemoveId; 906 delete this._attachDiv; 907 this._attachDiv = this._attachRemoveId = this._attachDivId = this._uploadFormId = null; 908 909 if (this._attachmentRow) delete this._attachmentRow; 910 this._attachmentRow = null; 911 // reset any attachment related vars 912 this._attachCount = 0; 913 }; 914 915 ZmCalItemEditView.prototype._submitAttachments = 916 function() { 917 var callback = new AjxCallback(this, this._attsDoneCallback); 918 var um = appCtxt.getUploadManager(); 919 window._uploadManager = um; 920 um.execute(callback, document.getElementById(this._uploadFormId)); 921 }; 922 923 ZmCalItemEditView.prototype._showRecurDialog = 924 function(repeatType) { 925 if (!this._repeatSelectDisabled) { 926 this._initRecurDialog(repeatType); 927 this._recurDialog.popup(); 928 } 929 }; 930 931 ZmCalItemEditView.prototype._initRecurDialog = 932 function(repeatType) { 933 if (!this._recurDialog) { 934 this._recurDialog = new ZmApptRecurDialog(appCtxt.getShell(), this.uid); 935 this._recurDialog.addSelectionListener(DwtDialog.OK_BUTTON, new AjxListener(this, this._recurOkListener)); 936 this._recurDialog.addSelectionListener(DwtDialog.CANCEL_BUTTON, new AjxListener(this, this._recurCancelListener)); 937 } 938 var type = repeatType || this._recurDialogRepeatValue; 939 var sd = (AjxDateUtil.simpleParseDateStr(this._startDateField.value)) || (new Date()); 940 var ed = (AjxDateUtil.simpleParseDateStr(this._endDateField.value)) || (new Date()); 941 this._recurDialog.initialize(sd, ed, type, this._calItem); 942 }; 943 944 ZmCalItemEditView.prototype._showTimeFields = 945 function(show) { 946 // override if applicable 947 }; 948 949 // Returns a string representing the form content 950 ZmCalItemEditView.prototype._formValue = 951 function(excludeAttendees) { 952 // override 953 }; 954 955 ZmCalItemEditView.prototype._getComponents = 956 function() { 957 return { above: [this._topContainer], aside: [] }; 958 }; 959 960 ZmCalItemEditView.prototype._resizeNotes = 961 function() { 962 var bodyFieldId = this._notesHtmlEditor.getBodyFieldId(); 963 if (this._bodyFieldId != bodyFieldId) { 964 this._bodyFieldId = bodyFieldId; 965 this._bodyField = document.getElementById(this._bodyFieldId); 966 } 967 968 var editorBounds = this.boundsForChild(this._notesHtmlEditor); 969 970 var rowWidth = editorBounds.width; 971 var rowHeight = editorBounds.height; 972 973 var components = this._getComponents(); 974 975 AjxUtil.foreach(components.above, function(c) { 976 rowHeight -= Dwt.getOuterSize(c).y || 0; 977 }); 978 979 AjxUtil.foreach(components.aside, function(c) { 980 rowWidth -= Dwt.getOuterSize(c).x || 0; 981 }); 982 983 if (rowWidth > 0) { 984 // ensure a sensible minimum height 985 rowHeight = Math.max(rowHeight, DwtCssStyle.asPixelCount('20rem')); 986 this._notesHtmlEditor.setSize(rowWidth, rowHeight); 987 } 988 989 Dwt.setSize(this._topContainer, rowWidth, Dwt.CLEAR); 990 }; 991 992 ZmCalItemEditView.prototype._handleRepeatDescFieldHover = 993 function(ev, isHover) { 994 if (isHover) { 995 var html = this._repeatDescField.innerHTML; 996 if (html && html.length > 0) { 997 this._repeatDescField.style.cursor = (this._repeatSelectDisabled || this._repeatSelect.getValue() == "NON") 998 ? "default" : "pointer"; 999 1000 if (this._rdfTooltip == null) { 1001 this._rdfTooltip = appCtxt.getShell().getToolTip(); 1002 } 1003 1004 var content = ["<div style='width:300px'>", html, "</div>"].join(""); 1005 this._rdfTooltip.setContent(content); 1006 this._rdfTooltip.popup((ev.pageX || ev.clientX), (ev.pageY || ev.clientY)); 1007 } 1008 } else { 1009 if (this._rdfTooltip) { 1010 this._rdfTooltip.popdown(); 1011 } 1012 1013 this._repeatDescField.style.cursor = (this._repeatSelectDisabled || this._repeatSelect.getValue() == "NON") 1014 ? "default" : "pointer"; 1015 1016 } 1017 }; 1018 1019 1020 // Listeners 1021 1022 ZmCalItemEditView.prototype._dateButtonListener = 1023 function(ev) { 1024 var calDate = ev.item == this._startDateButton 1025 ? AjxDateUtil.simpleParseDateStr(this._startDateField.value) 1026 : AjxDateUtil.simpleParseDateStr(this._endDateField.value); 1027 1028 // if date was input by user and its foobar, reset to today's date 1029 if (calDate == null || isNaN(calDate)) { 1030 calDate = new Date(); 1031 } 1032 1033 // always reset the date to current field's date 1034 var menu = ev.item.getMenu(); 1035 var cal = menu.getItem(0); 1036 cal.setDate(calDate, true); 1037 ev.item.popup(); 1038 if (AjxEnv.isIE) { 1039 menu.getHtmlElement().style.width = "180px"; 1040 } 1041 }; 1042 1043 ZmCalItemEditView.prototype._reminderButtonListener = 1044 function(ev) { 1045 var menu = ev.item.getMenu(); 1046 var reminderItem = menu.getItem(0); 1047 ev.item.popup(); 1048 }; 1049 1050 ZmCalItemEditView.prototype._reminderSelectionListener = 1051 function(ev) { 1052 if(ev.item && ev.item instanceof DwtMenuItem){ 1053 this._reminderSelectInput.setValue(ev.item.getText()); 1054 this._reminderValue = ev.item.getData("value"); 1055 1056 var enabled = this._reminderValue != 0; 1057 this._reminderEmailCheckbox.setEnabled(enabled); 1058 this._reminderDeviceEmailCheckbox.setEnabled(enabled); 1059 1060 // make sure that we're really allowed to enable these controls! 1061 if (enabled) { 1062 this._setEmailReminderControls(); 1063 } 1064 return; 1065 } 1066 }; 1067 1068 ZmCalItemEditView.prototype._dateCalSelectionListener = 1069 function(ev) { 1070 var parentButton = ev.item.parent.parent; 1071 var newDate = AjxDateUtil.simpleComputeDateStr(ev.detail); 1072 1073 this._oldStartDate = AjxDateUtil.simpleParseDateStr(this._startDateField.value); 1074 this._oldEndDate = AjxDateUtil.simpleParseDateStr(this._endDateField.value); 1075 1076 // change the start/end date if they mismatch 1077 var calItem = this._calItem; 1078 if (parentButton == this._startDateButton) { 1079 var ed = AjxDateUtil.simpleParseDateStr(this._endDateField.value); 1080 if (ed && (ed.valueOf() < ev.detail.valueOf())) { 1081 this._endDateField.value = newDate; 1082 } else if (this._oldEndDate && this._endDateField.value != newDate && (calItem.type === ZmItem.APPT)) { 1083 // Only preserve duration for Appts 1084 var delta = this._oldEndDate.getTime() - this._oldStartDate.getTime(); 1085 this._endDateField.value = AjxDateUtil.simpleComputeDateStr(new Date(ev.detail.getTime() + delta)); 1086 } 1087 this._startDateField.value = newDate; 1088 } else if(parentButton == this._endDateButton) { 1089 var sd = AjxDateUtil.simpleParseDateStr(this._startDateField.value); 1090 if (sd && (sd.valueOf() > ev.detail.valueOf())) 1091 this._startDateField.value = newDate; 1092 this._endDateField.value = newDate; 1093 } 1094 1095 if(this._hasRepeatSupport) { 1096 var repeatType = this._repeatSelect.getValue(); 1097 1098 if (calItem.isCustomRecurrence() && 1099 this._mode != ZmCalItem.MODE_EDIT_SINGLE_INSTANCE) 1100 { 1101 this._checkRecurrenceValidity = true; 1102 this._initRecurDialog(repeatType); 1103 // Internal call of the custom recurrence dialog code - 1104 // Suppress the callback function 1105 this._enableCustomRecurCallback = false; 1106 this._recurOkListener(); 1107 this._enableCustomRecurCallback = true; 1108 } 1109 else 1110 { 1111 var sd = AjxDateUtil.simpleParseDateStr(this._startDateField.value); 1112 if(sd) { 1113 this._calItem._recurrence.setRecurrenceStartTime(sd.getTime()); 1114 this._setRepeatDesc(this._calItem); 1115 } 1116 } 1117 } 1118 }; 1119 1120 ZmCalItemEditView.prototype._resetRecurrence = 1121 function(calItem) { 1122 var recur = calItem._recurrence; 1123 if(!recur) { return; } 1124 var startTime = calItem.getStartTime(); 1125 recur.setRecurrenceStartTime(startTime); 1126 }; 1127 1128 ZmCalItemEditView.prototype._repeatChangeListener = 1129 function(ev) { 1130 var newSelectVal = ev._args.newValue; 1131 if (newSelectVal == "CUS") { 1132 this._oldRepeatValue = ev._args.oldValue; 1133 this._showRecurDialog(); 1134 } else { 1135 this._repeatDescField.innerHTML = newSelectVal != "NON" ? AjxStringUtil.htmlEncode(ZmMsg.customize) : ""; 1136 this._repeatDescField.className = newSelectVal != "NON" ? "FakeAnchor" : ""; 1137 } 1138 this.notifyListeners(ZmCalItemEditView._REPEAT_CHANGE, ev); 1139 }; 1140 1141 ZmCalItemEditView.prototype._recurOkListener = 1142 function(ev) { 1143 var popdown = true; 1144 this._recurDialogRepeatValue = this._recurDialog.getSelectedRepeatValue(); 1145 if (this._recurDialogRepeatValue == "NON") { 1146 this._repeatSelect.setSelectedValue(this._recurDialogRepeatValue); 1147 this._repeatDescField.innerHTML = ""; 1148 } else { 1149 if (this._recurDialog.isValid()) { 1150 this._repeatSelect.setSelectedValue("CUS"); 1151 // update the recur language 1152 var temp = this._getClone(this._calItem); 1153 this._getRecurrence(temp); 1154 var sd = (AjxDateUtil.simpleParseDateStr(this._startDateField.value)); 1155 // If date changed...chnage the values 1156 if (temp._recurrence._startDate.getDate() != sd.getDate() || 1157 temp._recurrence._startDate.getMonth() != sd.getMonth() || 1158 temp._recurrence._startDate.getFullYear() != sd.getFullYear()) 1159 { 1160 if (this._checkRecurrenceValidity) { 1161 this.validateRecurrence(temp._recurrence._startDate, temp._recurrence._startDate, sd, temp); 1162 this._checkRecurrenceValidity = false; 1163 } else { 1164 this._startDateField.value = AjxDateUtil.simpleComputeDateStr(temp._recurrence._startDate); 1165 this._endDateField.value = AjxDateUtil.simpleComputeDateStr(temp._recurrence._startDate); 1166 this.startDate = temp._recurrence._startDate; 1167 this.endDate = temp._recurrence._startDate; 1168 this._calItem._startDate = this.startDate ; 1169 this._calItem._endDate = this.startDate ; 1170 this._setRepeatDesc(temp); 1171 } 1172 1173 } else { 1174 this._setRepeatDesc(temp); 1175 } 1176 } else { 1177 // give feedback to user about errors in recur dialog 1178 popdown = false; 1179 } 1180 } 1181 1182 if (popdown) { 1183 this._recurDialog.popdown(); 1184 } 1185 if (this._customRecurDialogCallback && this._enableCustomRecurCallback) { 1186 this._customRecurDialogCallback.run(); 1187 } 1188 }; 1189 1190 ZmCalItemEditView.prototype.validateRecurrence = 1191 function(startDate, endDate, sd, temp) { 1192 this._newRecurrenceStartDate = startDate; 1193 this._newRecurrenceEndDate = endDate; 1194 1195 var ps = this._dateResetWarningDlg = appCtxt.getYesNoMsgDialog(); 1196 ps.reset(); 1197 ps.setMessage(ZmMsg.validateRecurrence, DwtMessageDialog.WARNING_STYLE); 1198 1199 ps.registerCallback(DwtDialog.YES_BUTTON, this._dateChangeCallback, this, [startDate, endDate, sd, temp]); 1200 ps.registerCallback(DwtDialog.NO_BUTTON, this._ignoreDateChangeCallback, this, [startDate, endDate, sd, temp]); 1201 ps.popup(); 1202 }; 1203 1204 ZmCalItemEditView.prototype._dateChangeCallback = 1205 function(startDate, endDate, sd, temp) { 1206 this._dateResetWarningDlg .popdown(); 1207 this._startDateField.value = AjxDateUtil.simpleComputeDateStr(temp._recurrence._startDate); 1208 this._endDateField.value = AjxDateUtil.simpleComputeDateStr(temp._recurrence._startDate); 1209 this.startDate = temp._recurrence._startDate; 1210 this.endDate = temp._recurrence._startDate; 1211 this._calItem._startDate = this.startDate ; 1212 this._calItem._endDate = this.startDate ; 1213 this._setRepeatDesc(temp); 1214 }; 1215 1216 ZmCalItemEditView.prototype._ignoreDateChangeCallback = 1217 function(startDate, endDate, sd, temp) { 1218 this._dateResetWarningDlg.popdown(); 1219 if (this._oldStartDate && this._oldEndDate) { 1220 this._startDateField.value = AjxDateUtil.simpleComputeDateStr(this._oldStartDate); 1221 this._endDateField.value = AjxDateUtil.simpleComputeDateStr(this._oldEndDate); 1222 this.startDate = this._oldStartDate; 1223 this.endDate = this._oldEndDate; 1224 this._calItem._startDate = this.startDate; 1225 this._calItem._endDate = this.endDate; 1226 if (this._calItem._recurrence) { 1227 this._calItem._recurrence._startDate.setTime(this.startDate.getTime()); 1228 } 1229 this._setRepeatDesc(this._calItem); 1230 } 1231 }; 1232 1233 ZmCalItemEditView.prototype._recurCancelListener = 1234 function(ev) { 1235 // reset the selected option to whatever it was before user canceled 1236 this._repeatSelect.setSelectedValue(this._oldRepeatValue); 1237 this._recurDialog.popdown(); 1238 }; 1239 1240 ZmCalItemEditView.prototype._controlListener = 1241 function(ev) { 1242 this.resize(); 1243 }; 1244 1245 1246 // Callbacks 1247 1248 ZmCalItemEditView.prototype._attsDoneCallback = function(status, attId) { 1249 DBG.println(AjxDebug.DBG1, "Attachments: status = " + status + ", attId = " + attId); 1250 if (status == AjxPost.SC_OK) { 1251 //Checking for Zero sized/wrong path attachments 1252 var zeroSizedAttachments = false; 1253 if (typeof attId != "string") { 1254 var attachmentIds = []; 1255 for (var i = 0; i < attId.length; i++) { 1256 var att = attId[i]; 1257 if (att.s == 0) { 1258 zeroSizedAttachments = true; 1259 continue; 1260 } 1261 attachmentIds.push(att.aid); 1262 } 1263 attId = attachmentIds.length > 0 ? attachmentIds.join(",") : null; 1264 } 1265 if (zeroSizedAttachments){ 1266 appCtxt.setStatusMsg(ZmMsg.zeroSizedAtts); 1267 } 1268 this._controller.saveCalItem(attId); 1269 1270 } else if (status == AjxPost.SC_UNAUTHORIZED) { 1271 // It looks like the re-login code was copied from mail's ZmComposeView, and it never worked here. 1272 // Just let it present the login screen. 1273 var ex = new AjxException("Authorization Error during attachment upload", ZmCsfeException.SVC_AUTH_EXPIRED); 1274 this._controller._handleException(ex); 1275 } else { 1276 // bug fix #2131 - handle errors during attachment upload. 1277 this._controller.popupUploadErrorDialog(ZmItem.APPT, status, ZmMsg.errorTryAgain); 1278 this._controller.enableToolbar(true); 1279 } 1280 }; 1281 1282 1283 ZmCalItemEditView.prototype._getDefaultFocusItem = 1284 function() { 1285 return this._subjectField; 1286 }; 1287 1288 ZmCalItemEditView.prototype._handleOnClick = 1289 function(el) { 1290 // figure out which input field was clicked 1291 if (el.id == this._repeatDescId) { 1292 this._oldRepeatValue = this._repeatSelect.getValue(); 1293 if(this._oldRepeatValue != "NON") { 1294 this._showRecurDialog(this._oldRepeatValue); 1295 } 1296 } else if (el.id.indexOf("_att_") != -1) { 1297 this._removeAttachment(el.id); 1298 } 1299 }; 1300 1301 ZmCalItemEditView.prototype.handleDateFocus = 1302 function(el) { 1303 var isStartDate = (el == this._startDateField); 1304 if(isStartDate) { 1305 this._oldStartDateValue = el.value; 1306 }else { 1307 this._oldEndDateValue = el.value; 1308 } 1309 }; 1310 1311 ZmCalItemEditView.prototype.handleDateFieldChange = 1312 function(el) { 1313 var sdField = this._startDateField; 1314 var edField = this._endDateField; 1315 var oldStartDate = this._oldStartDateValue ? AjxDateUtil.simpleParseDateStr(this._oldStartDateValue) : null; 1316 ZmApptViewHelper.handleDateChange(sdField, edField, (el == sdField), false, oldStartDate); 1317 }; 1318 1319 ZmCalItemEditView.prototype.handleStartDateChange = 1320 function(sd) { 1321 var calItem = this._calItem; 1322 var repeatType = this._repeatSelect.getValue(); 1323 if (calItem.isCustomRecurrence() && 1324 this._mode != ZmCalItem.MODE_EDIT_SINGLE_INSTANCE) 1325 { 1326 var temp = this._getClone(this._calItem); 1327 this._oldStartDate = temp._startDate; 1328 this._oldEndDate = temp._endDate; 1329 this._checkRecurrenceValidity = true; 1330 this._initRecurDialog(repeatType); 1331 // Internal call of the custom recurrence dialog code - 1332 // Suppress the callback function 1333 this._enableCustomRecurCallback = false; 1334 this._recurOkListener(); 1335 this._enableCustomRecurCallback = true; 1336 } 1337 else 1338 { 1339 calItem._recurrence.setRecurrenceStartTime(sd.getTime()); 1340 this._setRepeatDesc(calItem); 1341 } 1342 }; 1343 1344 ZmCalItemEditView.prototype._setEmailReminderControls = 1345 function() { 1346 var email = appCtxt.get(ZmSetting.CAL_EMAIL_REMINDERS_ADDRESS); 1347 var emailText = ZmCalItemEditView.__getReminderCheckboxText(ZmMsg.emailWithAddress, AjxStringUtil.htmlEncode(email)); 1348 var emailEnabled = Boolean(email); 1349 this._reminderEmailCheckbox.setEnabled(emailEnabled); 1350 this._reminderEmailCheckbox.setText(emailText); 1351 1352 var deviceEmail = appCtxt.get(ZmSetting.CAL_DEVICE_EMAIL_REMINDERS_ADDRESS); 1353 var deviceEmailText = ZmCalItemEditView.__getReminderCheckboxText(ZmMsg.deviceEmailWithAddress, AjxStringUtil.htmlEncode(deviceEmail)); 1354 var deviceEmailEnabled = appCtxt.get(ZmSetting.CAL_DEVICE_EMAIL_REMINDERS_ENABLED) && Boolean(deviceEmail); 1355 this._reminderDeviceEmailCheckbox.setEnabled(deviceEmailEnabled); 1356 this._reminderDeviceEmailCheckbox.setText(deviceEmailText); 1357 1358 var configureEnabled = !emailEnabled && !deviceEmailEnabled; 1359 this._reminderEmailCheckbox.setVisible(!configureEnabled); 1360 this._reminderDeviceEmailCheckbox.setVisible((!configureEnabled && appCtxt.get(ZmSetting.CAL_DEVICE_EMAIL_REMINDERS_ENABLED))); 1361 }; 1362 1363 ZmCalItemEditView.__getReminderCheckboxText = function(pattern, email) { 1364 if (!email) { 1365 var onclick = 'skin.gotoPrefs("NOTIFICATIONS");return false;'; 1366 email = [ 1367 "<a href='#notifications' onclick='",onclick,"'>", 1368 ZmMsg.remindersConfigureNow, 1369 "</a>" 1370 ].join(""); 1371 } 1372 return AjxMessageFormat.format(pattern,[email]); 1373 }; 1374 1375 ZmCalItemEditView.prototype._settingChangeListener = 1376 function(ev) { 1377 if (ev.type != ZmEvent.S_SETTING) { return; } 1378 var id = ev.source.id; 1379 if (id == ZmSetting.CAL_EMAIL_REMINDERS_ADDRESS || id == ZmSetting.CAL_DEVICE_EMAIL_REMINDERS_ADDRESS) { 1380 this._setEmailReminderControls(); 1381 } 1382 }; 1383 1384 ZmCalItemEditView.prototype.deactivate = 1385 function() { 1386 this._controller.inactive = true; 1387 }; 1388 1389 // Static methods 1390 1391 ZmCalItemEditView._onClick = 1392 function(ev) { 1393 ev = ev || window.event; 1394 var el = DwtUiEvent.getTarget(ev); 1395 var edv = AjxCore.objectWithId(el._editViewId); 1396 if (edv) { 1397 edv._handleOnClick(el); 1398 } 1399 }; 1400 1401 ZmCalItemEditView._onKeyDown = 1402 function(ev) { 1403 ev = ev || window.event; 1404 var el = DwtUiEvent.getTarget(ev); 1405 if (el.id.indexOf("_att_") != -1) { 1406 // ignore enter key press in IE otherwise it tries to send the attachment! 1407 var key = DwtKeyEvent.getCharCode(ev); 1408 return !DwtKeyEvent.IS_RETURN[key]; 1409 } 1410 }; 1411 1412 ZmCalItemEditView._onMouseOver = 1413 function(ev) { 1414 ev = DwtUiEvent.getEvent(ev); 1415 var el = DwtUiEvent.getTarget(ev); 1416 var edv = AjxCore.objectWithId(el._editViewId); 1417 if (el == edv._repeatDescField) { 1418 edv._handleRepeatDescFieldHover(ev, true); 1419 } 1420 }; 1421 1422 ZmCalItemEditView._onMouseOut = 1423 function(ev) { 1424 ev = DwtUiEvent.getEvent(ev); 1425 var el = DwtUiEvent.getTarget(ev); 1426 var edv = AjxCore.objectWithId(el._editViewId); 1427 if (el == edv._repeatDescField) { 1428 edv._handleRepeatDescFieldHover(ev, false); 1429 } 1430 }; 1431 1432 ZmCalItemEditView._onChange = 1433 function(ev) { 1434 var el = DwtUiEvent.getTarget(ev); 1435 var edv = AjxCore.objectWithId(el._editViewId); 1436 var sdField = edv._startDateField; 1437 edv.handleDateFieldChange(el); 1438 1439 var calItem = edv._calItem; 1440 var sd = AjxDateUtil.simpleParseDateStr(sdField.value); 1441 edv.handleStartDateChange(sd); 1442 }; 1443 1444 ZmCalItemEditView._onFocus = 1445 function(ev) { 1446 var el = DwtUiEvent.getTarget(ev); 1447 var edv = AjxCore.objectWithId(el._editViewId); 1448 edv.handleDateFocus(el); 1449 }; 1450