Issues (149)

js/util/siteapp.util.triggers.js (7 issues)

1
'use strict';
2
3
const MutationObserver = (function () {
4
  var prefixes = ['WebKit', 'Moz', 'O', 'Ms', ''];
5
  for (var i=0; i < prefixes.length; i++) {
6
    if (`${prefixes[i]}MutationObserver` in window) {
7
      return window[`${prefixes[i]}MutationObserver`];
8
    }
9
  }
10
  return false;
11
}());
12
13
const triggers = (el, type) => {
14
  el.data(type).split(' ').forEach(id => {
15
    $(`#${id}`)[ type === 'close' ? 'trigger' : 'triggerHandler'](`${type}.${Triggers._ns}.trigger`, [el]);
16
  });
17
};
18
19
var Triggers = {
20
  Listeners: {
21
    Basic: {},
22
    Global: {}
23
  },
24
  Initializers: {}
25
}
26
27
Triggers.Listeners.Basic  = {
28
  openListener: function() {
29
    triggers($(this), 'open');
30
  },
31
  closeListener: function() {
32
    let id = $(this).data('close');
33
    if (id) {
34
      triggers($(this), 'close');
35
    }
36
    else {
37
      $(this).trigger('close.'+Triggers._ns+'.trigger');
38
    }
39
  },
40
  toggleListener: function() {
41
    let id = $(this).data('toggle');
42
    if (id) {
43
      triggers($(this), 'toggle');
44
    } else {
45
      $(this).trigger('toggle.'+Triggers._ns+'.trigger');
46
    }
47
  },
48
  closeableListener: function(e) {
49
    e.stopPropagation();
50
    let animation = $(this).data('closable');
51
52
    if(animation !== ''){
53
      Motion.animateOut($(this), animation, function() {
0 ignored issues
show
The variable Motion seems to be never declared. If this is a global, consider adding a /** global: Motion */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
54
        $(this).trigger('closed.'+Triggers._ns);
55
      });
56
    }else{
57
      $(this).fadeOut().trigger('closed.'+Triggers._ns);
58
    }
59
  },
60
  toggleFocusListener: function() {
61
    let id = $(this).data('toggle-focus');
62
    $(`#${id}`).triggerHandler('toggle.'+Triggers._ns+'.trigger', [$(this)]);
63
  }
64
};
65
66
// Elements with [data-open] will reveal a plugin that supports it when clicked.
67
Triggers.Initializers.addOpenListener = ($elem) => {
68
  $elem.off('click.'+Triggers._ns+'.trigger', Triggers.Listeners.Basic.openListener);
69
  $elem.on('click.'+Triggers._ns+'.trigger', '[data-open]', Triggers.Listeners.Basic.openListener);
70
}
71
72
// Elements with [data-close] will close a plugin that supports it when clicked.
73
// If used without a value on [data-close], the event will bubble, allowing it to close a parent component.
74
Triggers.Initializers.addCloseListener = ($elem) => {
75
  $elem.off('click.'+Triggers._ns+'.trigger', Triggers.Listeners.Basic.closeListener);
76
  $elem.on('click.'+Triggers._ns+'.trigger', '[data-close]', Triggers.Listeners.Basic.closeListener);
77
}
78
79
// Elements with [data-toggle] will toggle a plugin that supports it when clicked.
80
Triggers.Initializers.addToggleListener = ($elem) => {
81
  $elem.off('click.'+Triggers._ns+'.trigger', Triggers.Listeners.Basic.toggleListener);
82
  $elem.on('click.'+Triggers._ns+'.trigger', '[data-toggle]', Triggers.Listeners.Basic.toggleListener);
83
}
84
85
// Elements with [data-closable] will respond to close.'+Triggers._ns+'.trigger events.
86
Triggers.Initializers.addCloseableListener = ($elem) => {
87
  $elem.off('close.'+Triggers._ns+'.trigger', Triggers.Listeners.Basic.closeableListener);
88
  $elem.on('close.'+Triggers._ns+'.trigger', '[data-closeable], [data-closable]', Triggers.Listeners.Basic.closeableListener);
89
}
90
91
// Elements with [data-toggle-focus] will respond to coming in and out of focus
92
Triggers.Initializers.addToggleFocusListener = ($elem) => {
93
  $elem.off('focus.'+Triggers._ns+'.trigger blur.'+Triggers._ns+'.trigger', Triggers.Listeners.Basic.toggleFocusListener);
94
  $elem.on('focus.'+Triggers._ns+'.trigger blur.'+Triggers._ns+'.trigger', '[data-toggle-focus]', Triggers.Listeners.Basic.toggleFocusListener);
95
}
96
97
98
99
// More Global/complex listeners and triggers
100
Triggers.Listeners.Global  = {
101
  resizeListener: function($nodes) {
102
    if(!MutationObserver){//fallback for IE 9
103
      $nodes.each(function(){
104
        $(this).triggerHandler('resizeme.'+Triggers._ns+'.trigger');
105
      });
106
    }
107
    //trigger all listening elements and signal a resize event
108
    $nodes.attr('data-events', "resize");
109
  },
110
  scrollListener: function($nodes) {
111
    if(!MutationObserver){//fallback for IE 9
112
      $nodes.each(function(){
113
        $(this).triggerHandler('scrollme.'+Triggers._ns+'.trigger');
114
      });
115
    }
116
    //trigger all listening elements and signal a scroll event
117
    $nodes.attr('data-events', "scroll");
118
  },
119
  closeMeListener: function(e, pluginId){
120
    let plugin = e.namespace.split('.')[0];
121
    let plugins = $(`[data-${plugin}]`).not(`[data-yeti-box="${pluginId}"]`);
122
123
    plugins.each(function(){
124
      let _this = $(this);
125
      _this.triggerHandler('close.'+Triggers._ns+'.trigger', [_this]);
126
    });
127
  }
128
}
129
130
// Global, parses whole document.
131
Triggers.Initializers.addClosemeListener = function(pluginName) {
132
  var yetiBoxes = $('[data-yeti-box]'),
133
      plugNames = ['dropdown', 'tooltip', 'reveal'];
134
135
  if(pluginName){
136
    if(typeof pluginName === 'string'){
137
      plugNames.push(pluginName);
138
    }else if(typeof pluginName === 'object' && typeof pluginName[0] === 'string'){
139
      plugNames.concat(pluginName);
140
    }else{
141
      console.error('Plugin names must be strings');
142
    }
143
  }
144
  if(yetiBoxes.length){
145
    let listeners = plugNames.map((name) => {
146
      return `closeme.${Triggers._ns}.${name}`;
147
    }).join(' ');
148
149
    $(window).off(listeners).on(listeners, Triggers.Listeners.Global.closeMeListener);
150
  }
151
}
152
153
function debounceGlobalListener(debounce, trigger, listener) {
154
  let timer, args = Array.prototype.slice.call(arguments, 3);
155
  $(window).off(trigger).on(trigger, function(e) {
0 ignored issues
show
The parameter e is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
156
    if (timer) { clearTimeout(timer); }
157
    timer = setTimeout(function(){
158
      listener.apply(null, args);
159
    }, debounce || 10);//default time to emit scroll event
160
  });
161
}
162
163
Triggers.Initializers.addResizeListener = function(debounce){
164
  let $nodes = $('[data-resize]');
165
  if($nodes.length){
166
    debounceGlobalListener(debounce, 'resize.'+Triggers._ns+'.trigger', Triggers.Listeners.Global.resizeListener, $nodes);
167
  }
168
}
169
170
Triggers.Initializers.addScrollListener = function(debounce){
171
  let $nodes = $('[data-scroll]');
172
  if($nodes.length){
173
    debounceGlobalListener(debounce, 'scroll.'+Triggers._ns+'.trigger', Triggers.Listeners.Global.scrollListener, $nodes);
174
  }
175
}
176
177
Triggers.Initializers.addMutationEventsListener = function($elem) {
178
  if(!MutationObserver){ return false; }
179
  let $nodes = $elem.find('[data-resize], [data-scroll], [data-mutate]');
180
  //element callback
181
  var listeningElementsMutation = function (mutationRecordsList) {
182
    var $target = $(mutationRecordsList[0].target);
183
    
184
    
185
    //trigger the event handler for the element depending on type
186
    switch (mutationRecordsList[0].type) {
187
      case "attributes":
188
        if ($target.attr("data-events") === "scroll" && mutationRecordsList[0].attributeName === "data-events") {
189
          $target.triggerHandler('scrollme.'+Triggers._ns+'.trigger', [$target, window.pageYOffset]);
190
        }
191
        if ($target.attr("data-events") === "resize" && mutationRecordsList[0].attributeName === "data-events") {
192
          $target.triggerHandler('resizeme.'+Triggers._ns+'.trigger', [$target]);
193
         }
194
        if (mutationRecordsList[0].attributeName === "style") {
195
          $target.closest("[data-mutate]").attr("data-events","mutate");
196
          $target.closest("[data-mutate]").triggerHandler('mutateme.'+Triggers._ns+'.trigger', [$target.closest("[data-mutate]")]);
197
        }
198
        break;
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
199
200
      case "childList":
201
    	////console.//log('mutation closest:', $target.closest("[data-mutate]"));
202
    	// init newly inserted components
203
        $target.closest("[data-mutate]").attr("data-events","mutate");
204
        $target.closest("[data-mutate]").triggerHandler('mutateme.'+Triggers._ns+'.trigger', [$target.closest("[data-mutate]")]);
205
        if ($target.data(Triggers._ns+'Plugin')) {
206
        	var plgIn = $target.data(Triggers._ns+'Plugin');
207
        	if ( plgIn.reflow ) { 
208
            	$target.thalia('reflow');
209
        		//plgIn.reflow(); 
210
        	}
211
        } else {
212
		    $target.thalia();
213
        }
214
	    $($target.closest("[data-mutate]")).trigger('mutateme');
215
	    //$($target.closest("[data-mutate]")).trigger('mutate');
216
        break;
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
217
218
      default:
219
        return false;
220
      //nothing
221
    }
222
  };
223
224
  if ($nodes.length) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if $nodes.length is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
225
    //for each element that needs to listen for resizing, scrolling, or mutation add a single observer
226
    for (var i = 0; i <= $nodes.length - 1; i++) {
227
      var elementObserver = new MutationObserver(listeningElementsMutation);
228
      elementObserver.observe($nodes[i], { attributes: true, childList: true, characterData: false, subtree: true, attributeFilter: ["data-events", "style"] });
229
    }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
230
  }
