PageSourceSearch

https://www.consortia.com/scripts/jquery.backstretch.js?m=1773664704

js consortia.com collected 2026-10-02 02:18:42 UTC 22,049 bytes, 646 lines download raw bytes

1/*! Backstretch - v2.0.3 - 2012-11-30
2* http://srobbin.com/jquery-plugins/backstretch/
3* Copyright (c) 2012 Scott Robbin; Licensed MIT
4
5Modified by Ben Beckford and Jonathan Spicer, Chris Curd Design Ltd. to allow for HTML5 video
6Video sources should be passed in the images parameter as an array with an
7image as the first option for backwards compatability with browsers that
8don't support HTML5 video. Also added support for Vimeo Embed links.
9*/
10
11;(function ($, window, undefined) {
12  'use strict';
13
14  /* PLUGIN DEFINITION
15   * ========================= */
16
17  $.fn.backstretch = function (images, options) {
18    // We need at least one image
19    if (images === undefined || images.length === 0) {
20      $.error("No images were supplied for Backstretch");
21    }
22
23    /*
24     * Scroll the page one pixel to get the right window height on iOS
25     * Pretty harmless for everyone else
26    */
27    if ($(window).scrollTop() === 0 ) {
28      window.scrollTo(0, 0);
29    }
30
31    return this.each(function () {
32      var $this = $(this)
33        , obj = $this.data('backstretch');
34
35      // If we've already attached Backstretch to this element, remove the old instance.
36      if (obj) {
37        // Merge the old options with the new
38        options = $.extend(obj.options, options);
39
40        // Remove the old instance
41        obj.destroy(true);
42      }
43
44      obj = new Backstretch(this, images, options);
45      $this.data('backstretch', obj);
46    });
47  };
48
49  // If no element is supplied, we'll attach to body
50  $.backstretch = function (images, options) {
51    // Return the instance
52    return $('body')
53            .backstretch(images, options)
54            .data('backstretch');
55  };
56
57  // Custom selector
58  $.expr[':'].backstretch = function(elem) {
59    return $(elem).data('backstretch') !== undefined;
60  };
61
62  /* DEFAULTS
63   * ========================= */
64
65  $.fn.backstretch.defaults = {
66      centeredX: true        // Should we center the image on the X axis?
67    , centeredY: true        // Should we center the image on the Y axis?
68    , duration: 5000         // Amount of time in between slides (if slideshow)
69    , fade: 0                // Speed of fade transition between slides
70    , wrapInner: false       // Add backstretchInner wrapper inside backstretch wrapper (useful for parallax)
71    , resizeMethod: 'cover'  // Based off of background size property
72    , videoMuted: true       // Add muted="true" to videos
73    , videoPreload: "auto"   // video preload="" attribute
74    , videoOnPlaying: ""     // video onplaying="" attribute
75    , videoPoster: ""        // video poster="" attribute
76    , videoAutoPlay: "true"  // video autoplay="" attribute
77    , videoLoop: "loop"      // video loop="" attribute
78    , videoID: ""            // video id="" attribute
79    , lazyLoad: false           // video lazyload attribute
80    , videoPlaysInline: true    // video playsinline attribute
81    , alignRight: false    // Align the image or video to the right hand side
82  };
83
84  /* STYLES
85   *
86   * Baked-in styles that we'll apply to our elements.
87   * In an effort to keep the plugin simple, these are not exposed as options.
88   * That said, anyone can override these in their own stylesheet.
89   * ========================= */
90  var styles = {
91      wrap: {
92          left: 0
93        , top: 0
94        , overflow: 'hidden'
95        , margin: 0
96        , padding: 0
97        , height: '100%'
98        , width: '100%'
99        , zIndex: -999999
100        , '-webkit-transform': 'translateZ(0)'
101        , '-moz-transform'   : 'translateZ(0)'
102        , '-ms-transform'    : 'translateZ(0)'
103        , '-o-transform'     : 'translateZ(0)'
104        , 'transform'        : 'translateZ(0)'
105        , '-webkit-backface-visibility': 'hidden'
106        , '-moz-backface-visibility'   : 'hidden'
107        , '-ms-backface-visibility'    : 'hidden'
108        , 'backface-visibility'        : 'hidden'
109      }
110    ,
111      wrapInner: {
112          position: 'relative'
113      }
114    , img: {
115          position: 'absolute'
116        , display: 'none'
117        , margin: 0
118        , padding: 0
119        , border: 'none'
120        , width: 'auto'
121        , height: 'auto'
122        , maxWidth: 'none'
123        , zIndex: -999999
124        , '-webkit-transform': 'translateZ(0)'
125        , '-moz-transform'   : 'translateZ(0)'
126        , '-ms-transform'    : 'translateZ(0)'
127        , '-o-transform'     : 'translateZ(0)'
128        , 'transform'        : 'translateZ(0)'
129        , '-webkit-backface-visibility': 'hidden'
130        , '-moz-backface-visibility'   : 'hidden'
131        , '-ms-backface-visibility'    : 'hidden'
132        , 'backface-visibility'        : 'hidden'
133      }
134    , video: {
135          position: 'absolute'
136        , display: 'block'
137        , margin: 0
138        , padding: 0
139        , border: 'none'
140        , width: 'auto'
141        , height: 'auto'
142        , maxWidth: 'none'
143        , zIndex: -999999
144      }
145  };
146
147  /* CLASS DEFINITION
148   * ========================= */
149  var Backstretch = function (container, images, options) {
150    this.options = $.extend({}, $.fn.backstretch.defaults, options || {});
151
152    /* In its simplest form, we allow Backstretch to be called on an image path.
153     * e.g. $.backstretch('/path/to/image.jpg')
154     * So, we need to turn this back into an array.
155     */
156    var lazyLoadAttribute = this.options.lazyLoad ? ' loading="lazy"' : '';
157    this.images = $.isArray(images) ? images : [images];
158
159    if(this.images.length > 1)
160    {
161      this.videoMode = isVideo(this.images[1]);
162      if (!this.videoMode) {
163        this.vimeoMode = isVimeo(this.images[1]);
164      }
165    }
166    else
167    {
168      this.videoMode = isVideo(this.images[0]);
169      if (!this.videoMode) {
170        this.vimeoMode = isVimeo(this.images[0]);
171      }
172    }
173
174    if(this.videoMode || this.vimeoMode)
175    {
176      if(!document.createElement('video').canPlayType)
177      {
178        this.videoMode = false;
179        this.images = this.images[0];
180        this.images = [this.images];
181      }
182      else
183      {
184        if(!isVideo(this.images[0]) && !isVimeo(this.images[0]))
185        {
186          this.images.shift();
187        }
188      }
189    }
190
191
192    if(!this.videoMode && !this.vimeoMode)
193    {
194      // Preload images
195      $.each(this.images, function () {
196        $('<img '+lazyLoadAttribute+' />')[0].src = this;
197      });
198    }
199
200    // Convenience reference to know if the container is body.
201    this.isBody = container === document.body;
202
203    /* We're keeping track of a few different elements
204     *
205     * Container: the element that Backstretch was called on.
206     * Wrap: a DIV that we place the image into, so we can hide the overflow.
207     * Root: Convenience reference to help calculate the correct height.
208     */
209    this.$container = $(container);
210
211    if(options == null || options.resizeMethod == null)
212    {
213      if(this.$container.css('background-size') == 'contain')
214      {
215        this.options.resizeMethod = 'contain';
216      }
217    }
218
219	this.$wrap = $('<div class="backstretch"></div>').css(styles.wrap).appendTo(this.$container);
220
221	if(this.options.wrapInner || this.vimeoMode)
222	{
223		if (this.vimeoMode) {
224			this.$wrapInner = $('<div class="backstretchInner loading"></div>').css(styles.wrapInner).appendTo(this.$wrap);
225		}
226		else {
227			this.$wrapInner = $('<div class="backstretchInner"></div>').css(styles.wrapInner).appendTo(this.$wrap);
228		}
229	}
230	else
231	{
232		this.$wrapInner = this.$wrap;
233	}
234	this.$root = this.isBody ? supportsFixedPosition ? $(window) : $(document) : this.$container;
235
236    // Non-body elements need some style adjustments
237    if (!this.isBody) {
238      // If the container is statically positioned, we need to make it relative,
239      // and if no zIndex is defined, we should set it to zero.
240      var position = this.$container.css('position')
241        , zIndex = this.$container.css('zIndex');
242
243      this.$container.css({
244          position: position === 'static' ? 'relative' : position
245        , zIndex: zIndex === 'auto' ? 0 : zIndex
246        , background: 'none'
247      });
248
249      // Needs a higher z-index
250      this.$wrap.css({zIndex: -999998});
251    }
252
253    // Fixed or absolute positioning?
254    this.$wrap.css({
255      position: this.isBody && supportsFixedPosition ? 'fixed' : 'absolute'
256    });
257
258    // Set the first image
259    this.index = 0;
260    this.show(this.index);
261
262    // Listen for resize
263    $(window).on('resize.backstretch', $.proxy(this.resize, this))
264             .on('orientationchange.backstretch', $.proxy(function () {
265                // Need to do this in order to get the right window height
266                if (this.isBody && window.pageYOffset === 0) {
267                  window.scrollTo(0, 1);
268                  this.resize();
269                }
270             }, this));
271  };
272
273  /* PUBLIC METHODS
274   * ========================= */
275  Backstretch.prototype = {
276      resize: function () {
277        try {
278          var bgCSS = {left: 0, top: 0, right: 'auto'}
279            , rootWidth = this.isBody ? this.$root.width() : this.$root.innerWidth()
280            , bgWidth = rootWidth
281            , rootHeight = this.isBody ? ( window.innerHeight ? window.innerHeight : this.$root.height() ) : this.$root.innerHeight()
282            , bgHeight = bgWidth / this.$img.data('ratio')
283            , bgOffset;
284
285            if(this.options.resizeMethod == 'contain')
286            {
287              // Make adjustments based on image ratio
288              if (bgHeight >= rootHeight) {
289                  bgHeight = rootHeight;
290                  bgWidth = bgHeight * this.$img.data('ratio');
291
292                  bgOffset = (rootWidth - bgWidth) / 2;
293                  if(this.options.centeredX) {
294                    bgCSS.left = bgOffset + 'px';
295                  }
296                  if(this.options.alignRight)
297                  {
298                    bgCSS.left = 'auto';
299                    bgCSS.right = 0;
300                  }
301              } else {
302                  bgOffset = (rootHeight - bgHeight) / 2;
303                  if(this.options.centeredY) {
304                    bgCSS.top = bgOffset + 'px';
305                  }
306              }
307            }
308            else
309            {
310              // Make adjustments based on image ratio
311              if (bgHeight >= rootHeight) {
312                  bgOffset = (bgHeight - rootHeight) / 2;
313                  if(this.options.centeredY) {
314                    bgCSS.top = '-' + bgOffset + 'px';
315                  }
316              } else {
317                  bgHeight = rootHeight;
318                  bgWidth = bgHeight * this.$img.data('ratio');
319                  bgOffset = (bgWidth - rootWidth) / 2;
320                  if(this.options.centeredX) {
321                    bgCSS.left = '-' + bgOffset + 'px';
322                  }
323                  if(this.options.alignRight)
324                  {
325                    bgCSS.left = 'auto';
326                    bgCSS.right = 0;
327                  }
328              }
329            }
330            this.$wrap.css({width: rootWidth, height: rootHeight})
331            this.$wrap.find('img:not(.deleteable),video:not(.deleteable),iframe:not(.deleteable)').css({width: bgWidth, height: bgHeight}).css(bgCSS);
332        } catch(err) {
333            // IE7 seems to trigger resize before the image is loaded.
334            // This try/catch block is a hack to let it fail gracefully.
335        }
336
337        return this;
338      }
339
340      // Show the slide at a certain position
341    , show: function (index) {
342        // Validate index
343        if (Math.abs(index) > this.images.length - 1) {
344          return;
345        } else {
346          this.index = index;
347        }
348
349        // Vars
350        var self = this
351          , oldImage = self.$wrap.find('img').addClass('deleteable')
352          , evt = $.Event('backstretch.show', {
353              relatedTarget: self.$container[0]
354            });
355        var lazyLoad = this.options.lazyLoad == true;
356        var lazyLoadClass = this.options.lazyLoad ? ' class="lazy"' : '';
357        var lazyLoadAttribute = this.options.lazyLoad ? ' loading="lazy"' : '';
358        // Pause the slideshow
359        clearInterval(self.interval);
360
361        if(this.videoMode)
362        {
363          var mutevideo = this.options.videoMuted == true;
364
365          // New video
366          var onplayingAttr = ' onplaying="'+this.options.videoOnPlaying.toString()+'"';
367          var posterAttr = ' poster="'+this.options.videoPoster.toString()+'"';
368          var preloadAttr = ' preload="'+this.options.videoPreload.toString()+'"';
369          var autoplayAttr = this.options.videoAutoPlay ? ' autoplay="autoplay"' : '';
370          var mutedAttr = this.options.videoMuted ? ' muted="muted"' : '';
371          var loopAttr = this.options.videoLoop ? ' loop="loop"' : '';
372          var inlineAttr = this.options.videoPlaysInline ? ' playsinline' : '';
373          var videoSourceHTML = "";
374          for(var j in self.images)
375          {
376            if (lazyLoad) {
377              videoSourceHTML += getVideoSourceHTML(self.images[j], true);
378            } else {
379              videoSourceHTML += getVideoSourceHTML(self.images[j]);
380            }
381          }
382          self.$img = $('<video '+lazyLoadClass+' id="'+this.options.videoID+'" canplay="false" '+loopAttr+autoplayAttr+posterAttr+onplayingAttr+preloadAttr+mutedAttr+inlineAttr+'>'+videoSourceHTML+'</video>')
383                        .css(styles.video)
384                        .bind('canplay', function (e) {
385                          var imgWidth = this.width || $(e.target).width()
386                            , imgHeight = this.height || $(e.target).height();
387
388                          // Save the ratio
389                          $(this).data('ratio', imgWidth / imgHeight);
390
391                          // Resize
392                          self.resize();
393
394                        })
395                        .appendTo(self.$wrapInner);
396
397                        // fix for browsers that don't support the muted attribute
398                        self.$img[0].muted = mutevideo ? "muted" : "";
399
400
401        }
402        else if(this.vimeoMode)
403        {
404
405    		var vimeoSource = "";
406    		for(var j in self.images)
407    		{
408    			vimeoSource += self.images[j];
409        }
410          var vimeoWidth = 0;
411          var vimeoHeight = 0;
412          self.$img = $('<iframe style="opacity: 0" canplay="false" src="'+vimeoSource+'?background=1&dnt=1" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>')
413            .css(styles.video).on('load', function (e) {
414              var imgWidth = this.width || $(e.target).width(),
415              imgHeight = this.height || $(e.target).height();
416            }).appendTo(self.$wrapInner);
417            $(window).on('beforeunload', function() {
418              self.$img.css('opacity', '0');
419          });
420          // This is the URL of the video you want to load
421          var videourl = vimeoSource;
422          // This is the oEmbed endpoint for Vimeo (we're using JSON)
423          var endpoint = 'https://vimeo.com/api/oembed.json';
424          // Put together the URL
425          videourl = endpoint + '?url=' + encodeURIComponent(videourl);
426          $.ajax({
427            type : 'GET',
428            url : videourl,
429            format: "json",
430            dataType : 'jsonP',
431            success : function(data){
432              vimeoWidth = data.width - 1;
433              vimeoHeight = data.height;
434              // Save the ratio
435                self.$img.data('ratio', vimeoWidth / vimeoHeight);
436              // Resize
437                self.resize();
438            },
439            error : function(XMLHttpRequest, textStatus, errorThrown) {
440              //alert('error');
441              }
442          });
443
444          $.getScript( "https://player.vimeo.com/api/player.js", function( data, textStatus, jqxhr ) {
445            var player = new Vimeo.Player(self.$img[0]);
446            player.on('timeupdate', function(data) {
447              if (data.seconds > 0) {
448                self.$img.stop().animate({'opacity': '1'}, 500);
449                self.$img.parent().removeClass('loading');
450                player.off('timeupdate');
451              }
452            });
453          });
454
455				}
456				else
457				{
458					// New image
459					self.$img = $('<img '+lazyLoadAttribute+' />')
460												.css(styles.img)
461												.bind('load', function (e) {
462													var imgWidth = this.width || $(e.target).width()
463														, imgHeight = this.height || $(e.target).height();
464
465                          // Save the ratio
466                          $(this).data('ratio', imgWidth / imgHeight);
467
468                          // Add alt text
469                          $(this).attr('alt', self.$container.data('alt'));
470
471                          if(self.options.resizeMethod == 'contain')
472                          {
473                            oldImage.fadeOut(self.options.speed || self.options.fade);
474                          }
475
476                          // Show the image, then delete the old one
477                          // "speed" option has been deprecated, but we want backwards compatibilty
478                          $(this).fadeIn(self.options.speed || self.options.fade, function () {
479                            oldImage.remove();
480
481                            // Resume the slideshow
482                            if (!self.paused) {
483                              self.cycle();
484                            }
485
486                            // Trigger the event
487                            self.$container.trigger(evt, self);
488                          });
489
490                          // Resize
491                          self.resize();
492                        })
493                        .appendTo(self.$wrapInner);
494
495          // Hack for IE img onload event
496          self.$img.attr('src', self.images[index]);
497        }
498        return self;
499      }
500
501    , next: function () {
502        // Next slide
503        return this.show(this.index < this.images.length - 1 ? this.index + 1 : 0);
504      }
505
506    , prev: function () {
507        // Previous slide
508        return this.show(this.index === 0 ? this.images.length - 1 : this.index - 1);
509      }
510
511    , pause: function () {
512        // Pause the slideshow
513        this.paused = true;
514        return this;
515      }
516
517    , resume: function () {
518        // Resume the slideshow
519        this.paused = false;
520        this.next();
521        return this;
522      }
523
524    , cycle: function () {
525        // Start/resume the slideshow
526        if(this.images.length > 1) {
527          // Clear the interval, just in case
528          clearInterval(this.interval);
529
530          this.interval = setInterval($.proxy(function () {
531            // Check for paused slideshow
532            if (!this.paused) {
533              this.next();
534            }
535          }, this), this.options.duration);
536        }
537        return this;
538      }
539
540    , destroy: function (preserveBackground) {
541        // Stop the resize events
542        $(window).off('resize.backstretch orientationchange.backstretch');
543
544        // Clear the interval
545        clearInterval(this.interval);
546
547        // Remove Backstretch
548        if(!preserveBackground) {
549          this.$wrap.remove();
550        }
551        this.$container.removeData('backstretch');
552      }
553  };
554
555  var isVideo = function(path) {
556    var ext = path.split(".");
557    ext = ext[ext.length-1];
558    ext = ext.split("?").shift();
559    if(ext == "mp4" || ext == "m4v" || ext == "ogv" || ext == "flv" || ext == "webm" || ext == "asf" || ext == "avi" || path.indexOf("progressive_redirect") >= 0)
560    {
561      return(true);
562    }
563    else
564    {
565      return(false);
566    }
567  }
568
569  var isVimeo= function(path) {
570    if (path.indexOf("player.vimeo") >= 0)
571    {
572      return(true);
573    }
574    else
575    {
576      return(false);
577    }
578  }
579
580  var getVideoSourceHTML = function(path, lazy) {
581    var ext = path.split(".");
582    ext = ext[ext.length-1];
583    ext = ext.split("?").shift();
584    if (path.indexOf("progressive_redirect") >= 0) // Added to accomodate
585    {
586      ext = 'mp4';
587    }
588    if (lazy) {
589      return('<source data-src="'+path+'" type="video/'+ext+'">');
590    } else {
591      return('<source src="'+path+'" type="video/'+ext+'">');
592    }
593  }
594
595  /* SUPPORTS FIXED POSITION?
596   *
597   * Based on code from jQuery Mobile 1.1.0
598   * http://jquerymobile.com/
599   *
600   * In a nutshell, we need to figure out if fixed positioning is supported.
601   * Unfortunately, this is very difficult to do on iOS, and usually involves
602   * injecting content, scrolling the page, etc.. It's ugly.
603   * jQuery Mobile uses this workaround. It's not ideal, but works.
604   *
605   * Modified to detect IE6
606   * ========================= */
607
608  var supportsFixedPosition = (function () {
609    var ua = navigator.userAgent
610      , platform = navigator.platform
611        // Rendering engine is Webkit, and capture major version
612      , wkmatch = ua.match( /AppleWebKit\/([0-9]+)/ )
613      , wkversion = !!wkmatch && wkmatch[ 1 ]
614      , ffmatch = ua.match( /Fennec\/([0-9]+)/ )
615      , ffversion = !!ffmatch && ffmatch[ 1 ]
616      , operammobilematch = ua.match( /Opera Mobi\/([0-9]+)/ )
617      , omversion = !!operammobilematch && operammobilematch[ 1 ]
618      , iematch = ua.match( /MSIE ([0-9]+)/ )
619      , ieversion = !!iematch && iematch[ 1 ];
620
621    return !(
622      // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
623      ((platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1  || platform.indexOf( "iPod" ) > -1 ) && wkversion && wkversion < 534) ||
624
625      // Opera Mini
626      (window.operamini && ({}).toString.call( window.operamini ) === "[object OperaMini]") ||
627      (operammobilematch && omversion < 7458) ||
628
629      //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
630      (ua.indexOf( "Android" ) > -1 && wkversion && wkversion < 533) ||
631
632      // Firefox Mobile before 6.0 -
633      (ffversion && ffversion < 6) ||
634
635      // WebOS less than 3
636      ("palmGetResource" in window && wkversion && wkversion < 534) ||
637
638      // MeeGo
639      (ua.indexOf( "MeeGo" ) > -1 && ua.indexOf( "NokiaBrowser/8.5.0" ) > -1) ||
640
641      // IE6
642      (ieversion && ieversion <= 6)
643    );
644  }());
645
646}(jQuery, window));

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.