/**
* Copyright (c) 2014, Oracle and/or its affiliates.
* All rights reserved.
*/
(function() { // Toolbar wrapper function, to keep "private static members" private
/**
* @ojcomponent oj.ojToolbar
* @augments oj.baseComponent
* @since 0.6
*
* @classdesc
* <h3 id="toolbarOverview-section">
* JET Toolbar Component
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#toolbarOverview-section"></a>
* </h3>
*
* <p>Description: Themeable, WAI-ARIA-compliant toolbar component.
*
* <p>The JET Toolbar component can contain [JET Buttons]{@link oj.ojButton}, [JET Buttonsets]{@link oj.ojButtonset}, and non-focusable content
* such as separator icons. Toolbar provides WAI-ARIA-compliant focus management.
*
* <p>A toolbar that contains radios should contain all radios in the radio group.
*
*
* <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="keyboard-appdev-section">
* Keyboard Application Developer Information
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#keyboard-appdev-section"></a>
* </h3>
*
* <p>Any buttonsets placed in the toolbar should have <code class="prettyprint">focusManagement</code> set to <code class="prettyprint">"none"</code>,
* so as not to compete with the toolbar's focus management.
*
* <p>The application should not do anything to interfere with the Toolbar's focus management, such as setting the <code class="prettyprint">tabindex</code>
* of the buttons. Also, enabled buttons should remain user-visible, without which arrow-key navigation to the button would cause the focus to
* seemingly disappear.
*
*
* <h3 id="a11y-section">
* Accessibility
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#a11y-section"></a>
* </h3>
*
* <p>JET Toolbar takes care of focus management, as noted above.
*
* <p>As shown in the online demos, the application is responsible for applying <code class="prettyprint">aria-label</code> and/or
* <code class="prettyprint">aria-controls</code> attributes to the toolbar element, if applicable per the instructions that follow:
*
* <p>If this toolbar is (or might be) placed in context with other toolbars, then the application should apply an
* <code class="prettyprint">aria-label</code> to the toolbar element to distinguish it, e.g. an "Edit" toolbar. The
* <code class="prettyprint">aria-label</code> is optional when there is only one toolbar.
*
* <p>If the toolbar is controlling something else on the page, e.g. bold / italic / underline buttons controlling a rich
* text editor, then the application should apply an <code class="prettyprint">aria-controls</code> attribute to the toolbar element,
* e.g. <code class="prettyprint">aria-controls="myTextEditor"</code>.
*
* <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="styling-section">
* Styling
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#styling-section"></a>
* </h3>
*
* <p>The following CSS classes can be applied by the page author as needed.
*
* <table class="generic-table styling-table">
* <thead>
* <tr>
* <th>Class</th>
* <th>Can be applied to</th>
* <th>Use</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>oj-toolbar-separator</td>
* <td>A span in the toolbar serving as an accessible toolbar separator</td>
* <td>Separators should be placed around any buttonsets in the toolbar, and anywhere else in the toolbar that
* a separator is desirable. Use the following markup for accessibility:
* <pre class="prettyprint">
* <code><span role="separator" aria-orientation="vertical" class="oj-toolbar-separator"></span>
* </code></pre>
* </td>
* </tr>
* <tr>
* <td>oj-toolbar-top-border</td>
* <td>Toolbar's root element, or the oj-toolbars element</td>
* <td>Applies a top border to the toolbar, or to the oj-toolbars element, in themes not having this border by default.</td>
* </tr>
* <tr>
* <td>oj-toolbar-bottom-border</td>
* <td>Toolbar's root element, or the oj-toolbars element</td>
* <td>Applies a bottom border to the toolbar, or to the oj-toolbars element, in themes not having this border by default.</td>
* </tr>
* <tr>
* <td>oj-toolbar-no-chrome</td>
* <td>Toolbar's root element, or the oj-toolbars element</td>
* <td>Removes "chrome" (background and border) from the toolbar(s), in themes having this chrome by default.</td>
* </tr>
* <tr>
* <td>oj-toolbars</td>
* <td>An outer div representing a multiple toolbar layout</td>
* <td>Contains one or more oj-toolbar-row elements.</td>
* </tr>
* <tr>
* <td>oj-toolbar-row</td>
* <td>Child divs of the oj-toolbar element</td>
* <td>Each oj-toolbar-row div is a row containing one or more toolbars.</td>
* </tr>
* </tbody>
* </table>
*
*
* <h3 id="rtl-section">
* Reading direction
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#rtl-section"></a>
* </h3>
*
* <p>The only supported way to set the reading direction (LTR or RTL) is to set the <code class="prettyprint">"dir"</code> attribute on the
* <code class="prettyprint"><html></code> element of the page. As with any JET component, in the unusual case that the reading direction
* is changed post-create, the toolbar must be <code class="prettyprint">refresh()</code>ed, or the page must be reloaded.
*
*
* <h3 id="pseudos-section">
* Pseudo-selectors
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#pseudos-section"></a>
* </h3>
*
* <p>The <code class="prettyprint">:oj-toolbar</code> pseudo-selector can be used in jQuery expressions to select JET Toolbars. For example:
*
* <pre class="prettyprint">
* <code>$( ":oj-toolbar" ) // selects all JET Toolbars on the page
* $myEventTarget.closest( ":oj-toolbar" ) // selects the closest ancestor that is a JET Toolbar
* </code></pre>
*
*
* <h3 id="binding-section">
* Declarative Binding
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#binding-section"></a>
* </h3>
*
* <p>For components like Toolbar and Menu that contain a number of like items, applications may wish to use a <code class="prettyprint">foreach</code> Knockout binding
* to stamp out the contents. This binding cannot live on the same node as the JET <code class="prettyprint">ojComponent</code> binding, and must instead live on a nested
* virtual element as follows:
*
* <pre class="prettyprint">
* <code><div id="myToolbar" class="oj-button-half-chrome"
* data-bind="ojComponent: {component: 'ojToolbar'}">
* <!-- ko foreach: myButtons -->
* <button data-bind="attr: {id: id},
* ojComponent: { component: 'ojButton', label: label }">
* </button>
* <!-- /ko -->
* </div>
* </code></pre>
*
*
* <h3 id="jqui2jet-section">
* JET for jQuery UI developers
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#jqui2jet-section"></a>
* </h3>
*
* <ol>
* <li>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.</li>
* </ol>
*
* <p>Also, event names for all JET components are prefixed with "oj", instead of component-specific prefixes like "toolbar" or "menu".
* E.g. if JQUI had a toolbar component, and if it followed the usual pattern, then it would have a <code class="prettyprint">toolbarcreate</code>
* event, while JET's is called <code class="prettyprint">ojcreate</code>, as shown in the doc for that event.
* Reason: This makes the API more powerful. It allows apps to listen to "foo" events from <em>all</em> JET components via:
*
* <pre class="prettyprint">
* <code>$( ".selector" ).on( "ojfoo", myFunc);
* </code></pre>
*
* or to "foo" events only from JET Toolbars (the JQUI functionality) via:
*
* <pre class="prettyprint">
* <code>$( ".selector" ).on( "ojfoo", ":oj-toolbar", myFunc);
* </code></pre>
*
*
* <!-- - - - - Above this point, the tags are for the class.
* Below this point, the tags are for the constructor (initializer). - - - - - - -->
*
*
* @desc Creates a JET Toolbar.
*
* @param {Object=} options a map of option-value pairs to set on the component
*
* @example <caption>Initialize the toolbar with no options specified:</caption>
* $( ".selector" ).ojToolbar();
*
* @example <caption>Initialize the toolbar with a callback specified:</caption>
* $( ".selector" ).ojToolbar( { "create": function( event, ui ) {} } );
*
* @example <caption>Initialize the toolbar via the JET <code class="prettyprint">ojComponent</code> binding:</caption>
* <div id="beverages" data-bind="ojComponent: { component: 'ojToolbar',
* create: setupToolbar }">
*/
oj.__registerWidget("oj.ojToolbar", $['oj']['baseComponent'], {
widgetEventPrefix : "oj",
options: { // options is in externs.js. TODO: same as other prototype fields.
/**
* <p>JET Toolbar does not support a
* <code class="prettyprint">disabled</code> option. The following
* one-liner can be used to disable or enable all buttons in a toolbar:
*
* <pre class="prettyprint">
* <code>$("#myToolbar").find(":oj-button").ojButton("option", "disabled", myBoolean);
* </code></pre>
*
*
* @member
* @name disabled
* @memberof oj.ojToolbar
* @instance
*/
// disabled option declared in superclass, but we still want the above API doc
// Events
/**
* Triggered when the toolbar is created.
*
* @event
* @name create
* @memberof oj.ojToolbar
* @instance
* @property {Event} event <code class="prettyprint">jQuery</code> event object
* @property {Object} ui Empty object included for consistency with other events
*
* @example <caption>Initialize the toolbar with the <code class="prettyprint">create</code> callback specified:</caption>
* $( ".selector" ).ojToolbar({
* "create": function( event, ui ) {}
* });
*
* @example <caption>Bind an event listener to the <code class="prettyprint">ojcreate</code> event:</caption>
* $( ".selector" ).on( "ojcreate", function( event, ui ) {} );
*/
// create event declared in superclass, but we still want the above API doc
},
_InitOptions: function(originalDefaults, constructorOptions) {
this._super(originalDefaults, constructorOptions);
if ('disabled' in constructorOptions)
oj.Logger.warn("Caller attempted to set the 'disabled' option on Toolbar, but Toolbar does not support the 'disabled' option. See API doc.");
},
_ComponentCreate: function() {
this._super();
this.element
.addClass( "oj-toolbar oj-component" )
.attr( "role", "toolbar" );
this._setup(true);
},
// Override to set custom launcher
_NotifyContextMenuGesture: function(menu, event, eventType) {
// Set launcher to the current tabbable button
// For toggle buttons, launcher must be the hidden focusable input, but for Shift-F10 we want the CM aligned to the Button's root element, not that
// hidden input. This is no change from the default for push buttons, since in that case the root element and launcher (input) are the same.
var currentButton = this.element.find(":oj-button[tabindex=0]");
this._OpenContextMenu(event, eventType, {
"launcher": currentButton,
"position": {"of": eventType==="keyboard" ? currentButton.ojButton("widget") : event}
});
},
_setOption: function( key, value ) { // Override of protected base class method. Method name needn't be quoted since is in externs.js.
if ( key === "disabled" ) {
oj.Logger.warn("Caller attempted to set the 'disabled' option on Toolbar, but Toolbar does not support the 'disabled' option. See API doc. Ignoring the call.");
return;
}
// best to call super at end, so that optionChange (fired at end of super) is fired at very end
this._superApply( arguments );
},
/**
* Refreshes the toolbar, including the following:
*
* <ul>
* <li>Re-applies focus management / keyboard navigation.
* <li>Rechecks the reading direction (LTR vs. RTL).
* </ul>
*
*
* <p>A <code class="prettyprint">refresh()</code> is required in the following circumstances:
* <ul>
* <li>After buttons are added to or removed from the toolbar.</li>
* <li>After a change to the <code class="prettyprint">disabled</code> status of any of the buttons in the toolbar.</li>
* <li>After a programmatic change to the <code class="prettyprint">checked</code> status of a radio button in the toolbar
* (which should be done via Buttonset's <code class="prettyprint">checked</code> option). This applies only to radios,
* not to checkboxes or push buttons.</li>
* <li>After the reading direction (LTR vs. RTL) changes.</li>
* </ul>
*
* <p>This method does not accept any arguments.
*
* @expose
* @memberof oj.ojToolbar
* @instance
*
* @example <caption>Invoke the <code class="prettyprint">refresh</code> method:</caption>
* $( ".selector" ).ojToolbar( "refresh" );
*/
refresh: function() {
this._super();
this._setup(false);
},
_setup: function(isCreate) { // Private, not an override (not in base class). Method name unquoted so will be safely optimized (renamed) by GCC as desired.
var self = this;
this.isRtl = this._GetReadingDirection() === "rtl";
// When toolbar is binding listeners to buttons, use the Toolbar's eventNamespace, not the Button's
// eventNamespace, to facilitate later unbinding only the Toolbar listeners.
// For checkbox/radio, we're binding to inputs, not labels.
// Put listeners on every button, b/c it's too unreliable to put them on the toolbar node and rely on event bubbling.
// - E.g. bubbling doesn't work for antonym buttons (is this still true after the refactoring?) -- see comment on Button._setLabelOption().
// - Likewise, focus mgmt can't just break if app listener stops propagation.
// - Both of these problems still happen when using the delegation / selector overload of .on(); there is no special JQ bubbling magic.
this.$buttons = this.element.find( ":oj-button" )
.unbind( "keydown" + this.eventNamespace )
.bind( "keydown" + this.eventNamespace, function(event) {
self._handleKeyDown(event, $(this));
})
.unbind( "click" + this.eventNamespace )
.bind( "click" + this.eventNamespace, function(event) {
if ( !$(this).ojButton("option", "disabled") ) {
// Normally the button will be tabbable after the click, since (a) if we reach here, the clicked button is enabled, and
// (b) an unchecked radio before the click will normally be checked after the click. But just in case it's unchecked
// (e.g. due to app listener), we let callee run it thru _mapToTabbable() before using, as usual.
self._setTabStop( $(this) );
}
})
.unbind( "focus" + this.eventNamespace )
.bind( "focus" + this.eventNamespace, function(event) {
self._setTabStop( $(this) );
});
// the subset of Toolbar buttons that are enabled. Disabled buttons are not tabbable.
this.$enabledButtons = this.$buttons.filter(function(index) {
return !$( this ).ojButton( "option", "disabled" );
});
this._initTabindexes(isCreate);
},
// For create, make only the first enabled button tabbable. (We decided to have Shift-Tab go to first, not last, button.)
// For refreshes, keep the existing tabstop if we can, otherwise proceed as with create.
// Either way, if that button is a radio and some radio in its group is checked, make that one tabbable instead.
// If there are no enabled buttons, makes them all untabbable.
// No return value.
_initTabindexes: function(isCreate) { // Private, not an override (not in base class). Method name unquoted so will be safely optimized (renamed) by GCC as desired.
// even for refreshes where we'll wind up keeping the same tabstop, must make everything untabbable first, to ensure any new buttons become untabbable.
var $last = $(this._lastTabStop);
this._lastTabStop = undefined;
this.$buttons.attr( "tabindex", "-1" );
var $newTabStop; // callee might map this to radio groupmate
// TBD: for refreshes when $last is a disabled radio with a checked enabled groupmate and they are in the toolbar, the groupmate would be
// a valid tabstop, but this defaults to the first.
if (isCreate || !$last.is(this.$enabledButtons)) {
// is create, or is refresh but must treat like create b/c $last is empty, or not enabled, or no longer in the toolbar
$newTabStop = this.$enabledButtons.first(); // if empty (none enabled), no tabstop will be set
} else {
// is a refresh, and $last is non-empty and is an enabled button still in the toolbar. May be a radio whose groupmate
// has become checked, in which case callee will map it to that groupmate.
$newTabStop = $last;
}
this._setTabStop( $newTabStop );
},
// For each button in $button (in our usage always 0-1 button hence $button singular), if that button is an unchecked radio
// with a checked groupmate (which means it's not tabbable), then map it to the checked one (checked enabled radios are
// tabbable, and we know it's enabled per the argument below).
//
// $button contains 0 or more buttons to map. Must be enabled since disabled buttons aren't tabbable.
// Returns the mapped JQ object (which the caller will make the tabstop).
//
// We know that this.$enabledButtons contains all buttons in $button, and all of their potentially checked radio-groupmates, since:
// - The above "enabled" requirement guarantees that $button's contents are all in $enabledButtons.
// - The prohibition against radio groupmates that are not in the toolbar, and the prohibition against checked disabled groupmates
// of enabled radios, guarantee that if $button is a radio, then all of its potentially checked groupmates are enabled and thus in
// $enabledButtons.
//
// Firefox browser issue: (TODO: should we doc this? File FF and/or JET bug?)
//
// When this method maps an unchecked radio to its checked groupmate, the caller ensures that the former still has focus, but the latter
// is the tabstop for when the user tabs out and back in. When tabbing / Shift-Tabbing from the unchecked radio in the direction of the
// checked one, Chrome and IE9 are smart enough to tab out of the radio group to the adjacent tabstop as desired. But in FF, focus goes to
// the checked one, which is not what we want.
//
// Reason: Chrome and IE9 are smart enough never to tab within a radio group. If focus is in the radio group, Tab and Shift-Tab exit the radio group.
// But in FF, [a radio is reachable via Tab/Shift-Tab from within the group] if [it's enabled, tabindex != -1, and either checked or has no
// checked groupmates], i.e. [it would be reachable via Tab/Shift-Tab from outside the group if all its groupmates happened to be untabbable
// (e.g. disabled)]. In other words, FF is the only one that fails to distinguish between radios that could be valid tabstops from outside, and
// those that should be valid tabstops from inside.
//
// This impl improves on the native behavior. In FF, in an unchecked radio group, every single radio is a tabstop. Our use of tabindex=-1
// guarantees that we never tab within the group in that case. It's only a checked groupmate that can be tabbed to from within. (So at
// most one unwanted tabstop.)
//
// After much time and effort, the latter issue seems to be infeasible to fix in any robust, non-brittle way. E.g.:
// - Clearing all tabstops and restoring on tab-out of toolbar: FF provides no reliable way to find out whether a blur is exiting the entire
// toolbar. The obvious setTimeout workaround needed to be unacceptably long (e.g. 250ms) and even then was unreliable. If we ever fail
// to restore the tabstop, the toolbar becomes untabbable and inaccessible.
// - Every other approach had similar robustness issues.
_mapToTabbable: function( $button ) { // Private, not an override (not in base class). Method name unquoted so will be safely optimized (renamed) by GCC as desired.
var $enabledButtons = this.$enabledButtons;
return $button.map(function(index, elem) {
// Buttons other than radios, and checked radios, are always tabbable if they're enabled, which this method requires.
// Radios w/ name="" (incl name omitted) are not in a radio group, not even with other radios with w/ name="". Radios
// with no groupmates are always tabbable, since either they're checked, or they're unchecked with no checked groupmate.
if (elem.type != "radio" || elem.checked || elem.name == "") {
return elem;
} else {
// elem is unchecked radio in real (not "") group, which is tabbable iff no groupmate is checked. Per above doc, we know that
// all of its potentially checked groupmates are in $enabledButtons.
var $checkedRadio = _radioGroup(elem, $enabledButtons).filter(":checked");
return ($checkedRadio.length ? $checkedRadio[0] : elem);
}
});
},
// Set which button is in the tab sequence.
// $button should contain 0 or 1 button to be made tabbable (since at most one should be tabbable at a time).
// If 0 (i.e. no enabled buttons), all will become untabbable. If 1, it must be tabbable in every way (e.g. enabled) except possibly
// being an unchecked radio with a checked groupmate, which this method will map to its checked groupmate, which
// we know is enabled thus tabbable since we require that checked radios with enabled groupmates not be disabled.
// No return value.
_setTabStop: function( $button ) { // Private, not an override (not in base class). Method name unquoted so will be safely optimized (renamed) by GCC as desired.
// if (!window.setTabStopCounter) window.setTabStopCounter=1; // REMOVE, is only for console.log's
// console.log("in _setTabStop: " + window.setTabStopCounter++ + ". Orig (premap) button checked: " + $button[0].checked); // + " and is:");
// console.log($button[0]);
$button = this._mapToTabbable( $button );
var button = $button[0]; // button is undefined iff $button is empty iff we need to clear all tabstops b/c there are no enabled buttons to make tabbable
var last = this._lastTabStop; // last is undefined iff $(last) is empty iff there are no existing tabstops to clear (b/c _initTabindexes just ran
// or previously there were no enabled buttons to make tabbable)
// console.log("mapped button and last button are:"); console.log(button); console.log(last); console.log(".");
// Cases: both are undefined: have no tabstops; want to keep it that way (b/c none enabled), so do nothing
// both are node X: X is the tabstop; want to keep it that way, so do nothing
// last is node X; button is undefined: X is the tabstop; want to clear it w/o replacing it (b/c none enabled). This logic does that.
// last is undefined; button is node X: no existing tabstop; want to make X the tabstop. This logic does that.
// last is node X; button is node Y: X is the tabstop; want to clear it and make Y the tabstop. This logic does that.
if ( button !== last) {
//console.log("setting tab stop to " + $button.attr("id")); console.log("$(last).length:"); console.log($(last).length);
$(last).attr( "tabindex", "-1" ); // no-op iff $(last) is empty iff (see comment above)
$button.attr( "tabindex", "0" ); // no-op iff $button is empty iff (see comment above)
this._lastTabStop = button;
}
},
// No return value.
_handleKeyDown: function(event, $button) { // Private, not an override (not in base class). Method name unquoted so will be safely optimized (renamed) by GCC as desired.
switch (event.which) {
case $.ui.keyCode.LEFT: // left arrow
case $.ui.keyCode.RIGHT: // right arrow
event.preventDefault();
var $enabledButtons = this.$enabledButtons;
var length = $enabledButtons.length;
if (length<2) // nowhere to navigate to; currently focused button is the only enabled one in toolbar
break;
var oldIndex = $enabledButtons.index($button);
var increment = ((event.which == $.ui.keyCode.RIGHT) ^ this.isRtl) ? 1 : -1;
var newIndex = (oldIndex+increment+length)%length; // wrap around if at start/end of toolbar
// A11y office recommended treating radios like other buttons: Arrow moves focus without selecting, Spacebar selects,
// which we prefer too. Since we're using role='button', not 'radio', we don't need to follow the WAI-ARIA radio behavior
// where Arrow moves focus and selects, Ctrl-Arrow moves focus without selecting.
$enabledButtons.eq(newIndex).focus();
break;
case $.ui.keyCode.UP: // up arrow
case $.ui.keyCode.DOWN: // down arrow
// Per above comment, treating radios like buttons, which have no native or WAI-ARIA-mandated up/down arrow behavior,
// so disable native focus-and-select behavior.
if ( $button.attr("type")=="radio" )
event.preventDefault();
break;
// Don't need Space/Enter handlers. For all buttons except already-checked radios in some browsers, Space/Enter fire a click event
// (natively or manually), which already calls _setTabStop. For checked radios (which are focused if they're getting
// this key event), _setTabStop has already been called for whichever happened 2nd: focus (an already checked radio) or
// check (an already focused radio) via click/Space/Enter. If checking was done programmatically (via Bset.checked option), we require a refresh().
}
},
_destroy: function() { // Override of protected base class method. Method name needn't be quoted since is in externs.js.
// this entire stmt can be removed once restoreAttrs code is running.
this.element
.removeClass( "oj-toolbar oj-component" )
.removeAttr( "role" );
// Since we're not destroying the buttons themselves, the restoreAttrs code will NOT take care of this.
this.$buttons.attr( "tabindex", "0" ); // bsets in a toolbar should not have focusMgmt turned on, so this is OK, but should revert to orig value, not assume 0.
}
// API doc for inherited methods with no JS in this file:
/**
* Returns a <code class="prettyprint">jQuery</code> object containing the root element of the Toolbar component.
*
* @method
* @name oj.ojToolbar#widget
* @memberof oj.ojToolbar
* @instance
* @return {jQuery} the root element of the component
*
* @example <caption>Invoke the <code class="prettyprint">widget</code> method:</caption>
* var widget = $( ".selector" ).ojToolbar( "widget" );
*/
/**
* Removes the toolbar functionality completely. This will return the element back to its pre-init state,
* and remove the toolbar's focus management from the contained buttons.
*
* <p>This method does not accept any arguments.
*
* @method
* @name oj.ojToolbar#destroy
* @memberof oj.ojToolbar
* @instance
*
* @example <caption>Invoke the <code class="prettyprint">destroy</code> method:</caption>
* $( ".selector" ).ojToolbar( "destroy" );
*/
// Fragments:
/**
* <p>All Toolbar touch interaction is with the individual buttons. See the [JET Button]{@link oj.ojButton} touch gesture doc.
*
* @ojfragment touchDoc - Used in touch gesture section of classdesc, and standalone gesture doc
* @memberof oj.ojToolbar
*/
/**
* <p>JET Toolbar is a single tabstop, with arrow-key navigation within the toolbar, as follows:
*
* <table class="keyboard-table">
* <thead>
* <tr>
* <th>Key</th>
* <th>Action</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td><kbd>LeftArrow</kbd></td>
* <td>Navigates to the previous enabled button on the left, wrapping around at the end.</td>
* </tr>
* <tr>
* <td><kbd>RightArrow</kbd></td>
* <td>Navigates to the next enabled button on the right, wrapping around at the end.</td>
* </tr>
* </tbody>
* </table>
*
* <p>See also the [JET Button]{@link oj.ojButton} keyboard doc, for details on interacting with
* the individual buttons.
*
* @ojfragment keyboardDoc - Used in keyboard section of classdesc, and standalone gesture doc
* @memberof oj.ojToolbar
*/
});
// -----------------------------------------------------------------------------
// "private static members" shared by all toolbars:
// -----------------------------------------------------------------------------
/**
* This is the same as _radioGroup() in Buttonset's code, minus the code branches that toolbar's caller never reaches.
* In the cases that toolbar's caller reaches, the behavior should be the same. So if Buttonset and Toolbar ever share code,
* keep Buttonset's copy of this function for use by both components.
*
* In all cases, the return value includes only radios that are an :oj-button, i.e. radios that have been buttonized.
*
* Where this method looks for radio groupmates: This method will only look in $elems, and will not attempt to weed out any
* false positives as defined below. (So return value includes the specified radio iff it's an :oj-button in $elems.)
*
* Radios w/ name="" (incl name omitted) are not in a radio group (i.e. no SelectOne semantics), not even with other radios with
* w/ name="". So if radio is nameless, the return value will include only radio (or nothing at all if it isn't an :oj-button, or
* if $elems doesn't include radio).
*
* False positives: radios with nonempty names that match radio's name, but are actually not groupmates (i.e. no SelectOne
* relationship), e.g. because they're from a different form.
*
*
* @param {!Element} radio a radio button. Not a JQ object, other button or element type, or null.
* @param {jQuery=} $elems optional JQ object, containing 0 or more elems that aren't necessarily radios or buttons, in which to look for groupmates.
* E.g. the elements in a buttonset or toolbar. Must not contain any false positives as defined above.
*/
var _radioGroup = function( radio, $elems ) {
var name = radio.name,
$radios;
if ( name ) {
name = name.replace( /'/g, "\\'" ); // escape single quotes
var selector = ":radio[name='" + name + "']:oj-button";
$radios = $elems.filter( selector );
} else {
$radios = $elems.filter(radio).filter(":oj-button");
}
return $radios;
};
}() ); // end of Toolbar wrapper function
Source: src/main/javascript/oracle/oj/ojtoolbar/ojtoolbar.js
Oracle® JavaScript Extension Toolkit (JET)
1.1.2
E65298-01