/**
* Copyright (c) 2014, Oracle and/or its affiliates.
* All rights reserved.
*/
/*!
* jQuery UI Popup @VERSION
* http://jqueryui.com
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/menu/
*
* Depends:
* jquery.ui.core.js
* jquery.ui.widget.js
* jquery.ui.position.js
*/(function ()
{
/**
* List of all pseudo marker selectors that defines rules for where a tail is aligned.
* @private
* @const
*/
var _TAIL_STYLES = ["oj-left", "oj-center", "oj-right", "oj-top", "oj-middle", "oj-bottom"];
/**
* Mapping of horizontal-vertical (x,y) positon using alignment to jet tail pseudo marker selectors.
*
* horizontal: right, left, center
* vertical: top, bottom, middle
*
* @private
* @const
*/
var _TAIL_ALIGN_RULES =
{
'right-top' : 'oj-right oj-top',
'right-middle' : 'oj-right oj-middle',
'right-bottom' : 'oj-right oj-bottom',
'left-top' : 'oj-left oj-top',
'left-middle' : 'oj-left oj-middle',
'left-bottom' : 'oj-left oj-bottom',
'center-top' : 'oj-center oj-top',
'center-middle' : 'oj-center oj-bottom',
'center-bottom' : 'oj-center oj-bottom'
};
/**
* @ojcomponent oj.ojPopup
* @augments oj.baseComponent
*
* @classdesc
* <h3 id="popupOverview-section">
* JET Popup Component
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#popupOverview-section"></a></h3>
*
* <p>Description: Themeable, WAI-ARIA-compliant popup that can display arbitrary content.</p>
*
* <p>A JET popup can be created from a block ( <code class="prettyprint"><div></code> ) or inline element
* ( <code class="prettyprint"><span></code> ). This element will become the immediate child of the content element.
* Dynamic content can be inserted under this element.</p>
*
* <pre class="prettyprint">
* <code><span id="popup">
* Hello World!
* </span>
* </code></pre>
*
* <p>For WAI-ARIA compliance, JET automatically adds <code class="prettyprint">role="tooltip"</code> to
* the root popup dom element. It also adds the <code class="prettyprint">aria-describedby="popup-id"</code>
* to the launcher while the popup is open.
* </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"}
*
* <br/><br/>
*
* <h3 id="accessibility-section">
* Accessibility
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#accessibility-section"></a>
* </h3>
*
* <p>One point often overlooked is making the gestures that launch a popup accessible.
* There are no constraints to what events a page developer might choose to trigger opening a
* popup. The choice should be accessible for screen reader users. Page
* developers should take care when using mouse events to trigger opening of a popup.
* This is especially important if the content of the popup can't be derived from other
* visible areas on the page. In cases that mouseover, mouseout, mouseenter, mouseleave and hover
* events are used to launch popups, there needs to be a keyboard functional equivalency.
* </p>
*
* <h3 id="reparenting-section">
* Reparenting
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#reparenting-section"></a>
* </h3>
*
* <p id="reparenting-strategy">
* When popups are open, they will be reparented in the document and reparented back when closed.
* When open, the location of the popup within the document will be in context of how it's used.
* Popups open from other popups will be relocated in the document into the nearest parent popup's layer.
* The popups layer defines its z-index weight "stacking context". The ojPopup's layer is marked with
* the "oj-popup-layer" style.
* The context of opening is defined by the launcher argument passed to the open method. If not open from
* another popup, the popup will be reparented to a container in the document body. Popups of the same type
* are assigned the same z-index values. The layering between peer popups reflect the opening order.
* The popup that has active focus will be assigned a greater z-index value. This is applied to the popup's
* layer by way of the "oj-focus-within" pseudo selector applied with "oj-popup-layer" selector. The page
* author has control over z-index weights by way of the "oj-popup-layer" selector.
* </p>
* <p>
* There are known caveats with this design. However, these scenarios are considered "bad use"
* based on our JET popup strategy.
* </p>
* <ol>
* <li>Events raised within the popup will not bubble up to the popup's original ancestors. Instead, listeners for popup events should
* be applied to either the popup's root element, or the document.</li>
* <li>Likewise, developers should not use CSS descendant selectors, or similar logic, that assumes that the popup will remain a child
* of its original parent.</li>
* <li>Popups containing iframes are problematic. The iframe elements "may" fire a HTTP GET request for its src attribute
* each time the iframe is reparented in the document.</li>
* <li>In some browsers, reparenting a popup that contains elements having overflow, will cause these overflow elements to
* reset their scrollTop.</li>
* </ol>
*
* <h3 id="eventHandling-section">
* Event Handling
* <a class="bookmarkable-link" title="Bookmarkable Link" href="#eventHandling-section"></a>
* </h3>
* <ul>
* <li>beforeClose(event, ui) - Triggered before a popup closes. Event can prevent closing the popup; However, there are cases
* the framework must veto, such as when the popup is destroyed.</li>
* <li>beforeOpen(event, ui) - Triggered before a popup closes. Event can prevent opening the popup.</li>
* <li>close(event, ui) - Triggered after the popup has closed.</li>
* <li>create(event, ui) - Triggered after the component has been bound to an associated dom element.</li>
* <li>focus(event, ui) - Triggered when initial focus is established on opening, depending on the value of the initalFocus
* option, or <kbd>F6</kbd> focus toggle from the associated launcher.</li>
* <li>open(event, ui) - Triggered after the popup has been made visible.</li>
* </ul>
*
* @desc Creates a JET Popup.
*
* @param {Object=} options a map of option-value pairs to set on the component
*
* @example <caption>Initialize the popup with no options specified:</caption>
* $( ".selector" ).ojPopup();
*
* @example <caption>Initialize the popup with behaviors of a notewindow:</caption>
* $( ".selector" ).ojPopup({initialFocus: 'none', autoDismiss: 'focusLoss', tail: 'simple'});
*
* @example <caption>Initialize a popup via the JET <code class="prettyprint">ojComponent</code> binding:</caption>
* <div id="popup1" data-bind="ojComponent: {component: 'ojPopup'}">This is a popup!</div>
*
*/
oj.__registerWidget("oj.ojPopup", $['oj']['baseComponent'],
{
widgetEventPrefix : "oj",
options :
{
/**
* Defines conditions that will cause an open popup to auto close dismiss. A value of <code class="prettyprint">focusLoss</code>
* defines the dismissal condition where focus has left the content of the popup or from the associated
* launcher or if what the popup is aligned to is not fully visible within an overflow area.
*
* @expose
* @memberof! oj.ojPopup
* @instance
* @type {string}
* @default <code class="prettyprint">"focusLoss"</code>
* @ojvalue {string} "none" disables auto dismissal behaviors.
* @ojvalue {string} "focusLoss" defines auto dismissal behavior when focus leaves the content of the popup or
* associated launcher. In addition, if what the popup is positioned to is clipped within an
* overflow area, the popup will auto close dismiss.
*
* @example <caption>Initialize the popup with <code class="prettyprint">autoDismiss</code> option specified:</caption>
* $( ".selector" ).ojPopup( { "autoDismiss": "focusLoss" } );
*
* @example <caption>Get or set the <code class="prettyprint">autoDismiss</code> option, after initialization:</caption>
* // getter
* var autoDismiss = $( ".selector" ).ojPopup( "option", "autoDismiss" );
* // setter
* $( ".selector" ).ojPopup( "option", "autoDismiss", "none" );
*/
autoDismiss: 'focusLoss',
/**
* Defines the presents of border, shadow and background color of the root popup dom. Value of
* <code class="prettyprint">none</code> applies the <code class="prettyprint">oj-popup-no-chrome</code>
* selector defined by the active theme to the root dom of the popup to remove the default chrome.
*
* @expose
* @memberof! oj.ojPopup
* @instance
* @type {string}
* @default <code class="prettyprint">"default"</code>
* @ojvalue {string} "default" describes the popups border, shadow, and background color defined by the active theme.
* @ojvalue {string} "none" turns off the outer chrome defined by the active theme.
*
* @example <caption>Initialize the popup with <code class="prettyprint">chrome</code> option specified:</caption>
* $( ".selector" ).ojPopup( { "chrome": "none" } );
*
* @example <caption>Get or set the <code class="prettyprint">chrome</code> option, after initialization:</caption>
* // getter
* var chrome = $( ".selector" ).ojPopup( "option", "chrome" );
*
* // setter
* $( ".selector" ).ojPopup( "option", "chrome", "none" );
*/
chrome : 'default',
/**
* Determines if the popup should steal focus to its content when initially open. A value of <code class="prettyprint">none</code>
* prevents the popup from grabbing focus when open.
*
* @expose
* @memberof! oj.ojPopup
* @instance
* @type {string}
* @default <code class="prettyprint">"none"</code>
* @ojvalue {string} "none" prevents the popup from stealing focus when open.
* @ojvalue {string} "firstFocusable" defines that a popup should grab focus to its content when open.
*
* @example <caption>Initialize the popup with <code class="prettyprint">initialFocus</code> option specified:</caption>
* $( ".selector" ).ojPopup( { "initialFocus": "none" } );
*
* @example <caption>Get or set the <code class="prettyprint">initialFocus</code> option, after initialization:</caption>
* // getter
* var initialFocus = $( ".selector" ).ojPopup( "option", "initialFocus" );
*
* // setter
* $( ".selector" ).ojPopup( "option", "initialFocus", "none" );
*/
initialFocus : 'none',
/**
* <p>Position object is defined by the jquery position API and is used to establish the location the
* popup will appear relative to another element. The position object contains the following properties:
* "my", "at", "of", "collision", "using" and "within".</p>
*
* <p>The "my" and "at" properties defines aligment points relative to the popup and other element. The
* "my" property represents the popups alignment where the "at" property represents the other element
* that can be identified by "of" or defauts to the launcher when the popup opens. The values of these
* properties describe a "horizontal vertical" location.</p>
*
* <p>Acceptable "horizontal" alignments values are: "right", "center", "left", "start", "end". Note: Jet has
* added "start" and "end" options to be more RTL friendly. The Jet values of "start" and "end" normalize
* to "right" or "left" depending on the direction of the document.</p>
*
* <p>Acceptable "vertical" alignment values are: "top", "center" and "bottom".</p>
*
* The following is a short summary of the most interesting positon properties:
* <ul>
* <li><code class="prettyprint">my</code> - A "vertical horizontal" rule that defines the location of the popup
* used for alignment.</li>
* <li><code class="prettyprint">at</code> - A "vertical horizontal" rule that defines the location of the
* other element for used alignment. The other element is defined by "of" or defaults to the open launcher
* argument if not specified.</li>
* </ul>
*
* @expose
* @memberof! oj.ojPopup
* @instance
* @type {Object}
* @default <code class="prettyprint">{my: "start top", at: "start bottom", collision: "flip"}</code>
*
* @example <caption>Initialize the popup with <code class="prettyprint">position</code> option specified:</caption>
* $( ".selector" ).ojPopup( { "position": {"my": "left top", "at": "right top"} } );
*
* @example <caption>Get or set the <code class="prettyprint">position</code> option, after initialization:</caption>
* // getter
* var position = $( ".selector" ).ojPopup( "option", "position" );
*
* // setter
* $( ".selector" ).ojPopup( "option", "position", {"my": "start bottom", "at": "end+14 top" } );
*/
position :
{
/**
* Defines which position on the popup to align with the target ("of") element: "horizontal vertical" alignment.
* A single value such as "right" will be normalized to "right center", "top" will be normalized to "center top"
* (following CSS convention). Acceptable horizontal values: "left", "center", "right".
* Acceptable vertical values: "top", "center", "bottom". Example: "left top" or "center center".
* Each dimension can also contain offsets, in pixels or percent, e.g., "right+10 top-25%". Percentage offsets are relative
* to the popup being positioned.
*
*
* @expose
* @memberof! oj.ojPopup
* @instance
* @alias position.my
* @type {string}
* @default <code class="prettyprint">start top</code>
*/
'my' : 'start top',
/**
* Defines which position on the target element ("of") to align the positioned element against: "horizontal vertical"
* alignment. See the my option for full details on possible values. Percentage offsets are relative to the target element.
*
* @expose
* @type {string}
* @memberof! oj.ojPopup
* @instance
* @alias position.at
* @default <code class="prettyprint">start bottom</code>
*/
'at' : 'start bottom',
/**
* Which element to position the popup against. The default is the <code class="prettyprint">launcher</code> argument
* passed to the <code class="prettyprint">open</code> method. If you provide a selector or jQuery object,
* the first matching element will be used. If you provide an event object, the pageX and pageY properties
* will be used.
*
* @expose
* @memberof! oj.ojPopup
* @instance
* @alias position.of
* @type {string}
* @default <code class="prettyprint">''</code>
*/
'of' : '',
/**
* When the positioned element overflows the window in some direction, move it to an alternative position. Similar to my and
* at, this accepts a single value or a pair for horizontal/vertical, e.g., "flip", "fit", "fit flip", "fit none".
*
* <ul>
* <li>"flip": Flips the element to the opposite side of the target and the collision detection is run again to see if it
* will fit. Whichever side allows more of the element to be visible will be used.</li>
* <li>"fit": Shift the element away from the edge of the window.</li>
* <li>"flipfit": First applies the flip logic, placing the element on whichever side allows more of the element to be
* visible. Then the fit logic is applied to ensure as much of the element is visible as possible.</li>
* <li>"none": Does not apply any collision detection.</li>
* </ul>
* @expose
* @memberof! oj.ojPopup
* @instance
* @alias position.collision
* @type {string}
* @default <code class="prettyprint">flip</code>
*/
'collision' : 'flip'
},
/**
* Determines if a decoration will be displayed from the popup that points to the element the popup is aligned to.
* The <code class="prettyprint">simple</code> value enables the tail defined by the current theme. In addtion,
* the <code class="prettyprint">oj-popup-tail-simple</code> selector will be applied to the root dom element. This
* is to allow the box-shadow, z-index and other chrome styling to vary per tail decoration.
*
* @expose
* @memberof! oj.ojPopup
* @instance
* @type {string}
* @default <code class="prettyprint">"none"</code>
* @ojvalue {string} "none" no decoration will be displayed from the popup pointing to the launcher.
* @ojvalue {string} "simple" enables showing the tail defined by the current theme.
*
* @example <caption>Initialize the popup with <code class="prettyprint">tail</code> option specified:</caption>
* $( ".selector" ).ojPopup( { "tail": "simple" } );
*
* @example <caption>Get or set the <code class="prettyprint">tail</code> option, after initialization:</caption>
* // getter
* var tail = $( ".selector" ).ojPopup( "option", "tail" );
*
* // setter
* $( ".selector" ).ojPopup( "option", "tail", "simple" );
*/
tail : 'none',
// Events
/**
* Triggered before the popup is launched via the <code class="prettyprint">open()</code> method.
* The launch can be cancelled by calling <code class="prettyprint">event.preventDefault()</code>.
*
* @expose
* @event
* @memberof! oj.ojPopup
* @instance
* @property {Event} event <code class="prettyprint">jQuery</code> event object
* @property {Object} ui currently empty
*
* @example <caption>Initialize the popup with the <code class="prettyprint">beforeOpen</code> callback specified:</caption>
* $( ".selector" ).ojPopup({
* "beforeOpen": function( event, ui ) {}
* });
*
* @example <caption>Bind an event listener to the <code class="prettyprint">ojbeforeopen</code> event:</caption>
* $( ".selector" ).on( "ojbeforeopen", function( event, ui ) {} );
*/
beforeOpen : null,
/**
* Triggered after the popup is launched via the <code class="prettyprint">open()</code> method.
*
* @expose
* @event
* @memberof! oj.ojPopup
* @instance
* @property {Event} event <code class="prettyprint">jQuery</code> event object
* @property {Object} ui currently empty
*
* @example <caption>Initialize the popup with the <code class="prettyprint">open</code> callback specified:</caption>
* $( ".selector" ).ojPopup({
* "open": function( event, ui ) {}
* });
*
* @example <caption>Bind an event listener to the <code class="prettyprint">ojopen</code> event:</caption>
* $( ".selector" ).on( "ojopen", function( event, ui ) {} );
*/
open : null,
/**
* Triggered before the popup is dismissed via the <code class="prettyprint">close()</code> method.
* The close can be cancelled by calling <code class="prettyprint">event.preventDefault()</code>.
*
* @expose
* @event
* @memberof! oj.ojPopup
* @instance
* @property {Event} event <code class="prettyprint">jQuery</code> event object
* @property {Object} ui currently empty
*
* @example <caption>Initialize the popup with the <code class="prettyprint">beforeClose</code> callback specified:</caption>
* $( ".selector" ).ojPopup({
* "beforeClose": function( event, ui ) {}
* });
*
* @example <caption>Bind an event listener to the <code class="prettyprint">ojbeforeclose</code> event:</caption>
* $( ".selector" ).on( "ojbeforeclose", function( event, ui ) {} );
*/
beforeClose : null,
/**
* Triggered after the popup is dismissed via the <code class="prettyprint">close()</code> method.
*
* @expose
* @event
* @memberof! oj.ojPopup
* @instance
* @property {Event} event <code class="prettyprint">jQuery</code> event object
* @property {Object} ui currently empty
*
* @example <caption>Initialize the popup with the <code class="prettyprint">close</code> callback specified:</caption>
* $( ".selector" ).ojPopup({
* "close": function( event, ui ) {}
* });
*
* @example <caption>Bind an event listener to the <code class="prettyprint">ojclose</code> event:</caption>
* $( ".selector" ).on( "ojclose", function( event, ui ) {} );
*/
close : null,
/**
* Triggered after focus has been transfered to the popup. This will occur after the
* <code class="prettyprint">open()</code> method is called, depending on the value
* of the <code class="prettyprint">initialFocus</code> option. It's also triggered when using the
* <kbd>F6</kbd> key to toggle focus from the associated launcher element to the content of the popup.
*
* @expose
* @event
* @memberof! oj.ojPopup
* @instance
* @property {Event} event <code class="prettyprint">jQuery</code> event object
* @property {Object} ui currently empty
*
* @example <caption>Initialize the popup with the <code class="prettyprint">focus</code> callback specified:</caption>
* $( ".selector" ).ojPopup({
* "focus": function( event, ui ) {}
* });
*
* @example <caption>Bind an event listener to the <code class="prettyprint">ojfocus</code> event:</caption>
* $( ".selector" ).on( "ojfocus", function( event, ui ) {} );
*/
focus : null
},
/**
* @memberof! oj.ojPopup
* @instance
* @protected
* @override
* @return {void}
*/
_ComponentCreate: function ()
{
this._super();
var rootStyle = this._getRootStyle();
var rootElement = $("<div>");
this._rootElement = rootElement.hide().addClass(rootStyle).attr("aria-hidden", "true");
rootElement.addClass("oj-component");
var content = $("<div>").addClass([rootStyle, "content"].join("-"));
content.attr("role", "presentation");
content.appendTo(rootElement); // @HTMLUpdateOK
this.element.after(rootElement); // @HTMLUpdateOK
this.element.appendTo(content); // @HTMLUpdateOK
this.element.show();
this._createTail();
this._setChrome();
// callback that overrides the positon['using'] for setting the tail.
this._usingCallback = $.proxy(this._usingHandler, this);
},
/**
* @memberof! oj.ojPopup
* @instance
* @protected
* @override
* @return {void}
*/
_destroy : function ()
{
if (this.isOpen())
this._closeImplicitly();
this._destroyTail();
delete this._usingCallback;
delete this._popupServiceEvents;
oj.DomUtils.unwrap(this.element, this._rootElement); // @HTMLUpdateOK
this.element.hide();
var closeDelayTimer = this._closeDelayTimer;
if (!isNaN(closeDelayTimer))
{
delete this._closeDelayTimer;
window.clearTimeout(closeDelayTimer);
}
this._destroyVoiceOverAssist();
this._super();
},
/**
* Returns a <code class="prettyprint">jQuery</code> object containing the generated wrapper.
* This method does not accept any arguments.
*
* @expose
* @name oj.ojpopup#widget
* @memberof! oj.ojPopup
* @instance
* @return {jQuery} the popup
*
* @example <caption>Invoke the <code class="prettyprint">widget</code> method:</caption>
* var widget = $( ".selector" ).ojPopup( "widget" );
*/
widget : function ()
{
return this._rootElement;
},
/**
* Opens the popup. This method accepts two arguments but both are optional.
*
*
* @expose
* @method
* @name oj.ojPopup#open
* @memberof! oj.ojPopup
* @instance
* @param {?(string|jQuery|Element)} launcher jquery object, jquery selector or dom element that
* is associated with the popup.
* @param {?Object} position an element relative to another
* @return {void}
* @fires oj.ojPopup#beforeOpen
* @fires oj.ojPopup#open
*
* @example <caption>Invoke the <code class="prettyprint">open</code> method:</caption>
* var open = $( ".selector" ).ojPopup( "open" );
*/
open : function (launcher, position)
{
if (this.isOpen())
{
this.close();
//if beforeClose handler prevents that action, just bail out.
if (this.isOpen())
return;
}
this._setLauncher(launcher);
var rootElement = this._rootElement;
launcher = this._launcher;
oj.Assert.assertPrototype(rootElement, jQuery);
oj.Assert.assertPrototype(launcher, jQuery);
if (oj.StringUtils.isEmptyOrUndefined(rootElement.attr("id")))
rootElement.attr("id", this._getSubId("wrapper"));
if (this._trigger("beforeOpen") === false)
return;
this._setPosition(position);
var options = this.options;
this._setAutoDismiss(options["autoDismiss"]);
this._addDescribedBy();
rootElement.attr("role", "tooltip");
position = options["position"];
var isRtl = this._GetReadingDirection() === "rtl";
position = oj.PositionUtils.normalizeHorizontalAlignment(position, isRtl);
// build layer class selectors applied to the popup layer
var rootStyle = this._getRootStyle();
var layerClass = [rootStyle, "layer"].join("-");
var tailDecoration = options['tail'];
if ("none" !== tailDecoration)
layerClass += " " + [rootStyle, "tail", tailDecoration].join("-");
/** @type {!Object.<oj.PopupService.OPTION, ?>} */
var psOptions = {};
psOptions[oj.PopupService.OPTION.POPUP] = rootElement;
psOptions[oj.PopupService.OPTION.LAUNCHER] = launcher;
psOptions[oj.PopupService.OPTION.POSITION] = position;
psOptions[oj.PopupService.OPTION.EVENTS] = this._getPopupServiceEvents();
psOptions[oj.PopupService.OPTION.LAYER_SELECTORS] = layerClass;
oj.PopupService.getInstance().open(psOptions);
this._trigger("open");
this._intialFocus();
this._initVoiceOverAssist();
this._on(rootElement, {'keydown': this._keyHandler, 'keyup': this._keyHandler});
if (launcher && launcher.length > 0)
this._on(launcher, {'keydown': this._keyHandler, 'keyup': this._keyHandler});
},
/**
* Closes the popup. This method does not accept any arguments.
*
* @expose
* @method
* @name oj.ojPopup#close
* @memberof! oj.ojPopup
* @instance
* @return {void}
* @fires oj.ojPopup#beforeClose
* @fires oj.ojPopup#close
*
* @example <caption>Invoke the <code class="prettyprint">close</code> method:</caption>
* var close = $( ".selector" ).ojPopup( "close" );
*/
close : function ()
{
if (!this.isOpen())
return;
if (this._trigger("beforeClose") === false && !this._ignoreBeforeCloseResultant)
return;
// if the content has focus, restore the the launcher
this._restoreFocus();
var launcher = this._launcher;
var rootElement = this._rootElement;
oj.Assert.assertPrototype(rootElement, jQuery);
oj.Assert.assertPrototype(launcher, jQuery);
this._off(rootElement, "keydown keyup");
if (launcher && launcher.length > 0)
this._off(launcher, "keydown keyup");
//clean up voice over assist
this._destroyVoiceOverAssist();
/** @type {!Object.<oj.PopupService.OPTION, ?>} */
var psOptions = {};
psOptions[oj.PopupService.OPTION.POPUP] = rootElement;
oj.PopupService.getInstance().close(psOptions);
this._removeDescribedBy();
this._setAutoDismiss();
delete this._launcher;
var position = this.options["position"];
// if the open has set the of because one was not provided by default,
// remove the override to the launcher.
if (position["_ofo"])
{
delete position["_ofo"];
delete position["of"];
}
this._trigger("close");
},
/**
* Returns the state of whether the popup is currently open. This method does not accept any arguments.
*
* @expose
* @method
* @name oj.ojPopup#isOpen
* @memberof! oj.ojPopup
* @instance
* @return {boolean} <code>true</code> if the popup is open.
*
* @example <caption>Invoke the <code class="prettyprint">isOpen</code> method:</caption>
* var isOpen = $( ".selector" ).ojPopup( "isOpen" );
*/
isOpen : function ()
{
return this._rootElement.is(":visible");
},
/**
* Causes the popup to reevaluate its position. Call this function after
* the content of the popup has dynamically changed while the popup is open.
*
* <p>This method does not accept any arguments.</p>
*
* @expose
* @method
* @name oj.ojPopup#isOpen
* @memberof! oj.ojPopup
* @instance
* @return {void}
* @override
*
* @example <caption>Invoke the <code class="prettyprint">refresh</code> method:</caption>
* $( ".selector" ).ojPopup( "refresh" );
*/
refresh : function ()
{
this._super();
if (this.isOpen())
this._reposition();
// trigger refresh of descendents
var rootElement = this._rootElement;
oj.Assert.assertPrototype(rootElement, jQuery);
oj.PopupService.getInstance().triggerOnDescendents(rootElement, oj.PopupService.EVENT.POPUP_REFRESH);
},
/**
* @memberof! oj.ojPopup
* @instance
* @protected
* @param {string} key option name
* @param {?Object} value of the target option identified by the key
* @override
*/
_setOption : function (key, value)
{
var options = this.options;
switch (key)
{
case "tail":
if (value !== options["tail"])
{
this._setTail(value);
}
break;
case "chrome":
if (value !== options["chrome"])
this._setChrome(value);
break;
case "position":
this._setPosition(value);
this.refresh();
// don't call super because setPosition sets the option after creating a new
// instance. This prevents the same position instance from getting registered
// with multiple component instances.
return;
case "autoDismiss":
if (this.isOpen() && value !== options["autoDismiss"])
this._setAutoDismiss(value);
break;
}
this._superApply(arguments);
},
/**
* Returns the root selector prefix for the popup components. In the future if the popup is subcassed,
* this method can be made protected and override to change the root selector names for the target
* component.
*
* @memberof! oj.ojPopup
* @instance
* @private
* @return {string}
*/
_getRootStyle : function ()
{
return "oj-popup";
},
/**
* Handles setting up the target tail.
*
* @memberof! oj.ojPopup
* @instance
* @private
* @param {string} tail option value
* @return {void}
*/
_setTail : function (tail)
{
this._destroyTail();
this._createTail(tail);
this._reposition();
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {string} tail option value
* @return {void}
*/
_createTail : function (tail)
{
var tailDecoration = tail ? tail : this.options['tail'];
if ("none" === tailDecoration)
return;
var rootStyle = this._getRootStyle();
var tailMarkerStyle = [rootStyle, "tail"].join("-");
var tailStyle = [tailMarkerStyle, tailDecoration].join("-");
var tailDom = $("<div>").hide();
tailDom.addClass(tailMarkerStyle).addClass(tailStyle);
tailDom.attr("role", "presentation");
// id over "marker style" due to nesting popups in popups
this._tailId = "#" + tailDom.attr("id", this._getSubId("tail")).attr("id");
var rootElement = this._rootElement;
oj.Assert.assertPrototype(rootElement, jQuery);
tailDom.appendTo(rootElement); // @HTMLUpdateOK
// tail "value" style is applied to the root dom for shadow and z-index adjustments
rootElement.addClass(tailStyle);
// The tail can change the z-index of the layer that defines the stacking context
// of the popup. If the popup is open, update the layers class.
if (this.isOpen())
{
var layerClass = [rootStyle, "layer"].join("-");
layerClass += " " + tailStyle;
/** @type {!Object.<oj.PopupService.OPTION, ?>} */
var options = {};
options[oj.PopupService.OPTION.POPUP] = rootElement;
options[oj.PopupService.OPTION.LAYER_SELECTORS] = layerClass;
oj.PopupService.getInstance().changeOptions(options);
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {jQuery}
*/
_getTail : function ()
{
var tailId = this._tailId;
if (!tailId)
return null;
return $(tailId);
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {void}
*/
_destroyTail : function ()
{
var tail = this._getTail();
if (tail)
tail.remove();
delete this._tailId;
var tailDecoration = this.options['tail'];
var rootStyle = this._getRootStyle();
var tailStyle = [rootStyle, "tail", tailDecoration].join("-");
var rootElement = this._rootElement;
oj.Assert.assertPrototype(rootElement, jQuery);
rootElement.removeClass(tailStyle);
// if the popup is open, reseed the layer class removing the
// tail style.
if (this.isOpen())
{
var layerClass = [rootStyle, "layer"].join("-");
/** @type {!Object.<oj.PopupService.OPTION, ?>} */
var options = {};
options[oj.PopupService.OPTION.POPUP] = rootElement;
options[oj.PopupService.OPTION.LAYER_SELECTORS] = layerClass;
oj.PopupService.getInstance().changeOptions(options);
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {string} chrome option value
* @return {void}
*/
_setChrome : function (chrome)
{
var chromeDecoration = (chrome ? chrome : this.options["chrome"]);
var noChromeStyle = [this._getRootStyle(), "no-chrome"].join("-");
var rootElement = this._rootElement;
oj.Assert.assertPrototype(rootElement, jQuery);
if ("default" === chromeDecoration && rootElement.hasClass(noChromeStyle))
rootElement.removeClass(noChromeStyle);
else if ("none" === chromeDecoration && !rootElement.hasClass(noChromeStyle))
rootElement.addClass(noChromeStyle);
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {string|Node|jQuery|null} launcher provided when the popup is open
* @return {void}
*/
_setLauncher: function (launcher)
{
if (!launcher)
launcher = $(document.activeElement);
else if ($.type(launcher) === "string")//id jquery selector
launcher = $(launcher);
else if (launcher.nodeType === 1)//dom element
launcher = $(launcher);
// if a jquery collection, select the first dom node not in the popups content
if (launcher instanceof jQuery && launcher.length > 1)
{
var rootElement = this._rootElement;
oj.Assert.assertPrototype(rootElement, jQuery);
for (var i = 0;i < launcher.length;i++)
{
var target = launcher[0];
//if (rootElement.has(target).length === 0) {
if (!oj.DomUtils.isAncestorOrSelf(rootElement[0], target))
{
launcher = $(target);
break;
}
}
}
else if (!(launcher instanceof jQuery) || //object is not a jq
((launcher instanceof jQuery) && launcher.length === 0))// empty jq collection
launcher = $(document.activeElement);
oj.Assert.assertPrototype(launcher, jQuery);
this._launcher = launcher;
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {?Object} position object set as an option or passed as an argument to the open method.
* @return {void}
*/
_setPosition : function (position)
{
var options = this.options;
// new position extends the existing object
if (position)
options["position"] = $.extend(options[position], position);
// grab the updated position
position = options["position"];
var usingCallback = this._usingCallback;
oj.Assert.assertFunction(usingCallback);
// if they provided a using function that is not our callback, stash it
// away so that we can delegate to it in our proxy.
if ($.isFunction(position["using"]) && position["using"] !== usingCallback)
position["origUsing"] = position["using"];
// override with our proxy to handle positioning of the tail
position["using"] = usingCallback;
//override "of" alignment node to the launcher if not specified
var launcher = this._launcher;
oj.Assert.assertPrototype(launcher, jQuery);
if (!position["of"])
{
position["of"] = launcher;
position["_ofo"] = true;
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {Object} pos "my" element associated with the position object
* @param {Object} props directions as to where the element should be moved
* @return {void}
*/
_usingHandler: function(pos, props)
{
var rootElement = props["element"]["element"];
oj.Assert.assertPrototype(rootElement, jQuery);
var tail = this._getTail();
if (!tail)
rootElement.css(pos);
else
{
tail.hide();
for (var i = 0; i < _TAIL_STYLES.length; i++)
{
tail.removeClass(_TAIL_STYLES[i]);
rootElement.removeClass(_TAIL_STYLES[i]);
}
tail.removeAttr("style");
// bug 19987878
// Check if "of" alignment is to a x,y versus a dom element. The horizontal
// rule returned from jquery UI position defaults to "left" or "right". The height and
// width of the target will be zero when the "of" is not a DOM element.
// Use the position.my alignment rules over what jquery returns when aligned to a point.
if (props["target"] && props["target"]["height"] === 0 && props["target"]["width"] === 0)
{
var isRtl = this._GetReadingDirection() === "rtl";
var position = oj.PositionUtils.normalizeHorizontalAlignment(this.options["position"], isRtl);
var myrule = position["my"];
if (!oj.StringUtils.isEmptyOrUndefined(myrule))
{
var myrules = myrule.split(" ");
var suggestedHrule = myrules[0];
var suggestedVrule = "middle";
if (myrules.length > 1)
suggestedVrule = (myrules[1] === "center") ? "middle" : myrules[1];
props['horizontal'] = suggestedHrule;
props['vertical'] = suggestedVrule;
}
}
var alignMnemonic = [props["horizontal"], props["vertical"]].join("-");
var tailStyle = _TAIL_ALIGN_RULES[alignMnemonic];
oj.Assert.assertString(tailStyle);
tail.addClass(tailStyle);
rootElement.addClass(tailStyle);
tail.show();
// adjust the vertical and horizontal positioning to account for the tail
// so that the page developer doesn't have to factor that in
if ("left" === props["horizontal"] || "right" === props["horizontal"])
{
var tailHOffset = (tail.outerWidth() - 1) * ("left" === props["horizontal"] ? 1 : -1);
pos["left"] = pos["left"] + tailHOffset;
}
else if ("center" === props["horizontal"])
{
var tailVOffset = (tail.outerHeight() - 1) * ("top" === props["vertical"] ? 1 : -1);
pos["top"] = pos["top"] + tailVOffset;
}
rootElement.css(pos);
// adjustments to the vertical or horizontal centering. The 50% alignment is from
// the edge of the tail versus the center of the image. The tail can't be located
// at "center, middle". In this case (dead center), horizintal center looks better
// on small viewports (_TAIL_ALIGN_RULES["center-middle"] === 'oj-center oj-bottom')
if ("center" === props["horizontal"])
{
var rootWidth = rootElement.width();
var leftPercent = Math.round((((rootWidth / 2) - (tail.outerWidth() / 2)) / rootWidth) * 100);
tail.css(
{
left: leftPercent + '%'
});
}
else if ("middle" === props["vertical"])
{
var rootHeight = rootElement.height();
var topPercent = Math.round((((rootHeight / 2) - (tail.outerHeight() / 2)) / rootHeight) * 100);
tail.css(
{
top: topPercent + '%'
});
}
}
// call on the original using regardless of the tail
var options = this.options;
var origUsing = options["position"]["origUsing"];
if (origUsing)
origUsing(pos, props);
// The "origUsing" could alter the positon. This check needs to be last.
// When focusLoss auto dismissal is enabled, implicitly close the popup when the
// position.of is clipped in an overflow container.
if ("focusLoss" === options["autoDismiss"])
{
if (oj.PositionUtils.isAligningPositionClipped(props))
{
// Ignore focus back to what had focus before the popup was open. Focus
// restore could fight scroll if the popup was closed due to the aligning
// element being clipped.
this._ignoreRestoreFocus = true;
this._closeDelayTimer = this._delay($.proxy(this._closeImplicitly, this), 1);
}
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {void}
*/
_reposition : function ()
{
var rootElement = this._rootElement;
oj.Assert.assertPrototype(rootElement, jQuery);
var position = this.options["position"];
oj.Assert.assertObject(position);
var isRtl = this._GetReadingDirection() === "rtl";
rootElement.position(oj.PositionUtils.normalizeHorizontalAlignment(position, isRtl));
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {boolean=} skipOptionCheck focus established via keyboard versus from open API
* @return {void}
*/
_intialFocus : function (skipOptionCheck)
{
var options = this.options;
if (!skipOptionCheck && "none" === options["initialFocus"])
return;
if (skipOptionCheck || "firstFocusable" === options["initialFocus"])
{
var nodes = this.element.find(":focusable");
if (nodes.length > 0)
{
var first = nodes[0];
oj.Assert.assertDomElement(first);
$(first).focus();
}
else
{
var rootElement = this._rootElement;
oj.Assert.assertPrototype(rootElement, jQuery);
rootElement.attr("tabindex", "-1");
rootElement.focus();
}
this._trigger("focus");
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {Element} activeElement from the event being handled
* @param {boolean!} includeChildren when true the focus test will include the scope of any
* child popups.
* @return {boolean} <code>true</code> if the active element is within the content of the popup
*/
_isFocusInPopup : function (activeElement, includeChildren)
{
if (!activeElement)
activeElement = document.activeElement;
oj.Assert.assertDomElement(activeElement);
// added to avoid automation issues where an active element is not established
if (!activeElement)
return false;
var rootElement = this._rootElement;
oj.Assert.assertPrototype(rootElement, jQuery);
// popups that are children are siblings to the parent popup within the
// layer that defines the stacking context.
if (includeChildren)
rootElement = rootElement.parent();
return oj.DomUtils.isAncestorOrSelf(rootElement[0], activeElement);
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {Element} activeElement from the event being handled
* @return {boolean} <code>true</code> if the active element the launcher or a decedent of the launcher
*/
_isFocusInLauncher : function (activeElement)
{
if (!activeElement)
activeElement = document.activeElement;
oj.Assert.assertDomElement(activeElement);
var launcher = this._launcher;
oj.Assert.assertPrototype(launcher, jQuery);
// return launcher.is(activeElement) || launcher.has(activeElement).length > 0;
return oj.DomUtils.isAncestorOrSelf(launcher[0], activeElement);
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {void}
*/
_restoreFocus : function ()
{
if (this._ignoreRestoreFocus)
{
delete this._ignoreRestoreFocus;
return;
}
// extend the focus check to include popups that are children
if (this._isFocusInPopup(null, true))
{
var launcher = this._launcher;
oj.Assert.assertPrototype(launcher, jQuery);
launcher.focus();
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {jQuery.Event|Event} event keydown
* @return {void}
*/
_keyHandler : function (event)
{
if (event.isDefaultPrevented())
return;
var eventType = event.type;
/** @type {?} */
var target = event.target;
oj.Assert.assertDomElement(target);
if ("keyup" === eventType && event.keyCode === $.ui.keyCode.ESCAPE &&
(this._isFocusInPopup(target) || this._isFocusInLauncher(target)))
{
event.preventDefault();
this.close();
}
else if ("keydown" === eventType && event.keyCode === 117)
{
//F6 - toggle focus to launcher or popup
if (this._isFocusInPopup(target))
{
event.preventDefault();
var launcher = this._launcher;
oj.Assert.assertPrototype(launcher, jQuery);
launcher.focus();
}
else if (this._isFocusInLauncher(target))
{
event.preventDefault();
this._intialFocus(true);
}
}
else if ("keydown" === eventType && event.keyCode === $.ui.keyCode.TAB &&
this._isFocusInPopup(target))
{
// TAB within popup
var nodes = this.element.find(":tabbable");
if (nodes.length > 0)
{
var firstNode = nodes[0];
oj.Assert.assertDomElement(firstNode);
var lastNode = nodes[nodes.length - 1];
oj.Assert.assertDomElement(lastNode);
if (firstNode === target && event.shiftKey)
{
//tabbing backwards, cycle focus to last node
event.preventDefault();
$(lastNode).focus();
}
else if (lastNode === target && !event.shiftKey)
{
//tabbing forwards, cycle to the first node
event.preventDefault();
$(firstNode).focus();
}
}
else
{
// if there is nothing in the popup that is tabbable, handle as a F6
// toggle to the launcher
event.preventDefault();
var launcher = this._launcher;
oj.Assert.assertPrototype(launcher, jQuery);
launcher.focus();
}
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {string|null} autoDismiss option value
* @return {void}
*/
_setAutoDismiss : function (autoDismiss)
{
// unregister any existing handlers, might need to add mouseOut in the future
var focusLossCallback = this._focusLossCallback;
var events = this._getPopupServiceEvents();
if (focusLossCallback)
{
delete events[oj.PopupService.EVENT.POPUP_AUTODISMISS];
delete this._focusLossCallback;
}
if ("focusLoss" === autoDismiss)
{
focusLossCallback = this._focusLossCallback = $.proxy(this._dismissalHandler, this);
events[oj.PopupService.EVENT.POPUP_AUTODISMISS] = focusLossCallback;
}
if (this.isOpen())
{
var rootElement = this._rootElement;
oj.Assert.assertPrototype(rootElement, jQuery);
/** @type {!Object.<oj.PopupService.OPTION, ?>} */
var options = {};
options[oj.PopupService.OPTION.POPUP] = rootElement;
options[oj.PopupService.OPTION.EVENTS] = events;
oj.PopupService.getInstance().changeOptions(options);
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {Event} event native doc
* @return {void}
*/
_dismissalHandler : function (event)
{
var launcher = this._launcher;
var rootElement = this._rootElement;
oj.Assert.assertPrototype(launcher, jQuery);
oj.Assert.assertPrototype(rootElement, jQuery);
// child popups are siblings to the parent in the layer.
var layer = rootElement.parent();
/** @type {?} */
var target = event.target;
oj.Assert.assertDomElement(target);
// if the target is on the focus skip link next to the launcher, ignore.
var focusSkipLink = this._focusSkipLink;
if (focusSkipLink)
{
var link = focusSkipLink.getLink();
if (link && oj.DomUtils.isAncestorOrSelf(link[0], target))
return;
}
// if event target is not under the laucher or popup root dom subtrees, dismiss
if (!oj.DomUtils.isAncestorOrSelf(launcher[0], target) && !oj.DomUtils.isAncestorOrSelf(layer[0], target))
{
if ($(target).is(":focusable"))
{
// If the dismissal event target can take focus and the
// event type is a mousedown or touchstart, wait for the focus event
// to trigger dismissal. This allows the blur to happen
// on input components which triggers validation.
if ("mousedown" === event.type || "touchstart" === event.type)
return;
this._ignoreRestoreFocus = true;
}
// invoke standard close dismissal that can be canceled via the beforeclose
// event.
this.close();
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {void}
*/
_addDescribedBy: function ()
{
var launcher = this._launcher;
var rootElement = this._rootElement;
oj.Assert.assertPrototype(launcher, jQuery);
oj.Assert.assertPrototype(rootElement, jQuery);
var popupId = rootElement.attr("id");
var describedby = launcher.attr("aria-describedby");
var tokens = describedby ? describedby.split(/\s+/) : [];
tokens.push(popupId);
describedby = $.trim(tokens.join(" "));
launcher.attr("aria-describedby", describedby);
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {void}
*/
_removeDescribedBy: function() {
var launcher = this._launcher;
var rootElement = this._rootElement;
oj.Assert.assertPrototype(launcher, jQuery);
oj.Assert.assertPrototype(rootElement, jQuery);
var popupId = rootElement.attr("id");
var describedby = launcher.attr("aria-describedby");
var tokens = describedby ? describedby.split(/\s+/) : [];
var index = $.inArray(popupId, tokens);
if (index !== -1)
tokens.splice(index, 1);
describedby = $.trim(tokens.join(" "));
if (describedby)
launcher.attr("aria-describedby", describedby);
else
launcher.removeAttr("aria-describedby");
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {void}
*/
_initVoiceOverAssist: function()
{
var isIOSVOSupported = (oj.AgentUtils.getAgentInfo()['os'] === oj.AgentUtils.OS.IOS);
var liveRegion = this._liveRegion;
if (!liveRegion)
liveRegion = this._liveRegion = new oj.PopupLiveRegion();
var message;
if (isIOSVOSupported)
message = this.getTranslatedString("none" === this.options["initialFocus"] ?
"ariaLiveRegionInitialFocusNoneTouch":
"ariaLiveRegionInitialFocusFirstFocusableTouch");
else
message = this.getTranslatedString("none" === this.options["initialFocus"] ?
"ariaLiveRegionInitialFocusNone":
"ariaLiveRegionInitialFocusFirstFocusable");
liveRegion.announce(message);
if (isIOSVOSupported)
{
var focusSkipLink = this._focusSkipLink;
if (!focusSkipLink)
{
var focusSkipLinkId = this._getSubId("focusSkipLink");
var launcher = this._launcher;
var callback = $.proxy(this._intialFocus, this, true);
message = this.getTranslatedString("ariaFocusSkipLink");
this._focusSkipLink = new oj.PopupSkipLink(launcher, message, callback, focusSkipLinkId);
}
var closeSkipLink = this._closeSkipLink;
if (!closeSkipLink)
{
var closeSkipLinkId = this._getSubId("closeSkipLink");
var element = this.element;
var callback = $.proxy(this._closeImplicitly, this);
message = this.getTranslatedString("ariaCloseSkipLink");
this._closeSkipLink = new oj.PopupSkipLink(element, message, callback, closeSkipLinkId);
}
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {void}
*/
_destroyVoiceOverAssist: function()
{
var liveRegion = this._liveRegion;
if (liveRegion)
{
liveRegion.destroy();
delete this._liveRegion;
}
var focusSkipLink = this._focusSkipLink;
if (focusSkipLink)
{
focusSkipLink.destroy();
delete this._focusSkipLink;
}
var closeSkipLink = this._closeSkipLink;
if (closeSkipLink)
{
closeSkipLink.destroy();
delete this._closeSkipLink;
}
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @param {string} sub id that will become a composite id prefixed with the components uuid
* @return {string}
*/
_getSubId: function(sub)
{
/** @type {?} */
var id = this.element.attr("id");
if (oj.StringUtils.isEmptyOrUndefined(id))
id = this["uuid"];
return [id, sub].join("_");
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {void}
*/
_surrogateRemoveHandler: function()
{
var element = this.element;
element.remove();
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {!Object.<oj.PopupService.EVENT, function(...)>}
*/
_getPopupServiceEvents: function()
{
if (!this._popupServiceEvents)
{
/** @type {!Object.<oj.PopupService.EVENT, function(...)>} **/
var events = this._popupServiceEvents = {};
events[oj.PopupService.EVENT.POPUP_CLOSE] = $.proxy(this._closeImplicitly, this);
events[oj.PopupService.EVENT.POPUP_REMOVE] = $.proxy(this._surrogateRemoveHandler, this);
events[oj.PopupService.EVENT.POPUP_REFRESH] = $.proxy(this.refresh, this);
}
return this._popupServiceEvents;
},
/**
* @memberof! oj.ojPopup
* @instance
* @private
* @return {void}
*/
_closeImplicitly: function()
{
this._ignoreBeforeCloseResultant = true;
this.close();
delete this._ignoreBeforeCloseResultant;
}
});
}
// Fragments:
/**
* <table class="keyboard-table">
* <thead>
* <tr>
* <th>Target</th>
* <th>Gesture</th>
* <th>Action</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td>Outside popup or launcher</td>
* <td><kbd>Tap</kbd></td>
* <td>Close the popup.</td>
* </tr>
* </tbody>
* </table>
*
* <p>Disabled items do not allow any touch interaction.
*
* @ojfragment touchDoc - Used in touch gesture section of classdesc, and standalone gesture doc
* @memberof oj.ojPopup
*/
/**
* <table class="keyboard-table">
* <thead>
* <tr>
* <th>Target</th>
* <th>Key</th>
* <th>Action</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td rowspan = "3">Focus within Popup</td>
* <td><kbd>Tab</kbd> or <kbd>Shift + Tab</kbd></td>
* <td>Navigate the content of the popup.</td>
* </tr>
* <tr>
* <td><kbd>F6</kbd></td>
* <td>Move focus to the launcher.</td>
* </tr>
* <tr>
* <td><kbd>Esc</kbd></td>
* <td>Close the open popup.</td>
* </tr>
* <tr>
* <td rowspan = "1">Popup Launcher</td>
* <td><kbd>F6</kbd></td>
* <td>Move focus to the first tab stop within the open popup. If there is not a tab stop within the
* content, focus is established on the popup.</td>
* </tr>
* </tbody>
* </table>
*
* @ojfragment keyboardDoc - Used in keyboard section of classdesc, and standalone gesture doc
* @memberof oj.ojPopup
*/
());
Source: src/main/javascript/oracle/oj/ojpopup/ojpopup.js
Oracle® JavaScript Extension Toolkit (JET)
1.1.2
E65298-01