1 | import $ from './jquery';
|
2 | import keyCode from './key-code';
|
3 | import amdify from './internal/amdify';
|
4 | import skate from './internal/skate';
|
5 | import state from './internal/state';
|
6 | import { warn } from './internal/log'
|
7 |
|
8 | export function getTrigger (element) {
|
9 | return state(element).get('last-trigger') || findControllers(element)[0];
|
10 | }
|
11 |
|
12 | export function setTrigger (element, trigger) {
|
13 | var validTrigger = trigger && trigger.nodeType && trigger.nodeType === 1;
|
14 | return state(element).set('last-trigger', validTrigger ? trigger : false);
|
15 | }
|
16 |
|
17 | export function hasTrigger (element) {
|
18 | return !!getTrigger(element);
|
19 | }
|
20 |
|
21 | export function doIfTrigger (element, callback) {
|
22 | var trigger = getTrigger(element);
|
23 |
|
24 | if (trigger) {
|
25 | callback(trigger);
|
26 | }
|
27 | }
|
28 |
|
29 | export function forEachTrigger (element, callback) {
|
30 | return Array.prototype.forEach.call(findControllers(element), callback);
|
31 | }
|
32 |
|
33 | function isNestedAnchor(trigger, target) {
|
34 | var $closestAnchor = $(target).closest('a[href]', trigger);
|
35 | return !!$closestAnchor.length && $closestAnchor[0] !== trigger;
|
36 | }
|
37 |
|
38 | function findControllers(element) {
|
39 | const frames = window.frames;
|
40 | const selector = `[aria-controls="${element.id}"]`;
|
41 |
|
42 | let controllers = [];
|
43 | let someFramesAreCrossOrigin = false;
|
44 | for (let i = 0 ; i < frames.length; i++) {
|
45 | try {
|
46 | let nodeList = frames[i].document.querySelectorAll(selector);
|
47 | controllers = controllers.concat(Array.prototype.slice.apply(nodeList));
|
48 | } catch (e) {
|
49 |
|
50 | someFramesAreCrossOrigin = true;
|
51 | }
|
52 | }
|
53 | const currentDocumentControllers = document.querySelectorAll(selector);
|
54 | const allControllers = Array.prototype.slice.apply(currentDocumentControllers).concat(controllers);
|
55 | if (allControllers.length === 0 && someFramesAreCrossOrigin === true) {
|
56 | warn(
|
57 | [
|
58 | `No triggers found for element (${element.id}) in iframes from the same origin.`,
|
59 | 'However some iframes in this document are cross-origin.',
|
60 | 'The trigger-element relations crossing the origin boundary are not supported.'
|
61 | ].join(' ')
|
62 | )
|
63 | }
|
64 | return allControllers;
|
65 | }
|
66 |
|
67 | function findControlled(trigger) {
|
68 | return document.getElementById(trigger.getAttribute('aria-controls'));
|
69 | }
|
70 |
|
71 | function isEnabled (element) {
|
72 | return element.getAttribute('aria-disabled') !== 'true';
|
73 | }
|
74 |
|
75 | function triggerMessage(trigger, e) {
|
76 | if (isEnabled(trigger)) {
|
77 | var component = findControlled(trigger);
|
78 | if (component && component.message) {
|
79 | component.message(e);
|
80 | }
|
81 | }
|
82 | }
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | function msg(e, type) {
|
89 | const { target, currentTarget, relatedTarget } = e;
|
90 | const { keyCode, which } = e;
|
91 | return {
|
92 | type,
|
93 | data: type === 'keydown' ? which || keyCode : undefined,
|
94 | target,
|
95 | currentTarget,
|
96 | relatedTarget,
|
97 | preventDefault: () => e.preventDefault()
|
98 | };
|
99 | }
|
100 |
|
101 | function focusingToControlledElement(trigger, e) {
|
102 | let relatedTarget = e.relatedTarget;
|
103 |
|
104 | if (!relatedTarget) {
|
105 | relatedTarget = document.activeElement;
|
106 | }
|
107 | const $component = $(findControlled(trigger));
|
108 | return $component.find(relatedTarget).length > 0;
|
109 | }
|
110 |
|
111 | const events = {
|
112 | click(trigger, e) {
|
113 | if (!isNestedAnchor(trigger, e.target)) {
|
114 | triggerMessage(trigger, e);
|
115 | e.preventDefault();
|
116 | }
|
117 | },
|
118 | keydown(trigger, e) {
|
119 | const key = e.data;
|
120 | if (key === keyCode.ENTER || key === keyCode.SPACE) {
|
121 | e.preventDefault();
|
122 | e.type = 'click';
|
123 | events.click(trigger, e);
|
124 | }
|
125 | },
|
126 | mouseenter(trigger, e) {
|
127 | triggerMessage(trigger, e);
|
128 | },
|
129 | mouseleave(trigger, e) {
|
130 | triggerMessage(trigger, e);
|
131 | },
|
132 | focus(trigger, e) {
|
133 | triggerMessage(trigger, e);
|
134 | },
|
135 | blur(trigger, e) {
|
136 | if (focusingToControlledElement(trigger, e)){
|
137 | return;
|
138 | }
|
139 | triggerMessage(trigger, e);
|
140 | }
|
141 | };
|
142 |
|
143 | Object.keys(events).forEach(function(name) {
|
144 | const handler = events[name];
|
145 | $(document).on(`${name}.aui-trigger`, '[data-aui-trigger]', function(e) {
|
146 | handler(e.currentTarget, msg(e, name));
|
147 | });
|
148 | });
|
149 |
|
150 | skate('data-aui-trigger', {
|
151 | type: skate.type.ATTRIBUTE,
|
152 | prototype: {
|
153 | disable: function () {
|
154 | this.setAttribute('aria-disabled', 'true');
|
155 | },
|
156 | enable: function () {
|
157 | this.setAttribute('aria-disabled', 'false');
|
158 | },
|
159 | isEnabled: function () {
|
160 | return isEnabled(this);
|
161 | }
|
162 | }
|
163 | });
|
164 |
|
165 | amdify('aui/trigger');
|