1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 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) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * @overview 26 * This file contains the edit contact view classes. 27 */ 28 29 /** 30 * Creates the edit contact view. 31 * @class 32 * This class represents the edit contact view. 33 * 34 * @param {DwtComposite} parent the parent 35 * @param {ZmContactController} controller the controller 36 * 37 * @extends DwtForm 38 */ 39 ZmEditContactView = function(parent, controller) { 40 if (arguments.length == 0) return; 41 42 var form = { 43 ondirty: this._handleDirty, 44 items: this.getFormItems() 45 }; 46 47 var params = { 48 id: "editcontactform", 49 parent: parent, 50 className: "ZmEditContactView", 51 posStyle: DwtControl.ABSOLUTE_STYLE, 52 form: form 53 }; 54 DwtForm.call(this, params); 55 56 // add details menu, if needed 57 var details = this.getControl("DETAILS"); 58 if (details) { 59 var menu = this.__getDetailsMenu(); 60 if (menu) { 61 details.setMenu(menu); 62 details.addSelectionListener(new AjxListener(details, details.popup, [menu])); 63 } 64 else { 65 this.setVisible("DETAILS", false); 66 } 67 } 68 69 // save other state 70 this._controller = controller; 71 72 ZmTagsHelper.setupListeners(this); 73 74 this._changeListener = new AjxListener(this, this._contactChangeListener); 75 76 this.setScrollStyle(Dwt.SCROLL); 77 this.clean = false; 78 }; 79 80 ZmEditContactView.prototype = new DwtForm; 81 ZmEditContactView.prototype.constructor = ZmEditContactView; 82 83 /** 84 * Returns a string representation of the object. 85 * 86 * @return {String} a string representation of the object 87 */ 88 ZmEditContactView.prototype.toString = function() { 89 return "ZmEditContactView"; 90 }; 91 92 // form information that you can override 93 94 /** 95 * Gets the form items. 96 * 97 * @return {Hash} a hash of form items 98 */ 99 ZmEditContactView.prototype.getFormItems = function() { 100 if (!this._formItems) { 101 this._formItems = [ 102 // debug 103 // { id: "DEBUG", type: "DwtText", ignore:true }, 104 // header pseudo-items 105 { id: "FULLNAME", type: "DwtText", className: "contactHeader", 106 getter: this._getFullName, ignore: true }, 107 // contact attribute fields 108 { id: "IMAGE", type: "ZmEditContactViewImage" }, 109 { id: "ZIMLET_IMAGE", type: "DwtText" }, 110 { id: "PREFIX", type: "DwtInputField", width: 38, tooltip: ZmMsg.namePrefix, hint: ZmMsg.AB_FIELD_prefix, visible: "get('SHOW_PREFIX')" }, 111 { id: "FIRST", type: "DwtInputField", width: 95, tooltip: ZmMsg.firstName, hint: ZmMsg.AB_FIELD_firstName, visible: "get('SHOW_FIRST')", onblur: "this._controller.updateTabTitle()" }, 112 { id: "MIDDLE", type: "DwtInputField", width: 95, tooltip: ZmMsg.middleName, hint: ZmMsg.AB_FIELD_middleName, visible: "get('SHOW_MIDDLE')" }, 113 { id: "MAIDEN", type: "DwtInputField", width: 95, tooltip: ZmMsg.maidenName, hint: ZmMsg.AB_FIELD_maidenName, visible: "get('SHOW_MAIDEN')" }, 114 { id: "LAST", type: "DwtInputField", width: 95, tooltip: ZmMsg.lastName, hint: ZmMsg.AB_FIELD_lastName, visible: "get('SHOW_LAST')" , onblur: "this._controller.updateTabTitle()"}, 115 { id: "SUFFIX", type: "DwtInputField", width: 38, tooltip: ZmMsg.nameSuffix, hint: ZmMsg.AB_FIELD_suffix, visible: "get('SHOW_SUFFIX')" }, 116 { id: "NICKNAME", type: "DwtInputField", width: 66, hint: ZmMsg.AB_FIELD_nickname, visible: "get('SHOW_NICKNAME')" }, 117 { id: "COMPANY", type: "DwtInputField", width: 209, hint: ZmMsg.AB_FIELD_company, visible: "get('SHOW_COMPANY')", onblur: "this._controller.updateTabTitle()" }, 118 { id: "TITLE", type: "DwtInputField", width: 209, hint: ZmMsg.AB_FIELD_jobTitle, visible: "get('SHOW_TITLE')" }, 119 { id: "DEPARTMENT", type: "DwtInputField", width: 209, hint: ZmMsg.AB_FIELD_department, visible: "get('SHOW_DEPARTMENT')" }, 120 { id: "NOTES", type: "DwtInputField", hint: ZmMsg.notes, width: "47em", rows:4 }, 121 // phonetic name fields 122 { id: "PHONETIC_PREFIX", visible: "this.isVisible('PREFIX')", ignore:true }, 123 { id: "PHONETIC_FIRST", type: "DwtInputField", width: 95, hint: ZmMsg.AB_FIELD_phoneticFirstName, visible: "this.isVisible('FIRST')" }, 124 { id: "PHONETIC_MIDDLE", visible: "this.isVisible('MIDDLE')", ignore:true }, 125 { id: "PHONETIC_MAIDEN", visible: "this.isVisible('MAIDEN')", ignore:true }, 126 { id: "PHONETIC_LAST", type: "DwtInputField", width: 95, hint: ZmMsg.AB_FIELD_phoneticLastName, visible: "this.isVisible('LAST')" }, 127 { id: "PHONETIC_SUFFIX", visible: "this.isVisible('SUFFIX')", ignore:true }, 128 { id: "PHONETIC_COMPANY", type: "DwtInputField", width: 209, hint: ZmMsg.AB_FIELD_phoneticCompany, visible: "this.isVisible('COMPANY')" }, 129 // contact list fields 130 { id: "EMAIL", type: "ZmEditContactViewInputSelectRows", rowitem: { 131 type: "ZmEditContactViewInputSelect", equals:ZmEditContactViewInputSelect.equals, params: { 132 inputWidth: 352, tooltip: ZmMsg.email, hint: ZmMsg.emailAddrHint, options: this.getEmailOptions() 133 } 134 }, validator: ZmEditContactView.emailValidator }, 135 { id: "PHONE", type: "ZmEditContactViewInputSelectRows", rowitem: { 136 type: "ZmEditContactViewInputSelect", equals:ZmEditContactViewInputSelect.equals, params: { 137 inputWidth: 351, tooltip: ZmMsg.phone, hint: ZmMsg.phoneNumberHint, options: this.getPhoneOptions() 138 } 139 } }, 140 { id: "IM", type: "ZmEditContactViewInputSelectRows", rowitem: { 141 type: "ZmEditContactViewIM", equals: ZmEditContactViewIM.equals, params: { 142 inputWidth: 351, tooltip: ZmMsg.imShort, hint: ZmMsg.imScreenNameHint, options: this.getIMOptions() 143 } 144 } }, 145 { id: "ADDRESS", type: "ZmEditContactViewInputSelectRows", 146 rowtemplate: "abook.Contacts#ZmEditContactViewAddressRow", 147 rowitem: { type: "ZmEditContactViewAddress", equals: ZmEditContactViewAddress.equals, 148 params: { options: this.getAddressOptions() } 149 } 150 }, 151 { id: "URL", type: "ZmEditContactViewInputSelectRows", rowitem: { 152 type: "ZmEditContactViewInputSelect", equals:ZmEditContactViewInputSelect.equals, params: { 153 inputWidth: 351, hint: ZmMsg.url, options: this.getURLOptions() 154 } 155 } }, 156 { id: "OTHER", type: "ZmEditContactViewInputSelectRows", rowitem: { 157 type: "ZmEditContactViewOther", equals:ZmEditContactViewInputSelect.equals, params: { 158 inputWidth: 300, 159 selectInputWidth: 112, 160 hint: ZmMsg.date, 161 options: this.getOtherOptions() 162 } 163 }, validator: ZmEditContactViewOther.validator }, 164 // other controls 165 { id: "DETAILS", type: "DwtButton", 166 label: "\u00BB", // » 167 tooltip: ZmMsg.chooseFields, 168 ignore:true, 169 className: "ZmEditContactViewDetailsButton", 170 template: "abook.Contacts#ZmEditContactViewDetailsButton", 171 onblur: "this._controller.updateTabTitle()" 172 }, 173 { id: "FILE_AS", type: "DwtSelect", onchange: this._handleFileAsChange, items: this.getFileAsOptions(), tooltip: ZmMsg.fileAs }, 174 { id: "FOLDER", type: "DwtButton", image: "ContactsFolder", imageAltText: ZmMsg.location, tooltip: ZmMsg.location, 175 enabled: "this._contact && !this._contact.isReadOnly()", 176 onclick: this._handleFolderButton 177 }, 178 { id: "TAG", type: "DwtControl", 179 enabled: "this._contact && !this._contact.isShared()", 180 visible: "appCtxt.get(ZmSetting.TAGGING_ENABLED)" 181 }, 182 { id: "ACCOUNT", type: "DwtLabel", 183 visible: "appCtxt.multiAccounts" 184 }, 185 // NOTE: Return false onclick to prevent default action 186 { id: "VIEW_IMAGE", ignore: true, onclick: "open(get('IMAGE')) && false", visible: "get('IMAGE')" }, 187 { id: "REMOVE_IMAGE", ignore: true, onclick: this._handleRemoveImage, visible: "get('IMAGE')" }, 188 // pseudo-items 189 { id: "JOB", notab: true, ignore:true, visible: "get('SHOW_TITLE') && get('SHOW_DEPARTMENT')" }, 190 { id: "TITLE_DEPARTMENT_SEP", notab: true, 191 ignore:true, visible: "get('SHOW_TITLE') && get('SHOW_DEPARTMENT')" 192 } 193 ]; 194 } 195 return this._formItems; 196 }; 197 198 /** 199 * validate the array of email addresses. (0, 1 or more, each from a row in the edit view) 200 * @param {Array} emails 201 * @returns {*} 202 */ 203 ZmEditContactView.emailValidator = function(emails) { 204 for (var i = 0; i < emails.length; i++) { 205 var address = emails[i]; 206 if (address && !AjxEmailAddress.validateAddress(address)) { 207 throw ZmMsg.invalidEmailAddress; 208 } 209 } 210 return true; 211 }; 212 213 /** 214 * Gets the form item with the given id. 215 * <p> 216 * <strong>Note:</strong> 217 * This method is especially useful as a way to modify the default 218 * set of form items without redeclaring the entire form declaration. 219 * 220 * @param {String} id [string] Form item identifier. 221 * @param {Array} [formItems] the list of form items. If not 222 * specified, the form items array returned 223 * by {@link #getFormItems} is used. 224 * 225 * @return {Array} the form items or <code>null</code> for none 226 */ 227 ZmEditContactView.prototype.getFormItemById = function(id, formItems) { 228 formItems = formItems || this.getFormItems() || []; 229 for (var i = 0; i < formItems.length; i++) { 230 var item = formItems[i]; 231 if (item.id == id) return item; 232 } 233 return null; 234 }; 235 236 /** 237 * Gets the email options. 238 * 239 * @return {Object} returns <code>null</code> 240 */ 241 ZmEditContactView.prototype.getEmailOptions = function() { 242 return null; 243 }; 244 245 /** 246 * Gets the phone options. 247 * 248 * @return {Array} an array of phone options 249 */ 250 ZmEditContactView.prototype.getPhoneOptions = function() { 251 return [ 252 { value: ZmContact.F_mobilePhone, label: ZmMsg.phoneLabelMobile }, 253 { value: ZmContact.F_workPhone, label: ZmMsg.phoneLabelWork }, 254 { value: ZmContact.F_workFax, label: ZmMsg.phoneLabelWorkFax }, 255 // { value: "office", label: ZmMsg.office }, 256 { value: ZmContact.F_companyPhone, label: ZmMsg.phoneLabelCompany }, 257 { value: ZmContact.F_homePhone, label: ZmMsg.phoneLabelHome }, 258 { value: ZmContact.F_homeFax, label: ZmMsg.phoneLabelHomeFax }, 259 { value: ZmContact.F_pager, label: ZmMsg.phoneLabelPager }, 260 { value: ZmContact.F_callbackPhone, label: ZmMsg.phoneLabelCallback }, 261 { value: ZmContact.F_assistantPhone, label: ZmMsg.phoneLabelAssistant }, 262 { value: ZmContact.F_carPhone, label: ZmMsg.phoneLabelCar }, 263 { value: ZmContact.F_otherPhone, label: ZmMsg.phoneLabelOther }, 264 { value: ZmContact.F_otherFax, label: ZmMsg.phoneLabelOtherFax } 265 ]; 266 }; 267 268 /** 269 * Gets the IM options. 270 * 271 * @return {Array} an array of IM options 272 */ 273 ZmEditContactView.prototype.getIMOptions = function() { 274 return [ 275 { value: "xmpp", label: ZmMsg.imGateway_xmpp }, 276 { value: "yahoo", label: ZmMsg.imGateway_yahoo }, 277 { value: "aol", label: ZmMsg.imGateway_aol }, 278 { value: "msn", label: ZmMsg.imGateway_msn }, 279 { value: "im", label: ZmMsg.other } 280 ]; 281 }; 282 283 /** 284 * Gets the address options. 285 * 286 * @return {Array} an array of address options 287 */ 288 ZmEditContactView.prototype.getAddressOptions = function() { 289 return [ 290 { value: "home", label: ZmMsg.home }, 291 { value: "work", label: ZmMsg.work }, 292 { value: "other", label: ZmMsg.other } 293 ]; 294 }; 295 296 /** 297 * Gets the URL options. 298 * 299 * @return {Array} an array of URL options 300 */ 301 ZmEditContactView.prototype.getURLOptions = function() { 302 return [ 303 { value: ZmContact.F_homeURL, label: ZmMsg.home }, 304 { value: ZmContact.F_workURL, label: ZmMsg.work }, 305 { value: ZmContact.F_otherURL, label: ZmMsg.other } 306 ]; 307 }; 308 309 /** 310 * Gets the other options. 311 * 312 * @return {Array} an array of other options 313 */ 314 ZmEditContactView.prototype.getOtherOptions = function() { 315 return [ 316 { value: ZmContact.F_birthday, label: ZmMsg.AB_FIELD_birthday }, 317 { value: ZmContact.F_anniversary, label: ZmMsg.AB_FIELD_anniversary }, 318 { value: "custom", label: ZmMsg.AB_FIELD_custom } 319 ]; 320 }; 321 322 /** 323 * Gets the "file as" options. 324 * 325 * @return {Array} an array of "file as" options 326 */ 327 ZmEditContactView.prototype.getFileAsOptions = function() { 328 return [ 329 { id: "FA_LAST_C_FIRST", value: ZmContact.FA_LAST_C_FIRST, label: ZmMsg.AB_FILE_AS_lastFirst }, 330 { id: "FA_FIRST_LAST", value: ZmContact.FA_FIRST_LAST, label: ZmMsg.AB_FILE_AS_firstLast }, 331 { id: "FA_COMPANY", value: ZmContact.FA_COMPANY, label: ZmMsg.AB_FILE_AS_company }, 332 { id: "FA_LAST_C_FIRST_COMPANY", value: ZmContact.FA_LAST_C_FIRST_COMPANY, label: ZmMsg.AB_FILE_AS_lastFirstCompany }, 333 { id: "FA_FIRST_LAST_COMPANY", value: ZmContact.FA_FIRST_LAST_COMPANY, label: ZmMsg.AB_FILE_AS_firstLastCompany }, 334 { id: "FA_COMPANY_LAST_C_FIRST", value: ZmContact.FA_COMPANY_LAST_C_FIRST, label: ZmMsg.AB_FILE_AS_companyLastFirst }, 335 { id: "FA_COMPANY_FIRST_LAST", value: ZmContact.FA_COMPANY_FIRST_LAST, label: ZmMsg.AB_FILE_AS_companyFirstLast } 336 // TODO: [Q] ZmContact.FA_CUSTOM ??? 337 ]; 338 }; 339 340 // 341 // Constants 342 // 343 344 // Message dialog placement 345 ZmEditContactView.DIALOG_X = 50; 346 ZmEditContactView.DIALOG_Y = 100; 347 348 ZmEditContactView.SHOW_ID_PREFIXES = [ 349 "PREFIX","FIRST","MIDDLE","MAIDEN","LAST","SUFFIX","NICKNAME","TITLE","DEPARTMENT","COMPANY" 350 ]; 351 ZmEditContactView.SHOW_ID_LABELS = [ 352 ZmMsg.AB_FIELD_prefix, 353 ZmMsg.AB_FIELD_firstName, 354 ZmMsg.AB_FIELD_middleName, 355 ZmMsg.AB_FIELD_maidenName, 356 ZmMsg.AB_FIELD_lastName, 357 ZmMsg.AB_FIELD_suffix, 358 ZmMsg.AB_FIELD_nickname, 359 ZmMsg.AB_FIELD_jobTitle, 360 ZmMsg.AB_FIELD_department, 361 ZmMsg.AB_FIELD_company 362 ]; 363 364 ZmEditContactView.ALWAYS_SHOW = { 365 FIRST: true, LAST: true, TITLE: true, COMPANY: true 366 }; 367 368 ZmEditContactView.ATTRS = { 369 FILE_AS: ZmContact.F_fileAs, 370 FOLDER: ZmContact.F_folderId, 371 IMAGE: ZmContact.F_image, 372 ZIMLET_IMAGE: ZmContact.F_zimletImage, 373 PREFIX: ZmContact.F_namePrefix, 374 SUFFIX: ZmContact.F_nameSuffix, 375 MAIDEN: ZmContact.F_maidenName, 376 FIRST: ZmContact.F_firstName, 377 PHONETIC_FIRST: ZmContact.F_phoneticFirstName, 378 MIDDLE: ZmContact.F_middleName, 379 LAST: ZmContact.F_lastName, 380 PHONETIC_LAST: ZmContact.F_phoneticLastName, 381 NICKNAME: ZmContact.F_nickname, 382 TITLE: ZmContact.F_jobTitle, 383 DEPARTMENT: ZmContact.F_department, 384 COMPANY: ZmContact.F_company, 385 PHONETIC_COMPANY: ZmContact.F_phoneticCompany, 386 NOTES: ZmContact.F_notes 387 }; 388 389 ZmEditContactView.updateFieldLists = function() { 390 391 ZmEditContactView.LISTS = { 392 ADDRESS: {attrs:ZmContact.ADDRESS_FIELDS}, // NOTE: placeholder for custom handling 393 EMAIL: {attrs:ZmContact.EMAIL_FIELDS, onlyvalue:true}, 394 PHONE: {attrs:ZmContact.PHONE_FIELDS}, 395 IM: {attrs:ZmContact.IM_FIELDS, onlyvalue:true}, 396 URL: {attrs:ZmContact.URL_FIELDS}, 397 OTHER: {attrs:ZmContact.OTHER_FIELDS} 398 }; 399 400 }; // updateFieldLists 401 ZmEditContactView.updateFieldLists(); 402 403 // 404 // Data 405 // 406 407 ZmEditContactView.prototype.TEMPLATE = "abook.Contacts#ZmEditContactView"; 408 409 // 410 // Public methods 411 // 412 413 /** 414 * Sets the contact. 415 * 416 * @param {ZmContact} contact the contact 417 * @param {Boolean} isDirty <code>true</code> if the contact is dirty 418 */ 419 ZmEditContactView.prototype.set = function(contact, isDirty) { 420 if (typeof arguments[0] == "string") { 421 DwtForm.prototype.set.apply(this, arguments); 422 return; 423 } 424 425 // save contact 426 this._contact = this._item = contact; 427 428 // fill in base fields 429 for (var id in ZmEditContactView.ATTRS) { 430 var value = contact.getAttr(ZmEditContactView.ATTRS[id]); 431 if (id === "FOLDER" || id === "IMAGE") { 432 continue; 433 } 434 if (id === "FILE_AS") { 435 value = value || ZmContact.FA_LAST_C_FIRST; 436 } 437 this.setValue(id, value); 438 } 439 this.setValue("IMAGE", (contact && contact.getImageUrl(ZmContact.NO_MAX_IMAGE_WIDTH)) || "", true); 440 441 // fill in folder field 442 if (this.getControl("FOLDER")) { 443 var folderOrId = contact && contact.getAddressBook(); 444 if (!folderOrId && (appCtxt.getCurrentViewType() == ZmId.VIEW_CONTACT_SIMPLE)) { 445 var overview = appCtxt.getApp(ZmApp.CONTACTS).getOverview(); 446 folderOrId = overview && overview.getSelected(); 447 if (folderOrId && folderOrId.type != ZmOrganizer.ADDRBOOK) { 448 folderOrId = null; 449 } 450 if (folderOrId && folderOrId.id && folderOrId.id == ZmFolder.ID_DLS) { //can't create under Distribution Lists virtual folder 451 folderOrId = null; 452 } 453 } 454 455 //check introduced to avoid choosing a readonly/shared folder as default folder location 456 this._setFolder((folderOrId && !folderOrId.isReadOnly()) ? folderOrId : ZmOrganizer.ID_ADDRBOOK); 457 458 } 459 460 if (this.getControl("TAG")) 461 this._setTags(contact); 462 463 // check show detail items for fields with values 464 for (var id in ZmEditContactView.ATTRS) { 465 var showId = "SHOW_"+id; 466 var control = this.getControl(showId); 467 if (control == null) continue; 468 var checked = id in ZmEditContactView.ALWAYS_SHOW || (this.getValue(id) || "") != ""; 469 this.setValue(showId, checked); 470 control.setChecked(checked, true); // skip notify 471 } 472 473 // populate lists 474 this._listAttrs = {}; 475 var nattrs = contact.getNormalizedAttrs(); 476 for (var id in ZmEditContactView.LISTS) { 477 switch (id) { 478 case "ADDRESS": { 479 this.__initRowsAddress(nattrs, id, this._listAttrs); 480 break; 481 } 482 case "OTHER": { 483 var list = ZmEditContactView.LISTS[id]; 484 this.__initRowsOther(nattrs, id, list.attrs, list.onlyvalue, this._listAttrs); 485 break; 486 } 487 default: { 488 var list = ZmEditContactView.LISTS[id]; 489 this.__initRowsControl(nattrs, id, list.attrs, list.onlyvalue, this._listAttrs); 490 } 491 } 492 } 493 494 // mark form as clean and update display 495 if (!isDirty) { 496 this.reset(true); 497 } 498 this._handleDirty(); 499 this.update(); 500 501 // listen to changes in the contact 502 if (contact) { 503 contact.removeChangeListener(this._changeListener); 504 } 505 contact.addChangeListener(this._changeListener); 506 507 // notify zimlets that a new contact is being shown. 508 appCtxt.notifyZimlets("onContactEdit", [this, this._contact, this._htmlElId]); 509 }; 510 511 /** 512 * Gets the contact. 513 * 514 * @return {ZmContact} the contact 515 */ 516 ZmEditContactView.prototype.getContact = function() { 517 return this._contact; 518 }; 519 520 /** 521 * Gets the modified attributes. 522 * 523 * @return {Hash} a hash of attributes 524 */ 525 ZmEditContactView.prototype.getModifiedAttrs = function() { 526 var itemIds = this.getDirtyItems(); 527 var counts = {}; 528 var attributes = {}; 529 530 // get list of modified attributes 531 for (var i = 0; i < itemIds.length; i++) { 532 var id = itemIds[i]; 533 if (id == "ACCOUNT") { continue; } 534 var value = this.getValue(id); 535 if (id in ZmEditContactView.LISTS) { 536 var items = value; 537 var addressTypeCounts = []; 538 for (var j = 0; j < items.length; j++) { 539 var item = items[j]; 540 if (id == "ADDRESS") { 541 var type = item.type; 542 addressTypeCounts[type] = addressTypeCounts[type] || 1; 543 var itemAttributes = {}; 544 var foundNonEmptyAttr = false; 545 for (var prop in item) { 546 if (prop === "type") { 547 continue; 548 } 549 var value = item[prop]; 550 var att = ZmContact.getAttributeName(type + prop, addressTypeCounts[type]); 551 itemAttributes[att] = value; 552 foundNonEmptyAttr = foundNonEmptyAttr || value; 553 } 554 if (foundNonEmptyAttr) { 555 addressTypeCounts[type]++; 556 for (var itemAtt in itemAttributes) { 557 attributes[itemAtt] = itemAttributes[itemAtt]; 558 } 559 } 560 } 561 else { 562 var onlyvalue = ZmEditContactView.LISTS[id] && ZmEditContactView.LISTS[id].onlyvalue; 563 var v = onlyvalue ? item : item.value; 564 if (!v) continue; 565 var list = ZmEditContactView.LISTS[id]; 566 var a = onlyvalue ? list.attrs[0] : item.type; 567 if (id === "OTHER" && AjxUtil.arrayContains(AjxUtil.values(ZmEditContactView.ATTRS), a)) { 568 //ignore attributes named the same as one of our basic attributes. 569 continue; 570 } 571 if (!counts[a]) counts[a] = 0; 572 var count = ++counts[a]; 573 a = ZmContact.getAttributeName(a, count); 574 attributes[a] = v; 575 } 576 } 577 } 578 else { 579 var a = ZmEditContactView.ATTRS[id]; 580 attributes[a] = value; 581 } 582 } 583 584 // compare against existing fields 585 var anames = AjxUtil.keys(attributes); 586 var listAttrs = this._listAttrs; 587 for (var id in listAttrs) { 588 if (!this.isDirty(id)) continue; 589 var prefixes = AjxUtil.uniq(AjxUtil.map(listAttrs[id], ZmContact.getPrefix)); 590 for (var i = 0; i < prefixes.length; i++) { 591 // clear fields from original contact from normalized attr names 592 var attrs = AjxUtil.keys(this._contact.getAttrs(prefixes[i])); 593 var complement = AjxUtil.complement(anames, attrs); 594 for (var j = 0; j < complement.length; j++) { 595 attributes[complement[j]] = ""; 596 } 597 } 598 } 599 600 // was anything modified? 601 if (AjxUtil.keys(attributes).length == 0) { 602 return null; 603 } 604 605 // make sure we set the folder (when new) 606 if (!attributes[ZmContact.F_folderId] && !this._contact.id) { 607 attributes[ZmContact.F_folderId] = this.getValue("FOLDER"); 608 } 609 610 if (attributes[ZmContact.F_image] && attributes[ZmContact.F_image] === this._contact.getAttr(ZmContact.F_zimletImage)) { 611 // Slightly hacky - 2 cases could lead here: 612 // 1. user had an uploaded image, and deleted it. The field (which we use to show both cases) reverts to show the Zimlet (external) image. 613 // 2. user didn't have an uploaded image. Field was showing the the Zimlet image. 614 // In both cases we need to clear the Zimlet URL from F_image. For case 2 we don't really need to set the field at all since it doesn't change, but 615 // this way it's simpler. 616 attributes[ZmContact.F_image] = ""; 617 } 618 // set the value for IMAGE to just the attachment id 619 if (attributes[ZmContact.F_image]) { 620 var value = this.getValue("IMAGE"); 621 var m = /aid=(.*)/.exec(value); 622 if (m) { 623 // NOTE: ZmContact.modify expects the "aid_" prefix. 624 attributes[ZmContact.F_image] = "aid_"+m[1]; 625 } 626 } 627 628 return attributes; 629 }; 630 631 /** 632 * Checks if the view is empty. 633 * 634 * @return {Boolean} <code>true</code> if the view is empty 635 */ 636 ZmEditContactView.prototype.isEmpty = function(items) { 637 items = items || this._items; 638 for (var id in items) { 639 var item = items[id]; 640 if (this.isIgnore(id) || id == "FILE_AS") continue; 641 var value = this.getValue(id); 642 if (value) { 643 if (!AjxUtil.isArray(value)) { 644 if (id == "FOLDER") { 645 if (value != item.ovalue) return false; 646 } else { 647 if (value !== "") return false; 648 } 649 } else { 650 for (var i=0; i<value.length; i++) { 651 var valueitem = value[i]; 652 if (valueitem) { 653 if (id=="ADDRESS") { 654 if (!ZmEditContactViewAddress.equals(valueitem, {type: valueitem.type})) return false; 655 } else { 656 if (!(valueitem.value==="" || valueitem==="")) return false; 657 } 658 } 659 } 660 } 661 } 662 } 663 return true; 664 }; 665 666 /** 667 * @private 668 */ 669 ZmEditContactView.prototype.enableInputs = function(bEnable) { 670 // ignore 671 }; 672 673 /** 674 * Cleanup the view. 675 * 676 */ 677 ZmEditContactView.prototype.cleanup = function() { 678 this._contact = this._item = null; 679 this.clean = true; 680 }; 681 682 ZmEditContactView.prototype.dispose = 683 function() { 684 ZmTagsHelper.disposeListeners(this); 685 DwtComposite.prototype.dispose.apply(this, arguments); 686 }; 687 688 ZmEditContactView.prototype._setTags = 689 function(contact) { 690 //use the helper to get the tags. 691 var tagsHtml = ZmTagsHelper.getTagsHtml(contact || this._item, this); 692 693 var tagControl = this.getControl("TAG"); 694 if (!tagControl) { 695 return; 696 } 697 tagControl.clearContent(); 698 if (tagsHtml.length > 0) { 699 tagControl.setContent(tagsHtml); 700 tagControl.setVisible(true); 701 } 702 else { 703 tagControl.setVisible(false); 704 } 705 }; 706 707 ZmEditContactView._onBlur = 708 function() { 709 this._controller._updateTabTitle(); 710 }; 711 712 // 713 // ZmListController methods 714 // 715 716 /** 717 * Gets the list. 718 * 719 * @return {ZmContactList} the list 720 */ 721 ZmEditContactView.prototype.getList = function() { return null; }; 722 723 /** 724 * Gets the controller. 725 * 726 * @return {ZmContactController} the controller 727 */ 728 ZmEditContactView.prototype.getController = function() { 729 return this._controller; 730 }; 731 732 // Following two overrides are a hack to allow this view to pretend it's a list view 733 ZmEditContactView.prototype.getSelection = function() { 734 return this.getContact(); 735 }; 736 737 ZmEditContactView.prototype.getSelectionCount = function() { 738 return 1; 739 }; 740 741 /** 742 * Gets the title. 743 * 744 * @return {String} the title 745 */ 746 ZmEditContactView.prototype.getTitle = function() { 747 return [ZmMsg.zimbraTitle, ZmMsg.contact].join(": "); 748 }; 749 750 // 751 // ZmListView methods 752 // 753 754 ZmEditContactView.prototype._checkItemCount = function() {}; 755 ZmEditContactView.prototype._handleResponseCheckReplenish = function() {}; 756 757 // 758 // Protected methods 759 // 760 761 /** 762 * @private 763 */ 764 ZmEditContactView.prototype._getFullName = function(defaultToNull) { 765 var contact = { 766 fileAs: this.getValue("FILE_AS"), 767 firstName: this.getValue("FIRST"), lastName: this.getValue("LAST"), 768 company: this.getValue("COMPANY") 769 }; 770 return ZmContact.computeFileAs(contact) || (defaultToNull ? null : ZmMsg.noName); 771 }; 772 773 /** 774 * @private 775 */ 776 ZmEditContactView.prototype._getDefaultFocusItem = function() { 777 return this.getControl(appCtxt.get(ZmSetting.PHONETIC_CONTACT_FIELDS) ? "LAST" : "FIRST"); 778 }; 779 780 /** 781 * @private 782 */ 783 ZmEditContactView.prototype._setFolder = function(organizerOrId) { 784 var organizer = organizerOrId instanceof ZmOrganizer ? organizerOrId : appCtxt.getById(organizerOrId); 785 this.setLabel("FOLDER", organizer.getName()); 786 this.setValue("FOLDER", organizer.id); 787 this.setImage("FOLDER", organizer.getIconWithColor(), ZmMsg.locationLabel); 788 if (appCtxt.multiAccounts) { 789 this.setValue("ACCOUNT", organizer.getAccount().getDisplayName()); 790 } 791 }; 792 793 /** 794 * @private 795 */ 796 ZmEditContactView.prototype._getDialogXY = 797 function() { 798 var loc = Dwt.toWindow(this.getHtmlElement(), 0, 0, null, true); 799 return new DwtPoint(loc.x + ZmEditContactView.DIALOG_X, loc.y + ZmEditContactView.DIALOG_Y); 800 }; 801 802 // listeners 803 804 /** 805 * @private 806 */ 807 ZmEditContactView.prototype._handleDirty = function() { 808 var items = this.getDirtyItems(); 809 // toggle save 810 var toolbar = this._controller && this._controller.getCurrentToolbar(); 811 if (toolbar) { 812 var dirty = items.length > 0 ? items.length > 1 || items[0] != "IMAGE" || this._contact.id : false; 813 814 // Creating a new contact with only the folder set should not be saveable until at least one other field has a value 815 var needitems = AjxUtil.hashCopy(this._items); 816 delete needitems["FOLDER"]; 817 var empty = this.isEmpty(needitems); // false if one or more fields are set, excluding the folder field 818 819 toolbar.enable(ZmOperation.SAVE, dirty && !empty); 820 } 821 // debug information 822 this.setValue('DEBUG', items.join(', ')); 823 }; 824 825 /** 826 * @private 827 */ 828 ZmEditContactView.prototype._handleDetailCheck = function(itemId, id) { 829 this.setValue(itemId, !this.getValue(itemId)); 830 this.update(); 831 var control = this.getControl(id); 832 if (control) { 833 control.disableFocusHdlr(); //disable focus handler so hint is displayed 834 control.focus(); 835 control.enableFocusHdlr(); //re-enable 836 //Bug fix # 80423 - attach a onKeyDown handler for Firefox. 837 if (AjxEnv.isFirefox) { 838 control.enableKeyDownHdlr(); 839 } 840 } 841 }; 842 843 ZmEditContactView.prototype._handleRemoveImage = function() { 844 var image = this.getValue("IMAGE", ""); //could be user uploaded, or zimlet (e.g. LinkedInImage Zimlet) one since we use both in same field. 845 var zimletImage = this.getValue("ZIMLET_IMAGE", ""); 846 if (image !== zimletImage) { 847 //user uploaded image - this is the one we remove. Show the zimlet one instead. 848 this.set("IMAGE", zimletImage); 849 return; 850 } 851 //otherwise it's the Zimlet image we remove 852 this.set("ZIMLET_IMAGE", ""); 853 this.set("IMAGE", ""); //show the image field empty. Again we use the same field for regular user uploaded and for zimlet provided. 854 }; 855 856 /** 857 * @private 858 */ 859 ZmEditContactView.prototype._handleFileAsChange = function() { 860 var fa = this.getValue("FILE_AS"); 861 var showCompany = 862 ZmEditContactView.ALWAYS_SHOW["COMPANY"] || 863 fa == ZmContact.FA_COMPANY || 864 fa == ZmContact.FA_LAST_C_FIRST_COMPANY || 865 fa == ZmContact.FA_FIRST_LAST_COMPANY || 866 fa == ZmContact.FA_COMPANY_LAST_C_FIRST || 867 fa == ZmContact.FA_COMPANY_FIRST_LAST 868 ; 869 var company = this.getValue("COMPANY"); 870 if (showCompany) { 871 this.setValue("SHOW_COMPANY", true); 872 this.setVisible("COMPANY", true); 873 } 874 else if (!company) { 875 this.setValue("SHOW_COMPANY", false); 876 this.setVisible("COMPANY", false); 877 } 878 }; 879 880 /** 881 * @private 882 */ 883 ZmEditContactView.prototype._handleFolderButton = function(ev) { 884 var dialog = appCtxt.getChooseFolderDialog(); 885 dialog.registerCallback(DwtDialog.OK_BUTTON, new AjxCallback(this, this._handleChooseFolder)); 886 var params = { 887 overviewId: dialog.getOverviewId(ZmApp.CONTACTS), 888 title: ZmMsg.chooseAddrBook, 889 treeIds: [ZmOrganizer.ADDRBOOK], 890 skipReadOnly: true, 891 skipRemote: false, 892 noRootSelect: true, 893 appName: ZmApp.CONTACTS 894 }; 895 params.omit = {}; 896 params.omit[ZmFolder.ID_TRASH] = true; 897 dialog.popup(params); 898 }; 899 900 /** 901 * @private 902 */ 903 ZmEditContactView.prototype._handleChooseFolder = function(organizer) { 904 var dialog = appCtxt.getChooseFolderDialog(); 905 dialog.popdown(); 906 this._setFolder(organizer); 907 }; 908 909 /** 910 * @private 911 */ 912 ZmEditContactView.prototype._contactChangeListener = function(ev) { 913 if (ev.type != ZmEvent.S_CONTACT) return; 914 if (ev.event == ZmEvent.E_TAGS || ev.event == ZmEvent.E_REMOVE_ALL) { 915 this._setTags(); 916 } 917 }; 918 919 // 920 // Private methods 921 // 922 923 /** 924 * @private 925 */ 926 ZmEditContactView.prototype.__getDetailsMenu = function() { 927 var menu = new DwtMenu({parent: this.getControl("DETAILS"), style: DwtMenu.POPUP_STYLE, id: "ContactDetailsMenu"}); 928 var ids = ZmEditContactView.SHOW_ID_PREFIXES; 929 var labels = ZmEditContactView.SHOW_ID_LABELS; 930 var count = 0; 931 for (var i = 0; i < ids.length; i++) { 932 var id = ids[i]; 933 if (this.getControl(id)) { 934 var menuitem = new DwtMenuItem({parent: menu, style: DwtMenuItem.CHECK_STYLE, id: "ContactDetailsMenu_" + id}); 935 menuitem.setText(labels[i]); 936 // NOTE: Always show first and last but don't allow to change 937 if (id in ZmEditContactView.ALWAYS_SHOW) { 938 menuitem.setChecked(true, true); 939 menuitem.setEnabled(false); 940 } 941 var itemId = "SHOW_"+id; 942 var listener = new AjxListener(this, this._handleDetailCheck, [itemId, id]); 943 menuitem.addSelectionListener(listener); 944 this._registerControl({ id: itemId, control: menuitem, ignore: true }); 945 count++; 946 } 947 } 948 return count > 2 ? menu : null; 949 }; 950 951 /** 952 * @private 953 */ 954 ZmEditContactView.prototype.__initRowsControl = 955 function(nattrs,id,prefixes,onlyvalue,listAttrs,skipSetValue) { 956 var array = []; 957 for (var j = 0; j < prefixes.length; j++) { 958 var prefix = prefixes[j]; 959 for (var i = 1; true; i++) { 960 var a = ZmContact.getAttributeName(prefix, i); 961 if (a != prefix && AjxUtil.indexOf(prefixes, a) != -1) break; 962 var value = nattrs[a]; 963 if (!value) break; 964 array.push(onlyvalue ? value : { type:prefix,value:value }); 965 if (!listAttrs[id]) listAttrs[id] = []; 966 listAttrs[id].push(a); 967 } 968 } 969 if (!skipSetValue) { 970 this.setValue(id, array); 971 } 972 return array; 973 }; 974 975 /** 976 * @private 977 */ 978 ZmEditContactView.prototype.__initRowsOther = 979 function(nattrs,id,prefixes,onlyvalue,listAttrs) { 980 var array = this.__initRowsControl.call(this,nattrs,id,prefixes,onlyvalue,listAttrs,true); 981 982 // gather attributes we know about 983 var attributes = {}; 984 for (var attrId in ZmEditContactView.ATTRS) { 985 attributes[ZmEditContactView.ATTRS[attrId]] = true; 986 } 987 for (var listId in ZmEditContactView.LISTS) { 988 var list = ZmEditContactView.LISTS[listId]; 989 if (!list.attrs) continue; 990 for (var i = 0; i < list.attrs.length; i++) { 991 attributes[list.attrs[i]] = true; 992 } 993 } 994 for (var i = 0; i < ZmContact.ADDR_PREFIXES.length; i++) { 995 var prefix = ZmContact.ADDR_PREFIXES[i]; 996 for (var j = 0; j < ZmContact.ADDR_SUFFIXES.length; j++) { 997 var suffix = ZmContact.ADDR_SUFFIXES[j]; 998 attributes[prefix+suffix] = true; 999 } 1000 } 1001 1002 // add attributes on contact that we don't know about 1003 for (var aname in nattrs) { 1004 var anameNormalized = ZmContact.getPrefix(aname); 1005 if (ZmContact.IS_IGNORE[anameNormalized]) continue; 1006 if (!(anameNormalized in attributes)) { 1007 array.push({type:anameNormalized,value:nattrs[aname]}); 1008 if (!listAttrs[id]) listAttrs[id] = []; 1009 listAttrs[id].push(aname); 1010 } 1011 } 1012 1013 this.setValue(id, array); 1014 }; 1015 1016 /** 1017 * @private 1018 */ 1019 ZmEditContactView.prototype.__initRowsAddress = function(nattrs,id,listAttrs) { 1020 var array = []; 1021 var prefixes = ZmContact.ADDR_PREFIXES; 1022 var suffixes = ZmContact.ADDR_SUFFIXES; 1023 for (var k = 0; k < prefixes.length; k++) { 1024 var prefix = prefixes[k]; 1025 for (var j = 1; true; j++) { 1026 var address = null; 1027 for (var i = 0; i < suffixes.length; i++) { 1028 var suffix = suffixes[i]; 1029 var a = ZmContact.getAttributeName(prefix+suffix, j); 1030 var value = nattrs[a]; 1031 if (!value) continue; 1032 if (!address) address = {}; 1033 address[suffix] = value; 1034 if (!listAttrs[id]) listAttrs[id] = []; 1035 listAttrs[id].push(a); 1036 } 1037 if (!address) break; 1038 address.type = prefix; 1039 array.push(address); 1040 } 1041 } 1042 this.setValue("ADDRESS", array); 1043 }; 1044 1045 // functions 1046 1047 1048 // 1049 // Class: ZmEditContactViewImage 1050 // 1051 /** 1052 * Creates the contact view image. 1053 * @class 1054 * This class represents a contact view image. 1055 * 1056 * @param {Hash} params a hash of parameters 1057 * 1058 * @extends DwtControl 1059 * 1060 * @private 1061 */ 1062 ZmEditContactViewImage = function(params) { 1063 if (arguments.length == 0) return; 1064 params.className = params.className || "ZmEditContactViewImage"; 1065 params.posStyle = Dwt.RELATIVE_STYLE; 1066 params.id = params.parent.getHTMLElId()+"_IMAGE"; 1067 DwtControl.apply(this, arguments); 1068 1069 var el = this.getHtmlElement(); 1070 el.innerHTML = [ 1071 "<div style='width:48;height:48'>", 1072 "<img id='",this._htmlElId,"_img' width='48' height='48'>", 1073 "</div>", 1074 "<div id='",this._htmlElId,"_badge' style='position:absolute;" 1075 ,"bottom:",(AjxEnv.isMozilla ? -4 : 0), ";right:", (AjxEnv.isMozilla ? 3 : 0),"px'>" 1076 ].join(""); 1077 el.style.cursor = "pointer"; 1078 1079 this._src = ""; 1080 this._imgEl = document.getElementById(this._htmlElId+"_img"); 1081 this._imgEl.onload = AjxCallback.simpleClosure(this._imageLoaded, this); 1082 this._badgeEl = document.getElementById(this._htmlElId+"_badge"); 1083 1084 this._setMouseEvents(); 1085 1086 this.addListener(DwtEvent.ONMOUSEOVER, new AjxListener(Dwt.addClass, [el,DwtControl.HOVER])); 1087 this.addListener(DwtEvent.ONMOUSEOUT, new AjxListener(Dwt.delClass, [el,DwtControl.HOVER])); 1088 this.addListener(DwtEvent.ONMOUSEUP, new AjxListener(this, this._chooseImage)); 1089 1090 this.setToolTipContent(ZmMsg.addImg); 1091 }; 1092 ZmEditContactViewImage.prototype = new DwtControl; 1093 ZmEditContactViewImage.prototype.constructor = ZmEditContactViewImage; 1094 ZmEditContactViewImage.prototype.isFocusable = true; 1095 1096 /** 1097 * Returns a string representation of the object. 1098 * 1099 * @return {String} a string representation of the object 1100 * @private 1101 */ 1102 ZmEditContactViewImage.prototype.toString = function() { 1103 return "ZmEditContactViewImage"; 1104 }; 1105 1106 // Constants 1107 1108 ZmEditContactViewImage.IMAGE_URL = "/service/content/proxy?aid=@aid@"; 1109 1110 // Public methods 1111 1112 /** 1113 * Sets the image value. 1114 * 1115 * @param {String} value the image src value 1116 * @private 1117 */ 1118 ZmEditContactViewImage.prototype.setValue = function(value, promptOnError) { 1119 // Save current image source to display in case user picks an improper file. 1120 this._currentSrc = this._src; 1121 // Save original image source to know if "Do you want to save..." prompt is needed 1122 if (typeof this._originalSrc === "undefined") { 1123 this._originalSrc = this._src; 1124 } 1125 this._src = value; 1126 if (!value) { 1127 this._imgEl.src = ZmZimbraMail.DEFAULT_CONTACT_ICON; 1128 this._badgeEl.className = "ImgAdd"; 1129 this.setToolTipContent(ZmMsg.addImg); 1130 this._imgEl.alt = ZmMsg.addImg; 1131 } 1132 else { 1133 this._imgEl.src = value; 1134 this._badgeEl.className = "ImgEditBadge"; 1135 this.setToolTipContent(ZmMsg.editImg); 1136 this._imgEl.alt = ZmMsg.editImg; 1137 } 1138 this.parent.setDirty("IMAGE", true); 1139 this._imgEl.onerror = this._handleCorruptImageError.bind(this, promptOnError); 1140 }; 1141 1142 /** 1143 * Gets the value. 1144 * 1145 * @return {String} the image src value 1146 * @private 1147 */ 1148 ZmEditContactViewImage.prototype.getValue = function() { 1149 return this._src; 1150 }; 1151 1152 // Protected methods 1153 1154 ZmEditContactViewImage.prototype._focus = function() { 1155 Dwt.addClass(this.getHtmlElement(), DwtControl.FOCUSED); 1156 }; 1157 ZmEditContactViewImage.prototype._blur = function() { 1158 Dwt.delClass(this.getHtmlElement(), DwtControl.FOCUSED); 1159 }; 1160 1161 /** 1162 * @private 1163 */ 1164 ZmEditContactViewImage.prototype._imageLoaded = function() { 1165 this._imgEl.removeAttribute("width"); 1166 this._imgEl.removeAttribute("height"); 1167 var w = this._imgEl.width; 1168 var h = this._imgEl.height; 1169 this._imgEl.setAttribute(w>h ? 'width' : 'height', 48); 1170 }; 1171 1172 /** 1173 * @private 1174 */ 1175 ZmEditContactViewImage.prototype._chooseImage = function() { 1176 var dialog = appCtxt.getUploadDialog(); 1177 dialog.setAllowedExtensions(["png","jpg","jpeg","gif"]); 1178 var folder = null; 1179 var callback = new AjxCallback(this, this._handleImageSaved); 1180 var title = ZmMsg.uploadImage; 1181 var location = null; 1182 var oneFileOnly = true; 1183 var noResolveAction = true; 1184 var showNotes = false; 1185 var isImage = true; 1186 dialog.popup(null, folder, callback, title, location, oneFileOnly, noResolveAction, showNotes ,isImage); 1187 }; 1188 1189 /** 1190 * @private 1191 */ 1192 ZmEditContactViewImage.prototype._handleImageSaved = function(folder, filenames, files) { 1193 var dialog = appCtxt.getUploadDialog(); 1194 dialog.popdown(); 1195 this.setValue(ZmEditContactViewImage.IMAGE_URL.replace(/@aid@/, files[0].guid), true); 1196 this.parent.update(); 1197 }; 1198 1199 /** 1200 * @private 1201 */ 1202 ZmEditContactViewImage.prototype._createElement = function() { 1203 return document.createElement("FIELDSET"); 1204 }; 1205 1206 /** 1207 * @private 1208 */ 1209 ZmEditContactViewImage.prototype._handleCorruptImageError = function(promptOnError) { 1210 // display current image if exists, otherwise will set default contact image 1211 this.setValue(this._currentSrc); 1212 if (this._originalSrc == this._currentSrc) { 1213 // == not === to acount for null and "" which should satisfy the condition 1214 this.parent.setDirty("IMAGE", false); 1215 } 1216 if (promptOnError) { 1217 // Don't display this dialog in cases where image is missing not due to user input. 1218 // E.g. LinkedIn changed their url schema which led to broken links. In such cases 1219 // don't obtrusively prompt the user to select an image, but pretend it was never set. 1220 this._popupCorruptImageErrorDialog(); 1221 } 1222 }; 1223 1224 /** 1225 * @private 1226 */ 1227 ZmEditContactViewImage.prototype._popupCorruptImageErrorDialog = function() { 1228 var dlg = this.corruptImageErrorDlg; 1229 if(dlg){ 1230 dlg.popup(); 1231 } 1232 else{ 1233 dlg = appCtxt.getMsgDialog(); 1234 this.corruptImageErrorDlg = dlg; 1235 dlg.setMessage(ZmMsg.errorCorruptImageFile, DwtMessageDialog.CRITICAL_STYLE, ZmMsg.corruptFile); 1236 dlg.setButtonListener(DwtDialog.OK_BUTTON, new AjxListener(this, this._corruptImageErrorDialogOkListener)); 1237 dlg.popup(); 1238 } 1239 }; 1240 1241 /** 1242 * @private 1243 */ 1244 ZmEditContactViewImage.prototype._corruptImageErrorDialogOkListener = function() { 1245 this.corruptImageErrorDlg.popdown(); 1246 this._chooseImage(); 1247 }; 1248 1249 // 1250 // Class: ZmEditContactViewRows 1251 // 1252 1253 /** 1254 * Creates the contact view rows. 1255 * @class 1256 * This class represents the contact view rows. 1257 * 1258 * @param {Hash} params a hash of parameters 1259 * 1260 * @extends DwtFormRows 1261 * 1262 * @private 1263 */ 1264 ZmEditContactViewRows = function(params) { 1265 if (arguments.length == 0) return; 1266 if (!params.formItemDef) params.formItemDef = {}; 1267 // keep track of maximums 1268 var rowitem = params.formItemDef.rowitem; 1269 var rowparams = rowitem && rowitem.params; 1270 var rowoptions = this._options = (rowparams && rowparams.options) || []; 1271 for (var i = 0; i < rowoptions.length; i++) { 1272 var option = rowoptions[i]; 1273 if (option.max) { 1274 if (!this._maximums) this._maximums = {}; 1275 this._maximums[option.value] = { max: option.max, count: 0 }; 1276 } 1277 } 1278 // create rows control 1279 params.formItemDef.id = params.formItemDef.id || Dwt.getNextId(); 1280 params.formItemDef.onremoverow = "this.setDirty(true)"; 1281 params.className = params.className || "ZmEditContactViewRows"; 1282 params.id = [params.parent.getHTMLElId(),params.formItemDef.id].join("_"); 1283 DwtFormRows.apply(this, arguments); 1284 this.setScrollStyle(Dwt.VISIBLE); 1285 }; 1286 ZmEditContactViewRows.prototype = new DwtFormRows; 1287 ZmEditContactViewRows.prototype.constructor = ZmEditContactViewRows; 1288 1289 /** 1290 * Returns a string representation of the object. 1291 * 1292 * @return {String} a string representation of the object 1293 * @private 1294 */ 1295 ZmEditContactViewRows.prototype.toString = function() { 1296 return "ZmEditContactViewRows"; 1297 }; 1298 1299 ZmEditContactViewRows.prototype.TEMPLATE = "abook.Contacts#ZmEditContactViewRows"; 1300 1301 // Public methods 1302 1303 ZmEditContactViewRows.prototype.setDirty = function() { 1304 DwtFormRows.prototype.setDirty.apply(this, arguments); 1305 this.parent.setDirty(this._itemDef.id, this.isDirty()); 1306 }; 1307 1308 /** 1309 * Checks if the row of the given type is at maximum. 1310 * 1311 * @param {constant} type the type 1312 * @return {Boolean} <code>true</code> if at maximum 1313 * @private 1314 */ 1315 ZmEditContactViewRows.prototype.isMaxedOut = function(type) { 1316 var maximums = this._maximums && this._maximums[type]; 1317 return maximums != null && maximums.count >= maximums.max; 1318 }; 1319 1320 /** 1321 * Checks if all rows are at maximum. 1322 * 1323 * @return {Boolean} <code>true</code> if at maximum 1324 * @private 1325 */ 1326 ZmEditContactViewRows.prototype.isAllMaxedOut = function() { 1327 if (!this._options || this._options.length == 0) return false; 1328 // determine which ones are maxed out 1329 var count = 0; 1330 for (var i = 0; i < this._options.length; i++) { 1331 var type = this._options[i].value; 1332 count += this.isMaxedOut(type) ? 1 : 0; 1333 } 1334 // are all of the options maxed out? 1335 return count >= this._options.length; 1336 }; 1337 1338 // 1339 // Class: ZmEditContactViewInputSelectRows 1340 // 1341 1342 /** 1343 * Creates the input select rows. 1344 * @class 1345 * This class represents the input select rows for the contact view. 1346 * 1347 * @param {Hash} params a hash of parameters 1348 * 1349 * @extends ZmEditContactViewRows 1350 * 1351 * @private 1352 */ 1353 ZmEditContactViewInputSelectRows = function(params) { 1354 if (arguments.length == 0) return; 1355 ZmEditContactViewRows.apply(this, arguments); 1356 }; 1357 ZmEditContactViewInputSelectRows.prototype = new ZmEditContactViewRows; 1358 ZmEditContactViewInputSelectRows.prototype.constructor = ZmEditContactViewInputSelectRows; 1359 1360 /** 1361 * Returns a string representation of the object. 1362 * 1363 * @return {String} a string representation of the object 1364 * @private 1365 */ 1366 ZmEditContactViewInputSelectRows.prototype.toString = function() { 1367 return "ZmEditContactViewInputSelectRows"; 1368 }; 1369 1370 // DwtFormRows methods 1371 1372 /** 1373 * Gets the max rows. 1374 * 1375 * @return {int} the maximum rows 1376 * @private 1377 */ 1378 ZmEditContactViewInputSelectRows.prototype.getMaxRows = function() { 1379 return this.isAllMaxedOut() ? this.getRowCount() : ZmEditContactViewRows.prototype.getMaxRows.call(this); 1380 }; 1381 1382 /** 1383 * Sets the value. 1384 * 1385 * @param {Array|String} array an array of {String} values 1386 * @private 1387 */ 1388 ZmEditContactViewInputSelectRows.prototype.setValue = function(array) { 1389 if (arguments[0] instanceof Array) { 1390 DwtFormRows.prototype.setValue.apply(this, arguments); 1391 this._resetMaximums(); 1392 } 1393 else { 1394 var id = String(arguments[0]); 1395 var adjust1 = id && this._subtract(id); 1396 DwtFormRows.prototype.setValue.apply(this, arguments); 1397 var adjust2 = id && this._add(id); 1398 if (adjust1 || adjust2) this._adjustMaximums(); 1399 } 1400 }; 1401 1402 /** 1403 * Adds a row. 1404 * 1405 * @param {ZmItem} itemDef the item definition (not used) 1406 * @param {int} index the index to add the row at 1407 * @private 1408 */ 1409 ZmEditContactViewInputSelectRows.prototype.addRow = function(itemDef, index) { 1410 DwtFormRows.prototype.addRow.apply(this, arguments); 1411 index = index != null ? index : this.getRowCount() - 1; 1412 var adjust = this._add(index); 1413 if (adjust) this._adjustMaximums(); 1414 var value = this.getValue(index); 1415 // select first one that is not maxed out 1416 if (value && this.isMaxedOut(value.type) && this._options.length > 0 && 1417 this._maximums[value.type].count > this._maximums[value.type].max) { 1418 var options = this._options; 1419 for (var i = 0; i < options.length; i++) { 1420 var option = options[i]; 1421 if (!this.isMaxedOut(option.value)) { 1422 value.type = option.value; 1423 this.setValue(index, value); 1424 break; 1425 } 1426 } 1427 } 1428 1429 if (this._rowCount >= this._maxRows) { 1430 for (var i = 0; i < this._rowCount; i++) { 1431 this.setVisible(this._items[i]._addId, false); 1432 } 1433 } 1434 if (AjxEnv.isFirefox) this._updateLayout(); 1435 }; 1436 1437 /** 1438 * Removes a row. 1439 * 1440 * @param {String} indexOrId the row index or item id 1441 * @private 1442 */ 1443 ZmEditContactViewInputSelectRows.prototype.removeRow = function(indexOrId) { 1444 var adjust = this._subtract(indexOrId); 1445 DwtFormRows.prototype.removeRow.apply(this, arguments); 1446 if (adjust) this._adjustMaximums(); 1447 if (AjxEnv.isFirefox) this._updateLayout(); 1448 }; 1449 1450 /** 1451 * @private 1452 */ 1453 ZmEditContactViewInputSelectRows.prototype._setControlIds = function(rowId, index) { 1454 DwtFormRows.prototype._setControlIds.call(this, rowId, index); 1455 var item = this._items[rowId]; 1456 var control = item && item.control; 1457 if (control && control._setControlIds) { 1458 control._setControlIds(rowId, index); 1459 } 1460 }; 1461 1462 // Protected methods 1463 1464 /** 1465 * @private 1466 */ 1467 ZmEditContactViewInputSelectRows.prototype._subtract = function(indexOrId) { 1468 var value = this.getValue(indexOrId); 1469 return this._subtractType(value && value.type); 1470 }; 1471 ZmEditContactViewInputSelectRows.prototype._subtractType = function(type) { 1472 if (!this._maximums || !this._maximums[type]) return false; 1473 this._maximums[type].count--; 1474 return true; 1475 }; 1476 ZmEditContactViewInputSelectRows.prototype._add = function(indexOrId) { 1477 var value = this.getValue(indexOrId); 1478 return this._addType(value && value.type); 1479 }; 1480 ZmEditContactViewInputSelectRows.prototype._addType = function(type) { 1481 if (!this._maximums || !this._maximums[type]) return false; 1482 this._maximums[type].count++; 1483 return true; 1484 }; 1485 1486 ZmEditContactViewInputSelectRows.prototype._adjustMaximums = function() { 1487 if (!this._maximums || !this._options) return; 1488 // determine which ones are maxed out 1489 var enabled = {}; 1490 var count = 0; 1491 for (var i = 0; i < this._options.length; i++) { 1492 var type = this._options[i].value; 1493 var maxed = this.isMaxedOut(type); 1494 enabled[type] = !maxed; 1495 count += maxed ? 1 : 0; 1496 } 1497 // are all of the options maxed out? 1498 var allMaxed = count == this._options.length; 1499 // en/disable controls as needed 1500 var rowCount = this.getRowCount(); 1501 for (var i = 0; i < rowCount; i++) { 1502 var control = this.getControl(i); 1503 if (control.enableOptions) { 1504 control.enableOptions(enabled); 1505 } 1506 // TODO: Will this override the max rows add button visibility? 1507 this.setVisible(this._items[i]._addId, !allMaxed); 1508 } 1509 }; 1510 1511 // TODO: This is a hack to avoid bad counting error. Should 1512 // TODO: really find the cause of the error. 1513 ZmEditContactViewInputSelectRows.prototype._resetMaximums = function() { 1514 if (!this._maximums) return; 1515 for (var type in this._maximums) { 1516 this._maximums[type].count = 0; 1517 } 1518 var rowCount = this.getRowCount(); 1519 for (var i = 0; i < rowCount; i++) { 1520 var value = this.getValue(i); 1521 var maximum = this._maximums[value && value.type]; 1522 if (maximum) { 1523 maximum.count++; 1524 } 1525 } 1526 }; 1527 1528 // On FF, the selects are sometimes rendered incorrectly. 1529 ZmEditContactViewInputSelectRows.prototype._updateLayout = function() { 1530 for (var i = 0, cnt = this.getRowCount(); i < cnt; i++) { 1531 this.getControl(i).reRenderSelect(); 1532 this.getControl(i).reRenderInput(); 1533 } 1534 }; 1535 1536 // 1537 // Class: ZmEditContactViewInputSelect 1538 // 1539 1540 /** 1541 * Creates the contact view input select. 1542 * @class 1543 * This class represents an input select. 1544 * 1545 * @param {Hash} params a hash of parameters 1546 * 1547 * @extends DwtComposite 1548 * 1549 * @private 1550 */ 1551 ZmEditContactViewInputSelect = function(params) { 1552 if (arguments.length == 0) return; 1553 this._formItemId = params.formItemDef.id; 1554 this._options = params.options || []; 1555 this._cols = params.cols; 1556 this._rows = params.rows; 1557 this._hint = params.hint; 1558 DwtComposite.apply(this, arguments); 1559 this._tabGroup = new DwtTabGroup(this._htmlElId); 1560 this._createHtml(params.template); 1561 if (this._input && (params.inputWidth || params.inputHeight)) { 1562 Dwt.setSize(this._input.getInputElement(), params.inputWidth, params.inputHeight); 1563 } 1564 }; 1565 ZmEditContactViewInputSelect.prototype = new DwtComposite; 1566 ZmEditContactViewInputSelect.prototype.constructor = ZmEditContactViewInputSelect; 1567 1568 /** 1569 * Returns a string representation of the object. 1570 * 1571 * @return {String} a string representation of the object 1572 * @private 1573 */ 1574 ZmEditContactViewInputSelect.prototype.toString = function() { 1575 return "ZmEditContactViewInputSelect"; 1576 }; 1577 1578 // Data 1579 1580 ZmEditContactViewInputSelect.prototype.TEMPLATE = "abook.Contacts#ZmEditContactViewInputSelect"; 1581 1582 // Public methods 1583 1584 /** 1585 * Sets the value. 1586 * 1587 * @param {Object} value the value 1588 * @private 1589 */ 1590 ZmEditContactViewInputSelect.prototype.setValue = function(value) { 1591 var hasOptions = this._options.length > 0; 1592 var inputValue = hasOptions ? value && value.value : value; 1593 if (hasOptions && this._select) { 1594 this._select.setSelectedValue((value && value.type) || this._options[0].value); 1595 } 1596 if (this._input) { 1597 if (this._select) 1598 this._input.setEnabled(this._select.getValue() != "_NONE"); 1599 this._input.setValue(inputValue || ""); 1600 } 1601 }; 1602 1603 /** 1604 * Gets the value. 1605 * 1606 * @return {Object} the value 1607 * @private 1608 */ 1609 ZmEditContactViewInputSelect.prototype.getValue = function() { 1610 var hasOptions = this._options.length > 0; 1611 var inputValue = this._input ? this._input.getValue() : ""; 1612 return hasOptions ? { 1613 type: this._select ? this._select.getValue() : "", 1614 value: inputValue 1615 } : inputValue; 1616 }; 1617 1618 /** 1619 * Sets the dirty flag. 1620 * 1621 * @param {Boolean} dirty (not used) 1622 * @private 1623 */ 1624 ZmEditContactViewInputSelect.prototype.setDirty = function(dirty) { 1625 if (this.parent instanceof DwtForm) { 1626 this.parent.setDirty(true); 1627 } 1628 }; 1629 1630 /** 1631 * Checks if the two items are equal. 1632 * 1633 * @param {Object} a item a 1634 * @param {Object} b item b 1635 * 1636 * @private 1637 */ 1638 ZmEditContactViewInputSelect.equals = function(a, b) { 1639 if (a === b) return true; 1640 if (!a || !b) return false; 1641 var hasOptions = this._options.length > 0; 1642 return hasOptions ? a.type == b.type && a.value == b.value : a == b; 1643 }; 1644 1645 // Hooks 1646 1647 ZmEditContactViewInputSelect.prototype.enableOptions = function(enabled) { 1648 if (!this._select || !this._select.enableOption) return; 1649 var type = this.getValue().type; 1650 for (var id in enabled) { 1651 this._select.enableOption(id, id == type || enabled[id]); 1652 } 1653 }; 1654 1655 // Protected methods 1656 1657 ZmEditContactViewInputSelect.prototype._focus = 1658 function() { 1659 this._input.focus(); 1660 }; 1661 1662 ZmEditContactViewInputSelect.prototype._setControlIds = function(rowId, index) { 1663 var id = this.getHTMLElId(); 1664 this._setControlId(this, id+"_value"); 1665 this._setControlId(this._input, id); 1666 this._setControlId(this._select, id+"_select"); 1667 }; 1668 1669 ZmEditContactViewInputSelect.prototype._setControlId = DwtFormRows.prototype._setControlId; 1670 1671 ZmEditContactViewInputSelect.prototype._createHtml = function(templateId) { 1672 var tabIndexes = this._tabIndexes = []; 1673 this._createHtmlFromTemplate(templateId || this.TEMPLATE, {id:this._htmlElId}); 1674 tabIndexes.sort(DwtForm.__byTabIndex); 1675 for (var i = 0; i < tabIndexes.length; i++) { 1676 var control = tabIndexes[i].control; 1677 this._tabGroup.addMember(control.getTabGroupMember() || control); 1678 } 1679 }; 1680 1681 ZmEditContactViewInputSelect.prototype._createHtmlFromTemplate = function(templateId, data) { 1682 DwtComposite.prototype._createHtmlFromTemplate.apply(this, arguments); 1683 1684 var tabIndexes = this._tabIndexes; 1685 var inputEl = document.getElementById(data.id+"_input"); 1686 if (inputEl) { 1687 this._input = this._createInput(); 1688 this._input.replaceElement(inputEl); 1689 if (inputEl.getAttribute("notab") != "true") { 1690 tabIndexes.push({ 1691 tabindex: inputEl.getAttribute("tabindex") || Number.MAX_VALUE, 1692 control: this._input 1693 }); 1694 } 1695 } 1696 1697 var selectEl = document.getElementById(data.id+"_select"); 1698 var hasOptions = this._options.length > 0; 1699 if (hasOptions && selectEl) { 1700 this._select = this._createSelect(this._options); 1701 this._select.addChangeListener(new AjxListener(this, this._handleSelectChange)); 1702 this._select.replaceElement(selectEl); 1703 if (selectEl.getAttribute("notab") != "true") { 1704 tabIndexes.push({ 1705 tabindex: selectEl.getAttribute("tabindex") || Number.MAX_VALUE, 1706 control: this._select 1707 }); 1708 } 1709 this._select.setVisible(this._options.length > 1); 1710 if (this._input) 1711 this._input.setEnabled(this._select.getValue() != "_NONE"); 1712 } 1713 }; 1714 1715 ZmEditContactViewInputSelect.prototype._createInput = function() { 1716 var input = new DwtInputField({parent:this,size:this._cols,rows:this._rows}); 1717 input.setHint(this._hint); 1718 input.setHandler(DwtEvent.ONKEYDOWN, AjxCallback.simpleClosure(this._handleInputKeyDown, this, input)); 1719 input.setHandler(DwtEvent.ONKEYUP, AjxCallback.simpleClosure(this._handleInputKeyUp, this, input)); 1720 input.setHandler(DwtEvent.ONMOUSEDOWN, AjxCallback.simpleClosure(this._handleMouseDown, this, input)); 1721 if (AjxEnv.isIE && !AjxEnv.isIE9 && !AjxEnv.isIE8) { 1722 // Add a handler to account for IE's 'clear an input field' X control. IE10+ 1723 input.setHandler(DwtEvent.ONMOUSEUP, this._handleMouseUp.bind(this, input)); 1724 } 1725 input.setHandler(DwtEvent.ONPASTE, AjxCallback.simpleClosure(this._onPaste, this, input)); 1726 return input; 1727 }; 1728 1729 ZmEditContactViewInputSelect.prototype._createSelect = function(options) { 1730 var id = [this.getHTMLElId(),"select"].join("_"); 1731 var select = new DwtSelect({parent:this,id:id}); 1732 for (var i = 0; i < options.length; i++) { 1733 var option = options[i]; 1734 var maxedOut = this.parent.isMaxedOut(option.value); 1735 select.addOption(option.label || option.value, i == 0 && !maxedOut, option.value); 1736 if (maxedOut) { 1737 select.enableOption(option.value, false); 1738 } 1739 } 1740 return select; 1741 }; 1742 1743 ZmEditContactViewInputSelect.prototype.reRenderSelect = function() { 1744 if (this._select && this._select.updateRendering) 1745 this._select.updateRendering(); 1746 }; 1747 1748 ZmEditContactViewInputSelect.prototype.reRenderInput = function() { 1749 if (this._input) { 1750 var value = this._input.getValue(); 1751 if (value && value != "") { 1752 this._input.setValue(value+" "); 1753 this._input.setValue(value); 1754 } 1755 } 1756 }; 1757 1758 ZmEditContactViewInputSelect.prototype._handleInputKeyDown = function(input, evt) { 1759 var value = input.getValue(); 1760 input.setData("OLD_VALUE", value); 1761 return true; 1762 }; 1763 1764 ZmEditContactViewInputSelect.prototype._handleInputKeyUp = function(input, evt) { 1765 var ovalue = input.getData("OLD_VALUE"); 1766 var nvalue = input.getValue(); 1767 if (ovalue != null && ovalue != nvalue) { 1768 this.setDirty(true); 1769 } 1770 return true; 1771 }; 1772 1773 ZmEditContactViewInputSelect.prototype._handleMouseDown = 1774 function(input, evt) { 1775 var value = input.getValue(); 1776 input.setData("OLD_VALUE", value); 1777 return true; 1778 }; 1779 1780 ZmEditContactViewInputSelect.prototype._handleMouseUp = function(input, evt) { 1781 // Handle IE's 'clear the input field' X - Delay testing until its had a chance to 1782 // clear the field 1783 setTimeout(this._checkCleared.bind(this, input), 0); 1784 return true; 1785 }; 1786 ZmEditContactViewInputSelect.prototype._checkCleared = function(input) { 1787 // Check for a change in input 1788 var ovalue = input.getData("OLD_VALUE"); 1789 var nvalue = input.getValue(); 1790 if (ovalue != null && ovalue != nvalue) { 1791 this.setDirty(true); 1792 } 1793 }; 1794 1795 ZmEditContactViewInputSelect.prototype._onPaste = 1796 function(input, evt) { 1797 var ovalue = input.getData("OLD_VALUE"); 1798 if (ovalue != null) { 1799 AjxTimedAction.scheduleAction(new AjxTimedAction(this, this._checkInput, [input]), 100); 1800 } 1801 }; 1802 1803 ZmEditContactViewInputSelect.prototype._checkInput = 1804 function(input) { 1805 var ovalue = input.getData("OLD_VALUE"); 1806 var nvalue = input.getValue(); 1807 if (ovalue != null && ovalue != nvalue) { 1808 this.setDirty(true); 1809 } 1810 return true; 1811 }; 1812 1813 1814 ZmEditContactViewInputSelect.prototype._handleSelectChange = function(evt, skipFocus) { 1815 var args = evt._args; 1816 var adjust1 = this.parent._subtractType(args.oldValue); 1817 var adjust2 = this.parent._addType(args.newValue); 1818 if (adjust1 || adjust2) { 1819 this.parent._adjustMaximums(); 1820 } 1821 this.setDirty(true); 1822 if (this._input && this._select) { 1823 var enabled = this._select.getValue() != "_NONE"; 1824 this._input.setEnabled(enabled); 1825 if (enabled && !skipFocus) 1826 this._input.focus(); 1827 } 1828 }; 1829 1830 // DwtControl methods 1831 1832 ZmEditContactViewInputSelect.prototype.getTabGroupMember = function() { 1833 return this._tabGroup; 1834 }; 1835 1836 /** 1837 * Creates the input select rows. 1838 * @class 1839 * This class represents the input double select rows for the contact view. 1840 * 1841 * @param {Hash} params a hash of parameters 1842 * 1843 * @extends ZmEditContactViewRows 1844 * 1845 * @private 1846 */ 1847 ZmEditContactViewInputDoubleSelectRows = function(params) { 1848 if (arguments.length == 0) return; 1849 ZmEditContactViewInputSelectRows.apply(this, arguments); 1850 1851 var rowitem = params.formItemDef.rowitem; 1852 var rowparams = rowitem && rowitem.params; 1853 var rowoptions2 = this._options2 = (rowparams && rowparams.options2) || []; 1854 for (var i = 0; i < rowoptions2.length; i++) { 1855 var option = rowoptions2[i]; 1856 if (option.max) { 1857 if (!this._maximums2) this._maximums2 = {}; 1858 this._maximums2[option.value] = { max: option.max, count: 0 }; 1859 } 1860 } 1861 1862 }; 1863 ZmEditContactViewInputDoubleSelectRows.prototype = new ZmEditContactViewInputSelectRows; 1864 ZmEditContactViewInputDoubleSelectRows.prototype.constructor = ZmEditContactViewInputDoubleSelectRows; 1865 1866 /** 1867 * Returns a string representation of the object. 1868 * 1869 * @return {String} a string representation of the object 1870 * @private 1871 */ 1872 ZmEditContactViewInputDoubleSelectRows.prototype.toString = function() { 1873 return "ZmEditContactViewInputDoubleSelectRows"; 1874 }; 1875 1876 ZmEditContactViewInputDoubleSelectRows.prototype._subtract = function(indexOrId) { 1877 var value = this.getValue(indexOrId); 1878 var a = this._subtractType(value && value.type); 1879 var b = this._subtractType2(value && value.type2); 1880 return a && b; 1881 }; 1882 ZmEditContactViewInputDoubleSelectRows.prototype._subtractType2 = function(type) { 1883 if (!this._maximums2 || !this._maximums2[type]) return false; 1884 this._maximums2[type].count--; 1885 return true; 1886 }; 1887 ZmEditContactViewInputDoubleSelectRows.prototype._add = function(indexOrId) { 1888 var value = this.getValue(indexOrId); 1889 var a = this._addType(value && value.type); 1890 var b = this._addType2(value && value.type2); 1891 return a || b; 1892 }; 1893 ZmEditContactViewInputDoubleSelectRows.prototype._addType2 = function(type) { 1894 if (!this._maximums2 || !this._maximums2[type]) return false; 1895 this._maximums2[type].count++; 1896 return true; 1897 }; 1898 1899 ZmEditContactViewInputDoubleSelectRows.prototype._adjustMaximums = function() { 1900 ZmEditContactViewInputSelectRows.prototype._adjustMaximums.call(this); 1901 if (!this._maximums2 || !this._options2) return; 1902 // determine which ones are maxed out 1903 var enabled = {}; 1904 var count = 0; 1905 for (var i = 0; i < this._options2.length; i++) { 1906 var type = this._options2[i].value; 1907 var maxed = this.isMaxedOut2(type); 1908 enabled[type] = !maxed; 1909 count += maxed ? 1 : 0; 1910 } 1911 // are all of the options maxed out? 1912 var allMaxed = count == this._options2.length; 1913 // en/disable controls as needed 1914 var rowCount = this.getRowCount(); 1915 for (var i = 0; i < rowCount; i++) { 1916 var control = this.getControl(i); 1917 if (control.enableOptions) { 1918 control.enableOptions(enabled); 1919 } 1920 // TODO: Will this override the max rows add button visibility? 1921 this.setVisible(this._items[i]._addId, !allMaxed); 1922 } 1923 }; 1924 1925 // TODO: This is a hack to avoid bad counting error. Should 1926 // TODO: really find the cause of the error. 1927 ZmEditContactViewInputDoubleSelectRows.prototype._resetMaximums = function() { 1928 ZmEditContactViewInputSelectRows.prototype._resetMaximums.call(this); 1929 if (!this._maximums2) return; 1930 for (var type in this._maximums2) { 1931 this._maximums2[type].count = 0; 1932 } 1933 var rowCount = this.getRowCount(); 1934 for (var i = 0; i < rowCount; i++) { 1935 var value = this.getValue(i); 1936 var maximum = this._maximums2[value && value.type2]; 1937 if (maximum) { 1938 maximum.count++; 1939 } 1940 } 1941 }; 1942 1943 ZmEditContactViewInputDoubleSelectRows.prototype.addRow = function(itemDef, index) { 1944 DwtFormRows.prototype.addRow.apply(this, arguments); 1945 index = index != null ? index : this.getRowCount() - 1; 1946 var adjust = this._add(index); 1947 if (adjust) this._adjustMaximums(); 1948 var value = this.getValue(index); 1949 // select first one that is not maxed out 1950 1951 var typeChanged = false; 1952 if (value && this.isMaxedOut(value.type) && this._options.length > 0 && 1953 this._maximums[value.type].count > this._maximums[value.type].max) { 1954 var options = this._options; 1955 for (var i = 0; i < options.length; i++) { 1956 var option = options[i]; 1957 if (!this.isMaxedOut(option.value)) { 1958 value.type = option.value; 1959 typeChanged = true; 1960 break; 1961 } 1962 } 1963 } 1964 1965 if (value && this.isMaxedOut2(value.type2) && this._options2.length > 0 && 1966 this._maximums2[value.type2].count > this._maximums2[value.type2].max) { 1967 var options = this._options2; 1968 for (var i = 0; i < options.length; i++) { 1969 var option = options[i]; 1970 if (!this.isMaxedOut2(option.value)) { 1971 value.type2 = option.value; 1972 typeChanged = true; 1973 break; 1974 } 1975 } 1976 } 1977 if (typeChanged) 1978 this.setValue(index, value); 1979 1980 if (this._rowCount >= this._maxRows) { 1981 for (var i = 0; i < this._rowCount; i++) { 1982 this.setVisible(this._items[i]._addId, false); 1983 } 1984 } 1985 }; 1986 1987 ZmEditContactViewInputDoubleSelectRows.prototype.isMaxedOut2 = function(type) { 1988 var maximums = this._maximums2 && this._maximums2[type]; 1989 return maximums != null && maximums.count >= maximums.max; 1990 }; 1991 1992 /** 1993 * Checks if all rows are at maximum. 1994 * 1995 * @return {Boolean} <code>true</code> if at maximum 1996 * @private 1997 */ 1998 ZmEditContactViewInputDoubleSelectRows.prototype.isAllMaxedOut = function() { 1999 if (ZmEditContactViewInputSelectRows.prototype.isAllMaxedOut.call(this)) return true; 2000 if (!this._options2 || this._options2.length == 0) return false; 2001 // determine which ones are maxed out 2002 var count = 0; 2003 for (var i = 0; i < this._options2.length; i++) { 2004 var type = this._options2[i].value; 2005 count += this.isMaxedOut2(type) ? 1 : 0; 2006 } 2007 // are all of the options maxed out? 2008 return count >= this._options2.length; 2009 }; 2010 2011 // 2012 // Class: ZmEditContactViewInputDoubleSelect 2013 // 2014 2015 /** 2016 * Creates the contact view input double select. 2017 * @class 2018 * This class represents an input with two selects. 2019 * 2020 * @param {Hash} params a hash of parameters 2021 * 2022 * @extends ZmEditContactViewInputSelect 2023 * 2024 * @private 2025 */ 2026 2027 ZmEditContactViewInputDoubleSelect = function(params) { 2028 if (arguments.length == 0) return; 2029 this._options2 = params.options2 || []; 2030 ZmEditContactViewInputSelect.apply(this, arguments); 2031 }; 2032 ZmEditContactViewInputDoubleSelect.prototype = new ZmEditContactViewInputSelect; 2033 ZmEditContactViewInputDoubleSelect.prototype.constructor = ZmEditContactViewInputDoubleSelect; 2034 2035 /** 2036 * Returns a string representation of the object. 2037 * 2038 * @return {String} a string representation of the object 2039 * @private 2040 */ 2041 ZmEditContactViewInputDoubleSelect.prototype.toString = function() { 2042 return "ZmEditContactViewInputDoubleSelect"; 2043 }; 2044 2045 // Data 2046 2047 ZmEditContactViewInputDoubleSelect.prototype.TEMPLATE = "abook.Contacts#ZmEditContactViewInputDoubleSelect"; 2048 2049 // Public methods 2050 2051 /** 2052 * Sets the value. 2053 * 2054 * @param {Object} value the value 2055 * @private 2056 */ 2057 ZmEditContactViewInputDoubleSelect.prototype.setValue = function(value) { 2058 var hasOptions = this._options.length > 0; 2059 var hasOptions2 = this._options2.length > 0; 2060 var inputValue = hasOptions || hasOptions2 ? value && value.value : value; 2061 if (hasOptions && this._select) { 2062 this._select.setSelectedValue((value && value.type) || this._options[0].value); 2063 } 2064 if (hasOptions2 && this._select2) { 2065 this._select2.setSelectedValue((value && value.type2) || this._options2[0].value); 2066 } 2067 if (this._input) { 2068 if (this._select || this._select2) 2069 this._input.setEnabled((this._select && this._select.getValue() != "_NONE") && (this._select2 && this._select2.getValue() != "_NONE")); 2070 this._input.setValue(inputValue || ""); 2071 } 2072 }; 2073 2074 /** 2075 * Gets the value. 2076 * 2077 * @return {Object} the value 2078 * @private 2079 */ 2080 ZmEditContactViewInputDoubleSelect.prototype.getValue = function() { 2081 var hasOptions = this._options.length > 0; 2082 var hasOptions2 = this._options2.length > 0; 2083 var inputValue = this._input ? this._input.getValue() : ""; 2084 return hasOptions || hasOptions2 ? { 2085 type: this._select ? this._select.getValue() : "", 2086 type2: this._select2 ? this._select2.getValue() : "", 2087 value: inputValue 2088 } : inputValue; 2089 }; 2090 2091 /** 2092 * Checks if the two items are equal. 2093 * 2094 * @param {Object} a item a 2095 * @param {Object} b item b 2096 * 2097 * @private 2098 */ 2099 ZmEditContactViewInputDoubleSelect.equals = function(a, b) { 2100 if (a === b) return true; 2101 if (!a || !b) return false; 2102 var hasOptions = this._options.length > 0; 2103 var hasOptions2 = this._options2.length > 0; 2104 if (hasOptions) { 2105 if (a.type != b.type || a.value != b.value) 2106 return false; 2107 } 2108 if (hasOptions2) { 2109 if (a.type2 != b.type2 || a.value != b.value) 2110 return false; 2111 } 2112 if (!hasOptions && !hasOptions2) 2113 if (a != b) 2114 return false; 2115 return true; 2116 }; 2117 2118 // Hooks 2119 2120 ZmEditContactViewInputDoubleSelect.prototype.enableOptions = function(enabled, enabled2) { 2121 if (this._select && this._select.enableOption) { 2122 var type = this.getValue().type; 2123 for (var id in enabled) { 2124 this._select.enableOption(id, id == type || enabled[id]); 2125 } 2126 } 2127 if (this._select2 && this._select2.enableOption) { 2128 var type = this.getValue().type2; 2129 for (var id in enabled2) { 2130 this._select2.enableOption(id, id == type || enabled2[id]); 2131 } 2132 } 2133 }; 2134 2135 // Protected methods 2136 2137 ZmEditContactViewInputDoubleSelect.prototype._setControlIds = function(rowId, index) { 2138 var id = this.getHTMLElId(); 2139 this._setControlId(this, id+"_value"); 2140 this._setControlId(this._input, id); 2141 this._setControlId(this._select, id+"_select"); 2142 this._setControlId(this._select2, id+"_select2"); 2143 }; 2144 2145 ZmEditContactViewInputDoubleSelect.prototype._setControlId = DwtFormRows.prototype._setControlId; 2146 2147 2148 ZmEditContactViewInputDoubleSelect.prototype._createHtmlFromTemplate = function(templateId, data) { 2149 DwtComposite.prototype._createHtmlFromTemplate.apply(this, arguments); 2150 2151 var tabIndexes = this._tabIndexes; 2152 var inputEl = document.getElementById(data.id+"_input"); 2153 if (inputEl) { 2154 this._input = this._createInput(); 2155 this._input.replaceElement(inputEl); 2156 if (inputEl.getAttribute("notab") != "true") { 2157 tabIndexes.push({ 2158 tabindex: inputEl.getAttribute("tabindex") || Number.MAX_VALUE, 2159 control: this._input 2160 }); 2161 } 2162 } 2163 2164 var selectEl = document.getElementById(data.id+"_select"); 2165 var hasOptions = this._options.length > 0; 2166 if (hasOptions && selectEl) { 2167 this._select = this._createSelect(this._options); 2168 this._select.addChangeListener(new AjxListener(this, this._handleSelectChange)); 2169 this._select.replaceElement(selectEl); 2170 if (selectEl.getAttribute("notab") != "true") { 2171 tabIndexes.push({ 2172 tabindex: selectEl.getAttribute("tabindex") || Number.MAX_VALUE, 2173 control: this._select 2174 }); 2175 } 2176 this._select.setVisible(this._options.length > 1); 2177 } 2178 2179 var selectEl2 = document.getElementById(data.id+"_select2"); 2180 var hasOptions2 = this._options2.length > 0; 2181 if (hasOptions2 && selectEl2) { 2182 this._select2 = this._createSelect2(this._options2); 2183 this._select2.addChangeListener(new AjxListener(this, this._handleSelectChange2)); 2184 this._select2.replaceElement(selectEl2); 2185 if (selectEl2.getAttribute("notab") != "true") { 2186 tabIndexes.push({ 2187 tabindex: selectEl.getAttribute("tabindex") || Number.MAX_VALUE, 2188 control: this._select2 2189 }); 2190 } 2191 this._select2.setVisible(this._options2.length > 1); 2192 } 2193 2194 if (this._input) { 2195 if (this._select || this._select2) 2196 this._input.setEnabled((this._select && this._select.getValue() != "_NONE") && (this._select2 && this._select2.getValue() != "_NONE")); 2197 } 2198 }; 2199 2200 ZmEditContactViewInputDoubleSelect.prototype._createSelect2 = function(options) { 2201 var id = [this.getHTMLElId(),"select2"].join("_"); 2202 var select = new DwtSelect({parent:this,id:id}); 2203 for (var i = 0; i < options.length; i++) { 2204 var option = options[i]; 2205 select.addOption(option.label || option.value, i == 0, option.value); 2206 } 2207 return select; 2208 }; 2209 2210 ZmEditContactViewInputDoubleSelect.prototype.reRenderSelect = function() { 2211 ZmEditContactViewInputSelect.prototype.reRenderSelect.call(this); 2212 this._select2.updateRendering(); 2213 }; 2214 2215 ZmEditContactViewInputDoubleSelect.prototype._handleSelectChange = function(evt, skipFocus) { 2216 var args = evt._args; 2217 var adjust1 = this.parent._subtractType(args.oldValue); 2218 var adjust2 = this.parent._addType(args.newValue); 2219 if (adjust1 || adjust2) { 2220 this.parent._adjustMaximums(); 2221 } 2222 this.setDirty(true); 2223 if (this._input) { 2224 var enabled = this._select.getValue() != "_NONE" && this._select2.getValue() != "_NONE"; 2225 this._input.setEnabled(enabled); 2226 if (enabled && !skipFocus) 2227 this._input.focus(); 2228 } 2229 }; 2230 2231 ZmEditContactViewInputDoubleSelect.prototype._handleSelectChange2 = function(evt, skipFocus) { 2232 var args = evt._args; 2233 var adjust1 = this.parent._subtractType2(args.oldValue); 2234 var adjust2 = this.parent._addType2(args.newValue); 2235 if (adjust1 || adjust2) { 2236 this.parent._adjustMaximums(); 2237 } 2238 this.setDirty(true); 2239 if (this._input) { 2240 var enabled = this._select.getValue() != "_NONE" && this._select2.getValue() != "_NONE"; 2241 this._input.setEnabled(enabled); 2242 if (enabled && !skipFocus) 2243 this._input.focus(); 2244 } 2245 }; 2246 2247 // 2248 // Class: ZmEditContactViewOther 2249 // 2250 2251 /** 2252 * Creates the contact view other. 2253 * @class 2254 * This class represents the contact view other field. 2255 * 2256 * @param {Hash} params a hash of parameters 2257 * 2258 * @extends ZmEditContactViewInputSelect 2259 * 2260 * @private 2261 */ 2262 ZmEditContactViewOther = function(params) { 2263 if (arguments.length == 0) return; 2264 ZmEditContactViewInputSelect.apply(this, arguments); 2265 var option = params.options && params.options[0]; 2266 this.setValue({type:option && option.value}); 2267 if (this._select && (params.selectInputWidth || params.selectInputHeight)) { 2268 Dwt.setSize(this._select.input, params.selectInputWidth, params.selectInputHeight); 2269 } 2270 }; 2271 ZmEditContactViewOther.prototype = new ZmEditContactViewInputSelect; 2272 ZmEditContactViewOther.prototype.constructor = ZmEditContactViewOther; 2273 2274 /** 2275 * Returns a string representation of the object. 2276 * 2277 * @return {String} a string representation of the object 2278 * @private 2279 */ 2280 ZmEditContactViewOther.prototype.toString = function() { 2281 return "ZmEditContactViewOther"; 2282 }; 2283 2284 // Data 2285 2286 ZmEditContactViewOther.prototype.TEMPLATE = "abook.Contacts#ZmEditContactViewOther"; 2287 2288 ZmEditContactViewOther.prototype.DATE_ATTRS = { "birthday": true, "anniversary": true }; 2289 2290 ZmEditContactViewOther.validator = function(item) { 2291 if (AjxUtil.isArray(item)) { 2292 if (!item.length) return true; 2293 var result = []; 2294 for (var i=0; i<item.length; i++) { 2295 var value = ZmEditContactViewOther.validator(item[i]); 2296 if (value || value==="") 2297 result.push({type: item[i].type, value: value}); 2298 else 2299 return false; 2300 } 2301 return result; 2302 } else { 2303 if (item.type in ZmEditContactViewOther.prototype.DATE_ATTRS || item.type.replace(/^other/,"").toLowerCase() in ZmEditContactViewOther.prototype.DATE_ATTRS) { 2304 var dateStr = AjxStringUtil.trim(item.value); 2305 if (dateStr.length) { 2306 var aDate = ZmEditContactViewOther.parseDate(dateStr); 2307 if (isNaN(aDate) || aDate == null) { 2308 throw ZmMsg.errorDate; 2309 } 2310 return ZmEditContactViewOther.formatDate(aDate); 2311 } 2312 return dateStr; 2313 } 2314 if (/\d+$/.test(item.type)) { 2315 throw AjxMessageFormat.format(ZmMsg.errorInvalidContactOtherFieldName, item.type); 2316 } 2317 return item.value; 2318 } 2319 }; 2320 2321 // Public methods 2322 2323 /** 2324 * Sets the value. 2325 * 2326 * @param {Object} value the value 2327 * @private 2328 */ 2329 ZmEditContactViewOther.prototype.setValue = function(value) { 2330 ZmEditContactViewInputSelect.prototype.setValue.apply(this, arguments); 2331 this._resetPicker(); 2332 }; 2333 2334 /** 2335 * Gets the value. 2336 * 2337 * @return {Object} the value 2338 * @private 2339 */ 2340 ZmEditContactViewOther.prototype.getValue = function() { 2341 return { 2342 type: this._select.getValue() || this._select.getText(), 2343 value: this._input.getValue() 2344 }; 2345 }; 2346 2347 // Protected methods 2348 2349 ZmEditContactViewOther.prototype._setControlIds = function(rowId, index) { 2350 var id = this.getHTMLElId(); 2351 ZmEditContactViewInputSelect.prototype._setControlIds.apply(this, arguments); 2352 this._setControlId(this._picker, id+"_picker"); 2353 }; 2354 2355 ZmEditContactViewOther.prototype._createHtmlFromTemplate = function(templateId, data) { 2356 ZmEditContactViewInputSelect.prototype._createHtmlFromTemplate.apply(this, arguments); 2357 2358 var tabIndexes = this._tabIndexes; 2359 var pickerEl = document.getElementById(data.id+"_picker"); 2360 if (pickerEl) { 2361 var id = [this.getHTMLElId(),"picker"].join("_"); 2362 this._picker = new DwtButton({parent:this,id:id}); 2363 this._picker.setImage("CalendarApp", null, ZmMsg.chooseDate); 2364 this._picker.popup = ZmEditContactViewOther.__DwtButton_popup; // HACK 2365 2366 var menu = new DwtMenu({parent:this._picker,style:DwtMenu.GENERIC_WIDGET_STYLE}); 2367 this._picker.getHtmlElement().className += " ZmEditContactViewOtherCalendar"; 2368 this._picker.setMenu(menu); 2369 this._picker.replaceElement(pickerEl); 2370 2371 var listener = new AjxListener(this, this._handleDropDown); 2372 this._picker.addSelectionListener(listener); 2373 this._picker.addDropDownSelectionListener(listener); 2374 2375 var container = new DwtComposite({parent:menu}); 2376 // TODO: use template? 2377 2378 var calendar = new DwtCalendar({parent:container}); 2379 calendar.setSkipNotifyOnPage(true); 2380 calendar.setDate(new Date()); 2381 calendar.setFirstDayOfWeek(appCtxt.get(ZmSetting.CAL_FIRST_DAY_OF_WEEK) || 0); 2382 calendar.addSelectionListener(new AjxListener(this,this._handleDateSelection,[calendar])); 2383 tabIndexes.push({ 2384 tabindex: pickerEl.getAttribute("tabindex") || Number.MAX_VALUE, 2385 control: this._picker 2386 }); 2387 this._calendar = calendar; 2388 2389 var checkbox = new DwtCheckbox({parent:container}); 2390 checkbox.setText(ZmMsg.includeYear); 2391 checkbox.addSelectionListener(new AjxListener(this, this._handleDateSelection,[calendar])); 2392 this._calendarIncludeYear = checkbox; 2393 } 2394 }; 2395 2396 // HACK: This function executes in the scope of the calendar picker 2397 // HACK: button. It avoids the calendar being resized and scrolled 2398 // HACK: when there's not enough room to display the menu below the 2399 // HACK: button. 2400 ZmEditContactViewOther.__DwtButton_popup = function() { 2401 var button = this; 2402 var size = button.getSize(); 2403 var location = Dwt.toWindow(button.getHtmlElement(), 0, 0); 2404 var menu = button.getMenu(); 2405 var menuSize = menu.getSize(); 2406 var windowSize = DwtShell.getShell(window).getSize(); 2407 if ((location.y + size.y) + menuSize.y > windowSize.y) { 2408 button._menuPopupStyle = DwtButton.MENU_POPUP_STYLE_ABOVE; 2409 } 2410 if (AjxEnv.isIE) { 2411 menu.getHtmlElement().style.width = "150px"; 2412 } 2413 DwtButton.prototype.popup.call(button, menu); 2414 }; 2415 2416 ZmEditContactViewOther.prototype._createSelect = function() { 2417 var id = [this.getHTMLElId(),"select"].join("_"); 2418 var select = new DwtComboBox({parent:this,inputParams:{size:14},id:id}); 2419 var options = this._options || []; 2420 for (var i = 0; i < options.length; i++) { 2421 var option = options[i]; 2422 select.add(option.label || option.value, option.value, i == 0); 2423 } 2424 select.addChangeListener(new AjxListener(this, this._resetPicker)); 2425 // HACK: Make it look like a DwtSelect. 2426 select.setSelectedValue = select.setValue; 2427 return select; 2428 }; 2429 2430 ZmEditContactViewOther.prototype._resetPicker = function() { 2431 if (this._picker) { 2432 var type = this.getValue().type; 2433 this._picker.setVisible(type in this.DATE_ATTRS); 2434 } 2435 }; 2436 2437 ZmEditContactViewOther.parseDate = function(dateStr) { 2438 // NOTE: Still try to parse date string in locale-specific 2439 // NOTE: format for backwards compatibility. 2440 var noYear = dateStr.match(/^--/); 2441 var pattern = noYear ? "--MM-dd" : "yyyy-MM-dd"; 2442 var aDate = AjxDateFormat.parse(pattern, dateStr); 2443 2444 if (isNaN(aDate) || aDate == null) { 2445 aDate = AjxDateUtil.simpleParseDateStr(dateStr); 2446 } 2447 else if (noYear) { 2448 aDate.setFullYear(0); 2449 } 2450 return aDate; 2451 }; 2452 2453 ZmEditContactViewOther.formatDate = function(date) { 2454 var pattern = date.getFullYear() == 0 ? "--MM-dd" : "yyyy-MM-dd"; 2455 return AjxDateFormat.format(pattern, date); 2456 }; 2457 2458 ZmEditContactViewOther._getDateFormatter = function() { 2459 if (!ZmEditContactViewOther._formatter) { 2460 ZmEditContactViewOther._formatter = new AjxDateFormat("yyyy-MM-dd"); 2461 } 2462 return ZmEditContactViewOther._formatter; 2463 }; 2464 2465 ZmEditContactViewOther.prototype._handleDropDown = function(evt) { 2466 var value = this.getValue().value; 2467 var date = ZmEditContactViewOther.parseDate(value) || new Date(); 2468 var includeYear = date.getFullYear() !== 0; 2469 // NOTE: Temporarilly set the year to the current year in the 2470 // NOTE: case of a date without a year set (i.e. full year == 0). 2471 // NOTE: This is done so that the calendar doesn't show the 2472 // NOTE: wrong year. 2473 if (!includeYear) { 2474 date.setFullYear(new Date().getFullYear()); 2475 } 2476 this._calendarIncludeYear.setSelected(includeYear); //see bug 46952 and bug 83177 2477 this._calendar.setDate(date); 2478 this._picker.popup(); 2479 }; 2480 2481 ZmEditContactViewOther.prototype._handleDateSelection = function(calendar) { 2482 this._picker.getMenu().popdown(); 2483 2484 if (!calendar) calendar = this._calendar; 2485 var date = calendar.getDate(); 2486 if (!this._calendarIncludeYear.isSelected()) { 2487 date = new Date(date.getTime()); 2488 date.setFullYear(0); 2489 } 2490 2491 var value = this.getValue(); 2492 value.value = ZmEditContactViewOther.formatDate(date); 2493 this.setValue(value); 2494 this.parent.setDirty(true); 2495 }; 2496 2497 ZmEditContactViewOther.prototype._handleSelectChange = function(evt) { 2498 ZmEditContactViewInputSelect.prototype._handleSelectChange.call(this, evt, true); 2499 }; 2500 2501 // 2502 // Class: ZmEditContactViewIM 2503 // 2504 /** 2505 * Creates the contact view IM field. 2506 * @class 2507 * This class represents the contact view IM field. 2508 * 2509 * @param {Hash} params a hash of parameters 2510 * 2511 * @extends ZmEditContactViewInputSelect 2512 * 2513 * @private 2514 */ 2515 ZmEditContactViewIM = function(params) { 2516 if (arguments.length == 0) return; 2517 ZmEditContactViewInputSelect.apply(this, arguments); 2518 }; 2519 ZmEditContactViewIM.prototype = new ZmEditContactViewInputSelect; 2520 ZmEditContactViewIM.prototype.constructor = ZmEditContactViewIM; 2521 2522 /** 2523 * Returns a string representation of the object. 2524 * 2525 * @return {String} a string representation of the object 2526 * @private 2527 */ 2528 ZmEditContactViewIM.prototype.toString = function() { 2529 return "ZmEditContactViewIM"; 2530 }; 2531 2532 // constants 2533 2534 ZmEditContactViewIM.RE_VALUE = /^(.*?):\/\/(.*)$/; 2535 2536 // Public methods 2537 2538 ZmEditContactViewIM.prototype.setValue = function(value) { 2539 var m = ZmEditContactViewIM.RE_VALUE.exec(value); 2540 value = m ? { type:m[1],value:m[2] } : { type:"xmpp",value:value }; 2541 ZmEditContactViewInputSelect.prototype.setValue.call(this, value); 2542 }; 2543 ZmEditContactViewIM.prototype.getValue = function() { 2544 var value = ZmEditContactViewInputSelect.prototype.getValue.call(this); 2545 return value.value ? [value.type, value.value].join("://") : ""; 2546 }; 2547 2548 // 2549 // Class: ZmEditContactViewIMDouble 2550 // 2551 /** 2552 * Creates the contact view IM field. 2553 * @class 2554 * This class represents the contact view IM field. 2555 * 2556 * @param {Hash} params a hash of parameters 2557 * 2558 * @extends ZmEditContactViewInputSelect 2559 * 2560 * @private 2561 */ 2562 ZmEditContactViewIMDouble = function(params) { 2563 if (arguments.length == 0) return; 2564 ZmEditContactViewInputDoubleSelect.apply(this, arguments); 2565 }; 2566 ZmEditContactViewIMDouble.prototype = new ZmEditContactViewInputDoubleSelect; 2567 ZmEditContactViewIMDouble.prototype.constructor = ZmEditContactViewIMDouble; 2568 2569 /** 2570 * Returns a string representation of the object. 2571 * 2572 * @return {String} a string representation of the object 2573 * @private 2574 */ 2575 ZmEditContactViewIMDouble.prototype.toString = function() { 2576 return "ZmEditContactViewIMDouble"; 2577 }; 2578 2579 // constants 2580 2581 ZmEditContactViewIMDouble.RE_VALUE = /^(.*?):\/\/(.*)$/; 2582 2583 // Public methods 2584 2585 ZmEditContactViewIMDouble.prototype.setValue = function(value) { 2586 var obj; 2587 if (!value || value == "") { 2588 obj = { type2:"_NONE", value:"", type: null }; 2589 } else { 2590 var url = value.type ? value.value : value; 2591 var m = ZmEditContactViewIMDouble.RE_VALUE.exec(url); 2592 obj = m ? { type2:m[1], value:m[2], type: value.type?value.type:null } : { type2: value.type2 || "other", value: url, type: value.type ? value.type : null }; 2593 } 2594 ZmEditContactViewInputDoubleSelect.prototype.setValue.call(this, obj); 2595 }; 2596 2597 ZmEditContactViewIMDouble.prototype.getValue = function() { 2598 var value = ZmEditContactViewInputDoubleSelect.prototype.getValue.call(this); 2599 var url = (value.type2=="_NONE" || value.value=="") ? "" : [value.type2, value.value].join("://"); 2600 var obj = value.type2 ? { 2601 type: value.type, 2602 type2: value.type2, 2603 value: url 2604 } : url; 2605 return obj; 2606 }; 2607 2608 ZmEditContactViewIMDouble.equals = function(a,b) { 2609 if (a === b) return true; 2610 if (!a || !b) return false; 2611 return a.type == b.type && 2612 a.type2 == b.type2 && 2613 a.value == b.value; 2614 }; 2615 2616 // 2617 // Class: ZmEditContactViewAddress 2618 // 2619 /** 2620 * Creates the contact view address input field. 2621 * @class 2622 * This class represents the address input field. 2623 * 2624 * @param {Hash} params a hash of parameters 2625 * 2626 * @extends ZmEditContactViewInputSelect 2627 * 2628 * @private 2629 */ 2630 ZmEditContactViewAddress = function(params) { 2631 if (arguments.length == 0) return; 2632 ZmEditContactViewInputSelect.call(this, params); 2633 }; 2634 ZmEditContactViewAddress.prototype = new ZmEditContactViewInputSelect; 2635 ZmEditContactViewAddress.prototype.constructor = ZmEditContactViewAddress; 2636 2637 /** 2638 * Returns a string representation of the object. 2639 * 2640 * @return {String} a string representation of the object 2641 * @private 2642 */ 2643 ZmEditContactViewAddress.prototype.toString = function() { 2644 return "ZmEditContactViewAddress"; 2645 }; 2646 2647 // Data 2648 2649 ZmEditContactViewAddress.prototype.TEMPLATE = "abook.Contacts#ZmEditContactViewAddressSelect"; 2650 2651 // Public methods 2652 2653 ZmEditContactViewAddress.prototype.setValue = function(value) { 2654 ZmEditContactViewInputSelect.prototype.setValue.apply(this, arguments); 2655 value = value || {}; 2656 this._select.setSelectedValue(value.type); 2657 this._input.setValue("STREET", value.Street); 2658 this._input.setValue("CITY", value.City); 2659 this._input.setValue("STATE", value.State); 2660 this._input.setValue("ZIP", value.PostalCode); 2661 this._input.setValue("COUNTRY", value.Country); 2662 this._input.setDirty(false); 2663 this._input.update(); 2664 }; 2665 2666 ZmEditContactViewAddress.prototype.getValue = function() { 2667 return { 2668 type: this._select.getValue(), 2669 Street: this._input.getValue("STREET"), 2670 City: this._input.getValue("CITY"), 2671 State: this._input.getValue("STATE"), 2672 PostalCode: this._input.getValue("ZIP"), 2673 Country: this._input.getValue("COUNTRY") 2674 }; 2675 }; 2676 2677 ZmEditContactViewAddress.equals = function(a,b) { 2678 if (a === b) return true; 2679 if (!a || !b) return false; 2680 return a.type == b.type && 2681 a.Street == b.Street && a.City == b.City && a.State == b.State && 2682 a.PostalCode == b.PostalCode && a.Country == b.Country; 2683 }; 2684 2685 // Protected methods 2686 2687 ZmEditContactViewAddress.prototype._setControlIds = function(rowId, index) { 2688 var id = this.getHTMLElId(); 2689 ZmEditContactViewInputSelect.prototype._setControlIds.apply(this, arguments); 2690 var fieldIds = ["STREET", "CITY", "STATE", "ZIP", "COUNTRY"]; 2691 for (var i = 0; i < fieldIds.length; i++) { 2692 var fieldId = fieldIds[i]; 2693 var form = this._input.getControl(fieldId); 2694 this._setControlId.call(form, form, [id,fieldId].join("_")); 2695 } 2696 }; 2697 2698 ZmEditContactViewAddress.prototype._createInput = function() { 2699 var form = { 2700 template: "abook.Contacts#ZmEditContactViewAddress", 2701 // NOTE: The parent is a ZmEditContactViewInputSelect which knows 2702 // NOTE: its item ID and will set the dirty state on the main 2703 // NOTE: form appropriately. 2704 ondirty: "this.parent._handleDirty()", 2705 items: [ 2706 { id: "STREET", type: "DwtInputField", width: 343, rows: 2, 2707 hint: ZmMsg.AB_FIELD_street, params: { forceMultiRow: true } 2708 }, 2709 { id: "CITY", type: "DwtInputField", width: 160, hint: ZmMsg.AB_FIELD_city }, 2710 { id: "STATE", type: "DwtInputField", width: 90, hint: ZmMsg.AB_FIELD_state }, 2711 { id: "ZIP", type: "DwtInputField", width: 80, hint: ZmMsg.AB_FIELD_postalCode }, 2712 { id: "COUNTRY", type: "DwtInputField", width: 343, hint: ZmMsg.AB_FIELD_country } 2713 ] 2714 }; 2715 return new DwtForm({parent:this,form:form}); 2716 }; 2717 2718 ZmEditContactViewAddress.prototype._handleDirty = function() { 2719 if (this._input && this._input.isDirty()) { 2720 this.parent.setDirty(true); 2721 } 2722 }; 2723