Source: src/main/javascript/oracle/oj/ojdatetimepicker/ojdatepicker.js

Oracle® JavaScript Extension Toolkit (JET)
1.1.2

E65298-01

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

/**
 * @preserve Copyright 2013 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 */

/**
 * Placed here to avoid duplicate code for ojdatepicker + ojtimepicker
 *
 * Used for oj.EditableValueUtils.initializeOptionsFromDom
 *
 * @ignore
 */
function coerceIsoString(value)
{
  //reason for coersion is if one refreshes the page; then the input element's value might be the formatted string
  //thought about setting element's value to parsed value on destroy but goes against what destroy is suppose to do
  return this.options["converter"]["parse"](value);
}

/**
 * Placed here to avoid duplicate code for ojdatepicker + ojtimepicker
 *
 * @ignore
 */
function getImplicitDateTimeRangeValidator(options, converter)
{
  var dateTimeRangeTranslations = options['translations']['dateTimeRange'] || {},
          translations = [{'category': 'hint', 'entries': ['min', 'max', 'inRange']},
                          {'category': 'messageDetail', 'entries': ['rangeUnderflow', 'rangeOverflow']},
                          {'category': 'messageSummary', 'entries': ['rangeUnderflow', 'rangeOverflow']}],
          dateTimeRangeOptions = {'min': options['min'], 'max': options['max'], 'converter': converter};

  //note the translations are defined in ojtranslations.js, but it is possible to set it to null, so for sanity
  if(!$.isEmptyObject(dateTimeRangeTranslations))
  {
    for(var i=0, j=translations.length; i < j; i++)
    {
      var category = dateTimeRangeTranslations[translations[i]['category']];

      if(category)
      {
        var translatedContent = {},
            entries = translations[i]['entries'];

        for(var k=0, l=entries.length; k < l; k++)
        {
          translatedContent[entries[k]] = category[entries[k]];
        }

        dateTimeRangeOptions[translations[i]['category']] = translatedContent;
      }
    }
  }

  return oj.Validation.validatorFactory(oj.ValidatorFactory.VALIDATOR_TYPE_DATETIMERANGE).createValidator(dateTimeRangeOptions);
}

/**
 * Shared for ojInputDate + ojInputTime
 *
 * @ignore
 */
function disableEnableSpan(children, val)
{
  var filteredChildren = children.filter("span");
  if (val)
  {
    filteredChildren.addClass("oj-disabled").removeClass("oj-enabled oj-default");
  }
  else
  {
    filteredChildren.removeClass("oj-disabled").addClass("oj-enabled oj-default");
  }
}

/**
 * For dayMetaData
 * 
 * @ignore
 */
function _getMetaData(dayMetaData, position, params) {
  if(!dayMetaData || position === params.length) {
    return dayMetaData;
  }

  var nextPos = position + 1;
  return _getMetaData(dayMetaData[params[position]], nextPos, params) || _getMetaData(dayMetaData["*"], nextPos, params);
}

/**
 * Copies time over from the from date to the to date. Used for ojtimepicker + ojdatetimepicker as well.
 *
 * @ignore
 */
function copyTimeOver(from, to)
{
  to.setHours(from.getHours());
  to.setMinutes(from.getMinutes());
  to.setSeconds(from.getSeconds());
  to.setMilliseconds(from.getMilliseconds());
  return to;
}

/**
 * Bind hover events for datepicker elements.
 * Done via delegate so the binding only occurs once in the lifetime of the parent div.
 * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
 *
 * @ignore
 */
function bindHover(dpDiv)
{
  var selector = ".oj-datepicker-prev-icon, .oj-datepicker-prev-icon .oj-clickable-icon-nocontext.oj-component-icon, .oj-datepicker-next-icon, .oj-datepicker-next-icon .oj-clickable-icon-nocontext.oj-component-icon, .oj-datepicker-calendar td a";
  return dpDiv.delegate(selector, "mouseout", function ()
  {
    $(this).removeClass("oj-hover");
  }).delegate(selector, "mouseover", function ()
  {
    $(this).addClass("oj-hover");
  }).delegate(selector, "focus", function ()
  {
    $(this).addClass("oj-focus");
  }).delegate(selector, "blur", function ()
  {
    $(this).removeClass("oj-focus");
  });
}

//to display the suffix for the year
var yearDisplay = oj.Validation.converterFactory(oj.ConverterFactory.CONVERTER_TYPE_DATETIME).createConverter(
{
  "year" : "numeric"
});


/*!
 * JET Input Date @VERSION
 * http://jqueryui.com
 *
 * Copyright 2013 jQuery Foundertion and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * Depends:
 *  jquery.ui.widget.js
 */
/**
 * @ojcomponent oj.ojInputDate
 * @augments oj.inputBase
 * @since 0.6
 *
 * @classdesc
 * <h3 id="inputDateOverview-section">
 *   JET ojInputDate Component
 *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#inputDateOverview-section"></a>
 * </h3>
 *
 * <p>Description: ojInputDate provides basic support for datepicker selection.
 * <h3 id="keyboard-section">
 *   Keyboard End User Information
 *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#keyboard-section"></a>
 * </h3>
 *
 * <p>When focus is on the input element.
 *
 * <table class="keyboard-table">
 *   <thead>
 *     <tr>
 *       <th>Key</th>
 *       <th>Use</th>
 *     </tr>
 *   </thead>
 *   <tbody>
 *     <tr>
 *       <td><kbd>DownArrow or UpArrow</kbd></td>
 *       <td>Shows the grid and moves the focus into the expanded date grid</td>
 *     </tr>
 *     <tr>
 *       <td><kbd>Esc</kbd></td>
 *       <td>Close the grid.</tr>
 *     <tr>
 * </tbody></table>
 *
 * <br/>
 * <p>When focus is on the picker.
 *
 * <table class="keyboard-table">
 *   <thead>
 *     <tr>
 *       <th>Key</th>
 *       <th>Use</th>
 *     </tr>
 *   </thead>
 *   <tbody>
 *     <tr>
 *       <td><kbd>Enter</kbd></td>
 *       <td>Select the currently focused day</td>
 *     </tr>
 *     <tr>
 *       <td><kbd>UpArrow</kbd></td>
 *       <td>Move up in the grid.</tr>
 *     <tr>
 *       <td><kbd>DownArrow</kbd></td>
 *       <td>Move down in the grid.</tr>
 *     <tr>
 *       <td><kbd>RightArrow</kbd></td>
 *       <td>Move right in the grid.</tr>
 *     <tr>
 *       <td><kbd>LeftArrow</kbd></td>
 *       <td>Move left in the grid.</tr>
 *     <tr>
 *       <td><kbd>Esc</kbd></td>
 *       <td>Close the grid.</tr>
 *     <tr>
 *       <td><kbd>Home</kbd></td>
 *       <td>Move focus to first day of the month.</tr>
 *     <tr>
 *       <td><kbd>End</kbd></td>
 *       <td>Move focus to last day of the month.</tr>
 *     <tr>
 *       <td><kbd>PageUp</kbd></td>
 *       <td>Switch to previous month.</tr>
 *     <tr>
 *       <td><kbd>PageDown</kbd></td>
 *       <td>Switch to next month.</tr>
 *     <tr>
 *       <td><kbd>Alt + PageUp</kbd></td>
 *       <td>Switch to previous year.</tr>
 *     <tr>
 *       <td><kbd>Alt + PageDown</kbd></td>
 *       <td>Switch to next year.</tr>
 *     <tr>
 *       <td><kbd>Ctrl + Alt + PageUp</kbd></td>
 *       <td>Switch to previous by stepBigMonths.</tr>
 *     <tr>
 *       <td><kbd>Ctrl + Alt + PageDown</kbd></td>
 *       <td>Switch to next by stepBigMonths.</tr>
 *     <tr>
 *       <td><kbd>Ctrl + Alt + T</kbd></td>
 *       <td>Places focus on Today button if it exists.</tr>
 *    </tbody>
 * </table>
 *
 * <h3 id="pseudos-section">
 *   Pseudo-selectors
 *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#pseudos-section"></a>
 * </h3>
 *
 * <pre class="prettyprint">
 * <code>$( ":oj-inputDate" )            // selects all JET input on the page
 * </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 input component.
 * For inputDate, you should put an <code>id</code> on the input, and then set
 * the <code>for</code> attribute on the label to be the input's id.
 * </p>
 * <h3 id="label-section">
 *   Label and InputDate
 *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#label-section"></a>
 * </h3>
 * <p>
 * For accessibility, you should associate a label element with the input
 * by putting an <code>id</code> on the input, and then setting the
 * <code>for</code> attribute on the label to be the input'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="binding-section">
 *   Declarative Binding
 *   <a class="bookmarkable-link" title="Bookmarkable Link" href="#binding-section"></a>
 * </h3>
 *
 * <pre class="prettyprint">
 * <code>
 *    <input id="dateId" data-bind="ojComponent: {component: 'ojInputDate'}" />
 * </code>
 * </pre>
 *
 * @desc Creates or re-initializes a JET ojInputDate
 *
 * @param {Object=} options a map of option-value pairs to set on the component
 *
 * @example <caption>Initialize the input element with no options specified:</caption>
 * $( ".selector" ).ojInputDate();
 *
 * @example <caption>Initialize the input element with some options:</caption>
 * $( ".selector" ).ojInputDate( { "disabled": true } );
 *
 * @example <caption>Initialize the input element via the JET <code class="prettyprint">ojComponent</code> binding:</caption>
 * <input id="dateId" data-bind="ojComponent: {component: 'ojInputDate'}" />
 */