231
}
232
233
Triggers.Initializers.addSimpleListeners = function() {
234
  let $document = $(document);
235
236
  Triggers.Initializers.addOpenListener($document);
237
  Triggers.Initializers.addCloseListener($document);
238
  Triggers.Initializers.addToggleListener($document);
239
  Triggers.Initializers.addCloseableListener($document);
240
  Triggers.Initializers.addToggleFocusListener($document);
241
242
}
243
244
Triggers.Initializers.addGlobalListeners = function() {
245
  let $document = $(document);
246
  Triggers.Initializers.addMutationEventsListener($document);
247
  Triggers.Initializers.addResizeListener();
248
  Triggers.Initializers.addScrollListener();
249
  Triggers.Initializers.addClosemeListener();
250
}
251
252
253
Triggers.init = function($, Siteapp) {
254
  if (typeof($.triggersInitialized) === 'undefined') {
255
    let $document = $(document);
0 ignored issues
show
The variable $document seems to be never used. Consider removing it.
Loading history...
256
257
    if(document.readyState === "complete") {
258
      Triggers.Initializers.addSimpleListeners();
259
      Triggers.Initializers.addGlobalListeners();
260
    } else {
261
      $(document).ready(() => {
262
      //$(window).on('load', () => {
263
        Triggers.Initializers.addSimpleListeners();
264
        Triggers.Initializers.addGlobalListeners();
265
      });
266
    }
267
268
269
    $.triggersInitialized = true;
270
  }
271
272
  if(Siteapp) {
273
    Siteapp.Triggers = Triggers;
274
    // Legacy included to be backwards compatible for now.
275
    Siteapp.IHearYou = Triggers.Initializers.addGlobalListeners
276
  }
277
}
278
279
export {Triggers};
280
281