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 - }, - /** * If 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) - ); - }, - /** *

Invoke the close method from * the Dialog which contains the child element.

@@ -934,6 +1054,21 @@ A.mix( return DialogManager.findByChild(child).close(); }, + /** + * 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 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=0?ae:0);A.focus();},_initButtons:function(){var A=this;var af=A.get(c);if(af.length){var ae=new p.Toolbar({children:af});ae._DEFAULT_CONTEXT=A;ae.render(A.footerNode);A.fire("contentUpdate");A.buttons=ae;}},_initLazyComponents:function(){var A=this;A.get(m);A.get(n);},_setDefaultARIAValues:function(){var A=this;var ae=A.icons;if(!A.get(j)){return;}A.aria.setRole("dialog",A.get(V));if(ae){var af=ae.item(A._closeId)||null;if(af){A.aria.setAttribute("controls",A.get("id"),af.get(V));}}},_setDragInstance:function(ae){var A=this;if(A.get(o)){ae=new p.DD.Drag(A.get(C));A._updateDDConstrain2view(ae);}return ae;},_setResizableInstance:function(ae){var A=this;if(A.get(e)){ae=new p.Resize(A.get(F));}return ae;},_setStack:function(ae){var A=this;if(ae){p.DialogManager.register(A);}else{p.DialogManager.remove(A);}return ae;},_syncResizableDimentions:function(af){var A=this;var ag=af.info;var ae=af.type;if((ae===M)||((ae===q)&&!af.currentTarget.get(ab))){A.set(I,ag.offsetHeight);A.set(f,ag.offsetWidth); +}},_updateDDConstrain2view:function(ae){var A=this;ae.plug(p.Plugin.DDConstrained,{constrain2view:A.get(K)});}};p.Dialog=p.Component.create({AUGMENTS:[r,p.WidgetPosition,p.WidgetStack,p.WidgetPositionAlign,p.WidgetPositionConstrain],EXTENDS:p.Panel,NAME:w});var l=new p.OverlayManager({zIndexBase:1000});var S={};l._MODALS=S;l.after(["dialog:destroy","dialog:modalChange","dialog:render","dialog:visibleChange"],function(ae){var A=ae.target;if(A){var af=A.get("id");if(ae.type!=="dialog:destroy"&&A.get("visible")&&A.get("modal")){S[af]=true;p.DialogMask.show();l._blockIFrameFocus();}else{delete S[af];if(u.isEmpty(S)){p.DialogMask.hide();l._unblockIFrameFocus();}}}});p.mix(l,{closeByChild:function(A){return l.findByChild(A).close();},findByChild:function(A){return p.Widget.getByNode(p.one(A).ancestor(x+y,true));},refreshByChild:function(ae){var A=l.findByChild(ae);if(A&&A.io){A.io.start();}},_blockIFrameFocus:function(){p.all("iframe").each(function(){if(this.ancestor(x+y)===null){if(!this.hasAttribute(U)){this.setAttribute(U,this.get(Q));}this.set(Q,-1);}});},_unblockIFrameFocus:function(){p.all("iframe").each(function(){if(this.hasAttribute(U)){this.set(Q,this.getAttribute(U));}});}});p.DialogManager=l;},"@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 diff --git a/build/aui-dialog/aui-dialog.js b/build/aui-dialog/aui-dialog.js index e01f0d8e5f7..71e3d9ed6f4 100644 --- a/build/aui-dialog/aui-dialog.js +++ b/build/aui-dialog/aui-dialog.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 - }, - /** * If 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) - ); - }, - /** *

Invoke the close method from * the Dialog which contains the child element.

@@ -934,6 +1054,21 @@ A.mix( return DialogManager.findByChild(child).close(); }, + /** + * 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 start method * from the IOPlugin plugged on this Dialog