oj.__registerWidget("oj.ojInputDate", $['oj']['inputBase'],
{
  version : "1.0.0",
  widgetEventPrefix : "oj",

  //-------------------------------------From base---------------------------------------------------//
  _CLASS_NAMES : "oj-inputdatetime-input",
  _WIDGET_CLASS_NAMES : "oj-inputdatetime-date-only oj-component oj-inputdatetime",
  _ELEMENT_TRIGGER_WRAPPER_CLASS_NAMES : "",
  _INPUT_HELPER_KEY: "inputHelp",
  _ATTR_CHECK : [{"attr": "type", "setMandatory": "text"}],
  _GET_INIT_OPTIONS_PROPS:  [{attribute: "disabled", defaultOptionValue: false, validateOption: true},
                             {attribute: 'pattern', defaultOptionValue: ""},
                             {attribute: "title", defaultOptionValue: ""},
                             {attribute: "placeholder", defaultOptionValue: ""},
                             {attribute: "value", defaultOptionValue: null, coerceDomValue: coerceIsoString},
                             {attribute: "required", defaultOptionValue: false,
                              coerceDomValue: true, validateOption: true},
                             {attribute: 'readonly', option: 'readOnly', defaultOptionValue: false,
                             validateOption: true},
                             {attribute: "min", defaultOptionValue: null, coerceDomValue: coerceIsoString},
                             {attribute: "max", defaultOptionValue: null, coerceDomValue: coerceIsoString}],
  //-------------------------------------End from base-----------------------------------------------//

  _TRIGGER_CLASS : "oj-inputdatetime-input-trigger",
  _TRIGGER_CALENDAR_CLASS : "oj-inputdatetime-calendar-icon",

  _CURRENT_CLASS : "oj-datepicker-current-day",
  _DAYOVER_CLASS : "oj-datepicker-days-cell-over",
  _UNSELECTABLE_CLASS : "oj-datepicker-unselectable",

  _DATEPICKER_DESCRIPTION_ID : "oj-datepicker-desc",
  _CALENDAR_DESCRIPTION_ID : "oj-datepicker-calendar",
  _MAIN_DIV_ID : "oj-datepicker-div",

  _INLINE_CLASS : "oj-datepicker-inline",
  _INPUT_CONTAINER_CLASS : " oj-inputdatetime-input-container",
  _INLINE_WIDGET_CLASS: " oj-inputdatetime-inline",

  options :
  {
    /**
     * <p>
     * Note that Jet framework prohibits setting subset of options which are object types.<br/><br/>
     * For example $(".selector").ojInputDate("option", "datePicker", {footerLayout: "today"}); is prohibited as it will
     * wipe out all other sub-options for "datePicker" object.<br/><br/> If one wishes to do this [by above syntax or knockout] one
     * will have to get the "datePicker" object, modify the necessary sub-option and pass it to above syntax.<br/><br/>
     *
     * The properties supported on the datePicker option are:
     *
     * @property {string=} footerLayout Will dictate what content is shown within the footer of the calendar. <br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {footerLayout: ""}}</code> with possible values being
     * <ul>
     *   <li>"" - Do not show anything</li>
     *   <li>"today" - the today button</li>
     * </ul>
     * <br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.footerLayout", "today");</code>
     *
     * @property {string=} changeMonth Whether the month should be rendered as a dropdown instead of text. <br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {changeMonth: "select"}}</code> with possible values being
     * <ul>
     *  <li>"select" - As a dropdown</li>
     *  <li>"none" - As a text</li>
     * </ul>
     * <br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.changeMonth", "none");</code>
     *
     * @property {string=} changeYear Whether the year should be rendered as a dropdown instead of text. <br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {changeYear: "select"}}</code> with possible values being
     * <ul>
     *  <li>"select" - As a dropdown</li>
     *  <li>"none" - As a text</li>
     * </ul>
     * <br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.changeYear", "none");</code>
     *
     * @property {number=} currentMonthPos The position in multipe months at which to show the current month (starting at 0). <br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {currentMonthPos: 0}}</code> <br/><br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.currentMonthPos", 1);</code>
     *
     * @property {string=} daysOutsideMonth Dictates the behavior of days outside the current viewing month. <br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {daysOutsideMonth: "hidden"}}</code> with possible values being
     * <ul>
     *  <li>"hidden" - Days outside the current viewing month will be hidden</li>
     *  <li>"visible" - Days outside the current viewing month will be visible</li>
     *  <li>"selectable" - Days outside the current viewing month will be visible + selectable</li>
     * </ul>
     * <br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.daysOutsideMonth", "visible");</code>
     *
     * @property {number=} numberOfMonths The number of months to show at once. Note that if one is using a numberOfMonths > 4 then one should define a CSS rule
     * for the width of each of the months. For example if numberOfMonths is set to 6 then one should define a CSS rule .oj-datepicker-multi-6 .oj-datepicker-group
     * providing the width each month should take in percentage.  <br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {numberOfMonths: 1}}</code> <br/><br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.numberOfMonths", 2);</code>
     *
     * @property {string=} showOn When the datepicker should be shown. <br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {showOn: "image"}}</code> with possible values being
     * <ul>
     *  <li>"focus" - when the element receives focus or when the trigger calendar image is clicked</li>
     *  <li>"image" - when the trigger calendar image is clicked</li>
     * </ul>
     * <br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.showOn", "focus");</code>
     *
     * @property {string|number=} stepMonths How the prev + next will step back/forward the months. <br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {stepMonths: "numberOfMonths"}}</code>
     * <ul>
     *  <li>"numberOfMonths" - Will use numberOfMonths option value as value</li>
     *  <li>number - Number of months to step back/forward</li>
     * </ul>
     * <br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.stepMonths", 2);</code>
     *
     * @property {number=} stepBigMonths Number of months to step back/forward for the (Alt + Page up) + (Alt + Page down) key strokes.  <br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {stepBigMonths: 12}}</code><br/><br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.stepBigMonths", 3);</code>
     *
     * @property {string=} weekDisplay Whether week of the year will be shown.<br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {weekDisplay: "none"}}</code>
     * <ul>
     *  <li>"number" - Will show the week of the year as a number</li>
     *  <li>"none" - Nothing will be shown</li>
     * </ul>
     * <br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.weekDisplay", "number");</code>
     *
     * @property {string=} yearRange The range of years displayed in the year drop-down: either relative to today's year ("-nn:+nn"),
     * relative to the currently selected year ("c-nn:c+nn"), absolute ("nnnn:nnnn"), or combinations of these formats ("nnnn:-nn"). <br/><br/>
     * The default value is <code class="prettyprint">{datePicker: {yearRange: "c-10:c+10"}}</code><br/><br/>
     * Example <code class="prettyprint">$(".selector").ojInputDate("option", "datePicker.yearRange", "c-5:c+10");</code>
     * </p>
     *
     * @expose
     * @instance
     * @memberof! oj.ojInputDate
     * @type {Object}
     */
    datePicker:
    {
      /**
       * @expose
       */
      footerLayout : "",

      /**
       * @expose
       */
      changeMonth : "select",

      /**
       * @expose
       */
      changeYear : "select",

      /**
       * @expose
       */
      currentMonthPos : 0,

      /**
       * @expose
       */
      daysOutsideMonth : "hidden",

      /**
       * @expose
       */
      numberOfMonths : 1,

      /**
       * @expose
       */
      showOn : "image",

      /**
       * @expose
       */
      stepMonths : "numberOfMonths",

      /**
       * @expose
       */
      stepBigMonths : 12,

      /**
       * @expose
       */
      weekDisplay : "none", // "number" to show week of the year, "none" to not show it

      /**
       * @expose
       */
      yearRange : "c-10:c+10" // Range of years to display in drop-down,
      // either relative to today's year (-nn:+nn), relative to currently displayed year
      // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)

    },

    /**
     * A datetime converter instance that duck types {@link oj.DateTimeConverter}. Or an object literal
     * containing the properties listed below.
     *
     * The converter used for ojInputDate. Page authors can set a custom converter by creating one using the datetime converter factory
     * and providing custom options -
     * oj.Validation.converterFactory(oj.ConverterFactory.CONVERTER_TYPE_DATETIME).createConverter(customOptions).
     *
     * <p>
     * When <code class="prettyprint">converter</code> option changes due to programmatic
     * intervention, the component performs various tasks based on the current state it is in. </br>
     *
     * <h4>Steps Performed Always</h4>
     * <ul>
     * <li>Any cached converter instance is cleared and new converter created. The converter hint is
     * pushed to messaging. E.g., notewindow displays the new hint(s).
     * </li>
     * </ul>
     *
     * <h4>Running Validation</h4>
     * <ul>
     * <li>if component is valid when <code class="prettyprint">converter</code> option changes, the
     * display value is refreshed.</li>
     * <li>if component is invalid and is showing messages -
     * <code class="prettyprint">messagesShown</code> option is non-empty, when
     * <code class="prettyprint">converter</code> option changes then all component messages are
     * cleared and full validation run using the current display value on the component.
     * <ul>
     *   <li>if there are validation errors, then <code class="prettyprint">value</code>
     *   option is not updated, and the error pushed to <code class="prettyprint">messagesShown</code>
     *   option. The display value is not refreshed in this case. </li>
     *   <li>if no errors result from the validation, the <code class="prettyprint">value</code>
     *   option is updated; page author can listen to the <code class="prettyprint">optionChange</code>
     *   event on the <code class="prettyprint">value</code> option to clear custom errors. The
     *   display value is refreshed with the formatted value provided by converter.</li>
     * </ul>
     * </li>
     * <li>if component is invalid and has deferred messages when converter option changes, the
     *   display value is again refreshed with the formatted value provided by converter.</li>
     * </ul>
     *
     * <h4>Clearing Messages</h4>
     * <ul>
     * <li>Only messages created by the component are cleared. This includes both
     * <code class="prettyprint">messagesHidden</code> and <code class="prettyprint">messagesShown</code>
     *  options.</li>
     * <li><code class="prettyprint">messagesCustom</code> option is not cleared.</li>
     * </ul>
     * </p>
     *
     * @property {string} type - the converter type registered with the oj.ConverterFactory.
     * Usually 'datetime'. See {@link oj.DateTimeConverterFactory} for details. <br/>
     * E.g., <code class="prettyprint">{converter: {type: 'datetime'}</code>
     * @property {Object=} options - optional Object literal of options that the converter expects.
     * See {@link oj.IntlDateTimeConverter} for options supported by the jet datetime converter.
     * E.g., <code class="prettyprint">{converter: {type: 'datetime', options: {formatType: 'date'}}</code>
     *
     *
     * @expose
     * @instance
     * @memberof! oj.ojInputDate
     * @default <code class="prettyprint">oj.Validation.converterFactory(oj.ConverterFactory.CONVERTER_TYPE_DATETIME).createConverter()</code>
     */
    converter : oj.Validation.converterFactory(oj.ConverterFactory.CONVERTER_TYPE_DATETIME).createConverter(
    {
      "day" : "2-digit", "month" : "2-digit", "year" : "2-digit"
    }),

    /**
     * The maximum selectable date. When set to null, there is no maximum.
     *
     * <ul>
     *  <li> type string - local ISOString meaning lacking Z, timezone, and timezone offset
     *  <li> null - no limit
     * </ul>
     *
     * @example <caption>Initialize the component with the <code class="prettyprint">max</code> option:</caption>
     * <input id="date" data-bind="ojComponent: {component: 'ojInputDate', max: '2014-09-25'}" />
     *
     * @expose
     * @instance
     * @memberof! oj.ojInputDate
     * @default <code class="prettyprint">null</code>
     */
    max : undefined,

    /**
     * The minimum selectable date. When set to null, there is no minimum.
     *
     * <ul>
     *  <li> type string - local ISOString meaning lacking Z, timezone, and timezone offset
     *  <li> null - no limit
     * </ul>
     *
     * @example <caption>Initialize the component with the <code class="prettyprint">min</code> option:</caption>
     * <input id="date" data-bind="ojComponent: {component: 'ojInputDate', min: '2014-08-25'}" />
     *
     * @expose
     * @instance
     * @memberof! oj.ojInputDate
     * @default <code class="prettyprint">null</code>
     */
    min : undefined,

    /**
     * Additional info to be used when rendering the day
     *
     * This should be a JavaScript Function reference which accepts as its argument the following JSON format
     * {fullYear: Date.getFullYear(), month: Date.getMonth()+1, date: Date.getDate()}
     *
     * and returns null or all or partial JSON data of
     * {disabled: true|false, className: "additionalCSS", tooltip: 'Stuff to display'}
     *
     * @expose
     * @instance
     * @memberof! oj.ojInputDate
     * @type {Function}
     * @default <code class="prettyprint">null</code>
     */
    dayFormatter : null

    /**
     * Additional info to be used when rendering the day
     *
     * This should be in the following JSON format with the year, month, day based on Date.getFullYear(), Date.getMonth()+1, and Date.getDate():
     * {year: {month: {day: {disabled: true|false, className: "additionalCSS", tooltip: 'Stuff to display'}}}
     *
     * There also exists a special '*' character which represents ALL within that field [i.e. * within year, represents for ALL year].
     * 
     * Note that this option will override the value of the dayFormatter option. Setting both dayFormatter and dayMetaData options is not supported.
     * 
     * @expose
     * @name dayMetaData
     * @instance
     * @memberof! oj.ojInputDate
     * @default <code class="prettyprint">null</code>
     * @example <code class="prettyprint">{2013: {11: {25: {disabled: true, className: 'holiday', tooltip: 'Stuff to display'}, 5: {disabled: true}}}}}</code>
     */

    // DOCLETS
    /**
     * The placeholder text to set on the element. Though it is possible to set placeholder
     * attribute on the element itself, the component will only read the value when the component
     * is created. Subsequent changes to the element's placeholder attribute will not be picked up
     * and page authors should update the option directly.
     *
     * @example <caption>Initialize the component with the <code class="prettyprint">placeholder</code> option:</caption>
     * <!-- Foo is InputDate, InputDateTime />
     * <input id="date" data-bind="ojComponent: {component: 'ojFoo', placeholder: 'Birth Date'}" />
     *
     * @example <caption>Initialize <code class="prettyprint">placeholder</code> option from html attribute:</caption>
     * <!-- Foo is InputDate, InputDateTime />
     * <input id="date" data-bind="ojComponent: {component: 'ojFoo'}" placeholder="User Name" />
     *
     * @default when the option is not set, the element's placeholder attribute is used if it exists.
     * If the attribute is not set then the default can be the converter hint provided by the
     * datetime converter. See displayOptions for details.
     *
     * @access public
     * @instance
     * @expose
     * @name placeholder
     * @instance
     * @memberof! oj.ojInputDate
     */

    /**
     * List of validators used by component when performing validation. Each item is either an
     * instance that duck types {@link oj.Validator}, or is an Object literal containing the
     * properties listed below. Implicit validators created by a component when certain options
     * are present (e.g. <code class="prettyprint">required</code> option), are separate from
     * validators specified through this option. At runtime when the component runs validation, it
     * combines the implicit validators with the list specified through this option.
     * <p>
     * Hints exposed by validators are shown in the notewindow by default, or as determined by the
     * 'validatorHint' property set on the <code class="prettyprint">displayOptions</code>
     * option.
     * </p>
     *
     * <p>
     * When <code class="prettyprint">validators</code> option changes due to programmatic
     * intervention, the component may decide to clear messages and run validation, based on the
     * current state it is in. </br>
     *
     * <h4>Steps Performed Always</h4>
     * <ul>
     * <li>The cached list of validator instances are cleared and new validator hints is pushed to
     * messaging. E.g., notewindow displays the new hint(s).
     * </li>
     * </ul>
     *
     * <h4>Running Validation</h4>
     * <ul>
     * <li>if component is valid when validators changes, component does nothing other than the
     * steps it always performs.</li>
     * <li>if component is invalid and is showing messages -
     * <code class="prettyprint">messagesShown</code> option is non-empty, when
     * <code class="prettyprint">validators</code> changes then all component messages are cleared
     * and full validation run using the display value on the component.
     * <ul>
     *   <li>if there are validation errors, then <code class="prettyprint">value</code>
     *   option is not updated and the error pushed to <code class="prettyprint">messagesShown</code>
     *   option.
     *   </li>
     *   <li>if no errors result from the validation, the <code class="prettyprint">value</code>
     *   option is updated; page author can listen to the <code class="prettyprint">optionChange</code>
     *   event on the <code class="prettyprint">value</code> option to clear custom errors.</li>
     * </ul>
     * </li>
     * <li>if component is invalid and has deferred messages when validators changes, it does
     * nothing other than the steps it performs always.</li>
     * </ul>
     * </p>
     *
     * <h4>Clearing Messages</h4>
     * <ul>
     * <li>Only messages created by the component are cleared.  These include ones in
     * <code class="prettyprint">messagesHidden</code> and <code class="prettyprint">messagesShown</code>
     *  options.</li>
     * <li><code class="prettyprint">messagesCustom</code> option is not cleared.</li>
     * </ul>
     * </p>
     *
     * @property {string} type - the validator type that has a {@link oj.ValidatorFactory} that can
     * be retrieved using the {@link oj.Validation} module. For a list of supported validators refer
     * to {@link oj.ValidatorFactory}. <br/>
     * @property {Object=} options - optional Object literal of options that the validator expects.
     *
     * @example <caption>Initialize the component with validator object literal:</caption>
     * $(".selector").ojInputDate({
     *   validators: [{
     *     type: 'dateTimeRange',
     *     options : {
     *       max: '2014-09-10',
     *       min: '2014-09-01'
     *     }
     *   }],
     * });
     *
     * NOTE: oj.Validation.validatorFactory('dateTimeRange') returns the validator factory that is used
     * to instantiate a range validator for dateTime.
     *
     * @example <caption>Initialize the component with multiple validator instances:</caption>
     * var validator1 = new MyCustomValidator({'foo': 'A'});
     * var validator2 = new MyCustomValidator({'foo': 'B'});
     * // Foo is InputText, InputNumber, Select, etc.
     * $(".selector").ojFoo({
     *   value: 10,
     *   validators: [validator1, validator2]
     * });
     *
     * @expose
     * @name validators
     * @instance
     * @memberof oj.ojInputDate
     * @type {Array|undefined}
     */

    /**
     * The value of the ojInputDate component which should be a local ISOString meaning lacking Z, timezone, and timezone offset.
     *
     * @example <caption>Initialize the component with the <code class="prettyprint">value</code> option:</caption>
     * <input id="date" data-bind="ojComponent: {component: 'ojInputDate', value: '2014-09-10'}" />
     * @example <caption>Initialize the component with the <code class="prettyprint">value</code> option specified programmatically
     * using oj.IntlConverterUtils.dateToLocalIso :</caption>
     * $(".selector").ojInputDate({'value': oj.IntlConverterUtils.dateToLocalIso(new Date())});<br/>
     * @example <caption>Get or set the <code class="prettyprint">value</code> option, after initialization:</caption>
     * // Getter: returns Today's date in ISOString
     * $(".selector").ojInputDate("option", "value");
     * // Setter: sets it to a different date
     * $(".selector").ojInputDate("option", "value", oj.IntlConverterUtils.dateToLocalIso(new Date(2013, 0, 1)));
     *
     * @expose
     * @name value
     * @instance
     * @memberof! oj.ojInputDate
     * @default When the option is not set, the element's value property is used as its initial value
     * if it exists. This value must be a local ISOString meaning lacking Z, timezone, and timezone offset.
     */

  },

  /**
   * @ignore
   * @protected
   */
  _InitBase : function __InitBase()
  {
    this._triggerNode = null;
    this._ignoreShow = false; //only case is when of showOn of focus and one hides the element [need to avoid showing]
    this._maxRows = 4;

    this._currentDay = 0;
    this._drawMonth = this._currentMonth = 0;
    this._drawYear = this._currentYear = 0;

    this._datePickerDefaultValidators = {};

    var nodeName = this.element[0].nodeName.toLowerCase();
    this._isInLine = (nodeName === "div" || nodeName === "span");

    this._dpDiv = bindHover($("<div id='" + this._GetSubId(this._MAIN_DIV_ID) + "' role='region' aria-describedby='" + this._GetSubId(this._DATEPICKER_DESCRIPTION_ID) + "' class='oj-datepicker-content'></div>"));
    //@HTMLUpdateOK
    $("body").append(this._dpDiv);

    if(this._isInLine)
    {
      //if inline then there is no input element, so reset _CLASS_NAMES
      // TODO:Jmw trying to understand what to do in the case of inline. If it is dateTime inline, then I don't wrap the date part.
      // But if it is just date inline, I should... but the use case is probably not frequent.
      this._WIDGET_CLASS_NAMES += this._INLINE_WIDGET_CLASS;
      this._CLASS_NAMES = "";
    }
    else
    {
      //append input container class to be applied to the root node as well, since not inline
      //[note the special case where input container class will NOT be on the widget node is when
      //ojInputDateTime is of inline and ojInputTime places container around the input element]
      // jmw. this is now different. It's no longer on the widget. I add a new wrapper dom.
      // Ji will need to help me with this probably.
      // One thing I know I'm not doing is wrapping the calendar if only date. hmm...
      this._ELEMENT_TRIGGER_WRAPPER_CLASS_NAMES += this._INPUT_CONTAINER_CLASS;
      var self = this;
      this._popUpDpDiv = this._dpDiv.ojPopup({"initialFocus": "none",
                                              "open": function () {
                              self._dpDiv.find(".oj-datepicker-calendar").focus();
                          }});
    }
  },

  /**
   * @protected
   * @override
   * @instance
   * @memberof! oj.ojInputDate
   */
  _ComponentCreate : function __ComponentCreate()
  {
    this._InitBase();

    var retVal = this._super();

    if(this.options["dayMetaData"])
    {
      this.options["dayFormatter"] = (function(value)
      {
        return function(dateInfo) {
          return _getMetaData(value, 0, [dateInfo["fullYear"], dateInfo["month"], dateInfo["date"]]);
        };
      })(this.options["dayMetaData"]);
    }

    //Need to set the currentDay, currentMonth, currentYear to either the value or the default of today's Date
    //Note that these are days indicator for the datepicker, so it is correct in using today's date even if value
    //hasn't been set
    this._setCurrentDate(this._getDate());

    // jmw. Add a wrapper around the element and the trigger. This is needed so that we can
    // add inline messages to the root dom node. We want the input+trigger to be one child and
    // the inline messages to be another child of the root dom node. This way the inline
    // messages can be stacked after the main component, and will grow or shrink in size the same
    // as the main component.
    // doing this in InputBase now. $(this.element).wrap( $("<div>").addClass(this._ELEMENT_TRIGGER_WRAPPER_CLASS_NAMES));

    if (this._isInLine)
    {
      //@HTMLUpdateOK
      this.element.append(this._dpDiv);
      this.element.addClass(this._INLINE_CLASS); //by applying the inline class it places margin bottom, to separate in case ojInputTime exists

      // Set display:block in place of inst._dpDiv.show() which won't work on disconnected elements
      // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
      this._dpDiv.css("display", "block");
    }
    else
    {
      this._attachTrigger();
    }

    return retVal;
  },

  /**
   * @protected
   * @override
   * @instance
   * @memberof! oj.ojInputDate
   */
  _AfterCreate : function ()
  {
    var ret = this._superApply(arguments);

    this._disableEnable(this.options["disabled"]);

    return ret;
  },

  /**
   * @ignore
   * @protected
   * @override
   */
  _setOption : function __setOption(key, value, flags)
  {

    var retVal = null;

    //When a null, undefined, or "" value is passed in set to null for consistency
    //note that if they pass in 0 it will also set it null
    if (key === "value")
    {
      if(!value)
      {
        value = null;
      }

      retVal = this._super(key, value, flags);
      this._setCurrentDate(value);
      if(this._datepickerShowing())
      {
        this._updateDatepicker();
      }
      return retVal;
    }

    if (key === "dayMetaData")
    {
      //need to invoke w/ dayFormatter and return for the case where user invoke $("selector").ojInputDate("option", "dayMetaData", {});
      //since that doesn't trigger ComponentBinding

      this._setOption("dayFormatter", function(dateInfo) {
          return _getMetaData(value, 0, [dateInfo["fullYear"], dateInfo["month"], dateInfo["date"]]);
      }, flags);
      return; //avoid setting in this.options and etc
    }

    retVal = this._super(key, value, flags);

    if (key === "disabled")
    {
      this._disableEnable(value);
    }
    else if (key === "max" || key === "min")
    {
      //since validators are immutable, they will contain min + max as local values. B/c of this will need to recreate
      this._datePickerDefaultValidators[oj.ValidatorFactory.VALIDATOR_TYPE_DATETIMERANGE] = this._getValidator("min");

      this._AfterSetOptionValidators();
    }
    else if(key === "readOnly" && value)
    {
      this.hide();
    }
    else if (key === "dayFormatter")
    {
      //since validators are immutable, they will contain dayFormatter as local values. B/c of this will need to recreate
      this._datePickerDefaultValidators[oj.ValidatorFactory.VALIDATOR_TYPE_DATERESTRICTION] = this._getValidator("dayFormatter");

      this._AfterSetOptionValidators();
    }

    if (key === "datePicker" && flags["subkey"] === "currentMonthPos")
    {
      //need to reset up the drawMonth + drawYear
      this._setCurrentDate(this.options["value"]);
    }

    var updateDatePicker = {"max": true, "min": true, "dayFormatter": true, "datePicker": true, "translations": true};

    if(this._datepickerShowing() && key in updateDatePicker)
    {
      this._updateDatepicker();
    }

    return retVal;
  },

  /**
   * Need to override due to usage of display: inline-table [as otherwise for webkit the hidden content takes up
   * descent amount of space]
   *
   * @protected
   * @instance
   * @memberOf !oj.ojInputDate
   */
  _AppendInputHelperParent : function __AppendInputHelperParent()
  {
    return this._triggerNode;
  },

  /**
   * @ignore
   * @protected
   * @override
   */
  _destroy : function __destroy()
  {
    var retVal = this._super();

    this.element.off("focus");

    if (this._triggerNode)
    {
      this._triggerNode.remove();
    }

    if(this._isInLine)
    {
      //need to remove disabled + readOnly b/c they are set by super classes and datepicker is special in that this.element
      //can be a div element for inline mode
      this.element.removeProp("disabled");
      this.element.removeProp("readonly");
    }

    this._dpDiv.remove();
    return retVal;
  },

  _datepickerShowing: function()
  {
    return this._isInLine || this._popUpDpDiv.ojPopup("isOpen");
  },

  /**
   * This function will create the necessary calendar trigger container [i.e. image to launch the calendar]
   * and perform any attachment to events
   *
   * @private
   */
  _attachTrigger : function __attachTrigger()
  {
    var showOn = this.options["datePicker"]["showOn"],
        triggerContainer = $("<span>").addClass(this._TRIGGER_CLASS);

    if (showOn === "focus")
    {
      // pop-up date picker when in the marked field
      this.element.on("focus", $.proxy(this.show, this));
    }

    // pop-up date picker when button clicked
    var triggerCalendar = $("<span title='" + this._getCalendarTitle() + "'/>").addClass(this._TRIGGER_CALENDAR_CLASS + " oj-clickable-icon-nocontext oj-component-icon");
    //@HTMLUpdateOK
    triggerContainer.append(triggerCalendar);

    var self = this;
    triggerCalendar.on("click", function ()
    {
      if (self._datepickerShowing())
      {
        self.hide();
      }
      else
      {
        self.show();
      }
      return false;
    }).on("mouseenter", function ()
    {
      $(this).addClass("oj-hover");
    }).on("mousedown", function ()
    {
      $(this).addClass("oj-active");
    }).on("mouseup", function ()
    {
      $(this).removeClass("oj-active");
    }).on("mouseleave", function ()
    {
      $(this).removeClass("oj-hover oj-active");
    });

    this._triggerIcon = triggerCalendar;
    this._triggerNode = triggerContainer;
    //@HTMLUpdateOK
    this.element.after(triggerContainer);
  },

  //This handler is when an user keys down with the calendar having focus
  _doCalendarKeyDown : function __doCalendarKeyDown(event)
  {
    var sel, handled = false,
        kc = $.ui.keyCode,
        isRTL = this._IsRTL();

    if (this._datepickerShowing())
    {
      switch (event.keyCode)
      {
        case 84: //t character
          if (event.altKey && event.ctrlKey)
          {
            this._dpDiv.find(".oj-datepicker-current").focus();
            handled = true;
          }
          break;
        case kc.TAB:
          this.hide();
          // hide on tab out
          break;
        case kc.ENTER:
          sel = $("td." + this._DAYOVER_CLASS + ":not(." + this._CURRENT_CLASS + ")", this._dpDiv);
          if (sel[0])
          {
            this._selectDay(this._currentMonth, this._currentYear, sel[0], event);
          }
          //need to return false so preventing default + stop propagation here
          event.preventDefault();
          event.stopPropagation();
          return false;
        case kc.ESCAPE:
          this.hide();
          this.element.focus();
          handled = true;
          break;// hide on escape
        case kc.PAGE_UP:
          if(event.ctrlKey && event.altKey)
          {
            this._adjustDate(- this.options["datePicker"]["stepBigMonths"], "M", true);
          }
          else if (event.altKey)
          {
            this._adjustDate( - 1, "Y", true);
          }
          else
          {
            this._adjustDate(- this._getStepMonths(), "M", true);
          }
          handled = true;
          break;// previous month/year on page up/+ ctrl
        case kc.PAGE_DOWN:
          if(event.ctrlKey && event.altKey)
          {
            this._adjustDate(+ this.options["datePicker"]["stepBigMonths"], "M", true);
          }
          else if (event.altKey)
          {
            this._adjustDate(1, "Y", true);
          }
          else
          {
            this._adjustDate(+ this._getStepMonths(), "M", true);
          }
          handled = true;
          break;// next month/year on page down/+ ctrl
        case kc.END:
          this._currentDay = this._getDaysInMonth(this._currentYear, this._currentMonth);
          this._updateDatepicker(true);
          handled = true;
          break;
        case kc.HOME:
          this._currentDay = 1;
          this._updateDatepicker(true);
          handled = true;
          break;
        case kc.LEFT:
          this._adjustDate((isRTL ?  + 1 :  - 1), "D", true);
          // -1 day on ctrl or command +left
          if (event.originalEvent.altKey)
          {
            this._adjustDate((event.ctrlKey ?  - this.options["datePicker"]["stepBigMonths"] :  - this._getStepMonths()), "M", true);
          }
          // next month/year on alt +left on Mac
          handled = true;
          break;
        case kc.UP:
          this._adjustDate( - 7, "D", true);
          handled = true;
          break;// -1 week on ctrl or command +up
        case kc.RIGHT:
          this._adjustDate((isRTL ?  - 1 :  + 1), "D", true);
          // +1 day on ctrl or command +right
          if (event.originalEvent.altKey)
          {
            this._adjustDate((event.ctrlKey ?  + this.options["datePicker"]["stepBigMonths"] :  + this._getStepMonths()), "M", true);
          }
          // next month/year on alt +right
          handled = true;
          break;
        case kc.DOWN:
          this._adjustDate( + 7, "D", true);
          handled = true;
          break;// +1 week on ctrl or command +down
        default : ;
      }
    }
    else if (event.keyCode === kc.HOME && event.ctrlKey)
    {
      // display the date picker on ctrl+home
      this.show();
      handled = true;
    }

    if (handled)
    {
      event.preventDefault();
      event.stopPropagation();
    }

  },

  /**
   * Thie function will update the calendar display
   *
   * @private
   * @param {boolean=} focusOnCalendar
   */
  _updateDatepicker : function __updateDatepicker(focusOnCalendar)
  {
    this._maxRows = 4;//Reset the max number of rows being displayed (see #7043)
    var generatedHtmlContent = this._generateHTML();

    //@HTMLUpdateOK
    this._dpDiv.empty().append(generatedHtmlContent.html);

    var buttons = $("button", this._dpDiv);

    if(buttons.length > 0)
    {
      if(buttons.length === 1)
      {
        //need to center it as requested by UX
        $(buttons[0]).addClass("oj-datepicker-single-button");
      }

      $.each(buttons, function (index, ele)
      {
        $(ele).ojButton();
      });

    }

    this._attachHandlers();

    if (generatedHtmlContent.dayOverId)
    {
      this._dpDiv.find(".oj-datepicker-calendar").attr("aria-activedescendant", generatedHtmlContent.dayOverId);
    }

    var numMonths = this._getNumberOfMonths(),
        cols = numMonths[1],
        width = 17;

    this._dpDiv.removeClass("oj-datepicker-multi-2 oj-datepicker-multi-3 oj-datepicker-multi-4").width("");
    if (cols > 1)
    {
      this._dpDiv.addClass("oj-datepicker-multi-" + cols).css("width", (width * cols) + "em");
    }
    this._dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") + "Class"]("oj-datepicker-multi");

    // #6694 - don't focus the input if it's already focused
    // this breaks the change event in IE
    if (this._datepickerShowing() && this.element.is(":visible") && !this.element.is(":disabled"))
    {
      if (!focusOnCalendar)
      {
        if (!this._isInLine && this.element[0] !== document.activeElement)
        {
          this.element.focus();
        }
      }
      else
      {
        var calendar = this._dpDiv.find(".oj-datepicker-calendar");
        if (calendar[0] !== document.activeElement)
        {
          calendar.focus();
        }
      }
    }

  },

  /**
   * Adjust one of the date sub-fields.
   *
   * @private
   * @param {number} offset
   * @param {string} period
   * @param {boolean=} focusOnCalendar
   */
  _adjustDate : function __adjustDate(offset, period, focusOnCalendar)
  {
    if (this.options["disabled"])
    {
      return;
    }
    this._adjustInstDate(offset + (period === "M" ? this.options["datePicker"]["currentMonthPos"] : 0), // undo positioning
    period);
    this._updateDatepicker(focusOnCalendar);
  },

  /**
   * Action for current link.
   *
   * @private
   */
  _gotoToday : function __gotoToday()
  {
    var date = new Date();

    this._currentDay = date.getDate();
    this._drawMonth = this._currentMonth = date.getMonth();
    this._drawYear = this._currentYear = date.getFullYear();

    this._adjustDate();
  },

  /**
   * Action for selecting a new month/year.
   *
   * @private
   * @param {Object} select
   * @param {string} period
   */
  _selectMonthYear : function __selectMonthYear(select, period)
  {
    var selected = parseInt(select.options[select.selectedIndex].value, 10);

    if (period === "M")
    {
      this._currentMonth = this._drawMonth = selected;
    }
    else
    {
      this._currentYear = this._drawYear = selected;
    }

    //@HTMLUpdateOK
    //Take care of accessibility
    $("#" + this._GetSubId(this._CALENDAR_DESCRIPTION_ID)).html(this._EscapeXSS(this.options["monthWide"][this._drawMonth]) + " " + yearDisplay.format(oj.IntlConverterUtils.dateToLocalIso(new Date(this._drawYear, this._drawMonth, 1))));

    this._adjustDate(0, 0, true);
  },

  //Action for selecting a day.
  _selectDay : function __selectDay(month, year, td, event)
  {
    if ($(td).hasClass(this._UNSELECTABLE_CLASS) || this.options["disabled"])
    {
      return;
    }

    this._currentDay = $("a", td).html();
    this._currentMonth = month;
    this._currentYear = year;

    var converterUtils = oj.IntlConverterUtils,
        value = this.options['value'] ?converterUtils.isoToLocalDate(this.options['value']) : null;
    if (value)
    {
      var temp = new Date(this._currentYear, this._currentMonth, this._currentDay);

      //need to preserve the time portion when of ojInputDateTime
      copyTimeOver(value, temp);

      value = temp;
    }
    else
    {
      value = new Date(this._currentYear, this._currentMonth, this._currentDay);
    }

    var isoString = converterUtils.dateToLocalIso(value);
    var formatted = this._GetConverter()["format"](isoString);
    this._SetDisplayValue( formatted ); //need to set the display value, since _SetValue doesn't trigger it per discussion
    this._SetValue(isoString, event);   //TODO test w/ VALIDATORS_ONLY
    this.hide();
  },

  //A date may be specified as an exact value or a relative one.
  _determineDate : function __determineDate(date, defaultDate)
  {
    return !date ? defaultDate : (typeof date === "string" ? oj.IntlConverterUtils.isoToLocalDate(date) : new Date(date.getTime()));
  },

  /**
   * Set the date(s) directly.
   *
   * @private
   * @param {Object} date
   */
  _setCurrentDate : function __setCurrentDate(date)
  {
    var newDate = this._determineDate(date, this._getTodayDate());

    this._currentDay = newDate.getDate();
    this._drawMonth = this._currentMonth = newDate.getMonth();
    this._drawYear = this._currentYear = newDate.getFullYear();

    this._adjustInstDate();
  },

  _getStepMonths : function __getStepMonths()
  {
    var stepMonths = this.options["datePicker"]["stepMonths"];
    return $.isNumeric(stepMonths) ? stepMonths : this.options["datePicker"]["numberOfMonths"];
  },

  /**
   * Attach the onxxx handlers.  These are declared statically so
   * they work with static code transformers like Caja.
   *
   * @private
   */
  _attachHandlers : function __attachHandlers()
  {
    var stepMonths = this._getStepMonths(), self = this;
    this._dpDiv.find("[data-handler]").map(function ()
    {
      var handler =
      {
        /** @expose */
        prev : function (evt)
        {
          self._adjustDate( - stepMonths, "M", true);
          evt.preventDefault();
        },
        /** @expose */
        next : function (evt)
        {
          self._adjustDate( + stepMonths, "M", true);
          evt.preventDefault();
        },
        /** @expose */
        today : function (evt)
        {
          if((evt.type === "keyup" && evt.keyCode === 13) || evt.type === "click")
          {
            self._gotoToday();
            evt.preventDefault();
            evt.stopPropagation();
          }
        },
        /** @expose */
        selectDay : function (evt)
        {
          self._selectDay( + this.getAttribute("data-month"),  + this.getAttribute("data-year"), this, evt);
          return false;
        },
        /** @expose */
        selectMonth : function ()
        {
          self._selectMonthYear(this, "M");
          return false;
        },
        /** @expose */
        selectYear : function ()
        {
          self._selectMonthYear(this, "Y");
          return false;
        },
        /** @expose */
        calendarKey : function (evt)
        {
          self._doCalendarKeyDown(evt);
        }
      };
      $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
    });
  },

  /**
   * Returns a boolean of whether the print date is outside the min + max range, ignoring time.
   *
   * @private
   */
  _outSideMinMaxRange : function __outSideMinMaxRange(printDate, minDate, maxDate)
  {
    minDate = minDate ? new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate()) : null;
    maxDate = maxDate ? new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate()) : null;
    return (minDate !== null && printDate < minDate) || (maxDate !== null && printDate > maxDate);
  },

  /**
   * Generate the HTML for the current state of the date picker.
   *
   * @private
   */
  _generateHTML : function __generateHTML()
  {
    var maxDraw, prevText, prev, nextText, next, currentText, gotoDate, todayControl,
        footerLayout, weekDisplay, dayNames = this.options["dayWide"], dayNamesMin = this.options["dayNarrow"],
        monthNames = this.options["monthWide"], monthNamesShort = this.options["monthAbbreviated"],
        firstDay = this.options["firstDayOfWeek"], daysOutsideMonth, html, dow, row, group, col, selected, rowCellId,
        dayOverClass, dayOverId = "", calender, thead, day, daysInMonth, leadDays, curRows, numRows,
        printDate, dRow, tbody, daySettings, otherMonth, unselectable, tempDate = new Date(),
        today = new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate()), // clear time
        isRTL = this._IsRTL(), footerLayoutDisplay = this.options["datePicker"]["footerLayout"], numMonths = this._getNumberOfMonths(),
        currentMonthPos = this.options["datePicker"]["currentMonthPos"], dayFormatter = this.options["dayFormatter"],
        currMetaData = null, isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1), minDate = this._getMinMaxDate("min"),
        maxDate = this._getMinMaxDate("max"), drawMonth = this._drawMonth - currentMonthPos, drawYear = this._drawYear,
        compareDate = new Date(this._currentYear, this._currentMonth, this._currentDay), valueDate = new Date(this._getDate()),
        selectedDay = valueDate.getDate(), selectedMonth = valueDate.getMonth(), selectedYear = valueDate.getFullYear(),
        wDisabled = this.options["disabled"], calculatedWeek, weekText = this._EscapeXSS(this.getTranslatedString("weekText")),
        converterUtils = oj.IntlConverterUtils;

    valueDate.setHours(0);
    valueDate.setMinutes(0);
    valueDate.setSeconds(0);
    valueDate.setMilliseconds(0);

    if (drawMonth < 0)
    {
      drawMonth += 12;
      drawYear--;
    }

    if(minDate)
    {
      var minDraw = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
      minDraw = (maxDate && maxDate < minDraw ? maxDate : minDraw); //tech shouldn't this error out? [previous existing jquery logic so keep, maybe a reason]
      while (new Date(drawYear, drawMonth, this._getDaysInMonth(drawYear, drawMonth)) < minDraw)
      {
        drawMonth++;
        if (drawMonth > 11)
        {
          drawMonth = 0;
          drawYear++;
        }
      }
    }

    if (maxDate)
    {
      maxDraw = new Date(maxDate.getFullYear(), maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate());
      maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw); //tech shouldn't this error out? [previous existing jquery logic so keep, maybe a reason]
      while (new Date(drawYear, drawMonth, 1) > maxDraw)
      {
        drawMonth--;
        if (drawMonth < 0)
        {
          drawMonth = 11;
          drawYear--;
        }
      }
    }
    this._drawMonth = drawMonth;
    this._drawYear = drawYear;

    prevText = this._EscapeXSS(this.getTranslatedString("prevText"));

    prev = (this._canAdjustMonth( - 1, drawYear, drawMonth) && !wDisabled ? "<a href='#' class='oj-datepicker-prev-icon oj-enabled oj-default oj-component-icon oj-clickable-icon-nocontext' data-handler='prev' data-event='click'" + " title='" + prevText + "'></a>" : "<a class='oj-datepicker-prev-icon oj-disabled oj-component-icon oj-clickable-icon-nocontext' title='" + prevText + "'></a>");

    nextText = this._EscapeXSS(this.getTranslatedString("nextText"));

    next = (this._canAdjustMonth( + 1, drawYear, drawMonth) && !wDisabled ? "<a href='#' class='oj-datepicker-next-icon oj-enabled oj-default oj-component-icon oj-clickable-icon-nocontext' data-handler='next' data-event='click'" + " title='" + nextText + "'></a>" : "<a class='oj-datepicker-next-icon oj-disabled oj-component-icon oj-clickable-icon-nocontext' title='" + nextText + "'></a>");

    currentText = this._EscapeXSS(this.getTranslatedString("currentText"));
    gotoDate = today;

    footerLayout = "";
    todayControl = "<button type='button' class='oj-datepicker-current oj-priority-secondary' data-handler='today' data-event='click keyup'" + ">" + currentText + "</button>";

    if(footerLayoutDisplay.length > 1) //keep the code for future multiple buttons
    {
      var todayIndex = footerLayoutDisplay.indexOf("today"),
          loop = 0,
          footerLayoutButtons = [{index: todayIndex, content: (this._isInRange(gotoDate) ? todayControl : "")}];

      //rather than using several if + else statements, sort the content to add by index of the strings
      footerLayoutButtons.sort(function(a, b)
      {
        return a.index - b.index;
      });

      //continue to loop until the index > -1 [contains the string]
      while(loop < footerLayoutButtons.length && footerLayoutButtons[loop].index < 0) { loop++; }

      while(loop < footerLayoutButtons.length)
      {
        footerLayout += footerLayoutButtons[loop++].content;
      }

      if(footerLayout.length > 0)
      {
        footerLayout = "<div class='oj-datepicker-buttonpane'>" + footerLayout + "</div>";
      }
    }

    weekDisplay = this.options["datePicker"]["weekDisplay"];

    daysOutsideMonth = this.options["datePicker"]["daysOutsideMonth"];
    html = "";

    var monthControl = "all";
    for (row = 0;row < numMonths[0];row++)
    {
      group = "";
      this._maxRows = 4;
      for (col = 0;col < numMonths[1];col++)
      {
        monthControl = "all";
        calender = "";
        if (isMultiMonth)
        {
          calender += "<div class='oj-datepicker-group";
          if (numMonths[1] > 1)
          {
            switch (col)
            {
              case 0:
                calender += " oj-datepicker-group-first";
                monthControl = (isRTL ? "right" : "left");
                break;
              case numMonths[1] - 1:
                calender += " oj-datepicker-group-last";
                monthControl = (isRTL ? "left" : "right");
                break;
              default :
                calender += " oj-datepicker-group-middle";
                monthControl = "";
                break;
            }
          }
          calender += "'>";
        }
        calender += "<div class='oj-datepicker-header" + (wDisabled ? " oj-disabled " : " oj-enabled oj-default ") + "'>" + (/all|left/.test(monthControl) && row === 0 ? (isRTL ? next : prev) : "") + (/all|right/.test(monthControl) && row === 0 ? (isRTL ? prev : next) : "") + this._generateMonthYearHeader(drawMonth, drawYear, minDate, maxDate, row > 0 || col > 0, monthNames, monthNamesShort, isMultiMonth) + // draw month headers
"</div><table class='oj-datepicker-calendar" + (wDisabled ? " oj-disabled " : " oj-enabled oj-default ") + "' tabindex=-1 data-handler='calendarKey' data-event='keydown' aria-readonly='true' role='grid' " + "aria-labelledby='" + this._GetSubId(this._CALENDAR_DESCRIPTION_ID) + "'><thead role='presentation'>" + "<tr role='row'>";
        thead = (weekDisplay === "number" ? "<th class='oj-datepicker-week-col'>" + this._EscapeXSS(this.getTranslatedString("weekHeader")) + "</th>" : "");
        for (dow = 0;dow < 7;dow++)
        {
          // days of the week
          day = (dow + parseInt(firstDay, 10)) % 7;
          thead += "<th role='columnheader' aria-label='" + dayNames[day] + "'" + ((dow + firstDay + 6) % 7 >= 5 ? " class='oj-datepicker-week-end'" : "") + ">" + "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
        }
        calender += thead + "</tr></thead><tbody role='presentation'>";
        daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
        if (drawYear === selectedYear && drawMonth === selectedMonth)
        {
          selectedDay = Math.min(selectedDay, daysInMonth);
        }
        leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
        curRows = Math.ceil((leadDays + daysInMonth) / 7);// calculate the number of rows to generate
        numRows = (isMultiMonth ? this._maxRows > curRows ? this._maxRows : curRows : curRows);//If multiple months, use the higher number of rows (see #7043)
        this._maxRows = numRows;
        printDate = new Date(drawYear, drawMonth, 1 - leadDays);
        for (dRow = 0;dRow < numRows;dRow++)
        {
          // create date picker rows
          calender += "<tr role='row'>";

          calculatedWeek = this._GetConverter().calculateWeek(converterUtils.dateToLocalIso(printDate));
          tbody = (weekDisplay === "none" ? "" : "<td class='oj-datepicker-week-col' role='rowheader' aria-label='" + weekText + " " + calculatedWeek + "'>" + calculatedWeek + "</td>");
          for (dow = 0;dow < 7;dow++)
          {
            // create date picker days
            otherMonth = (printDate.getMonth() !== drawMonth);
            selected = printDate.getTime() === valueDate.getTime();
            rowCellId = "oj-dp-" + this["uuid"] + "-" + dRow + "-" + dow + "-" + row + "-" + col;
            dayOverClass = (printDate.getTime() === compareDate.getTime() && drawMonth === this._currentMonth);
            if (dayOverClass)
            {
              dayOverId = rowCellId;
            }

            daySettings = [true, ""];
            var pYear = printDate.getFullYear(),
                pMonth = printDate.getMonth(),
                pDate = printDate.getDate();

            if (dayFormatter)
            {
              currMetaData = dayFormatter({"fullYear": pYear, "month": pMonth+1, "date": pDate}); //request to start from 1 rather than 0
              if (currMetaData)
              {
                //has content
                daySettings = [!currMetaData["disabled"], currMetaData["className"] || ""];
                if (currMetaData["tooltip"])
                {
                  daySettings.push(currMetaData["tooltip"]);
                }
              }
            }
            var selectedDate = printDate.getTime() === valueDate.getTime();

            unselectable = (otherMonth && daysOutsideMonth !== "selectable") || !daySettings[0] || this._outSideMinMaxRange(printDate, minDate, maxDate);
            tbody += "<td role='gridcell' aria-disabled='" + !!unselectable + "' aria-selected='" + selected + "' id='" + rowCellId + "' " + "class='" + ((dow + firstDay + 6) % 7 >= 5 ? " oj-datepicker-week-end" : "") + // highlight weekends
(otherMonth ? " oj-datepicker-other-month" : "") + // highlight days from other months
(dayOverClass ? " " + this._DAYOVER_CLASS : "") + // highlight selected day
(unselectable || wDisabled ? " " + this._UNSELECTABLE_CLASS + " oj-disabled" : " oj-enabled ") + // highlight unselectable days
(otherMonth && daysOutsideMonth === "hidden" ? "" : " " + daySettings[1] + // highlight custom dates
(selected ? " " + this._CURRENT_CLASS : "") + // highlight selected day
(printDate.getTime() === today.getTime() ? " oj-datepicker-today" : "")) + "'" + // highlight today (if different)
((!otherMonth || daysOutsideMonth !== "hidden") && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "'") + "'" : "") + // cell title
(unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
(otherMonth && daysOutsideMonth === "hidden" ? " " : // display for other months
(unselectable || wDisabled ? "<span class='oj-disabled'>" + printDate.getDate() + "</span>" : "<a class='oj-enabled" + (selectedDate ? " oj-selected" : "") + // highlight selected day
(otherMonth ? " oj-priority-secondary" : "") + // distinguish dates from other months
"' " + (selectedDate || dayOverClass ? "" : "tabindex='-1' ") + " href='#'>" + printDate.getDate() + "</a>")) + "</td>";// display selectable date
            printDate.setDate(printDate.getDate() + 1);
          }
          calender += tbody + "</tr>";
        }
        drawMonth++;
        if (drawMonth > 11)
        {
          drawMonth = 0;
          drawYear++;
        }
        calender += "</tbody></table>" + (isMultiMonth ? "</div>" + ((numMonths[0] > 0 && col === numMonths[1] - 1) ? "<div class='oj-datepicker-row-break'></div>" : "") : "");
        group += calender;
      }
      html += group;
    }
    html += footerLayout;
    return {html : html, dayOverId : dayOverId};
  },

  /**
   * Generate the month and year header.
   *
   * @private
   */
  _generateMonthYearHeader : function __generateMonthYearHeader(drawMonth, drawYear, minDate, maxDate, secondary, monthNames, monthNamesShort, isMultiMonth)
  {

    var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
        changeMonth = this.options["datePicker"]["changeMonth"], changeYear = this.options["datePicker"]["changeYear"],
        positionOfMonthToYear = oj.LocaleData.isMonthPriorToYear() ? "before" : "after",
        html = "<div class='oj-datepicker-title' role='header'>", monthHtml = "",
        wDisabled = this.options["disabled"], converterUtils = oj.IntlConverterUtils;

    // month selection
    if (secondary || changeMonth === "none")
    {
      monthHtml += "<span class='oj-datepicker-month'>" + monthNames[drawMonth] + "</span>";
    }
    else
    {
      inMinYear = (minDate && minDate.getFullYear() === drawYear);
      inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
      monthHtml += "<select tabindex='-1' class='oj-datepicker-month " + (wDisabled ? "oj-disabled' disabled" : "oj-enabled'") + " data-handler='selectMonth' data-event='change' role='listbox'>";
      for (month = 0;month < 12;month++)
      {
        if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth()))
        {
          monthHtml += "<option role='option' value='" + month + "' aria-selected='" + (month === drawMonth ? "true' selected='selected'" : "false'") + ">" + monthNamesShort[month] + "</option>";
        }
      }
      monthHtml += "</select>";
    }

    if (positionOfMonthToYear === "before")
    {
      html += monthHtml + (secondary || !((changeMonth === "select") && (changeYear === "select")) ? " " : "");
    }

    // year selection
    if (!this.yearshtml)
    {
      this.yearshtml = "";
      if (secondary || changeYear === "none")
      {
        html += "<span class='oj-datepicker-year'>" + yearDisplay.format(converterUtils.dateToLocalIso(new Date(drawYear, drawMonth, 1))) + "</span>";
      }
      else
      {
        // determine range of years to display
        years = this.options["datePicker"]["yearRange"].split(":");
        thisYear = new Date().getFullYear();
        determineYear = function (value)
        {
          var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) : (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) : parseInt(value, 10)));
          return (isNaN(year) ? thisYear : year);
        };
        year = determineYear(years[0]);
        endYear = Math.max(year, determineYear(years[1] || ""));
        year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
        endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
        this.yearshtml += "<select tabindex='-1' class='oj-datepicker-year " + (wDisabled ? "oj-disabled' disabled" : "oj-enabled'") + " data-handler='selectYear' data-event='change' role='listbox'>";
        for (;year <= endYear;year++)
        {
          this.yearshtml += "<option role='option' value='" + year + "' aria-selected='" + (year === drawYear ? "true' selected='selected'" : "false'") + ">" + yearDisplay.format(converterUtils.dateToLocalIso(new Date(year, drawMonth, 1))) + "</option>";
        }
        this.yearshtml += "</select>";

        html += this.yearshtml;
        this.yearshtml = null;
      }
    }

    if (positionOfMonthToYear === "after")
    {
      html += (secondary || !((changeMonth === "select") && (changeYear === "select")) ? " " : "") + monthHtml;
    }

    if(!isMultiMonth || !secondary) {
      html += "<span class='oj-helper-hidden-accessible' id='" + this._GetSubId(this._CALENDAR_DESCRIPTION_ID) + "'>" + monthNames[drawMonth] + " " + yearDisplay.format(converterUtils.dateToLocalIso(new Date(drawYear, drawMonth, 1))) + "</span>";
      html += "<span class='oj-helper-hidden-accessible' id='" + this._GetSubId(this._DATEPICKER_DESCRIPTION_ID) + "'>" + this._EscapeXSS(this.getTranslatedString("datePicker")) + "</span>";
    }

    html += "</div>";// Close datepicker_header
    return html;
  },

  /**
   * Adjust one of the date sub-fields.
   *
   * @private
   */
  _adjustInstDate : function __adjustInstDate(offset, period)
  {
    var year = this._drawYear + (period === "Y" ? offset : 0),
        month = this._drawMonth + (period === "M" ? offset : 0),
        day = Math.min(this._currentDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
        date = new Date(year, month, day);

    this._currentDay = date.getDate();
    this._drawMonth = this._currentMonth = date.getMonth();
    this._drawYear = this._currentYear = date.getFullYear();
  },

  /**
   * Determine the number of months to show.
   *
   * @private
   */
  _getNumberOfMonths : function __getNumberOfMonths()
  {
    var numMonths = this.options["datePicker"]["numberOfMonths"];
    numMonths = typeof numMonths === "string" ? parseInt(numMonths, 10) : numMonths;
    return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
  },

  /**
   * Determine the current maximum date - ensure no time components are set.
   *
   * @private
   */
  _getMinMaxDate : function __getMinMaxDate(minMax)
  {
    return this._determineDate(this.options[minMax], null);
  },

  /**
   * Find the number of days in a given month.
   *
   * @private
   */
  _getDaysInMonth : function __getDaysInMonth(year, month)
  {
    return 32 - new Date(year, month, 32).getDate();
  },

  /**
   * Find the day of the week of the first of a month.
   *
   * @private
   */
  _getFirstDayOfMonth : function __getFirstDayOfMonth(year, month)
  {
    return new Date(year, month, 1).getDay();
  },

  /**
   * Determines if we should allow a "next/prev" month display change.
   *
   * @private
   */
  _canAdjustMonth : function __canAdjustMonth(offset, curYear, curMonth)
  {
    var numMonths = this._getNumberOfMonths(), date = new Date(curYear, curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1);

    if (offset < 0)
    {
      date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
    }
    return this._isInRange(date);
  },

  /**
   * Is the given date in the accepted range?
   *
   * @private
   */
  _isInRange : function __isInRange(date)
  {
    var yearSplit, currentYear, minDate = this._getMinMaxDate("min"), maxDate = this._getMinMaxDate("max"), minYear = null, maxYear = null, years = this.options["datePicker"]["yearRange"];
    if (years)
    {
      yearSplit = years.split(":");
      currentYear = new Date().getFullYear();
      minYear = parseInt(yearSplit[0], 10);
      maxYear = parseInt(yearSplit[1], 10);
      if (yearSplit[0].match(/[+\-].*/))
      {
        minYear += currentYear;
      }
      if (yearSplit[1].match(/[+\-].*/))
      {
        maxYear += currentYear;
      }
    }

    return ((!minDate || date.getTime() >= minDate.getTime()) && (!maxDate || date.getTime() <= maxDate.getTime()) && (!minYear || date.getFullYear() >= minYear) && (!maxYear || date.getFullYear() <= maxYear));
  },

  _getCalendarTitle : function __getCalendarTitle()
  {
    return this._EscapeXSS(this.getTranslatedString("tooltipCalendar" + (this.options["disabled"] ? "Disabled" : "")));
  },

  /**
   * To disable or enable the widget
   *
   * @private
   * @instance
   */
  _disableEnable : function __disableEnable(val)
  {
    if (this._triggerNode)
    {
      disableEnableSpan(this._triggerNode.children(), val);
      this._triggerNode.find("." + this._TRIGGER_CALENDAR_CLASS).attr("title", this._getCalendarTitle());
    }

    if(val)
    {
      this.hide();
    }

    //need to update the look, note that if it is displaying the datepicker dropdown it would be hidden in _setOption function
    if(this._isInLine)
    {
      this._updateDatepicker();
    }
  },

  /**
   * Invoke super only if it is not inline
   *
   * @ignore
   * @protected
   * @override
   * @instance
   * @memberof! oj.ojInputDate
   */
  _AppendInputHelper : function __AppendInputHelper()
  {
    if (!this._isInLine)
    {
      this._superApply(arguments);
    }
  },

  /**
   * This handler will set the value from the input text element.
   *
   * @ignore
   * @protected
   * @override
   * @param {Event} event
   * @instance
   * @memberof! oj.ojInputDate
   */
  _onBlurHandler : function __onBlurHandler(event)
  {
    if(this._isInLine)
    {
      return;
    }

    this._superApply(arguments);
  },

  /**
   * This handler will be invoked when keydown is triggered for this.element. When is of inline ignore the keydowns
   *
   * @ignore
   * @protected
   * @override
   * @param {Event} event
   * @instance
   * @memberof! oj.ojInputDate
   */
  _onKeyDownHandler : function __onKeyDownHandler(event)
  {
    if(this._isInLine)
    {
      return;
    }

    this._superApply(arguments);

    var kc = $.ui.keyCode,
        handled = false;

    if (this._datepickerShowing())
    {

      switch (event.keyCode)
      {
        case kc.TAB:
          this.hide();
          break;
        case kc.ESCAPE:
          this.hide();
          handled = true;
          break;
        case kc.UP: ;
        case kc.DOWN:
          this._dpDiv.find(".oj-datepicker-calendar").focus();
          handled = true;
          break;
        default: ;
      }

    }
    else
    {

      switch (event.keyCode)
      {
        case kc.UP: ;
        case kc.DOWN:
          this._SetValue(this._GetDisplayValue(), event);
          this.show();
          handled = true;
          break;
        default: ;
      }

    }

    if (handled)
    {
      event.preventDefault();
      event.stopPropagation();
      return false;
    }
  },

  /**
   * Ignore for when of inline, since then this.element would be of div and has a funky nature
   *
   * @param {String} displayValue of the new string to be displayed
   *
   * @memberof! oj.ojInputDate
   * @instance
   * @protected
   * @override
  */
  _SetDisplayValue : function (displayValue)
  {
    if(!this._isInLine)
    {
      this._superApply(arguments);
    }

    this._setCurrentDate(this._getDate());

    //so this is a change in behavior from original design. Previously it was decided that app developer
    //would have to invoke refresh to render the calendar after setting the new value programatically; however now it is
    //required to hook it in when _SetDisplayValue is invoked [can't use _SetValue b/c that function is not invoked
    //when developer invokes ("option", "value", oj.IntlConverterUtils.dateToLocalIso(new Date()))
    if(this._datepickerShowing())
    {
      this._updateDatepicker();
    }
  },

  /**
   * Need to override since apparently we allow users to set the converter to null, undefined, and etc and when
   * they do we use the default converter
   *
   * @return {Object} a converter instance or null
   *
   * @memberof! oj.ojInputDate
   * @instance
   * @protected
   * @override
   */
  _GetConverter : function ()
  {
    return this.options['converter'] ?
        this._superApply(arguments) :
        $["oj"]["ojInputDate"]["prototype"]["options"]["converter"];
  },

  /**
   * @ignore
   * @protected
   * @override
   * @instance
   * @memberof! oj.ojInputDate
   */
  _GetElementValue : function ()
  {
    return this.options['value'] || "";
  },

  /**
   * @protected
   * @override
   * @instance
   * @memberof! oj.ojInputDate
   * @return {string}
   */
  _GetDefaultStyleClass : function ()
  {
    return "oj-inputdate";
  },

  /**
   * Sets up the default dateTimeRange and dateRestriction validators.
   *
   * @ignore
   * @protected
   * @override
   * @instance
   * @memberof! oj.ojInputDate
   */
  _GetImplicitValidators : function ()
  {
    var ret = this._superApply(arguments);

    if(this.options['min'] != null || this.options['max'] != null)
    {
      //need to alter how the default validators work as validators are now immutable
      this._datePickerDefaultValidators[oj.ValidatorFactory.VALIDATOR_TYPE_DATETIMERANGE] = this._getValidator("min");
    }

    if(this.options["dayFormatter"] != null)
    {
      this._datePickerDefaultValidators[oj.ValidatorFactory.VALIDATOR_TYPE_DATERESTRICTION] = this._getValidator("dayFormatter");
    }

    return $.extend(this._datePickerDefaultValidators, ret);
  },

  /**
   * Notifies the component that its subtree has been removed from the document programmatically after the component has
   * been created
   * @memberof! oj.ojInputDate
   * @instance
   * @protected
   */
  _NotifyDetached: function()
  {
    this.hide();
  },

  /**
   * Notifies the component that its subtree has been made hidden programmatically after the component has
   * been created
   * @memberof! oj.ojInputDate
   * @instance
   * @protected
   */
  _NotifyHidden: function()
  {
    this.hide();
  },

  _getValidator : function (key)
  {
    var validator = null;

    if(key === "min" || key === "max")
    {

      validator = getImplicitDateTimeRangeValidator(this.options, this._GetConverter());
    }
    else if(key === "dayFormatter")
    {
      var dateRestrictionOptions = {'dayFormatter': this.options["dayFormatter"],
                                    'converter': this._GetConverter() };

      $.extend(dateRestrictionOptions, this.options['translations']['dateRestriction'] || {});
      validator = oj.Validation.validatorFactory(oj.ValidatorFactory.VALIDATOR_TYPE_DATERESTRICTION).createValidator(dateRestrictionOptions);
    }

    return validator;
  },


  /**
   * Gets today's date w/o time
   *
   * @private
   * @return {Object} date
   */
  _getTodayDate : function __getTodayDate()
  {
    var date = new Date();
    date.setHours(0);
    date.setMinutes(0);
    date.setSeconds(0);
    date.setMilliseconds(0);
    return date;
  },

  /**
   * Retrieve the default date shown on opening.
   *
   * @private
   */
  _getDate : function __getDate()
  {
    return this._determineDate(this.options['value'], this._getTodayDate());
  },

  /**
   * Return the subcomponent node represented by the documented locator attribute values. <br/>
   * If the locator is null or no subId string is provided then this method returns the element that
   * this component was initalized with. <br/>
   * If a subId was provided but a subcomponent node cannot be located this method returns null.
   *
   * <p>If the <code class="prettyprint">locator</code> or its <code class="prettyprint">subId</code> is
   * <code class="prettyprint">null</code>, then this method returns the element on which this component was initalized.
   *
   * <p>If a <code class="prettyprint">subId</code> was provided but no corresponding node
   * can be located, then this method returns <code class="prettyprint">null</code>.
   *
   * @expose
   * @override
   * @memberof oj.ojInputDate
   * @instance
   *
   * @param {Object} locator An Object containing, at minimum, a <code class="prettyprint">subId</code>
   * property. See the table for details on its fields.
   *
   * @property {string=} locator.subId - A string that identifies a particular DOM node in this component.
   *
   * <p>The supported sub-ID's are documented in the <a href="#subids-section">Sub-ID's</a> section of this document.
   *
   * @property {number=} locator.index - A zero-based index, used to locate a message content node
   * or a hint node within the popup.
   * @returns {Element|null} The DOM node located by the <code class="prettyprint">subId</code> string passed in
   * <code class="prettyprint">locator</code>, or <code class="prettyprint">null</code> if none is found.
   *
   * @example <caption>Get the node for a certain subId:</caption>
   * var node = $( ".selector" ).ojInputDate( "getNodeBySubId", {'subId': 'oj-some-sub-id'} );
   */
  getNodeBySubId: function(locator)
  {
    var node = null,
        subId = locator && locator['subId'],
        dpDiv = this._dpDiv;

    if(subId)
    {
      switch(subId)
      {
      case "oj-datepicker-content": node = dpDiv[0]; break;
      case "oj-inputdatetime-calendar-icon": node = $(".oj-inputdatetime-calendar-icon", this._triggerNode)[0]; break;
      case "oj-datepicker-prev-icon": node = $(".oj-datepicker-prev-icon", dpDiv)[0]; break;
      case "oj-datepicker-next-icon": node = $(".oj-datepicker-next-icon", dpDiv)[0]; break;
      case "oj-datepicker-month": node = $(".oj-datepicker-month", dpDiv)[0]; break;
      case "oj-datepicker-year": node = $(".oj-datepicker-year", dpDiv)[0]; break;
      case "oj-datepicker-current": node = $(".oj-datepicker-current", dpDiv)[0]; break;
      case "oj-inputdatetime-date-input": node = this._isInLine ? null : this.element[0]; break;
      default: node = null;
      }
    }

    // Non-null locators have to be handled by the component subclasses
    return node || this._superApply(arguments);
  },

  /**
   * Returns the subId string for the given child DOM node.  For more details, see
   * <a href="#getNodeBySubId">getNodeBySubId</a>.
   *
   * @expose
   * @override
   * @memberof oj.ojInputDate
   * @instance
   *
   * @param {!Element} node - child DOM node
   * @return {string|null} The subId for the DOM node, or <code class="prettyprint">null</code> when none is found.
   *
   * @example <caption>Get the subId for a certain DOM node:</caption>
   * // Foo is ojInputNumber, ojInputDate, etc.
   * var subId = $( ".selector" ).ojFoo( "getSubIdByNode", nodeInsideComponent );
   */
  getSubIdByNode: function(node)
  {
    var dpDiv = this._dpDiv,
        subId = null,
        checks = [{"selector": ".oj-inputdatetime-calendar-icon", "ele": this._triggerNode},
                  {"selector": ".oj-datepicker-prev-icon", "ele": dpDiv},
                  {"selector": ".oj-datepicker-next-icon", "ele": dpDiv},
                  {"selector": ".oj-datepicker-month", "ele": dpDiv},
                  {"selector": ".oj-datepicker-year", "ele": dpDiv},
                  {"selector": ".oj-datepicker-current", "ele": dpDiv}];

    if(node === dpDiv[0])
    {
      return "oj-datepicker-content";
    }
    if(!this._isInLine && node === this.element[0])
    {
      return "oj-inputdatetime-date-input";
    }

    for(var i=0, j=checks.length; i < j; i++)
    {
      var map = checks[i],
          entry = $(map["selector"], map["ele"]);

      if(entry.length === 1 && entry[0] === node)
      {
        subId = map["selector"].substr(1);
        break;
      }
    }

    return subId || this._superApply(arguments);
  },

  /**
   * Hides the datepicker
   *
   * @expose
   * @memberof! oj.ojInputDate
   * @instance
   */
  hide : function __hide()
  {

    if (this._datepickerShowing() && !this._isInLine)
    {
      this._popUpDpDiv.ojPopup("close");

      if(this.options["datePicker"]["showOn"] === "focus")
      {
        this._ignoreShow = true;
      }
      this.element.focus();
    }

    return this;
  },

  /**
   * @expose
   * @memberof! oj.ojInputDate
   * @instance
   */
  refresh : function __refresh()
  {
    if(this._triggerNode)
    {
      this._triggerNode.find("." + this._TRIGGER_CALENDAR_CLASS).attr("title", this._getCalendarTitle());
    }
    return this._superApply(arguments) || this;
  },

  /**
   * Shows the datepicker
   *
   * @expose
   * @memberof! oj.ojInputDate
   * @instance
   */
  show : function __show()
  {
    if (this._datepickerShowing() || this.options["disabled"] || this.options["readOnly"])
    {
      return;
    }

    if (this._ignoreShow)
    {
      //set within hide or elsewhere and focus is placed back on this.element
      this._ignoreShow = false;
      return;
    }

    var rtl = this._IsRTL();

    //to avoid flashes on Firefox
    this._dpDiv.empty();
    this._updateDatepicker();

    var position = oj.PositionUtils.normalizeHorizontalAlignment({"my" : "start top", "at" : "start bottom", "of" : this.element, "collision" : "fit"}, rtl);
    this._popUpDpDiv.ojPopup("open", this._triggerIcon, position);

    return this;
  }

});

