'use strict'; import $ from 'jquery'; import { onLoad } from './foundation.core.utils'; import { Motion } from './foundation.util.motion'; const MutationObserver = (function () { var prefixes = ['WebKit', 'Moz', 'O', 'Ms', '']; for (var i=0; i < prefixes.length; i++) { if (`${prefixes[i]}MutationObserver` in window) { return window[`${prefixes[i]}MutationObserver`]; } } return false; }()); const triggers = (el, type) => { el.data(type).split(' ').forEach(id => { $(`#${id}`)[ type === 'close' ? 'trigger' : 'triggerHandler'](`${type}.zf.trigger`, [el]); }); }; var Triggers = { Listeners: { Basic: {}, Global: {} }, Initializers: {} } Triggers.Listeners.Basic = { openListener: function() { triggers($(this), 'open'); }, closeListener: function() { let id = $(this).data('close'); if (id) { triggers($(this), 'close'); } else { $(this).trigger('close.zf.trigger'); } }, toggleListener: function() { let id = $(this).data('toggle'); if (id) { triggers($(this), 'toggle'); } else { $(this).trigger('toggle.zf.trigger'); } }, closeableListener: function(e) { e.stopPropagation(); let animation = $(this).data('closable'); if(animation !== ''){ Motion.animateOut($(this), animation, function() { $(this).trigger('closed.zf'); }); }else{ $(this).fadeOut().trigger('closed.zf'); } }, toggleFocusListener: function() { let id = $(this).data('toggle-focus'); $(`#${id}`).triggerHandler('toggle.zf.trigger', [$(this)]); } }; // Elements with [data-open] will reveal a plugin that supports it when clicked. Triggers.Initializers.addOpenListener = ($elem) => { $elem.off('click.zf.trigger', Triggers.Listeners.Basic.openListener); $elem.on('click.zf.trigger', '[data-open]', Triggers.Listeners.Basic.openListener); } // Elements with [data-close] will close a plugin that supports it when clicked. // If used without a value on [data-close], the event will bubble, allowing it to close a parent component. Triggers.Initializers.addCloseListener = ($elem) => { $elem.off('click.zf.trigger', Triggers.Listeners.Basic.closeListener); $elem.on('click.zf.trigger', '[data-close]', Triggers.Listeners.Basic.closeListener); } // Elements with [data-toggle] will toggle a plugin that supports it when clicked. Triggers.Initializers.addToggleListener = ($elem) => { $elem.off('click.zf.trigger', Triggers.Listeners.Basic.toggleListener); $elem.on('click.zf.trigger', '[data-toggle]', Triggers.Listeners.Basic.toggleListener); } // Elements with [data-closable] will respond to close.zf.trigger events. Triggers.Initializers.addCloseableListener = ($elem) => { $elem.off('close.zf.trigger', Triggers.Listeners.Basic.closeableListener); $elem.on('close.zf.trigger', '[data-closeable], [data-closable]', Triggers.Listeners.Basic.closeableListener); } // Elements with [data-toggle-focus] will respond to coming in and out of focus Triggers.Initializers.addToggleFocusListener = ($elem) => { $elem.off('focus.zf.trigger blur.zf.trigger', Triggers.Listeners.Basic.toggleFocusListener); $elem.on('focus.zf.trigger blur.zf.trigger', '[data-toggle-focus]', Triggers.Listeners.Basic.toggleFocusListener); } // More Global/complex listeners and triggers Triggers.Listeners.Global = { resizeListener: function($nodes) { if(!MutationObserver){//fallback for IE 9 $nodes.each(function(){ $(this).triggerHandler('resizeme.zf.trigger'); }); } //trigger all listening elements and signal a resize event $nodes.attr('data-events', "resize"); }, scrollListener: function($nodes) { if(!MutationObserver){//fallback for IE 9 $nodes.each(function(){ $(this).triggerHandler('scrollme.zf.trigger'); }); } //trigger all listening elements and signal a scroll event $nodes.attr('data-events', "scroll"); }, closeMeListener: function(e, pluginId){ let plugin = e.namespace.split('.')[0]; let plugins = $(`[data-${plugin}]`).not(`[data-yeti-box="${pluginId}"]`); plugins.each(function(){ let _this = $(this); _this.triggerHandler('close.zf.trigger', [_this]); }); } } // Global, parses whole document. Triggers.Initializers.addClosemeListener = function(pluginName) { var yetiBoxes = $('[data-yeti-box]'), plugNames = ['dropdown', 'tooltip', 'reveal']; if(pluginName){ if(typeof pluginName === 'string'){ plugNames.push(pluginName); }else if(typeof pluginName === 'object' && typeof pluginName[0] === 'string'){ plugNames.concat(pluginName); }else{ console.error('Plugin names must be strings'); } } if(yetiBoxes.length){ let listeners = plugNames.map((name) => { return `closeme.zf.${name}`; }).join(' '); $(window).off(listeners).on(listeners, Triggers.Listeners.Global.closeMeListener); } } function debounceGlobalListener(debounce, trigger, listener) { let timer, args = Array.prototype.slice.call(arguments, 3); $(window).off(trigger).on(trigger, function(e) { if (timer) { clearTimeout(timer); } timer = setTimeout(function(){ listener.apply(null, args); }, debounce || 10);//default time to emit scroll event }); } Triggers.Initializers.addResizeListener = function(debounce){ let $nodes = $('[data-resize]'); if($nodes.length){ debounceGlobalListener(debounce, 'resize.zf.trigger', Triggers.Listeners.Global.resizeListener, $nodes); } } Triggers.Initializers.addScrollListener = function(debounce){ let $nodes = $('[data-scroll]'); if($nodes.length){ debounceGlobalListener(debounce, 'scroll.zf.trigger', Triggers.Listeners.Global.scrollListener, $nodes); } } Triggers.Initializers.addMutationEventsListener = function($elem) { if(!MutationObserver){ return false; } let $nodes = $elem.find('[data-resize], [data-scroll], [data-mutate]'); //element callback var listeningElementsMutation = function (mutationRecordsList) { var $target = $(mutationRecordsList[0].target); //trigger the event handler for the element depending on type switch (mutationRecordsList[0].type) { case "attributes": if ($target.attr("data-events") === "scroll" && mutationRecordsList[0].attributeName === "data-events") { $target.triggerHandler('scrollme.zf.trigger', [$target, window.pageYOffset]); } if ($target.attr("data-events") === "resize" && mutationRecordsList[0].attributeName === "data-events") { $target.triggerHandler('resizeme.zf.trigger', [$target]); } if (mutationRecordsList[0].attributeName === "style") { $target.closest("[data-mutate]").attr("data-events","mutate"); $target.closest("[data-mutate]").triggerHandler('mutateme.zf.trigger', [$target.closest("[data-mutate]")]); } break; case "childList": $target.closest("[data-mutate]").attr("data-events","mutate"); $target.closest("[data-mutate]").triggerHandler('mutateme.zf.trigger', [$target.closest("[data-mutate]")]); break; default: return false; //nothing } }; if ($nodes.length) { //for each element that needs to listen for resizing, scrolling, or mutation add a single observer for (var i = 0; i <= $nodes.length - 1; i++) { var elementObserver = new MutationObserver(listeningElementsMutation); elementObserver.observe($nodes[i], { attributes: true, childList: true, characterData: false, subtree: true, attributeFilter: ["data-events", "style"] }); } } } Triggers.Initializers.addSimpleListeners = function() { let $document = $(document); Triggers.Initializers.addOpenListener($document); Triggers.Initializers.addCloseListener($document); Triggers.Initializers.addToggleListener($document); Triggers.Initializers.addCloseableListener($document); Triggers.Initializers.addToggleFocusListener($document); } Triggers.Initializers.addGlobalListeners = function() { let $document = $(document); Triggers.Initializers.addMutationEventsListener($document); Triggers.Initializers.addResizeListener(); Triggers.Initializers.addScrollListener(); Triggers.Initializers.addClosemeListener(); } Triggers.init = function ($, Foundation) { onLoad($(window), function () { if ($.triggersInitialized !== true) { Triggers.Initializers.addSimpleListeners(); Triggers.Initializers.addGlobalListeners(); $.triggersInitialized = true; } }); if(Foundation) { Foundation.Triggers = Triggers; // Legacy included to be backwards compatible for now. Foundation.IHearYou = Triggers.Initializers.addGlobalListeners } } export {Triggers};