diff --git a/build/aui-dialog/aui-dialog-debug.js b/build/aui-dialog/aui-dialog-debug.js index e01f0d8e5f7..71e3d9ed6f4 100644 --- a/build/aui-dialog/aui-dialog-debug.js +++ b/build/aui-dialog/aui-dialog-debug.js @@ -23,12 +23,12 @@ var Lang = A.Lang, BOUNDING_BOX = 'boundingBox', BUTTON = 'button', BUTTONS = 'buttons', + CLICK_OUTSIDE = 'clickoutside', CLOSE = 'close', CLOSETHICK = 'closethick', CONSTRAIN_TO_VIEWPORT = 'constrain2view', DATA_TABINDEX = 'data-tabindex', DD = 'dd', - DEFAULT = 'default', DESTROY_ON_CLOSE = 'destroyOnClose', DIALOG = 'dialog', DOT = '.', @@ -36,6 +36,7 @@ var Lang = A.Lang, DRAG_GUTTER = 5, DRAG_INSTANCE = 'dragInstance', DRAGGABLE = 'draggable', + FOCUS_OUTSIDE = 'focusoutside', FOOTER_CONTENT = 'footerContent', HD = 'hd', HEIGHT = 'height', @@ -52,7 +53,9 @@ var Lang = A.Lang, TAB_INDEX = 'tabIndex', USE_ARIA = 'useARIA', VIEWPORT_REGION = 'viewportRegion', + VISIBLE = 'visible', WIDTH = 'width', + Z_INDEX = 'zIndex', EV_RESIZE = 'resize:resize', EV_RESIZE_END = 'resize:end', @@ -61,8 +64,6 @@ var Lang = A.Lang, CSS_DIALOG = getCN(DIALOG), CSS_DIALOG_HD = getCN(DIALOG, HD), - CSS_ICON_LOADING = getCN(ICON, LOADING), - CSS_PREFIX = getCN(DD), NODE_BLANK_TEXT = DOC.createTextNode(''); @@ -119,15 +120,6 @@ var Dialog = function(config) { A.mix( Dialog, { - /** - * Static property provides a string to identify the class. - * - * @property Dialog.NAME - * @type String - * @static - */ - NAME: DIALOG, - /** * Static property used to define the default attribute * configuration for the Dialog. @@ -171,8 +163,8 @@ A.mix( * @type Array */ buttons: { - value: [], - validator: isArray + validator: isArray, + value: [] }, /** @@ -196,8 +188,8 @@ A.mix( */ constrain2view: { setter: '_setConstrain2view', - value: false, - validator: isBoolean + validator: isBoolean, + value: false }, /** @@ -210,19 +202,8 @@ A.mix( * @type boolean */ destroyOnClose: { - value: false, - validator: isBoolean - }, - - /** - * Boolean specifying if the Panel should be draggable. - * - * @attribute draggable - * @default true - * @type boolean - */ - draggable: { - value: true + validator: isBoolean, + value: false }, /** @@ -238,15 +219,26 @@ A.mix( return A.merge( { bubbleTargets: instance, - node: instance.get(BOUNDING_BOX), - handles: [ DOT + CSS_DIALOG_HD ] + handles: [DOT + CSS_DIALOG_HD], + node: instance.get(BOUNDING_BOX) }, val || {} ); }, - writeOnce: true, + validator: isObject, value: {}, - validator: isObject + writeOnce: true + }, + + /** + * Boolean specifying if the Panel should be draggable. + * + * @attribute draggable + * @default true + * @type boolean + */ + draggable: { + value: true }, /** @@ -262,6 +254,33 @@ A.mix( value: null }, + /** + * @attribute focusOn + * @type array + * + * @description An array of objects corresponding to the nodes and events that will trigger a re-focus back on the widget. + * The implementer can supply an array of objects, with each object having the following properties: + *
eventName: (string, required): The eventName to listen to.
+ *node: (Y.Node, optional): The Y.Node that will fire the event (defaults to the boundingBox of the widget)
+ *By default, this attribute consists of two objects which will cause the widget to re-focus if anything + * outside the widget is clicked on or focussed upon.
+ */ + focusOn: { + validator: A.Lang.isArray, + valueFn: function() { + return [ + { + // node: this.get(BOUNDING_BOX), + eventName: CLICK_OUTSIDE + }, + { + //node: this.get(BOUNDING_BOX), + eventName: FOCUS_OUTSIDE + } + ]; + } + }, + /** * True if the Panel should be displayed in a modal fashion, * automatically creating a transparent mask over the document that @@ -278,6 +297,17 @@ A.mix( value: false }, + /** + * Boolean specifying if the Panel should be resizable. + * + * @attribute resizable + * @default true + * @type boolean + */ + resizable: { + value: true + }, + /** * Resize configuration. * @@ -290,24 +320,24 @@ A.mix( return A.merge( { + after: { + end: A.bind(instance._syncResizableDimentions, instance), + resize: A.bind(instance._syncResizableDimentions, instance) + }, bubbleTargets: instance, + constrain2view: true, handles: 'r,br,b', minHeight: 100, minWidth: 200, - constrain2view: true, node: instance.get(BOUNDING_BOX), - proxy: true, - after: { - end: A.bind(instance._syncResizableDimentions, instance), - resize: A.bind(instance._syncResizableDimentions, instance) - } + proxy: true }, val || {} ); }, - writeOnce: true, + validator: isObject, value: {}, - validator: isObject + writeOnce: true }, /** @@ -323,17 +353,6 @@ A.mix( value: null }, - /** - * Boolean specifying if the Panel should be resizable. - * - * @attribute resizable - * @default true - * @type boolean - */ - resizable: { - value: true - }, - /** * Iftrue
give stacking habilities to the Dialog.
*
@@ -342,11 +361,11 @@ A.mix(
* @type boolean
*/
stack: {
- value: true,
setter: function(v) {
return this._setStack(v);
},
- validator: isBoolean
+ validator: isBoolean,
+ value: true
},
/**
@@ -360,11 +379,22 @@ A.mix(
close: 'Close dialog'
}
}
- }
+ },
+
+ /**
+ * Static property provides a string to identify the class.
+ *
+ * @property Dialog.NAME
+ * @type String
+ * @static
+ */
+ NAME: DIALOG
}
);
Dialog.prototype = {
+ _uiHandlesModal: null,
+
/**
* Construction logic executed during Dialog instantiation. Lifecycle.
*
@@ -374,9 +404,9 @@ Dialog.prototype = {
initializer: function(config) {
var instance = this;
- var icons = instance.get(ICONS);
- var close = instance.get(CLOSE);
var buttons = instance.get(BUTTONS);
+ var close = instance.get(CLOSE);
+ var icons = instance.get(ICONS);
if (buttons && buttons.length && !instance.get(FOOTER_CONTENT)) {
instance.set(FOOTER_CONTENT, NODE_BLANK_TEXT);
@@ -386,12 +416,12 @@ Dialog.prototype = {
var closeId = A.guid();
var closeConfig = {
- icon: CLOSETHICK,
- id: closeId,
handler: {
- fn: instance.close,
- context: instance
+ context: instance,
+ fn: instance.close
},
+ icon: CLOSETHICK,
+ id: closeId,
title: instance.get('strings').close
};
@@ -431,6 +461,10 @@ Dialog.prototype = {
bindUI: function() {
var instance = this;
+ if (instance.get(MODAL)) {
+ instance.after('focusOnChange', instance._afterFocusOnChange());
+ }
+
instance._bindLazyComponents();
},
@@ -445,16 +479,19 @@ Dialog.prototype = {
var instance = this;
if (instance.get(USE_ARIA)) {
- instance.plug(A.Plugin.Aria, {
- attributes: {
- visible: {
- ariaName: 'hidden',
- format: function(value) {
- return !value;
+ instance.plug(
+ A.Plugin.Aria,
+ {
+ attributes: {
+ visible: {
+ ariaName: 'hidden',
+ format: function(value) {
+ return !value;
+ }
}
}
}
- });
+ );
}
},
@@ -468,9 +505,8 @@ Dialog.prototype = {
destructor: function() {
var instance = this;
- var boundingBox = instance.get(BOUNDING_BOX);
+ A.Event.purgeElement(instance.get(BOUNDING_BOX), true);
- A.Event.purgeElement(boundingBox, true);
A.DialogManager.remove(instance);
},
@@ -486,35 +522,125 @@ Dialog.prototype = {
var viewportRegion = A.getDoc().get(VIEWPORT_REGION);
- instance.move([ viewportRegion.left + toInt(offsetLeft), viewportRegion.top + toInt(offsetTop) ]);
+ var viewportLeft = viewportRegion.left + toInt(offsetLeft);
+ var viewportTop = viewportRegion.top + toInt(offsetTop);
+
+ instance.move([viewportLeft, viewportTop]);
},
/**
- * Bind a mouseenter
listener to the boundingBox
- * to invoke the
- * _initLazyComponents.
- * Performance reasons.
+ * Fires the close event to close the Dialog.
*
- * @method _bindLazyComponents
- * @private
+ * @method close
*/
- _bindLazyComponents: function() {
+ close: function() {
var instance = this;
- var boundingBox = instance.get(BOUNDING_BOX);
+ instance.fire('close');
+ },
- boundingBox.on('mouseenter', A.bind(instance._initLazyComponents, instance));
+ /**
+ * Fires after the value of the
+ * constrain2view attribute change.
+ *
+ * @method _afterConstrain2viewChange
+ * @param {EventFacade} event
+ * @protected
+ */
+ _afterConstrain2viewChange: function(event) {
+ var instance = this;
+
+ instance._updateDDConstrain2view(
+ instance.get(DRAG_INSTANCE)
+ );
},
/**
- * Fires the close event to close the Dialog.
+ * Fires after the value of the
+ * draggable attribute change.
*
- * @method close
+ * @method _afterDraggableChange
+ * @param {EventFacade} event
+ * @protected
*/
- close: function() {
+ _afterDraggableChange: function(event) {
var instance = this;
- instance.fire('close');
+ instance.set(DRAG_INSTANCE, null);
+ },
+
+ /**
+ * Fires after the value of the
+ * dragInstance attribute change.
+ *
+ * @method _afterDragInstanceChange
+ * @param {EventFacade} event
+ * @protected
+ */
+ _afterDragInstanceChange: function(event) {
+ var instance = this;
+
+ var prevVal = event.prevVal;
+
+ if (prevVal) {
+ prevVal.destroy();
+ }
+ },
+
+ /**
+ * Handles the drag start event
+ * If "constrain2view" property is set to false this function will constrain the dialog to a region
+ * in order to prevent moving it to unreachable position
+ *
+ * @method _afterDragStart
+ * @param {EventFacade} event
+ * @protected
+ */
+ _afterDragStart: function(event) {
+ var instance = this;
+
+ var constrain2view = instance.get(CONSTRAIN_TO_VIEWPORT);
+
+ if (!constrain2view) {
+ var dragInstance = instance.get(DRAG_INSTANCE);
+
+ var dragNode = dragInstance.get('dragNode');
+
+ var dragNodeRegion = dragNode.get('region');
+ var viewportRegion = dragNode.get('viewportRegion');
+
+ var defaultOffset = [0, 0];
+
+ var deltaXY = dragInstance.deltaXY || defaultOffset;
+ var mouseXY = dragInstance.mouseXY || defaultOffset;
+
+ dragInstance.plug(
+ A.Plugin.DDConstrained,
+ {
+ constrain: {
+ bottom: viewportRegion.bottom + (dragNodeRegion.height - deltaXY[1]) - DRAG_GUTTER,
+ left: viewportRegion.left - deltaXY[0] + DRAG_GUTTER,
+ right: viewportRegion.right + (dragNodeRegion.right - mouseXY[0]) + DRAG_GUTTER,
+ top: viewportRegion.top - deltaXY[1] + DRAG_GUTTER
+ }
+ }
+ );
+ }
+ },
+
+ /**
+ * Default function called when focusOn Attribute is changed. Remove existing listeners and create new listeners.
+ *
+ * @method _afterFocusOnChange
+ */
+ _afterFocusOnChange : function(event) {
+ var instance = this;
+
+ instance._detachUIHandlesModal();
+
+ if (instance.get(VISIBLE)) {
+ instance._attachUIHandlesModal();
+ }
},
/**
@@ -535,6 +661,99 @@ Dialog.prototype = {
instance.get(IO);
},
+ /**
+ * Fires after the value of the
+ * resizable attribute change.
+ *
+ * @method _afterResizableChange
+ * @param {EventFacade} event
+ * @protected
+ */
+ _afterResizableChange: function(event) {
+ var instance = this;
+
+ instance.set(RESIZABLE_INSTANCE, null);
+ },
+
+ /**
+ * Fires after the value of the
+ * resizableInstance attribute change.
+ *
+ * @method _afterResizableInstanceChange
+ * @param {EventFacade} event
+ * @protected
+ */
+ _afterResizableInstanceChange: function(event) {
+ var instance = this;
+
+ var prevVal = event.prevVal;
+
+ if (prevVal) {
+ prevVal.destroy();
+ }
+ },
+
+ /**
+ * Attaches UI Listeners for "clickoutside" and "focusoutside" on the widget. When these events occur, and the widget is modal, focus is shifted back onto the widget.
+ *
+ * @method _attachUIHandlesModal
+ */
+ _attachUIHandlesModal: function() {
+ var instance = this;
+
+ var boundingBox = instance.get(BOUNDING_BOX);
+ var focusOn = instance.get('focusOn');
+ var maskNode = instance.get('maskNode');
+
+ var focus = A.bind(instance._focus, instance);
+
+ var uiHandles = [];
+
+ for (var i = 0; i < focusOn.length; i++) {
+ var ev = focusOn[i].eventName;
+ var keyCode = focusOn[i].keyCode;
+ var node = focusOn[i].node;
+
+ //no keycode or node defined
+ if (!node && !keyCode && ev) {
+ uiHandles.push(boundingBox.on(ev, focus));
+ }
+
+ //node defined, no keycode (not a keypress)
+ else if (node && !keyCode && ev) {
+ uiHandles.push(node.on(ev, focus));
+ }
+
+ //node defined, keycode defined, event defined (its a key press)
+ else if (node && keyCode && ev) {
+ uiHandles.push(node.on(ev, focus, keyCode));
+ }
+
+ else {
+ A.log('focusOn ATTR Error: The event with name "' + ev + '" could not be attached.');
+ }
+ }
+
+ instance._uiHandlesModal = uiHandles;
+ },
+
+ /**
+ * Bind a mouseenter
listener to the boundingBox
+ * to invoke the
+ * _initLazyComponents.
+ * Performance reasons.
+ *
+ * @method _bindLazyComponents
+ * @private
+ */
+ _bindLazyComponents: function() {
+ var instance = this;
+
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ boundingBox.on('mouseenter', A.bind(instance._initLazyComponents, instance));
+ },
+
/**
* Handles the close event logic.
*
@@ -553,6 +772,41 @@ Dialog.prototype = {
}
},
+ /**
+ * Detaches all UI Listeners that were set in _attachUIHandlesModal from the widget.
+ *
+ * @method _detachUIHandlesModal
+ */
+ _detachUIHandlesModal: function() {
+ var instance = this;
+
+ A.each(
+ instance._uiHandlesModal,
+ function(h) {
+ h.detach();
+ }
+ );
+
+ instance._uiHandlesModal = null;
+ },
+
+ /**
+ * Provides mouse and tab focus to the widget's bounding box.
+ *
+ * @method _focus
+ */
+ _focus: function(event) {
+ var instance = this;
+
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ var oldTI = boundingBox.get('tabIndex');
+
+ boundingBox.set('tabIndex', oldTI >= 0 ? oldTI : 0);
+
+ instance.focus();
+ },
+
/**
* Render the buttons on the footer of the Dialog.
*
@@ -614,7 +868,7 @@ Dialog.prototype = {
if (icons) {
var closeThick = icons.item(instance._closeId) || null;
- if (closeThick){
+ if (closeThick) {
instance.aria.setAttribute('controls', instance.get('id'), closeThick.get(BOUNDING_BOX));
}
}
@@ -694,8 +948,8 @@ Dialog.prototype = {
_syncResizableDimentions: function(event) {
var instance = this;
- var type = event.type;
var info = event.info;
+ var type = event.type;
if ((type === EV_RESIZE_END) ||
((type === EV_RESIZE) && !event.currentTarget.get(PROXY))) {
@@ -720,133 +974,14 @@ Dialog.prototype = {
constrain2view: instance.get(CONSTRAIN_TO_VIEWPORT)
}
);
- },
-
- /**
- * Fires after the value of the
- * constrain2view attribute change.
- *
- * @method _afterConstrain2viewChange
- * @param {EventFacade} event
- * @protected
- */
- _afterConstrain2viewChange: function(event) {
- var instance = this;
-
- instance._updateDDConstrain2view(
- instance.get(DRAG_INSTANCE)
- );
- },
-
- /**
- * Fires after the value of the
- * draggable attribute change.
- *
- * @method _afterDraggableChange
- * @param {EventFacade} event
- * @protected
- */
- _afterDraggableChange: function(event) {
- var instance = this;
-
- instance.set(DRAG_INSTANCE, null);
- },
-
- /**
- * Fires after the value of the
- * dragInstance attribute change.
- *
- * @method _afterDragInstanceChange
- * @param {EventFacade} event
- * @protected
- */
- _afterDragInstanceChange: function(event) {
- var instance = this;
-
- if (event.prevVal) {
- event.prevVal.destroy();
- }
- },
-
- /**
- * Handles the drag start event
- * If "constrain2view" property is set to false this function will constrain the dialog to a region
- * in order to prevent moving it to unreachable position
- *
- * @method _afterDragStart
- * @param {EventFacade} event
- * @protected
- */
- _afterDragStart: function(event) {
- var instance = this;
-
- var constrain2view = instance.get(CONSTRAIN_TO_VIEWPORT);
-
- if (!constrain2view) {
- var dragInstance = instance.get(DRAG_INSTANCE);
-
- var dragNode = dragInstance.get('dragNode');
-
- var viewportRegion = dragNode.get('viewportRegion');
-
- var dragNodeRegion = dragNode.get('region');
-
- var defaultOffset = [0, 0];
-
- var deltaXY = dragInstance.deltaXY || defaultOffset;
-
- var mouseXY = dragInstance.mouseXY || defaultOffset;
-
- dragInstance.plug(
- A.Plugin.DDConstrained,
- {
- constrain: {
- bottom: viewportRegion.bottom + (dragNodeRegion.height - deltaXY[1]) - DRAG_GUTTER,
- left: viewportRegion.left - deltaXY[0] + DRAG_GUTTER,
- right: viewportRegion.right + (dragNodeRegion.right - mouseXY[0]) + DRAG_GUTTER,
- top: viewportRegion.top - deltaXY[1] + DRAG_GUTTER
- }
- }
- );
- }
- },
-
- /**
- * Fires after the value of the
- * resizable attribute change.
- *
- * @method _afterResizableChange
- * @param {EventFacade} event
- * @protected
- */
- _afterResizableChange: function(event) {
- var instance = this;
-
- instance.set(RESIZABLE_INSTANCE, null);
- },
-
- /**
- * Fires after the value of the
- * resizableInstance attribute change.
- *
- * @method _afterResizableInstanceChange
- * @param {EventFacade} event
- * @protected
- */
- _afterResizableInstanceChange: function(event) {
- var instance = this;
-
- if (event.prevVal) {
- event.prevVal.destroy();
- }
}
};
A.Dialog = A.Component.create(
{
- NAME: DIALOG,
+ AUGMENTS: [Dialog, A.WidgetPosition, A.WidgetStack, A.WidgetPositionAlign, A.WidgetPositionConstrain],
EXTENDS: A.Panel,
- AUGMENTS: [Dialog, A.WidgetPosition, A.WidgetStack, A.WidgetPositionAlign, A.WidgetPositionConstrain]
+ NAME: DIALOG
}
);
@@ -902,21 +1037,6 @@ DialogManager.after(
A.mix(
DialogManager,
{
- /**
- * Find the Widget instance based on a child
- * element.
- *
- * @method findByChild
- * @for DialogManager
- * @param {Node | String} child Child node of the Dialog.
- * @return {Widget}
- */
- findByChild: function(child) {
- return A.Widget.getByNode(
- A.one(child).ancestor(DOT + CSS_DIALOG, true)
- );
- },
-
/**
* Invoke the close method from
* the Dialog which contains the child
element.
Invoke the start method
* from the IOPlugin plugged on this Dialog
diff --git a/build/aui-dialog/aui-dialog-min.js b/build/aui-dialog/aui-dialog-min.js
index 733ec60ec60..1ff4656db2e 100644
--- a/build/aui-dialog/aui-dialog-min.js
+++ b/build/aui-dialog/aui-dialog-min.js
@@ -1,2 +1,2 @@
-AUI.add("aui-dialog",function(p){var ac=p.Lang,v=p.Object,K=ac.isBoolean,C=ac.isArray,P=ac.isObject,j=ac.toInt,E=p.WidgetStdMod,M=p.config.doc,u="",W="boundingBox",Z="button",c="buttons",X="close",Q="closethick",L="constrain2view",V="data-tabindex",d="dd",S="default",Y="destroyOnClose",x="dialog",y=".",D="dragConfig",aa=5,l="dragInstance",n="draggable",g="footerContent",T="hd",J="height",H="icon",b="icons",s="io",w="loading",B="modal",ab="proxy",e="resizable",G="resizableConfig",m="resizableInstance",F="stack",R="tabIndex",i="useARIA",O="viewportRegion",f="width",q="resize:resize",N="resize:end",h=p.getClassName,z=h(x),a=h(x,T),t=h(H,w),o=h(d),I=M.createTextNode("");var r=function(A){if(!p.DialogMask){p.DialogMask=new p.OverlayMask({visible:true}).render();}};p.mix(r,{NAME:x,ATTRS:{bodyContent:{value:I},buttons:{value:[],validator:C},close:{value:true},constrain2view:{setter:"_setConstrain2view",value:false,validator:K},destroyOnClose:{value:false,validator:K},draggable:{value:true},dragConfig:{setter:function(ad){var A=this;return p.merge({bubbleTargets:A,node:A.get(W),handles:[y+a]},ad||{});},writeOnce:true,value:{},validator:P},dragInstance:{setter:"_setDragInstance",value:null},modal:{lazyAdd:false,validator:K,value:false},resizableConfig:{setter:function(ad){var A=this;return p.merge({bubbleTargets:A,handles:"r,br,b",minHeight:100,minWidth:200,constrain2view:true,node:A.get(W),proxy:true,after:{end:p.bind(A._syncResizableDimentions,A),resize:p.bind(A._syncResizableDimentions,A)}},ad||{});},writeOnce:true,value:{},validator:P},resizableInstance:{setter:"_setResizableInstance",value:null},resizable:{value:true},stack:{value:true,setter:function(A){return this._setStack(A);},validator:K},strings:{value:{close:"Close dialog"}}}});r.prototype={initializer:function(ae){var A=this;var ag=A.get(b);var ai=A.get(X);var ah=A.get(c);if(ah&&ah.length&&!A.get(g)){A.set(g,I);}if(ai){var af=p.guid();var ad={icon:Q,id:af,handler:{fn:A.close,context:A},title:A.get("strings").close};if(ag){ag.push(ad);}A.set(b,ag);A._closeId=af;}A.publish("close",{defaultFn:A._close});A.addTarget(p.DialogManager);A.after("constrain2viewChange",A._afterConstrain2viewChange);A.after("drag:start",A._afterDragStart);A.after("draggableChange",A._afterDraggableChange);A.after("dragInstanceChange",A._afterDragInstanceChange);A.after("render",A._afterRenderer);A.after("resizableChange",A._afterResizableChange);A.after("resizableInstanceChange",A._afterResizableInstanceChange);},bindUI:function(){var A=this;A._bindLazyComponents();},syncUI:function(){var A=this;if(A.get(i)){A.plug(p.Plugin.Aria,{attributes:{visible:{ariaName:"hidden",format:function(ad){return !ad;}}}});}},destructor:function(){var A=this;var ad=A.get(W);p.Event.purgeElement(ad,true);p.DialogManager.remove(A);},alignToViewport:function(ae,ad){var A=this;var af=p.getDoc().get(O);A.move([af.left+j(ae),af.top+j(ad)]);},_bindLazyComponents:function(){var A=this;var ad=A.get(W);ad.on("mouseenter",p.bind(A._initLazyComponents,A));},close:function(){var A=this;A.fire("close");},_afterRenderer:function(ad){var A=this;A._initButtons();A.get(F);A.get(s);},_close:function(){var A=this;if(A.get(Y)){A.destroy();}else{A.hide();}},_initButtons:function(){var A=this;var ae=A.get(c);if(ae.length){var ad=new p.Toolbar({children:ae});ad._DEFAULT_CONTEXT=A;ad.render(A.footerNode);A.fire("contentUpdate");A.buttons=ad;}},_initLazyComponents:function(){var A=this;A.get(l);A.get(m);},_setDefaultARIAValues:function(){var A=this;var ad=A.icons;if(!A.get(i)){return;}A.aria.setRole("dialog",A.get(W));if(ad){var ae=ad.item(A._closeId)||null;if(ae){A.aria.setAttribute("controls",A.get("id"),ae.get(W));}}},_setDragInstance:function(ad){var A=this;if(A.get(n)){ad=new p.DD.Drag(A.get(D));A._updateDDConstrain2view(ad);}return ad;},_setResizableInstance:function(ad){var A=this;if(A.get(e)){ad=new p.Resize(A.get(G));}return ad;},_setStack:function(ad){var A=this;if(ad){p.DialogManager.register(A);}else{p.DialogManager.remove(A);}return ad;},_syncResizableDimentions:function(ae){var A=this;var ad=ae.type;var af=ae.info;if((ad===N)||((ad===q)&&!ae.currentTarget.get(ab))){A.set(J,af.offsetHeight);A.set(f,af.offsetWidth);}},_updateDDConstrain2view:function(ad){var A=this;ad.plug(p.Plugin.DDConstrained,{constrain2view:A.get(L)});},_afterConstrain2viewChange:function(ad){var A=this;A._updateDDConstrain2view(A.get(l));},_afterDraggableChange:function(ad){var A=this;A.set(l,null);},_afterDragInstanceChange:function(ad){var A=this;if(ad.prevVal){ad.prevVal.destroy();}},_afterDragStart:function(A){var al=this;var ah=al.get(L);if(!ah){var ak=al.get(l);var af=ak.get("dragNode");var ag=af.get("viewportRegion");var ae=af.get("region");var aj=[0,0];var ad=ak.deltaXY||aj;var ai=ak.mouseXY||aj;ak.plug(p.Plugin.DDConstrained,{constrain:{bottom:ag.bottom+(ae.height-ad[1])-aa,left:ag.left-ad[0]+aa,right:ag.right+(ae.right-ai[0])+aa,top:ag.top-ad[1]+aa}});}},_afterResizableChange:function(ad){var A=this;A.set(m,null);},_afterResizableInstanceChange:function(ad){var A=this;if(ad.prevVal){ad.prevVal.destroy();}}};p.Dialog=p.Component.create({NAME:x,EXTENDS:p.Panel,AUGMENTS:[r,p.WidgetPosition,p.WidgetStack,p.WidgetPositionAlign,p.WidgetPositionConstrain]});var k=new p.OverlayManager({zIndexBase:1000});var U={};k._MODALS=U;k.after(["dialog:destroy","dialog:modalChange","dialog:render","dialog:visibleChange"],function(ad){var A=ad.target;if(A){var ae=A.get("id");if(ad.type!=="dialog:destroy"&&A.get("visible")&&A.get("modal")){U[ae]=true;p.DialogMask.show();k._blockIFrameFocus();}else{delete U[ae];if(v.isEmpty(U)){p.DialogMask.hide();k._unblockIFrameFocus();}}}});p.mix(k,{findByChild:function(A){return p.Widget.getByNode(p.one(A).ancestor(y+z,true));},closeByChild:function(A){return k.findByChild(A).close();},refreshByChild:function(ad){var A=k.findByChild(ad);if(A&&A.io){A.io.start();}},_blockIFrameFocus:function(){p.all("iframe").each(function(){if(this.ancestor(y+z)===null){if(!this.hasAttribute(V)){this.setAttribute(V,this.get(R));
-}this.set(R,-1);}});},_unblockIFrameFocus:function(){p.all("iframe").each(function(){if(this.hasAttribute(V)){this.set(R,this.getAttribute(V));}});}});p.DialogManager=k;},"@VERSION@",{requires:["aui-panel","dd-constrain","aui-button-item","aui-overlay-manager","aui-overlay-mask","aui-io-plugin","aui-resize"],skinnable:true});
\ No newline at end of file
+AUI.add("aui-dialog",function(p){var ad=p.Lang,u=p.Object,J=ad.isBoolean,B=ad.isArray,O=ad.isObject,k=ad.toInt,D=p.WidgetStdMod,L=p.config.doc,t="",V="boundingBox",Y="button",c="buttons",T="clickoutside",W="close",P="closethick",K="constrain2view",U="data-tabindex",d="dd",X="destroyOnClose",w="dialog",x=".",C="dragConfig",Z=5,m="dragInstance",o="draggable",g="focusoutside",h="footerContent",R="hd",I="height",G="icon",b="icons",s="io",v="loading",z="modal",ab="proxy",e="resizable",F="resizableConfig",n="resizableInstance",E="stack",Q="tabIndex",j="useARIA",N="viewportRegion",aa="visible",f="width",ac="zIndex",q="resize:resize",M="resize:end",i=p.getClassName,y=i(w),a=i(w,R),H=L.createTextNode("");var r=function(A){if(!p.DialogMask){p.DialogMask=new p.OverlayMask({visible:true}).render();}};p.mix(r,{ATTRS:{bodyContent:{value:H},buttons:{validator:B,value:[]},close:{value:true},constrain2view:{setter:"_setConstrain2view",validator:J,value:false},destroyOnClose:{validator:J,value:false},dragConfig:{setter:function(ae){var A=this;return p.merge({bubbleTargets:A,handles:[x+a],node:A.get(V)},ae||{});},validator:O,value:{},writeOnce:true},draggable:{value:true},dragInstance:{setter:"_setDragInstance",value:null},focusOn:{validator:p.Lang.isArray,valueFn:function(){return[{eventName:T},{eventName:g}];}},modal:{lazyAdd:false,validator:J,value:false},resizable:{value:true},resizableConfig:{setter:function(ae){var A=this;return p.merge({after:{end:p.bind(A._syncResizableDimentions,A),resize:p.bind(A._syncResizableDimentions,A)},bubbleTargets:A,constrain2view:true,handles:"r,br,b",minHeight:100,minWidth:200,node:A.get(V),proxy:true},ae||{});},validator:O,value:{},writeOnce:true},resizableInstance:{setter:"_setResizableInstance",value:null},stack:{setter:function(A){return this._setStack(A);},validator:J,value:true},strings:{value:{close:"Close dialog"}}},NAME:w});r.prototype={_uiHandlesModal:null,initializer:function(af){var A=this;var ai=A.get(c);var aj=A.get(W);var ah=A.get(b);if(ai&&ai.length&&!A.get(h)){A.set(h,H);}if(aj){var ag=p.guid();var ae={handler:{context:A,fn:A.close},icon:P,id:ag,title:A.get("strings").close};if(ah){ah.push(ae);}A.set(b,ah);A._closeId=ag;}A.publish("close",{defaultFn:A._close});A.addTarget(p.DialogManager);A.after("constrain2viewChange",A._afterConstrain2viewChange);A.after("drag:start",A._afterDragStart);A.after("draggableChange",A._afterDraggableChange);A.after("dragInstanceChange",A._afterDragInstanceChange);A.after("render",A._afterRenderer);A.after("resizableChange",A._afterResizableChange);A.after("resizableInstanceChange",A._afterResizableInstanceChange);},bindUI:function(){var A=this;if(A.get(z)){A.after("focusOnChange",A._afterFocusOnChange());}A._bindLazyComponents();},syncUI:function(){var A=this;if(A.get(j)){A.plug(p.Plugin.Aria,{attributes:{visible:{ariaName:"hidden",format:function(ae){return !ae;}}}});}},destructor:function(){var A=this;p.Event.purgeElement(A.get(V),true);p.DialogManager.remove(A);},alignToViewport:function(ah,ag){var ae=this;var ai=p.getDoc().get(N);var af=ai.left+k(ah);var A=ai.top+k(ag);ae.move([af,A]);},close:function(){var A=this;A.fire("close");},_afterConstrain2viewChange:function(ae){var A=this;A._updateDDConstrain2view(A.get(m));},_afterDraggableChange:function(ae){var A=this;A.set(m,null);},_afterDragInstanceChange:function(ae){var A=this;var af=ae.prevVal;if(af){af.destroy();}},_afterDragStart:function(A){var am=this;var ai=am.get(K);if(!ai){var al=am.get(m);var ag=al.get("dragNode");var af=ag.get("region");var ah=ag.get("viewportRegion");var ak=[0,0];var ae=al.deltaXY||ak;var aj=al.mouseXY||ak;al.plug(p.Plugin.DDConstrained,{constrain:{bottom:ah.bottom+(af.height-ae[1])-Z,left:ah.left-ae[0]+Z,right:ah.right+(af.right-aj[0])+Z,top:ah.top-ae[1]+Z}});}},_afterFocusOnChange:function(ae){var A=this;A._detachUIHandlesModal();if(A.get(aa)){A._attachUIHandlesModal();}},_afterRenderer:function(ae){var A=this;A._initButtons();A.get(E);A.get(s);},_afterResizableChange:function(ae){var A=this;A.set(n,null);},_afterResizableInstanceChange:function(ae){var A=this;var af=ae.prevVal;if(af){af.destroy();}},_attachUIHandlesModal:function(){var aj=this;var af=aj.get(V);var al=aj.get("focusOn");var ah=aj.get("maskNode");var am=p.bind(aj._focus,aj);var A=[];for(var ag=0;ag eventName: (string, required): The eventName to listen to. node: (Y.Node, optional): The Y.Node that will fire the event (defaults to the boundingBox of the widget) By default, this attribute consists of two objects which will cause the widget to re-focus if anything
+ * outside the widget is clicked on or focussed upon. Invoke the close method from
* the Dialog which contains the Invoke the start method
* from the IOPlugin plugged on this Dialog
true
give stacking habilities to the Dialog.
*
@@ -342,11 +361,11 @@ A.mix(
* @type boolean
*/
stack: {
- value: true,
setter: function(v) {
return this._setStack(v);
},
- validator: isBoolean
+ validator: isBoolean,
+ value: true
},
/**
@@ -360,11 +379,22 @@ A.mix(
close: 'Close dialog'
}
}
- }
+ },
+
+ /**
+ * Static property provides a string to identify the class.
+ *
+ * @property Dialog.NAME
+ * @type String
+ * @static
+ */
+ NAME: DIALOG
}
);
Dialog.prototype = {
+ _uiHandlesModal: null,
+
/**
* Construction logic executed during Dialog instantiation. Lifecycle.
*
@@ -374,9 +404,9 @@ Dialog.prototype = {
initializer: function(config) {
var instance = this;
- var icons = instance.get(ICONS);
- var close = instance.get(CLOSE);
var buttons = instance.get(BUTTONS);
+ var close = instance.get(CLOSE);
+ var icons = instance.get(ICONS);
if (buttons && buttons.length && !instance.get(FOOTER_CONTENT)) {
instance.set(FOOTER_CONTENT, NODE_BLANK_TEXT);
@@ -386,12 +416,12 @@ Dialog.prototype = {
var closeId = A.guid();
var closeConfig = {
- icon: CLOSETHICK,
- id: closeId,
handler: {
- fn: instance.close,
- context: instance
+ context: instance,
+ fn: instance.close
},
+ icon: CLOSETHICK,
+ id: closeId,
title: instance.get('strings').close
};
@@ -431,6 +461,10 @@ Dialog.prototype = {
bindUI: function() {
var instance = this;
+ if (instance.get(MODAL)) {
+ instance.after('focusOnChange', instance._afterFocusOnChange());
+ }
+
instance._bindLazyComponents();
},
@@ -445,16 +479,19 @@ Dialog.prototype = {
var instance = this;
if (instance.get(USE_ARIA)) {
- instance.plug(A.Plugin.Aria, {
- attributes: {
- visible: {
- ariaName: 'hidden',
- format: function(value) {
- return !value;
+ instance.plug(
+ A.Plugin.Aria,
+ {
+ attributes: {
+ visible: {
+ ariaName: 'hidden',
+ format: function(value) {
+ return !value;
+ }
}
}
}
- });
+ );
}
},
@@ -468,9 +505,8 @@ Dialog.prototype = {
destructor: function() {
var instance = this;
- var boundingBox = instance.get(BOUNDING_BOX);
+ A.Event.purgeElement(instance.get(BOUNDING_BOX), true);
- A.Event.purgeElement(boundingBox, true);
A.DialogManager.remove(instance);
},
@@ -486,35 +522,125 @@ Dialog.prototype = {
var viewportRegion = A.getDoc().get(VIEWPORT_REGION);
- instance.move([ viewportRegion.left + toInt(offsetLeft), viewportRegion.top + toInt(offsetTop) ]);
+ var viewportLeft = viewportRegion.left + toInt(offsetLeft);
+ var viewportTop = viewportRegion.top + toInt(offsetTop);
+
+ instance.move([viewportLeft, viewportTop]);
},
/**
- * Bind a mouseenter
listener to the boundingBox
- * to invoke the
- * _initLazyComponents.
- * Performance reasons.
+ * Fires the close event to close the Dialog.
*
- * @method _bindLazyComponents
- * @private
+ * @method close
*/
- _bindLazyComponents: function() {
+ close: function() {
var instance = this;
- var boundingBox = instance.get(BOUNDING_BOX);
+ instance.fire('close');
+ },
- boundingBox.on('mouseenter', A.bind(instance._initLazyComponents, instance));
+ /**
+ * Fires after the value of the
+ * constrain2view attribute change.
+ *
+ * @method _afterConstrain2viewChange
+ * @param {EventFacade} event
+ * @protected
+ */
+ _afterConstrain2viewChange: function(event) {
+ var instance = this;
+
+ instance._updateDDConstrain2view(
+ instance.get(DRAG_INSTANCE)
+ );
},
/**
- * Fires the close event to close the Dialog.
+ * Fires after the value of the
+ * draggable attribute change.
*
- * @method close
+ * @method _afterDraggableChange
+ * @param {EventFacade} event
+ * @protected
*/
- close: function() {
+ _afterDraggableChange: function(event) {
var instance = this;
- instance.fire('close');
+ instance.set(DRAG_INSTANCE, null);
+ },
+
+ /**
+ * Fires after the value of the
+ * dragInstance attribute change.
+ *
+ * @method _afterDragInstanceChange
+ * @param {EventFacade} event
+ * @protected
+ */
+ _afterDragInstanceChange: function(event) {
+ var instance = this;
+
+ var prevVal = event.prevVal;
+
+ if (prevVal) {
+ prevVal.destroy();
+ }
+ },
+
+ /**
+ * Handles the drag start event
+ * If "constrain2view" property is set to false this function will constrain the dialog to a region
+ * in order to prevent moving it to unreachable position
+ *
+ * @method _afterDragStart
+ * @param {EventFacade} event
+ * @protected
+ */
+ _afterDragStart: function(event) {
+ var instance = this;
+
+ var constrain2view = instance.get(CONSTRAIN_TO_VIEWPORT);
+
+ if (!constrain2view) {
+ var dragInstance = instance.get(DRAG_INSTANCE);
+
+ var dragNode = dragInstance.get('dragNode');
+
+ var dragNodeRegion = dragNode.get('region');
+ var viewportRegion = dragNode.get('viewportRegion');
+
+ var defaultOffset = [0, 0];
+
+ var deltaXY = dragInstance.deltaXY || defaultOffset;
+ var mouseXY = dragInstance.mouseXY || defaultOffset;
+
+ dragInstance.plug(
+ A.Plugin.DDConstrained,
+ {
+ constrain: {
+ bottom: viewportRegion.bottom + (dragNodeRegion.height - deltaXY[1]) - DRAG_GUTTER,
+ left: viewportRegion.left - deltaXY[0] + DRAG_GUTTER,
+ right: viewportRegion.right + (dragNodeRegion.right - mouseXY[0]) + DRAG_GUTTER,
+ top: viewportRegion.top - deltaXY[1] + DRAG_GUTTER
+ }
+ }
+ );
+ }
+ },
+
+ /**
+ * Default function called when focusOn Attribute is changed. Remove existing listeners and create new listeners.
+ *
+ * @method _afterFocusOnChange
+ */
+ _afterFocusOnChange : function(event) {
+ var instance = this;
+
+ instance._detachUIHandlesModal();
+
+ if (instance.get(VISIBLE)) {
+ instance._attachUIHandlesModal();
+ }
},
/**
@@ -535,6 +661,99 @@ Dialog.prototype = {
instance.get(IO);
},
+ /**
+ * Fires after the value of the
+ * resizable attribute change.
+ *
+ * @method _afterResizableChange
+ * @param {EventFacade} event
+ * @protected
+ */
+ _afterResizableChange: function(event) {
+ var instance = this;
+
+ instance.set(RESIZABLE_INSTANCE, null);
+ },
+
+ /**
+ * Fires after the value of the
+ * resizableInstance attribute change.
+ *
+ * @method _afterResizableInstanceChange
+ * @param {EventFacade} event
+ * @protected
+ */
+ _afterResizableInstanceChange: function(event) {
+ var instance = this;
+
+ var prevVal = event.prevVal;
+
+ if (prevVal) {
+ prevVal.destroy();
+ }
+ },
+
+ /**
+ * Attaches UI Listeners for "clickoutside" and "focusoutside" on the widget. When these events occur, and the widget is modal, focus is shifted back onto the widget.
+ *
+ * @method _attachUIHandlesModal
+ */
+ _attachUIHandlesModal: function() {
+ var instance = this;
+
+ var boundingBox = instance.get(BOUNDING_BOX);
+ var focusOn = instance.get('focusOn');
+ var maskNode = instance.get('maskNode');
+
+ var focus = A.bind(instance._focus, instance);
+
+ var uiHandles = [];
+
+ for (var i = 0; i < focusOn.length; i++) {
+ var ev = focusOn[i].eventName;
+ var keyCode = focusOn[i].keyCode;
+ var node = focusOn[i].node;
+
+ //no keycode or node defined
+ if (!node && !keyCode && ev) {
+ uiHandles.push(boundingBox.on(ev, focus));
+ }
+
+ //node defined, no keycode (not a keypress)
+ else if (node && !keyCode && ev) {
+ uiHandles.push(node.on(ev, focus));
+ }
+
+ //node defined, keycode defined, event defined (its a key press)
+ else if (node && keyCode && ev) {
+ uiHandles.push(node.on(ev, focus, keyCode));
+ }
+
+ else {
+ A.log('focusOn ATTR Error: The event with name "' + ev + '" could not be attached.');
+ }
+ }
+
+ instance._uiHandlesModal = uiHandles;
+ },
+
+ /**
+ * Bind a mouseenter
listener to the boundingBox
+ * to invoke the
+ * _initLazyComponents.
+ * Performance reasons.
+ *
+ * @method _bindLazyComponents
+ * @private
+ */
+ _bindLazyComponents: function() {
+ var instance = this;
+
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ boundingBox.on('mouseenter', A.bind(instance._initLazyComponents, instance));
+ },
+
/**
* Handles the close event logic.
*
@@ -553,6 +772,41 @@ Dialog.prototype = {
}
},
+ /**
+ * Detaches all UI Listeners that were set in _attachUIHandlesModal from the widget.
+ *
+ * @method _detachUIHandlesModal
+ */
+ _detachUIHandlesModal: function() {
+ var instance = this;
+
+ A.each(
+ instance._uiHandlesModal,
+ function(h) {
+ h.detach();
+ }
+ );
+
+ instance._uiHandlesModal = null;
+ },
+
+ /**
+ * Provides mouse and tab focus to the widget's bounding box.
+ *
+ * @method _focus
+ */
+ _focus: function(event) {
+ var instance = this;
+
+ var boundingBox = instance.get(BOUNDING_BOX);
+
+ var oldTI = boundingBox.get('tabIndex');
+
+ boundingBox.set('tabIndex', oldTI >= 0 ? oldTI : 0);
+
+ instance.focus();
+ },
+
/**
* Render the buttons on the footer of the Dialog.
*
@@ -614,7 +868,7 @@ Dialog.prototype = {
if (icons) {
var closeThick = icons.item(instance._closeId) || null;
- if (closeThick){
+ if (closeThick) {
instance.aria.setAttribute('controls', instance.get('id'), closeThick.get(BOUNDING_BOX));
}
}
@@ -694,8 +948,8 @@ Dialog.prototype = {
_syncResizableDimentions: function(event) {
var instance = this;
- var type = event.type;
var info = event.info;
+ var type = event.type;
if ((type === EV_RESIZE_END) ||
((type === EV_RESIZE) && !event.currentTarget.get(PROXY))) {
@@ -720,133 +974,14 @@ Dialog.prototype = {
constrain2view: instance.get(CONSTRAIN_TO_VIEWPORT)
}
);
- },
-
- /**
- * Fires after the value of the
- * constrain2view attribute change.
- *
- * @method _afterConstrain2viewChange
- * @param {EventFacade} event
- * @protected
- */
- _afterConstrain2viewChange: function(event) {
- var instance = this;
-
- instance._updateDDConstrain2view(
- instance.get(DRAG_INSTANCE)
- );
- },
-
- /**
- * Fires after the value of the
- * draggable attribute change.
- *
- * @method _afterDraggableChange
- * @param {EventFacade} event
- * @protected
- */
- _afterDraggableChange: function(event) {
- var instance = this;
-
- instance.set(DRAG_INSTANCE, null);
- },
-
- /**
- * Fires after the value of the
- * dragInstance attribute change.
- *
- * @method _afterDragInstanceChange
- * @param {EventFacade} event
- * @protected
- */
- _afterDragInstanceChange: function(event) {
- var instance = this;
-
- if (event.prevVal) {
- event.prevVal.destroy();
- }
- },
-
- /**
- * Handles the drag start event
- * If "constrain2view" property is set to false this function will constrain the dialog to a region
- * in order to prevent moving it to unreachable position
- *
- * @method _afterDragStart
- * @param {EventFacade} event
- * @protected
- */
- _afterDragStart: function(event) {
- var instance = this;
-
- var constrain2view = instance.get(CONSTRAIN_TO_VIEWPORT);
-
- if (!constrain2view) {
- var dragInstance = instance.get(DRAG_INSTANCE);
-
- var dragNode = dragInstance.get('dragNode');
-
- var viewportRegion = dragNode.get('viewportRegion');
-
- var dragNodeRegion = dragNode.get('region');
-
- var defaultOffset = [0, 0];
-
- var deltaXY = dragInstance.deltaXY || defaultOffset;
-
- var mouseXY = dragInstance.mouseXY || defaultOffset;
-
- dragInstance.plug(
- A.Plugin.DDConstrained,
- {
- constrain: {
- bottom: viewportRegion.bottom + (dragNodeRegion.height - deltaXY[1]) - DRAG_GUTTER,
- left: viewportRegion.left - deltaXY[0] + DRAG_GUTTER,
- right: viewportRegion.right + (dragNodeRegion.right - mouseXY[0]) + DRAG_GUTTER,
- top: viewportRegion.top - deltaXY[1] + DRAG_GUTTER
- }
- }
- );
- }
- },
-
- /**
- * Fires after the value of the
- * resizable attribute change.
- *
- * @method _afterResizableChange
- * @param {EventFacade} event
- * @protected
- */
- _afterResizableChange: function(event) {
- var instance = this;
-
- instance.set(RESIZABLE_INSTANCE, null);
- },
-
- /**
- * Fires after the value of the
- * resizableInstance attribute change.
- *
- * @method _afterResizableInstanceChange
- * @param {EventFacade} event
- * @protected
- */
- _afterResizableInstanceChange: function(event) {
- var instance = this;
-
- if (event.prevVal) {
- event.prevVal.destroy();
- }
}
};
A.Dialog = A.Component.create(
{
- NAME: DIALOG,
+ AUGMENTS: [Dialog, A.WidgetPosition, A.WidgetStack, A.WidgetPositionAlign, A.WidgetPositionConstrain],
EXTENDS: A.Panel,
- AUGMENTS: [Dialog, A.WidgetPosition, A.WidgetStack, A.WidgetPositionAlign, A.WidgetPositionConstrain]
+ NAME: DIALOG
}
);
@@ -902,21 +1037,6 @@ DialogManager.after(
A.mix(
DialogManager,
{
- /**
- * Find the Widget instance based on a child
- * element.
- *
- * @method findByChild
- * @for DialogManager
- * @param {Node | String} child Child node of the Dialog.
- * @return {Widget}
- */
- findByChild: function(child) {
- return A.Widget.getByNode(
- A.one(child).ancestor(DOT + CSS_DIALOG, true)
- );
- },
-
/**
* child
element.