// Add custom getters for properties from the LocaleElements bundle
oj.Components.setDefaultOptions(
  {
    'ojInputDate':
    {
      'firstDayOfWeek': oj.Components.createDynamicPropertyGetter(
        function()
        {
           return oj.LocaleData.getFirstDayOfWeek();
        }),

      'dayWide': oj.Components.createDynamicPropertyGetter(
        function()
        {
           return oj.LocaleData.getDayNames("wide");
        }),

      'dayNarrow': oj.Components.createDynamicPropertyGetter(
        function()
        {
            return oj.LocaleData.getDayNames("narrow");
        }),

      'monthWide': oj.Components.createDynamicPropertyGetter(
        function()
        {
           return oj.LocaleData.getMonthNames("wide");
        }),

      'monthAbbreviated': oj.Components.createDynamicPropertyGetter(
        function()
        {
           return oj.LocaleData.getMonthNames("abbreviated");
        })
    }
  }
);

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

/**
 * <p>Sub-ID for the ojInputDate and ojInputDateTime component's input element. Note that if component is inline for
 * ojInputDate it would return null whereas ojInputDateTime would return the input element of the internally created
 * ojInputTime component.
 *
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 *
 * @ojsubid
 * @member
 * @name oj-inputdatetime-date-input
 * @memberof oj.ojInputDate
 * @instance
 *
 * @example <caption>Get the node for the input element:</caption>
 * var node = $( ".selector" ).ojInputDate( "getNodeBySubId", {'subId': 'oj-inputdatetime-date-input'} );
 */

