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