1 2 /* 3 * ***** BEGIN LICENSE BLOCK ***** 4 * Zimbra Collaboration Suite Web Client 5 * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 6 * 7 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at: https://www.zimbra.com/license 10 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 11 * have been added to cover use of software over a computer network and provide for limited attribution 12 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 13 * 14 * Software distributed under the License is distributed on an "AS IS" basis, 15 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 16 * See the License for the specific language governing rights and limitations under the License. 17 * The Original Code is Zimbra Open Source Web Client. 18 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 19 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 20 * 21 * All portions of the code are Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 22 * ***** END LICENSE BLOCK ***** 23 */ 24 25 /** 26 * Creates a new appointment controller to manage appointment creation/editing. 27 * @constructor 28 * @class 29 * This class manages appointment creation/editing. 30 * 31 * @author Parag Shah 32 * 33 * @param {DwtShell} container the containing shell 34 * @param {ZmApp} app the containing app 35 * @param {constant} type controller type 36 * @param {string} sessionId the session id 37 * 38 * @extends ZmBaseController 39 */ 40 ZmCalItemComposeController = function(container, app, type, sessionId) { 41 if (arguments.length == 0) { return; } 42 ZmBaseController.apply(this, arguments); 43 this._elementsToHide = ZmAppViewMgr.LEFT_NAV; 44 45 this._onAuthTokenWarningListener = this._onAuthTokenWarningListener.bind(this); 46 appCtxt.addAuthTokenWarningListener(this._onAuthTokenWarningListener); 47 }; 48 49 ZmCalItemComposeController.prototype = new ZmBaseController; 50 ZmCalItemComposeController.prototype.constructor = ZmCalItemComposeController; 51 52 ZmCalItemComposeController.prototype.isZmCalItemComposeController = true; 53 ZmCalItemComposeController.prototype.toString = function() { return "ZmCalItemComposeController"; }; 54 55 ZmCalItemComposeController.DEFAULT_TAB_TEXT = ZmMsg.appointment; 56 57 ZmCalItemComposeController.SAVE_CLOSE = "SAVE_CLOSE"; 58 ZmCalItemComposeController.SEND = "SEND"; 59 ZmCalItemComposeController.SAVE = "SAVE"; 60 ZmCalItemComposeController.APPT_MODE = "APPT"; 61 ZmCalItemComposeController.MEETING_MODE = "MEETING"; 62 63 // Public methods 64 65 ZmCalItemComposeController.prototype.show = 66 function(calItem, mode, isDirty) { 67 68 this._mode = mode; 69 if (this._toolbar.toString() != "ZmButtonToolBar") { 70 this._createToolBar(); 71 } 72 var initial = this.initComposeView(); 73 this._app.pushView(this._currentViewId); 74 this._composeView.set(calItem, mode, isDirty); 75 this._composeView.reEnableDesignMode(); 76 this._initToolbar(mode); 77 if (initial) { 78 this._setComposeTabGroup(); 79 } 80 }; 81 82 ZmCalItemComposeController.prototype._preHideCallback = 83 function(view, force) { 84 85 ZmController.prototype._preHideCallback.call(this); 86 return force ? true : this.popShield(); 87 }; 88 89 ZmCalItemComposeController.prototype._preUnloadCallback = 90 function(view) { 91 return !this._composeView.isDirty(); 92 }; 93 94 95 ZmCalItemComposeController.prototype._preShowCallback = 96 function() { 97 return true; 98 }; 99 100 ZmCalItemComposeController.prototype._postShowCallback = 101 function(view, force) { 102 var ta = new AjxTimedAction(this, this._setFocus); 103 AjxTimedAction.scheduleAction(ta, 10); 104 }; 105 106 ZmCalItemComposeController.prototype._postHideCallback = 107 function() { 108 // overload me 109 }; 110 111 ZmCalItemComposeController.prototype.popShield = 112 function() { 113 if (!this._composeView.isDirty()) { 114 this._composeView.cleanup(); 115 return true; 116 } 117 118 var ps = this._popShield = appCtxt.getYesNoCancelMsgDialog(); 119 ps.reset(); 120 ps.setMessage(ZmMsg.askToSave, DwtMessageDialog.WARNING_STYLE); 121 ps.registerCallback(DwtDialog.YES_BUTTON, this._popShieldYesCallback, this); 122 ps.registerCallback(DwtDialog.NO_BUTTON, this._popShieldNoCallback, this); 123 ps.popup(this._composeView._getDialogXY()); 124 125 return false; 126 }; 127 128 ZmCalItemComposeController.prototype._onAuthTokenWarningListener = 129 function() { 130 // The auth token will expire in less than five minutes, so we must issue 131 // issue a last, hard save. This method is typically called more than once. 132 try { 133 if (this._composeView && this._composeView.isDirty()) { 134 // bypass most of the validity checking logic 135 var calItem = this._composeView.getCalItem(); 136 return this._saveCalItemFoRealz(calItem, null, null, true); 137 } 138 } catch(ex) { 139 var msg = AjxUtil.isString(ex) ? 140 AjxMessageFormat.format(ZmMsg.errorSavingWithMessage, errorMsg) : 141 ZmMsg.errorSaving; 142 143 appCtxt.setStatusMsg(msg, ZmStatusView.LEVEL_CRITICAL); 144 } 145 }; 146 147 /** 148 * Gets the appt view. 149 * 150 * @return {ZmApptView} the appt view 151 */ 152 ZmCalItemComposeController.prototype.getItemView = function() { 153 return this._composeView; 154 }; 155 156 /** 157 * Gets the toolbar. 158 * 159 * @return {ZmButtonToolBar} the toolbar 160 */ 161 ZmCalItemComposeController.prototype.getToolbar = 162 function() { 163 return this._toolbar; 164 }; 165 166 /** 167 * Saves the calendar item. 168 * 169 * @param {String} attId the item id 170 */ 171 ZmCalItemComposeController.prototype.saveCalItem = 172 function(attId) { 173 // override 174 }; 175 176 /** 177 * Toggles the spell check button. 178 * 179 * @param {Boolean} toggled if <code>true</code>, select the spell check button 180 */ 181 ZmCalItemComposeController.prototype.toggleSpellCheckButton = 182 function(toggled) { 183 var spellCheckButton = this._toolbar.getButton(ZmOperation.SPELL_CHECK); 184 if (spellCheckButton) { 185 spellCheckButton.setSelected((toggled || false)); 186 } 187 }; 188 189 ZmCalItemComposeController.prototype.initComposeView = 190 function(initHide) { 191 if (!this._composeView) { 192 this._composeView = this._createComposeView(); 193 var callbacks = {}; 194 callbacks[ZmAppViewMgr.CB_PRE_HIDE] = new AjxCallback(this, this._preHideCallback); 195 callbacks[ZmAppViewMgr.CB_PRE_UNLOAD] = new AjxCallback(this, this._preUnloadCallback); 196 callbacks[ZmAppViewMgr.CB_POST_SHOW] = new AjxCallback(this, this._postShowCallback); 197 callbacks[ZmAppViewMgr.CB_PRE_SHOW] = new AjxCallback(this, this._preShowCallback); 198 callbacks[ZmAppViewMgr.CB_POST_HIDE] = new AjxCallback(this, this._postHideCallback); 199 if (this._toolbar.toString() != "ZmButtonToolBar") 200 this._createToolBar(); 201 var elements = this.getViewElements(null, this._composeView, this._toolbar); 202 203 this._app.createView({ viewId: this._currentViewId, 204 viewType: this._currentViewType, 205 elements: elements, 206 hide: this._elementsToHide, 207 controller: this, 208 callbacks: callbacks, 209 tabParams: this._getTabParams()}); 210 if (initHide) { 211 this._composeView.preload(); 212 } 213 return true; 214 } 215 return false; 216 }; 217 218 ZmCalItemComposeController.prototype._getTabParams = 219 function() { 220 return {id:this.tabId, image:"CloseGray", hoverImage:"Close", text:ZmCalItemComposeController.DEFAULT_TAB_TEXT, textPrecedence:76, 221 tooltip:ZmCalItemComposeController.DEFAULT_TAB_TEXT, style: DwtLabel.IMAGE_RIGHT}; 222 }; 223 224 ZmCalItemComposeController.prototype._createComposeView = 225 function() { 226 // override 227 }; 228 229 ZmCalItemComposeController.prototype._setComposeTabGroup = 230 function(setFocus) { 231 // override 232 }; 233 234 ZmCalItemComposeController.prototype._setFocus = 235 function(focusItem, noFocus) { 236 DBG.println(AjxDebug.KEYBOARD, "timed action restoring focus to " + focusItem + "; noFocus = " + noFocus); 237 this._restoreFocus(focusItem, noFocus); 238 }; 239 240 ZmCalItemComposeController.prototype.getKeyMapName = 241 function() { 242 // override 243 }; 244 245 ZmCalItemComposeController.prototype.handleKeyAction = 246 function(actionCode) { 247 DBG.println(AjxDebug.DBG2, "ZmCalItemComposeController.handleKeyAction"); 248 switch (actionCode) { 249 case ZmKeyMap.SAVE: 250 this._saveListener(); 251 break; 252 253 case ZmKeyMap.CANCEL: 254 this._cancelListener(); 255 break; 256 257 258 case ZmKeyMap.HTML_FORMAT: 259 if (appCtxt.get(ZmSetting.HTML_COMPOSE_ENABLED)) { 260 var mode = this._composeView.getComposeMode(); 261 var newMode = (mode == Dwt.TEXT) ? Dwt.HTML : Dwt.TEXT; 262 this._formatListener(null, newMode); 263 // reset the radio button for the format button menu 264 var formatBtn = this._toolbar.getButton(ZmOperation.COMPOSE_OPTIONS); 265 if (formatBtn) { 266 formatBtn.getMenu().checkItem(ZmHtmlEditor.VALUE, newMode, true); 267 } 268 } 269 break; 270 271 default: 272 return ZmController.prototype.handleKeyAction.call(this, actionCode); 273 break; 274 } 275 return true; 276 }; 277 278 ZmCalItemComposeController.prototype.mapSupported = 279 function(map) { 280 return (map == "editor"); 281 }; 282 283 ZmCalItemComposeController.prototype.getTabView = 284 function() { 285 return this._composeView; 286 }; 287 288 /** 289 * inits check mark for menu item depending on compose mode preference. 290 * 291 * @private 292 */ 293 ZmCalItemComposeController.prototype.setFormatBtnItem = 294 function(skipNotify, composeMode) { 295 var mode; 296 if (composeMode) { 297 mode = composeMode; 298 } else { 299 var bComposeEnabled = appCtxt.get(ZmSetting.HTML_COMPOSE_ENABLED); 300 var composeFormat = appCtxt.get(ZmSetting.COMPOSE_AS_FORMAT); 301 mode = (bComposeEnabled && composeFormat == ZmSetting.COMPOSE_HTML) 302 ? Dwt.HTML : Dwt.TEXT; 303 } 304 305 var formatBtn = this._toolbar.getButton(ZmOperation.COMPOSE_OPTIONS); 306 if (formatBtn) { 307 var menu = formatBtn.getMenu ? formatBtn.getMenu() : null; 308 if(menu) { 309 menu.checkItem(ZmHtmlEditor.VALUE, mode, skipNotify); 310 } 311 } 312 }; 313 314 ZmCalItemComposeController.prototype.setOptionsBtnItem = 315 function(skipNotify, composeMode) { 316 var mode; 317 if (composeMode) { 318 mode = composeMode; 319 } else { 320 var bComposeEnabled = appCtxt.get(ZmSetting.HTML_COMPOSE_ENABLED); 321 var composeFormat = appCtxt.get(ZmSetting.COMPOSE_AS_FORMAT); 322 mode = (bComposeEnabled && composeFormat == ZmSetting.COMPOSE_HTML) 323 ? Dwt.HTML : Dwt.TEXT; 324 } 325 326 var formatBtn = this._toolbar.getButton(ZmOperation.COMPOSE_OPTIONS); 327 if (formatBtn) { 328 formatBtn.getMenu().checkItem(ZmHtmlEditor.VALUE, mode, skipNotify); 329 } 330 }; 331 332 // Private / Protected methods 333 334 335 ZmCalItemComposeController.prototype._initToolbar = 336 function(mode) { 337 if (this._toolbar.toString() != "ZmButtonToolBar") { 338 this._createToolBar(); 339 } 340 341 this.enableToolbar(true); 342 343 var isNew = (mode == null || mode == ZmCalItem.MODE_NEW || mode == ZmCalItem.MODE_NEW_FROM_QUICKADD); 344 345 var cancelButton = this._toolbar.getButton(ZmOperation.CANCEL); 346 if (isNew) { 347 cancelButton.setText(ZmMsg.cancel); 348 } else { 349 cancelButton.setText(ZmMsg.close); 350 } 351 352 var saveButton = this._toolbar.getButton(ZmOperation.SAVE); 353 //use send button for forward appt view 354 if(ZmCalItem.FORWARD_MAPPING[mode]) { 355 saveButton.setText(ZmMsg.send); 356 } 357 358 var printButton = this._toolbar.getButton(ZmOperation.PRINT); 359 if (printButton) { 360 printButton.setEnabled(!isNew); 361 } 362 363 appCtxt.notifyZimlets("initializeToolbar", [this._app, this._toolbar, this, this._currentViewId], {waitUntilLoaded:true}); 364 }; 365 366 367 ZmCalItemComposeController.prototype._createToolBar = 368 function() { 369 370 var buttons = [ZmOperation.SEND_INVITE, ZmOperation.SAVE, ZmOperation.CANCEL, ZmOperation.SEP]; 371 372 if (appCtxt.get(ZmSetting.ATTACHMENT_ENABLED)) { 373 buttons.push(ZmOperation.ATTACHMENT); 374 } 375 376 if (appCtxt.get(ZmSetting.PRINT_ENABLED)) { 377 buttons.push(ZmOperation.PRINT); 378 } 379 380 if (appCtxt.isSpellCheckerAvailable()) { 381 buttons.push(ZmOperation.SPELL_CHECK); 382 } 383 buttons.push(ZmOperation.SEP, ZmOperation.COMPOSE_OPTIONS); 384 385 this._toolbar = new ZmButtonToolBar({ 386 parent: this._container, 387 buttons: buttons, 388 overrides: this._getButtonOverrides(buttons), 389 context: this._currentViewId, 390 controller: this 391 }); 392 this._toolbar.addSelectionListener(ZmOperation.SAVE, new AjxListener(this, this._saveListener)); 393 this._toolbar.addSelectionListener(ZmOperation.CANCEL, new AjxListener(this, this._cancelListener)); 394 395 if (appCtxt.get(ZmSetting.PRINT_ENABLED)) { 396 this._toolbar.addSelectionListener(ZmOperation.PRINT, new AjxListener(this, this._printListener)); 397 } 398 399 if (appCtxt.get(ZmSetting.ATTACHMENT_ENABLED)) { 400 this._toolbar.addSelectionListener(ZmOperation.ATTACHMENT, new AjxListener(this, this._attachmentListener)); 401 } 402 403 var sendButton = this._toolbar.getButton(ZmOperation.SEND_INVITE); 404 sendButton.setVisible(false); 405 406 // change default button style to toggle for spell check button 407 var spellCheckButton = this._toolbar.getButton(ZmOperation.SPELL_CHECK); 408 if (spellCheckButton) { 409 spellCheckButton.setAlign(DwtLabel.IMAGE_LEFT | DwtButton.TOGGLE_STYLE); 410 } 411 412 var optionsButton = this._toolbar.getButton(ZmOperation.COMPOSE_OPTIONS); 413 if (optionsButton) { 414 optionsButton.setVisible(false); //start it hidden, and show in case it's needed. 415 } 416 417 if (optionsButton && appCtxt.get(ZmSetting.HTML_COMPOSE_ENABLED)) { 418 optionsButton.setVisible(true); 419 420 var m = new DwtMenu({parent:optionsButton}); 421 optionsButton.setMenu(m); 422 423 var mi = new DwtMenuItem({parent:m, style:DwtMenuItem.RADIO_STYLE, id:[ZmId.WIDGET_MENU_ITEM,this._currentViewId,ZmOperation.FORMAT_HTML].join("_")}); 424 mi.setImage("HtmlDoc"); 425 mi.setText(ZmMsg.formatAsHtml); 426 mi.setData(ZmHtmlEditor.VALUE, Dwt.HTML); 427 mi.addSelectionListener(new AjxListener(this, this._formatListener)); 428 429 mi = new DwtMenuItem({parent:m, style:DwtMenuItem.RADIO_STYLE, id:[ZmId.WIDGET_MENU_ITEM,this._currentViewId,ZmOperation.FORMAT_TEXT].join("_")}); 430 mi.setImage("GenericDoc"); 431 mi.setText(ZmMsg.formatAsText); 432 mi.setData(ZmHtmlEditor.VALUE, Dwt.TEXT); 433 mi.addSelectionListener(new AjxListener(this, this._formatListener)); 434 } 435 436 this._toolbar.addSelectionListener(ZmOperation.SPELL_CHECK, new AjxListener(this, this._spellCheckListener)); 437 }; 438 439 ZmCalItemComposeController.prototype.showErrorMessage = 440 function(errorMsg) { 441 var dialog = appCtxt.getMsgDialog(); 442 dialog.reset(); 443 //var msg = ZmMsg.errorSaving + (errorMsg ? (":<p>" + errorMsg) : "."); 444 var msg = errorMsg ? AjxMessageFormat.format(ZmMsg.errorSavingWithMessage, errorMsg) : ZmMsg.errorSaving; 445 dialog.setMessage(msg, DwtMessageDialog.CRITICAL_STYLE); 446 dialog.popup(); 447 this.enableToolbar(true); 448 }; 449 450 ZmCalItemComposeController.prototype._saveCalItemFoRealz = function(calItem, attId, notifyList, force) { 451 452 var recurringChanges = this._composeView.areRecurringChangesDirty(); 453 454 if (this._composeView.isDirty() || recurringChanges || force) { 455 // bug: 16112 - check for folder existance 456 if (calItem.getFolder() && calItem.getFolder().noSuchFolder) { 457 var msg = AjxMessageFormat.format(ZmMsg.errorInvalidFolder, calItem.getFolder().name); 458 this.showErrorMessage(msg); 459 return false; 460 } 461 if(this._composeView.isReminderOnlyChanged()) { 462 calItem.setMailNotificationOption(false); 463 } 464 var callback = new AjxCallback(this, this._handleResponseSave, calItem); 465 var errorCallback = new AjxCallback(this, this._handleErrorSave, calItem); 466 this._doSaveCalItem(calItem, attId, callback, errorCallback, notifyList); 467 } else { 468 if (this._action == ZmCalItemComposeController.SAVE && !this._composeView.isDirty()) { 469 this.enableToolbar(true); 470 } 471 472 if (this.isCloseAction()){ 473 this._composeView.cleanup(); // bug: 27600 clean up edit view to avoid stagnant attendees 474 this.closeView(); 475 } 476 } 477 }; 478 479 ZmCalItemComposeController.prototype._doSaveCalItem = 480 function(calItem, attId, callback, errorCallback, notifyList){ 481 if(this._action == ZmCalItemComposeController.SEND) 482 calItem.send(attId, callback, errorCallback, notifyList); 483 else 484 calItem.save(attId, callback, errorCallback, notifyList); 485 }; 486 487 ZmCalItemComposeController.prototype.isCloseAction = 488 function() { 489 return ( this._action == ZmCalItemComposeController.SEND || this._action == ZmCalItemComposeController.SAVE_CLOSE ); 490 }; 491 492 ZmCalItemComposeController.prototype._handleResponseSave = 493 function(calItem, result) { 494 try { 495 if (calItem.__newFolderId) { 496 var folder = appCtxt.getById(calItem.__newFolderId); 497 calItem.__newFolderId = null; 498 this._app.getListController()._doMove(calItem, folder, null, false); 499 } 500 501 calItem.handlePostSaveCallbacks(); 502 if(this.isCloseAction()) { 503 this.closeView(); 504 } 505 appCtxt.notifyZimlets("onSaveApptSuccess", [this, calItem, result]);//notify Zimlets on success 506 } catch (ex) { 507 DBG.println(ex); 508 } finally { 509 this._composeView.cleanup(); 510 } 511 }; 512 513 ZmCalItemComposeController.prototype._handleErrorSave = 514 function(calItem, ex) { 515 var status = this._getErrorSaveStatus(calItem, ex); 516 return status.handled; 517 }; 518 519 ZmCalItemComposeController.prototype._getErrorSaveStatus = 520 function(calItem, ex) { 521 // TODO: generalize error message for calItem instead of just Appt 522 var status = calItem.processErrorSave(ex); 523 status.handled = false; 524 525 if (status.continueSave) { 526 this.saveCalItemContinue(calItem); 527 status.handled = true; 528 } else { 529 // Enable toolbar if not attempting to continue the Save 530 this.enableToolbar(true); 531 if (status.errorMessage) { 532 // Handled the error, display the error message 533 status.handled = true; 534 var dialog = appCtxt.getMsgDialog(); 535 dialog.setMessage(status.errorMessage, DwtMessageDialog.CRITICAL_STYLE); 536 dialog.popup(); 537 } 538 appCtxt.notifyZimlets("onSaveApptFailure", [this, calItem, ex]); 539 } 540 541 return status; 542 }; 543 544 // Spell check methods 545 546 ZmCalItemComposeController.prototype._spellCheckAgain = 547 function() { 548 this._composeView.getHtmlEditor().discardMisspelledWords(); 549 this._doSpellCheck(); 550 return false; 551 }; 552 553 ZmCalItemComposeController.prototype.enableToolbar = 554 function(enabled) { 555 this._toolbar.enableAll(enabled); 556 }; 557 558 // Listeners 559 560 // Save button was pressed 561 ZmCalItemComposeController.prototype._saveListener = 562 function(ev) { 563 this._action = ZmCalItemComposeController.SAVE; 564 this.enableToolbar(false); 565 if (this._doSave() === false) { 566 return; 567 } 568 }; 569 570 // Cancel button was pressed 571 ZmCalItemComposeController.prototype._cancelListener = 572 function(ev) { 573 this._action = ZmCalItemComposeController.SAVE_CLOSE; 574 this._app.popView(); 575 }; 576 577 ZmCalItemComposeController.prototype._printListener = 578 function() { 579 // overload me. 580 }; 581 582 // Attachment button was pressed 583 ZmCalItemComposeController.prototype._attachmentListener = 584 function(ev) { 585 this._composeView.addAttachmentField(); 586 }; 587 588 ZmCalItemComposeController.prototype._formatListener = 589 function(ev, mode) { 590 if (!mode && !(ev && ev.item.getChecked())) return; 591 592 mode = mode || ev.item.getData(ZmHtmlEditor.VALUE); 593 if (mode == this._composeView.getComposeMode()) return; 594 595 if (mode == Dwt.TEXT) { 596 // if formatting from html to text, confirm w/ user! 597 if (!this._textModeOkCancel) { 598 var dlgId = this._composeView.getHTMLElId() + "_formatWarning"; 599 this._textModeOkCancel = new DwtMessageDialog({id: dlgId, parent:this._shell, buttons:[DwtDialog.OK_BUTTON, DwtDialog.CANCEL_BUTTON]}); 600 this._textModeOkCancel.setMessage(ZmMsg.switchToText, DwtMessageDialog.WARNING_STYLE); 601 this._textModeOkCancel.registerCallback(DwtDialog.OK_BUTTON, this._textModeOkCallback, this); 602 this._textModeOkCancel.registerCallback(DwtDialog.CANCEL_BUTTON, this._textModeCancelCallback, this); 603 } 604 this._textModeOkCancel.popup(this._composeView._getDialogXY()); 605 } else { 606 this._composeView.setComposeMode(mode); 607 } 608 }; 609 610 ZmCalItemComposeController.prototype._spellCheckListener = 611 function(ev) { 612 var spellCheckButton = this._toolbar.getButton(ZmOperation.SPELL_CHECK); 613 var htmlEditor = this._composeView.getHtmlEditor(); 614 615 if (spellCheckButton.isToggled()) { 616 var callback = new AjxCallback(this, this.toggleSpellCheckButton) 617 if (!htmlEditor.spellCheck(callback)) 618 this.toggleSpellCheckButton(false); 619 } else { 620 htmlEditor.discardMisspelledWords(); 621 } 622 }; 623 624 ZmCalItemComposeController.prototype._doSave = 625 function() { 626 // check if all fields are populated w/ valid values 627 try { 628 if (this._composeView.isValid()) { 629 return this.saveCalItem(); 630 } 631 } catch(ex) { 632 if (AjxUtil.isString(ex)) { 633 this.showErrorMessage(ex); 634 } else { 635 DBG.dumpObj(AjxDebug.DBG1, ex); 636 } 637 638 return false; 639 } 640 }; 641 642 643 // Callbacks 644 645 ZmCalItemComposeController.prototype._doSpellCheck = 646 function() { 647 var text = this._composeView.getHtmlEditor().getTextVersion(); 648 var soap = AjxSoapDoc.create("CheckSpellingRequest", "urn:zimbraMail"); 649 soap.getMethod().appendChild(soap.getDoc().createTextNode(text)); 650 var cmd = new ZmCsfeCommand(); 651 var callback = new AjxCallback(this, this._spellCheckCallback); 652 cmd.invoke({soapDoc:soap, asyncMode:true, callback:callback}); 653 }; 654 655 ZmCalItemComposeController.prototype._popShieldYesCallback = 656 function() { 657 this._popShield.popdown(); 658 this._action = ZmCalItemComposeController.SAVE_CLOSE; 659 if (this._doSave()) { 660 appCtxt.getAppViewMgr().showPendingView(true); 661 } 662 }; 663 664 ZmCalItemComposeController.prototype._popShieldNoCallback = 665 function() { 666 this._popShield.popdown(); 667 this.enableToolbar(true); 668 try { 669 // bug fix #33001 - prism throws exception with this method: 670 appCtxt.getAppViewMgr().showPendingView(true); 671 } catch(ex) { 672 // so do nothing 673 } finally { 674 // but make sure cleanup is *always* called 675 this._composeView.cleanup(); 676 } 677 }; 678 679 ZmCalItemComposeController.prototype._closeView = 680 function() { 681 this._app.popView(true,this._currentViewId); 682 this._composeView.cleanup(); 683 }; 684 685 ZmCalItemComposeController.prototype._textModeOkCallback = 686 function(ev) { 687 this._textModeOkCancel.popdown(); 688 this._composeView.setComposeMode(Dwt.TEXT); 689 }; 690 691 ZmCalItemComposeController.prototype._textModeCancelCallback = 692 function(ev) { 693 this._textModeOkCancel.popdown(); 694 // reset the radio button for the format button menu 695 var formatBtn = this._toolbar.getButton(ZmOperation.COMPOSE_OPTIONS); 696 if (formatBtn) { 697 formatBtn.getMenu().checkItem(ZmHtmlEditor.VALUE, Dwt.HTML, true); 698 } 699 this._composeView.reEnableDesignMode(); 700 }; 701