/**
 * <p>Sub-ID for the calendar drop down node.
 *
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 *
 * @ojsubid
 * @member
 * @name oj-datepicker-content
 * @memberof oj.ojInputDate
 * @instance
 *
 * @example <caption>Get the calendar drop down node:</caption>
 * // Foo is ojInputDate or ojInputDateTime.
 * var node = $( ".selector" ).ojFoo( "getNodeBySubId", {'subId': 'oj-datepicker-content'} );
 */

/**
 * <p>Sub-ID for the calendar icon that triggers the calendar drop down.
 *
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 *
 * @ojsubid
 * @member
 * @name oj-inputdatetime-calendar-icon
 * @memberof oj.ojInputDate
 * @instance
 *
 * @example <caption>Get the calendar icon that triggers the calendar drop down:</caption>
 * // Foo is ojInputDate or ojInputDateTime.
 * var node = $( ".selector" ).ojFoo( "getNodeBySubId", {'subId': 'oj-inputdatetime-calendar-icon'} );
 */

/**
 * <p>Sub-ID for the previous month icon.
 *
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 *
 * @ojsubid
 * @member
 * @name oj-datepicker-prev-icon
 * @memberof oj.ojInputDate
 * @instance
 *
 * @example <caption>Get the previous month icon:</caption>
 * // Foo is ojInputDate or ojInputDateTime.
 * var node = $( ".selector" ).ojFoo( "getNodeBySubId", {'subId': 'oj-datepicker-prev-icon'} );
 */

