UNPKG

8.94 kBJavaScriptView Raw
1'use strict';
2
3import $ from 'jquery';
4import { Motion } from './foundation.util.motion';
5
6const MutationObserver = (function () {
7 var prefixes = ['WebKit', 'Moz', 'O', 'Ms', ''];
8 for (var i=0; i < prefixes.length; i++) {
9 if (`${prefixes[i]}MutationObserver` in window) {
10 return window[`${prefixes[i]}MutationObserver`];
11 }
12 }
13 return false;
14}());
15
16const triggers = (el, type) => {
17 el.data(type).split(' ').forEach(id => {
18 $(`#${id}`)[ type === 'close' ? 'trigger' : 'triggerHandler'](`${type}.zf.trigger`, [el]);
19 });
20};
21
22var Triggers = {
23 Listeners: {
24 Basic: {},
25 Global: {}
26 },
27 Initializers: {}
28}
29
30Triggers.Listeners.Basic = {
31 openListener: function() {
32 triggers($(this), 'open');
33 },
34 closeListener: function() {
35 let id = $(this).data('close');
36 if (id) {
37 triggers($(this), 'close');
38 }
39 else {
40 $(this).trigger('close.zf.trigger');
41 }
42 },
43 toggleListener: function() {
44 let id = $(this).data('toggle');
45 if (id) {
46 triggers($(this), 'toggle');
47 } else {
48 $(this).trigger('toggle.zf.trigger');
49 }
50 },
51 closeableListener: function(e) {
52 e.stopPropagation();
53 let animation = $(this).data('closable');
54
55 if(animation !== ''){
56 Motion.animateOut($(this), animation, function() {
57 $(this).trigger('closed.zf');
58 });
59 }else{
60 $(this).fadeOut().trigger('closed.zf');
61 }
62 },
63 toggleFocusListener: function() {
64 let id = $(this).data('toggle-focus');
65 $(`#${id}`).triggerHandler('toggle.zf.trigger', [$(this)]);
66 }
67};
68
69// Elements with [data-open] will reveal a plugin that supports it when clicked.
70Triggers.Initializers.addOpenListener = ($elem) => {
71 $elem.off('click.zf.trigger', Triggers.Listeners.Basic.openListener);
72 $elem.on('click.zf.trigger', '[data-open]', Triggers.Listeners.Basic.openListener);
73}
74
75// Elements with [data-close] will close a plugin that supports it when clicked.
76// If used without a value on [data-close], the event will bubble, allowing it to close a parent component.
77Triggers.Initializers.addCloseListener = ($elem) => {
78 $elem.off('click.zf.trigger', Triggers.Listeners.Basic.closeListener);
79 $elem.on('click.zf.trigger', '[data-close]', Triggers.Listeners.Basic.closeListener);
80}
81
82// Elements with [data-toggle] will toggle a plugin that supports it when clicked.
83Triggers.Initializers.addToggleListener = ($elem) => {
84 $elem.off('click.zf.trigger', Triggers.Listeners.Basic.toggleListener);
85 $elem.on('click.zf.trigger', '[data-toggle]', Triggers.Listeners.Basic.toggleListener);
86}
87
88// Elements with [data-closable] will respond to close.zf.trigger events.
89Triggers.Initializers.addCloseableListener = ($elem) => {
90 $elem.off('close.zf.trigger', Triggers.Listeners.Basic.closeableListener);
91 $elem.on('close.zf.trigger', '[data-closeable], [data-closable]', Triggers.Listeners.Basic.closeableListener);
92}
93
94// Elements with [data-toggle-focus] will respond to coming in and out of focus
95Triggers.Initializers.addToggleFocusListener = ($elem) => {
96 $elem.off('focus.zf.trigger blur.zf.trigger', Triggers.Listeners.Basic.toggleFocusListener);
97 $elem.on('focus.zf.trigger blur.zf.trigger', '[data-toggle-focus]', Triggers.Listeners.Basic.toggleFocusListener);
98}
99
100
101
102// More Global/complex listeners and triggers
103Triggers.Listeners.Global = {
104 resizeListener: function($nodes) {
105 if(!MutationObserver){//fallback for IE 9
106 $nodes.each(function(){
107 $(this).triggerHandler('resizeme.zf.trigger');
108 });
109 }
110 //trigger all listening elements and signal a resize event
111 $nodes.attr('data-events', "resize");
112 },
113 scrollListener: function($nodes) {
114 if(!MutationObserver){//fallback for IE 9
115 $nodes.each(function(){
116 $(this).triggerHandler('scrollme.zf.trigger');
117 });
118 }
119 //trigger all listening elements and signal a scroll event
120 $nodes.attr('data-events', "scroll");
121 },
122 closeMeListener: function(e, pluginId){
123 let plugin = e.namespace.split('.')[0];
124 let plugins = $(`[data-${plugin}]`).not(`[data-yeti-box="${pluginId}"]`);
125
126 plugins.each(function(){
127 let _this = $(this);
128 _this.triggerHandler('close.zf.trigger', [_this]);
129 });
130 }
131}
132
133// Global, parses whole document.
134Triggers.Initializers.addClosemeListener = function(pluginName) {
135 var yetiBoxes = $('[data-yeti-box]'),
136 plugNames = ['dropdown', 'tooltip', 'reveal'];
137
138 if(pluginName){
139 if(typeof pluginName === 'string'){
140 plugNames.push(pluginName);
141 }else if(typeof pluginName === 'object' && typeof pluginName[0] === 'string'){
142 plugNames.concat(pluginName);
143 }else{
144 console.error('Plugin names must be strings');
145 }
146 }
147 if(yetiBoxes.length){
148 let listeners = plugNames.map((name) => {
149 return `closeme.zf.${name}`;
150 }).join(' ');
151
152 $(window).off(listeners).on(listeners, Triggers.Listeners.Global.closeMeListener);
153 }
154}
155
156function debounceGlobalListener(debounce, trigger, listener) {
157 let timer, args = Array.prototype.slice.call(arguments, 3);
158 $(window).off(trigger).on(trigger, function(e) {
159 if (timer) { clearTimeout(timer); }
160 timer = setTimeout(function(){
161 listener.apply(null, args);
162 }, debounce || 10);//default time to emit scroll event
163 });
164}
165
166Triggers.Initializers.addResizeListener = function(debounce){
167 let $nodes = $('[data-resize]');
168 if($nodes.length){
169 debounceGlobalListener(debounce, 'resize.zf.trigger', Triggers.Listeners.Global.resizeListener, $nodes);
170 }
171}
172
173Triggers.Initializers.addScrollListener = function(debounce){
174 let $nodes = $('[data-scroll]');
175 if($nodes.length){
176 debounceGlobalListener(debounce, 'scroll.zf.trigger', Triggers.Listeners.Global.scrollListener, $nodes);
177 }
178}
179
180Triggers.Initializers.addMutationEventsListener = function($elem) {
181 if(!MutationObserver){ return false; }
182 let $nodes = $elem.find('[data-resize], [data-scroll], [data-mutate]');
183
184 //element callback
185 var listeningElementsMutation = function (mutationRecordsList) {
186 var $target = $(mutationRecordsList[0].target);
187
188 //trigger the event handler for the element depending on type
189 switch (mutationRecordsList[0].type) {
190 case "attributes":
191 if ($target.attr("data-events") === "scroll" && mutationRecordsList[0].attributeName === "data-events") {
192 $target.triggerHandler('scrollme.zf.trigger', [$target, window.pageYOffset]);
193 }
194 if ($target.attr("data-events") === "resize" && mutationRecordsList[0].attributeName === "data-events") {
195 $target.triggerHandler('resizeme.zf.trigger', [$target]);
196 }
197 if (mutationRecordsList[0].attributeName === "style") {
198 $target.closest("[data-mutate]").attr("data-events","mutate");
199 $target.closest("[data-mutate]").triggerHandler('mutateme.zf.trigger', [$target.closest("[data-mutate]")]);
200 }
201 break;
202
203 case "childList":
204 $target.closest("[data-mutate]").attr("data-events","mutate");
205 $target.closest("[data-mutate]").triggerHandler('mutateme.zf.trigger', [$target.closest("[data-mutate]")]);
206 break;
207
208 default:
209 return false;
210 //nothing
211 }
212 };
213
214 if ($nodes.length) {
215 //for each element that needs to listen for resizing, scrolling, or mutation add a single observer
216 for (var i = 0; i <= $nodes.length - 1; i++) {
217 var elementObserver = new MutationObserver(listeningElementsMutation);
218 elementObserver.observe($nodes[i], { attributes: true, childList: true, characterData: false, subtree: true, attributeFilter: ["data-events", "style"] });
219 }
220 }
221}
222
223Triggers.Initializers.addSimpleListeners = function() {
224 let $document = $(document);
225
226 Triggers.Initializers.addOpenListener($document);
227 Triggers.Initializers.addCloseListener($document);
228 Triggers.Initializers.addToggleListener($document);
229 Triggers.Initializers.addCloseableListener($document);
230 Triggers.Initializers.addToggleFocusListener($document);
231
232}
233
234Triggers.Initializers.addGlobalListeners = function() {
235 let $document = $(document);
236 Triggers.Initializers.addMutationEventsListener($document);
237 Triggers.Initializers.addResizeListener();
238 Triggers.Initializers.addScrollListener();
239 Triggers.Initializers.addClosemeListener();
240}
241
242
243Triggers.init = function($, Foundation) {
244 if (typeof($.triggersInitialized) === 'undefined') {
245 let $document = $(document);
246
247 if(document.readyState === "complete") {
248 Triggers.Initializers.addSimpleListeners();
249 Triggers.Initializers.addGlobalListeners();
250 } else {
251 $(window).on('load', () => {
252 Triggers.Initializers.addSimpleListeners();
253 Triggers.Initializers.addGlobalListeners();
254 });
255 }
256
257
258 $.triggersInitialized = true;
259 }
260
261 if(Foundation) {
262 Foundation.Triggers = Triggers;
263 // Legacy included to be backwards compatible for now.
264 Foundation.IHearYou = Triggers.Initializers.addGlobalListeners
265 }
266}
267
268export {Triggers};