PageSourceSearch

https://geckoalliance.com/wp-content/themes/gecko/js/jquery.youtube-background.js?v=1.0.8

js geckoalliance.com collected 2026-10-09 05:22:21 UTC 36,725 bytes, 1,138 lines download raw bytes

1/**
2 * jquery.youtube-background v1.0.14 | Nikola Stamatovic <@stamat> | MIT
3 */
4
5(function () {
6  'use strict';
7
8  function hasClass(element, className) {
9    if (element.classList) {
10      return element.classList.contains(className);
11    }
12    return new RegExp('\\b'+ className+'\\b').test(element.className);
13  }
14
15  function addClass(element, classNames) {
16    if (element.classList) {
17      const classes = classNames.split(' ');
18      for (var i = 0; i < classes.length; i++) {
19        const el_class = classes[i];
20        element.classList.add(el_class);
21      }
22      return;
23    }
24
25    if (!hasClass(element, className)) {
26      element.className += ' ' + className;
27    }
28  }
29
30  function removeClass(element, className) {
31    if (element.classList) {
32      element.classList.remove(className);
33      return;
34    }
35
36    element.className = element.className.replace(new RegExp('\\b'+ className+'\\b', 'g'), '');
37  }
38
39  function isMobile() {
40    let is_mobile = false;
41    (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) is_mobile = true;})(navigator.userAgent||navigator.vendor||window.opera);
42
43    return is_mobile;
44  }
45
46  function getRandomIntInclusive(min, max) {
47    min = Math.ceil(min);
48    max = Math.floor(max);
49    return Math.floor(Math.random() * (max - min + 1)) + min; //The maximum is inclusive and the minimum is inclusive
50  }
51
52  function parseResolutionString(res) {
53    const pts = res.split(/\s?:\s?/i);
54    const DEFAULT_RESOLUTION = 16/9;
55    if (pts.length < 2) {
56      return DEFAULT_RESOLUTION;
57    }
58
59    const w = parseInt(pts[0], 10);
60    const h = parseInt(pts[1], 10);
61
62    if (isNaN(w) || isNaN(h)) {
63      return DEFAULT_RESOLUTION;
64    }
65
66    return w/h;
67  }
68
69  function parseProperties(params, defaults, element, attr_prefix) {
70    let res_params = {};
71
72    if (!params) {
73      res_params = defaults;
74    } else {
75      for (let k in defaults) {
76        if (!params.hasOwnProperty(k)) {
77          //load in defaults if the param hasn't been set
78          res_params[k] = defaults[k];
79        }
80      }
81    }
82
83    if (!element) return res_params;
84    // load params from data attributes
85    for (let k in res_params) {
86      let data;
87
88      if (attr_prefix instanceof Array) {
89        for (var i = 0; i < attr_prefix.length; i++) {
90          const temp_data = element.getAttribute(attr_prefix[i]+k);
91          if (temp_data) {
92            data = temp_data;
93            break;
94          }
95        }
96      } else {
97        data = element.getAttribute(attr_prefix+k);
98      }
99
100      if (data !== undefined && data !== null) {
101        data = data === 'false' ? false : data;
102        data = /^\d+$/.test(data) ? parseInt(data, 10) : data;
103        data = /^\d+\.\d+$/.test(data) ? parseFloat(data) : data;
104        res_params[k] = data;
105      }
106    }
107
108    return res_params;
109  }
110
111  const tag = document.createElement('script');
112  tag.src = "https://www.youtube.com/player_api";
113  const firstScriptTag = document.getElementsByTagName('script')[0];
114  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
115
116  function YoutubeBackground(elem, params, id, uid) {
117    this.is_mobile = isMobile();
118
119    this.element = elem;
120    this.ytid = id;
121    this.uid = uid;
122    this.player = null;
123    this.buttons = {};
124
125    this.state = {};
126    this.state.play = false;
127    this.state.mute = false;
128
129    this.params = {};
130
131    const DEFAULTS = {
132      'pause': false, //deprecated
133      'play-button': false,
134      'mute-button': false,
135      'autoplay': true,
136      'muted': true,
137      'loop': true,
138      'mobile': false,
139      'load-background': true,
140      'resolution': '16:9',
141      'onStatusChange': function() {},
142      'inline-styles': true,
143      'fit-box': false,
144      'offset': 200,
145      'start-at': 0,
146      'end-at': 0,
147      'poster': null
148    };
149
150    this.__init__ = function () {
151      if (!this.ytid) {
152        return;
153      }
154
155      this.params = parseProperties(params, DEFAULTS, this.element, ['data-ytbg-', 'data-vbg-']);
156      //pause deprecated
157      if (this.params.pause) {
158        this.params['play-button'] = this.params.pause;
159      }
160      this.params.resolution_mod = parseResolutionString(this.params.resolution);
161      this.state.playing = this.params.autoplay;
162      this.state.muted = this.params.muted;
163      this.buildHTML();
164      this.injectPlayer();
165
166
167      if (this.params['play-button']) {
168        this.generateActionButton({
169          name: 'play',
170          className: 'play-toggle',
171          innerHtml: '<i class="fa"></i>',
172          initialState: false,
173          stateClassName: 'paused',
174          condition_parameter: 'autoplay',
175          stateChildClassNames: ['fa-pause-circle', 'fa-play-circle'],
176          actions: ['play', 'pause']
177        });
178      }
179
180      if (this.params['mute-button']) {
181        this.generateActionButton({
182          name: 'mute',
183          className: 'mute-toggle',
184          innerHtml: '<i class="fa"></i>',
185          initialState: true,
186          stateClassName: 'muted',
187          condition_parameter: 'muted',
188          stateChildClassNames: ['fa-volume-up', 'fa-volume-mute'],
189          actions: ['unmute', 'mute']
190        });
191      }
192    };
193
194    this.__init__();
195  }
196
197  YoutubeBackground.prototype.initYTPlayer = function () {
198    const self = this;
199    if (window.hasOwnProperty('YT')) {
200      this.player = new YT.Player(this.uid, {
201        events: {
202          'onReady': function(event) {
203            self.onVideoPlayerReady(event);
204          },
205          'onStateChange': function(event) {
206            self.onVideoStateChange(event);
207          },
208          'onError' : function(event) {
209            //console.error('player_api', event);
210          }
211        }
212      });
213    }
214  };
215
216  YoutubeBackground.prototype.seekTo = function (seconds) {
217    if (seconds > 0) {
218      this.player.seekTo(seconds, true);
219    }
220  };
221
222  YoutubeBackground.prototype.onVideoPlayerReady = function (event) {
223    if (this.params.autoplay) {
224      this.seekTo(this.params['start-at']);
225      this.player.playVideo();
226    }
227  };
228
229  YoutubeBackground.prototype.onVideoStateChange = function (event) {
230    if (event.data === 0 && this.params.loop) {
231      this.seekTo(this.params['start-at']);
232      this.player.playVideo();
233    }
234
235    if (event.data === -1 && this.params.autoplay) {
236      this.seekTo(this.params['start-at']);
237      this.player.playVideo();
238      this.element.dispatchEvent(new CustomEvent('video-background-play', { bubbles: true, detail: this }));
239    }
240
241    if (event.data === 1) {
242      this.iframe.style.opacity = 1;
243    }
244
245    this.params["onStatusChange"](event);
246  };
247
248  YoutubeBackground.prototype.injectPlayer = function () {
249    this.iframe = document.createElement('iframe');
250    this.iframe.setAttribute('frameborder', 0);
251    this.iframe.setAttribute('allow', 'autoplay; mute');
252    let src = `https://www.youtube.com/embed/${this.ytid}?&enablejsapi=1&disablekb=1&controls=0&rel=0&iv_load_policy=3&cc_load_policy=0&playsinline=1&showinfo=0&modestbranding=1&fs=0`;
253
254    if (this.params.muted) {
255      src += '&mute=1';
256    }
257
258    if (this.params.autoplay) {
259      src += '&autoplay=1';
260    }
261
262    if (this.params.loop) {
263      src += '&loop=1';
264    }
265
266    if (this.params['end-at'] > 0) {
267      src += `&end=${this.params['end-at']}`;
268    }
269
270    this.iframe.src = src;
271
272    if (this.uid) {
273      this.iframe.id = this.uid;
274    }
275
276    if (this.params['inline-styles']) {
277      this.iframe.style.top = '50%';
278      this.iframe.style.left = '50%';
279      this.iframe.style.transform = 'translateX(-50%) translateY(-50%)';
280      this.iframe.style.position = 'absolute';
281      this.iframe.style.opacity = 0;
282    }
283
284    this.element.appendChild(this.iframe);
285
286    if (this.params['fit-box']) {
287      this.iframe.style.width = '100%';
288      this.iframe.style.height = '100%';
289    } else {
290      const self = this;
291
292      //TODO❗️: maybe a spacer or at least add requestAnimationFrame
293      function onResize() {
294        const h = self.iframe.parentNode.offsetHeight + self.params.offset; // since showinfo is deprecated and ignored after September 25, 2018. we add +200 to hide it in the overflow
295        const w = self.iframe.parentNode.offsetWidth + self.params.offset;
296        const res = self.params.resolution_mod;
297
298        if (res > w/h) {
299          self.iframe.style.width = h*res + 'px';
300          self.iframe.style.height = h + 'px';
301        } else {
302          self.iframe.style.width = w + 'px';
303          self.iframe.style.height = w/res + 'px';
304        }
305      }
306
307      if (window.hasOwnProperty('ResizeObserver')) {
308        const resize_observer = new ResizeObserver(() => {
309          window.requestAnimationFrame(onResize);
310        });
311        resize_observer.observe(this.element);
312      } else {
313        window.addEventListener('resize', () => {
314          window.requestAnimationFrame(onResize);
315        });
316      }
317      onResize();
318    }
319  };
320
321  YoutubeBackground.prototype.buildHTML = function () {
322    const parent = this.element.parentNode;
323    // wrap
324    addClass(this.element, 'youtube-background video-background');
325
326    //set css rules
327    const wrapper_styles = {
328      "height" : "100%",
329      "width" : "100%",
330      "z-index": "0",
331      "position": "absolute",
332      "overflow": "hidden",
333      "top": 0, // added by @insad
334      "left": 0,
335      "bottom": 0,
336      "right": 0
337    };
338
339    if (!this.params['mute-button']) {
340      wrapper_styles["pointer-events"] = "none"; // avoid right mouse click popup menu
341    }
342
343    if (this.params['load-background'] || this.params['poster']) {
344      if (this.params['load-background']) wrapper_styles['background-image'] = 'url(https://img.youtube.com/vi/'+this.ytid+'/maxresdefault.jpg)';
345      if (this.params['poster']) wrapper_styles['background-image'] = this.params['poster'];
346      wrapper_styles['background-size'] = 'cover';
347      wrapper_styles['background-repeat'] = 'no-repeat';
348      wrapper_styles['background-position'] = 'center';
349    }
350
351    if (this.params['inline-styles']) {
352      for (let property in wrapper_styles) {
353        this.element.style[property] = wrapper_styles[property];
354      }
355      
356      if (!['absolute', 'fixed', 'relative', 'sticky'].indexOf(parent.style.position)) {
357        parent.style.position = 'relative';
358      }
359    }
360
361    if (this.is_mobile && !this.params.mobile) {
362      return this.element;
363    }
364
365    // set play/mute controls wrap
366    if (this.params['play-button'] || this.params['mute-button']) {
367      const controls = document.createElement('div');
368      controls.className = 'video-background-controls';
369
370      controls.style.position = 'absolute';
371      controls.style.top = '10px';
372      controls.style.right = '10px';
373      controls.style['z-index'] = 2;
374
375      this.controls_element = controls;
376      parent.appendChild(controls);
377    }
378
379    return this.element;
380  };
381
382  YoutubeBackground.prototype.play = function () {
383    //TODO: solve this with ARIA toggle states. P.S. warning repetitive code!!!
384    if (this.buttons.hasOwnProperty('play')) {
385      const btn_obj = this.buttons.play;
386      removeClass(btn_obj.element, btn_obj.button_properties.stateClassName);
387      addClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[0]);
388      removeClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[1]);
389    }
390
391    if (this.player) {
392      if (this.params['start-at'] && this.player.getCurrentTime() < this.params['start-at'] ) {
393        this.seekTo(this.params['start-at']);
394      }
395      this.player.playVideo();
396      this.element.dispatchEvent(new CustomEvent('video-background-play', { bubbles: true, detail: this }));
397    }
398  };
399
400  YoutubeBackground.prototype.pause = function () {
401    //TODO: solve this with ARIA toggle states
402    if (this.buttons.hasOwnProperty('play')) {
403      const btn_obj = this.buttons.play;
404      addClass(btn_obj.element, btn_obj.button_properties.stateClassName);
405      removeClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[0]);
406      addClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[1]);
407    }
408
409    if (this.player) {
410      this.player.pauseVideo();
411      this.element.dispatchEvent(new CustomEvent('video-background-pause', { bubbles: true, detail: this }));
412    }
413  };
414
415  YoutubeBackground.prototype.unmute = function () {
416    //TODO: solve this with ARIA toggle states
417    if (this.buttons.hasOwnProperty('mute')) {
418      const btn_obj = this.buttons.mute;
419      removeClass(btn_obj.element, btn_obj.button_properties.stateClassName);
420      addClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[0]);
421      removeClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[1]);
422    }
423
424    if (this.player) {
425      this.player.unMute();
426      this.element.dispatchEvent(new CustomEvent('video-background-unmute', { bubbles: true, detail: this }));
427    }
428  };
429
430  YoutubeBackground.prototype.mute = function () {
431    //TODO: solve this with ARIA toggle states
432    if (this.buttons.hasOwnProperty('mute')) {
433      const btn_obj = this.buttons.mute;
434      addClass(btn_obj.element, btn_obj.button_properties.stateClassName);
435      removeClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[0]);
436      addClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[1]);
437    }
438
439    if (this.player) {
440      this.player.mute();
441      this.element.dispatchEvent(new CustomEvent('video-background-mute', { bubbles: true, detail: this }));
442    }
443  };
444
445  //TODO: refactor states to be equal for all buttons
446  YoutubeBackground.prototype.generateActionButton = function (obj) {
447    const btn = document.createElement('button');
448    btn.className = obj.className;
449    btn.innerHTML = obj.innerHtml;
450    addClass(btn.firstChild, obj.stateChildClassNames[0]);
451
452    //TODO: solve this with ARIA toggle states
453    if (this.params[obj.condition_parameter] === obj.initialState) {
454      addClass(btn, obj.stateClassName);
455      removeClass(btn.firstChild, obj.stateChildClassNames[0]);
456      addClass(btn.firstChild, obj.stateChildClassNames[1]);
457    }
458
459    const self = this;
460    btn.addEventListener('click', function(e) {
461      if (hasClass(this, obj.stateClassName)) {
462        self.state[obj.name] = false;
463        self[obj.actions[0]]();
464      } else {
465        self.state[obj.name] = true;
466        self[obj.actions[1]]();
467      }
468    });
469
470    this.buttons[obj.name] = {
471      element: btn,
472      button_properties: obj
473    };
474
475    this.controls_element.appendChild(btn);
476  };
477
478  function VimeoBackground(elem, params, id, uid) {
479    this.is_mobile = isMobile();
480
481    this.element = elem;
482    this.vid = id;
483    this.uid = uid;
484    this.player = null;
485    this.buttons = {};
486
487    this.state = {};
488    this.state.play = false;
489    this.state.mute = false;
490
491    this.params = {};
492
493    const DEFAULTS = {
494  //    'pause': false, //deprecated
495  //    'play-button': false,
496  //    'mute-button': false,
497      'autoplay': true,
498      'muted': true,
499      'loop': true,
500      'mobile': false,
501  //    'load-background': true,
502      'resolution': '16:9',
503      'inline-styles': true,
504      'fit-box': false,
505      'offset': 200,
506      'start-at': 0,
507      'poster': null
508    };
509
510    this.__init__ = function () {
511      if (!this.vid) {
512        return;
513      }
514
515      this.params = parseProperties(params, DEFAULTS, this.element, ['data-ytbg-', 'data-vbg-']);
516      //pause deprecated
517      if (this.params.pause) {
518        this.params['play-button'] = this.params.pause;
519      }
520      this.params.resolution_mod = parseResolutionString(this.params.resolution);
521      this.state.playing = this.params.autoplay;
522      this.state.muted = this.params.muted;
523
524      this.buildHTML();
525      this.injectPlayer();
526    };
527
528    this.__init__();
529  }
530
531  VimeoBackground.prototype.injectPlayer = function () {
532    this.iframe = document.createElement('iframe');
533    this.iframe.setAttribute('frameborder', 0);
534    this.iframe.setAttribute('allow', ['autoplay; mute']);
535    let src = 'https://player.vimeo.com/video/'+this.vid+'?background=1&controls=0';
536
537    if (this.params.muted) {
538      src += '&muted=1';
539    }
540
541    if (this.params.autoplay) {
542      src += '&autoplay=1';
543    }
544
545    if (this.params.loop) {
546      src += '&loop=1&autopause=0';
547    }
548
549    //WARN❗️ this is a hash not a query param
550    if (this.params['start-at']) {
551      src += '#t=' + this.params['start-at'] + 's';
552    }
553
554    this.iframe.src = src;
555
556    if (this.uid) {
557      this.iframe.id = this.uid;
558    }
559
560    if (this.params['load-background'] || this.params['poster']) {
561      //if (this.params['load-background']) wrapper_styles['background-image'] = 'url(https://img.youtube.com/vi/'+this.ytid+'/maxresdefault.jpg)';
562      if (this.params['poster']) wrapper_styles['background-image'] = this.params['poster'];
563      wrapper_styles['background-size'] = 'cover';
564      wrapper_styles['background-repeat'] = 'no-repeat';
565      wrapper_styles['background-position'] = 'center';
566    }
567
568    if (this.params['inline-styles']) {
569      this.iframe.style.top = '50%';
570      this.iframe.style.left = '50%';
571      this.iframe.style.transform = 'translateX(-50%) translateY(-50%)';
572      this.iframe.style.position = 'absolute';
573      this.iframe.style.opacity = 1;
574    }
575
576    this.element.appendChild(this.iframe);
577
578    if (this.params['fit-box']) {
579      this.iframe.style.width = '100%';
580      this.iframe.style.height = '100%';
581    } else {
582      const self = this;
583
584      const onResize = function() {
585        const h = self.iframe.parentNode.offsetHeight + self.params.offset; // since showinfo is deprecated and ignored after September 25, 2018. we add +200 to hide it in the overflow
586        const w = self.iframe.parentNode.offsetWidth + self.params.offset;
587        const res = self.params.resolution_mod;
588
589        if (res > w/h) {
590          self.iframe.style.width = h*res + 'px';
591          self.iframe.style.height = h + 'px';
592        } else {
593          self.iframe.style.width = w + 'px';
594          self.iframe.style.height = w/res + 'px';
595        }
596      };
597
598      if (window.hasOwnProperty('ResizeObserver')) {
599        const resize_observer = new ResizeObserver(() => {
600          window.requestAnimationFrame(onResize);
601        });
602        resize_observer.observe(this.element);
603      } else {
604        window.addEventListener('resize', () => {
605          window.requestAnimationFrame(onResize);
606        });
607      }
608      onResize();
609    }
610  };
611
612  VimeoBackground.prototype.buildHTML = function () {
613    const parent = this.element.parentNode;
614    // wrap
615    addClass(this.element, 'youtube-background');
616
617    //set css rules
618    const wrapper_styles = {
619      "height" : "100%",
620      "width" : "100%",
621      "z-index": "0",
622      "position": "absolute",
623      "overflow": "hidden",
624      "top": 0, // added by @insad
625      "left": 0,
626      "bottom": 0,
627      "right": 0
628    };
629
630    if (this.params['load-background'] || this.params['poster']) {
631      //if (this.params['load-background']) wrapper_styles['background-image'] = 'url(https://img.youtube.com/vi/'+this.ytid+'/maxresdefault.jpg)';
632      if (this.params['poster']) wrapper_styles['background-image'] = this.params['poster'];
633      wrapper_styles['background-size'] = 'cover';
634      wrapper_styles['background-repeat'] = 'no-repeat';
635      wrapper_styles['background-position'] = 'center';
636    }
637
638    if (!this.params['mute-button']) {
639      wrapper_styles["pointer-events"] = "none"; // avoid right mouse click popup menu
640    }
641
642    if (this.params['load-background']) {
643      //TODO: wrapper_styles['background-image'] = 'url(https://img.youtube.com/vi/'+this.vid+'/maxresdefault.jpg)';
644      wrapper_styles['background-size'] = 'cover';
645      wrapper_styles['background-repeat'] = 'no-repeat';
646      wrapper_styles['background-position'] = 'center';
647    }
648
649    if (this.params['inline-styles']) {
650      for (let property in wrapper_styles) {
651        this.element.style[property] = wrapper_styles[property];
652      }
653
654      if (!['absolute', 'fixed', 'relative', 'sticky'].indexOf(parent.style.position)) {
655        parent.style.position = 'relative';
656      }
657    }
658
659    return this.element;
660  };
661
662  function VideoBackground(elem, params, vid_data, uid) {
663    this.is_mobile = isMobile();
664
665    this.element = elem;
666    this.link = vid_data.link;
667    this.ext = vid_data.id;
668    this.uid = uid;
669    this.player = null;
670    this.buttons = {};
671
672    this.state = {};
673    this.state.play = false;
674    this.state.mute = false;
675
676    this.params = {};
677
678    const MIME_MAP = {
679      'ogv' : 'video/ogg',
680      'ogm' : 'video/ogg',
681      'ogg' : 'video/ogg',
682      'avi' : 'video/avi',
683      'mp4' : 'video/mp4',
684      'webm' : 'video/webm'
685    };
686
687    const DEFAULTS = {
688      'pause': false, //deprecated
689      'play-button': false,
690      'mute-button': false,
691      'autoplay': true,
692      'muted': true,
693      'loop': true,
694      'mobile': false,
695      'resolution': '16:9',
696      'inline-styles': true,
697      'fit-box': false,
698      'offset': 200,
699  //    'start-at': 0,
700  //    'end-at': 0,
701      'poster': null
702    };
703
704    this.__init__ = function () {
705      if (!this.link || !this.ext) {
706        return;
707      }
708
709      this.mime = MIME_MAP[this.ext.toLowerCase()];
710      this.params = parseProperties(params, DEFAULTS, this.element, ['data-ytbg-', 'data-vbg-']);
711      //pause deprecated
712      if (this.params.pause) {
713        this.params['play-button'] = this.params.pause;
714      }
715      this.params.resolution_mod = parseResolutionString(this.params.resolution);
716      this.state.playing = this.params.autoplay;
717      this.state.muted = this.params.muted;
718
719      this.buildHTML();
720      this.injectPlayer();
721
722
723      if (this.params['play-button']) {
724        this.generateActionButton({
725          name: 'play',
726          className: 'play-toggle',
727          innerHtml: '<i class="fa"></i>',
728          initialState: false,
729          stateClassName: 'paused',
730          condition_parameter: 'autoplay',
731          stateChildClassNames: ['fa-pause-circle', 'fa-play-circle'],
732          actions: ['play', 'pause']
733        });
734      }
735
736      if (this.params['mute-button']) {
737        this.generateActionButton({
738          name: 'mute',
739          className: 'mute-toggle',
740          innerHtml: '<i class="fa"></i>',
741          initialState: true,
742          stateClassName: 'muted',
743          condition_parameter: 'muted',
744          stateChildClassNames: ['fa-volume-up', 'fa-volume-mute'],
745          actions: ['unmute', 'mute']
746        });
747      }
748    };
749
750    this.__init__();
751  }
752
753  VideoBackground.prototype.seekTo = function (seconds) {
754    if (this.player.hasOwnProperty('fastSeek')) {
755      this.player.fastSeek(seconds);
756      return;
757    }
758    this.player.currentTime = seconds;
759  };
760
761  VideoBackground.prototype.injectPlayer = function () {
762    this.player = document.createElement('video');
763    this.player.muted = this.params.muted;
764    this.player.autoplay = this.params.autoplay;
765    this.player.loop = this.params.loop;
766    this.player.playsinline = true;
767
768    this.player.setAttribute('id', this.uid);
769
770    if (this.params['inline-styles']) {
771      this.player.style.top = '50%';
772      this.player.style.left = '50%';
773      this.player.style.transform = 'translateX(-50%) translateY(-50%)';
774      this.player.style.position = 'absolute';
775      this.player.style.opacity = 0;
776
777      this.player.addEventListener('canplay', (e) => {
778        e.target.style.opacity = 1;
779      });
780    }
781
782    const self = this;
783    /*
784    this.player.addEventListener('canplay', (e) => {
785      if (self.params['start-at'] && self.params.autoplay) {
786        self.seekTo(self.params['start-at']);
787      }
788    });
789
790    this.player.addEventListener('canplaythrough', (e) => {
791      if (self.params['end-at'] > 0) {
792      self.player.addEventListener('timeupdate', (e) => {
793        if (self.params['end-at'] >= self.player.currentTime) {
794          self.seekTo(self.params['start-at']);
795        }
796      });
797    }
798    });
799    */
800
801    const source = document.createElement('source');
802    source.setAttribute('src', this.link);
803    source.setAttribute('type', this.mime);
804    this.player.appendChild(source);
805    this.element.appendChild(this.player);
806
807    if (this.params['fit-box']) {
808      this.player.style.width = '100%';
809      this.player.style.height = '100%';
810    } else {
811      //TODO❗️: maybe a spacer or at least add requestAnimationFrame
812      function onResize() {
813        const h = self.player.parentNode.offsetHeight + self.params.offset; // since showinfo is deprecated and ignored after September 25, 2018. we add +200 to hide it in the overflow
814        const w = self.player.parentNode.offsetWidth + self.params.offset;
815        const res = self.params.resolution_mod;
816
817        if (res > w/h) {
818          self.player.style.width = h*res + 'px';
819          self.player.style.height = h + 'px';
820        } else {
821          self.player.style.width = w + 'px';
822          self.player.style.height = w/res + 'px';
823        }
824      }
825
826      if (window.hasOwnProperty('ResizeObserver')) {
827        const resize_observer = new ResizeObserver(() => {
828          window.requestAnimationFrame(onResize);
829        });
830        resize_observer.observe(this.element);
831      } else {
832        window.addEventListener('resize', () => {
833          window.requestAnimationFrame(onResize);
834        });
835      }
836      onResize();
837    }
838  };
839
840  VideoBackground.prototype.buildHTML = function () {
841    const parent = this.element.parentNode;
842    // wrap
843    addClass(this.element, 'video-background');
844
845    //set css rules
846    const wrapper_styles = {
847      "height" : "100%",
848      "width" : "100%",
849      "z-index": "0",
850      "position": "absolute",
851      "overflow": "hidden",
852      "top": 0, // added by @insad
853      "left": 0,
854      "bottom": 0,
855      "right": 0
856    };
857
858    if (!this.params['mute-button']) {
859      wrapper_styles["pointer-events"] = "none"; // avoid right mouse click popup menu
860    }
861
862    if (this.params['load-background'] || this.params['poster']) {
863      if (this.params['poster']) wrapper_styles['background-image'] = `url('${this.params['poster']}')`;
864      wrapper_styles['background-size'] = 'cover';
865      wrapper_styles['background-repeat'] = 'no-repeat';
866      wrapper_styles['background-position'] = 'center';
867    }
868
869    if (this.params['inline-styles']) {
870      for (let property in wrapper_styles) {
871        this.element.style[property] = wrapper_styles[property];
872      }
873
874      if (!['absolute', 'fixed', 'relative', 'sticky'].indexOf(parent.style.position)) {
875        parent.style.position = 'relative';
876      }
877    }
878
879    if (this.is_mobile && !this.params.mobile) {
880      return this.element;
881    }
882
883    // set play/mute controls wrap
884    if (this.params['play-button'] || this.params['mute-button']) {
885      const controls = document.createElement('div');
886      controls.className = 'video-background-controls';
887
888      controls.style.position = 'absolute';
889      controls.style.top = '10px';
890      controls.style.right = '10px';
891      controls.style['z-index'] = 2;
892
893      this.controls_element = controls;
894      parent.appendChild(controls);
895    }
896
897    return this.element;
898  };
899
900  VideoBackground.prototype.play = function () {
901    //TODO: solve this with ARIA toggle states. P.S. warning repetitive code!!!
902    if (this.buttons.hasOwnProperty('play')) {
903      const btn_obj = this.buttons.play;
904      removeClass(btn_obj.element, btn_obj.button_properties.stateClassName);
905      addClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[0]);
906      removeClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[1]);
907    }
908
909    if (this.player) {
910      /* if (this.params['start-at'] && this.player.currentTime < this.params['start-at'] ) {
911        this.seekTo(this.params['start-at']);
912      } */
913      this.player.play();
914      this.element.dispatchEvent(new CustomEvent('video-background-play', { bubbles: true, detail: this }));
915    }
916  };
917
918  VideoBackground.prototype.pause = function () {
919    //TODO: solve this with ARIA toggle states
920    if (this.buttons.hasOwnProperty('play')) {
921      const btn_obj = this.buttons.play;
922      addClass(btn_obj.element, btn_obj.button_properties.stateClassName);
923      removeClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[0]);
924      addClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[1]);
925    }
926
927    if (this.player) {
928      this.player.pause();
929      this.element.dispatchEvent(new CustomEvent('video-background-pause', { bubbles: true, detail: this }));
930    }
931  };
932
933  VideoBackground.prototype.unmute = function () {
934    //TODO: solve this with ARIA toggle states
935    if (this.buttons.hasOwnProperty('mute')) {
936      const btn_obj = this.buttons.mute;
937      removeClass(btn_obj.element, btn_obj.button_properties.stateClassName);
938      addClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[0]);
939      removeClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[1]);
940    }
941
942    if (this.player) {
943      this.player.muted = false;
944      this.element.dispatchEvent(new CustomEvent('video-background-unmute', { bubbles: true, detail: this }));
945    }
946  };
947
948  VideoBackground.prototype.mute = function () {
949    //TODO: solve this with ARIA toggle states
950    if (this.buttons.hasOwnProperty('mute')) {
951      const btn_obj = this.buttons.mute;
952      addClass(btn_obj.element, btn_obj.button_properties.stateClassName);
953      removeClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[0]);
954      addClass(btn_obj.element.firstChild, btn_obj.button_properties.stateChildClassNames[1]);
955    }
956
957    if (this.player) {
958      this.player.muted = true;
959      this.element.dispatchEvent(new CustomEvent('video-background-mute', { bubbles: true, detail: this }));
960    }
961  };
962
963  //TODO: refactor states to be equal for all buttons
964  VideoBackground.prototype.generateActionButton = function (obj) {
965    const btn = document.createElement('button');
966    btn.className = obj.className;
967    btn.innerHTML = obj.innerHtml;
968    addClass(btn.firstChild, obj.stateChildClassNames[0]);
969
970    //TODO: solve this with ARIA toggle states
971    if (this.params[obj.condition_parameter] === obj.initialState) {
972      addClass(btn, obj.stateClassName);
973      removeClass(btn.firstChild, obj.stateChildClassNames[0]);
974      addClass(btn.firstChild, obj.stateChildClassNames[1]);
975    }
976
977    const self = this;
978    btn.addEventListener('click', function(e) {
979      if (hasClass(this, obj.stateClassName)) {
980        self.state[obj.name] = false;
981        self[obj.actions[0]]();
982      } else {
983        self.state[obj.name] = true;
984        self[obj.actions[1]]();
985      }
986    });
987
988    this.buttons[obj.name] = {
989      element: btn,
990      button_properties: obj
991    };
992
993    this.controls_element.appendChild(btn);
994  };
995
996  function VideoBackgrounds(selector, params) {
997    this.elements = selector;
998
999    if (typeof selector === 'string') {
1000      this.elements = document.querySelectorAll(selector);
1001    }
1002
1003    this.index = {};
1004    this.re = {};
1005    this.re.YOUTUBE = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/ ]{11})/i;
1006    this.re.VIMEO = /(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|video\/|)(\d+)(?:[a-zA-Z0-9_\-]+)?/i;
1007    this.re.VIDEO = /\/[^\/]+\.(mp4|ogg|ogv|ogm|webm|avi)\s?$/i;
1008
1009    this.__init__ = function () {
1010      for (let i = 0; i < this.elements.length; i++) {
1011        const element = this.elements[i];
1012
1013        const link = element.getAttribute('data-youtube') || element.getAttribute('data-vbg');
1014        const vid_data = this.getVidID(link);
1015
1016        if (!vid_data) {
1017          continue;
1018        }
1019
1020        const uid = this.generateUID(vid_data.id);
1021
1022        if (!uid) {
1023          continue;
1024        }
1025
1026        switch (vid_data.type) {
1027          case 'YOUTUBE':
1028            const yb = new YoutubeBackground(element, params, vid_data.id, uid);
1029            this.index[uid] = yb;
1030            break;
1031          case 'VIMEO':
1032            const vm = new VimeoBackground(element, params, vid_data.id, uid);
1033            this.index[uid] = vm;
1034            break;
1035          case 'VIDEO':
1036            const vid = new VideoBackground(element, params, vid_data, uid);
1037            this.index[uid] = vid;
1038            break;
1039        }
1040      }
1041
1042      this.initYTPlayers(/*function() {
1043        //TODO: FIX!
1044        if (params &&
1045          (params.hasOwnProperty('activity_timeout')
1046            || params.hasOwnProperty('inactivity_timeout'))) {
1047          this.activity_monitor = new ActivityMonitor(function () {
1048              self.playVideos();
1049            }, function() {
1050              self.pauseVideos();
1051            },
1052            params ? params.activity_timeout : null,
1053            params ? params.inactivity_timeout : null,
1054            ['mousemove', 'scroll']
1055          );
1056        }
1057      }*/);
1058    };
1059
1060    this.__init__();
1061  }
1062
1063  VideoBackgrounds.prototype.getVidID = function (link) {
1064    if (link !== undefined && link !== null) {
1065      for (let k in this.re) {
1066        const pts = link.match(this.re[k]);
1067
1068        if (pts && pts.length) {
1069          this.re[k].lastIndex = 0;
1070          return {
1071            id: pts[1],
1072            type: k,
1073            regex_pts: pts,
1074            link: link
1075          };
1076        }
1077      }
1078    }
1079
1080    return null;
1081  };
1082
1083
1084  VideoBackgrounds.prototype.generateUID = function (pref) {
1085    //index the instance
1086    let uid = pref +'-'+ getRandomIntInclusive(0, 9999);
1087    while (this.index.hasOwnProperty(uid)) {
1088      uid = pref +'-'+ getRandomIntInclusive(0, 9999);
1089    }
1090
1091    return uid;
1092  };
1093
1094  VideoBackgrounds.prototype.pauseVideos = function () {
1095    for (let k in this.index) {
1096      this.index[k].pause();
1097    }
1098  };
1099
1100  VideoBackgrounds.prototype.playVideos = function () {
1101    for (let k in this.index) {
1102      this.index[k].play();
1103    }
1104  };
1105
1106  VideoBackgrounds.prototype.initYTPlayers = function (callback) {
1107    const self = this;
1108
1109    window.onYouTubeIframeAPIReady = function () {
1110      for (let k in self.index) {
1111        if (self.index[k] instanceof YoutubeBackground) {
1112          self.index[k].initYTPlayer();
1113        }
1114      }
1115
1116      if (callback) {
1117        setTimeout(callback, 100);
1118      }
1119    };
1120
1121    if (window.hasOwnProperty('YT') && window.YT.loaded) {
1122      window.onYouTubeIframeAPIReady();
1123    }
1124  };
1125
1126  if (typeof jQuery == 'function') {
1127    (function ($) {
1128      $.fn.youtube_background = function (params) {
1129        const $this = $(this);
1130        new VideoBackgrounds(this, params);
1131        return $this;
1132      };
1133    })(jQuery);
1134  }
1135
1136  window.VideoBackgrounds = VideoBackgrounds;
1137
1138})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.