/**
* Copyright (c) 2014, Oracle and/or its affiliates.
* All rights reserved.
*/
(function() {
/*!
* JET Radioset @VERSION
*/
/**
* @ojcomponent oj.ojRadioset
* @augments oj.editableValue
* @since 0.6
* @classdesc
* <p>
* The JET Radioset component (ojRadioset) enhances a group of
* <code class="prettyprint">input type="radio"</code> elements. It
* manages the selected value of the group and it adds required validation.
* It also adds and removes the correct
* oj-* styles to the dom elements so it has the JET styling and is themable.
* </p>
* <p>To use an ojRadioset, group all the inputs and their labels within a
* container dom element, e.g., <code class="prettyprint">div</code>.
* For accessibility, set <code class="prettyprint">aria-labelledby</code> on
* this container dom element.
* Also set each input's <code class="prettyprint">id</code> attribute, and
* refer to that in the input's label's
* <code class="prettyprint">for</code> attribute.
* Then create the ojRadioset on this container dom element.
* </p>
* <p>
* The <code class="prettyprint">fieldset</code>/<code class="prettyprint">legend</code> elements
* are not a supported way
* to group and label ojRadioset, so <code class="prettyprint">fieldset</code> cannot be the
* container dom element on which you create the ojRadioset.
* Grouping with a <code class="prettyprint">div</code> element and using
* a <code class="prettyprint">label</code> element allows you to
* lay out your labels/fields in more ways than if you used a fieldset/legend.
* Both are equally accessible.
* </p>
* <p>
* Radioset is used by selecting a container element which contains the
* radio input elements and calling <code class="prettyprint">ojRadioset()</code>.
* You can enable and disable a radio set,
* which will enable and disable all contained radios.
* </p>
* <p>
* Radioset does not have a readOnly option since HTML does not support
* readonly on radios and checkboxes.
* </p>
*
*
* <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="a11y-section">
* Accessibility
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#a11y-section"></a>
* </h3>
*
* <p>JET Radioset takes care of setting
* <code class="prettyprint">role="radiogroup"</code> on the radioset element.
*
* <p>As shown in the online demos, the application is responsible for applying
* <code class="prettyprint">aria-labelledby</code>
* to point to the radioset's <code class="prettyprint">label</code> element for the group of radios.
* <p>Disabled content: JET supports an accessible luminosity contrast ratio,
* as specified in <a href="http://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast">WCAG 2.0 - Section 1.4.3 "Contrast"</a>,
* in the themes that are accessible. (See the "Theming" chapter of the JET Developer Guide for more information on which
* themes are accessible.) Note that Section 1.4.3 says that text or images of text that are part of an inactive user
* interface component have no contrast requirement. Because disabled content may not meet the minimum contrast ratio
* required of enabled content, it cannot be used to convey meaningful information.<p>
*
*
* <h3 id="label-section">
* Label and Radioset
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#label-section"></a>
* </h3>
* <p>
* For accessibility, you should associate a <code class="prettyprint">label</code> element with the
* radioset by putting an <code class="prettyprint">id</code> on the label, and then setting the
* <code class="prettyprint">aria-labelledby</code> attribute on the radioset dom to be the label's
* <code class="prettyprint">id</code>.
* Note: The radioset's label is not the same as the label for each radio. The
* radioset's label will have the required and help information on it,
* not the label for each radio.
* </p>
* <p>
* The component will decorate its associated label with required and help
* information, if the <code class="prettyprint">required</code> and
* <code class="prettyprint">help</code> options are set.
* </p>
*
* * <h3 id="styling-section">
* Styling
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#styling-section"></a>
* </h3>
* <table class="generic-table styling-table">
* <thead>
* <tr>
* <th>Class(es)</th>
* <th>Description</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>oj-choice-row</td>
* <td><p>Used to line up the input and the label with each other on the same line, and
* following input and label on the next line.
*
* <p>The class is applied as follows:
*
* <ul>
* <li>The class must be applied to the span surrounding the input and label to keep them aligned.</li>
* </ul>
* </td>
* </tr>
* <tr>
* <td>oj-choice-row-inline</td>
* <td><p>Used to line up the input and the label with each other on the same line, and
* following input and label on the same line as well.
*
* <p>The class is applied as follows:
*
* <ul>
* <li>The class must be applied to the span surrounding the input and label to keep them aligned.</li>
* </ul>
* </td>
* </tr>
* </tbody>
* </table>
* <h3 id="jqui2jet-section">
* JET for jQuery UI developers
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#jqui2jet-section"></a>
* </h3>
*
* <p>
* All JQUI and JET components inherit <code class="prettyprint">disable()</code>
* and <code class="prettyprint">enable()</code> methods from the base class.
* This API duplicates the functionality of the
* <code class="prettyprint">disabled</code> option.
* In JET, to keep the API as lean as possible, we have chosen not to document
* these methods outside of this section.
* </p>
* <!-- - - - - Above this point, the tags are for the class.
* Below this point, the tags are for the constructor (initializer). - - - - - - -->
*
* @desc Creates a JET Radioset.
* For JET Radioset, if the DOM changes (for example, you add/remove a radio, or change the disabled
* attribute on a radio),
* you should <code class="prettyprint">refresh()</code>.
*
* @param {Object=} options a map of option-value pairs to set on the component
*
* @example <caption>Initialize the radioset with no options specified:</caption>
* $(".selector").ojRadioset();
*
* @example <caption>Initialize the radioset with some options and callbacks specified:</caption>
* $( ".selector" ).ojRadioset( { "value": "copy", "valuechange":
* function( event, ui ) {alert("valuechanged from " + ui.previousValue + " to " + ui.value);} } );
* @example <caption>Initialize component using widget API</caption>
* <label id="grouplabel">Greetings</label>
* <div id="radioset" aria-labelledby="grouplabel">
* <input id="helloid" value="hello" type="radio" name="greetings"/>
* <label for="helloid"/>Hello</label>
* <input id="bonjourid" value="bonjour" type="radio" name="greetings"/>
* <label for="bonjourid"/>Bonjour</label>
* <input id="ciaoid" value="ciao" type="radio" name="greetings"/>
* <label for="ciaoid"/>Ciao</label>
* <div>
* <br/>
* // set the value to "ciao". (The 'ciao' radio will be checked)
* $("#radioset").ojRadioset({'option', 'value', 'ciao'});
*
* @example <caption>Initialize a radioset via the JET
* <code class="prettyprint">ojComponent</code> binding:</caption>
* <label id="grouplabel">Time</label>
* <div id="radioset" data-bind="ojComponent: {component: 'ojRadioset', value: 'night'}
* aria-labelledby="grouplabel" >
* <input id="morningid" value="morning" type="radio" name="time"/>
* <label for="morningid"/>Morning</label>
* <input id="nightid" value="night" type="radio" name="time"/>
* <label for="nightid"/>Night</label>
* <div>
* <br/>
* @example <caption>Using knockout, value bind to observable:</caption>
* <label id="grouplabel">Time</label>
* <div id="radioset" data-bind="ojComponent: {component: 'ojRadioset',
* value: currentTime}
* aria-labelledby="grouplabel" >
* <input id="morningid" value="morning" type="radio" name="time"/>
* <label for="morningid"/>Morning</label>
* <input id="nightid" value="night" type="radio" name="time"/>
* <label for="nightid"/>Night</label>
* <div>
* <br/>
* // in the model, make the currentTime variable a knockout observable.
* // The model and the component's value option will stay in sync. Change the
* // component's value option and the model will change. Change the model,
* // and the component's value option will change. Click on a radio, and both
* // will change.
* self.currentTime = ko.observable("night");
*/
oj.__registerWidget("oj.ojRadioset", $['oj']['editableValue'],
{
version : "1.0.0",
defaultElement : "<div>",
widgetEventPrefix : "oj",
options :
{
/**
* <p>
* Disabled <code class="prettyprint">true</code> disables the component and disables all the
* inputs/labels.
* Disabled <code class="prettyprint">false</code> enables the component, and leaves the
* inputs' <code class="prettyprint">disabled</code> property as it is in the dom.
* <p>
* After create time, the disabled state should be set via this API,
* not by setting the underlying DOM attribute.
*
* <p>The 2-way <code class="prettyprint">disabled</code> binding offered by
* the <code class="prettyprint">ojComponent</code> binding
* should be used instead of Knockout's built-in <code class="prettyprint">disable</code>
* and <code class="prettyprint">enable</code> bindings,
* as the former sets the API, while the latter sets the underlying DOM attribute.
*
* @example <caption>Initialize component with <code class="prettyprint">disabled</code> option:</caption>
* $(".selector").ojRadioset({"disabled": true});
*
* @expose
* @type {?boolean}
* @default <code class="prettyprint">false</code>
* @public
* @instance
* @memberof oj.ojRadioset
*/
disabled: false,
/**
* The value of the component.
*
* <p>
* When <code class="prettyprint">value</code> option changes due to programmatic
* intervention, the component always clears all messages and runs deferred validation, and
* always refreshes UI display value.</br>
*
* <h4>Clearing Messages</h4>
* <ul>
* <li>All messages are cleared. This includes
* <code class="prettyprint">messagesHidden</code>, <code class="prettyprint">messagesShown</code>
* and <code class="prettyprint">messagesCustom</code> options.</li>
* </ul>
*
*
* <h4>Running Validation</h4>
* <ul>
* <li>component always runs deferred validation; if there is a validation error the
* <code class="prettyprint">messagesHidden</code> option is updated.</li>
* </ul>
* </p>
*
* @example <caption>Initialize the component with the <code class="prettyprint">value</code> option specified:</caption>
* $(".selector").ojRadioset({'value': 'coffee'});<br/>
* @example <caption>Get or set <code class="prettyprint">value</code> option, after initialization:</caption>
* // Getter: returns 'coffee'
* $(".selector").ojRadioset("option", "value");
* // Setter: sets 'tea'
* $(".selector").ojRadioset("option", "value", 'tea');
*
* @expose
* @access public
* @instance
* @default <code class="prettyprint">null</code>
* When the option is not set, the value of the checked radio is used, if a radio is checked.
* @memberof oj.ojRadioset
* @type {string|undefined|null}
*/
value: undefined
},
/**** start Public APIs ****/
/**
* Refreshes the radioset
* <p>A <code class="prettyprint">refresh()</code> is required
* when a radioset is programatically changed, like in the following circumstances:
* <ul>
* <li>After radios are added or removed or modified (without using ojRadioset) in the DOM.</li>
* <li>After a radio's disabled dom attribute is changed. Since there is no api to change an
* individual radio's disabled state, the only way to do this is to change the radio's
* disabled attribute in dom and then to call refresh on the ojRadioset.</li>
* </ul>
* @expose
* @memberof oj.ojRadioset
* @instance
* @example <caption>Invoke the <code class="prettyprint">refresh</code> method:</caption>
* $( ".selector" ).ojRadioset( "refresh" );
*/
refresh: function()
{
// Set _ResetComponentState. It is called first in EditableValue's refresh,
// and we override it in ojradioset to reset the this.$radios in case some have
// been deleted or overridden.
this._super();
this._setup();
},
/**
* Returns a jQuery object containing the element visually representing the radioset.
*
* <p>This method does not accept any arguments.
*
* @expose
* @memberof oj.ojRadioset
* @instance
* @return {jQuery} the radio
*/
widget : function ()
{
return this.uiRadioset;
},
/**** end Public APIs ****/
/**** start internal widget functions ****/
/**
* @protected
* @override
* @instance
* @memberof oj.ojRadioset
*/
_InitOptions : function (originalDefaults, constructorOptions)
{
var checkedRadio, domValue;
// TODO: When Max fixes it so that disabled can be undefined in initial options, then
// change it to undefined. Otherwise we aren't getting the defaultOptionValue of false,
// since the initializeOptionsFromDom code checks for undefined before it sets default.
// However, if we set disabled: false in our widget options, then this would be the merged
// value that was written to this.options, and we wouldn't have to use the defaultOptionValue here.
// There is a debate going on about which is the best thing to do.
// Right now I use disabled: null, but that's not so good, because disabled is a boolean,
// and when I init ojRadioset, then ask for the disabled option, it shouldn't return null.
var props = [{attribute: "disabled", defaultOptionValue: false, validateOption: true},
{attribute: "placeholder", defaultOptionValue: ""},
{attribute: "required", defaultOptionValue: false,
coerceDomValue: true, validateOption: true},
{attribute: "title", defaultOptionValue: ""}
// {attribute: "value", defaultOptionValue: null} // code below sets value
];
this._super(originalDefaults, constructorOptions);
oj.EditableValueUtils.initializeOptionsFromDom(props, constructorOptions, this);
this.$radios = this._findRadiosWithMatchingName();
// component, app, and constructor are merged into this.options.option by the time _InitOptions
// is called. Let's take this example:
// component (widget) default - 'foo'<br/>
// app default - 'bar'<br/>
// dom - 'lucy'<br/>
// constructorOptions['value'] - undefined<br/>
// this.options.option is set to 'bar' initially. We don't want to just take this value, because
// we want DOM value to win over the app and components default if DOM is set.
// Therefore, the component needs to check if the constructorOptions['value'] is
// undefined and if so, set value option to 'lucy' (the DOM value in this example). <br/>
//
// use DOM value if constructorOptions is undefined. if DOM value is undefined, then
// leave this.options["value"] alone since it's the merged app/widget default at this point.
if (constructorOptions['value'] === undefined)
{
// return the checked values by simply looking at DOM node
checkedRadio = this.$radios.filter(":checked");
domValue = (checkedRadio.length === 0) ? undefined : checkedRadio.val();
// we only use the dom if SOMETHING is checked. If nothing is checked, we stay with whatever
// is in this.options['value'].
if (domValue !== undefined)
{
// when defaulting from DOM we want to trigger optionChange to writeback new value
this.option("value", domValue, {'_context':{writeback: true, internalSet: true}});
}
// widget defaults to null
if (this.options['value'] === undefined)
this.options['value'] = null;
}
},
/**
* After _ComponentCreate and _AfterCreate,
* the widget should be 100% set up. this._super should be called first.
* @override
* @protected
* @memberof oj.ojRadioset
* @instance
*/
_ComponentCreate : function ()
{
var element = this.element;
this._super();
// first check to see if this.element is NOT a fieldset. If fieldset, throw error.
if (element.is("fieldset"))
{
throw new Error("ojRadioset cannot be bound to a fieldset. Use a div instead.");
}
// turn each radio into ojRadioCheckbox.
this.$radios._ojRadioCheckbox();
this.uiRadioset = element.addClass("oj-radioset oj-component")
.attr( "role", "radiogroup" );
this._on(this._events);
},
/**
* This is where we do things right after the component was created.
* this._super should be called first.
*
* @override
* @memberof oj.ojRadioset
* @instance
* @protected
*/
_AfterCreate: function()
{
this._super();
this._setup();
},
/**
* Resets this.$radios. This is called at the beginning of a refresh in EditableValue
* @override
* @memberof oj.ojRadioset
* @instance
* @protected
*/
_ResetComponentState : function ()
{
// we could have added, removed, or modified radios, so we need to re-find all the
// inputs on refresh and turn the ones that aren't already ojRadioCheckboxes into them.
this.$radios = this._findRadiosWithMatchingName();
// we have a rule for refresh: if we have a public API for it, then the app dev has to use the
// option, and not expect changing the dom will update the state with refresh.
// However, ojRadioset does not expose a public API for the individual radios' disabled state
// to the app developer. Our private ojRadioCheckbox component has a disabled option that
// our code has access to.
// For each radio, we need to look at the disabled attribute dom and update the
// ojradiocheckbox's disabled option.
// !! ensures it is a boolean
// update the private ojradiocheckbox component's disabled option to keep it in sync with the dom
this.$radios.filter( ".oj-radio" ).each(function()
{
var disabledValue = $( this ).attr("disabled") !== undefined ?
!!$( this ).prop("disabled") : false;
$( this )._ojRadioCheckbox("option", "disabled", disabledValue);
});
// no need to refresh the ojRadioCheckbox's that exist since we have options for everything.
// of the type=radio inputs that are not yet ojRadioCheckboxs, make them ojRadioCheckboxes.
// create ojRadioCheckboxes on any new ones.
this.$radios.not(".oj-radio")._ojRadioCheckbox();
},
/**
* Sets focus on the element that naturally gets focus. For radioset, this is the first radio <br/>
*
* @returns {*} a truthy value if focus was set to the intended element, a falsey value
* otherwise.
* @override
* @memberof oj.ojRadioset
* @instance
* @protected
* @since 0.7
*/
Focus : function ()
{
this._GetContentElement().first().focus();
return true;
},
/**
* Sets the disabled option onto the dom.
* This is a no-op for checkboxset since its root dom element is a div, and disabled is
* invalid on a div. If we did try to set disabled on the div, then restore attributes doesn't
* work correctly since it wasn't saved correctly.
* @param {Object} node - dom node
*
* @memberof oj.ojRadioset
* @instance
* @protected
* @since 1.0.0
*/
_SetDisabledDom : function(node)
{
// no-op
},
/**
* Returns a jquery object that is a set of elements that are input type radio
* and have the name of the first radio found.
*
* @return {Object} jquery object of all the radios within the root dom element
* that have the same 'name' attribute as the first radio found.
* @private
*/
_findRadiosWithMatchingName : function ()
{
//return this.element.find('input[type=radio]'); // simplest thing to do.
var element = this.element;
var first = element.find("input[type=radio]:first"),
name, allradios, selector;
if (first.length === 0)
{
oj.Logger.warn("Could not find any input type=radio within this element");
}
// get the name attribute of the first input radio
name = first.attr("name");
// find all input radios with matching name
if (name === undefined)
{
// search for all radios with no name
allradios = element.find("input[type=radio]");
// now loop and find the ones without 'name' attribute
return allradios.not("[name]");
}
else
{
// search for all radios with the name
selector = "input[type=radio][name=" + name + "]";
return element.find(selector);
}
},
// Override to set custom launcher
_NotifyContextMenuGesture: function(menu, event, eventType)
{
// Setting the launcher to the checked radio if any (since that's what's tabbable in mainstream browsers),
// else the first enabled radio (when no selection, all enabled radios are tabbable).
// Component owner should feel free to specify a different launcher if appropriate.
// See the superclass JSDoc for _OpenContextMenu for tips on choosing a launcher.
var radios = this.element.find("input[type=radio]");
var checked = radios.filter(":checked");
var launcher = checked.length ? checked : radios.filter(":enabled").first();
this._OpenContextMenu(event, eventType, {"launcher": launcher});
},
/**
* _setup is called on create and refresh. Use the disabled option to
* update the component. If the component's option is disabled, then
* leave it alone.
* @memberof oj.ojRadioset
* @instance
* @private
*/
_setup: function()
{
// at this point we already have this.$radios set to a list of radios for this radioset.
this._propagateDisabled(this.options.disabled );
},
_events :
{
'change' : function (event)
{
this._HandleChangeEvent(event);
}
},
/**
* @param {Event} event DOM event
* @override
* @protected
* @memberof oj.ojRadioset
*/
_HandleChangeEvent: function(event)
{
// TODO make sure the target is an input radio?
// TODO any more checks I need to do?
//alert("XYZ In _changeSetValue target is " + event.target + " And the value of the input is " + event.target.value);
// should I double check that the event.target is the same as the 'checked'?
// if (event.target === this.$radios.filter(":checked"))???
//
var submittedValue = this._GetDisplayValue();
// run full validation. There is no need to check if values have changed
// since for checkboxset/radiosetif we get into this function we know value has changed.
// passing in doValueChangeCheck: false will skip the new-old value comparison
this._SetValue(submittedValue, event, _sValueChangeCheckFalse);
},
/**
* Returns the display value that is ready to be passed to the converter.
*
* @param {Object} value the stored value if available that needs to be formatted for display
* @override
* @protected
* @memberof oj.ojRadioset
*/
_GetDisplayValue : function (value)
{
// return the value of the 'checked' radio
return this._GetElementValue();
},
/**
* Called when the display value on the element needs to be updated
* as a result of a value change.
* ojRadioset stores a String value, and this value matches the value
* of the currently checked radio. So, if we need to set the display value,
* what this means is we need to 'check' the radio whose value matches the
* displayValue.
*
* @param {String} displayValue of the new string to be displayed
* @override
* @protected
* @memberof oj.ojRadioset
*/
_SetDisplayValue : function (displayValue)
{
var valueFilter, radioWithMatchingValue;
// If we found a radio with a matching value, and it isn't already checked,
// then check it. If it is already checked, do nothing.
// If we can't find a radio with a matching value, then we uncheck
// all the radios. We do this so that the radio state matches the value option
// value.
valueFilter = "[value='" + displayValue + "']";
if (valueFilter !== undefined && this.$radios !== undefined)
{
radioWithMatchingValue =
this.$radios.filter(valueFilter);
// found a radio with a matching value
if (radioWithMatchingValue !== undefined && radioWithMatchingValue.length > 0)
{
// if not already checked, then mark as checked.
if (!radioWithMatchingValue.prop('checked'))
{
radioWithMatchingValue._ojRadioCheckbox("option", "checked", true);
}
}
else
{
// did not find any radios with a matching value, so uncheck
// all of them.
this.$radios._ojRadioCheckbox("option", "checked", false);
}
}
},
/**
* Returns the element's value. Normally, this is a call to this.element.val(),
* but in the case of ojRadioset, the element's value is really the value
* of the checked radio in the set.
* @override
* @protected
* @memberof oj.ojRadioset
*/
_GetElementValue : function ()
{
// "input:checked" selects radios that are currently checked as
// reflected in their boolean (true or false) checked property,
// which is affected when the user clicks the radio for example.
// for radio, there will be one or none checked;
// if none are checked, return null (checkedRadio.val() is undefined if nothing is checked)
var checkedRadio = this.$radios.filter(":checked");
if (checkedRadio.length === 0)
return null;
else
return checkedRadio.val();
},
/**
* 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}
* @memberof oj.ojRadioset
* @override
* @protected
*/
_GetDefaultStyleClass : function ()
{
return "oj-radioset";
},
/**
* Returns a jquery object of the elements representing the
* content nodes (input type=radio). This is used in EditableValue to add
* aria-describedby to the input when there is a help icon, to add
* aria-required and aria-invalid
* @protected
* @override
* @memberof oj.ojRadioset
*/
_GetContentElement : function ()
{
return this._findRadiosWithMatchingName();
},
/**
* Called when a aria-required attribute needs to be set or removed.
* Most inputs/selects need aria-required on the input element (aka 'content')
* But it is not legal to have aria-required on radio/checkboxes, nor on
* radiogroup/group.
* Subclasses can override to put aria-required where they want or not put it at all.
*
* @param {Object=} value the current value of the required option
* @memberof oj.ojRadioset
* @instance
* @protected
*/
_RefreshAriaRequired : function (value)
{
// Radiogroup/group does not support aria-required so we can't use it there.
},
/**
* Called to find out if aria-required is unsupported. This is needed for the label.
* It is not legal to have aria-required on radio/checkboxes, nor on
* radiogroup/group.
* @memberof oj.ojRadioset
* @instance
* @protected
* @return {boolean}
*/
_AriaRequiredUnsupported : function()
{
return true;
},
_propagateDisabled: function( disabled ) {
disabled = !!disabled;
this.$radios.each(function()
{
// this is the technique to use to call package-private functions
// Calling it like this.$radios.ojRadioCheckbox("__setAncestorComponentDisabled",value)
// gives an error because jquery prevents you from calling functions with an "_"
//
// This is how we handle 'disabled' for radioset. We don't change the radiocheckbox
// component's disabled option ever since if we do that we've lost what the initial disabled
// state is (we store the disabled dom value from the radio into its disabled option)
// and we need that when we refresh. Instead what we do
// is we mark if its ancestor (the radioset) is disabled or not. Then, when we render
// out the radios 'disabled' state, like oj-disabled, we look to see if it is 'effectively
// disabled' (see _IsEffectivelyDisabled call in ojRadioCheckbox), that is if its
// option is disabled OR its ancestor (the radioset) is disabled.
$( this ).data("oj-_ojRadioCheckbox").__setAncestorComponentDisabled(disabled);
});
this.$radios._ojRadioCheckbox("refreshDisabled");
},
/**
* Note that _setOption does not get called during create in the super class.
* It only gets called when the component has already been created.
* @override
* @private
*/
_setOption : function (key, value)
{
this._superApply(arguments);
if ( key === "disabled" )
{
this._propagateDisabled(value);
}
},
/**
* Return the subcomponent node represented by the documented locator attribute values.
* Test authors should target spinner sub elements using the following names:
* <ul>
* <li><b>oj-radioset-inputs</b>: An Array of the radioset's input elements</li>
* </ul>
*
* @expose
* @override
* @memberof oj.ojRadioset
* @instance
* @param {Object} locator An Object containing at minimum a subId property
* whose value is a string, documented by the component, that allows the component to
* look up the subcomponent associated with that string. It contains:
* <ul>
* <li>
* component: optional - in the future there may be more than one component
* contained within a page element
* </li>
* <li>
* subId: the string, documented by the component, that the component expects
* in getNodeBySubId to locate a particular subcomponent
* </li>
* </ul>
* @returns {Element|null} the subcomponent located by the subId string
* passed in locator, if found.
*/
getNodeBySubId: function(locator)
{
var node = this._super(locator), subId;
if (!node)
{
subId = locator['subId'];
if (subId === "oj-radioset-inputs") {
node = this.$radios.get();
}
}
// Non-null locators have to be handled by the component subclasses
return node || null;
},
/**
* @ignore
* @protected
* @override
*/
_destroy : function ()
{
var ret = this._super();
if (this.$radios)
{
this.$radios._ojRadioCheckbox( "destroy" );
}
return ret;
}
/**** end internal widget functions ****/
/**
* Removes the radioset functionality completely.
* This will return the element back to its pre-init state.
*
* <p>This method does not accept any arguments.
*
* @method
* @name oj.ojRadioset#destroy
* @memberof oj.ojRadioset
* @instance
*
* @example <caption>Invoke the <code class="prettyprint">destroy</code> method:</caption>
* $( ".selector" ).ojRadioset( "destroy" );
*/
});
////////////////// SUB-IDS //////////////////
/**
* <p>Sub-ID for the radioset's radios.
*
* <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and
* <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
*
* @ojsubid
* @member
* @name oj-radioset-inputs
* @memberof oj.ojRadioset
* @instance
*
* @example <caption>Get the nodes for the radios:</caption>
* var nodes = $( ".selector" ).ojRadioset( "getNodeBySubId", {'subId': 'oj-radioset-inputs'} );
*/
// Fragments:
/**
* <table class="keyboard-table">
* <thead>
* <tr>
* <th>Target</th>
* <th>Gesture</th>
* <th>Action</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Input</td>
* <td><kbd>Tap</kbd></td>
* <td>Select the input.</td>
* </tr>
* <tr>
* <td>Input's label</td>
* <td><kbd>Tap</kbd></td>
* <td>Select the corresponding input.</td>
* </tr>
* {@ojinclude "name":"labelTouchDoc"}
* </tbody>
* </table>
*
*
* @ojfragment touchDoc - Used in touch gesture section of classdesc, and standalone gesture doc
* @memberof oj.ojRadioset
*/
/**
* <table class="keyboard-table">
* <thead>
* <tr>
* <th>Target</th>
* <th>Key</th>
* <th>Action</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td rowspan="2">Input</td>
* <td><kbd>UpArrow</kbd></td>
* <td>Select the previous input in the group.</td>
* </tr>
* <tr>
* <td><kbd>DownArrow</kbd></td>
* <td>Select the next input in the group.</td>
* </tr>
* <tr>
* <td>Tabbable element immediately before radioset</td>
* <td><kbd>Tab</kbd></td>
* <td>Set focus to the checked radio input. If hints, title or messages exist in a notewindow,
* pop up the notewindow.</td>
* </tr>
* {@ojinclude "name":"labelKeyboardDoc"}
* </tbody>
* </table>
*
*
* @ojfragment keyboardDoc - Used in keyboard section of classdesc, and standalone gesture doc
* @memberof oj.ojRadioset
*/
// -----------------------------------------------------------------------------
// "private static members" shared by all radiosets
// -----------------------------------------------------------------------------
/**
* do not do a value change check in _SetValue
*/
var _sValueChangeCheckFalse = {doValueChangeCheck: false};
}());
Source: src/main/javascript/oracle/oj/ojradioset/ojradioset.js
Oracle® JavaScript Extension Toolkit (JET)
1.1.2
E65298-01