/**
* Copyright (c) 2014, Oracle and/or its affiliates.
* All rights reserved.
*/
(function() {
/*!
* JET Checkboxset @VERSION
*/
/**
* @ojcomponent oj.ojCheckboxset
* @augments oj.editableValue
* @since 0.6
* @classdesc
* <p>
* The JET Checkboxset component (ojCheckboxset) enhances a group of
* <code class="prettyprint">input type="checkbox"</code> elements and
* manages the selected values of the group. 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 ojCheckboxset, group one or more checkbox 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 ojCheckboxset 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 ojCheckboxset, so <code class="prettyprint">fieldset</code> cannot be the
* container dom element on which you create the ojCheckboxset.
* 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>
* Checkboxset is used by selecting a container element which contains the
* checkbox input elements and calling <code class="prettyprint">ojCheckboxset()</code>.
* You can enable and disable a checkbox set,
* which will enable and disable all contained checkboxes.
* </p>
* <p>
* Checkboxset does not have a readOnly option since HTML does not support
* readonly on radios and checkboxes.
* </p>
* <p>
* The component will decorate its associated label with required and help information, if
* the applicable API is set. See the <code class="prettyprint">required</code> and
* <code class="prettyprint">help</code>
* options. Use <code class="prettyprint">aria-labelledby</code> to associate the main label with
* the checkboxset component. Doing this also makes the checkboxset accessible.
* </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 Checkboxset takes care of setting <code class="prettyprint">role="group"</code>
* on the checkboxset element.
*
* <p>As shown in the online demos, the application is responsible for applying
* <code class="prettyprint">aria-labelledby</code>
* to point to the main label element for the group of checkboxes.
* <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 Checkboxset
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#label-section"></a>
* </h3>
* <p>
* For accessibility, you should associate a label element with the checkboxset
* by putting an <code class="prettyprint">id</code> on the label, and then setting the
* <code class="prettyprint">aria-labelledby</code> attribute on the checkboxset dom to be the
* label's id.
* Note: The checkboxset's label is not the same as the label for each checkbox.
* The checkboxset's label will have the required and help information on it,
* not the label for each checkbox.
* </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="eventHandling-section">
* Event Handling
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#eventHandling-section"></a>
* </h3>
* <ul>
* <li>optionChange(event, ui) - Type: ojoptionchange
* <p>
* Triggered if the value changes when the user interacts with the component
* (clicking on one of the checkboxe buttons); or if the value has
* changed programmatically via the value option.
* </li>
* </ul>
* <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 or re-initializes a JET Checkboxset. For JET components, re-initing
* is generally like a hard refresh, meaning it typically does everything
* <code class="prettyprint">refresh()</code> does, plus potentially a bit more.
* For JET Checkboxset, if the DOM changes (for example, you add/remove a checkbox, or change the disabled
* attribute on a checkbox), you should refresh.
*
* <p>Don't confuse the re-initializer with the <code class="prettyprint">option()</code> method,
* which (in one overload) also accepts a map of option-value pairs
* to set on the component, but does not re-init.
*
* @param {Object=} options a map of option-value pairs to set on the component
*
* @example <caption>Initialize the checkboxset with no options specified:</caption>
* $(".selector").ojCheckboxset();
*
* @example <caption>Initialize the checkboxset with some options and callbacks specified:</caption>
* $( ".selector" ).ojCheckboxset( { "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="checkboxset" aria-labelledby="grouplabel">
* <input id="helloid" value="hello" type="checkbox" name="greetings"/>
* <label for="helloid"/>Hello</label>
* <input id="bonjourid" value="bonjour" type="checkbox" name="greetings"/>
* <label for="bonjourid"/>Bonjour</label>
* <input id="ciaoid" value="ciao" type="checkbox" name="greetings"/>
* <label for="ciaoid"/>Ciao</label>
* <div>
* <br/>
* // set the value to "ciao". (The 'ciao' checkbox will be checked)
* $("#checkboxset").ojCheckboxset({'option', 'value', ['ciao']});
*
* @example <caption>Initialize a checkboxset via the JET <code class="prettyprint">ojComponent</code> binding:</caption>
* <label id="grouplabel">Time</label>
* <div id="checkboxset" data-bind="ojComponent: {component: 'ojCheckboxset', value: ['night']}
* aria-labelledby="grouplabel" >
* <input id="morningid" value="morning" type="checkbox" name="time"/>
* <label for="morningid"/>Morning</label>
* <input id="nightid" value="night" type="checkbox" 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="checkboxset" data-bind="ojComponent: {component: 'ojCheckboxset', value: currentTime}
* aria-labelledby="grouplabel" >
* <input id="morningid" value="morning" type="checkbox" name="time"/>
* <label for="morningid"/>Morning</label>
* <input id="nightid" value="night" type="checkbox" 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 checkbox, and both
* // will change.
* self.currentTime = ko.observable(["night"]);
*/
oj.__registerWidget("oj.ojCheckboxset", $['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
* disabled state 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").ojCheckboxset({"disabled": true});
*
* @expose
* @type {?boolean}
* @default <code class="prettyprint">false</code>
* @public
* @instance
* @memberof oj.ojCheckboxset
*/
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").ojCheckboxset({'value': ['coffee']});<br/>
* @example <caption>Get or set <code class="prettyprint">value</code> option, after initialization:</caption>
* // Getter: returns ['coffee']
* $(".selector").ojCheckboxset("option", "value");
* // Setter: sets ['coffee', 'tea']
* $(".selector").ojCheckboxset("option", "value", ['coffee', 'tea']);
*
* @expose
* @access public
* @instance
* @default <code class="prettyprint">[]</code>
* When the option is not set, value is set to an array built from the checked checkboxes, if
* any are checked.
* @memberof oj.ojCheckboxset
* @type {Array|undefined|null}
*/
value: undefined
},
/**** start Public APIs ****/
/**
* Refreshes the checkboxset
* <p>A <code class="prettyprint">refresh()</code> or re-init is required
* when a checkboxset is programatically changed, like in the following circumstances:
* <ul>
* <li>After checkboxes are added or removed or modified (without using ojCheckboxset) in the DOM.</li>
* <li>After a checkbox's disabled dom attribute is changed. Since there is no api to change an
* individual checkbox's disabled state, the only way to do this is to change the checkbox's
* disabled attribute in dom and then to call refresh on the ojCheckboxset.</li>
* </ul>
* @expose
* @memberof oj.ojCheckboxset
* @instance
* @example <caption>Invoke the <code class="prettyprint">refresh</code> method:</caption>
* $( ".selector" ).ojCheckboxset( "refresh" );
*/
refresh: function()
{
this._super();
this._setup();
},
/**
* Returns a jQuery object containing the element visually representing the checkboxset.
*
* <p>This method does not accept any arguments.
*
* @expose
* @memberof oj.ojCheckboxset
* @instance
* @return {jQuery} the checkbox
*/
widget : function ()
{
return this.uiCheckboxset;
},
/**** end Public APIs ****/
/**** start internal widget functions ****/
/**
* Overridden to set the options.value. When constructorOptions value is undefined,
* we read the CHECKED options on the checkboxes and build the value array from that.
*
* @memberof oj.ojCheckboxset
* @instance
* @protected
*/
_InitOptions : function (originalDefaults, constructorOptions)
{
var checkboxes, checkedValues = new Array(), selectedCheckbox, domValue, previousValue,
props = [{attribute: "disabled", defaultOptionValue: false, validateOption: true},
{attribute: "title", defaultOptionValue: ""},
{attribute: "placeholder", defaultOptionValue: ""},
//{attribute: "value", "defaultValue": null}, // code below sets value
{attribute: "required", defaultOptionValue: false,
coerceDomValue: true, validateOption: true
}];
this._super(originalDefaults, constructorOptions);
oj.EditableValueUtils.initializeOptionsFromDom(props, constructorOptions, this);
this.$checkboxes = this._findCheckboxesWithMatchingName();
// 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)
{
// constructor option for value is undefined. Then we check the dom.
selectedCheckbox = this.$checkboxes.filter(":checked");
if (selectedCheckbox.length > 0)
{
selectedCheckbox.each(function(){
checkedValues.push($(this).val());
});
domValue = checkedValues;
// when defaulting from DOM we want to trigger optionChange to writeback new value
this.option("value", domValue, {'_context':{writeback: true, internalSet: true}});
}
// if nothing is checked, we leave this.options["value"] as it is if not undefined, else
// the widget's default is [].
if (this.options["value"] === undefined)
this.options["value"] = [];
}
},
/**
* After _ComponentCreate and _AfterCreate,
* the widget should be 100% set up. this._super should be called first.
* @override
* @protected
* @memberof oj.ojCheckboxset
* @instance
*/
_ComponentCreate : function ()
{
this._super();
// first check to see if this.element is NOT a fieldset. If fieldset, throw error.
if (this.element.is("fieldset"))
{
throw new Error("ojCheckboxset cannot be bound to a fieldset. Use a div instead.");
}
// turn each checkbox into ojCheckbox. Do this first, since we need it
// in calls from 'create'.
this.$checkboxes._ojRadioCheckbox();
this.uiCheckboxset = this.element.addClass("oj-checkboxset oj-component")
.attr( "role", "group" );
this._on(this._events);
this._setup();
},
/**
* Resets this.checkboxes. This is called at the beginning of a refresh in EditableValue
* @override
* @memberof oj.ojCheckboxset
* @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.$checkboxes = this._findCheckboxesWithMatchingName();
// 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, ojCheckboxset does not expose a public API for the individual checkbox's disabled state
// to the app developer. Our private ojRadioCheckbox component has a disabled option that
// our code has access to.
// For each checkbox, 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.$checkboxes.filter( ".oj-checkbox" ).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.$checkboxes.not(".oj-checkbox")._ojRadioCheckbox();
},
/**
* Sets focus on the element that naturally gets focus. For radioset, this is the first checkbox <br/>
*
* @returns {*} a truthy value if focus was set to the intended element, a falsey value
* otherwise.
* @override
* @memberof oj.ojCheckboxset
* @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.ojCheckboxset
* @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 checkbox
* and have the name of the first checkbox found.
*
* @return {Object} jquery object of all the checkboxes within the root dom element
* that have the same 'name' attribute as the first checkbox found.
* @private
*/
_findCheckboxesWithMatchingName : function ()
{
//return this.element.find('input[type=checkbox]'); // simplest thing to do.
var first = this.element.find("input[type=checkbox]:first"),
name, allcheckboxes, selector;
if (first.length === 0)
{
oj.Logger.warn("Could not find any input type=checkbox within this element");
}
// get the name attribute of the first input checkbox
name = first.attr("name");
// find all input checkboxes with matching name
if (name === undefined)
{
// search for all checkboxes with no name
allcheckboxes = this.element.find("input[type=checkbox]");
// now loop and find the ones without 'name' attribute
return allcheckboxes.not("[name]");
}
else
{
// search for all checkboxes with the name
selector = "input[type=checkbox][name=" + name + "]";
return this.element.find(selector);
}
},
// Override to set custom launcher
_NotifyContextMenuGesture: function(menu, event, eventType)
{
// Setting the launcher to the first tabbable checkbox in the set.
// Component owner should feel free to specify a different launcher if appropriate,
// e.g. could specify the "current" checkbox rather than the first if desired.
// See the superclass JSDoc for _OpenContextMenu for tips on choosing a launcher.
var launcher = this.element.find("input[type=checkbox]:tabbable").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.ojCheckboxset
* @instance
* @private
*/
_setup: function()
{
// at this point we already have this.$checkboxes set to a list of checkboxes for this
// checkboxset
this._propagateDisabled(this.options.disabled );
},
_events :
{
'change' : function (event)
{
this._HandleChangeEvent(event);
}
},
/**
* @param {Event} event DOM event
* @override
* @protected
* @memberof oj.ojCheckboxset
*/
_HandleChangeEvent: function(event)
{
// TODO make sure the target is an input checkbox?
// 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.$checkboxes.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.ojCheckboxset
*/
_GetDisplayValue : function (value)
{
// return the value of the 'checked' checkboxes
return this._GetElementValue();
},
/**
* Called when the display value on the element needs to be updated
* as a result of a value change.
* ojCheckboxset stores an Array value, and this value matches the values
* of the currently checked checkboxes. So, if we need to set the display value,
* what this means is we need to 'check' the checkboxes whose values match the
* displayValue.
*
* @param {String} checkedBoxes of the new string to be displayed
* @override
* @protected
* @memberof oj.ojCheckboxset
*/
_SetDisplayValue : function (checkedBoxes)
{
var displayValue, valueFilter, checkboxWithMatchingValue;
this.$checkboxes._ojRadioCheckbox("option", "checked", false);
if (checkedBoxes != null)
{
// Uncheck all the checkboxes then iterate through and checkoff any matching checkboxes from the value
for(var i = 0; i < checkedBoxes.length; i++) {
displayValue = checkedBoxes[i];
valueFilter = "[value='" + displayValue + "']";
checkboxWithMatchingValue = this.$checkboxes.filter(valueFilter);
if (checkboxWithMatchingValue !== undefined && checkboxWithMatchingValue.length > 0)
{
// if not already checked, then mark as checked.
if (!checkboxWithMatchingValue.prop('checked'))
{
checkboxWithMatchingValue._ojRadioCheckbox("option", "checked", true);
}
}
}
}
},
/**
* Returns the element's value. Normally, this is a call to this.element.val(),
* but in the case of ojCheckboxset, the element's value is really the value
* of the checked checkboxes in the set.
* @override
* @protected
* @memberof oj.ojCheckboxset
*/
_GetElementValue : function ()
{
// "input:checked" selects checkboxes that are currently checked as
// reflected in their boolean (true or false) checked property,
// which is affected when the user clicks the checkbox for example.
// for checkbox, there will be one or none checked;
// if none are checked, return null (selectedCheckbox.val() is undefined if nothing is checked)
var checkedValues=new Array();
var selectedCheckbox = this.$checkboxes.filter(":checked");
if (selectedCheckbox.length === 0)
return [];
else
{
selectedCheckbox.each(function(){checkedValues.push($(this).val());});
return checkedValues;
}
},
/**
* 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
* checkboxset/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-checkboxset-label
* All input components must override
*
* @return {string}
* @memberof oj.ojCheckboxset
* @override
* @protected
*/
_GetDefaultStyleClass : function ()
{
return "oj-checkboxset";
},
/**
* Returns a jquery object of the elements representing the content nodes (checkboxes/labels).
* @protected
* @override
* @memberof oj.ojCheckboxset
*/
_GetContentElement : function ()
{
return this._findCheckboxesWithMatchingName();
},
/**
* 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.ojCheckboxset
* @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.ojCheckboxset
* @instance
* @protected
* @return {boolean}
*/
_AriaRequiredUnsupported : function()
{
return true;
},
_propagateDisabled: function( disabled ) {
disabled = !!disabled;
this.$checkboxes.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 the checkboxset. 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 checkboxset) is disabled or not. Then, when we render
// out the checkboxes '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 checkboxset) is disabled.
$( this ).data("oj-_ojRadioCheckbox").__setAncestorComponentDisabled(disabled);
});
this.$checkboxes._ojRadioCheckbox("refreshDisabled"); // re-render disabled
},
/**
* @override
* @private
*/
_setOption : function (key, value, flags)
{
this._super(key, value, flags);
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-checkboxset-inputs</b>: An Array of the checkboxset's input elements</li>
* </ul>
*
* @expose
* @override
* @memberof oj.ojCheckboxset
* @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-checkboxset-inputs") {
return this.$checkboxes.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.$checkboxes)
{
this.$checkboxes._ojRadioCheckbox( "destroy" );
}
return ret;
}
/**** end internal widget functions ****/
/**
* Removes the checkboxset functionality completely.
* This will return the element back to its pre-init state.
*
* <p>This method does not accept any arguments.
*
* @method
* @name oj.ojCheckboxset#destroy
* @memberof oj.ojCheckboxset
* @instance
*
* @example <caption>Invoke the <code class="prettyprint">destroy</code> method:</caption>
* $( ".selector" ).ojCheckboxset( "destroy" );
*/
// Fragments:
/**
* <table class="keyboard-table">
* <thead>
* <tr>
* <th>Target</th>
* <th>Gesture</th>
* <th>Action</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Checkbox</td>
* <td><kbd>Tap</kbd></td>
* <td> Select/unselect the input</td>
* </tr>
* <tr>
* <td>Input's Label</td>
* <td><kbd>Tap</kbd></td>
* <td> Select/unselect the corresponding input</td>
* </tr>
* <tr>
* <td>Label's help icon</td>
* <td><kbd>Tap</kbd></td>
* <td>Go the Help Source URL.</td>
* </tr>
* </tbody>
* </table>
*
*
* @ojfragment touchDoc - Used in touch gesture section of classdesc, and standalone gesture doc
* @memberof oj.ojCheckboxset
*/
/**
* <p>The checkboxset does not add any extra keyboard navigation to the input checkboxes.
* The keyboard interaction comes from the native browser.
* </p>
*
* @ojfragment keyboardDoc - Used in keyboard section of classdesc, and standalone gesture doc
* @memberof oj.ojCheckboxset
*/
});
////////////////// SUB-IDS //////////////////
/**
* <p>Sub-ID for the checkboxset's checkboxes.
*
* <p>See the <a href="#getNodeBySubId">getNodeBySubId</a> and
* <a href="#getSubIdByNode">getSubIdByNode</a> methods for details.
*
* @ojsubid
* @member
* @name oj-checkboxset-inputs
* @memberof oj.ojCheckboxset
* @instance
*
* @example <caption>Get the nodes for the checkboxes:</caption>
* var nodes = $( ".selector" ).ojRadioset( "getNodeBySubId", {'subId': 'oj-checkboxset-inputs'} );
*/
// -----------------------------------------------------------------------------
// "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/ojcheckboxset/ojcheckboxset.js
Oracle® JavaScript Extension Toolkit (JET)
1.1.2
E65298-01