1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 ZmPref = function(id, name, dataType) { 25 26 ZmSetting.call(this, id, name, ZmSetting.T_PREF, dataType); 27 28 this.origValue = null; 29 this.isDirty = false; 30 }; 31 32 ZmPref.prototype = new ZmSetting; 33 ZmPref.prototype.constructor = ZmPref; 34 35 ZmPref.prototype.isZmPref = true; 36 ZmPref.prototype.toString = function() { return "ZmPref"; }; 37 38 ZmPref.KEY_ID = "prefId_"; 39 40 ZmPref.TYPE_STATIC = "STATIC"; // static text 41 ZmPref.TYPE_INPUT = "INPUT"; 42 ZmPref.TYPE_CHECKBOX = "CHECKBOX"; 43 ZmPref.TYPE_COLOR = "COLOR"; 44 ZmPref.TYPE_RADIO_GROUP = "RADIO_GROUP"; 45 ZmPref.TYPE_SELECT = "SELECT"; 46 ZmPref.TYPE_COMBOBOX = "COMBOBOX"; 47 ZmPref.TYPE_TEXTAREA = "TEXTAREA"; 48 ZmPref.TYPE_PASSWORD = "PASSWORD"; 49 ZmPref.TYPE_IMPORT = "IMPORT"; 50 ZmPref.TYPE_EXPORT = "EXPORT"; 51 ZmPref.TYPE_SHORTCUTS = "SHORTCUTS"; 52 ZmPref.TYPE_CUSTOM = "CUSTOM"; 53 ZmPref.TYPE_LOCALES = "LOCALES"; 54 ZmPref.TYPE_FONT = "FONT"; 55 ZmPref.TYPE_FONT_SIZE = "FONT_SIZE"; 56 57 ZmPref.ORIENT_VERTICAL = "vertical"; 58 ZmPref.ORIENT_HORIZONTAL = "horizontal"; 59 60 ZmPref.MAX_ROWS = 7; 61 62 // custom functions for loading and validation 63 64 ZmPref.loadSkins = 65 function(setup) { 66 var skins = appCtxt.get(ZmSetting.AVAILABLE_SKINS); 67 setup.options = []; // re-init otherwise we could possibly have dupes. 68 for (var i = 0; i < skins.length; i++) { 69 var skin = skins[i]; 70 setup.options.push(skin); 71 var text = ZmMsg['theme-' + skin] || skin.substr(0, 1).toUpperCase() + skin.substr(1); 72 setup.displayOptions.push(text); 73 } 74 }; 75 76 ZmPref.loadCsvFormats = 77 function(setup){ 78 var formats = appCtxt.get(ZmSetting.AVAILABLE_CSVFORMATS); 79 if (!formats._options) { 80 var options = formats._options = []; 81 var displayOptions = formats._displayOptions = []; 82 for(var i=0; i<formats.length; i++){ 83 options.push(formats[i]); 84 } 85 options.sort(ZmPref.__BY_CSVFORMAT); 86 for(var i=0; i < options.length; i++){ 87 displayOptions.push((ZmMsg[options[i]] || options[i])); 88 } 89 } 90 setup.options = formats._options; 91 setup.displayOptions = formats._displayOptions; 92 }; 93 ZmPref.__BY_CSVFORMAT = function(a, b) { 94 if (a.match(/^zimbra/)) return -1; 95 if (b.match(/^zimbra/)) return 1; 96 if (a.match(/^yahoo/)) return -1; 97 if (b.match(/^yahoo/)) return 1; 98 return a.localeCompare(b); 99 }; 100 101 ZmPref.loadPageSizes = 102 function(setup) { 103 var max = (setup.maxSetting && appCtxt.get(setup.maxSetting)) || 100; 104 var list = []; 105 for (var i = 0; i < ZmPref.PAGE_SIZES.length; i++) { 106 var num = parseInt(ZmPref.PAGE_SIZES[i]); 107 if (num <= max) { 108 list.push(ZmPref.PAGE_SIZES[i]); 109 } 110 } 111 if (max > ZmPref.PAGE_SIZES[ZmPref.PAGE_SIZES.length - 1]) { 112 list.push(String(max)); 113 } 114 setup.displayOptions = setup.options = list; 115 }; 116 ZmPref.PAGE_SIZES = ["10", "25", "50", "100", "250", "500", "1000"]; 117 118 ZmPref.validateEmail = 119 function(emailStr) { 120 if (emailStr) { 121 // NOTE: Handle localhost for development purposes 122 return emailStr.match(/\@localhost$/i) || AjxEmailAddress.parse(emailStr) != null; 123 } 124 return true; 125 }; 126 127 ZmPref.validateEmailList = 128 function(emailStrArray) { 129 for(var i in emailStrArray) { 130 if(!ZmPref.validateEmail(emailStrArray[i])) return false; 131 } 132 return true; 133 }; 134 135 ZmPref.downloadSinceDisplay = 136 function(dateStr) { 137 if (!dateStr) { //usually it's "" in this case, but !dateStr would take care of 0 too (which is ZmMailApp.POP_DOWNLOAD_SINCE_ALL too) so changed it to !dateStr 138 return ZmMailApp.POP_DOWNLOAD_SINCE_ALL; 139 } 140 if (dateStr === appCtxt.get(ZmSetting.POP_DOWNLOAD_SINCE)) { 141 return ZmMailApp.POP_DOWNLOAD_SINCE_NO_CHANGE; 142 } 143 return ZmMailApp.POP_DOWNLOAD_SINCE_FROM_NOW; 144 }; 145 ZmPref.downloadSinceValue = 146 function(value) { 147 // == instead of === since the value is a string ("0") instead of a number (0) for some reason. 148 if (value == ZmMailApp.POP_DOWNLOAD_SINCE_ALL) { 149 return ""; 150 } 151 if (value == ZmMailApp.POP_DOWNLOAD_SINCE_NO_CHANGE) { 152 return appCtxt.get(ZmSetting.POP_DOWNLOAD_SINCE); 153 } 154 var date = new Date(); 155 date.setMinutes(date.getMinutes() + date.getTimezoneOffset()); 156 return AjxDateFormat.format("yyyyMMddHHmmss'Z'", date); 157 }; 158 159 ZmPref.validatePollingInterval = 160 function(interval) { 161 var minimum = appCtxt.get(ZmSetting.MIN_POLLING_INTERVAL); 162 if (interval && (!minimum || interval >= minimum)) { 163 return true; 164 } else { 165 var min = minimum / 60; 166 ZmPref.SETUP[ZmSetting.POLLING_INTERVAL].errorMessage = AjxMessageFormat.format(ZmMsg.invalidPollingInterval, min); 167 return false; 168 } 169 }; 170 171 ZmPref.pollingIntervalDisplay = 172 function(seconds) { 173 if (appCtxt.get(ZmSetting.INSTANT_NOTIFY) && seconds == appCtxt.get(ZmSetting.INSTANT_NOTIFY_INTERVAL)) 174 return seconds; 175 else 176 return seconds / 60; 177 }; 178 179 ZmPref.pollingIntervalValue = 180 function(minutes) { 181 if (appCtxt.get(ZmSetting.INSTANT_NOTIFY) && minutes == appCtxt.get(ZmSetting.INSTANT_NOTIFY_INTERVAL)) 182 return minutes; 183 else 184 return minutes * 60; 185 }; 186 187 188 ZmPref.int2DurationDay = 189 function(intValue) { 190 return intValue != null && intValue != 0 ? intValue + "d" : intValue; 191 }; 192 193 ZmPref.string2EmailList = 194 function(value) { 195 var emailList = []; 196 var addr,addrs = AjxEmailAddress.split(value); 197 if (addrs && addrs.length) { 198 for (var i = 0; i < addrs.length; i++) { 199 addr = addrs[i]; 200 var email = AjxEmailAddress.parse(addr); 201 if(email) { 202 addr = email.getAddress(); 203 } 204 if(addr) emailList.push(AjxStringUtil.htmlEncode(addr)); 205 } 206 } 207 return emailList; 208 }; 209 210 ZmPref.durationDay2Int = 211 function(durValue) { 212 return parseInt(durValue, 10); // NOTE: parseInt ignores non-digits 213 }; 214 215 ZmPref.approximateInterval = 216 function(value) { 217 var values = [].concat(ZmPref.SETUP["POLLING_INTERVAL"].options); 218 values.sort(ZmPref.__BY_NUMBER); 219 return ZmPref.approximateValue(values, value); 220 }; 221 222 ZmPref.approximateValue = 223 function(sortedValues, value) { 224 // find closest value 225 for (var i = 0; i < sortedValues.length + 1; i++) { 226 var a = sortedValues[i]; 227 var b = sortedValues[i+1]; 228 if (value < b) { 229 var da = value - a; 230 var db = b - value; 231 return da < db ? a : b; 232 } 233 } 234 return sortedValues[sortedValues.length - 1]; 235 }; 236 237 ZmPref.validateLifetime = 238 function(value) { 239 var globalValue = appCtxt.get(ZmSetting.MAIL_LIFETIME_GLOBAL); 240 if (globalValue == "0") return true; 241 return ZmPref.__BY_DURATION(value, globalValue) <= 0; 242 }; 243 244 ZmPref.validateLifetimeJunk = 245 function(value) { 246 var globalValue = appCtxt.get(ZmSetting.MAIL_LIFETIME_JUNK_GLOBAL); 247 if (globalValue == "0") return true; 248 return ZmPref.__BY_DURATION(value, globalValue) <= 0 && ZmPref.validateLifetime(value); 249 }; 250 251 ZmPref.validateLifetimeTrash = 252 function(value) { 253 var globalValue = appCtxt.get(ZmSetting.MAIL_LIFETIME_TRASH_GLOBAL); 254 if (globalValue == "0") return true; 255 return ZmPref.__BY_DURATION(value, globalValue) <= 0 && ZmPref.validateLifetime(value); 256 }; 257 258 ZmPref.approximateLifetimeInboxRead = 259 function(value) { 260 return ZmPref.approximateLifetime("MAIL_LIFETIME_INBOX_READ", value, ZmPref.validateLifetime); 261 }; 262 263 ZmPref.approximateLifetimeInboxUnread = 264 function(value) { 265 return ZmPref.approximateLifetime("MAIL_LIFETIME_INBOX_UNREAD", value, ZmPref.validateLifetime); 266 }; 267 268 ZmPref.approximateLifetimeJunk = 269 function(value) { 270 return ZmPref.approximateLifetime("MAIL_LIFETIME_JUNK", value, ZmPref.validateLifetimeJunk, ZmPref.validateLifetime); 271 }; 272 273 ZmPref.approximateLifetimeSent = 274 function(value) { 275 return ZmPref.approximateLifetime("MAIL_LIFETIME_SENT", value, ZmPref.validateLifetime); 276 }; 277 278 ZmPref.approximateLifetimeTrash = 279 function(value) { 280 return ZmPref.approximateLifetime("MAIL_LIFETIME_TRASH", value, ZmPref.validateLifetimeTrash, ZmPref.validateLifetime); 281 }; 282 283 ZmPref.approximateLifetime = 284 function(prefId, duration, validateFunc1/*, ..., validateFuncN*/) { 285 // convert durations to seconds 286 var values = [].concat(ZmPref.SETUP[prefId].options); 287 for (var i = 0; i < values.length; i++) { 288 var value = values[i]; 289 values[i] = ZmPref.__DUR2SECS(value != "0" ? value+"d" : value); 290 } 291 values.sort(ZmPref.__BY_NUMBER); 292 293 // remove invalid options 294 valuesLoop: for (var i = values.length - 1; i >= 0; i--) { 295 for (var j = 2; j < arguments.length; j++) { 296 var validateFunc = arguments[j]; 297 var value = ZmPref.__SECS2DUR(values[i]); 298 if (!validateFunc(value)) { 299 values.pop(); 300 continue valuesLoop; 301 } 302 } 303 break; 304 } 305 306 // if zero, the closest match is the greatest 307 var seconds; 308 if (duration == "0") { 309 seconds = values[values.length - 1]; 310 } 311 312 // approximate to closest number of seconds 313 else { 314 seconds = ZmPref.approximateValue(values, ZmPref.__DUR2SECS(duration+"d")); 315 } 316 317 // convert back to duration 318 duration = ZmPref.__SECS2DUR(seconds); 319 return duration != "0" ? parseInt(duration, 10) : 0; 320 }; 321 322 ZmPref.markMsgReadDisplay = 323 function(value) { 324 return (value > 0) ? 1 : value; 325 }; 326 327 ZmPref.markMsgReadValue = 328 function(value) { 329 if (value == ZmSetting.MARK_READ_TIME) { 330 var inputId = DwtId.makeId(ZmId.WIDGET_INPUT, ZmId.OP_MARK_READ); 331 var input = DwtControl.fromElementId(inputId); 332 if (input) { 333 return input.getValue() || ZmSetting.MARK_READ_NOW; 334 } 335 } 336 return value; 337 }; 338 339 ZmPref.setFormValue = 340 function(pref, value) { 341 var app = appCtxt.getApp(ZmApp.PREFERENCES); 342 var section = ZmPref.getPrefSectionWithPref(pref); 343 if (app && section) { 344 var page = app.getPreferencesPage(section.id); 345 if (page) page.setFormValue(pref, value); 346 } 347 }; 348 349 ZmPref.getFormValue = 350 function(pref) { 351 var app = appCtxt.getApp(ZmApp.PREFERENCES); 352 var section = ZmPref.getPrefSectionWithPref(pref); 353 if (app && section) { 354 var page = app.getPreferencesPage(section.id); 355 if (page) return page.getFormValue(pref); 356 } 357 }; 358 359 ZmPref.setIncludeOrig = 360 function(pref, value, list) { 361 362 pref.setValue(value); 363 pref.origValue = pref.copyValue(); 364 var settings = [ZmSetting.REPLY_INCLUDE_WHAT, ZmSetting.REPLY_USE_PREFIX, ZmSetting.REPLY_INCLUDE_HEADERS]; 365 var settingsHash = AjxUtil.arrayAsHash(settings); 366 var mainSetting = ZmSetting.REPLY_INCLUDE_ORIG; 367 if (!settingsHash[pref.id]) { 368 settings = [ZmSetting.FORWARD_INCLUDE_WHAT, ZmSetting.FORWARD_USE_PREFIX, ZmSetting.FORWARD_INCLUDE_HEADERS]; 369 mainSetting = ZmSetting.FORWARD_INCLUDE_ORIG; 370 } 371 372 var values = AjxUtil.map(settings, function(setting) { return appCtxt.get(setting); }); 373 var key = (values[0] == ZmSetting.INC_NONE || values[0] == ZmSetting.INC_ATTACH) ? values[0] : values.join("|"); 374 var newValue = ZmMailApp.INC_MAP_REV[key]; 375 var prefToChange = appCtxt.getSettings().getSetting(mainSetting); 376 prefToChange.setValue(newValue); 377 list.push(prefToChange); 378 }; 379 380 ZmPref.addOOOVacationExternalPrefToList = function(list, aPrefName, aNewPrefValue){ 381 var lastPrefValue = appCtxt.get(aPrefName); // prefvalue before saving .. 382 if (lastPrefValue !== aNewPrefValue) { // donot add pref to list if pref value is not changed 383 prefToAdd = appCtxt.getSettings().getSetting(aPrefName); 384 prefToAdd.setValue(aNewPrefValue); 385 list.push(prefToAdd); 386 } 387 }; 388 389 /** 390 * On saving, for OOO vacation external reply, depending upon the option that the user has chosen in 391 * external select dropdown, we add the relevant pref that maps to the selected option in dropdown to the list that constructs the request. 392 */ 393 ZmPref.addOOOVacationExternalPrefOnSave = function(pref, value, list, viewPage) { 394 395 var externalSelect, 396 selectedText; 397 398 ZmPref.addOOOVacationExternalPrefToList(list, ZmSetting.VACATION_EXTERNAL_SUPPRESS, value); 399 externalSelect = viewPage.getFormObject(ZmSetting.VACATION_EXTERNAL_SUPPRESS); 400 selectedText = externalSelect.getText(); 401 402 if (selectedText.indexOf(ZmMsg.vacationExternalReplySuppress) === -1) { //In external select, first three options are selected . 403 if (selectedText === ZmMsg.vacationExternalAllStandard) { //first option is selected .. 404 ZmPref.addOOOVacationExternalPrefToList(list, ZmSetting.VACATION_EXTERNAL_MSG_ENABLED, false); 405 return; 406 } 407 if (selectedText === ZmMsg.vacationExternalAllCustom) { //second option ALL is selected .. 408 ZmPref.addOOOVacationExternalPrefToList(list, ZmSetting.VACATION_EXTERNAL_TYPE, 'ALL'); 409 } 410 if (selectedText === ZmMsg.vacationExternalAllExceptABCustom) { //third option ALLNOTINAB is selected .. 411 ZmPref.addOOOVacationExternalPrefToList(list, ZmSetting.VACATION_EXTERNAL_TYPE, 'ALLNOTINAB'); 412 } 413 ZmPref.addOOOVacationExternalPrefToList(list, ZmSetting.VACATION_EXTERNAL_MSG_ENABLED, true); 414 } 415 }; 416 417 /* For OOO section, this method is called only for the first time after reloading . 418 When OOO section loads, depending upon which OOO external select option the user has earlier saved in preferences, 419 we show the relevant OOO vacation external select option, and make the external text area hide /show correspondingly. 420 We get the value of OOO vacation external pref from appCtxt and then proceed. 421 */ 422 ZmPref.initOOOVacationExternalSuppress = function() { 423 var section = ZmPref.getPrefSectionWithPref(ZmSetting.VACATION_EXTERNAL_MSG); 424 var view = appCtxt.getApp(ZmApp.PREFERENCES).getPrefController().getPrefsView().getView(section.id); 425 var externalTextArea = view.getFormObject(ZmSetting.VACATION_EXTERNAL_MSG); 426 var externalSelect = view.getFormObject(ZmSetting.VACATION_EXTERNAL_SUPPRESS); 427 if(appCtxt.get(ZmSetting.VACATION_EXTERNAL_SUPPRESS)){ // when last option is saved in preferences 428 externalTextArea.setVisible(false); 429 return; 430 } 431 if(!appCtxt.get(ZmSetting.VACATION_EXTERNAL_MSG_ENABLED)){ // handle when 1st option is selected 432 externalSelect.setSelected(0); 433 externalTextArea.setVisible(false); 434 return; 435 } 436 var stringToOptionValue = { 437 'ALL' : 1, 438 'ALLNOTINAB' : 2 439 }; 440 externalSelect.setSelected(stringToOptionValue[appCtxt.get(ZmSetting.VACATION_EXTERNAL_TYPE)]); 441 }; 442 443 ZmPref.initIncludeWhat = 444 function(select, value) { 445 // wait until prefix/headers checkboxes have been created 446 AjxTimedAction.scheduleAction(new AjxTimedAction(this, function() { 447 ZmPref._showIncludeOptions(select, (value == ZmSetting.INC_BODY || value == ZmSetting.INC_SMART)); 448 }), 100); 449 }; 450 451 ZmPref.onChangeIncludeWhat = 452 function(ev) { 453 var nv = ev._args.newValue; 454 var ov = ev._args.oldValue; 455 var newAllowOptions = (nv == ZmSetting.INC_BODY || nv == ZmSetting.INC_SMART); 456 var oldAllowOptions = (ov == ZmSetting.INC_BODY || ov == ZmSetting.INC_SMART); 457 if (newAllowOptions != oldAllowOptions) { 458 ZmPref._showIncludeOptions(ev._args.selectObj, newAllowOptions); 459 } 460 }; 461 462 ZmPref._showIncludeOptions = 463 function(select, show) { 464 var optionIds = (select._name == ZmSetting.REPLY_INCLUDE_WHAT) ? 465 [ZmSetting.REPLY_USE_PREFIX, ZmSetting.REPLY_INCLUDE_HEADERS] : 466 [ZmSetting.FORWARD_USE_PREFIX, ZmSetting.FORWARD_INCLUDE_HEADERS]; 467 for (var i = 0; i < optionIds.length; i++) { 468 var cbox = select.parent._dwtObjects[optionIds[i]]; 469 if (cbox) { 470 cbox.setVisible(show); 471 } 472 } 473 }; 474 475 ZmPref.getSendToFiltersActive = 476 function(ev, callback) { 477 if (ev.target.checked) { 478 if (callback) 479 callback.run(false); 480 return false; 481 } 482 AjxDispatcher.run("GetFilterController").hasOutgoingFiltersActive(callback); 483 }; 484 485 ZmPref.onChangeConfirm = 486 function(confirmMsg, showIfCallback, useCallback, revertCallback, ev) { 487 var show = false; 488 var callback = useCallback ? new AjxCallback(this, ZmPref._showOnChangeConfirm, [confirmMsg, revertCallback]) : null; 489 if (AjxUtil.isFunction(showIfCallback)) 490 show = showIfCallback(ev, callback); 491 else if (AjxUtil.isInstance(showIfCallback, AjxCallback)) 492 show = showIfCallback.run(ev, callback); 493 else 494 show = showIfCallback; 495 ZmPref._showOnChangeConfirm(confirmMsg, revertCallback, show); 496 }; 497 498 ZmPref._showOnChangeConfirm = 499 function(confirmMsg, revertCallback, show) { 500 if (show) { 501 if (show) { 502 var dialog = appCtxt.getYesNoMsgDialog(); 503 dialog.reset(); 504 dialog.setMessage(confirmMsg); 505 dialog.setButtonListener(DwtDialog.NO_BUTTON, new AjxListener(null, ZmPref._handleOnChangeConfirmNo, [revertCallback])); 506 dialog.popup(); 507 } 508 } 509 }; 510 511 ZmPref._handleOnChangeConfirmNo = 512 function(revertCallback) { 513 if (revertCallback) 514 revertCallback.run(); 515 appCtxt.getYesNoMsgDialog().popdown(); 516 }; 517 518 // Comparators 519 520 ZmPref.__BY_NUMBER = 521 function(a, b) { 522 if (a == b) return 0; 523 if (a == Math.POSITIVE_INFINITY || b == Math.NEGATIVE_INFINITY) return 1; 524 if (b == Math.POSITIVE_INFINITY || a == Math.NEGATIVE_INFINITY) return -1; 525 return Number(a) - Number(b); 526 }; 527 528 ZmPref.__BY_DURATION = 529 function(a, b) { 530 if (a == b) return 0; 531 if (a == "0") return 1; 532 if (b == "0") return -1; 533 var asecs = ZmPref.__DUR2SECS(a); 534 var bsecs = ZmPref.__DUR2SECS(b); 535 return asecs - bsecs; 536 }; 537 538 // Converters 539 540 ZmPref.__DURMULT = { "s": 1, "m": 60, "h": 3600, "d": 86400/*, "w": 604800*/ }; 541 ZmPref.__DURDIV = { /*604800: "w",*/ 86400: "d", 3600: "h", 60: "m", 1: "s" }; 542 543 ZmPref.__DUR2SECS = 544 function(duration) { 545 if (duration == "0") return Number.POSITIVE_INFINITY; 546 547 var type = duration.substring(duration.length - 1).toLowerCase(); 548 return parseInt(duration, 10) * ZmPref.__DURMULT[type]; 549 }; 550 551 ZmPref.__SECS2DUR = 552 function(seconds, type) { 553 if (seconds == Number.POSITIVE_INFINITY) return "0"; 554 555 var divisors = ZmPref.__DURDIV; 556 if (type) { 557 type = {}; 558 type[ ZmPref.__DURMULT[type] ] = type; 559 } 560 for (var divisor in divisors) { 561 var result = Math.floor(seconds / divisor); 562 if (result > 0) { 563 return [ result, divisors[divisor] ].join(""); 564 } 565 } 566 567 return "0"+type; 568 }; 569 570 // maximum value lengths 571 ZmPref.MAX_LENGTH = {}; 572 ZmPref.MAX_LENGTH[ZmSetting.INITIAL_SEARCH] = 512; 573 ZmPref.MAX_LENGTH[ZmSetting.SIGNATURE] = 1024; 574 ZmPref.MAX_LENGTH[ZmSetting.AWAY_MESSAGE] = 8192; 575 576 ZmPref.setPrefList = 577 function(prefsId, list) { 578 ZmPref[prefsId] = list; 579 }; 580 581 /** 582 * The SETUP object for a pref gets translated into a form input. 583 * Available properties are: 584 * 585 * displayName descriptive text 586 * displayFunc A function that returns the descriptive text. Only implemented for checkboxes. 587 * displayContainer type of form input: checkbox, select, input, or textarea 588 * options values for a select input 589 * displayOptions text for the select input's values 590 * validationFunction function to validate the value 591 * errorMessage message to show if validation fails 592 * precondition pref will not be displayed unless precondition is true 593 * inputId array of unique ids to be applied to input 594 */ 595 ZmPref.SETUP = {}; 596 597 ZmPref.registerPref = 598 function(id, params) { 599 ZmPref.SETUP[id] = params; 600 }; 601 602 /** Clears all of the preference sections. */ 603 ZmPref.clearPrefSections = 604 function() { 605 ZmPref._prefSectionMap = {}; 606 ZmPref._prefSectionArray = null; 607 }; 608 609 /** 610 * Registers a preferences section ("tab"). 611 * <p> 612 * The <code>params</code> argument can have the following properties: 613 * 614 * @param title [string] The section title. 615 * @param templateId [string] (Optional) The template associated to this 616 * section. If not specified, the id is used. 617 * Note: The default template base is: 618 * "prefs.Pages#". 619 * @param priority [int] The section priority used when determining 620 * the order of the sections. 621 * @param precondition [any] (Optional) Specifies the precondition 622 * under which this section is shown. 623 * @param prefs [Array] List of preferences that appear in this 624 * section. 625 * @param manageChanges [boolean] Determines whether this section manages 626 * its own pref changes. If true, then 627 * ZmPrefView#getChangedPrefs will not query 628 * the section for changes. 629 * @param manageDirty [boolean] Determines whether this section manages 630 * its "dirty" state (i.e. whether any prefs 631 * have changed values). If true, then 632 * ZmPrefView#getChangedPrefs will call 633 * isDirty() on the section view. 634 */ 635 ZmPref.registerPrefSection = 636 function(id, params) { 637 if (!id || !params) { return; } 638 639 // set template for section 640 var templateId = params.templateId || id; 641 if (!templateId.match(/#/)) { 642 templateId = ["prefs.Pages",templateId].join("#"); 643 } 644 params.templateId = templateId; 645 params.id = id; 646 647 // save section 648 appCtxt.set(ZmSetting.PREF_SECTIONS, params, id); 649 ZmPref._prefSectionArray = null; 650 }; 651 652 ZmPref.unregisterPrefSection = 653 function(id) { 654 appCtxt.set(ZmSetting.PREF_SECTIONS, null, id); 655 ZmPref._prefSectionArray = null; 656 }; 657 658 /** Returns the pref sections map. */ 659 ZmPref.getPrefSectionMap = 660 function() { 661 return appCtxt.get(ZmSetting.PREF_SECTIONS); 662 }; 663 664 /** Returns a sorted array of pref sections (based on priority). */ 665 ZmPref.getPrefSectionArray = 666 function() { 667 if (!ZmPref._prefSectionArray) { 668 ZmPref._prefSectionArray = []; 669 var prefSectionMap = appCtxt.get(ZmSetting.PREF_SECTIONS); 670 for (var id in prefSectionMap) { 671 ZmPref._prefSectionArray.push(prefSectionMap[id]); 672 } 673 ZmPref._prefSectionArray.sort(ZmPref.__BY_PRIORITY); 674 } 675 return ZmPref._prefSectionArray; 676 }; 677 678 /** Returns the section that contains the specified pref. */ 679 ZmPref.getPrefSectionWithPref = 680 function(prefId) { 681 var prefSectionMap = appCtxt.get(ZmSetting.PREF_SECTIONS); 682 for (var sectionId in prefSectionMap) { 683 var section = prefSectionMap[sectionId]; 684 if (section.prefs == null) continue; 685 686 for (var i = 0; i < section.prefs.length; i++) { 687 if (section.prefs[i] == prefId) { 688 return section; 689 } 690 } 691 } 692 return null; 693 }; 694 695 // Make sure the pref sections are init'd 696 ZmPref.clearPrefSections(); 697 698 // 699 // Private functions 700 // 701 702 ZmPref.__BY_PRIORITY = 703 function(a, b) { 704 return Number(a.priority) - Number(b.priority); 705 }; 706 707 ZmPref.regenerateSignatureEditor = 708 function( control ) { 709 var signaturePage = control.parent; 710 var valueEl = document.getElementById(signaturePage._htmlElId + "_SIG_EDITOR"); 711 var htmlEditor = new ZmHtmlEditor({ 712 parent: signaturePage, 713 parentElement: valueEl.parentNode, 714 textAreaId: "TEXTAREA_SIGNATURE", 715 autoFocus: true, 716 attachmentCallback: 717 signaturePage._insertImagesListener.bind(signaturePage) 718 }); 719 valueEl.parentNode.removeChild(valueEl); 720 signaturePage._sigEditor = htmlEditor; 721 signaturePage._populateSignatures(); 722 }; 723 724 ZmPref._normalizeFontId = function(id, dontFallback) { 725 var oldid = id; 726 id = id.replace(/,\s/g,",").replace(/'/g,"").toLowerCase(); // Make sure all ids that are supposed to be found in ZmPref.FONT_FAMILY are actually found 727 if (!dontFallback) { 728 var map = ZmPref.FONT_FAMILY; 729 if (map && !map[id]) { 730 var keys = AjxUtil.keys(map); 731 if (keys.length) { 732 var splitId = id.split(","); // e.g. ["times new roman","helvetica"] 733 for (var i=0; i<splitId.length; i++) { // Loop over input font names 734 for (var j=0; j<keys.length; j++) { // Loop over candidate styles, e.g. ["arial,sans-serif","times new roman,serif"] 735 if (keys[j].indexOf(splitId[i]) != -1) { 736 return keys[j]; 737 } 738 } 739 } 740 return keys[0]; 741 } 742 } 743 } 744 return id; 745 }; 746 ZmPref._normalizeFontName = function(fontId) { 747 return ZmPref.FONT_FAMILY[ZmPref._normalizeFontId(fontId)].name; 748 }; 749 ZmPref._normalizeFontValue = function(fontId) { 750 return ZmPref.FONT_FAMILY[ZmPref._normalizeFontId(fontId)].value; 751 }; 752 753 ZmPref.handleOOOVacationExternalOptionChange = function(ev) { 754 var section = ZmPref.getPrefSectionWithPref(ZmSetting.VACATION_EXTERNAL_MSG); 755 var view = appCtxt.getApp(ZmApp.PREFERENCES).getPrefController().getPrefsView().getView(section.id); 756 var externalTextArea = view.getFormObject(ZmSetting.VACATION_EXTERNAL_MSG); 757 var externalSelect = view.getFormObject(ZmSetting.VACATION_EXTERNAL_SUPPRESS); 758 var selectedIndex = externalSelect.getSelectedOption().getItem()._optionIndex; 759 760 if (selectedIndex === 0 || selectedIndex === 3 ) { 761 externalTextArea.setVisible(false); 762 } else { 763 externalTextArea.setVisible(true); 764 } 765 }; 766 767 // Keep NOTIF_ENABLED updated based on whether NOTIF_ADDRESS has a value 768 ZmPref.setMailNotificationAddressValue = function(pref, value, list, viewPage) { 769 770 pref.setValue(value); 771 list.push(pref); 772 773 var notifEnabledSetting = appCtxt.getSettings().getSetting(ZmSetting.NOTIF_ENABLED), 774 hasValue = !!value; 775 776 if (notifEnabledSetting.getValue() !== hasValue) { 777 notifEnabledSetting.setValue(hasValue); 778 list.push(notifEnabledSetting); 779 } 780 }; 781 782 ZmPref.FONT_FAMILY = {}; 783 (function() { 784 var KEYS = [ "fontFamilyIntl", "fontFamilyBase" ]; 785 var i, j, key, value, name; 786 for (j = 0; j < KEYS.length; j++) { 787 for (i = 1; value = AjxMsg[KEYS[j]+i+".css"]; i++) { 788 if (value.match(/^#+$/)) break; 789 value = ZmPref._normalizeFontId(value,true); 790 name = AjxMsg[KEYS[j]+i+".display"]; 791 ZmPref.FONT_FAMILY[value] = {name:name, value:value}; 792 } 793 } 794 })(); 795