martin-g commented on a change in pull request #361: WICKET-6666 initial 
checkin of new ModalDialog
URL: https://github.com/apache/wicket/pull/361#discussion_r283633181
 
 

 ##########
 File path: 
wicket-extensions/src/main/java/org/apache/wicket/extensions/ajax/markup/html/modal/ModalDialog.js
 ##########
 @@ -0,0 +1,424 @@
+/*
+ * 
+ * FEATURES
+ * - When modal is closed focus is restored to the element that had it before 
the modal was opened
+ * - Focus is trapped inside the modal when using tab/shift-tab
+ * - Focus is set on the first focusable element in the modal when it is opened
+ * - On Escape or click outside the modal a button with class x-modal-close 
will be clicked
+ * - Secondary close buttons can be added and marked with 
x-modal-close-secondary. Clicking these buttons forwards the
+ *   click to the primary x-modal-close button
+ * - Aria support
+ *   - Various aria attributes added to the modal making it behave as a dialog 
to screen readers
+ *   - aria-labelledby will be added if the modal content contains an element 
with x-modal-title class
+ *   - adia-describedby will be added if the modal content contains an element 
with x-modal-description class
+ * 
+ * ENTRY POINTS
+ * - window.wicket.modal.open: function(element, options)
+ *   - element: string|dom|jquery - dom element that will be body of modal
+ *   - options: object, see description below
+ * - window.wicket.modal.close: function(element)
+ *   - element: string|dom|jquery - dom element that was specified as body of 
modal
+ *  
+ * OPTIONS
+ * validate: boolean
+ *  - when modal is opened several checks will be performed
+ *  - error when modal content does not contain an element with x-modal-close 
class
+ *  - warning when modal content does not contain an element with 
modal-description class
+ *  - error when modal does not contain any focusable elements
+ * console: object
+ *  - an object used for reporting validation errors
+ *    - must have error(object...) method
+ *    - must have warn(object...) method
+ * 
+ * ROADMAP
+ * - Set max height of content as 80% of screen, also provide option later
+ * - Open full screen on small screens - css fix only via media queries?
+ * - Support for simultaneously opened modals - testing to make sure it works 
ok or do we need to implement stack tracking
+ * 
+ */
+;
+(function($, window, document, console, undefined) {
+       'use strict';
+
+       if (window.wicket && window.wicket.modal) {
+               return;
+       }
+
+       var DATA_KEY = "modal-dialog-data";
+       var OVERLAY_SELECTOR = ".modal-dialog-overlay";
+       var CONTAINER_SELECTOR = ".modal-dialog";
+       var SCROLL_SELECTOR=".modal-dialog-scroll-area";
+       var CONTENT_SELECTOR = ".modal-dialog-content";
+       var CLOSE_SELECTOR = ".x-modal-close";
+       var SECONDARY_CLOSE_SELECTOR = ".x-modal-close-secondary";
+       
+       //
+       // UTILITY METHODS
+       //
+
+       /** Retreives id of the element, creates one if none */
+       var getOrCreateIdCounter = 0;
+       function getOrCreateId(element) {
+               if (!element.attr("id")) {
+                       element.attr("id", "modal-autoid-" + 
(getOrCreateIdCounter++));
+               }
+               return element.attr("id");
+       }
+
+       /**
+        * Resolves a value to a dom node, useful when parsing arguments passed 
to
+        * functions
+        */
+       function resolveDomNode(element) {
+               if ((typeof element) === "string") {
+                       return $(document.getElementById(element));
+               } else if (element.tagName) {
+                       return $(element);
+               } else if (element instanceof $) {
+                       return element;
+               }
+               throw new Error("Cannot resolve value: " + element + " to dom 
node");
+       }
+
+       /** Finds all elements inside container that can receive focus */
+       function findFocusable(container) {
+               var focusables = 'a[href], area[href], input:not([disabled]), 
select:not([disabled]), textarea:not([disabled]), button:not([disabled]), 
iframe, object, embed, *[tabindex], *[contenteditable]';
+               return container.find(focusables).filter(":visible");
+       }
+
+       /**
+        * Finds all elements inside the container that can receive focus via 
the
+        * tab key
+        */
+       function findTabbable(container) {
+               return findFocusable(container).not("*[tabindex=-1]");
+       }
+
+       /** Focuses the first element inside the modal */
+       function focusDefaultFocusable(container) {
+               var matches = findFocusable(container);
+               var first = matches.not(".modal-dialog-close").first();
+               if (first.length > 0) {
+                       first.focus();
+               } else {
+                       matches.first().focus();
+               }
+       }
+
+       /**
+        * Finds and clicks the close button inside the modal. Returns true if
+        * button was found.
+        */
+       function findAndClickCloseButton(container) {
+               var matches = container.find(CLOSE_SELECTOR).filter(":visible");
+               if (matches.length > 0) {
+                       matches.first().click();
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       //
+       // BEHAVIORS
+       //
+       // Behaviors are event listeners that get called from open and close
+       // methods, they allow various aspects of code such as focus management 
and
+       // aria attribute management to be decoupled from each other making the
+       // overall code cleaner and easier to maintain.
+       //
+       // The structure of a behavior is an object with the following 
properties:
+       // initialize: function()
+       // - called before first modal is opened
+       // destroy: function()
+       // - called after last modal is closed
+       // prepare: function(overlayElement, contentElement, data)
+       // - called after overlay dom is constructed, but before it is inserted
+       // - into main dom
+       // open: function(overlayElement, contentElement, data)
+       // - called after overlayElement is inserted into main dom
+       // close: function(overlayElement, contentElement, data)
+       // - called after overlayElement is removed from main dom
+
+       //Scroll settings to remember for ios scroll to top issue. Currently, 
ios allows body 
+       //scrolling unless body is set to position: fixed, which causes the 
window to scroll to top.
+    var scrollTop;
+
+       /** Behavior that appends a css class to body as long as any modal is 
open */
+       var appendBodyClassBehavior = {
+
+               initialize : function() {
+                       var body = $("body");
+                       body.addClass("modal-dialog-open 
modal-dialog-no-scroll");
+
+                       scrollTop = $(window).scrollTop();
+
+            if (!!navigator.platform && 
/iPad|iPhone|iPod/.test(navigator.platform)) {
+                body.addClass("modal-dialog-open-ios");
+            }
+               },
+               terminate : function() {
+                       $("body").removeClass("modal-dialog-open 
modal-dialog-no-scroll modal-dialog-open-ios");
+            if (!!navigator.platform && 
/iPad|iPhone|iPod/.test(navigator.platform)) {
+               $(window).scrollTop(scrollTop);
+            }
+               }
+       };
+
+       /**
+        * Behavior that memorizes the focussed element when dialog is opened, 
and
+        * returns focus to it when dialog is closed
+        */
+       var returnFocusOnCloseBehavior = {
+               open : function(overlay, element, data) {
+                       data.opener = document.activeElement;
+                       if (data.options.validate) {
+                               if (!data.opener || $(data.opener).is("body")) {
+                                       data.options.console.error("Error 
saving focused element when opening the modal, it is either none or body: ",
 
 Review comment:
   What is the idea behind `options.console` ? To be able to use either 
`window.console`, `Wicket.Log` or a third party one ?

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to