1 |
efrain |
1 |
/**
|
|
|
2 |
* Provides support for focusing on different nodes after the Widget is
|
|
|
3 |
* hidden.
|
|
|
4 |
*
|
|
|
5 |
* If the focusOnPreviousTargetAfterHide attribute is true, then the module hooks
|
|
|
6 |
* into the show function for that Widget to try and determine which Node
|
|
|
7 |
* caused the Widget to be shown.
|
|
|
8 |
*
|
|
|
9 |
* Alternatively, the focusAfterHide attribute can be passed a Node.
|
|
|
10 |
*
|
|
|
11 |
* @module moodle-core-widget-focusafterhide
|
|
|
12 |
*/
|
|
|
13 |
|
|
|
14 |
var CAN_RECEIVE_FOCUS_SELECTOR = 'input:not([type="hidden"]), ' +
|
|
|
15 |
'a[href], button, textarea, select, ' +
|
|
|
16 |
'[tabindex], [contenteditable="true"]';
|
|
|
17 |
|
|
|
18 |
/**
|
|
|
19 |
* Provides support for focusing on different nodes after the Widget is
|
|
|
20 |
* hidden.
|
|
|
21 |
*
|
|
|
22 |
* @class M.core.WidgetFocusAfterHide
|
|
|
23 |
*/
|
|
|
24 |
function WidgetFocusAfterHide() {
|
|
|
25 |
Y.after(this._bindUIFocusAfterHide, this, 'bindUI');
|
|
|
26 |
if (this.get('rendered')) {
|
|
|
27 |
this._bindUIFocusAfterHide();
|
|
|
28 |
}
|
|
|
29 |
}
|
|
|
30 |
|
|
|
31 |
WidgetFocusAfterHide.ATTRS = {
|
|
|
32 |
/**
|
|
|
33 |
* Whether to focus on the target that caused the Widget to be shown.
|
|
|
34 |
*
|
|
|
35 |
* <em>If this is true, and a valid Node is found, any Node specified to focusAfterHide
|
|
|
36 |
* will be ignored.</em>
|
|
|
37 |
*
|
|
|
38 |
* @attribute focusOnPreviousTargetAfterHide
|
|
|
39 |
* @default false
|
|
|
40 |
* @type boolean
|
|
|
41 |
*/
|
|
|
42 |
focusOnPreviousTargetAfterHide: {
|
|
|
43 |
value: false
|
|
|
44 |
},
|
|
|
45 |
|
|
|
46 |
/**
|
|
|
47 |
* The Node to focus on after hiding the Widget.
|
|
|
48 |
*
|
|
|
49 |
* <em>Note: If focusOnPreviousTargetAfterHide is true, and a valid Node is found, then this
|
|
|
50 |
* value will be ignored. If it is true and not found, then this value will be used as
|
|
|
51 |
* a fallback.</em>
|
|
|
52 |
*
|
|
|
53 |
* @attribute focusAfterHide
|
|
|
54 |
* @default null
|
|
|
55 |
* @type Node
|
|
|
56 |
*/
|
|
|
57 |
focusAfterHide: {
|
|
|
58 |
value: null,
|
|
|
59 |
type: Y.Node
|
|
|
60 |
}
|
|
|
61 |
};
|
|
|
62 |
|
|
|
63 |
WidgetFocusAfterHide.prototype = {
|
|
|
64 |
/**
|
|
|
65 |
* The list of Event Handles which we should cancel when the dialogue is destroyed.
|
|
|
66 |
*
|
|
|
67 |
* @property uiHandleFocusAfterHide
|
|
|
68 |
* @type array
|
|
|
69 |
* @protected
|
|
|
70 |
*/
|
|
|
71 |
_uiHandlesFocusAfterHide: [],
|
|
|
72 |
|
|
|
73 |
/**
|
|
|
74 |
* A reference to the real show method which is being overwritten.
|
|
|
75 |
*
|
|
|
76 |
* @property _showFocusAfterHide
|
|
|
77 |
* @type function
|
|
|
78 |
* @default null
|
|
|
79 |
* @protected
|
|
|
80 |
*/
|
|
|
81 |
_showFocusAfterHide: null,
|
|
|
82 |
|
|
|
83 |
/**
|
|
|
84 |
* A reference to the detected previous target.
|
|
|
85 |
*
|
|
|
86 |
* @property _previousTargetFocusAfterHide
|
|
|
87 |
* @type function
|
|
|
88 |
* @default null
|
|
|
89 |
* @protected
|
|
|
90 |
*/
|
|
|
91 |
_previousTargetFocusAfterHide: null,
|
|
|
92 |
|
|
|
93 |
initializer: function() {
|
|
|
94 |
|
|
|
95 |
if (this.get('focusOnPreviousTargetAfterHide') && this.show) {
|
|
|
96 |
// Overwrite the parent method so that we can get the focused
|
|
|
97 |
// target.
|
|
|
98 |
this._showFocusAfterHide = this.show;
|
|
|
99 |
this.show = function(e) {
|
|
|
100 |
this._showFocusAfterHide.apply(this, arguments);
|
|
|
101 |
|
|
|
102 |
// We use a property rather than overriding the focusAfterHide parameter in
|
|
|
103 |
// case the target cannot be found at hide time.
|
|
|
104 |
this._previousTargetFocusAfterHide = null;
|
|
|
105 |
if (e && e.currentTarget) {
|
|
|
106 |
Y.log("Determined a Node which caused the Widget to be shown",
|
|
|
107 |
'debug', 'moodle-core-widget-focusafterhide');
|
|
|
108 |
this._previousTargetFocusAfterHide = e.currentTarget;
|
|
|
109 |
}
|
|
|
110 |
};
|
|
|
111 |
}
|
|
|
112 |
},
|
|
|
113 |
|
|
|
114 |
destructor: function() {
|
|
|
115 |
new Y.EventHandle(this.uiHandleFocusAfterHide).detach();
|
|
|
116 |
},
|
|
|
117 |
|
|
|
118 |
/**
|
|
|
119 |
* Set up the event handling required for this module to work.
|
|
|
120 |
*
|
|
|
121 |
* @method _bindUIFocusAfterHide
|
|
|
122 |
* @private
|
|
|
123 |
*/
|
|
|
124 |
_bindUIFocusAfterHide: function() {
|
|
|
125 |
// Detach the old handles first.
|
|
|
126 |
new Y.EventHandle(this.uiHandleFocusAfterHide).detach();
|
|
|
127 |
this.uiHandleFocusAfterHide = [
|
|
|
128 |
this.after('visibleChange', this._afterHostVisibleChangeFocusAfterHide)
|
|
|
129 |
];
|
|
|
130 |
},
|
|
|
131 |
|
|
|
132 |
/**
|
|
|
133 |
* Handle the change in UI visibility.
|
|
|
134 |
*
|
|
|
135 |
* This method changes the focus after the hide has taken place.
|
|
|
136 |
*
|
|
|
137 |
* @method _afterHostVisibleChangeFocusAfterHide
|
|
|
138 |
* @private
|
|
|
139 |
*/
|
|
|
140 |
_afterHostVisibleChangeFocusAfterHide: function() {
|
|
|
141 |
if (!this.get('visible')) {
|
|
|
142 |
if (this._attemptFocus(this._previousTargetFocusAfterHide)) {
|
|
|
143 |
Y.log("Focusing on the target automatically determined when the Widget was opened",
|
|
|
144 |
'debug', 'moodle-core-widget-focusafterhide');
|
|
|
145 |
|
|
|
146 |
} else if (this._attemptFocus(this.get('focusAfterHide'))) {
|
|
|
147 |
// Fall back to the focusAfterHide value if one was specified.
|
|
|
148 |
Y.log("Focusing on the target provided to focusAfterHide",
|
|
|
149 |
'debug', 'moodle-core-widget-focusafterhide');
|
|
|
150 |
|
|
|
151 |
} else {
|
|
|
152 |
Y.log("No valid focus target found - not returning focus.",
|
|
|
153 |
'debug', 'moodle-core-widget-focusafterhide');
|
|
|
154 |
|
|
|
155 |
}
|
|
|
156 |
}
|
|
|
157 |
},
|
|
|
158 |
|
|
|
159 |
_attemptFocus: function(node) {
|
|
|
160 |
var focusTarget = Y.one(node);
|
|
|
161 |
if (focusTarget) {
|
|
|
162 |
focusTarget = focusTarget.ancestor(CAN_RECEIVE_FOCUS_SELECTOR, true);
|
|
|
163 |
if (focusTarget) {
|
|
|
164 |
focusTarget.focus();
|
|
|
165 |
return true;
|
|
|
166 |
}
|
|
|
167 |
}
|
|
|
168 |
return false;
|
|
|
169 |
}
|
|
|
170 |
};
|
|
|
171 |
|
|
|
172 |
var NS = Y.namespace('M.core');
|
|
|
173 |
NS.WidgetFocusAfterHide = WidgetFocusAfterHide;
|