Source: src/main/javascript/oracle/oj/ojselectcombobox/ojselect.js

Oracle® JavaScript Extension Toolkit (JET)
1.1.2

E65298-01

/**
 * Copyright (c) 2014, Oracle and/or its affiliates.
 * All rights reserved.
 */

  /**
   * @ojcomponent oj.ojSelect
   * @augments oj.editableValue
   * @since 0.6
   *
   * @classdesc
   * <h3 id="selectOverview-section">
   *   JET Select Component
   *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#selectOverview-section"></a>
   * </h3>
   *
   * <p>Description: JET Select enhances a html select and option elements into a Select that supports
   * single-select and search filtering. Note: Muliple selection is not supported in V1.
   *
   * <p>A JET Select can be created with the following markup. By default, it creates a single-select
   * Select. 
   *
   * <pre class="prettyprint">
   * <code>
   * <select data-bind="ojComponent: {component: 'ojSelect'}">
   *     <option value="option 1">option 1</option>
   *     <option value="option 2">option 2</option>
   *     <option value="option 3">option 3</option>
   *     <option value="option 4">option 4</option>
   * </select>
   * </code></pre>
   *
   * <h3 id="touch-section">
   *   Touch End User Information
   *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#touch-section"></a>
   * </h3>
   *
   * {@ojinclude "name":"touchDoc"}
   *
   * <h3 id="keyboard-section">
   *   Keyboard End User Information
   *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#keyboard-section"></a>
   * </h3>
   * 
   * {@ojinclude "name":"keyboardDoc"}
   *
   *
   * <h3 id="rtl-section">
   *   Reading direction
   *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#rtl-section"></a>
   * </h3>
   *
   * <p>As with any JET component, in the unusual case that the directionality (LTR or RTL) changes post-init, the Select must be <code class="prettyprint">refresh()</code>ed.
   *
   *
   * <h3 id="pseudos-section">
   *   Pseudo-selectors
   *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#pseudos-section"></a>
   * </h3>
   *
   * <p>The <code class="prettyprint">:oj-select</code> pseudo-selector can be used in jQuery expressions to select JET Select.  For example:
   *
   * <pre class="prettyprint">
   * <code>$( ":oj-select" ) // selects all JET Select on the page
   * $myEventTarget.closest( ":oj-select" ) // selects the closest ancestor that is a JET Select
   * </code></pre>
   *
   * <h3 id="a11y-section">
   *   Accessibility
   *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#a11y-section"></a>
   * </h3>
   * <p>
   * It is up to the application developer to associate the label to the select component.
   * For select, you should put an <code>id</code> on the select, and then set 
   * the <code>for</code> attribute on the label to be the select's id.
   * </p>
   * <h3 id="label-section">
   *   Label and Select
   *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#label-section"></a>
   * </h3>
   * <p>
   * For accessibility, you should associate a label element with the select
   * by putting an <code>id</code> on the select, and then setting the 
   * <code>for</code> attribute on the label to be the select's id.
   * </p>
   * <p>
   * The component will decorate its associated label with required and help 
   * information, if the <code>required</code> and <code>help</code> options are set. 
   * </p>
   * <h3 id="jqui2jet-section">
   *   JET for jQuery UI developers
   *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#jqui2jet-section"></a>
   * </h3>
   *
   * <p>Event names for all JET components are prefixed with "oj", instead of component-specific prefixes like "Select".
   *
   * @desc Creates a JET Select.
   * @example <caption>Initialize the Select with no options specified:</caption>
   * $( ".selector" ).ojSelect();
   *
   * @example <caption>Initialize the Select with some options:</caption>
   * $( ".selector" ).ojSelect( { "placeholder": "Select a value." } );
   *
   * @example <caption>Initialize the Select via the JET <code class="prettyprint">ojComponent</code> binding:</caption>
   * <div id="select" data-bind="ojComponent: { component: 'ojSelect' }">
   */
  oj.__registerWidget("oj.ojSelect", $['oj']['editableValue'],
  {
    defaultElement : "<select>",
    widgetEventPrefix : "oj",
    options :
    {
      /**
       * The threshold for showing the search box in the dropdown when it's expanded.
       * The search box is always displayed when the results size is greater than
       * the threshold, otherwise the search box is initially turned off.
       * However, the search box is displayed as soon as the user starts typing.
       * This property only applies to single-select.
       *
       * @expose
       * @memberof! oj.ojSelect
       * @instance
       * @type {number}
       * @default <code class="prettyprint">10</code>
       */
      minimumResultsForSearch : 10,
      
    
      /**
       * The placeholder text to set on the element.<p>
       * If the <code class="prettyprint">placeholder</code> option is specified to a string, ojselect will adds a placeholder item at the beginning of the dropdown list with
       *<ul>
       *<li>displayValue: placeholder text</li>
       *<li>value: an empty string</li>
       *</ul>
       * The placeholder item in the dropdown is selectable. However, it's not a valid choice, i.e. validation will fail if the select component is a required field.<p>
       * The placeholder item doesn't participate in the filtering, so it will not appear in the result list with a filter specified.<p>
       * Placeholder text can be an empty string, please see the select placeholder cookbook demo.
       * 
       * @example <caption>Initialize the select with the <code class="prettyprint">placeholder</code> option specified:</caption>
       * $( ".selector" ).ojSelect( { "placeholder": "Please select ..." } );
       * 
       * @default <code class="prettyprint">undefined</code>
       * 
       * @expose 
       * @access public
       * @instance
       * @memberof! oj.ojSelect
       * @type {string|null|undefined}
       */    
      placeholder: undefined,
      
      /**
       * The id of the html list for the select.
       *
       * @example <caption>Initialize the select with the <code class="prettyprint">list</code> option specified:</caption>
       * $( ".selector" ).ojSelect( { "list": "list" } );
       *
       * @example <caption>The <code class="prettyprint">list</code> points to a html <code class="prettyprint">ul</code> element.
       * The value for the list item should be specified with <code class="prettyprint">oj-data-value</code> field. By default, we use the first text node for search filtering. An optional <code class="prettyprint">oj-data-label</code> field can be added to the list item, in which case it will take precedence over the text node.</caption></caption>
       * <ul id="list">
       * <li oj-data-value="li1">Item 1</li>
       * <li oj-data-value="li2">Item 2</li>
       * </ul>
       *
       * @expose
       * @memberof! oj.ojSelect
       * @instance
       * @type {string|null|undefined}
       */
      list: undefined,
      
      /**
       * If multi-select is enabled for the select.
       *
       * @expose
       * @memberof! oj.ojSelect
       * @instance
       * @type {boolean}
       * @default <code class="prettyprint">false</code>
       *
       * @example <caption>Initialize the Select with the <code class="prettyprint">multiple</code> option specified:</caption>
       * $( ".selector" ).ojSelect( { "multiple": true } );
       */
      multiple: false,
       

      /**
       * The option items for the Select. Instead of providing a list of option items, they can be specified as an array of objects containing value and label.
       * The value is used as the value of the option item and label as the label.
       *
       * @expose
       * @memberof! oj.ojSelect
       * @instance
       * @type {Array}
       *
       * @example <caption>Initialize the Select with the <code class="prettyprint">options</code> option specified:</caption>
       * $( ".selector" ).ojSelect( { "options": [{value: 'option1', label: 'option1'}, {value: 'option2', label: 'option2'}, {value: 'option3', label: 'option3'},] } );
        * @example <caption>Initialize the Select with group data:</caption>
       * $( ".selector" ).ojSelect( { "options": [{label : 'group1', children: [{value: 'option1', label: 'option1'}, {value: 'option2', label: 'option2'}]}, {label: 'group2', children: [{value: 'option3', label: 'option3'}]} ] } );
       */
      options : null,
      
      /**
       * Specify the key names to use in the options array.
       *
       * @expose
       * @memberof! oj.ojSelect
       * @instance
       * @type {Object}
       *
       * @example <caption>Initialize the Select with <code class="prettyprint">optionsKeys</code> specified. This allows the key names to be redefined in the options array.</caption>
       * $( ".selector" ).ojSelect( { "optionsKeys": {value : "state_abbr", label : "state_name"} } );
       * @example <caption>Redefine keys for data with subgroups.</caption>
       * $( ".selector" ).ojSelect( { "optionsKeys": {label : "regions", children : "states", childKeys : {value : "state_abbr", label : "state_name"}} } );
       */
      optionsKeys : null,

      /**
       * Triggered immediately before the Select drop down is expanded. 
       *
       * @expose
       * @event
       * @memberof! oj.ojSelect
       * @instance
       * @property {Event} event <code class="prettyprint">jQuery</code> event object
       * @property {Object} ui Parameters
       *
       * @example <caption>Initialize the Select with the <code class="prettyprint">beforeExpand</code> callback specified:</caption>
       * $( ".selector" ).ojSelect({
       *     "beforeExpand": function( event ) {}
       * });
       *
       * @example <caption>Bind an event listener to the <code class="prettyprint">ojbeforeexpand</code> event:</caption>
       * $( ".selector" ).on( "ojbeforeexpand", function( event, ui ) {} );
       */
      beforeExpand : null

      /** 
       * The value of the select component. The data type of value is array. Only the first element of the array is used.
       * 
       * Note:  <code class="prettyprint">value</code> option can only be bind to a knockout observableArray. 
       * 
       * @example <caption>Initialize the select with the <code class="prettyprint">value</code> option specified:</caption>
       * $(".selector").ojSelect({"value": ["option1"]});<br/>
       * 
       * @example <caption>Get or set the <code class="prettyprint">value</code> option, after initialization:</caption>
       * // Getter: returns value
       * $(".selector").ojSelect("option", "value");
       * // Setter: sets value with array containing "option1"
       * $(".selector").ojSelect("option", "value", ["option1"]);
       * 
       * @member 
       * @name  value
       * @access public
       * @instance
       * @default When the value option is not set, the first option is used as its initial value if it exists. 
       * @memberof! oj.ojSelect
       * @type {Array}
       */
    },

    /**
     * Returns a jQuery object containing the element visually representing the select.
     *
     * <p>This method does not accept any arguments.
     *
     * @expose
     * @memberof! oj.ojSelect
     * @instance
     * @return {jQuery} the select
     */
    widget : function ()
    {
      return this.select.container;
    },

    /**
     * @override
     * @private
     * @memberof! oj.ojSelect
     */
    _ComponentCreate : function ()
    {
      this._super();
      this._setup();
    },

    //ojselect
    _setup : function ()
    {
      var opts = {},
          multiple = this.options.multiple;
          
      opts.element = this.element;
      opts.ojContext = this;
      opts = $.extend(this.options, opts);

      this.select = multiple ? new _OjMultiSelect() : new _OjSingleSelect();
      this.select._init(opts);
    },

    /**
     * Refreshes the visual state of the select. JET components require a <code class="prettyprint">refresh()</code> or re-init after the DOM is programmatically changed underneath the component.
     *
     * <p>This method does not accept any arguments.
     * 
     * @expose 
     * @memberof! oj.ojSelect
     * @instance
     */
    refresh : function ()
    {
      this._super();

      this.select._destroy();
      this._setup();
      //TODO: apply value in options for the selected value

      //re apply root attributes settings
      this._SetRootAttributes();

      // re-apply oj-required on container
      this._Refresh("required", this.options['required']);
    },

    /**
     * @override
     * @private
     * @memberof! oj.ojSelect
     */
    _destroy : function ()
    {
      //Bug 20020271 - select destroy issue 
      this.select._destroy();
      this._super();
    },
    
    //19670760, dropdown popup should be closed on subtreeDetached notification.  
    _NotifyDetached : function() {
      this.select.close();
    },

    //19670760, dropdown popup should be closed on subtreeHidden notification.
    _NotifyHidden : function() {
      this.select.close(); 
    },

    /**
     * Set the placeholder.
     * @override
     * @protected
     * @memberof! oj.ojSelect
     */
    _SetPlaceholder : function(value)
    {
      /*
       * Commented out the content to fix the side effect from 
       * the change made in EditableValue._initComponentMessaging (Revision: 9016).
       * EditableValue called SetPlaceholder with an empty string which made
       * every ojselect to have an empty placeholder in the dropdown.
       * Note: don't remove the method because there is more calls from EditableValue
       * to ojselect before this.select is initialized.
       * ex: _GetContentElement

      if (this.select) 
      {
        this.select.opts.placeholder = value;
        this.select._setPlaceholder();
      }
      */
    },

    /**
     * whether the placeholder option is set
     * 
     * @memberof! oj.ojSelect
     * @instance
     * @protected
     */
    _HasPlaceholderSet : function()
    {
      //Bug 18926010 - an empty placeholder shows up if data changed after first binding 
      return typeof this.options['placeholder'] === 'string';
    },

    /**
     * Clear the placeholder option
     * 
     * @memberof! oj.ojSelect
     * @instance
     * @protected
     */
    _ClearPlaceholder : function()
    {
      //Bug 18926010 - an empty placeholder shows up if data changed after first binding 
      this._SetPlaceholderOption(null);
      this._SetPlaceholder(null);
    },

    //ojselect
    _InitOptions : function (originalDefaults, constructorOptions)
    {
      var props = [{attribute: "disabled", defaultOptionValue: null, validateOption: true},
                   {attribute: "placeholder", defaultOptionValue: null},
                   {attribute: "required", defaultOptionValue: false, 
                    coerceDomValue: true, validateOption: true},
                   {attribute: "title", defaultOptionValue: ""}
                   // {attribute: "value", defaultOptionValue: null}
                 ]; 
    
      this._super(originalDefaults, constructorOptions);
      oj.EditableValueUtils.initializeOptionsFromDom(props, constructorOptions, this);  

      // TODO: PAVI - Let's discuss
      if (this.options['value'] === undefined) {
        this.options['value'] = (this.element.attr('value') !== undefined) ? _ComboUtils.splitVal(this.element.val(), ",") : null;
      }
      else {
        //clone the value, otherwise _setDisplayValue will not be invoked on binding value to ko observableArray.
        //TODO: Need to revisit this once 18724975 is fixed.
        var value = this.options['value'];
        if (Array.isArray(value)) {
          value = value.slice(0);
        }
/*
        else if(typeof value ==='string') {
          value = [value];
        }
*/
        this.options['value'] = value;
      }
    },
    
    /**
     * Validates the component's value using the converter and all validators registered on 
     * the component. 
     * 
     * @expose 
     * @override
     * @memberof! oj.ojSelect
     * @instance
     * @returns {boolean} true if component passed validation, false if there were validation errors.
     */
    validate : function ()
    {
      //Bug 19231311 - select needs implementation fixes...
      if (this.select)
        return this._SetValue(this.select.getVal(), null, this._VALIDATE_METHOD_OPTIONS);

      return true;
    },
  
     /**
     * Updates display value.
     * @override
     * @protected
     * @memberof! oj.ojSelect
     */
    _SetDisplayValue: function(displayValue)
    {
      this.select._initSelection();
    },

    /**
     * Handles options specific to select.
     * @override
     * @protected
     * @memberof! oj.ojSelect
     */
    _setOption : function (key, value, flags)
    {
      if (key === "value") {
        //clone the value, otherwise _setDisplayValue will not be invoked on binding value to ko observableArray.
        //TODO: Need to revisit this once 18724975 is fixed.

        //placeholder
        if (this._HasPlaceholderSet() && value && value.length == 0) {
          this._super(key, value, flags);
          return;
        }

        var strVal = value;
        if (Array.isArray(value)) {
          value = value.slice(0);

          //Bug 20957774 - ojselect doesn't update when bound value is modified
          if (this.options['multiple'] !== true)
            strVal = value[0];
        }

        //Bug 19074312 - ojselect should ignore the invalid value set programmatically
        var element = this.select.datalist;
        if (! element)
          element = this.select.opts.element;

        //return if validation failed
        if ((this.options['multiple'] !== true && this.select.opts.validate(element, strVal))
            || this.options['multiple'])  
          this._super(key, value, flags);
        
        return;
      }
      else if (key === "placeholder")
      {
        this.select.opts.placeholder = value;
        this.select._setPlaceholder();
      }

      else if (key === "minimumResultsForSearch")
      {
        this.select.opts.minimumResultsForSearch = value;
      }

      this._super(key, value, flags);

      if (key === "disabled")
      {
        if (value)
          this.select._disable();
        else
          this.select._enable();
      }
      else if (key === "options")
      {
        //Bug 18926010 - an empty placeholder shows up if data changed after first binding
        //Bug 19941308 - ojselect - validator error message is not shown
        //Bug 19977578 - ojselect tooltip no longer appears once options and value observables change
        this.select.opts.options = value;
        this.select.opts = this.select._prepareOpts(this.select.opts);

        //make sure the value still valid
        this._super("value", this.select.getVal());
      }

    },

    _getDropdown : function ()
    {
      if (this.select && this.select._opened())
      {
        //Bug 20747359 - certain subids does not work inside a popup or dialog
        var dropdown = this.select.dropdown;
        if (dropdown && 
            dropdown.attr("data-oj-containerid") === this.select.containerId)
          return dropdown;
      }
      return null;
    },


//////////////////     SUB-IDS     //////////////////

/**
 * <p>Sub-ID for the selected text in the select box
 * 
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and 
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 * 
 * @ojsubid
 * @member
 * @name oj-select-chosen
 * @memberof oj.ojSelect
 * @instance
 * 
 * @example <caption>Get the selected text</caption>
 * var node = $( ".selector" ).ojSelect( "getNodeBySubId", {'subId': 'oj-select-chosen'} );
 */

/**
 * <p>Sub-ID for the dropdown box.
 * <p>See the <a href="#minimumResultsForSearch">minimumResultsForSearch</a> option for details.
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and 
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 * 
 * @ojsubid
 * @member
 * @name oj-select-drop
 * @memberof oj.ojSelect
 * @instance
 * 
 * @example <caption>Get the dropdown box</caption>
 * var node = $( ".selector" ).ojSelect( "getNodeBySubId", {'subId': 'oj-select-drop'} );
 */

/**
 * <p>Sub-ID for the search box. Note: the search box is not always visible.
 * <p>See the <a href="#minimumResultsForSearch">minimumResultsForSearch</a> option for details.
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and 
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 * 
 * @ojsubid
 * @member
 * @name oj-select-search
 * @memberof oj.ojSelect
 * @instance
 * 
 * @example <caption>Get the search box</caption>
 * var node = $( ".selector" ).ojSelect( "getNodeBySubId", {'subId': 'oj-select-search'} );
 */

/**
 * <p>Sub-ID for the search input element. Note that the search input is not always visible.
 * <p>See the <a href="#minimumResultsForSearch">minimumResultsForSearch</a> option for details.
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and 
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 * 
 * @ojsubid
 * @member
 * @name oj-listbox-input
 * @memberof oj.ojSelect
 * @instance
 * 
 * @example <caption>Get the search input element</caption>
 * var node = $( ".selector" ).ojSelect( "getNodeBySubId", {'subId': 'oj-listbox-input'} );
 */

/**
 * <p>Sub-ID for the filtered result list.
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and 
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 * 
 * @ojsubid
 * @member
 * @name oj-select-results
 * @memberof oj.ojSelect
 * @instance
 * 
 * @example <caption>Get the filtered result list</caption>
 * var node = $( ".selector" ).ojSelect( "getNodeBySubId", {'subId': 'oj-select-results'} );
 */

/**
 * <p>Sub-ID for the filtered result item. 
 * <p>Note: To lookup a filtered result item, the dropdown must be open and
 *       the locator object should have the following:<p>
 *          subId: 'oj-listbox-result-label' and 
 *          index: number
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and 
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 * 
 * @ojsubid
 * @member
 * @name oj-listbox-result-label
 * @memberof oj.ojSelect
 * @instance
 * 
 * @example <caption>Get the filtered result item</caption>
 * var node = $( ".selector" ).ojSelect( "getNodeBySubId", {'subId': 'oj-listbox-result-label'} );
 */
    getNodeBySubId: function(locator)
    {
      var node = null, subId;
      if (locator == null)
      {
        return this.select.container ? this.select.container[0] : null;
      }
      else
      {
        node = this._super(locator);
      }
	    
      if (!node)
      {
        subId = locator['subId'];
        if (subId === "oj-select-drop")
          subId = "oj-listbox-drop";

        if (subId === "oj-select-results")
          subId = "oj-listbox-results";

        if (subId === "oj-select-search")
          subId = "oj-listbox-search";

        switch (subId) {
          case "oj-select-choice":
          case "oj-select-chosen":
            node =  this.widget().find("." + subId)[0];
            break;

          case "oj-listbox-drop":
            var dropdown = this._getDropdown();
            if (dropdown) {
              node = dropdown[0];
            }
            break;

          case "oj-listbox-input":
          case "oj-listbox-search":
          case "oj-listbox-results":
            var dropdown = this._getDropdown();
            if (dropdown) {
              node = dropdown.find("." + subId)[0];
            }
            break;

          //Bug 18872085 - ojselect - not able to attach id for generated jet component
          case "oj-listbox-result-label":
            if (this._getDropdown())
            {
              //list of 'li'
              var ddlist =  $("#" + this.select.results.attr("id")).children();
              var index = locator['index'];

              if (ddlist.length && index < ddlist.length) {
                node = ddlist.eq(index).find("." + subId)[0];
              }
            }
            break;
        }
      }
      // Non-null locators have to be handled by the component subclasses
      return node || null;
    },

    /**
     * Returns the default styleclass for the component. Currently this is 
     * used to pass to the _ojLabel component, which will append -label and 
     * add the style class onto the label. This way we can style the label
     * specific to the input component. For example, for inline labels, the
     * radioset/checkboxset components need to have margin-top:0, whereas all the
     * other inputs need it to be .5em. So we'll have a special margin-top style 
     * for .oj-label-inline.oj-radioset-label
     * All input components must override
     * 
     * @return {string}
     * @expose
     * @memberof! oj.ojSelect
     * @override
     * @protected
     */
    _GetDefaultStyleClass : function ()
    {
      return "oj-select";
    },

    /**
     * Returns the messaging launcher element i.e., where user sets focus that triggers the popup. 
     * Usually this is the element input or select that will receive focus and on which the popup 
     * for messaging is initialized. 
     *
     * @override
     * @protected
     * @memberof! oj.ojSelect
     * @return {Object} jquery element which represents the messaging launcher component
     */
    _GetMessagingLauncherElement : function ()
    {
      return this.select.selection;
    },
    
    /**
     * Returns the jquery element that represents the content part of the component.
     * This is usually the component that user sets focus on (tabindex is set 0) and 
     * where aria attributes like aria-required, aria-labeledby etc. are set. This is
     * also the element where the new value is updated. Usually this is the same as
     * the _GetMessagingLauncherElement.
     *
     * @override
     * @protected
     * @memberof! oj.ojSelect
     * @return {Object} jquery element which represents the content.
     */
    _GetContentElement : function ()
    {
      return this.select.selection;
    }

    // Fragments:

    /**
     * <table class="keyboard-table">
     *   <thead>
     *     <tr>
     *       <th>Target</th>
     *       <th>Gesture</th>
     *       <th>Action</th>
     *     </tr>
     *   </thead>
     *   <tbody>
     *     <tr>
     *       <td>Select box or Arrow button</td>
     *       <td><kbd>Tap</kbd></td>
     *       <td>If the drop down is not open, expand the drop down list. Otherwise, close the drop down list.</td>
     *     </tr>
     *     <tr>
     *       <td>Option item</td>
     *       <td><kbd>Tap</kbd></td>
     *       <td>Tap on a option item in the drop down list to select.</td>
     *     </tr>
     *     <tr>
     *       <td>Selected Item with Clear Entry Button</td>
     *       <td><kbd>Tap</kbd></td>
     *       <td>Remove item from the selected items list by taping on the clear button next to the data item.</td>
     *     </tr>
     *     <tr>
     *       <td>Drop down</td>
     *       <td><kbd>swipe up/down</kbd></td>
     *       <td>Scroll the drop down list vertically</td>
     *     </tr>
     *   </tbody>
     * </table>
     *
     * @ojfragment touchDoc - Used in touch gesture section of classdesc, and standalone gesture doc
     * @memberof oj.ojSelect
     */
        
    /**
     * <table class="keyboard-table">
     *   <thead>
     *     <tr>
     *       <th>Target</th>
     *       <th>Key</th>
     *       <th>Action</th>
     *     </tr>
     *   </thead>
     *   <tbody>
     *     <tr>
     *       <td>Option item</td>
     *       <td><kbd>Enter</kbd></td>
     *       <td>Select the highlighted choice from the drop down list.</tr>
     *     </tr>
     *     <tr>
     *       <td>Drop down</td>
     *       <td><kbd>UpArrow or DownArrow</kbd></td>
     *       <td>Highlight the option item in the direction of the arrow. If the drop down is not open, expand the drop down list.</tr>
     *     </tr>
     *     <tr>
     *       <td>Drop down</td>
     *       <td><kbd>Esc</kbd></td>
     *       <td>Collapse the drop down list. If the drop down is already closed, do nothing.</tr>
     *     </tr>
     *   </tbody>
     * </table>
     *
     * <p>Disabled option items receive no highlight and are not selectable.
     *
     * @ojfragment keyboardDoc - Used in keyboard section of classdesc, and standalone gesture doc
     * @memberof oj.ojSelect
     */

  }
  );

oj.Components.setDefaultOptions(
  {
    // converterHint is defaulted to placeholder and notewindow in EditableValue. 
    // For ojselect, we don't want a converterHint. 
    // We used to use oj.Components.createDynamicPropertyGetter, but we don't need the 'context' yet,
    // so we switched it to this simplest code so that overriding displayOptions 
    // for defaultOptions is easier. We only need to define what we want to override, not
    // re-define all the sub-options of displayOptions.
    // See Bug 20441549 - allow multiple setdefaultoptions calls on properties with dynamic getter.
    'ojSelect': // properties for all ojSelect components 
    {
      'displayOptions':
        {
          'converterHint': ['none']
        }
    }
  }
);