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 25 /** 26 * Creates a select element. 27 * @constructor 28 * @class 29 * Widget to replace the native select element. 30 * <p> 31 * Note: Currently this does not support multiple selection. 32 * 33 * @param {hash} params a hash of parameters 34 * @param {DwtComposite} params.parent the parent widget 35 * @param {array} params.options a list of options. This can be either an array of {@link DwtSelectOption} or {String} objects. 36 * @param {string} params.className the CSS class 37 * @param {constant} params.posStyle the positioning style (see {@link DwtControl}) 38 * @param {boolean} [layout=true] layout to use: DwtMenu.LAYOUT_STACK, DwtMenu.LAYOUT_CASCADE or DwtMenu.LAYOUT_SCROLL. A value of [true] defaults to DwtMenu.LAYOUT_CASCADE and a value of [false] defaults to DwtMenu.LAYOUT_STACK. 39 * 40 * @extends DwtButton 41 * 42 * TODO: add option to keep options sorted by display text 43 */ 44 DwtSelect = function(params) { 45 46 if (arguments.length == 0) { return; } 47 48 params = Dwt.getParams(arguments, DwtSelect.PARAMS); 49 params.className = params.className || "ZSelect"; 50 params.posStyle = params.posStyle || Dwt.STATIC_STYLE; 51 this._legendId = params.legendId; 52 DwtButton.call(this, params); 53 54 var events = AjxEnv.isIE ? [DwtEvent.ONMOUSEDOWN, DwtEvent.ONMOUSEUP] : 55 [DwtEvent.ONMOUSEDOWN, DwtEvent.ONMOUSEUP, DwtEvent.ONMOUSEOVER, DwtEvent.ONMOUSEOUT]; 56 this._setEventHdlrs(events); 57 this._hasSetMouseEvents = true; 58 59 // initialize some variables 60 this._currentSelectedOption = null; 61 this._options = new AjxVector(); 62 this._optionValuesToIndices = {}; 63 this._selectedValue = this._selectedOption = null; 64 this._maxRows = params.maxRows || 0; 65 this._layout = params.layout; 66 this._congruent = params.congruent; 67 this._hrCount = 0; 68 69 // add options 70 var options = params.options; 71 if (options) { 72 for (var i = 0; i < options.length; ++i) { 73 this.addOption(options[i]); 74 } 75 } 76 77 // setup display 78 this.setDropDownImages("SelectPullDownArrow", // normal 79 "SelectPullDownArrowDis", // disabled 80 "SelectPullDownArrow", // hover 81 "SelectPullDownArrow"); // down 82 83 // add listeners 84 this._menuCallback = new AjxListener(this, this._createMenu); 85 this.setMenu(this._menuCallback, true); 86 }; 87 88 DwtSelect.PARAMS = ["parent", "options", "style", "className", "layout"]; 89 90 DwtSelect.prototype = new DwtButton; 91 DwtSelect.prototype.constructor = DwtSelect; 92 93 DwtSelect.prototype.isDwtSelect = true; 94 DwtSelect.prototype.toString = function() { return "DwtSelect"; }; 95 96 DwtSelect.prototype.role = 'combobox'; 97 98 // 99 // Constants 100 // 101 102 /** 103 * This template is only used for the auto-sizing of the select width. 104 * 105 * @private 106 */ 107 DwtSelect._CONTAINER_TEMPLATE = "dwt.Widgets#ZSelectAutoSizingContainer"; 108 109 // 110 // Data 111 // 112 113 // static 114 115 /** 116 * This keeps track of all instances out there 117 * 118 * @private 119 */ 120 DwtSelect._objectIds = [null]; 121 122 // templates 123 124 DwtSelect.prototype.TEMPLATE = "dwt.Widgets#ZSelect"; 125 126 // 127 // Public methods 128 // 129 130 // static 131 132 DwtSelect.getObjectFromElement = 133 function(element) { 134 return element && element.dwtObj 135 ? AjxCore.objectWithId(element.dwtObj) : null; 136 }; 137 138 // other 139 140 /** 141 * Adds an option. 142 * 143 * @param {string|DwtSelectOption|DwtSelectOptionData} option a {String} for the option value or the {@link DwtSelectOption} object 144 * @param {boolean} [selected] indicates whether option should be the selected option 145 * @param {Object} value if the option parameter is a {@link DwtSelectOption}, this will override the value already set in the option. 146 * @param {String} image (optional) 147 * @return {number} a handle to the newly added option 148 * 149 * TODO: support adding at an index 150 */ 151 DwtSelect.prototype.addOption = 152 function(option, selected, value, image) { 153 154 if (!option) { return -1; } 155 image = image || null; 156 157 var opt = null; 158 var val = null; 159 var id = null; 160 if (typeof(option) == 'string') { 161 val = value != null ? value : option; 162 opt = new DwtSelectOption(val, selected, option, this, null, image); 163 } else { 164 if (option instanceof DwtSelectOption) { 165 opt = option; 166 if (value) { 167 opt.setValue(value); 168 } 169 selected = opt.isSelected(); 170 } else if(option instanceof DwtSelectOptionData || option.value != null) { 171 val = value != null ? value : option.value; 172 opt = new DwtSelectOption(val, option.isSelected, option.displayValue, this, null, option.image, option.selectedValue, false, option.extraData, option.id); 173 selected = Boolean(option.isSelected); 174 id = option.id; 175 } else { 176 return -1; 177 } 178 } 179 180 this._options.add(opt); 181 if (this._options.size() == 1 || selected) { 182 this._setSelectedOption(opt); 183 } 184 185 // Insert the option into the table that's below the button. 186 // This is what gives the button the same size as the select menu. 187 var table = this._pseudoItemsEl; 188 var row = table.insertRow(-1); 189 var cell = row.insertCell(-1); 190 cell.className = 'ZSelectPseudoItem'; 191 cell.innerHTML = [ 192 "<div class='ZWidgetTitle'>", 193 AjxStringUtil.htmlEncode(opt.getDisplayValue()), 194 "</div>" 195 ].join(""); 196 197 this.fixedButtonWidth(); //good to call always to prevent future bugs due to the vertical space. 198 199 // Register listener to create new menu. 200 this.setMenu(this._menuCallback, true); 201 202 // return the index of the option. 203 this._optionValuesToIndices[opt.getValue()] = this._options.size() - 1; 204 return (this._options.size() - 1); 205 }; 206 207 DwtSelect.prototype.addHR = 208 function() { 209 opt = new DwtSelectOption("hr" + this._hrCount.toString(), false, "", this, null, null, null, true); 210 this._hrCount++; 211 this._options.add(opt); 212 }; 213 214 /** 215 * Removes an option. 216 * 217 * @param {DwtSelectOption} option option to remove 218 * 219 * @return {number} index of the option that was removed, or -1 if there was an error 220 */ 221 DwtSelect.prototype.removeOption = 222 function(option) { 223 224 if (!option) { return -1; } 225 226 // Register listener to create new menu. 227 this.setMenu(this._menuCallback, true); 228 229 this._options.remove(option); 230 var size = this._options.size(); 231 232 var value = option.getValue(); 233 var index = this._optionValuesToIndices[value]; 234 if (index != null) { 235 this._pseudoItemsEl.deleteRow(index); 236 if (this._selectedOption == option) { 237 if (size > 0) { 238 var newSelIndex = (index >= size) ? size - 1 : index; 239 this._setSelectedOption(this._options.get(newSelIndex)); 240 } 241 this.removeAttribute('aria-activedescendant'); 242 } 243 this.fixedButtonWidth(); //good to call always to prevent future bugs due to the vertical space. 244 } 245 246 delete this._optionValuesToIndices[value]; 247 for (var i = index; i < size; i++) { 248 var option = this._options.get(i); 249 this._optionValuesToIndices[option.getValue()] = i; 250 } 251 252 return index; 253 }; 254 255 /** 256 * Removes an option based on its value. 257 * 258 * @param {string} value value of the option to remove 259 * 260 * @return {number} index of the option that was removed, or -1 if there was an error 261 */ 262 DwtSelect.prototype.removeOptionWithValue = 263 function(value) { 264 265 var option = this.getOptionWithValue(value); 266 return option ? this.removeOption(option) : -1; 267 }; 268 269 DwtSelect.prototype.popup = 270 function() { 271 var menu = this.getMenu(); 272 if (!menu) { return; } 273 274 var selectElement = this._selectEl; 275 var selectBounds = Dwt.getBounds(selectElement); 276 277 // since buttons are often absolutely positioned, and menus aren't, we need x,y relative to window 278 var verticalBorder = (selectElement.style.borderLeftWidth == "") ? 0 : parseInt(selectElement.style.borderLeftWidth); 279 var horizontalBorder = (selectElement.style.borderTopWidth == "") ? 0 : parseInt(selectElement.style.borderTopWidth); 280 horizontalBorder += (selectElement.style.borderBottomWidth == "") ? 0 : parseInt(selectElement.style.borderBottomWidth); 281 282 var selectLocation = Dwt.toWindow(selectElement, 0, 0); 283 var x = selectLocation.x + verticalBorder; 284 var y = selectLocation.y + selectBounds.height + horizontalBorder; 285 menu.popup(0, x, y); 286 if (this._currentSelectedOption) { 287 menu.setSelectedItem(this._currentSelectedOption.getItem()); 288 } 289 }; 290 291 /** 292 * Renames an option. 293 * 294 * @param {Object} value the value of the option to rename 295 * @param {string} newValue the new display value 296 */ 297 DwtSelect.prototype.rename = 298 function(value, newValue) { 299 300 var option = this.getOptionWithValue(value); 301 if (!option) { return; } 302 option._displayValue = newValue; 303 304 if (this._selectedOption && (this._selectedOption._value == value)) { 305 this.setText(AjxStringUtil.htmlEncode(newValue)); 306 } 307 308 // Register listener to create new menu. 309 this.setMenu(this._menuCallback, true); 310 }; 311 312 /** 313 * Enables or disables an option. 314 * 315 * @param {Object} value the value of the option to enable/disable 316 * @param {boolean} enabled if <code>true</code>, enable the option 317 */ 318 DwtSelect.prototype.enableOption = 319 function(value, enabled) { 320 var option = this.getOptionWithValue(value); 321 if (!option) { return; } 322 if (option.enabled != enabled) { 323 option.enabled = enabled; 324 var item = option.getItem(); 325 if (item) { 326 item.setEnabled(enabled); 327 } 328 } 329 }; 330 331 /** 332 * Clears the options. 333 * 334 */ 335 DwtSelect.prototype.clearOptions = 336 function() { 337 var opts = this._options.getArray(); 338 for (var i = 0; i < opts.length; ++i) { 339 opts[i] = null; 340 } 341 this._options.removeAll(); 342 this._optionValuesToIndices = null; 343 this._optionValuesToIndices = []; 344 this._selectedValue = null; 345 this._selectedOption = null; 346 this._currentSelectedOption = null; 347 if (this._pseudoItemsEl) { 348 try { 349 this._pseudoItemsEl.innerHTML = ""; //bug 81504 350 } 351 catch (e) { 352 //do nothing - this happens in IE for some reason. Stupid IE. "Unknown runtime error". 353 } 354 } 355 }; 356 357 /** 358 * Sets the select name. 359 * 360 * @param {string} name the name 361 */ 362 DwtSelect.prototype.setName = 363 function(name) { 364 this._name = name; 365 }; 366 367 /** 368 * Gets the select name. 369 * 370 * @return {string} the name 371 */ 372 DwtSelect.prototype.getName = 373 function() { 374 return this._name; 375 }; 376 377 /** 378 * Sets the selected value. 379 * 380 * @param {Object} optionValue the value of the option to select 381 */ 382 DwtSelect.prototype.setSelectedValue = 383 function(optionValue) { 384 var index = this._optionValuesToIndices[optionValue]; 385 if (index != null) { 386 this.setSelected(index); 387 } 388 }; 389 390 /** 391 * Sets the option as the selected option. 392 * 393 * @param {number} optionHandle a handle to the option 394 * 395 * @see #addOption 396 */ 397 DwtSelect.prototype.setSelected = 398 function(optionHandle) { 399 var optionObj = this.getOptionWithHandle(optionHandle); 400 this.setSelectedOption(optionObj); 401 }; 402 403 /** 404 * Gets the option count. 405 * 406 * @return {number} the option count 407 */ 408 DwtSelect.prototype.getOptionCount = 409 function() { 410 return this._options.size(); 411 }; 412 413 /** 414 * Gets the options. 415 * 416 * @return {AjxVector} a vector of {@link DwtSelectOption} objects 417 */ 418 DwtSelect.prototype.getOptions = 419 function() { 420 return this._options; 421 }; 422 423 /** 424 * Gets the option . 425 * 426 * @param {number} optionHandle a handle to the option 427 * @return {DwtSelectOption} the option 428 * @see #addOption 429 */ 430 DwtSelect.prototype.getOptionWithHandle = 431 function(optionHandle) { 432 return this._options.get(optionHandle); 433 }; 434 435 DwtSelect.prototype.getOptionAtIndex = DwtSelect.prototype.getOptionWithHandle; 436 437 /** 438 * Gets the index for a given value. 439 * 440 * @param {Object} value the value 441 * @return {number} the index 442 */ 443 DwtSelect.prototype.getIndexForValue = 444 function(value) { 445 return this._optionValuesToIndices[value]; 446 }; 447 448 /** 449 * Gets the option for a given value. 450 * 451 * @param {Object} optionValue the value 452 * @return {DwtSelectOption} the option 453 */ 454 DwtSelect.prototype.getOptionWithValue = 455 function(optionValue) { 456 var index = this._optionValuesToIndices[optionValue]; 457 var option = null; 458 if (index != null) { 459 option = this.getOptionWithHandle(index); 460 } 461 return option; 462 }; 463 464 /** 465 * Sets the selected option. 466 * 467 * @param {Object} optionObj the object 468 */ 469 DwtSelect.prototype.setSelectedOption = 470 function(optionObj) { 471 if (optionObj) { 472 this._setSelectedOption(optionObj); 473 } 474 }; 475 476 /** 477 * Gets the selected value. 478 * 479 * @return {Object} the value 480 */ 481 DwtSelect.prototype.getValue = 482 function() { 483 return this._selectedValue; 484 }; 485 486 /** 487 * Gets the selected option. 488 * 489 * @return {DwtSelectOption} the selected option 490 */ 491 DwtSelect.prototype.getSelectedOption = 492 function() { 493 return this._selectedOption; 494 }; 495 496 /** 497 * Gets the selected option index. 498 * 499 * @return {number} the selected option index 500 */ 501 DwtSelect.prototype.getSelectedIndex = 502 function() { 503 return this.getIndexForValue(this.getValue()); 504 }; 505 506 /** 507 * Adds a change listener. 508 * 509 * @param {AjxListener} listener the listener 510 */ 511 DwtSelect.prototype.addChangeListener = 512 function(listener) { 513 this.addListener(DwtEvent.ONCHANGE, listener); 514 }; 515 516 /** 517 * Gets the count of options. 518 * 519 * @return {number} the count 520 */ 521 DwtSelect.prototype.size = 522 function() { 523 return this._options.size(); 524 }; 525 526 /** 527 * Disables the select. 528 */ 529 DwtSelect.prototype.disable = 530 function() { 531 this.setEnabled(false); 532 }; 533 534 /** 535 * Enables the select. 536 */ 537 DwtSelect.prototype.enable = 538 function() { 539 this.setEnabled(true); 540 }; 541 542 DwtSelect.prototype.setImage = 543 function(imageInfo) { 544 // dont call DwtButton base class! 545 DwtLabel.prototype.setImage.call(this, imageInfo); 546 }; 547 548 DwtSelect.prototype.setText = 549 function(text) { 550 // dont call DwtButton base class! 551 DwtLabel.prototype.setText.call(this, text); 552 }; 553 554 DwtSelect.prototype.dispose = 555 function() { 556 this._selectEl = null; 557 if (this._pseudoItemsEl) { 558 this._pseudoItemsEl.outerHTML = ""; 559 this._pseudoItemsEl = null; 560 } 561 this._containerEl = null; 562 563 DwtButton.prototype.dispose.call(this); 564 565 if (this._internalObjectId) { 566 DwtSelect._unassignId(this._internalObjectId); 567 } 568 }; 569 570 // 571 // Protected methods 572 // 573 574 // static 575 576 DwtSelect._assignId = 577 function(anObject) { 578 var myId = DwtSelect._objectIds.length; 579 DwtSelect._objectIds[myId]= anObject; 580 return myId; 581 }; 582 583 DwtSelect._getObjectWithId = 584 function(anId) { 585 return DwtSelect._objectIds[anId]; 586 }; 587 588 DwtSelect._unassignId = 589 function(anId) { 590 DwtSelect._objectIds[anId] = null; 591 }; 592 593 // other 594 595 /* use this in case you want the button to take as little space as needed, and not be aligned with the size of the drop-down. 596 Especially useful in cases where we mess up the button (remove the text) such as in ZmFreeBusySchedulerView 597 */ 598 DwtSelect.prototype.dynamicButtonWidth = 599 function() { 600 this._isDynamicButtonWidth = true; //if this is set, set this so fixedButtonWidth doesn't change this. 601 this._selectEl.style.width = "auto"; //set to default in case fixedButtonWidth was called before setting it explicitely. 602 this._pseudoItemsEl.style.display = "none"; 603 }; 604 605 /* 606 * Use this in case you want the select to be as wide as the widest option and 607 * the options hidden so they don't overflow outside containers. 608 */ 609 DwtSelect.prototype.fixedButtonWidth = 610 function(){ 611 if (this._isDynamicButtonWidth) { 612 return; 613 } 614 this._pseudoItemsEl.style.display = "block"; //in case this function was called before. This will fix the width of the _selectEl to match the options. 615 var elm = this._selectEl; 616 var width = elm.offsetWidth; 617 //offsetWidth is 0 if some parent (ancestor) has display:none which is the case only in Prefs pages when the select is setup. 618 //don't set width to 0px in this case as it acts inconsistent - filling the entire space. Better to keep it just dynamic. 619 if (width) { 620 elm.style.width = width + "px"; 621 } 622 this._pseudoItemsEl.style.display = "none"; 623 }; 624 625 DwtSelect.prototype._createHtmlFromTemplate = 626 function(templateId, data) { 627 // wrap params 628 var containerTemplateId = DwtSelect._CONTAINER_TEMPLATE; 629 var containerData = { 630 id: data.id, 631 selectTemplateId: templateId || this.TEMPLATE, 632 selectData: data 633 }; 634 635 // generate html 636 DwtButton.prototype._createHtmlFromTemplate.call(this, containerTemplateId, containerData); 637 this._selectEl = document.getElementById(data.id+"_select_container"); 638 this._pseudoItemsEl = document.getElementById(data.id+"_pseudoitems_container"); 639 // this has to be block for it to affect the layout. it is not seen because its visibility hidden for the TDs 640 // inside, and also "overflow:hidden" (so mouse over the hidden stuff does not highlight) 641 this._pseudoItemsEl.style.display = "block"; 642 // set classes 643 var el = this._containerEl = this.getHtmlElement(); 644 this._selectEl.className = el.className; 645 Dwt.addClass(el, "ZSelectAutoSizingContainer"); 646 this.removeAttribute("style"); 647 if (AjxEnv.isIE && !AjxEnv.isIE9up) { 648 el.style.overflow = "hidden"; 649 } 650 if (this._legendId) { 651 this.setAttribute('aria-labelledby', [ this._legendId, this._textEl.id ].join(' ')); 652 } 653 }; 654 655 DwtSelect.prototype._createMenu = function() { 656 657 var menu = new DwtSelectMenu(this); 658 var mi; 659 for (var i = 0, len = this._options.size(); i < len; ++i) { 660 var option = this._options.get(i); 661 if (option._hr) { 662 mi = new DwtMenuItem({parent:menu, style:DwtMenuItem.SEPARATOR_STYLE}); 663 mi.setEnabled(false); 664 } else { 665 var mi = new DwtSelectMenuItem(menu, option.id || Dwt.getNextId(menu._htmlElId + '_option_')); 666 var image = option.getImage(); 667 if (image) { 668 mi.setImage(image); 669 } 670 var text = option.getDisplayValue(); 671 if (text) { 672 mi.setText(AjxStringUtil.htmlEncode(text)); 673 } 674 mi.setEnabled(option.enabled); 675 676 mi.addSelectionListener(new AjxListener(this, this._handleOptionSelection)); 677 mi._optionIndex = i; 678 } 679 mi._optionIndex = i; 680 option.setItem(mi); 681 } 682 683 // Accessibility 684 var select = this; 685 menu.addPopupListener(function() { 686 select.setAttribute('aria-expanded', true); 687 }); 688 menu.addPopdownListener(function() { 689 select.setAttribute('aria-expanded', false); 690 select.removeAttribute('aria-activedescendant'); 691 }); 692 693 return menu; 694 }; 695 696 DwtSelect.prototype._handleOptionSelection = 697 function(ev) { 698 var menuItem = ev.item; 699 var optionIndex = menuItem._optionIndex; 700 var opt = this._options.get(optionIndex); 701 var oldValue = this.getValue(); 702 this._setSelectedOption(opt); 703 704 // notify our listeners 705 var args = new Object(); 706 args.selectObj = this; 707 args.newValue = opt.getValue(); 708 args.oldValue = oldValue; 709 var event = DwtUiEvent.getEvent(ev); 710 event._args = args; 711 this.notifyListeners(DwtEvent.ONCHANGE, event); 712 }; 713 714 DwtSelect.prototype._setSelectedOption = 715 function(option) { 716 var displayValue = option.getSelectedValue() || option.getDisplayValue(); 717 var image = option.getImage(); 718 if (this._selectedOption != option) { 719 if (displayValue) { 720 this.setText(AjxStringUtil.htmlEncode(displayValue)); 721 } 722 this.setImage(image); 723 this._selectedValue = option._value; 724 this._selectedOption = option; 725 } 726 this._updateSelection(option); 727 728 this.autoResize(); 729 }; 730 731 DwtSelect.prototype.autoResize = 732 function() { 733 /* bug: 21041 */ 734 var divElId = this.getHtmlElement(); 735 AjxTimedAction.scheduleAction(new AjxTimedAction(this, 736 function(){ 737 var divEl = document.getElementById(divElId.id); 738 if (divEl) { 739 divEl.style.width = divEl.childNodes[0].offsetWidth || "auto"; // offsetWidth doesn't work in IE if the element or one of its parents has display:none 740 } 741 }, 200)); 742 }; 743 744 DwtSelect.prototype._updateSelection = 745 function(newOption) { 746 var currOption = this._currentSelectedOption; 747 748 if (currOption) { 749 currOption.deSelect(); 750 } 751 this._currentSelectedOption = newOption; 752 if (!newOption) { 753 return; 754 } 755 newOption.select(); 756 var menu = this.getMenu(true); 757 if (!menu) { 758 return; 759 } 760 menu.setSelectedItem(newOption.getItem()); 761 }; 762 763 // Call this function to update the rendering of the element 764 // Firefox sometimes renders the element incorrectly on certain DOM updates, so this function rectifies that 765 DwtSelect.prototype.updateRendering = 766 function() { 767 var scrollStyle = this.getScrollStyle(); 768 this.setScrollStyle(scrollStyle == Dwt.VISIBLE ? Dwt.CLIP : Dwt.VISIBLE); 769 var reset = function() { 770 try { 771 this.setScrollStyle(scrollStyle); 772 } catch(e) {} 773 }; 774 var resetAction = new AjxTimedAction(this, reset); 775 AjxTimedAction.scheduleAction(resetAction, 4); 776 }; 777 778 // Accessibility - select has role of 'combobox', so 'aria-owns' is used instead of 'aria-haspopup' 779 DwtSelect.prototype._menuAdded = function(menu) { 780 this.setAttribute('aria-owns', menu._htmlElId); 781 }; 782 783 // Accessibility - with a role of 'combobox' we need to maintain 'aria-activedescendant' 784 DwtSelect.prototype._menuItemSelected = function(menuItem) { 785 this.setAttribute('aria-activedescendant', menuItem._htmlElId); 786 }; 787 788 789 // 790 // Class 791 // 792 793 /** 794 * Greg Solovyev 2/2/2004 added this class to be able to create a list of options 795 * before creating the DwtSelect control. This is a workaround an IE bug, that 796 * causes IE to crash with error R6025 when DwtSelectOption object are added to empty DwtSelect 797 * @class 798 * @constructor 799 * 800 * @private 801 */ 802 DwtSelectOptionData = function(value, displayValue, isSelected, selectedValue, image, id, extraData) { 803 if (value == null || displayValue == null) { return null; } 804 805 this.value = value; 806 this.displayValue = displayValue; 807 this.isSelected = isSelected; 808 this.selectedValue = selectedValue; 809 this.image = image; 810 this.extraData = extraData; 811 this.id = id || Dwt.getNextId(); 812 }; 813 814 // 815 // Class 816 // 817 818 /** 819 * Creates a select option. 820 * @constructor 821 * @class 822 * This class encapsulates the option object that the {@link DwtSelect} widget uses. 823 * 824 * @param {String} value this is the value for the object, it will be returned in any onchange event 825 * @param {Boolean} selected whether or not the option should be selected to start with 826 * @param {String} displayValue the value that the user will see (HTML encoding will be done on this value internally) 827 * @param {DwtSelect} owner not used 828 * @param {String} optionalDOMId not used 829 * @param {String} [selectedValue] the text value to use when this value is the currently selected value 830 * @param {Boolean} hr True => This option will be usd to create a unselectable horizontal rule 831 * @param {Object} extraData map of extra name/value pairs 832 */ 833 DwtSelectOption = function(value, selected, displayValue, owner, optionalDOMId, image, selectedValue, hr, extraData, id) { 834 this._value = value; 835 this._selected = selected; 836 this._displayValue = displayValue; 837 this._image = image; 838 this._selectedValue = selectedValue; 839 this._hr = hr; 840 this._extraData = extraData; 841 842 this.id = id; 843 844 this._internalObjectId = DwtSelect._assignId(this); 845 this.enabled = true; 846 }; 847 848 DwtSelectOption.prototype.toString = 849 function() { 850 return "DwtSelectOption"; 851 }; 852 853 /** 854 * Sets the item. 855 * 856 * @param {DwtSelectMenuItem} menuItem the menu item 857 */ 858 DwtSelectOption.prototype.setItem = 859 function(menuItem) { 860 this._menuItem = menuItem; 861 }; 862 863 /** 864 * Gets the item. 865 * 866 * @return {DwtSelectMenuItem} the menu item 867 */ 868 DwtSelectOption.prototype.getItem = 869 function(menuItem) { 870 return this._menuItem; 871 }; 872 873 /** 874 * Gets the display value. 875 * 876 * @return {String} the display value 877 */ 878 DwtSelectOption.prototype.getDisplayValue = 879 function() { 880 return this._displayValue; 881 }; 882 883 /** 884 * Gets the image. 885 * 886 * @return {String} the image 887 */ 888 DwtSelectOption.prototype.getImage = 889 function() { 890 return this._image; 891 }; 892 893 /** 894 * Gets the selected value. 895 * 896 * @return {String} the selected value 897 */ 898 DwtSelectOption.prototype.getSelectedValue = 899 function() { 900 return this._selectedValue; 901 }; 902 903 /** 904 * Gets the value. 905 * 906 * @return {String} the value 907 */ 908 DwtSelectOption.prototype.getValue = 909 function() { 910 return this._value; 911 }; 912 913 /** 914 * Sets the value. 915 * 916 * @param {String|Number} stringOrNumber the value 917 */ 918 DwtSelectOption.prototype.setValue = 919 function(stringOrNumber) { 920 this._value = stringOrNumber; 921 }; 922 923 /** 924 * Selects the option. 925 */ 926 DwtSelectOption.prototype.select = 927 function() { 928 this._selected = true; 929 }; 930 931 /** 932 * De-selects the option. 933 */ 934 DwtSelectOption.prototype.deSelect = 935 function() { 936 this._selected = false; 937 }; 938 939 /** 940 * Checks if the option is selected. 941 * 942 * @return {Boolean} <code>true</code> if the option is selected 943 */ 944 DwtSelectOption.prototype.isSelected = 945 function() { 946 return this._selected; 947 }; 948 949 /** 950 * Gets the id. 951 * 952 * @return {String} the id 953 */ 954 DwtSelectOption.prototype.getIdentifier = 955 function() { 956 return this._internalObjectId; 957 }; 958 959 DwtSelectOption.prototype.getExtraData = 960 function(key) { 961 return this._extraData && this._extraData[key]; 962 }; 963 964 965 966 /** 967 * Creates a select menu. 968 * @constructor 969 * @class 970 * This class represents a select menu. 971 * 972 * @param {DwtComposite} parent the parent 973 * 974 * @extends DwtMenu 975 */ 976 DwtSelectMenu = function(parent) { 977 DwtMenu.call(this, {parent:parent, style:DwtMenu.DROPDOWN_STYLE, className:"DwtMenu", layout:parent._layout, 978 maxRows:parent._maxRows, congruent:parent._congruent, 979 id:Dwt.getNextId(parent.getHTMLElId() + "_Menu_")}); 980 // Dwt.getNextId should be removed once Bug 66510 is fixed 981 }; 982 DwtSelectMenu.prototype = new DwtMenu; 983 DwtSelectMenu.prototype.constructor = DwtSelectMenu; 984 985 DwtSelectMenu.prototype.TEMPLATE = "dwt.Widgets#ZSelectMenu"; 986 987 DwtSelectMenu.prototype.toString = 988 function() { 989 return "DwtSelectMenu"; 990 }; 991 992 /** 993 * Creates a select menu item. 994 * @constructor 995 * @class 996 * This class represents a menu item. 997 * 998 * @param {DwtComposite} parent the parent 999 * 1000 * @extends DwtMenuItem 1001 */ 1002 DwtSelectMenuItem = function(parent, id) { 1003 DwtMenuItem.call(this, {parent:parent, style:DwtMenuItem.SELECT_STYLE, className:"ZSelectMenuItem", id: id}); 1004 }; 1005 DwtSelectMenuItem.prototype = new DwtMenuItem; 1006 DwtSelectMenuItem.prototype.constructor = DwtSelectMenuItem; 1007 1008 DwtSelectMenuItem.prototype.TEMPLATE = "dwt.Widgets#ZSelectMenuItem"; 1009 1010 DwtSelectMenuItem.prototype.isDwtSelectMenuItem = true; 1011 DwtSelectMenuItem.prototype.toString = function() { return "DwtSelectMenuItem"; }; 1012 1013 DwtSelectMenuItem.prototype.role = 'option'; 1014 1015 DwtLabel.prototype._textSet = function(text) {}; 1016