/**
 * <p>Sub-ID for the next month icon.
 *
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 *
 * @ojsubid
 * @member
 * @name oj-datepicker-next-icon
 * @memberof oj.ojInputDate
 * @instance
 *
 * @example <caption>Get the next month icon:</caption>
 * // Foo is ojInputDate or ojInputDateTime.
 * var node = $( ".selector" ).ojFoo( "getNodeBySubId", {'subId': 'oj-datepicker-next-icon'} );
 */

/**
 * <p>Sub-ID for the month span or select element.
 *
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 *
 * @ojsubid
 * @member
 * @name oj-datepicker-month
 * @memberof oj.ojInputDate
 * @instance
 *
 * @example <caption>Get the month span or select element:</caption>
 * // Foo is ojInputDate or ojInputDateTime.
 * var node = $( ".selector" ).ojFoo( "getNodeBySubId", {'subId': 'oj-datepicker-month'} );
 */

/**
 * <p>Sub-ID for the year span or select element.
 *
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 *
 * @ojsubid
 * @member
 * @name oj-datepicker-year
 * @memberof oj.ojInputDate
 * @instance
 *
 * @example <caption>Get the year span or select element:</caption>
 * // Foo is ojInputDate or ojInputDateTime.
 * var node = $( ".selector" ).ojFoo( "getNodeBySubId", {'subId': 'oj-datepicker-year'} );
 */

/**
 * <p>Sub-ID for the current/today button for button bar.
 *
 * <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and
 * <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
 *
 * @ojsubid
 * @member
 * @name oj-datepicker-current
 * @memberof oj.ojInputDate
 * @instance
 *
 * @example <caption>Get the current/today button for button bar:</caption>
 * // Foo is ojInputDate or ojInputDateTime.
 * var node = $( ".selector" ).ojFoo( "getNodeBySubId", {'subId': 'oj-datepicker-current'} );
 */