· 6 years ago · Oct 17, 2019, 06:58 AM
1/*
2** Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
3*/
4
5function AdfDhtmlPopupWindow()
6{
7 this.Init();
8}
9
10AdfObject.createSubclass(AdfDhtmlPopupWindow, AdfAbstractFloatingElement);
11
12// Hints that may be used before the class initialization.
13AdfDhtmlPopupWindow.HINT_ALIGN_ELEMENT = "alignElement";
14AdfDhtmlPopupWindow.HINT_MOUSEPOSITION = "mousePosition";
15AdfDhtmlPopupWindow.HINT_POSITION_CENTER = "screenCenterPosition"
16
17AdfDhtmlPopupWindow.HINT_TYPE = "type";
18AdfDhtmlPopupWindow.HINT_TYPE_MENU = "menu";
19AdfDhtmlPopupWindow.HINT_TYPE_NOTEWINDOW = "noteWindow";
20AdfDhtmlPopupWindow.HINT_TYPE_DIALOG = "dialogWindow";
21AdfDhtmlPopupWindow.HINT_TYPE_INLINESELECTOR = "inlineSelector";
22AdfDhtmlPopupWindow.HINT_TYPE_LOVCOMBOBOX = "lovCombobox";
23AdfDhtmlPopupWindow.HINT_TYPE_LAYER = "layer";
24
25/* title name for the popup that is included for screen reader mode menu type popups */
26AdfDhtmlPopupWindow.HINT_TITLE = "title";
27
28/* boolean value indicating if focus should be placed in popup */
29AdfDhtmlPopupWindow.HINT_FOCUS = "focus";
30
31AdfDhtmlPopupWindow.COMPONENT_EVENTS_ENABLED = "componentEventsEnabled";
32
33AdfDhtmlPopupWindow.HINT_AUTODISMISS_POPUP_TIMEOUT = "autodismissPopupTimeout";
34
35/* autodismissal hints */
36AdfDhtmlPopupWindow.HINT_AUTODISMISS = "autodismiss";
37// Id of the dom element to be used for autodismiss on mouseout
38AdfDhtmlPopupWindow.HINT_AUTODISMISS_NEVER = "autodismissNever";
39
40AdfDhtmlPopupWindow.HINT_AUTODISMISS_ALWAYS = "autodismissAlways";
41AdfDhtmlPopupWindow.HINT_AUTODISMISS_TIMEOUT = "autodismissTimeout";
42
43/**
44 * @deprecated private hint deprecated. Use public hint AdfRichPopup.HINT_LAUNCH_ID.
45 */
46AdfDhtmlPopupWindow.HINT_LAUNCH_SOURCE_ID = "launchId";
47
48AdfDhtmlPopupWindow.HINT_AUTODISMISS_MENU = "autodismissMenu";
49
50AdfDhtmlPopupWindow.HINT_AUTODISMISS_MOUSEOUT = "autodismissMouseOut";
51AdfDhtmlPopupWindow.HINT_AUTODISMISS_MOUSEOUT_ID = "autodismissMouseOut";
52
53AdfDhtmlPopupWindow.HINT_AUTODISMISS_INACTIVATE = "autodismissInactivate";
54
55AdfDhtmlPopupWindow.HINT_CLOSE_ON_ESCAPE = "closeOnEscape";
56AdfDhtmlPopupWindow.HINT_MAX_WIDTH = "maxWidth";
57
58AdfDhtmlPopupWindow.HINT_OPENED_HANDLER = "openedHandler";
59AdfDhtmlPopupWindow.HINT_OPENED_HANDLER_PARAM = "openedHandlerParam";
60
61AdfDhtmlPopupWindow.HINT_ALIGN = "align";
62AdfDhtmlPopupWindow.HINT_MODAL = "modal";
63AdfDhtmlPopupWindow.HINT_CONTENT = "content";
64AdfDhtmlPopupWindow.HINT_DRAG_ELEMENT = "dragElement";
65AdfDhtmlPopupWindow.HINT_LEFT_POSITION = "leftPosition";
66AdfDhtmlPopupWindow.HINT_TOP_POSITION = "topPosition";
67
68AdfDhtmlPopupWindow.HINT_CLOSE_HANDLER = "closeHandler";
69AdfDhtmlPopupWindow.HINT_CLOSE_HANDLER_PARAM = "closeHandlerParam";
70AdfDhtmlPopupWindow.HINT_DETACH_DRAG_START_HANDLER = "detachDragStartHandler";
71AdfDhtmlPopupWindow.HINT_DETACH_DRAG_END_HANDLER = "detachDragEndHandler";
72AdfDhtmlPopupWindow.HINT_COMPONENT_CLIENT_ID = "componentId";
73
74AdfDhtmlPopupWindow.HINT_ANIMATE = "animate";
75AdfDhtmlPopupWindow.HINT_RESTORE_IMMEDIATE = "restoreImmediate"
76
77// aria-label key for the element carrying role="dialog"
78AdfDhtmlPopupWindow.ARIA_LABEL= "ariaLabel" ;
79
80// hint to allow inline popup in screen reader mode
81AdfDhtmlPopupWindow.HINT_UNSCOPED = "unscoped";
82AdfDhtmlPopupWindow.HINT_UNSCOPED_ALWAYS = "always";
83
84/**
85 * Holds the name of the expando property added to the contentDom of the AdfDhtmlPopupWindow.HINT_CLOSE_HANDLER_PARAM
86 * when the floating element is dismissed by calling cancel.
87 */
88AdfDhtmlPopupWindow.__CANCELED_EXPANDO = "_isCanceled";
89AdfDhtmlPopupWindow.__REPLACEDOM_EXPANDO = "_isReplaceDom"
90AdfDhtmlPopupWindow.__POPUPID_EXPANDO = "data-afr-popupid";
91AdfDhtmlPopupWindow.__POPUP_CONTAINER_ID = "popup-container";
92AdfDhtmlPopupWindow.__POPUP_TITLE_ID = "title";
93
94AdfDhtmlPopupWindow._SLIDE_UP = "up";
95AdfDhtmlPopupWindow._SLIDE_DOWN = "down";
96AdfDhtmlPopupWindow._SLIDE_LEFT = "left";
97AdfDhtmlPopupWindow._SLIDE_RIGHT = "right";
98
99// In full screen popup mode, this attribute will be set on all ZOrderLayerContainer siblings
100// or to document body with existing display style value
101AdfDhtmlPopupWindow._FULL_SCREEN_EXPANDO = "afr-old-pop-style";
102
103// AdfDhtmlPopupWindow._POPUP_FULLSCREEN: Indicates that the popup will stretch
104// corner to corner, covering the entire screen space.
105//AdfDhtmlPopupWindow._POPUP_FULLSCREEN = "_afr_mobile_fullScreenPopup";
106
107// AdfDhtmlPopupWindow._POPUP_FULLHEIGHTPARTIALSCREEN: Indicates that the popup
108// will stretch to fill the height of the screen but may not cover the entire
109// screen width.
110//AdfDhtmlPopupWindow._POPUP_FULLHEIGHTPARTIALSCREEN = "_afr_mobile_fullHeightPartialScreenPopup";
111
112//AdfDhtmlPopupWindow._POPUP_PARTIALSCREEN: DOes not stretch
113//AdfDhtmlPopupWindow._POPUP_PARTIALSCREEN = "_afr_mobile_partialScreenPopup";
114
115// Note the current observed height of the navigator and mention that 75 is a safe
116// guesstimate to cover past and future nav bar heights.
117//AdfDhtmlPopupWindow._SCROLL_RESIZE_THRESHOLD = 75;
118AdfDhtmlPopupWindow.InitClass = function()
119{
120 // expandos - use short component id names for performance
121 this._SCROLL_POSITION = "_afrSclPtn";
122
123 // expando used to mark the temporary wrapper div created during animation
124 this._ANIMATE_WRAPPER_EXPANDO = "data-afr-taw";
125
126 this._POPUP_TAIL_TOP_LEFT_STYLECLASS = "AFPopupTailTopLeft";
127 this._POPUP_TAIL_TOP_RIGHT_STYLECLASS = "AFPopupTailTopRight";
128 this._POPUP_TAIL_TOP_CENTER_STYLECLASS = "AFPopupTailTopCenter";
129 this._POPUP_TAIL_BOTTOM_LEFT_STYLECLASS = "AFPopupTailBottomLeft";
130 this._POPUP_TAIL_BOTTOM_RIGHT_STYLECLASS = "AFPopupTailBottomRight";
131 this._POPUP_TAIL_BOTTOM_CENTER_STYLECLASS = "AFPopupTailBottomCenter";
132 this._POPUP_TAIL_MIDDLE_LEFT_STYLECLASS = "AFPopupTailMiddleLeft";
133 this._POPUP_TAIL_MIDDLE_RIGHT_STYLECLASS = "AFPopupTailMiddleRight";
134
135 this._POPUP_TAIL_STYLECLASS = "AFPopupTail";
136 this._POPUP_TAIL_SIMPLE_STYLECLASS = "AFPopupTailSimple";
137}
138
139AdfDhtmlPopupWindow.prototype.Init = function()
140{
141 // call super
142 AdfDhtmlPopupWindow.superclass.Init.call(this, AdfDhtmlZOrderManager.FLOATINGTYPE_WINDOW);
143
144 var rootElement = this.CreateDomElement();
145 // use setElement method to make sure element gets added to the correct layer
146 this.setElement(rootElement);
147 rootElement.style.display = "none";
148
149 // this._componentId;
150 // this._sizeTimeout;
151
152 // this._openedHandler;
153 // this._openedHandlerParam;
154
155 // this._positionManagerIndex = undefined;
156 // this._restoreFocusId = null;
157 // this._maxWidth = undefined;
158
159 // The duration of the animation
160 // this._animationDuration = undefined;
161
162 // this._animating = undefined;
163}
164
165/**
166 * Create and initialize the html. Subclasses must override this method
167 * @return {HTMLElement} the root dom element of this popup.
168 */
169AdfDhtmlPopupWindow.prototype.CreateDomElement = function()
170{
171 AdfAssert.failedInAbstractFunction();
172}
173
174/**
175 * Resultant indicates if popup window should hide on clip. This behavior is by default true
176 * for non-touch platforms but is inactivated for touch devices.
177 *
178 * @return {Boolean} if popup window should hide on clip
179 */
180AdfDhtmlPopupWindow.prototype.ShouldHideOnClip= function()
181{
182 return !AdfAgent.AGENT.isTouchFirstDevice();
183}
184
185AdfDhtmlPopupWindow.prototype.getInitialPopupTailPosition = function(alignHint)
186{
187 if (!alignHint)
188 {
189 return;
190 }
191
192 var popupTailPosition;
193
194 switch (alignHint)
195 {
196 case AdfRichPopup.ALIGN_AFTER_START:
197 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_TOP_LEFT_STYLECLASS;
198 break;
199 case AdfRichPopup.ALIGN_AFTER_END:
200 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_TOP_RIGHT_STYLECLASS;
201 break;
202 case AdfRichPopup.ALIGN_AFTER_CENTER:
203 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_TOP_CENTER_STYLECLASS;
204 break;
205 case AdfRichPopup.ALIGN_BEFORE_START:
206 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_BOTTOM_LEFT_STYLECLASS;
207 break;
208 case AdfRichPopup.ALIGN_BEFORE_END:
209 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_BOTTOM_RIGHT_STYLECLASS;
210 break;
211 case AdfRichPopup.ALIGN_BEFORE_CENTER:
212 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_BOTTOM_CENTER_STYLECLASS;
213 break;
214 case AdfRichPopup.ALIGN_END_AFTER:
215 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_BOTTOM_LEFT_STYLECLASS;
216 break;
217 case AdfRichPopup.ALIGN_END_BEFORE:
218 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_TOP_LEFT_STYLECLASS;
219 break;
220 case AdfRichPopup.ALIGN_END_CENTER:
221 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_MIDDLE_LEFT_STYLECLASS;
222 break;
223 case AdfRichPopup.ALIGN_START_AFTER:
224 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_BOTTOM_RIGHT_STYLECLASS;
225 break;
226 case AdfRichPopup.ALIGN_START_BEFORE:
227 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_TOP_RIGHT_STYLECLASS;
228 break;
229 case AdfRichPopup.ALIGN_START_CENTER:
230 popupTailPosition = AdfDhtmlPopupWindow._POPUP_TAIL_MIDDLE_RIGHT_STYLECLASS;
231 break;
232 default:
233 popupTailPosition = "";
234 }
235
236 return popupTailPosition;
237}
238
239AdfDhtmlPopupWindow.prototype._adjustPopupPosition = function(top, left, tailPosition)
240{
241
242 if (!this._tailElement)
243 {
244 return {"left": left, "top": top};
245 }
246 var tailWidth = this._tailElement.offsetWidth;
247 var tailHeight = this._tailElement.offsetHeight;
248
249 switch (tailPosition)
250 {
251 case AdfDhtmlPopupWindow._POPUP_TAIL_TOP_LEFT_STYLECLASS:
252 case AdfDhtmlPopupWindow._POPUP_TAIL_BOTTOM_LEFT_STYLECLASS:
253 left += tailWidth;
254 break;
255 case AdfDhtmlPopupWindow._POPUP_TAIL_MIDDLE_LEFT_STYLECLASS:
256 left +=tailWidth;
257 var rootHeight = this.getElement().offsetHeight;
258 var topPercent = Math.round((((rootHeight / 2) - (tailHeight / 2)) / rootHeight) * 100);
259 this._tailElement.style.top = topPercent + '%';
260 break;
261 case AdfDhtmlPopupWindow._POPUP_TAIL_TOP_RIGHT_STYLECLASS:
262 case AdfDhtmlPopupWindow._POPUP_TAIL_BOTTOM_RIGHT_STYLECLASS:
263 left -= tailWidth;
264 break;
265 case AdfDhtmlPopupWindow._POPUP_TAIL_MIDDLE_RIGHT_STYLECLASS:
266 left -=tailWidth;
267 var rootHeight = this.getElement().offsetHeight;
268 var topPercent = Math.round((((rootHeight / 2) - (tailHeight / 2)) / rootHeight) * 100);
269 this._tailElement.style.top = topPercent + '%';
270 break;
271 case AdfDhtmlPopupWindow._POPUP_TAIL_TOP_CENTER_STYLECLASS:
272 top +=tailHeight;
273 var rootWidth = this.getElement().offsetWidth;
274 var leftPercent = Math.round((((rootWidth / 2) - (tailWidth / 2)) / rootWidth) * 100);
275 this._tailElement.style.left = leftPercent + '%';
276 break;
277 case AdfDhtmlPopupWindow._POPUP_TAIL_BOTTOM_CENTER_STYLECLASS:
278 top -=tailHeight;
279 var rootWidth = this.getElement().offsetWidth;
280 var leftPercent = Math.round((((rootWidth / 2) - (tailWidth / 2)) / rootWidth) * 100);
281 this._tailElement.style.left = leftPercent + '%';
282 break;
283 default:
284 top = top;
285 left = left;
286 }
287
288 return {"left": left, "top": top};
289}
290
291AdfDhtmlPopupWindow.prototype.setPosition = function(top, left)
292{
293
294 var positionManagerIndex = this.GetPositionManagerIndex();
295 if(positionManagerIndex >=0 && this._tailElement)
296 {
297 var behavior = AdfPage.PAGE.getPositionManager().getComputedBehavior(positionManagerIndex);
298
299 if(behavior)
300 {
301 var verticalBehavior = behavior.verticalBehavior;
302 var horizontalBehavior = behavior.horizontalBehavior;
303 var styleClass = "AFPopupTail";
304
305 switch(verticalBehavior.floatingElementAnchor)
306 {
307 case AdfDhtmlPositionManager.TOP:
308 styleClass += "Top";
309 break;
310 case AdfDhtmlPositionManager.BOTTOM:
311 styleClass += "Bottom";
312 break;
313 case AdfDhtmlPositionManager.MIDDLE:
314 styleClass += "Middle";
315 break;
316 default:
317 AdfAssert.assert(false, "Invalid floatingElementAnchor for verticalBehavior provided.");
318 AdfLogger.LOGGER.warning("Invalid floatingElementAnchor for verticalBehavior provided.");
319 break;
320 }
321
322 switch(horizontalBehavior.floatingElementAnchor)
323 {
324 case AdfDhtmlPositionManager.LEFT:
325 styleClass += "Left";
326 break;
327 case AdfDhtmlPositionManager.RIGHT:
328 styleClass += "Right";
329 break;
330 case AdfDhtmlPositionManager.CENTER:
331 styleClass += "Center";
332 break;
333 default:
334 AdfAssert.assert(false, "Invalid floatingElementAnchor for horizontalBehavior provided.");
335 AdfLogger.LOGGER.warning("Invalid floatingElementAnchor for horizontalBehavior provided.");
336 break;
337 }
338
339 AdfDomUtils.removeCSSClassName(this._tailElement,this._tailPosition);
340 this._tailPosition = styleClass;
341 AdfDomUtils.addCSSClassName(this._tailElement, styleClass);
342 var newPosition = this._adjustPopupPosition(top, left, styleClass);
343 top = newPosition["top"];
344 left = newPosition["left"];
345 }
346 }
347
348 AdfDhtmlPopupWindow.superclass.setPosition.call(this, top, left);
349}
350
351AdfDhtmlPopupWindow.prototype.getTailElement = function()
352{
353 return this._tailElement;
354}
355
356/**
357 * Determines if the popup has a different rendering in screen reader mode as compared to
358 * the normal mode.
359 */
360AdfDhtmlPopupWindow.prototype.hasSpecialRenderingForScreenReader = function()
361{
362 return !!AdfPopupScopingUtils.isScopingContainer(this.getElement());
363}
364
365/**
366 * Returns true if screen reader mode is on and the popup has a 'regular' screen reader mode popup.
367 * @return true if screen reader mode is on and the popup has a 'regular' screen reader mode popup.
368 */
369 AdfDhtmlPopupWindow.prototype.isScreenReaderModePopup = function()
370 {
371 return (AdfPage.PAGE.isScreenReaderMode() && this.hasSpecialRenderingForScreenReader())
372 }
373
374/**
375 * Displays a popup window on the screen.
376 *
377 * @param {Object:null} hints a set of popup hints (see {@link AdfDhtmlPopupWindow})
378 * @see #hide
379 */
380AdfDhtmlPopupWindow.prototype.show = function(hints)
381{
382 AdfAssert.assertObject(hints, "Cannot show a popup element without hints");
383
384 // Start with timing
385
386 var componentId = hints[AdfDhtmlPopupWindow.HINT_COMPONENT_CLIENT_ID];
387 AdfPage.PAGE.__perfTimings(false, false, true, "popup show called: type=" + hints[AdfDhtmlPopupWindow.HINT_TYPE] + ", id=" + componentId); //changed
388
389 // save the component id of the popup
390 this._componentId = componentId;
391
392 var alignElement = hints[AdfDhtmlPopupWindow.HINT_ALIGN_ELEMENT];
393 this.setAlignElement(alignElement);
394
395 var align = hints[AdfRichPopup.HINT_ALIGN];
396 var restoreImmediate = hints[AdfDhtmlPopupWindow.HINT_RESTORE_IMMEDIATE];
397
398 this._closingAnimationHints = new Object;
399 this._closingAnimationHints[AdfDhtmlPopupHints.CLOSE_TRANSITION] = hints[AdfDhtmlPopupHints.CLOSE_TRANSITION];
400
401 // flag indicates the opening hander has not been called yet. This should only happen once.
402 this._isOpening = true;
403
404 // Popup opened event handler. The callback will be fired after the
405 // popup becomes visible to allow overriding focus.
406 this._openedHandler = hints[AdfDhtmlPopupWindow.HINT_OPENED_HANDLER];
407 this._openedHandlerParam = hints[AdfDhtmlPopupWindow.HINT_OPENED_HANDLER_PARAM];
408
409 var mousePosition = hints[AdfDhtmlPopupWindow.HINT_MOUSEPOSITION];
410 var autoDismiss = hints[AdfDhtmlPopupWindow.HINT_AUTODISMISS];
411
412 var screenCenterPosition = hints[AdfDhtmlPopupWindow.HINT_POSITION_CENTER];
413
414 // popup hint redirection for backwards compatibility
415 if (autoDismiss === true)
416 hints[AdfDhtmlPopupWindow.HINT_AUTODISMISS] = AdfDhtmlPopupWindow.HINT_AUTODISMISS_ALWAYS;
417
418 //AdfDhtmlPopupMenu subclass handles its own escape logic when not in screen reader mode
419 var closeOnEscape = hints[AdfDhtmlPopupWindow.HINT_CLOSE_ON_ESCAPE];
420 if (closeOnEscape == null)
421 {
422 // default is true
423 closeOnEscape = true;
424 }
425 this._closeOnEscape = closeOnEscape;
426
427 // client id of the launching source. if provided, included in escape handling
428 // also used by the AdfAutoDismissalManager for inline with HINT_AUTODISMISS_ALWAYS
429 this._launchSourceId = hints[AdfRichPopup.HINT_LAUNCH_ID];
430
431 // look for a max-width popup hint
432 var maxWidth = hints[AdfDhtmlPopupWindow.HINT_MAX_WIDTH];
433 if (maxWidth)
434 {
435 // if it has a value make sure it's numeric
436 AdfAssert.assertNumeric(maxWidth, "AdfDhtmlPopupWindow.HINT_MAX_WIDTH");
437 this._maxWidth = maxWidth;
438 }
439
440 var page = AdfPage.PAGE;
441 var component = page.findComponent(componentId);
442 AdfAssert.assert(component!=null, "Cannot show a popup element without a valid component");
443 this.getElement().id = AdfRichUIPeer.createSubId(componentId, AdfDhtmlPopupWindow.__POPUP_CONTAINER_ID);
444
445 // focus hint determines if by default focus should be established when the popup opens.
446 // for certain popup subclasses like the AdfDhtmNoteWindowPopupSelector, focus is turned off.
447 var focusHint = hints[AdfDhtmlPopupWindow.HINT_FOCUS];
448 if (focusHint == null)
449 focusHint = true; // default is true
450
451 // popups that are not notewindow get focus by default
452 // however, if we are in screen reader mode, we must establish focus in the popup
453 // since it will be the only thing visible
454 var hasSpecialRenderingForScreenReader = this.hasSpecialRenderingForScreenReader();
455 if (hints[AdfDhtmlPopupWindow.HINT_TYPE] != AdfDhtmlPopupWindow.HINT_TYPE_DIALOG)
456 focusHint = focusHint || hasSpecialRenderingForScreenReader;
457
458 // if restore immediate, the focus path before PPR is restored. This means that
459 // restoreImmediate only applies to a PPR.
460 if (restoreImmediate)
461 focusHint = false;
462
463 if(focusHint)
464 {
465 // If we are going to move the focus to the popup, first stash away
466 // the currently focused element's id so that we can restore the focus when
467 // we hide the popup.
468 var activeDomElement = page.getActiveDomElement();
469 if(activeDomElement)
470 {
471 var restoreId = activeDomElement.id;
472
473 // If the activeDomElement doesn't have an id, use the component id instead.
474 if(!restoreId)
475 {
476 restoreId = page.getActiveComponentId();
477
478 // If there's also no active component id, assign a unique id to the element.
479 // If this element gets PPRed, then we will not be able to restore focus to this element.
480 if (!restoreId)
481 {
482 restoreId = activeDomElement.id = (new Date()).getTime();
483 }
484 }
485
486 this._restoreFocusId = restoreId;
487 AdfLogger.LOGGER.fine("In AdfDhtmlPopupWindow#show(), _restoreFocusId set to " + restoreId);
488 }
489 }
490
491 // hang on to the focus hint for use by the FocusOnFirstElement
492 this.SetFocusOnOpen(focusHint);
493
494 var content = hints[AdfDhtmlPopupWindow.HINT_CONTENT];
495 if (content)
496 {
497 // Set the maximum-width of the popup. If we do not do this, and we have wide
498 // content in our popup, it will try to be 100% of the browser window's width.
499 // We can't set the table's width, because maxWidth doesn't work correctly on
500 // tables.
501 if (this._maxWidth)
502 {
503 content.style.maxWidth = this._maxWidth +"px";
504 }
505
506 // change the content to the wrapper if in screen reader mode
507 // the "begin" and "end" tags are added to the content
508 content = hints[AdfDhtmlPopupWindow.HINT_CONTENT] =
509 this.WrapContent(hints, content, hasSpecialRenderingForScreenReader);
510 this.setContent(content);
511 }
512
513 // Set/remove these attributes for all screen reader mode popups, regardless of
514 // hasSpecialRenderingForScreenReader being true or false
515 if (AdfPage.PAGE.isScreenReaderMode())
516 this.setAccessibleAttrs(hints);
517
518 var animateHint = hints[AdfDhtmlPopupWindow.HINT_ANIMATE];
519
520 // Initialize the duration of animation.
521 // animate = 'false' property will turn off the animation
522 if(page.isAnimationEnabled() && animateHint != AdfRichPopup.ANIMATE_FALSE)
523 {
524
525 var animProperty = this.GetAnimationDurationSkinProperty(hints);
526 if (animProperty)
527 {
528 //if animationDuration is zero, don't do animation
529 var animationDuration = parseInt(page.getLookAndFeel().getSkinProperty(animProperty));
530 if (animationDuration > 0)
531 {
532 animProperty = this.GetAnimateSkinProperty();
533 var animatePropertyValue = animProperty ? page.getLookAndFeel().getSkinProperty(animProperty) : null;
534
535 // If animate property value is set to "true", but skinning value is set to "false". We still want to animate
536 if (animatePropertyValue != AdfRichPopup.ANIMATE_FALSE || animateHint == AdfRichPopup.ANIMATE_TRUE)
537 {
538 this._animationDuration = animationDuration;
539 this._closingAnimationHints[AdfDhtmlPopupWindow.HINT_ANIMATE] = hints[AdfDhtmlPopupWindow.HINT_ANIMATE];
540 }
541 }
542 }
543 }
544
545 this.setCloseHandler(hints[AdfDhtmlPopupWindow.HINT_CLOSE_HANDLER]);
546 this.setCloseHandlerParam(hints[AdfDhtmlPopupWindow.HINT_CLOSE_HANDLER_PARAM]);
547
548 // if display on timeout returns true, the window will be shown after a
549 // timer thread has expired.
550 // we will not actually show the content on the screen until the timeout. This is
551 // so that all positioning and sizing is fully complete.
552 this.getElement().style.visibility = "hidden";
553
554 // registers the floating element with the position manager.
555 if(mousePosition)
556 {
557 // align defaults to after-start and by align mousePosition
558 this._positionAtMousePointer(mousePosition);
559 }
560 else if (screenCenterPosition)
561 {
562 this._positionAtScreenCenter();
563 }
564 else if (align)
565 {
566 // registers with position manager by align element or align position
567 this.Position(align);
568 }
569
570 // Let ATs know that we are launching a popup
571 this._announce("af_popup.STATUS_ENTERING_POPUP");
572
573 this.DoShow(hints);
574
575 var isFullScreen = hints[AdfDhtmlPopupHints.POPUP_FULLSCREEN] ;
576 var isFullHeightPartialScreen = hints[AdfDhtmlPopupHints.POPUP_FULLHEIGHTPARTIALSCREEN];
577 var isPartialScreen = hints[AdfDhtmlPopupHints.POPUP_PARTIALSCREEN];
578 if(isPartialScreen || isFullHeightPartialScreen)
579 this._fixBodyPosition(hints);
580
581 if( isFullScreen || isFullHeightPartialScreen)
582 {
583 var panelWindow = this._getFirstPanelWindow(component,hints);
584 if(panelWindow != null)
585 if(isFullHeightPartialScreen)
586 //setTimeout( function(){ panelWindow.setContentHeight(AdfPage.PAGE.getDomWindow().innerHeight);},150);
587 AdfPage.PAGE.scheduleTimer(this, AdfDhtmlPopupWindow._setContentHeight,{'panelWindow': panelWindow}, 150, {'isSynchronized' : true});
588 if(isFullScreen)
589 //panelWindow.setContentHeight(AdfPage.PAGE.getDomWindow().innerHeight);
590 AdfDhtmlPopupWindow._setContentHeight({'panelWindow': panelWindow});
591 }
592
593 // register with the dismissal manager
594 page.getAutoDismissalManager().addBehavior(component, hints);
595
596 var domWindow = page.getDomWindow();
597
598 // key used to capture the last active dom element as a page property
599 // @see #cancel
600 var activeDomElementIdKey = AdfDhtmlPopupWindow._createFocusPagePropertyKey(componentId);
601
602 // if we are restoring the popup immediate, we have to display the popup before we restore
603 // the focus path. So, this only applies to PPR restoration.
604 if (!restoreImmediate)
605 {
606 // invokes showing the popup in a seperate thread
607 this._sizeTimeout = page.scheduleTimer(this, this.createCallback(this._size), hints, 1, {'isSynchronized' : true});
608 }
609 else
610 {
611 // Shows the popup immediately. set the timeout to a non-null. This is used to
612 // identify hide has been called before the normal window time out of 50 ms.
613 this._sizeTimeout = 0;
614 this._size(hints);
615
616 // the focus path logic in a PPR (AdfDhtmlPage#_getFocusPath) is based on the
617 // dom hierarchy versus the component hiearchy. If we have been canceled due
618 // to a dom replacement, we need to handle restoring the focus path.
619 // We capture the last active dom element id as a page property when the popup
620 // is dismissed due to a dom replacement. Note that this only applies to a PPR
621 // that knocks down the popup. If there is a last active component id and it is
622 // located under the popup and is focusable, restore focus within the popup.
623 // see #cancel & #hide
624
625 var activeDomElementId = page.getPageProperty(activeDomElementIdKey);
626 if (activeDomElementId)
627 {
628 var restoreFocusElement = AdfAgent.AGENT.getElementById(activeDomElementId);
629 if (restoreFocusElement &&
630 AdfDomUtils.isAncestor(content, restoreFocusElement) &&
631 AdfFocusUtils.isFocusable(restoreFocusElement))
632 AdfFocusUtils.focusElement(restoreFocusElement);
633 }
634 else if (AdfDhtmlDialogManager.getInstance().getActiveDialog() == this)
635 {
636 this.FocusOnFirstElement(true);
637 }
638 }
639
640 // null out the key that holds the last active dom element
641 // @see #cancel
642 page.setPageProperty(activeDomElementIdKey, null);
643 var rootElement = this.getElement();
644 rootElement.setAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO, this._getPopupExpandoId(hints));
645}
646
647AdfDhtmlPopupWindow._setContentHeight = function(state)
648{
649 debugger;
650 state['panelWindow'].setContentHeight(AdfPage.PAGE.getDomWindow().innerHeight);
651}
652
653/**
654 * This getter exposes a popup hint that captures the popups's clientId.
655 * It is a quick way to find the owning popup component.
656 *
657 * @return {String} the clientId of the outer popup component
658 */
659AdfDhtmlPopupWindow.prototype.getPopupClientId = function()
660{
661 return this._componentId;
662}
663
664/*
665 * Invokes the opened handler callback. The callback and parameter list is passed
666 * by the invoking peer using the "HINT_CLOSE_HANDLER" and "HINT_CLOSE_HANDLER_PARAM"
667 * hint parameters.
668 *
669 * @return {void}
670 */
671AdfDhtmlPopupWindow.prototype.OpenedPopup = function(hints)
672{
673 // only run this once
674 if (!this._isOpening)
675 return;
676 delete this._isOpening;
677
678 // Queue open popup announcements after the popup is opened. Before these were getting lost on
679 // firefox when announced earlier, even when set to role alert and assertive.
680 this.announceOpeningViaLiveRegion(hints);
681
682 // register a listeners on the document
683 this._registerEventHandlers(hints);
684
685 // if the callback function doesn't exist exit
686 if(!(this._openedHandler))
687 {
688
689 // bug 16621214 - menus don't use open handlers
690 this.FocusOnFirstElement();
691
692
693 // Since we are bailing out, note perf timings.
694 // If we did not bail out, second call to perfTimings would include
695 // the handler call expense as well
696 AdfPage.PAGE.__perfTimings(false, false, true, "popup opened no handler present: id=" + this._componentId); // changed
697 return;
698 }
699
700 var domElement = this.getContent();
701 this.RestoreScrollValues(domElement);
702
703 // set defaut focus to the first tabable element
704 this.FocusOnFirstElement();
705
706 // invoke the callback passing the parameters
707 this._openedHandler(this._openedHandlerParam);
708
709 // remove references to the opended callback since this should only
710 // be invoke once
711 delete this._openedHandler;
712 delete this._openedHandlerParam;
713
714 AdfPage.PAGE.__perfTimings(false, false, true, "popup opened handler present: id=" + this._componentId) // changed
715}
716
717/**
718 * Wraps the content of the popup window.
719 * @return the wrapped content
720 */
721AdfDhtmlPopupWindow.prototype.WrapContent = function(hints, content, hasSpecialRenderingForScreenReader)
722{
723 var page = AdfPage.PAGE;
724 // Wrap the popup's content in a table because IE7 can't render popups that are position:relative
725 // when the renderingDirection=rtl. By using a table we can work around this.
726 var doc = page.getDomDocument();
727 var table = doc.createElement("table");
728
729 // generate a unique id for the dynamic nodes that will hold the popup's content when shown
730 // the id is used to register a resizeListener @See #_registerEventHandlers
731 this._resizeAnchorId = table.id = AdfRichUIPeer.CreateSubId(this._componentId, "" + (new Date()).getTime());
732 AdfAgent.AGENT.elementsAdded(table);
733
734 table.cellPadding = table.cellSpacing = 0;
735 table.insertRow(0).insertCell(0).appendChild(content);
736 table.style.position = "relative";
737 table.role = "presentation";
738
739 if (hasSpecialRenderingForScreenReader)
740 {
741 var laf = page.getLookAndFeel();
742 var agent = AdfAgent.AGENT;
743 var wrapper = doc.createElement("div");
744 var closeOnEscape = this._closeOnEscape;
745
746 // Create the start boundary marker that we use to wrap the popup content in screen reader mode.
747 // Having the contents identified by a header makes the popup easily identifiable and allows a
748 // JAWS user to easily jump to the start of the popup using the header quick key. Note that this
749 // can also come in handy considering that JAWS is inconsistent about following the focus to the
750 // popup when we launch.
751 //
752 // In theory the use of h1 for the start boundary means that all nested headers inside of the
753 // popup content should be demoted by one level. However, the gains that we get by using headers
754 // here far outweight these concerns, so we're gonna to roll with this for now.
755 var startElement = doc.createElement("h1");
756 startElement.id = AdfRichUIPeer.createSubId(this._componentId, AdfDhtmlPopupWindow.__POPUP_TITLE_ID);
757
758 // Create the end text that describes how to exit the popup, like "Press escape to exit popup."
759 // Note that base on feedback from Don Mauck, our accessibility evangelist, we have updated this
760 // from an H1 element to a DIV element, as he found it odd to have a header at the end with no
761 // content. Also, he specified the new descriptive text about pressing escape.
762 var endElement = doc.createElement("div");
763
764 // Provide some basic formatting so sighted users don't freak out, like a smaller font size for
765 // the h1 tag and a little margin before the closing text.
766 startElement.style.cssText = "font-size:small";
767 endElement.style.cssText = "margin-top: 6px";
768
769 var startTitle;
770 var isMenu = hints[AdfDhtmlPopupWindow.HINT_TYPE] == AdfDhtmlPopupWindow.HINT_TYPE_MENU;
771 var isPanelDrawer = hints[AdfDhtmlPopupWindow.HINT_TYPE] == AdfDhtmlPopupWindow.HINT_TYPE_LAYER;
772 if (isMenu || isPanelDrawer)
773 {
774 // Custom top text like "File Menu"
775 var hintTitle = hints[AdfDhtmlPopupWindow.HINT_TITLE];
776 var popupTitle = (hintTitle == null) ? "" : hintTitle;
777 var keyName = isMenu ? "af_popup.TIP_START_OF_MENU" :
778 "af_popup.TIP_START_OF_TITLED_POPUP";
779 startTitle = laf.getTranslatedString(keyName, popupTitle);
780 }
781 else
782 {
783 // Generic top text of "Popup"
784 startTitle = laf.getTranslatedString("af_popup.TIP_START_OF_POPUP");
785 }
786
787 var endTitle;
788 if (isMenu)
789 {
790 // Bottom text like "Press escape to exit the menu."
791 endTitle = (closeOnEscape ? laf.getTranslatedString("af_popup.TIP_END_OF_MENU") :
792 laf.getTranslatedString("af_popup.TIP_END_OF_MENU_DISABLE_CLOSE_ON_ESCAPE"));
793 }
794 else
795 {
796 // Bottom text like "Press escape to exit the popup."
797 endTitle = (closeOnEscape ? laf.getTranslatedString("af_popup.TIP_END_OF_POPUP") :
798 laf.getTranslatedString("af_popup.TIP_END_OF_POPUP_DISABLE_CLOSE_ON_ESCAPE"));
799 }
800
801 agent.setTextContent(startElement, startTitle);
802 agent.setTextContent(endElement, endTitle);
803
804 wrapper.appendChild(startElement);
805 wrapper.appendChild(table);
806 wrapper.appendChild(endElement);
807 return wrapper;
808 }
809 else
810 {
811 return table;
812 }
813}
814
815/**
816 * Registers listeners with the document.
817 */
818AdfDhtmlPopupWindow.prototype._registerEventHandlers = function(hints)
819{
820 var page = AdfPage.PAGE;
821 var agent = AdfAgent.AGENT;
822 var doc = page.getDomDocument();
823 var popupContainer = this.getElement();
824
825 if (!hints || !hints[AdfDhtmlPopupWindow.COMPONENT_EVENTS_ENABLED])
826 {
827 // listen for key up that bubbles up to the document
828 var keyUpCallback = this._keyUpCallback = this.createCallback(this._handleKeyUp);
829 agent.addBubbleEventListener(doc, "keyup", keyUpCallback);
830
831 // listen for key down events that bubble up to the popup-container
832 var keyDownCallback = this._keyDownCallback = this.createCallback(this._handleKeyDown);
833 agent.addBubbleEventListener(popupContainer, "keydown", keyDownCallback);
834 }
835
836 // insist the element has an id. This id is used to register resize notification listeners
837 // that will autosize the popup when the dom has changed.
838 var element = this.getElement().firstChild;
839
840 var resizeCallback = this._resizeCallback = this.createCallback(this._handleResize);
841 agent.addResizeListener(this._resizeAnchorId, resizeCallback);
842 if (hints[AdfDhtmlPopupHints.POPUP_FULLSCREEN])
843 {
844 this._scrollCallback = this.createCallback(this._onScroll);
845 AdfPage.PAGE.getDomDocument().addEventListener('scroll', this._scrollCallback);
846 }
847}
848/**
849 * If popup is set to be displayed in full screen mode, it method will readjust height of popup on
850 * scroll event triggered on document.
851 * This is required on touch devices that show/hide the nav bar dynamically in
852 * response to the scroll. This handler will ensure that the popup adjusts to
853 * accommodate for the presence or absence of the nav bar during the scroll.
854 */
855AdfDhtmlPopupWindow.prototype._onScroll = function()
856{
857 var newHeight = AdfPage.PAGE.getDomWindow().innerHeight;
858 var element = this.getElement();
859 var currentHeight = parseInt(element.style.maxHeight);
860 //var delta = Math.abs(currentHeight - newHeight);
861 //if(delta == 0 || delta > AdfDhtmlPopupHints.SCROLL_RESIZE_THRESHOLD )
862 // return;
863 element.style.maxHeight = newHeight +"px";
864 var content = this.getContent();
865 this.DoResizeNotifyDom(content);
866
867}
868
869/**
870 * Utility method to query popup id.
871 */
872AdfDhtmlPopupWindow.prototype._getPopupExpandoId = function(hints)
873{
874 if (hints[AdfDhtmlPopupHints.SURROGATE_CLIENT_ID])
875 {
876 // pannel draw uses a surrogate DIV to mark where the popup was defined before it is reparented to the zorder container
877 // when open versus using subids and leaving the root popup dom in place in the document.
878 return hints[AdfDhtmlPopupHints.SURROGATE_CLIENT_ID];
879 }
880 else
881 {
882 // bug 15931488
883 // assigns the popupId to the root element created on the client to hold the
884 // popups content. This is used by the auto dismissal manager to associate dom
885 // dynamically created on the client with the target popup.
886 return this._componentId;
887 }
888}
889
890/**
891 * If popup is set to be dispalyed in full screen mode by setting property
892 * AdfDhtmlPopupHints.POPUP_FULLSCREEN, 'displaye:none' will be added on all sibling of 'ZOrderLayerContainer'
893 */
894AdfDhtmlPopupWindow.prototype._hideZOrderLayerContainerSiblings = function(hints)
895{
896 var popupRoot = document.getElementById(AdfDhtmlZOrderManager.LAYERCONTAINER);
897 var siblings = popupRoot.parentElement.childNodes;
898 var agent = AdfAgent.AGENT;
899 var popupId = this._getPopupExpandoId(hints);
900 // In case of nested popups, first popup changes display values and restores it back on closure.
901 for (var i = 0;i < siblings.length;i++)
902 {
903 if (siblings[i] != popupRoot)
904 {
905 // In case popup is opened from another popup , no changes are needed
906 if(siblings[i].hasAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO))
907 break;
908 if (siblings[i].style.display)
909 agent.setAttribute(siblings[i], AdfDhtmlPopupWindow._FULL_SCREEN_EXPANDO, siblings[i].style.display);
910 siblings[i].style.display = 'none';
911 agent.setAttribute(siblings[i],AdfDhtmlPopupWindow.__POPUPID_EXPANDO, popupId);
912 }
913 }
914}
915/**
916 * Restores display value of all sibling of 'ZOrderLayerContainer'.
917 */
918AdfDhtmlPopupWindow.prototype._restoreZOrderLayerContainerSiblings = function()
919{
920 var popupRoot = document.getElementById(AdfDhtmlZOrderManager.LAYERCONTAINER);
921 var siblings = popupRoot.parentElement.childNodes;
922 var agent = AdfAgent.AGENT;
923 var popupId = this.getElement().getAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO)
924 for (var i = 0;i < siblings.length;i++)
925 {
926 if (siblings[i] != popupRoot)
927 {
928 // In case, popup opened by another popup closes, ignore resetting values.
929 if(siblings[i].getAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO) != popupId)
930 {
931 break;
932 }
933 var restoredValue = agent.getAttribute(siblings[i], AdfDhtmlPopupWindow._FULL_SCREEN_EXPANDO);
934 if (restoredValue)
935 siblings[i].style.display = restoredValue;
936 else
937 siblings[i].style.display = '';
938 siblings[i].removeAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO);
939 }
940 }
941}
942
943/**
944 * If popup is set to be dispalyed in partial screen mode by setting property
945 * AdfDhtmlPopupHints.POPUP_PARTIALSCREEN, 'position:fixed' will be added on body
946 */
947AdfDhtmlPopupWindow.prototype._fixBodyPosition = function(hints)
948{
949 var bodyElement = document.body;
950 var agent = AdfAgent.AGENT;
951
952 if (bodyElement.hasAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO))
953 return;
954
955 if (bodyElement.style.position)
956 agent.setAttribute(siblings[i], AdfDhtmlPopupWindow._FULL_SCREEN_EXPANDO, bodyElement.style.position);
957
958 var scrolledHeight = bodyElement.scrollTop;
959 agent.setAttribute(bodyElement, AdfDhtmlPopupWindow._SCROLL_POSITION, scrolledHeight);
960 bodyElement.style.position = "fixed";
961 bodyElement.style.top = scrolledHeight * -1 + "px";
962
963 agent.setAttribute(bodyElement, AdfDhtmlPopupWindow.__POPUPID_EXPANDO, this._getPopupExpandoId(hints));
964}
965
966/**
967 * Restores position value of body
968 */
969AdfDhtmlPopupWindow.prototype._undoFixBodyPosition = function()
970{
971 var bodyElement = document.body;
972 var agent = AdfAgent.AGENT;
973 var popupId = this.getElement().getAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO)
974
975 // In case, popup opened by another popup closes, ignore resetting values.
976 if(bodyElement.getAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO) != popupId)
977 return;
978
979 var restoredValue = agent.getAttribute(bodyElement, AdfDhtmlPopupWindow._FULL_SCREEN_EXPANDO);
980 if (restoredValue)
981 {
982 bodyElement.style.position = restoredValue;
983 bodyElement.removeAttribute(AdfDhtmlPopupWindow._FULL_SCREEN_EXPANDO);
984 }
985 else
986 bodyElement.style.position = '';
987
988 bodyElement.scrollTop = agent.getAttribute(bodyElement, AdfDhtmlPopupWindow._SCROLL_POSITION);
989 bodyElement.style.top = "0px";
990 bodyElement.removeAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO);
991 bodyElement.removeAttribute(AdfDhtmlPopupWindow._SCROLL_POSITION);
992}
993
994
995/**
996 * Inspects the content of the popup and returns the peer if the first child
997 * is a AdfRichPanelWindow component. If hints are given and the HINT_TYPE_DIALOG
998 * is not given, the content is not inspected.
999 *
1000 * @param {AdfUIComponent} component root popup component
1001 * @param {Object:null} hints popup hints
1002 * @return {AdfDhtmlPanelWindow:null} PanelWindow if the first child is a AdfRichPanelWindow
1003 */
1004AdfDhtmlPopupWindow.prototype._getFirstPanelWindow = function(component, hints)
1005{
1006 var panelWindowPeer = null;
1007 var hintType = hints[AdfDhtmlPopupWindow.HINT_TYPE];
1008
1009 // Dig into the popup content to look for panel window peer if the type is not known or
1010 // if it is explicitly set to dialogWindow
1011 if(hintType == null || (hintType == AdfDhtmlPopupWindow.HINT_TYPE_DIALOG))
1012 {
1013 component.visitChildren(this._visitChildrenForPanelWindow, this, true);
1014 if(this._firstChildPanelWindow)
1015 {
1016 panelWindowPeer = this._firstChildPanelWindow;
1017 delete this._firstChildPanelWindow;
1018 }
1019 }
1020 return panelWindowPeer;
1021}
1022
1023/**
1024 * Callback used by _getFirstPanelWindow to visitChildren on the component.
1025 * The callback looks at the first child to see if it is a AdfRichPanelWindow.
1026 * If the first child is a PanelWindow, it is assigned to a temporary private
1027 * variable. If the child is an AdfRichRegion, the search continues
1028 *
1029 * @param {AdfUIComponent} component child component visited
1030 * @return {Number} returns a value of 2 if traversal has finished, 0 if traversal needs to continue
1031 */
1032AdfDhtmlPopupWindow.prototype._visitChildrenForPanelWindow = function(component)
1033{
1034 var domWindow = AdfPage.PAGE.getDomWindow();
1035
1036 //if the first component found is a region then we want to drill down another layer
1037 if (domWindow.AdfRichRegion && component instanceof AdfRichRegion)
1038 return 0;
1039
1040 if(domWindow.AdfRichPanelWindow && component instanceof AdfRichPanelWindow)
1041 this._firstChildPanelWindow = component;
1042
1043 return 2;
1044}
1045
1046/**
1047 * Callback invoked every time the size of the floating element changes.
1048 * @see AdfAgent#addResizeListener
1049 */
1050AdfDhtmlPopupWindow.prototype._handleResize = function()
1051{
1052 this.autoSize();
1053 var content = this.getContent();
1054 this.DoResizeNotifyDom(content);
1055 var component = page.findComponent(this._componentId);
1056 var isFullScreen = component.getProperty(AdfDhtmlPopupHints.POPUP_FULLSCREEN);
1057 // on full screen mode reset maxHeight based on current window max height
1058 if(isFullScreen)
1059 content.style.maxHeight= AdfPage.PAGE.getDomWindow().innerHeight +"px"
1060
1061 // browser window resize event is not consistently queued for all browsers.
1062 // validate the position when detecting a content resize.
1063 if(this._positionManagerIndex != null)
1064 AdfPage.PAGE.getPositionManager().validatePosition(this._positionManagerIndex);
1065}
1066
1067/**
1068 * Unregisters listeners registered with the document.
1069 */
1070AdfDhtmlPopupWindow.prototype._unregisterEventHandlers = function()
1071{
1072
1073 var page = AdfPage.PAGE;
1074 var doc = page.getDomDocument();
1075 var agent = AdfAgent.AGENT;
1076 var popupContainer = this.getElement();
1077
1078 var keyUpCallback = this._keyUpCallback;
1079 if (keyUpCallback)
1080 {
1081 agent.removeBubbleEventListener(doc, "keyup", keyUpCallback);
1082 delete this._keyUpCallback;
1083 }
1084
1085 var keyDownCallback = this._keyDownCallback;
1086 if (keyDownCallback)
1087 {
1088 agent.removeBubbleEventListener(popupContainer, "keydown", keyDownCallback);
1089 delete this._keyDownCallback;
1090 }
1091
1092 var resizeCallback = this._resizeCallback;
1093 if (resizeCallback)
1094 {
1095 var element = this.getElement().firstChild;
1096 agent.removeResizeListener(this._resizeAnchorId, resizeCallback);
1097 delete this._resizeAnchorId;
1098 delete this._resizeCallback;
1099 }
1100 if(this._scrollCallback)
1101 {
1102 AdfPage.PAGE.getDomDocument().removeEventListener('scroll', this._scrollCallback);
1103 }
1104}
1105
1106/**
1107 * Hides this popup window.
1108 * @see #show
1109 */
1110AdfDhtmlPopupWindow.prototype.hide = function()
1111{
1112 // synchronize on this method using a private semaphore. If a popup is launched
1113 // using the focus trigger type, the return to focus logic will cause a race condition
1114 // unless blocked.
1115 if (this._isHiding)
1116 {
1117 return;
1118 }
1119 this._isHiding = true;
1120 AdfPage.PAGE.__perfTimings(false, false, true, "popup hide called: id=" + this._componentId);
1121
1122 // The popup is being dismissed even while the open animation is in progress.
1123 // Stop the animation and bring to opened state before proceeding further.
1124 if (this._animator && this._isOpening)
1125 {
1126 this._animator.stop();
1127 var wrapper = this.getElement().parentNode;
1128 AdfDhtmlPopupWindow.OpenAnimationComplete({wrapper:wrapper, myself:this});
1129 }
1130
1131 // If animation is enabled, then close popup with animation
1132 if (this._closingAnimationHints[AdfDhtmlPopupWindow.HINT_ANIMATE] != AdfRichPopup.ANIMATE_FALSE && this._animationDuration > 0)
1133 {
1134 this.AnimateClosing(this._closingAnimationHints);
1135 }
1136 else
1137 {
1138 // If animation is not enabled or its not of AdfRichPopup type, just hide the popup.
1139 this.ClosedPopup();
1140 }
1141
1142}
1143
1144/**
1145 * Hides this popup window from dom.
1146 * @see #show
1147 */
1148AdfDhtmlPopupWindow.prototype.ClosedPopup = function()
1149{
1150 // Let ATs know that we are leaving a popup
1151 this._announce("af_popup.STATUS_EXITING_POPUP");
1152
1153 var page = AdfPage.PAGE;
1154 var domElement = this.getContent();
1155
1156 // Set/remove these attributes for all screen reader mode popups, regardless of
1157 // hasSpecialRenderingForScreenReader being true or false
1158 if (AdfPage.PAGE.isScreenReaderMode())
1159 this.removeAccessibleAttrs();
1160
1161 this.SaveScrollValues(domElement);
1162
1163 // Must remove/unregister from the auto dismiss manager before restoring focus
1164 // because changing focus can cause the dismissal to hide the popup.
1165 // Depending on the browser, the dismissal hide (call to same method) might be
1166 // invoked before this method is completed causing a synchronization issue with the
1167 // AdfDhtmlZOrderManager (removing twice).
1168 var component = page.findComponent(this._componentId);
1169 var popupId = AdfDhtmlPopupWindow._getInternalPopupId(this, component);
1170 page.getAutoDismissalManager().removeBehavior(component, popupId);
1171
1172 // If the focus is currently in the popup, we need to restore the focus
1173 // to its previous location before we hide the popup. Note that before
1174 // restoring the focus, we also check to see whether there is a focus
1175 // change pending. This will be true if the app (or peer) attempts
1176 // to move the focus just before dismissing the popup. When this happens,
1177 // we want to honor the app/peer's focus change, so we avoid restoring the
1178 // focus.
1179
1180 // if canceled do to a dom replacement the the focus will be restored to the
1181 // last active dom element within the popup
1182 var isRestoring = this._isRestoring(component);
1183
1184 if (!isRestoring &&
1185 (page.getActiveDomElement() == null || AdfFocusUtils.containsFocus(this.getElement())) &&
1186 !AdfFocusUtils.isFocusChangePending())
1187 {
1188 // try/catch block added for bug 14676053 - return focus to flash object element
1189 try
1190 {
1191 this._restoreFocus();
1192 }
1193 catch (e)
1194 {
1195 AdfLogger.LOGGER.logErrorAsWarning(e, "restoring popup focus");
1196 }
1197 }
1198
1199 // unregister event handlers on the document and resizeListener on the content wraper
1200 //unregistering before calling closeHandler inorder to avoid any resize event triggers
1201 //while doing dom manipulation.
1202 this._unregisterEventHandlers();
1203 var isFullScreen = component.getProperty(AdfDhtmlPopupHints.POPUP_FULLSCREEN);
1204 if(isFullScreen)
1205 {
1206 this._restoreZOrderLayerContainerSiblings();
1207 }
1208
1209 var isFullHeightPartialScreenPopup = component.getProperty(AdfDhtmlPopupHints.POPUP_FULLHEIGHTPARTIALSCREEN);
1210 var isPartialScreenPopup = component.getProperty(AdfDhtmlPopupHints.POPUP_PARTIALSCREEN);
1211 if(isPartialScreenPopup || isFullHeightPartialScreenPopup)
1212 {
1213 this._undoFixBodyPosition();
1214 }
1215
1216 var closeHandler = this._closeHandler;
1217 if (closeHandler)
1218 {
1219 delete this._closeHandler;
1220 closeHandler(this._closeHandlerParam);
1221 delete this._closeHandlerParam;
1222 }
1223
1224 this.destroy();
1225
1226 delete this._isHiding;
1227
1228 page.__perfTimings(false, false, true, "popup hidden: id=" + this._componentId);
1229}
1230
1231/**
1232 * @param {AdfDhtmlPopupWindow} popupWindow target popup controller to find id associating
1233 * with the component
1234 * @param {AdfUIComponent} component associated with this instance of the popup controller
1235 * @return {string} internal popupId or null
1236 */
1237AdfDhtmlPopupWindow._getInternalPopupId = function(popupWindow, component)
1238{
1239 if (component)
1240 {
1241 var peer = component.getPeer();
1242 if (peer)
1243 {
1244 var popupList = peer.getAllPopups(component);
1245 if (popupList)
1246 {
1247 for (popupId in popupList)
1248 {
1249 if (popupList[popupId] === popupWindow)
1250 return popupId;
1251 }
1252 }
1253 }
1254 }
1255
1256 return null;
1257}
1258
1259/**
1260 * Used to determine if the popup should restore focus when closed. If the popup is
1261 * canceled and needs to be restored, we don't need to restore focus prior to when the
1262 * popup was open.
1263 *
1264 * @param {AdfRichPopup} component popup component
1265 * @return {boolean} true if the component is a {@link AdfRichPopup}, property autoCancel
1266 * is disabled, and the popup was canceled due to a dom replacement.
1267 */
1268AdfDhtmlPopupWindow.prototype._isRestoring = function(component)
1269{
1270 var isRestoring = false;
1271 if (component instanceof AdfRichPopup &&
1272 component.getAutoCancel() == AdfRichPopup.AUTO_CANCEL_DISABLED)
1273 {
1274 var key = AdfDhtmlPopupWindow._createFocusPagePropertyKey(component.getClientId());
1275 var activeDomElementId = page.getPageProperty(key);
1276 isRestoring = (activeDomElementId ? true : false);
1277 }
1278 return isRestoring;
1279}
1280
1281/**
1282 * Dismisses the floating element indicating an awkward state or unexpected state.
1283 * @param {boolean} replaceDom optional flag that indicates the popup was canceled
1284 * due to a dom replacement of its markup.
1285 * @override
1286 */
1287AdfDhtmlPopupWindow.prototype.cancel = function(replaceDom)
1288{
1289 // locate the closed handler parameter so that we can add
1290 // and expando property on the content dom to indicate the
1291 // popup was hidden unexpectedly
1292
1293 var agent = AdfAgent.AGENT;
1294 var closeHandlerParam = this._closeHandlerParam;
1295 var contentDom = closeHandlerParam.contentDom;
1296 agent.setExpandoProperty(contentDom, AdfDhtmlPopupWindow.__CANCELED_EXPANDO, true);
1297 agent.setExpandoProperty(contentDom, AdfDhtmlPopupWindow.__REPLACEDOM_EXPANDO, (replaceDom ? true : false));
1298
1299 // Don't perform animation on canceling the popup. Animation is to be performed only on hide
1300 if (this._closingAnimationHints && replaceDom)
1301 {
1302 this._closingAnimationHints[AdfDhtmlPopupWindow.HINT_ANIMATE] = AdfRichPopup.ANIMATE_FALSE;
1303 }
1304
1305 if (this._isHiding)
1306 {
1307 if (this._animator && replaceDom)
1308 {
1309 this._animator.stop();
1310 var wrapper = this.getElement().parentNode;
1311 AdfDhtmlPopupWindow.CloseAnimationComplete({wrapper:wrapper, myself:this});
1312 }
1313 return;
1314 }
1315
1316 // If the focus is currently in the popup, we need to restore the focus
1317 // to its previous location before we hide the popup. See #hide
1318 if (replaceDom &&
1319 AdfFocusUtils.containsFocus(this.getElement()))
1320 {
1321 var poppupId = this.getPopupClientId();
1322 var key = AdfDhtmlPopupWindow._createFocusPagePropertyKey(poppupId);
1323 var page = AdfPage.PAGE;
1324 var activeDomElement = page.getActiveDomElement();
1325 if (activeDomElement && activeDomElement.id)
1326 page.setPageProperty(key, activeDomElement.id);
1327 }
1328
1329 AdfDhtmlPopupWindow.superclass.cancel.call(this);
1330}
1331
1332/**
1333 * Generates a key based on the popup's client id by replacing all ":" char's with "$"
1334 * and then adding a sufix of "$restoreFocus". The returned key is passed to
1335 * {@link AdfDhtmlPage#setPageProperty} and {@link AdfDhtmlPage#getPageProperty}.
1336 * This value pair will hold the last active dom element before the popup was
1337 * cancled due to a dom replacement.
1338 *
1339 * @param {String} clientId of the popup component
1340 * @return {String} map safe key
1341 */
1342AdfDhtmlPopupWindow._createFocusPagePropertyKey = function(clientId)
1343{
1344 AdfAssert.assertString(clientId);
1345 var tokens = clientId.split(":");
1346 return tokens.join("$") + "$restoreFocus";
1347}
1348
1349/**
1350 * Restore the keyboard focus to its previous location
1351 */
1352AdfDhtmlPopupWindow.prototype._restoreFocus = function()
1353{
1354 // Currently we try to restore the focus first to the align element
1355 // (if we have an align element) and then to the element which previously
1356 // had the focus. This simple strategy works just fine in the common
1357 // cases where the element which triggers the popup launch is still present
1358 // when the popup is dismissed. However, this does not handle the less
1359 // common case where the element which triggers the popup is no longer
1360 // present when the popup is dismissed.
1361 //
1362 // An example of this less common case is nested popups, where the parent
1363 // popup is dismissed when the child popup is launched. In this case,
1364 // the child popup may attempt to restore the focus to the launch element in
1365 // the parent popup, which will fail since the parent popup is no longer
1366 // visible. In this case, the child popup should attempt to restore
1367 // the focus to the element which triggered the parent popup launch. However,
1368 // currently if we fail to find an element to restore the focus to, we
1369 // lame out and move the focus to the first visible tab stop in the page.
1370 //
1371 // The nested popup scenario comes up in inputComboboxListOfValues, where
1372 // we've got a popup used for the combobox drop-down list which subsequently
1373 // launches the Search dialog. At the time the Search dialog is launched,
1374 // the focus in in the drop-down popup. This popup is immediately dismissed,
1375 // which means that the Search dialog cannot restore the focus. Ideally in
1376 // this case the Search dialog should instead restore the focus to the
1377 // element which launched its parent - ie. all the way back to the input field.
1378
1379 // TODO aschwart
1380 // 1. Fix the nested popup case where parent is dismissed when child is launched.
1381 // 2. Try harder to find a suitable element to restore the focus to when
1382 // the element that we want to restore the focus to is no longer available.
1383 // For example, we may be able to find a suitable sibling which can take
1384 // the focus, which would be an improvement over using the body.
1385
1386 // REMOVED workaround for bug 7694024; In IE, if a textarea is in the content of the popup,
1387 // This code would not get reached due to the above delay for IE
1388
1389
1390 // Bug 13699927 - weird focus issues on ie when closing non-modal popup
1391 // IE doesn't like the focus getting changed during mouseDown event processing that
1392 // causes autodismissal to occur. If we make a focus change during mouseDown event handling, IE will
1393 // fire a focus event on the form. This fix makes IE behave like other browsers by getting the target
1394 // of the mouseDown and if it is focusable, overriding the restoreFocus logic to set the restoreFocusElement to be
1395 // the srcElement of the mouseDown event. Actual setting of the focus is delayed in IE so this focus can occur after
1396 // IE sets focus on the form element.
1397
1398 var agent = AdfAgent.AGENT;
1399 var page = AdfPage.PAGE;
1400 var focusContextCache = new Object();
1401 // Try to find a suitable focus element in this order:
1402 // *) (IE Mouse Event ONLY) event target
1403 // 1) alignElement
1404 // 2) restoreFocusId (if focusable)
1405 // 3) launchSourceElement
1406 // 4) restoreFocusId next tabstop (for non-focusable restoreFocusId)
1407 var restoreFocusElement;
1408 var element = this.getElement();
1409 if(agent.getPlatform() == AdfAgent.IE_PLATFORM)
1410 {
1411 // bug 13699927
1412 // dialogs don't cancel auto dismiss when you click outside their content
1413 // This logic is only for autodismissal of non-dialogs.
1414 var isaDialog = (this instanceof AdfDhtmlSimpleFloat);
1415 if (!isaDialog)
1416 {
1417 var event = page.getDomWindow().event;
1418 if(event && event.type.indexOf("mouse") !== -1)
1419 {
1420 var eventTarget = agent.getEventTarget(event);
1421 if(eventTarget && AdfFocusUtils.isFocusable(eventTarget) &&
1422 !AdfDomUtils.isAncestorOrSelf(element, eventTarget))
1423 {
1424 restoreFocusElement = eventTarget;
1425 }
1426 }
1427 }
1428 }
1429
1430 if(!restoreFocusElement)
1431 {
1432 restoreFocusElement = this.getAlignElement();
1433 }
1434
1435 AdfLogger.LOGGER.fine("In AdfDhtmlPopupWindow#_restoreFocus(), _restoreFocusId set to " + this._restoreFocusId);
1436
1437 if( this._restoreFocusId &&
1438 (!restoreFocusElement ||
1439 !AdfFocusUtils.isFocusable(restoreFocusElement, focusContextCache)))
1440 {
1441 restoreFocusElement = page.getDomDocument().getElementById(this._restoreFocusId);
1442 }
1443
1444 // If restoreFocusElement is not focusable, favor the launchSourceId over _restoreFocusId.
1445 if( !restoreFocusElement || !AdfFocusUtils.isFocusable(restoreFocusElement, focusContextCache) )
1446 {
1447 var launchSourceElement = this.GetLaunchSourceElement();
1448 if (launchSourceElement)
1449 {
1450 restoreFocusElement = launchSourceElement;
1451 }
1452 }
1453
1454 // After the above logic, we should have a restoreFocusElement, if not, we are done
1455 if(restoreFocusElement)
1456 {
1457 if(agent.getPlatform() == AdfAgent.IE_PLATFORM && !this.isScreenReaderModePopup())
1458 {
1459 // Create a callback for IE so that the focus is fired outside of the autodismissal mouseDown event handling
1460 // This workaround isn't needed when in screen reader mode (and would causes other focus issues).
1461 var focusParams = new Object();
1462 focusParams["focusElement"] = restoreFocusElement;
1463 focusParams["popupElement"] = element;
1464 page.scheduleTimer(this, this._delayedCheckedFocus, focusParams, 10, {'isSynchronized' : true});
1465 return;
1466 }
1467 else if(AdfFocusUtils.isFocusable(restoreFocusElement, focusContextCache))
1468 {
1469 AdfFocusUtils.focusElement(restoreFocusElement);
1470 return;
1471 }
1472 // if the launcher component exists but is not a tabstop,
1473 // (context menu on an outputText) look for the next tabstop.
1474 AdfFocusUtils.focusNextTabStop(restoreFocusElement);
1475 return;
1476 }
1477
1478 // Sigh, no luck. For the moment, just set the focus to the
1479 // first tab stop in the document (skipping skipLink if present)...
1480 AdfFocusUtils.focusFirstDocumentTabStop();
1481}
1482
1483/**
1484 * Callback for setting focus with a check to see if the element is focusable
1485 * and the focus element is outside of the popup element, and there is not currently
1486 * a focusasble element in focus. (IE focus behavior fix)
1487 */
1488AdfDhtmlPopupWindow.prototype._delayedCheckedFocus = function(params)
1489{
1490 var popupElement = params["popupElement"];
1491 var focusElement = params["focusElement"];
1492 if ( !AdfFocusUtils.isFocusable(AdfPage.PAGE.getActiveDomElement()) &&
1493 !AdfDomUtils.isAncestorOrSelf(popupElement, focusElement) )
1494 {
1495 // Restore focus to the focusElement, or the next tabstop if its not focusable.
1496 (AdfFocusUtils.isFocusable(focusElement)) ? AdfFocusUtils.focusElement(focusElement) :
1497 AdfFocusUtils.focusNextTabStop(focusElement);
1498 }
1499}
1500
1501/**
1502 * Get the launch source element (if available). May return null. This is a protected method
1503 * and may be overwritten by child classes (AdfDhtmlPopupMenu.js).
1504 */
1505AdfDhtmlPopupWindow.prototype.GetLaunchSourceElement = function()
1506{
1507 var launchSourceId = this._launchSourceId;
1508 return (launchSourceId) ? AdfAgent.AGENT.getElementById(launchSourceId) : null;
1509}
1510
1511/**
1512 * Sets the content of this popup window.
1513 * @param {HTMLElement} selectorContent the dom element that is the content of the popupwindow
1514 */
1515AdfDhtmlPopupWindow.prototype.setContent = function(selectorContent)
1516{
1517 var contentElement = this.GetContentParent();
1518 if (contentElement.firstChild)
1519 contentElement.removeChild(contentElement.firstChild);
1520 contentElement.appendChild(selectorContent);
1521}
1522
1523/**
1524 * Sets accessibility attributes for the container element
1525 * @param {Object:null} hints a set of popup hints (see AdfDhtmlPopupWindow)
1526 */
1527AdfDhtmlPopupWindow.prototype.setAccessibleAttrs = function(hints)
1528{
1529 // Override me
1530}
1531
1532/**
1533 * Unsets accessible attributes to the popup container element.
1534 */
1535AdfDhtmlPopupWindow.prototype.removeAccessibleAttrs = function()
1536{
1537 // Override me
1538}
1539
1540/**
1541 * Announce opening of the popup via the wai-aria live region. To be overriden by children.
1542 * @param {Object:null} hints a set of popup hints
1543 */
1544AdfDhtmlPopupWindow.prototype.announceOpeningViaLiveRegion = function(hints)
1545{
1546 // Override me
1547}
1548
1549/**
1550 * @return {HTMLElement} returns the dom element that is the content of this popup window
1551 */
1552AdfDhtmlPopupWindow.prototype.getContent = function()
1553{
1554 return this.GetContentParent().firstChild;
1555}
1556
1557/**
1558 * Gets the dom element that is the parent of the popup's content.
1559 * Subclasses must override this method
1560 * @return {HTMLElement} contentParentElement the parent of the content
1561 */
1562AdfDhtmlPopupWindow.prototype.GetContentParent = function()
1563{
1564 AdfAssert.failedInAbstractFunction();
1565}
1566
1567
1568/**
1569* Sets the Handler invoked when the selector is being dismissed
1570* @param {Function} handler callback
1571*/
1572AdfDhtmlPopupWindow.prototype.setCloseHandler = function(handler)
1573{
1574 this._closeHandler = handler;
1575}
1576
1577
1578/**
1579* Sets the parameter for the Handler invoked when the selector is being dismissed
1580* @param {Object} actual arguments to the close handler
1581* @see #setCloseHandler
1582*/
1583AdfDhtmlPopupWindow.prototype.setCloseHandlerParam = function(param)
1584{
1585 this._closeHandlerParam = param;
1586}
1587
1588/**
1589 * @return {Boolean} indicates the visual state of the selector
1590 */
1591AdfDhtmlPopupWindow.prototype.isVisible = function()
1592{
1593 return this.getElement().style.display != "none";
1594}
1595
1596/**
1597 * Do the work to actually display of the popup window.
1598 * @param {Object:null} hints a set of popup hints (see {@link AdfDhtmlPopupWindow})
1599 */
1600AdfDhtmlPopupWindow.prototype.DoShow = function(hints)
1601{
1602 AdfDhtmlPopupWindow.superclass.show.call(this);
1603
1604 if (this.hasSpecialRenderingForScreenReader() && !this.GetFocusOnOpen())
1605 return;
1606
1607 this.raiseToFront();
1608 if(hints[AdfDhtmlPopupHints.POPUP_FULLSCREEN])
1609 this._hideZOrderLayerContainerSiblings(hints);
1610}
1611
1612/**
1613 * Creates the shadow for this popup window.
1614 * @param {Boolean} afterAnimation flag indicates that we want to create the shadow after animation
1615 */
1616AdfDhtmlPopupWindow.prototype.CreateShadow = function(afterAnimation)
1617{
1618 // Do not create a shadow if we are animating. We will create the shadow later
1619 if(this.HasShadow() && (!this._animationDuration || afterAnimation))
1620 {
1621 AdfDhtmlShadowDecorator.createShadowDecorator(this.getShadowAnchor(), this.getElement().parentNode);
1622 }
1623}
1624
1625/**
1626 * @return whether or not the popup will have a shadow
1627 */
1628AdfDhtmlPopupWindow.prototype.HasShadow = function()
1629{
1630 return true;
1631}
1632
1633/**
1634 * @return {Boolean} always returns true indicating the position manager should invert the primary position
1635 * if can't be aligned to the primary align hint.
1636 */
1637AdfDhtmlPopupWindow.prototype.shouldInvertPosition= function()
1638{
1639 return true;
1640}
1641
1642/**
1643 * Unregister event handlers, remove the shadow decorator, unregister with
1644 * the position manager before calling on super.
1645 * @override
1646 */
1647AdfDhtmlPopupWindow.prototype.destroy = function()
1648{
1649 var agent = AdfAgent.AGENT;
1650 var page = AdfPage.PAGE;
1651
1652 // after the content has be re-parented by the close handler, unregister the
1653 // dynamic elements - registered id for the resizeListener. @see #WrapContent
1654 agent.elementsRemoved(this.getElement());
1655
1656 this._restoreFocusElement = null;
1657
1658 if(this._sizeTimeout != null)
1659 {
1660 page.cancelTimer(this._sizeTimeout);
1661 delete this._sizeTimeout;
1662 }
1663
1664 var shadowAnchor = this.getShadowAnchor();
1665 if (AdfDhtmlShadowDecorator.hasShadowDecorator(shadowAnchor))
1666 {
1667 AdfDhtmlShadowDecorator.removeShadowDecorator(shadowAnchor);
1668 }
1669 if(this._positionManagerIndex != null)
1670 page.getPositionManager().removeElement(this._positionManagerIndex);
1671
1672 this.setAlignElement(null);
1673
1674 AdfDhtmlPopupWindow.superclass.destroy.call(this);
1675}
1676
1677/**
1678 * Returns if the popup window is currently animating during the opening of the window.
1679 * @return true if the popup is currently animating during opening else false.
1680 */
1681AdfDhtmlPopupWindow.prototype.isAnimating = function()
1682{
1683 return this._animator != null || this._isOpening
1684}
1685
1686/**
1687 * @return {Number} numeric token used by the position manager service to track a registed
1688 * floating element.
1689 */
1690AdfDhtmlPopupWindow.prototype.GetPositionManagerIndex = function()
1691{
1692 return this._positionManagerIndex;
1693}
1694
1695/**
1696 * Handles the keydown event from the document. The _registerEventHanders and
1697 * _unregisterEventHandlers functions manage registration and called from the
1698 * show and hide functions.
1699 *
1700 * Intercepts the tab key press to cycle focus within the floating element. If focus
1701 * is on the last element, focus returns to the first tab-able element in the popup.
1702 *
1703 * @param {Event} event
1704 */
1705AdfDhtmlPopupWindow.prototype._handleKeyDown = function(event)
1706{
1707 var agent = AdfAgent.AGENT;
1708 var targetElement = agent.getEventTarget(event);
1709 var popupContainer = this.getElement();
1710 if (event.keyCode == AdfKeyStroke.TAB_KEY)
1711 {
1712 var firstFocusableElementInPopup = AdfFocusUtils.getFirstTabStop(popupContainer);
1713 var lastElement = AdfFocusUtils.getLastTabStop(popupContainer);
1714
1715 // If the popup doesn't contain any focusable content, ignore and eat the tab keyDown event.
1716 if (firstFocusableElementInPopup == null)
1717 {
1718 agent.eatEvent(event);
1719 return;
1720 }
1721
1722 if (event.shiftKey)
1723 {
1724 if (targetElement == firstFocusableElementInPopup)
1725 {
1726 AdfFocusUtils.focusPreviousTabStop(targetElement, popupContainer);
1727 agent.eatEvent(event);
1728 }
1729 }
1730 else
1731 {
1732 if (lastElement == targetElement)
1733 {
1734 AdfFocusUtils.focusNextTabStop(targetElement, popupContainer);
1735 agent.eatEvent(event);
1736 }
1737 }
1738 }
1739 else if (event.keyCode == AdfKeyStroke.F6_KEY)
1740 {
1741 if (AdfPage.PAGE.isScreenReaderMode() &&
1742 !this.hasSpecialRenderingForScreenReader() &&
1743 AdfDomUtils.isAncestorOrSelf(popupContainer, targetElement))
1744 {
1745 // F6 is for focus management on popup in accessibility mode
1746 this.HandleKeyNavigation(event);
1747 }
1748 }
1749}
1750
1751/**
1752 * Handles the keyup event from the document. The _registerEventHanders and
1753 * _unregisterEventHandlers functions manage registration and called from the
1754 * show and hide functions.
1755 *
1756 * The ESC key is handled if the HINT_CLOSE_ON_ESCAPE hint was turned on. This logic will
1757 * hide the floating element if the event target is within the popup. Or, if the
1758 * HINT_LAUNCH_SOURCE_ID hint is provided, the dom element it points to is considered within
1759 * the popup.
1760 *
1761 * @see #HandleEscapeKey
1762 *
1763 * @param {Event} keyup event
1764 */
1765AdfDhtmlPopupWindow.prototype._handleKeyUp = function(event)
1766{
1767 var agent = AdfAgent.AGENT;
1768 var keyCode = agent.getKeyCode(event);
1769 var targetElement = agent.getEventTarget(event);
1770
1771 if (keyCode == AdfKeyStroke.ESC_KEY)
1772 {
1773 // was HINT_CLOSE_ON_ESCAPE hint provided
1774 if(this._closeOnEscape)
1775 {
1776 var invokeEscapeHandler = false;
1777
1778 // was HINT_LAUNCH_SOURCE_ID hint provided
1779 var launchSourceId = this._launchSourceId;
1780
1781 if (AdfDomUtils.isAncestorOrSelf(this.getElement(), targetElement))
1782 {
1783 invokeEscapeHandler = true;
1784 }
1785 else if (launchSourceId)
1786 {
1787 var launchElement = agent.getElementById(launchSourceId);
1788 if (launchElement && AdfDomUtils.isAncestorOrSelf(launchElement, targetElement))
1789 {
1790 invokeEscapeHandler = true;
1791 }
1792 }
1793 if (invokeEscapeHandler)
1794 {
1795 this.HandleEscapeKey(event);
1796 }
1797 }
1798 }
1799
1800 if ((event.ctrlKey) && (event.altKey) && (keyCode == AdfKeyStroke.W_KEY))
1801 {
1802 if (AdfDomUtils.isAncestorOrSelf(this.getElement(), targetElement))
1803 {
1804 this.HandleKeyNavigation(event);
1805 }
1806 }
1807}
1808
1809/**
1810 * Overridable function to handle escape key.
1811 *
1812 * @param {Event} key up event
1813 */
1814AdfDhtmlPopupWindow.prototype.HandleEscapeKey = function(event)
1815{
1816 this.cancel();
1817 AdfAgent.AGENT.eatEvent(event);
1818}
1819
1820/**
1821 * Overridable function to move focus away from the popup
1822 *
1823 * @param {Event} key up event
1824 */
1825AdfDhtmlPopupWindow.prototype.HandleKeyNavigation = function(event)
1826{
1827}
1828
1829/**
1830 * Sizes the popup window and then validate the position with the position manager service.
1831 * If animation is enabled, animate the window sizing from the alignment computed
1832 * by the position manager.
1833 * @param {Object:null} hints a set of popup hints (see {@link AdfDhtmlPopupWindow})
1834 */
1835AdfDhtmlPopupWindow.prototype._size = function(hints)
1836{
1837 if(this._sizeTimeout == null)
1838 return;
1839
1840 delete this._sizeTimeout;
1841
1842 //Finalize the size of components inside popup and then validate the popup's position
1843 var domElement = this.getContent();
1844 this.DoResizeNotifyDom(domElement);
1845
1846 // We hard-set the width of the table. If we do not do this, the table will
1847 // try to wrap it's content after resizing. We won't get any notifications
1848 // of that. This causes weird behavior. For example, shadowDecorators are
1849 // not updated and out of sync with the element.
1850 var element = this.getElement(), elementStyle = element.style;
1851
1852 // Look to see if here was a popup hint, maximum width, provided.
1853 // The private attribute is assigned in the show method using
1854 // the AdfDhtmlPopupWindow.HINT_MAX_WIDTH hint. By default, the popup
1855 // will not have a maximum width. The content will determine the size.
1856
1857 var maxWidth = this._maxWidth;
1858 if (maxWidth)
1859 {
1860 // if there is a hints override, use the lesser of what the browser needs
1861 // or the popup max-width hint
1862 elementStyle.width = Math.min(element.offsetWidth, maxWidth) +"px";
1863 }
1864 else
1865 {
1866 // explicitly set the width to what the browser thinks it needs
1867 elementStyle.width = element.offsetWidth +"px";
1868 }
1869 if(hints[AdfDhtmlPopupHints.POPUP_FULLSCREEN])
1870 {
1871 elementStyle.maxHeight= AdfPage.PAGE.getDomWindow().innerHeight +"px";
1872 elementStyle.height = AdfPage.PAGE.getDomWindow().innerHeight +"px";
1873 }
1874
1875 // if registered with the position manager, compute the alignment
1876 if(this._positionManagerIndex != null)
1877 {
1878 // compute the center position of the pop up after it is sized.
1879 var screenCenterPosition = hints[AdfDhtmlPopupWindow.HINT_POSITION_CENTER];
1880 if (screenCenterPosition)
1881 {
1882 this.setAlignPosition(this.CalcAlignPosition());
1883 }
1884 AdfPage.PAGE.getPositionManager().validatePosition(this._positionManagerIndex);
1885 }
1886
1887 if (this.hasSpecialRenderingForScreenReader() && !this.GetFocusOnOpen())
1888 AdfPopupScopingUtils.scope(AdfPage.PAGE.getActiveDomElement());
1889
1890 // if animation is turned on, animate opening from the alignment position
1891 var animationDuration = this.GetAnimationDuration();
1892 if(animationDuration > 0)
1893 {
1894 this.AnimateOpening(hints);
1895 }
1896 else
1897 {
1898 // actually display the element
1899 elementStyle.visibility = "";
1900
1901 // create the shadow decorator
1902 this.CreateShadow();
1903
1904 // Invoke the popup opened callback handler. It's only invoked the first
1905 // time the resizing occurs.
1906 this.OpenedPopup(hints);
1907 }
1908}
1909
1910/**
1911 * Animates the opening of the popup.
1912 *
1913 * @see #GetAnimationDuration
1914 * @param {Object:null} hints a set of popup hints (see {@link AdfDhtmlPopupWindow})
1915 */
1916AdfDhtmlPopupWindow.prototype.AnimateOpening = function(hints)
1917{
1918 var launchSourceId = this._launchSourceId;
1919
1920 this.AnimateUsingWrapper(this.GetAnimationDuration(), hints, true);
1921}
1922
1923/**
1924 * To get popup anchors isRight and isBottom properties.
1925 */
1926AdfDhtmlPopupWindow.prototype._getAnchorProperties = function()
1927{
1928 var anchor = new Object;
1929 anchor.isRight = false;
1930 anchor.isBottom = false;
1931
1932 var positionManagerIndex = this.GetPositionManagerIndex();
1933
1934 if(positionManagerIndex >=0)
1935 {
1936 var behavior = AdfPage.PAGE.getPositionManager().getComputedBehavior(positionManagerIndex);
1937
1938 if (behavior)
1939 {
1940 anchor.isRight = behavior.horizontalBehavior.floatingElementAnchor == AdfDhtmlPositionManager.RIGHT;
1941 anchor.isBottom = behavior.verticalBehavior.floatingElementAnchor == AdfDhtmlPositionManager.BOTTOM;
1942 }
1943 }
1944
1945 return anchor;
1946}
1947
1948/**
1949 * To show animation while closing the popup.
1950 */
1951AdfDhtmlPopupWindow.prototype.AnimateClosing = function(hints)
1952{
1953 this.AnimateUsingWrapper(this.GetCloseAnimationDuration(), hints, false);
1954}
1955
1956/**
1957 * Removes the managed dom element replacing with a hidden div. The current coordinates are
1958 * captured and used as the finals for the animation.
1959 *
1960 * @param {Number} animationDuration elapsed time the opening animation should take. Configured per
1961 * component as a skinning property.
1962 * @param {Object} anchor.isRight aligment is anchored on the right edge of the dialog and
1963 * anchor.isBottom alignment is anchored on the buttom edge of the dialog
1964 * @param {Object:null} hints a set of popup hints (see {@link AdfDhtmlPopupWindow})
1965 */
1966AdfDhtmlPopupWindow.prototype.AnimateUsingWrapper = function(
1967 animationDuration,
1968 hints,
1969 isOpening)
1970{
1971 var rootElement = this.getElement();
1972
1973 // get the dom position style of root element
1974 var styleProperties = AdfDhtmlPopupWindow._getElementStyle(rootElement);
1975
1976 // create the wrapper to show animation on UI
1977 var wrapper = AdfDhtmlPopupWindow._createTransitionWrapper(rootElement);
1978
1979 var transitionProperties = AdfDhtmlPopupWindow._setUpWrapperTransition(rootElement, wrapper, hints, isOpening,
1980 styleProperties, this._getAnchorProperties());
1981 if (transitionProperties != null)
1982 {
1983 if (isOpening)
1984 {
1985 // save the scrollTop's so they can be restored back later
1986 this.SaveScrollValues(rootElement);
1987 }
1988
1989 rootElement.parentNode.replaceChild(wrapper, rootElement);
1990 wrapper.appendChild(rootElement);
1991
1992 if (isOpening)
1993 {
1994 // Before animating, make sure that there is a maskingFrame available to avoid potential iframe
1995 // creation at the end of the animation. (bug 7137710)
1996 AdfPage.PAGE.prepareMaskingFrame(rootElement);
1997 }
1998
1999 this._animator = AdfDhtmlElementAnimator.animate(
2000 AdfDhtmlElementAnimator.FRAME_METHOD_CONSTANT_SPEED,
2001 animationDuration,
2002 [
2003 {
2004 "element": wrapper,
2005 "properties" :transitionProperties
2006 }
2007 ],
2008 null,
2009 isOpening ? AdfDhtmlPopupWindow.OpenAnimationComplete : AdfDhtmlPopupWindow.CloseAnimationComplete,
2010 {"wrapper" :wrapper, "myself": this, "hints": hints});
2011 }
2012 else
2013 {
2014 if (isOpening)
2015 {
2016 // actually display the popup
2017 rootElement.style.visibility = "";
2018
2019 // create the shadow decorator for popup
2020 this.CreateShadow();
2021
2022 this.OpenedPopup(hints);
2023 }
2024 else
2025 {
2026 this.ClosedPopup();
2027 }
2028 }
2029}
2030
2031/**
2032 * To get the DOM position styles
2033 */
2034AdfDhtmlPopupWindow._getElementStyle = function (element)
2035{
2036 var elementStyle = {};
2037
2038 elementStyle.height = element.offsetHeight;
2039 elementStyle.width = element.offsetWidth;
2040 elementStyle.top = parseInt(element.style.top);
2041 elementStyle.left = parseInt(element.style.left);
2042 elementStyle.right = elementStyle.left + elementStyle.width;
2043
2044 return elementStyle;
2045}
2046
2047/**
2048 * To create popups wrapper div to show animation.
2049 */
2050AdfDhtmlPopupWindow._createTransitionWrapper = function (rootElement)
2051{
2052 var ownerDoc = rootElement.ownerDocument;
2053
2054 var wrapper = ownerDoc.createElement("div");
2055
2056 wrapper.setAttribute(AdfDhtmlPopupWindow._ANIMATE_WRAPPER_EXPANDO, true);
2057 wrapper.style.overflow = "hidden";
2058 wrapper.style.position = "absolute";
2059 wrapper.style.zIndex = rootElement.style.zIndex;
2060
2061 return wrapper;
2062}
2063
2064/**
2065 * To set wrapper transition related styles and return properties for animation.
2066 */
2067AdfDhtmlPopupWindow._setUpWrapperTransition = function (rootElement, wrapper, hints, isOpening,
2068 styleProperties, anchor)
2069{
2070 // To hold, is it for opening or closing popup
2071 var transition = isOpening ?
2072 hints[AdfDhtmlPopupHints.OPEN_TRANSITION] : hints[AdfDhtmlPopupHints.CLOSE_TRANSITION];
2073
2074 if (isOpening && (!transition || transition == AdfDhtmlPopupHints.TRANSITION_AUTO))
2075 {
2076 // While opening popup, if animation is enabled and af:transition is configured to "auto"
2077 // or af:transition is not configured for triggerType="open". Its default behavior.
2078 return AdfDhtmlPopupWindow._growTransition(rootElement, wrapper, styleProperties, anchor);
2079 }
2080 else if (transition == AdfDhtmlPopupHints.TRANSITION_VERTICAL ||
2081 transition == AdfDhtmlPopupHints.TRANSITION_HORIZONTAL)
2082 {
2083 // If transition is vertical or horizontal, it means sliding animation.
2084 return AdfDhtmlPopupWindow._slideTransition(rootElement, wrapper, styleProperties, isOpening, transition,
2085 anchor);
2086 }
2087
2088 // If af:transition is configured to "none", or configured unsupported transition type.
2089 return null;
2090}
2091
2092/**
2093 * This is default behavior to show popup animation as growing diagonally. The diagonal growing direction will be based
2094 * on two parameters, i.e. anchor.isRight, anchor.isBottom. The actual popup, rootElemnt, will not resize or animate,
2095 * it will float in the wrapper element. The root popup element will stick to two of the wrapper div edges, based on
2096 * anchor.isRight and anchor.isBottom values. Doing so, the root element will be positioned along with wrapper elements.
2097 * The approach is, first set the root element position styles, then initial wrapper animation properties, from where
2098 * animation will start, and at last compute animation properties, where wrapper animation will end.
2099 *
2100 * return properties, will be used by AdfDhtmlElementAnimator.animate() to make sliding frams, to show animation.
2101 */
2102AdfDhtmlPopupWindow._growTransition = function (rootElement, wrapper, styleProperties, anchor)
2103{
2104 // to set root element at top left in wrapper.
2105 var rootStyle = rootElement.style;
2106 rootStyle.top = "0px";
2107 rootStyle.left = "0px";
2108
2109 // To start animation from 1 X 1 px, set wrapper height and width.
2110 var wrapperStyle = wrapper.style;
2111 wrapperStyle.height = "1px";
2112 wrapperStyle.width = "1px";
2113
2114 // Both height and width needs to grow while showing animation
2115 var properties = { "height" : styleProperties.height,
2116 "width" : styleProperties.width};
2117
2118 if(anchor.isRight)
2119 {
2120 // If anchor is at right, then popup is at left side of aligned element,
2121 // It means we need to grow from right to left.
2122 wrapperStyle.left = (styleProperties.left + styleProperties.width - 1) + "px";
2123
2124 properties.left = styleProperties.left;
2125 }
2126 else
2127 {
2128 // If anchor is at left, then popup is at right side of aligned element,
2129 // It means we need to grow from left to right.
2130 wrapperStyle.left = styleProperties.left + "px";
2131 }
2132
2133 if(anchor.isBottom)
2134 {
2135 // If anchor is at bottom, then popup is at top side of aligned element,
2136 // It means we need to grow from bottom to top.
2137 wrapperStyle.top = (styleProperties.top + styleProperties.height +1) + "px";
2138
2139 properties.top = styleProperties.top;
2140 }
2141 else
2142 {
2143 // If anchor is at top, then popup is at bottom side of aligned element,
2144 // It means we need to grow from top to bottom.
2145 wrapperStyle.top = styleProperties.top + "px";
2146 }
2147
2148 // At the end make popup visible
2149 rootStyle.visibility = "";
2150
2151 return properties;
2152}
2153
2154/**
2155 * To set sliding animation on popup warpper.
2156 */
2157AdfDhtmlPopupWindow._slideTransition = function (rootElement, wrapper, styleProperties, isOpening, transition, anchor)
2158{
2159 var rootStyle = rootElement.style;
2160 var wrapperStyle = wrapper.style;
2161 var properties = {};
2162
2163 // get the sliding direction, Example slide towards left, right, up or down.
2164 var slideDirection = AdfDhtmlPopupWindow._getSlideDirection(isOpening, transition, anchor);
2165
2166 if (slideDirection == AdfDhtmlPopupWindow._SLIDE_LEFT || slideDirection == AdfDhtmlPopupWindow._SLIDE_RIGHT)
2167 {
2168 // If sliding direction is horizontal, slide left or right
2169 properties = AdfDhtmlPopupWindow._horizontalSlideTransition(rootStyle, wrapperStyle, styleProperties,
2170 isOpening, slideDirection);
2171 }
2172 else if (slideDirection == AdfDhtmlPopupWindow._SLIDE_UP || slideDirection == AdfDhtmlPopupWindow._SLIDE_DOWN)
2173 {
2174 // If sliding direction is vertical, slide up or down
2175 properties = AdfDhtmlPopupWindow._verticalSlideTransition(rootStyle, wrapperStyle, styleProperties,
2176 isOpening, slideDirection);
2177 }
2178
2179 rootStyle.visibility = "";
2180
2181 return properties;
2182}
2183
2184/**
2185 * To get sliding direction left, right, up or down. Sliding direction will be based on state of popup,
2186 * opening or closing, transition value and anchor position of popup.
2187 */
2188AdfDhtmlPopupWindow._getSlideDirection = function (isOpening, transition, anchor)
2189{
2190 // IF the transition is horizontal, then sliding would be left or right.
2191 // Else do sliding up or down.
2192 if (transition == AdfDhtmlPopupHints.TRANSITION_HORIZONTAL)
2193 {
2194 // If anchor is at right, it means popup is at left side of aligned element. So if anchor is at right,
2195 // and popup is opening, then do left sliding. Else do sliding towards right.
2196 if ((anchor.isRight && isOpening) || (!anchor.isRight && !isOpening))
2197 {
2198 return AdfDhtmlPopupWindow._SLIDE_LEFT;
2199 }
2200 else
2201 {
2202 return AdfDhtmlPopupWindow._SLIDE_RIGHT;
2203 }
2204 }
2205 else if (transition == AdfDhtmlPopupHints.TRANSITION_VERTICAL)
2206 {
2207 // If anchor is at bottom, it means popup is at top side of aligned element. So if anchor is at top,
2208 // and popup is opening, then do up sliding. Else do sliding towards down.
2209 if ((anchor.isBottom && isOpening) || (!anchor.isBottom && !isOpening))
2210 {
2211 return AdfDhtmlPopupWindow._SLIDE_UP;
2212 }
2213 else
2214 {
2215 return AdfDhtmlPopupWindow._SLIDE_DOWN;
2216 }
2217 }
2218}
2219
2220/**
2221 * To make horizontal sliding animation. The horizontal sliding direction will be based on two parameters,
2222 * i.e. isOpening, slideDirection. The actual popup, rootStyle, will not resize or animate, it will float in
2223 * the wrapper element. The root popup element will stick to two of the wrapper div edges, based on isOpening and
2224 * slideDirection values. Doing so, the root element will be positioned along with wrapper element sliding animation.
2225 * The approach is, first set the root element position, then set the initial wrapper animation properties,
2226 * from where animation will start, and compute animation properties, where wrapper animation will end.
2227 *
2228 * return properties, will be used by AdfDhtmlElementAnimator.animate() to make sliding frams, to show animation.
2229 */
2230AdfDhtmlPopupWindow._horizontalSlideTransition = function (rootStyle, wrapperStyle, styleProperties,
2231 isOpening, slideDirection)
2232{
2233 // Stick root popup to top side of wrapper.
2234 rootStyle.top = "0px";
2235
2236 wrapperStyle.top = styleProperties.top + "px";
2237 wrapperStyle.height = styleProperties.height + "px";
2238
2239 var properties = {};
2240
2241 if (isOpening) // Slide while opening popup
2242 {
2243 // start popup wrapper animation with width of 1px
2244 wrapperStyle.width = "1px";
2245
2246 // Animation will end at width of actual root popup width
2247 properties.width = styleProperties.width;
2248
2249 if (slideDirection == AdfDhtmlPopupWindow._SLIDE_LEFT) // slide left
2250 {
2251 // Stick root popup to wrappers left edge
2252 rootStyle.left = "0px";
2253
2254 // Start the animation from right side of popup
2255 wrapperStyle.left = (styleProperties.left + styleProperties.width - 1) + "px";
2256
2257 // Animation will end at left at actual root popup left.
2258 properties.left = styleProperties.left;
2259 }
2260 else // slide right
2261 {
2262 // Stick to right to wrappers right edge
2263 rootStyle.left = "auto";
2264 rootStyle.right = "0px";
2265
2266 wrapperStyle.left = styleProperties.left + "px";
2267 }
2268 }
2269 else // slide while closing the popup
2270 {
2271 // start animation from full width till 1px width
2272 wrapperStyle.width = styleProperties.width + "px";
2273 properties.width = 1;
2274
2275 if (slideDirection == AdfDhtmlPopupWindow._SLIDE_LEFT) // slide left
2276 {
2277 // stick root element to right side of wrapper edge
2278 rootStyle.left = "auto";
2279 rootStyle.right = "0px";
2280
2281 // Set wrapper left as left of root popup
2282 wrapperStyle.left = styleProperties.left + "px";
2283 }
2284 else // slide right
2285 {
2286 // Stick left side of root to the left edge of wrapper.
2287 rootStyle.left = "0px";
2288
2289 wrapperStyle.left = (styleProperties.left - 1) + "px";
2290
2291 // Animation will end at right side of rendered popup
2292 properties.left = (styleProperties.left + styleProperties.width - 1);
2293 }
2294 }
2295
2296 return properties;
2297}
2298
2299/**
2300 * To make vertical sliding animation. The vertical sliding direction will be based on two parameters, i.e. isOpening,
2301 * slideDirection. The actual popup, rootStyle, will not resize or animate, it will float in the wrapper element. The
2302 * root popup element will stick to two of the wrapper div edges, based on isOpening and slideDirection values. Doing
2303 * so, the root element will be positioned along with wrapper element sliding animation.The approach is, first set
2304 * the root element position, then set the initial wrapper animation properties, from where animation will start, and
2305 * compute animation properties, where wrapper animation will end.
2306 *
2307 * return properties, will be used by AdfDhtmlElementAnimator.animate() to make sliding frams, to show animation.
2308 */
2309AdfDhtmlPopupWindow._verticalSlideTransition = function (rootStyle, wrapperStyle, styleProperties,
2310 isOpening, slideDirection)
2311{
2312 // Stick root popup to left side of wrapper.
2313 rootStyle.left = "0px";
2314
2315 wrapperStyle.width = styleProperties.width + "px";
2316 wrapperStyle.left = styleProperties.left + "px";
2317
2318 var properties = {};
2319
2320 if (isOpening) // slide while opening popup
2321 {
2322 // start popup wrapper animation with height of 1px
2323 wrapperStyle.height = "1px";
2324
2325 // Animation will end at height and top of actual root popup height and top
2326 properties.top = styleProperties.top;
2327 properties.height = styleProperties.height;
2328
2329 if (slideDirection == AdfDhtmlPopupWindow._SLIDE_UP) // Slide upwards
2330 {
2331 // Stick root popup to wrappers top edge
2332 rootStyle.top = "0px";
2333
2334 // Start the animation from bottom side of popup
2335 wrapperStyle.top = (styleProperties.top + styleProperties.height - 1) + "px";
2336 }
2337 else // Slide downwards
2338 {
2339
2340 // Stick root to bottom of wrappers bottom edge
2341 rootStyle.top = "auto";
2342 rootStyle.bottom = "0px";
2343 rootStyle.height = styleProperties.height + "px";
2344
2345 wrapperStyle.top = (styleProperties.top + 1) + "px";
2346 }
2347 }
2348 else // slide while closing popup
2349 {
2350 // start animation from full height till 1px height
2351 wrapperStyle.height = styleProperties.height + "px";
2352 wrapperStyle.top = styleProperties.top + "px";
2353
2354 properties.height = 1;
2355
2356 if (slideDirection == AdfDhtmlPopupWindow._SLIDE_UP) // slide upwards
2357 {
2358 // stick root element to bottom side of wrapper edge
2359 rootStyle.top = "auto";
2360 rootStyle.bottom = "0px";
2361 }
2362 else // slide downwards
2363 {
2364 // Stick top side of root to the top edge of wrapper.
2365 rootStyle.top = "0px";
2366 rootStyle.height = styleProperties.height + "px";
2367
2368 // Set wrapper top as top of root popup
2369 wrapperStyle.top = styleProperties.top + "px";
2370
2371 // Animation will end at top side of rendered popup
2372 properties.top = styleProperties.top + styleProperties.height;
2373 }
2374 }
2375
2376 return properties;
2377}
2378
2379/**
2380 * Callback invoked after close animation has completed. Invokes the popup closing logic.
2381 *
2382 * @param {Object} context object holding the content element, hints and instance of this object
2383 */
2384AdfDhtmlPopupWindow.CloseAnimationComplete = function(context)
2385{
2386 var myself = context.myself;
2387 var wrapper = context.wrapper;
2388
2389 if (!myself._animator)
2390 return;
2391
2392 delete myself._animator;
2393
2394 AdfDhtmlPopupWindow._restoreRootPostAnimation(wrapper);
2395
2396 myself.ClosedPopup();
2397}
2398
2399
2400/**
2401 * Restores the dom element after animation has completed.
2402 * @param {Object} wrapper object holding the content element and instance of this object
2403 */
2404AdfDhtmlPopupWindow._restoreRootPostAnimation = function(wrapper)
2405{
2406 var wrapperStyle = wrapper.style;
2407 var rootElement = wrapper.firstChild;
2408 var rootStyle = rootElement.style;
2409
2410 rootStyle.right = wrapperStyle.right;
2411 rootStyle.left = wrapperStyle.left;
2412 rootStyle.top = wrapperStyle.top;
2413 wrapper.parentNode.replaceChild(rootElement, wrapper);
2414}
2415
2416/**
2417 * Callback invoked after open animation has completed. Restores the
2418 * dom element, creates the shadow decorator and then invokes the
2419 * popup opening logic.
2420 *
2421 * @see AdfDhtmlPopupWindow#_restoreRootPostAnimation
2422 * @see #CreateShadow
2423 * @see #OpenedPopup
2424 *
2425 * @param {Object} wrapper object holding the content element and instance of this object
2426 */
2427AdfDhtmlPopupWindow.OpenAnimationComplete = function(context)
2428{
2429 var wrapper = context.wrapper;
2430 var myself = context.myself;
2431 var hints = context.hints;
2432
2433 if (!myself._animator)
2434 return;
2435
2436 // see use of _animator in AdfDhtmlPopupLayer
2437 delete myself._animator;
2438
2439 AdfDhtmlPopupWindow._restoreRootPostAnimation(wrapper);
2440
2441 myself.CreateShadow(true);
2442
2443 myself.OpenedPopup(hints);
2444}
2445
2446/**
2447 * @return {Number} the duration of the animation for the popup
2448 */
2449AdfDhtmlPopupWindow.prototype.GetAnimationDuration = function()
2450{
2451 return this._animationDuration;
2452}
2453
2454/**
2455 *
2456 * @return {Number} the duration of the close animation for the popup
2457 * @Override
2458 */
2459AdfDhtmlPopupWindow.prototype.GetCloseAnimationDuration = function()
2460{
2461 return this._animationDuration;
2462}
2463
2464/**
2465 * @return {String} the skin property for animation duriation
2466 * @param {Object:null} hints a set of popup hints (see {@link AdfDhtmlPopupWindow})
2467 */
2468AdfDhtmlPopupWindow.prototype.GetAnimationDurationSkinProperty = function(hints)
2469{
2470 return null;
2471}
2472
2473/**
2474 * @return {String} the skin property for -tr-animate
2475 */
2476AdfDhtmlPopupWindow.prototype.GetAnimateSkinProperty = function()
2477{
2478 return null;
2479}
2480
2481
2482/**
2483 * Updates the size of the popup to match its content and resizes the shadow
2484 * decorator.
2485 */
2486AdfDhtmlPopupWindow.prototype.autoSize = function()
2487{
2488 var isOpening = this._isOpening;
2489
2490 if (!this.isVisible() || isOpening)
2491 {
2492 return;
2493 }
2494
2495 var element = this.getElement(), elementStyle = element.style;
2496
2497 // Look to see if here was a popup hint, maximum width, provided.
2498 // The private attribute is assigned in the show method using
2499 // the AdfDhtmlPopupWindow.HINT_MAX_WIDTH hint. By default, the popup
2500 // will not have a maximum width. The content will determine the size.
2501
2502 // Set the width temporarily to 'auto' so when we get the offset width, we are getting its true
2503 // value, and not just the value that was set earlier. Without setting width to auto here,
2504 // detachable menus with longer header names don't resize properly (bug 6507828).
2505 elementStyle.width = "auto";
2506
2507 var maxWidth = this._maxWidth;
2508 if (maxWidth)
2509 {
2510 // if there is a hints override, use the lesser of what the browser needs
2511 // or the popup max-width hint
2512 elementStyle.width = Math.min(element.offsetWidth, maxWidth) +"px";
2513 }
2514 else
2515 {
2516 // explicitly set the width to what the browser thinks it needs
2517 elementStyle.width = element.offsetWidth +"px";
2518 }
2519
2520 var anchor = this.getShadowAnchor();
2521 if (AdfDhtmlShadowDecorator.hasShadowDecorator(anchor))
2522 {
2523 AdfDhtmlShadowDecorator.moveShadowDecorator(anchor);
2524 AdfDhtmlShadowDecorator.showShadowDecorator(anchor, true);
2525 }
2526}
2527
2528
2529/**
2530 * Sets focus to the first content element in the window.
2531 *
2532 * @see #GetFocusContent
2533 */
2534AdfDhtmlPopupWindow.prototype.FocusOnFirstElement = function(force)
2535{
2536 // If we are animating we do not need to do any focus. The focus will be done post animation
2537 if (this._animator != null)
2538 return;
2539
2540 // if focus popup hint was not provided, don't try to set focus
2541 var focusHint = this.GetFocusOnOpen();
2542 if (!force && !(focusHint))
2543 {
2544 return;
2545 }
2546
2547
2548 // Only set focus on the container for IOS, since setting focus on an input field
2549 // causes weird issues with virtual keyboard etc
2550 var agent = AdfAgent.AGENT;
2551 var domWindow = agent.getDomWindow();
2552 var rootElement = this.getElement();
2553
2554 if (domWindow.AdfSafariMobileAgent && (agent instanceof AdfSafariMobileAgent) &&
2555 agent.getOS() != AdfAgent.ANDROID_OS)
2556 {
2557 rootElement.tabIndex = -1;
2558 AdfFocusUtils.focusElement(rootElement);
2559 return;
2560 }
2561
2562 //this.RestoreScrollValues(this.getContent());
2563
2564 // find the content area. this will be the same for inline selectors.
2565 // a subclass of AdfDhtmlSimpleFloat will override.
2566 var focusContentElement = this.GetFocusContent();
2567 var firstFocus = AdfFocusUtils.getFirstTabStop(focusContentElement);
2568 if (!(firstFocus))
2569 {
2570
2571 // fall back to the outer frame if focus can not be established
2572 // this would occur for the subclass AdfDhtmlSimpleFloat. The
2573 // "dialog" overrides the protected GetFocusContent() to look in the
2574 // center of the dialog. If the dialog content doesn't contain a
2575 // focusable element, fall back to the outer frame where something in
2576 // the header (closeIcon) or footer (button bar) will have focus elements.
2577
2578 if (rootElement != focusContentElement) {
2579 // root is not the same as GetFocusContent, look again
2580 firstFocus = AdfFocusUtils.getFirstTabStop(rootElement);
2581 }
2582
2583 if (!(firstFocus))
2584 {
2585 // if a focus stop cannot be found in the content, force focus by
2586 // setting the tabindex and then setting focus.
2587 focusContentElement.tabIndex = -1;
2588 AdfFocusUtils.focusElement(focusContentElement);
2589 }
2590 }
2591
2592 if (firstFocus)
2593 {
2594 // bug 14562816 - workaround for an issue seen in IE8 where the first focus is not
2595 // sticking for the second modal dialog.
2596 var agent = AdfAgent.AGENT;
2597 if (!AdfPage.PAGE.isScreenReaderMode() &&
2598 (AdfAgent.IE_PLATFORM != agent.getPlatform() ||
2599 (AdfAgent.IE_PLATFORM == agent.getPlatform() && agent.getVersion() > 8)))
2600 AdfFocusUtils.focusElement(firstFocus);
2601 else
2602 //bug 10251301 delay focus to allow for a heavy keydown that could be redirected
2603 //to the content of the popup versus the launcher component.
2604 AdfFocusUtils.focusElementDelayed(firstFocus, 1000);
2605 }
2606}
2607
2608/**
2609 * Returns the content dom node for the popup. This method should be overridden
2610 * by subclasses to return the inner content area versus the outer border/title
2611 * dom structure.
2612 *
2613 * @return {HTMLElement} content area of the popup
2614 */
2615AdfDhtmlPopupWindow.prototype.GetFocusContent = function()
2616{
2617 var element = this.getElement();
2618 return element;
2619}
2620
2621/**
2622 * Registers this floating element with the position manager based on the
2623 * alignment hint. Unregisters if already registered.
2624 *
2625 * @param {String} hintAlign alignment constants
2626 */
2627AdfDhtmlPopupWindow.prototype.Position = function(hintAlign)
2628{
2629 var page = AdfPage.PAGE;
2630 var positionManager = page.getPositionManager();
2631 var positionManagerIndex = this._positionManagerIndex;
2632 if (positionManagerIndex)
2633 {
2634 positionManager.removeElement(positionManagerIndex);
2635 delete this._positionManagerIndex;
2636 }
2637
2638 var ignoreShadow = !this.HasShadow();
2639 this._positionManagerIndex = positionManager.addFloatingElement(this, hintAlign, ignoreShadow);
2640}
2641
2642/**
2643 * Registers this floating element with the position manager based on a mouse
2644 * x,y position.
2645 *
2646 * @param {Object} x,y alignment location
2647 */
2648AdfDhtmlPopupWindow.prototype._positionAtMousePointer = function(pos)
2649{
2650 this._positionManagerIndex =
2651 AdfPage.PAGE.getPositionManager().addFloatingElementByPosition(this, pos);
2652}
2653
2654/**
2655 * Registers this floating element with the position manager based on screen center position.
2656 */
2657AdfDhtmlPopupWindow.prototype._positionAtScreenCenter = function()
2658{
2659 var page = AdfPage.PAGE;
2660 var positionManager = page.getPositionManager();
2661 var positionManagerIndex = this._positionManagerIndex;
2662 if (positionManagerIndex)
2663 {
2664 positionManager.removeElement(positionManagerIndex);
2665 delete this._positionManagerIndex;
2666 }
2667
2668 var ignoreShadow = !this.HasShadow();
2669 this._positionManagerIndex = positionManager.addFloatingElementByPosition(this, this.CalcAlignPosition(), ignoreShadow);
2670}
2671
2672
2673/**
2674 * Calculates the position of the dialog to center it when <b>not</b> aligned to an element.
2675 * @param {number=} leftPercentage (optional) percentage of horizontal alignment (default is .5)
2676 * @param {number=} topPercentage (optional) percentage of vertical alignment (default is .5)
2677 * @return {Object} (x, y) alignment position
2678 */
2679AdfDhtmlPopupWindow.prototype.CalcAlignPosition = function(leftPercentage, topPercentage)
2680{
2681 // if aligned by element, can not be aligned by position
2682 if (this.getAlignElement())
2683 return null;
2684
2685 if (!leftPercentage)
2686 leftPercentage = 0.5;
2687
2688 if (!topPercentage)
2689 topPercentage = 0.5;
2690
2691 var agent = AdfAgent.AGENT;
2692 var isRTL = AdfPage.PAGE.getLocaleContext().isRightToLeft();
2693
2694 var windowHeight = agent.getWindowHeight();
2695 var windowWidth = agent.getWindowWidth();
2696
2697 var docScrollTop = agent.getBrowserViewportScrollTop();
2698 var docScrollLeft = agent.getBrowserViewportScrollLeft();
2699
2700 // in FF the scrollbar height is included in the window height sometimes.
2701 // try to remove the scrollbar height from the window size
2702 if (docScrollTop > 0 && agent.getPlatform() == AdfAgent.GECKO_PLATFORM)
2703 {
2704 windowHeight = Math.min(windowHeight, agent.getDomDocument().body.clientHeight);
2705 }
2706
2707 var w = this.getWidth();
2708 var h = this.getHeight();
2709
2710 var dir = isRTL ? -1 : 1;
2711 var top = Math.max(0, Math.round((windowHeight * topPercentage) - (h / 2))) + docScrollTop;
2712 var left = Math.max(0, Math.round((windowWidth * leftPercentage) - (dir * (w / 2)))) + (dir * docScrollLeft);
2713
2714 // verify that the dialog will fit in the window with the target position; otherwise,
2715 // try to readjust with the deltas
2716 var ydelta = (windowHeight + docScrollTop) - (top + h);
2717 if (ydelta < 0)
2718 {
2719 top = Math.max(docScrollTop, top + ydelta);
2720 }
2721 var xdelta = (windowWidth + docScrollLeft) - (left + (dir * w));
2722 if (xdelta < 0)
2723 {
2724 left = Math.max(docScrollLeft, left + xdelta);
2725 }
2726
2727 var pos = {y:top, x:left};
2728
2729 return pos;
2730}
2731
2732/**
2733 * Perform a resize notification traversal when showing popup
2734 * contents so that geometry managing components are given the
2735 * opportunity to lay themselves out.
2736 *
2737 * @param {HTMLElement} target dom node
2738 */
2739AdfDhtmlPopupWindow.prototype.DoResizeNotifyDom = function(content)
2740{
2741 AdfPage.PAGE.doResizeNotifyDom(content, false);
2742}
2743
2744/**
2745 * Finds all child DOM elements that have scroll values and saves their scroll value information.
2746 *
2747 * By default, vertical scroll values are lost when DOM elements are reparented - which happens when
2748 * popups are hidden and then reshown. This method looks for child elements that have vertical
2749 * scroll values. Any scroll values found are saved so they can be restored if later the popup is
2750 * shown again.
2751 *
2752 * @param {HTMLElement} domElement the root domElement in the popup
2753 */
2754AdfDhtmlPopupWindow.prototype.SaveScrollValues = function(domElement)
2755{
2756 var childElements = domElement.getElementsByTagName("div");
2757 var childCount = childElements.length;
2758
2759 for (var i = 0; i < childCount; i++)
2760 {
2761 var currElement = childElements[i];
2762 var scrollTop = currElement.scrollTop;
2763 if (scrollTop > 0)
2764 {
2765 currElement.setAttribute(AdfDhtmlPopupWindow._SCROLL_POSITION, scrollTop);
2766 }
2767 }
2768}
2769
2770/**
2771 * Finds all child DOM elements that need their vertical scroll values restored and restores them.
2772 *
2773 * By default, vertical scroll values are lost when DOM elements are reparented - which happens when
2774 * popups are hidden and then reshown. This method looks for saved scroll values on components
2775 * (that were saved during the popup hide method, and restores any that are found.
2776 *
2777 * @param {HTMLElement} domElement the root domElement in the popup
2778 */
2779AdfDhtmlPopupWindow.prototype.RestoreScrollValues = function(domElement)
2780{
2781 var childElements = domElement.getElementsByTagName("div");
2782 var childCount = childElements.length;
2783 for (var y = 0; y < childCount; y++)
2784 {
2785 var currElement = childElements[y];
2786 var scrollPosition = currElement.getAttribute(AdfDhtmlPopupWindow._SCROLL_POSITION);
2787 if (scrollPosition && scrollPosition > 0)
2788 AdfDomUtils.setScrollTop(currElement, scrollPosition);
2789 }
2790}
2791
2792/**
2793 * Returns the clientId of the launching source if provided using
2794 * the AdfRichPopup.HINT_LAUNCH_ID popup hint. The launch source
2795 * id is passed as a popup hint by default when using showPopupBehavior.
2796 *
2797 * @return {String:null} clientId of the launching source or null if not provided
2798 */
2799AdfDhtmlPopupWindow.prototype.getLaunchSourceId = function()
2800{
2801 return this._launchSourceId;
2802}
2803
2804/**
2805 * @return {Boolean} <code>true<code> if the popup window should steal focus when
2806 * opened. The <code>AdfDhtmlPopupWindow.HINT_FOCUS</code> sets this value.
2807 */
2808AdfDhtmlPopupWindow.prototype.GetFocusOnOpen = function()
2809{
2810 return (this._focusHint ? true : false);
2811}
2812
2813/**
2814 * @param {Boolean} focusHint determines if the popup window should steal focus when
2815 * opened. The <code>AdfDhtmlPopupWindow.HINT_FOCUS</code> sets this value.
2816 */
2817AdfDhtmlPopupWindow.prototype.SetFocusOnOpen = function(focusHint)
2818{
2819 this._focusHint = focusHint;
2820}
2821
2822/**
2823 * Announces a popup status change to assistive technologies.
2824 *
2825 * @param {String} messageKey identifies the message to announce
2826 */
2827AdfDhtmlPopupWindow.prototype._announce = function(messageKey) {
2828 var page = AdfPage.PAGE;
2829 var message = page.getLookAndFeel().getTranslatedString(messageKey);
2830 page.announceToAssistiveTechnology(message);
2831}
2832
2833/**
2834 * @return {int:null} the value passed in by the <code>AdfDhtmlPopupWindow.HINT_MAX_WIDTH</code>
2835 * popup hint.
2836 */
2837AdfDhtmlPopupWindow.prototype.GetMaxWidth = function()
2838{
2839 return this._maxWidth;
2840}
2841
2842/**
2843 * Checks to see if the "ancestorNode" is a ancestor of "node"
2844 * or if they are the same. This is a popup specific version of
2845 * {@link AdfDomUtils.isAncestorOrSelf} that jumps back to the root dom
2846 * element of a open popup so that it reflects back to the origianl dom
2847 * structure before a popup is open. When the popup is open, it's content is
2848 * reparented in the document.
2849 *
2850 * @param {Node} ancestorNode some root node in the document
2851 * @param {Node} domElement target element to check if it is in the "logical" heritage of
2852 * of the ancestorNode
2853 * @return {boolean} <code>true</code> if the domElement is found within the heritage of the
2854 * ancestorNode.
2855 */
2856AdfDhtmlPopupWindow.isAncestorOrSelf = function(ancestorNode, domElement)
2857{
2858 var agent = AdfAgent.AGENT;
2859 while (domElement)
2860 {
2861 if (ancestorNode == domElement)
2862 return true;
2863
2864 if (domElement.nodeType == 1 && domElement.hasAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO))
2865 {
2866 popupId = domElement.getAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO);
2867 domElement = agent.getElementById(popupId)
2868 }
2869 else
2870 domElement = domElement.parentNode;
2871 }
2872
2873 return false;
2874}
2875
2876/**
2877 * Checks to see if the "domNode" is the root of the popupWindow.
2878 *
2879 * @param {Node} domNode target element to check if it is the root of the popupWindow
2880 * @return {boolean} <code>true</code> if the domNode is root of a popupWindow.
2881 */
2882AdfDhtmlPopupWindow.isPopupWindow = function(domNode)
2883{
2884 AdfAssert.assertDomNode(domNode);
2885
2886 if (domNode.nodeType == 1 && domNode.hasAttribute(AdfDhtmlPopupWindow.__POPUPID_EXPANDO))
2887 return true;
2888
2889 return false;
2890}
2891
2892/**
2893 * @param {String} id of the component that should establish focus when the popup is
2894 * dismissed.
2895 * @return {void}
2896 */
2897AdfDhtmlPopupWindow.prototype.setRestoreFocusId = function(id)
2898{
2899 this._restoreFocusId = id;
2900
2901 AdfLogger.LOGGER.fine("In AdfDhtmlPopupWindow#setRestoreFocusId(), _restoreFocusId set to " + this._restoreFocusId);
2902}