1 |
efrain |
1 |
YUI.add('event-focus', function (Y, NAME) {
|
|
|
2 |
|
|
|
3 |
/**
|
|
|
4 |
* Adds bubbling and delegation support to DOM events focus and blur.
|
|
|
5 |
*
|
|
|
6 |
* @module event
|
|
|
7 |
* @submodule event-focus
|
|
|
8 |
*/
|
|
|
9 |
var Event = Y.Event,
|
|
|
10 |
|
|
|
11 |
YLang = Y.Lang,
|
|
|
12 |
|
|
|
13 |
isString = YLang.isString,
|
|
|
14 |
|
|
|
15 |
arrayIndex = Y.Array.indexOf,
|
|
|
16 |
|
|
|
17 |
useActivate = (function() {
|
|
|
18 |
|
|
|
19 |
// Changing the structure of this test, so that it doesn't use inline JS in HTML,
|
|
|
20 |
// which throws an exception in Win8 packaged apps, due to additional security restrictions:
|
|
|
21 |
// http://msdn.microsoft.com/en-us/library/windows/apps/hh465380.aspx#differences
|
|
|
22 |
|
|
|
23 |
var supported = false,
|
|
|
24 |
doc = Y.config.doc,
|
|
|
25 |
p;
|
|
|
26 |
|
|
|
27 |
if (doc) {
|
|
|
28 |
|
|
|
29 |
p = doc.createElement("p");
|
|
|
30 |
p.setAttribute("onbeforeactivate", ";");
|
|
|
31 |
|
|
|
32 |
// onbeforeactivate is a function in IE8+.
|
|
|
33 |
// onbeforeactivate is a string in IE6,7 (unfortunate, otherwise we could have just checked for function below).
|
|
|
34 |
// onbeforeactivate is a function in IE10, in a Win8 App environment (no exception running the test).
|
|
|
35 |
|
|
|
36 |
// onbeforeactivate is undefined in Webkit/Gecko.
|
|
|
37 |
// onbeforeactivate is a function in Webkit/Gecko if it's a supported event (e.g. onclick).
|
|
|
38 |
|
|
|
39 |
supported = (p.onbeforeactivate !== undefined);
|
|
|
40 |
}
|
|
|
41 |
|
|
|
42 |
return supported;
|
|
|
43 |
}());
|
|
|
44 |
|
|
|
45 |
function define(type, proxy, directEvent) {
|
|
|
46 |
var nodeDataKey = '_' + type + 'Notifiers';
|
|
|
47 |
|
|
|
48 |
Y.Event.define(type, {
|
|
|
49 |
|
|
|
50 |
_useActivate : useActivate,
|
|
|
51 |
|
|
|
52 |
_attach: function (el, notifier, delegate) {
|
|
|
53 |
if (Y.DOM.isWindow(el)) {
|
|
|
54 |
return Event._attach([type, function (e) {
|
|
|
55 |
notifier.fire(e);
|
|
|
56 |
}, el]);
|
|
|
57 |
} else {
|
|
|
58 |
return Event._attach(
|
|
|
59 |
[proxy, this._proxy, el, this, notifier, delegate],
|
|
|
60 |
{ capture: true });
|
|
|
61 |
}
|
|
|
62 |
},
|
|
|
63 |
|
|
|
64 |
_proxy: function (e, notifier, delegate) {
|
|
|
65 |
var target = e.target,
|
|
|
66 |
currentTarget = e.currentTarget,
|
|
|
67 |
notifiers = target.getData(nodeDataKey),
|
|
|
68 |
yuid = Y.stamp(currentTarget._node),
|
|
|
69 |
defer = (useActivate || target !== currentTarget),
|
|
|
70 |
directSub;
|
|
|
71 |
|
|
|
72 |
notifier.currentTarget = (delegate) ? target : currentTarget;
|
|
|
73 |
notifier.container = (delegate) ? currentTarget : null;
|
|
|
74 |
|
|
|
75 |
// Maintain a list to handle subscriptions from nested
|
|
|
76 |
// containers div#a>div#b>input #a.on(focus..) #b.on(focus..),
|
|
|
77 |
// use one focus or blur subscription that fires notifiers from
|
|
|
78 |
// #b then #a to emulate bubble sequence.
|
|
|
79 |
if (!notifiers) {
|
|
|
80 |
notifiers = {};
|
|
|
81 |
target.setData(nodeDataKey, notifiers);
|
|
|
82 |
|
|
|
83 |
// only subscribe to the element's focus if the target is
|
|
|
84 |
// not the current target (
|
|
|
85 |
if (defer) {
|
|
|
86 |
directSub = Event._attach(
|
|
|
87 |
[directEvent, this._notify, target._node]).sub;
|
|
|
88 |
directSub.once = true;
|
|
|
89 |
}
|
|
|
90 |
} else {
|
|
|
91 |
// In old IE, defer is always true. In capture-phase browsers,
|
|
|
92 |
// The delegate subscriptions will be encountered first, which
|
|
|
93 |
// will establish the notifiers data and direct subscription
|
|
|
94 |
// on the node. If there is also a direct subscription to the
|
|
|
95 |
// node's focus/blur, it should not call _notify because the
|
|
|
96 |
// direct subscription from the delegate sub(s) exists, which
|
|
|
97 |
// will call _notify. So this avoids _notify being called
|
|
|
98 |
// twice, unnecessarily.
|
|
|
99 |
defer = true;
|
|
|
100 |
}
|
|
|
101 |
|
|
|
102 |
if (!notifiers[yuid]) {
|
|
|
103 |
notifiers[yuid] = [];
|
|
|
104 |
}
|
|
|
105 |
|
|
|
106 |
notifiers[yuid].push(notifier);
|
|
|
107 |
|
|
|
108 |
if (!defer) {
|
|
|
109 |
this._notify(e);
|
|
|
110 |
}
|
|
|
111 |
},
|
|
|
112 |
|
|
|
113 |
_notify: function (e, container) {
|
|
|
114 |
var currentTarget = e.currentTarget,
|
|
|
115 |
notifierData = currentTarget.getData(nodeDataKey),
|
|
|
116 |
axisNodes = currentTarget.ancestors(),
|
|
|
117 |
doc = currentTarget.get('ownerDocument'),
|
|
|
118 |
delegates = [],
|
|
|
119 |
// Used to escape loops when there are no more
|
|
|
120 |
// notifiers to consider
|
|
|
121 |
count = notifierData ?
|
|
|
122 |
Y.Object.keys(notifierData).length :
|
|
|
123 |
0,
|
|
|
124 |
target, notifiers, notifier, yuid, match, tmp, i, len, sub, ret;
|
|
|
125 |
|
|
|
126 |
// clear the notifications list (mainly for delegation)
|
|
|
127 |
currentTarget.clearData(nodeDataKey);
|
|
|
128 |
|
|
|
129 |
// Order the delegate subs by their placement in the parent axis
|
|
|
130 |
axisNodes.push(currentTarget);
|
|
|
131 |
// document.get('ownerDocument') returns null
|
|
|
132 |
// which we'll use to prevent having duplicate Nodes in the list
|
|
|
133 |
if (doc) {
|
|
|
134 |
axisNodes.unshift(doc);
|
|
|
135 |
}
|
|
|
136 |
|
|
|
137 |
// ancestors() returns the Nodes from top to bottom
|
|
|
138 |
axisNodes._nodes.reverse();
|
|
|
139 |
|
|
|
140 |
if (count) {
|
|
|
141 |
// Store the count for step 2
|
|
|
142 |
tmp = count;
|
|
|
143 |
axisNodes.some(function (node) {
|
|
|
144 |
var yuid = Y.stamp(node),
|
|
|
145 |
notifiers = notifierData[yuid],
|
|
|
146 |
i, len;
|
|
|
147 |
|
|
|
148 |
if (notifiers) {
|
|
|
149 |
count--;
|
|
|
150 |
for (i = 0, len = notifiers.length; i < len; ++i) {
|
|
|
151 |
if (notifiers[i].handle.sub.filter) {
|
|
|
152 |
delegates.push(notifiers[i]);
|
|
|
153 |
}
|
|
|
154 |
}
|
|
|
155 |
}
|
|
|
156 |
|
|
|
157 |
return !count;
|
|
|
158 |
});
|
|
|
159 |
count = tmp;
|
|
|
160 |
}
|
|
|
161 |
|
|
|
162 |
// Walk up the parent axis, notifying direct subscriptions and
|
|
|
163 |
// testing delegate filters.
|
|
|
164 |
while (count && (target = axisNodes.shift())) {
|
|
|
165 |
yuid = Y.stamp(target);
|
|
|
166 |
|
|
|
167 |
notifiers = notifierData[yuid];
|
|
|
168 |
|
|
|
169 |
if (notifiers) {
|
|
|
170 |
for (i = 0, len = notifiers.length; i < len; ++i) {
|
|
|
171 |
notifier = notifiers[i];
|
|
|
172 |
sub = notifier.handle.sub;
|
|
|
173 |
match = true;
|
|
|
174 |
|
|
|
175 |
e.currentTarget = target;
|
|
|
176 |
|
|
|
177 |
if (sub.filter) {
|
|
|
178 |
match = sub.filter.apply(target,
|
|
|
179 |
[target, e].concat(sub.args || []));
|
|
|
180 |
|
|
|
181 |
// No longer necessary to test against this
|
|
|
182 |
// delegate subscription for the nodes along
|
|
|
183 |
// the parent axis.
|
|
|
184 |
delegates.splice(
|
|
|
185 |
arrayIndex(delegates, notifier), 1);
|
|
|
186 |
}
|
|
|
187 |
|
|
|
188 |
if (match) {
|
|
|
189 |
// undefined for direct subs
|
|
|
190 |
e.container = notifier.container;
|
|
|
191 |
ret = notifier.fire(e);
|
|
|
192 |
}
|
|
|
193 |
|
|
|
194 |
if (ret === false || e.stopped === 2) {
|
|
|
195 |
break;
|
|
|
196 |
}
|
|
|
197 |
}
|
|
|
198 |
|
|
|
199 |
delete notifiers[yuid];
|
|
|
200 |
count--;
|
|
|
201 |
}
|
|
|
202 |
|
|
|
203 |
if (e.stopped !== 2) {
|
|
|
204 |
// delegates come after subs targeting this specific node
|
|
|
205 |
// because they would not normally report until they'd
|
|
|
206 |
// bubbled to the container node.
|
|
|
207 |
for (i = 0, len = delegates.length; i < len; ++i) {
|
|
|
208 |
notifier = delegates[i];
|
|
|
209 |
sub = notifier.handle.sub;
|
|
|
210 |
|
|
|
211 |
if (sub.filter.apply(target,
|
|
|
212 |
[target, e].concat(sub.args || []))) {
|
|
|
213 |
|
|
|
214 |
e.container = notifier.container;
|
|
|
215 |
e.currentTarget = target;
|
|
|
216 |
ret = notifier.fire(e);
|
|
|
217 |
}
|
|
|
218 |
|
|
|
219 |
if (ret === false || e.stopped === 2 ||
|
|
|
220 |
// If e.stopPropagation() is called, notify any
|
|
|
221 |
// delegate subs from the same container, but break
|
|
|
222 |
// once the container changes. This emulates
|
|
|
223 |
// delegate() behavior for events like 'click' which
|
|
|
224 |
// won't notify delegates higher up the parent axis.
|
|
|
225 |
(e.stopped && delegates[i+1] &&
|
|
|
226 |
delegates[i+1].container !== notifier.container)) {
|
|
|
227 |
break;
|
|
|
228 |
}
|
|
|
229 |
}
|
|
|
230 |
}
|
|
|
231 |
|
|
|
232 |
if (e.stopped) {
|
|
|
233 |
break;
|
|
|
234 |
}
|
|
|
235 |
}
|
|
|
236 |
},
|
|
|
237 |
|
|
|
238 |
on: function (node, sub, notifier) {
|
|
|
239 |
sub.handle = this._attach(node._node, notifier);
|
|
|
240 |
},
|
|
|
241 |
|
|
|
242 |
detach: function (node, sub) {
|
|
|
243 |
sub.handle.detach();
|
|
|
244 |
},
|
|
|
245 |
|
|
|
246 |
delegate: function (node, sub, notifier, filter) {
|
|
|
247 |
if (isString(filter)) {
|
|
|
248 |
sub.filter = function (target) {
|
|
|
249 |
return Y.Selector.test(target._node, filter,
|
|
|
250 |
node === target ? null : node._node);
|
|
|
251 |
};
|
|
|
252 |
}
|
|
|
253 |
|
|
|
254 |
sub.handle = this._attach(node._node, notifier, true);
|
|
|
255 |
},
|
|
|
256 |
|
|
|
257 |
detachDelegate: function (node, sub) {
|
|
|
258 |
sub.handle.detach();
|
|
|
259 |
}
|
|
|
260 |
}, true);
|
|
|
261 |
}
|
|
|
262 |
|
|
|
263 |
// For IE, we need to defer to focusin rather than focus because
|
|
|
264 |
// `el.focus(); doSomething();` executes el.onbeforeactivate, el.onactivate,
|
|
|
265 |
// el.onfocusin, doSomething, then el.onfocus. All others support capture
|
|
|
266 |
// phase focus, which executes before doSomething. To guarantee consistent
|
|
|
267 |
// behavior for this use case, IE's direct subscriptions are made against
|
|
|
268 |
// focusin so subscribers will be notified before js following el.focus() is
|
|
|
269 |
// executed.
|
|
|
270 |
if (useActivate) {
|
|
|
271 |
// name capture phase direct subscription
|
|
|
272 |
define("focus", "beforeactivate", "focusin");
|
|
|
273 |
define("blur", "beforedeactivate", "focusout");
|
|
|
274 |
} else {
|
|
|
275 |
define("focus", "focus", "focus");
|
|
|
276 |
define("blur", "blur", "blur");
|
|
|
277 |
}
|
|
|
278 |
|
|
|
279 |
|
|
|
280 |
}, '3.18.1', {"requires": ["event-synthetic"]});
|