PageSourceSearch

https://fmtempo.com.br/views/api/photobox-master/photobox/jquery.photobox.js

js fmtempo.com.br collected 2026-10-03 01:08:40 UTC 48,247 bytes, 1,119 lines download raw bytes

1/*!
2    photobox v1.9.2
3    (c) 2012 Yair Even Or
4
5    Do not sell this software or use it as part of a package which is sold
6*/
7
8
9;(function($, doc, win){
10    "use strict";
11
12    var Photobox, photobox, options, images=[], imageLinks, activeImage = -1, activeURL, lastActive, activeType, prevImage, nextImage, thumbsStripe, docElm, APControl, changeImage,
13        $doc = $(doc),
14        $win = $(win),
15        isOldIE = !('placeholder' in doc.createElement('input')),
16        noPointerEvents = (function(){ var el = $('<p>')[0]; el.style.cssText = 'pointer-events:auto'; return !el.style.pointerEvents})(),
17        isTouchDevice = false, // assume "false" unless there's a touch
18        thumbsContainerWidth, thumbsTotalWidth, activeThumb = $(),
19        blankImg = "data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",
20        transformOrigin = getPrefixed('transformOrigin'),
21        transition = getPrefixed('transition'),
22        transitionend = "transitionend webkitTransitionEnd oTransitionEnd otransitionend",
23          // Normalize rAF
24        raf = window.requestAnimationFrame
25           || window.webkitRequestAnimationFrame
26           || window.mozRequestAnimationFrame
27           || window.msRequestAnimationFrame
28           || function(cb) { return window.setTimeout(cb, 1000 / 60); },
29
30        // Preload images
31        preload = {}, preloadPrev = new Image(), preloadNext = new Image(),
32        // DOM elements
33        closeBtn, image, video, prevBtn, nextBtn, thumbsToggler, caption, captionText, pbLoader, autoplayBtn, thumbs, wrapper,
34
35        defaults = {
36            single        : false,        // if "true" - gallery will only show a single image, with no way to navigate
37            beforeShow    : null,         // Callback before showing an image
38            afterClose    : null,         // Callback after closing the gallery
39            loop          : true,         // Allows to navigate between first and last images
40            thumb         : null,         // A relative path from the link to the thumbnail (if it's not inside the link)
41            thumbs        : true,         // Show gallery thumbnails below the presented photo
42            thumbAttr     : 'data-src',   // Attribute to get the image for the thumbnail from
43            captionTmpl   : '<div class="title">{title}</div><div class="counter">({currentImageIdx}/{totalImagesCount})</div>',
44            autoplay      : false,        // should autoplay on first time or not
45            time          : 3000,         // autoplay interval, in miliseconds (less than 1000 will hide the autoplay button)
46            history       : false,        // should use history hashing if possible (HTML5 API)
47            hideFlash     : true,         // Hides flash elements on the page when photobox is activated. NOTE: flash elements must have wmode parameter set to "opaque" or "transparent" if this is set to false
48            zoomable      : true,         // disable/enable mousewheel image zooming
49            rotatable     : true,         // allow rotation of the image
50            wheelNextPrev : true,         // change image using mousewheel left/right
51            keys          : {
52                close : [27, 88, 67],    // keycodes to close photobox, default: esc (27), 'x' (88), 'c' (67)
53                prev  : [37, 80],        // keycodes to navigate to the previous image, default: Left arrow (37), 'p' (80)
54                next  : [39, 78]         // keycodes to navigate to the next image, default: Right arrow (39), 'n' (78)
55            }
56        },
57
58
59        // DOM structure
60        overlay = $('<div id="pbOverlay">').append(
61                        thumbsToggler = $('<input type="checkbox" id="pbThumbsToggler" checked hidden>'),
62                        pbLoader = $('<div class="pbLoader"><b></b><b></b><b></b></div>'),
63                        prevBtn = $('<div id="pbPrevBtn" class="prevNext"><b></b></div>').on('click', next_prev),
64                        nextBtn = $('<div id="pbNextBtn" class="prevNext"><b></b></div>').on('click', next_prev),
65                        wrapper = $('<div class="pbWrapper">').append(  // gives Perspective
66                            image = $('<img>'),
67                            video = $('<div class="pbVideo">')
68                        ),
69                        closeBtn = $('<div id="pbCloseBtn">').on('click', close)[0],
70                        autoplayBtn = $('<div id="pbAutoplayBtn">').append(
71                            $('<div class="pbProgress">')
72                        ),
73                        caption = $('<div id="pbCaption">').append(
74                            '<label for="pbThumbsToggler" title="Show/hide thumbnails"></label>',
75                            '<button title="Rotate right" class="rotateBtn">&#10227;</button>',
76                            captionText = $('<div class="pbCaptionText">'),
77                            thumbs = $('<div>').addClass('pbThumbs')
78                        )
79                    );
80
81    ///////////////////////////////////////////////
82    // Should remove this and use underscore/lodash if possible
83
84    function throttle(callback, duration){
85        var wait = false;
86        return function(){
87            if( !wait ){
88                callback.call();
89                wait = true;
90                setTimeout(function(){wait = false; }, duration);
91            }
92        }
93    }
94
95    ///////////////////////////////////////////////
96    // Initialization (on DOM ready)
97
98    function prepareDOM( force ){
99        // do not procceed if already called, unless forced to
100        if( document.body.contains(overlay[0]) && !force )
101            return;
102
103        noPointerEvents && overlay.hide();
104
105        $doc.on('touchstart.testMouse', function(){
106            $doc.off('touchstart.testMouse');
107            isTouchDevice = true;
108            overlay.addClass('mobile');
109        });
110
111        autoplayBtn.off().on('click', APControl.toggle);
112        // attach a delegated event on the thumbs container
113        thumbs.off().on('click', 'a', thumbsStripe.click);
114        // if useragent is IE < 10 (user deserves a slap on the face, but I gotta support them still...)
115        isOldIE && overlay.addClass('msie');
116
117        // cancel prorogation up to the overlay container so it won't close
118        overlay.off().on('click', 'img', function(e){
119            e.stopPropagation();
120        });
121
122        $(doc.body).append(overlay);
123
124        // need this for later:
125        docElm = doc.documentElement;
126    }
127
128    // @param [List of elements to work on, Custom settings, Callback after image is loaded]
129    $.fn.photobox = function(target, settings, callback){
130        prepareDOM();
131
132        return this.each(function(){
133            var PB_data = $(this).data('_photobox');
134
135            if( PB_data ){ // don't initiate the plugin more than once on the same element
136                if( target === 'destroy')
137                    PB_data.destroy();
138
139                return this;
140            }
141
142            if( typeof target != 'string' )
143                target = 'a';
144
145            if( target === 'prepareDOM' ){
146                prepareDOM( true );
147                return this;
148            }
149
150            // merge the user settings with the default settings object
151            settings = $.extend({}, defaults, settings || {});
152
153            // create an instance og Photobox
154            photobox = new Photobox(settings, this, target);
155
156            // Saves the insance on the gallery's target element
157            $(this).data('_photobox', photobox);
158
159            // add a callback to the specific gallery
160            photobox.callback = callback;
161        });
162    }
163
164    Photobox = function(_options, object, target){
165        this.options = $.extend({}, _options);
166        this.target = target;
167        this.selector = $(object || doc);
168
169        this.thumbsList = null;
170        // filter the links which actually HAS an image as a child
171        var filtered = this.imageLinksFilter( this.selector.find(target) );
172
173        this.imageLinks = filtered[0];  // Array of jQuery links
174        this.images = filtered[1];      // 2D Array of image URL & title
175        this.init();
176    };
177
178    Photobox.prototype = {
179        init : function(){
180            // Cache DOM elements for this instance
181            this.DOM = this.DOM();
182
183            this.DOM.rotateBtn.toggleClass('show', this.options.rotatable);
184
185            // if any node was added or removed from the Selector of the gallery
186            this.observerTimeout = null;
187            this.events.binding.call(this);
188        },
189
190        // happens only once
191        DOM : function(){
192            var DOM = {}
193
194            DOM.scope     = overlay;
195            DOM.rotateBtn = DOM.scope.find('.rotateBtn');
196
197            return DOM;
198        },
199
200        //check if DOM nodes were added or removed, to re-build the imageLinks and thumbnails
201        observeDOM : (function(){
202            var MutationObserver = win.MutationObserver || win.WebKitMutationObserver,
203                eventListenerSupported = win.addEventListener;
204
205            return function(obj, callback){
206                if( MutationObserver ){
207                    var that = this,
208                        // define a new observer
209                        obs = new MutationObserver(function(mutations, observer){
210                            if( mutations[0].addedNodes.length || mutations[0].removedNodes.length )
211                                callback(that);
212                        });
213                    // have the observer observe for changes in children
214                    obs.observe( obj, { childList:true, subtree:true });
215                }
216                else if( eventListenerSupported ){
217                    obj.addEventListener('DOMNodeInserted', $.proxy( callback, that ), false);
218                    obj.addEventListener('DOMNodeRemoved', $.proxy( callback, that ), false);
219                }
220            }
221        })(),
222
223        open : function(link){
224            var startImage = $.inArray(link, this.imageLinks);
225            // if image link does not exist in the imageLinks array (probably means it's not a valid part of the gallery)
226            if( startImage == -1 )
227                return false;
228
229            // load the right gallery selector...
230            options = this.options;
231            images = this.images;
232            imageLinks = this.imageLinks;
233
234            photobox = this;
235            this.setup(1);
236
237            overlay.on(transitionend, function(){
238                overlay.off(transitionend).addClass('on'); // class 'on' is set when the initial fade-in of the overlay is done
239                changeImage(startImage, true);
240            }).addClass('show');
241
242            if( isOldIE )
243                overlay.trigger('MSTransitionEnd');
244
245            return false;
246        },
247
248        imageLinksFilter : function(linksObj){
249            var that = this,
250                images = [],
251                caption = {},
252                captionlink;
253
254            function linksObjFiler(i){
255                // search for the thumb inside the link, if not found then see if there's a 'that.settings.thumb' pointer to the thumbnail
256                var link = $(this),
257                    thumbImg,
258                    thumbSrc = '';
259
260                caption.content = link[0].getAttribute('title') || '';
261
262                if( that.options.thumb )
263                    thumbImg = link.find(that.options.thumb)[0];
264
265                // try a direct child lookup
266                if( !that.options.thumb || !thumbImg )
267                    thumbImg = link.find('img')[0];
268
269                // if no img child found in the link
270                if( thumbImg ){
271                    captionlink = thumbImg.getAttribute('data-pb-captionlink');
272                    thumbSrc = thumbImg.getAttribute(that.options.thumbAttr) || thumbImg.getAttribute('src');
273                    caption.content = ( thumbImg.getAttribute('alt') || thumbImg.getAttribute('title') || '');
274                }
275
276
277                // if there is a caption link to be added:
278                if( captionlink ){
279                    captionlink = captionlink.split('[');
280                    // parse complex links: text[www.site.com]
281                    if( captionlink.length == 2 ){
282                        caption.linkText = captionlink[0];
283                        caption.linkHref = captionlink[1].slice(0,-1);
284                    }
285                    else{
286                        caption.linkText = captionlink;
287                        caption.linkHref = captionlink;
288                    }
289                    caption.content += ' <a href="'+ caption.linkHref +'">' + caption.linkText + '</a>';
290                }
291
292                images.push( [link[0].href, caption.content, thumbSrc] );
293
294                return true;
295            }
296
297            return [linksObj.filter(linksObjFiler), images];
298        },
299
300        // things that should happen every time the gallery opens or closes (some messed up code below..)
301        setup : function (open){
302            var fn = open ? "on" : "off";
303
304            // thumbs stuff
305            if( options.thumbs ){
306                if( !isTouchDevice ){
307                    thumbs[fn]('mouseenter.photobox', thumbsStripe.calc)
308                          [fn]('mousemove.photobox', thumbsStripe.move);
309                }
310            }
311
312            if( open ){
313                image.css({'transition':'0s'}).removeAttr('style'); // reset any transition that might be on the element (yes it's ugly)
314                overlay.show();
315                // Clean up if another gallery was viewed before, which had a thumbsList
316                thumbs
317                    .html( this.thumbsList )
318                    .trigger('mouseenter.photobox');
319
320                if( options.thumbs ){
321                    overlay.addClass('thumbs');
322                }
323                else{
324                    thumbsToggler.prop('checked', false);
325                    overlay.removeClass('thumbs');
326                }
327
328                // things to hide if there are less than 2 images
329                if( this.images.length < 2 ||  options.single )
330                    overlay.removeClass('thumbs hasArrows hasCounter hasAutoplay');
331                else{
332                    overlay.addClass('hasArrows hasCounter')
333
334                    // check is the autoplay button should be visible (per gallery) and if so, should it autoplay or not.
335                    if( options.time > 1000 ){
336                        overlay.addClass('hasAutoplay');
337                        if( options.autoplay )
338                            APControl.progress.start();
339                        else
340                            APControl.pause();
341                    }
342                    else
343                        overlay.removeClass('hasAutoplay');
344                }
345
346                options.hideFlash && $('iframe, object, embed').css('visibility', 'hidden');
347
348            } else {
349                $win.off('resize.photobox');
350            }
351
352            $doc.off("keydown.photobox")[fn]({ "keydown.photobox": keyDown });
353
354            if( isTouchDevice ){
355                overlay.removeClass('hasArrows'); // no need for Arrows on touch-enabled
356                wrapper[fn]('swipe', onSwipe);
357            }
358
359            if( options.zoomable ){
360                overlay[fn]({"mousewheel.photobox": $.proxy(this.events.callbacks.onScrollZoom, this) });
361                if( !isOldIE) thumbs[fn]({"mousewheel.photobox": thumbsResize });
362            }
363
364            if( !options.single && options.wheelNextPrev ){
365                overlay[fn]({"mousewheel.photobox": throttle(wheelNextPrev,1000) });
366            }
367        },
368
369        destroy : function(){
370            options = this.options;
371            this.selector
372                .off('click.photobox', this.target)
373                .removeData('_photobox');
374
375            close();
376        },
377
378        events : {
379            binding : function(){
380                var that = this;
381
382                // only generates the thumbStripe once, and listen for any DOM changes on the selector element, if so, re-generate
383                // This is done on "mouseenter" so images will not get called unless it's liekly that they would be needed
384                this.selector.one('mouseenter.photobox', this.target, function(e){
385                    that.thumbsList = thumbsStripe.generate.apply(that);
386                });
387
388                this.selector.on('click.photobox', this.target, function(e){
389                    e.preventDefault();
390                    that.open(this);
391                });
392
393                if( !isOldIE && this.selector[0].nodeType == 1 ) // observe normal nodes
394                    this.observeDOM( this.selector[0], $.proxy( this.events.callbacks.onDOMchanges, this ));
395
396                this.DOM.rotateBtn.on('click', this.events.callbacks.onRotateBtnClick);
397            },
398
399            callbacks : {
400                onDOMchanges : function(){
401                    var that = this;
402                     // use a timeout to prevent more than one DOM change event firing at once, and also to overcome the fact that IE's DOMNodeRemoved is fired BEFORE elements were actually removed
403                    clearTimeout(this.observerTimeout);
404                    that.observerTimeout = setTimeout( function(){
405                        var filtered = that.imageLinksFilter( that.selector.find(that.target) ),
406                            activeIndex = 0,
407                            isActiveUrl = false,
408                            i;
409
410                        // Make sure that ONLY DOM changes in the photobox number of items will trigger a change
411                        if(that.imageLinks.length == filtered[0].length)
412                            return;
413
414                        that.imageLinks = filtered[0];
415                        that.images = filtered[1];
416
417                        // if photobox is opened
418                        if( photobox ){
419                            // if gallery which was changed is the currently viewed one
419:
420                            if( that.selector == photobox.selector ){
421                                images = that.images;
422                                imageLinks = that.imageLinks;
423                                // check if the currently VIEWED photo has been detached from a photobox set
424                                // if so, remove navigation arrows
425                                // TODO: fix the "images" to be an object and not an array.
426                                for( i = images.length; i--; ){
427                                    if( images[i][0] == activeURL )
428                                        isActiveUrl = true;
429                                    // if not exits any more
430                                }
431                               // if( isActiveUrl ){
432                               //     overlay.removeClass('hasArrows');
433                               // }
434                            }
435                        }
436
437                        // if this gallery has thumbs
438                        //if( that.options.thumbs ){
439                            that.thumbsList = thumbsStripe.generate.apply(that);
440                            thumbs.html( that.thumbsList );
441                        //}
442
443                        if( that.images.length && activeURL && that.options.thumbs ){
444                            activeIndex = that.thumbsList.find('a[href="'+activeURL+'"]').eq(0).parent().index();
445
446                            if( activeIndex == -1 )
447                                activeIndex = 0;
448
449                            // updateIndexes(activeIndex);
450                            thumbsStripe.changeActive(activeIndex, 0);
451                        }
452                    }, 50);
453                },
454
455                onRotateBtnClick : function(){
456                    var rotation = image.data('rotation') || 0, // in "deg"
457                        imgScale = image.data('zoom') || 1
458
459                    rotation += 90;
460
461                    image.removeClass('zoomable').addClass('rotating');
462
463                    image.css('transform', 'rotate('+ rotation +'deg) scale('+ imgScale + ')')
464                        .data('rotation', rotation)
465                        .on(transitionend, function(){
466                            image.addClass('zoomable').removeClass('rotating');
467                        });
468                    },
469
470                onScrollZoom : function(e, deltaY, deltaX){
471                    if( deltaX ) return false;
472
473                    var that = this;
474
475                    if( activeType == 'video' ){
476                        var zoomLevel = video.data('zoom') || 1;
477                        zoomLevel += (deltaY / 10);
478                        if( zoomLevel < 0.5 )
479                            return false;
480
481                        video.data('zoom', zoomLevel).css({width:624*zoomLevel, height:351*zoomLevel});
482                    }
483                    else{
484                        raf(function() {
485                            var zoomLevel = image.data('zoom') || 1,
486                                rotation  = image.data('rotation') || 0,
487                                position  = image.data('position') || '50% 50%',
488                                boundingClientRect = image[0].getBoundingClientRect(),
489                                value;
490
491                            zoomLevel += (deltaY / 10);
492
493                            if( zoomLevel < 0.1 )
494                                zoomLevel = 0.1;
495
496                            image.data('zoom', zoomLevel);
497
498                            value = 'scale('+ zoomLevel +') rotate('+ rotation +'deg)';
499
500                            // if the image was zoomed and now is larger than the window size, allow mouse movemenet reposition
501                            if( boundingClientRect.height > docElm.clientHeight || boundingClientRect.width > docElm.clientWidth ){
502                                $doc.on('mousemove.photobox', that.events.callbacks.onMouseMoveimageReposition);
503                                value += ' translate('+ position +')';
504                            }
505                            else{
506                                $doc.off('mousemove.photobox');
507                               // image[0].style[transformOrigin] = '50% 50%';
508                            }
509
510                            image.css({ 'transform':value });
511                        });
512                    }
513                    return false;
514                },
515
516                // moves the image around during zoom mode on mousemove event
517                onMouseMoveimageReposition : function(e){
518                    raf(function() {
519                        var //y = (e.clientY / docElm.clientHeight) * (docElm.clientHeight + 200) - 100, // extend the range of the Y axis by 100 each side
520                            sensitivity = 1.5, // 1 = same as mouse more, and higher value is less sensitive to mouse move
521                            yDelta = (e.clientY / docElm.clientHeight * 100 - 50) / sensitivity, // subtract 50 because the real center is at "0%"
522                            xDelta = (e.clientX / docElm.clientWidth * 100 - 50) / sensitivity, // subtract 50 because the real center is at "0%"
523                            position,
524                            rotationAngel = image.data('rotation') || 0,
525                            rotation = (rotationAngel/90)%4 || 0,
526                            imgScale = image.data('zoom') || 1;
527
528                        if( rotation == 1 || rotation == 3 )
529                            position = yDelta.toFixed(2)+'%, ' + -xDelta.toFixed(2) +'%';
530                        else
531                            position = xDelta.toFixed(2)+'%, ' + yDelta.toFixed(2) +'%';
532
533                        image.data('position', position);
534
535                        // image[0].style[transformOrigin] = origin;
536                        image[0].style.transform = 'rotate('+ rotationAngel +'deg) scale('+ imgScale + ') translate(' + position + ')';
537                    });
538                }
539            }
540        }
541    }
542
543    // on touch-devices only
544    function onSwipe(e, Dx, Dy){
545        if( Dx == 1 ){
546            image.css({transform:'translateX(25%)', transition:'.2s', opacity:0});
547            setTimeout(function(){ changeImage(prevImage) }, 200);
548        }
549        else if( Dx == -1 ){
550            image.css({transform:'translateX(-25%)', transition:'.2s', opacity:0});
551            setTimeout(function(){ changeImage(nextImage) }, 200);
552        }
553
554        if( Dy == 1 )
555            thumbsToggler.prop('checked', true);
556        else if( Dy == -1 )
557            thumbsToggler.prop('checked', false);
558    }
559
560    // manage the (bottom) thumbs strip
561    thumbsStripe = (function(){
562        var containerWidth = 0,
563            scrollWidth    = 0,
564            posFromLeft    = 0,    // Stripe position from the left of the screen
565            stripePos      = 0,    // When relative mouse position inside the thumbs stripe
566            animated       = null,
567            padding,                 // in percentage to the containerWidth
568            el, $el, ratio, scrollPos, pos;
569
570        return{
571            // returns a <ul> element which is populated with all the gallery links and thumbs
572            generate : function(){
573                var thumbsList = $('<ul>'),
574                    elements   = [],
575                    len        = this.imageLinks.length,
576                    title, thumbSrc, link, type, i;
577
578                for( i = 0; i < len; i++ ){
579                    link = this.imageLinks[i];
580
581                    thumbSrc = this.images[i][2];
582                    // continue if has thumb
583                    if( !thumbSrc )
584                        continue;
585
586                    title = this.images[i][1];
587                    type = link.rel ? " class='" + link.rel +"'" : '';
588                    elements.push('<li'+ type +'><a href="'+ link.href +'"><img src="'+ thumbSrc +'" alt="" title="'+ title +'" /></a></li>');
589                };
590                thumbsList.html( elements.join('') );
591                return thumbsList;
592            },
593
594            click : function(e){
595                e.preventDefault();
596
597                activeThumb.removeClass('active');
598                activeThumb = $(this).parent().addClass('active');
599
600                var imageIndex = $(this.parentNode).index();
601                return changeImage(imageIndex, 0, 1);
602            },
603
604            changeActiveTimeout : null,
605            /** Highlights the thumb which represents the photo and centres the thumbs viewer on it.
606            **  @thumbClick - if a user clicked on a thumbnail, don't center on it
607            */
608            changeActive : function(index, delay, thumbClick){
609                if( !options.thumbs )
610                    return;
611
612                var lastIndex = activeThumb.index();
613                activeThumb.removeClass('active');
614                activeThumb = thumbs.find('li').eq(index).addClass('active');
615
616                if( thumbClick || !activeThumb[0] ) return;
617                // set the scrollLeft position of the thumbs list to show the active thumb
618                clearTimeout(this.changeActiveTimeout);
619                // give the images time to to settle on their new sizes (because of css transition) and then calculate the center...
620                this.changeActiveTimeout = setTimeout(
621                    function(){
622                        var pos = activeThumb[0].offsetLeft + activeThumb[0].clientWidth/2 - docElm.clientWidth/2;
623                        delay ? thumbs.delay(800) : thumbs.stop();
624                        thumbs.animate({scrollLeft: pos}, 500, 'swing');
625                    }, 200);
626            },
627
628            // calculate the thumbs container width, if the window has been resized
629            calc : function(e){
630                el = thumbs[0];
631
632                containerWidth       = el.clientWidth;
633                scrollWidth          = el.scrollWidth;
634                padding              = 0.15 * containerWidth;
635
636                posFromLeft          = thumbs.offset().left;
637                stripePos            = e.pageX - padding - posFromLeft;
638                pos                  = stripePos / (containerWidth - padding*2);
639                scrollPos            = (scrollWidth - containerWidth ) * pos;
640
641                thumbs.animate({scrollLeft:scrollPos}, 200);
642
643                clearTimeout(animated);
644                animated = setTimeout(function(){
645                    animated = null;
646                }, 200);
647
648                return this;
649            },
650
651            // move the stripe left or right according to mouse position
652            move : function(e){
653                // don't move anything until initial movement on 'mouseenter' has finished
654                if( animated ) return;
655
656                var ratio     = scrollWidth / containerWidth,
657                    stripePos = e.pageX - padding - posFromLeft, // the mouse X position, "normalized" to the carousel position
658                    pos, scrollPos;
659
660                if( stripePos < 0) stripePos = 0; //
661
662                pos = stripePos / (containerWidth - padding*2); // calculated position between 0 to 1
663                // calculate the percentage of the mouse position within the carousel
664                scrollPos = (scrollWidth - containerWidth ) * pos;
665
666                raf(function(){
667                    el.scrollLeft = scrollPos;
668                });
669            }
670        }
671    })();
672
673    // Autoplay controller
674    APControl = {
675        autoPlayTimer : false,
676        play : function(){
677            APControl.autoPlayTimer = setTimeout(function(){ changeImage(nextImage) }, options.time);
678            APControl.progress.start();
679            autoplayBtn.removeClass('play');
680            APControl.setTitle('Click to stop autoplay');
681            options.autoplay = true;
682        },
683        pause : function(){
684            clearTimeout(APControl.autoPlayTimer);
685            APControl.progress.reset();
686            autoplayBtn.addClass('play');
687            APControl.setTitle('Click to resume autoplay');
688            options.autoplay = false;
689        },
690        progress : {
691            reset : function(){
692                autoplayBtn.find('div').removeAttr('style');
693                setTimeout(function(){ autoplayBtn.removeClass('playing') },200);
694            },
695            start : function(){
696                if( !isOldIE)
697                    autoplayBtn.find('div').css(transition, options.time+'ms');
698                autoplayBtn.addClass('playing');
699            }
700        },
701        // sets the button Title property
702        setTitle : function(text){
703            if(text)
704                autoplayBtn.prop('title', text + ' (every ' + options.time/1000 + ' seconds)' );
705        },
706        // the button onClick handler
707        toggle : function(e){
708            e.stopPropagation();
709            APControl[ options.autoplay ? 'pause' : 'play']();
710        }
711    }
712
713    function getPrefixed(prop){
714        var i, s = doc.createElement('p').style, v = ['ms','O','Moz','Webkit'];
715        if( s[prop] == '' ) return prop;
716        prop = prop.charAt(0).toUpperCase() + prop.slice(1);
717        for( i = v.length; i--; )
718            if( s[v[i] + prop] == '' )
719                return (v[i] + prop);
720    }
721
722    function keyDown(event){
723        var code = event.keyCode, ok = options.keys, result;
724        // Prevent default keyboard action (like navigating inside the page)
725        return $.inArray(code, ok.close) >= 0 && close() ||
726               $.inArray(code, ok.next) >= 0 && !options.single && loophole(nextImage) ||
727               $.inArray(code, ok.prev) >= 0 && !options.single && loophole(prevImage) || true;
728    }
729
730    function wheelNextPrev(e, dY, dX){
731        if( dX == 1 )
732            loophole(nextImage);
733        else if( dX == -1 )
734            loophole(prevImage);
735    }
736
737
738    // serves as a callback for pbPrevBtn / pbNextBtn buttons but also is called on keypress events
739    function next_prev(){
740        // don't get crazy when user clicks next or prev buttons rapidly
741        //if( !image.hasClass('zoomable') )
742        //  return false;
743
744        var idx = (this.id == 'pbPrevBtn') ? prevImage : nextImage;
745
746        loophole(idx);
747        return false;
748    }
749
750    function updateIndexes(idx){
751        lastActive = activeImage;
752        activeImage = idx;
753        activeURL = images[idx][0];
754        prevImage = (activeImage || (options.loop ? images.length : 0)) - 1;
755        nextImage = ((activeImage + 1) % images.length) || (options.loop ? 0 : -1);
756    }
757
758    // check if looping is allowed before changing image/video.
759    // A pre-changeImage function, only for linear changes
760    function loophole(idx){
761        if( !options.loop ){
762            var afterLast = activeImage == images.length-1 && idx == nextImage,
763                beforeFirst = activeImage == 0 && idx == prevImage;
764
765            if( afterLast || beforeFirst )
766                return;
767        }
768
769        changeImage(idx);
770    }
771
772    changeImage = (function(){
773        var timer;
774
775        return function(imageIndex, firstTime, thumbClick){
776            // throttle mechanism
777            if( timer )
778                return;
779
780            timer = setTimeout(function(){
781                timer = null;
782            }, 150);
783
784            $doc.off('mousemove.photobox');
785
786            if( !imageIndex || imageIndex < 0 )
787                imageIndex = 0;
788
789            // hide/show next-prev buttons
790            if( !options.loop ){
791                //nextBtn[ imageIndex == images.length-1 ? 'addClass' : 'removeClass' ]('pbHide');
792                nextBtn.toggleClass('pbHide', imageIndex == images.length-1);
793                //prevBtn[ imageIndex == 0 ? 'addClass' : 'removeClass' ]('pbHide');
794                prevBtn.toggleClass('pbHide', imageIndex == 0);
795            }
796
797            // if there's a callback for this point:
798            if( typeof options.beforeShow == "function")
799                options.beforeShow(imageLinks[imageIndex]);
800
801            overlay.removeClass('error');
802
803            if( activeImage >= 0 )
804                overlay.addClass( imageIndex > activeImage ? 'next' : 'prev' );
805
806            updateIndexes(imageIndex);
807
808            // reset things
809            stop();
810            video.empty();
811            preload.onerror = null;
812            image.add(video).data('zoom', 1);
813
814            activeType = imageLinks[imageIndex].rel == 'video' ? 'video' : 'image';
815
816            // check if current link is a video
817            if( activeType == 'video' ){
818                video.html( newVideo() ).addClass('pbHide');
819                showContent(firstTime);
820            }
821            else{
822                // give a tiny delay to the preloader, so it won't be showed when images load very quickly
823                var loaderTimeout = setTimeout(function(){ overlay.addClass('pbLoading'); }, 50);
824
825                if( isOldIE ) overlay.addClass('pbHide'); // should wait for the image onload. just hide the image while old IE display the preloader
826
827                options.autoplay && APControl.progress.reset();
828                preload = new Image();
829                preload.onload = function(){
830                    preload.onload = null;
831
832                    if( prevImage >= 0 ) preloadPrev.src = images[prevImage][0];
833                    if( nextImage >= 0 ) preloadNext.src = images[nextImage][0];
834
835                    clearTimeout(loaderTimeout);
836                    showContent(firstTime);
837                };
838                preload.onerror = imageError;
839                preload.src = activeURL;
840            }
841
842            // Show Caption text
843            captionText.on(transitionend, captionTextChange).addClass('change');
844            if( firstTime || isOldIE ) captionTextChange();
845
846
847            thumbsStripe.changeActive(imageIndex, firstTime, thumbClick);
848            // Save url hash for current image
849            history.save();
850        }
851    })();
852
853    function newVideo(){
854        var url = images[activeImage][0],
855            sign = $('<a>').prop('href',images[activeImage][0])[0].search ? '&' : '?';
856        url += sign + 'vq=hd720&wmode=opaque';
857        return $("<iframe>").prop({ scrolling:'no', frameborder:0, allowTransparency:true, src:url }).attr({webkitAllowFullScreen:true, mozallowfullscreen:true, allowFullScreen:true});
858    }
859
860    // show the item's Title & Counter
861    function captionTextChange(){
862        var tmpl;
863        captionText.off(transitionend).removeClass('change');
864
865        if( options.captionTmpl ){
866            tmpl = options.captionTmpl
867                        .replace(/{title}/g, images[activeImage][1])
868                        .replace(/{currentImageIdx}/g, activeImage + 1)
869                        .replace(/{totalImagesCount}/g, images.length);
870
871            captionText.html(tmpl);
872        }
873    }
874
875    // Handles the history states when changing images
876    var history = {
877        save : function(){
878            // only save to history urls which are not already in the hash
879            if('pushState' in window.history && decodeURIComponent(window.location.hash.slice(1)) != activeURL && options.history ){
880                window.history.pushState( 'photobox', doc.title + '-' + images[activeImage][1], window.location.pathname + window.location.search + '#' + encodeURIComponent(activeURL) );
881            }
882        },
883        load : function(){
884            if( options && !options.history ) return false;
885            var hash = decodeURIComponent( window.location.hash.slice(1) ), i, j;
886            if( !hash && overlay.hasClass('show') )
887                close();
888
889            $('a[href="' + hash + '"]').trigger('click.photobox');
890        },
891        clear : function(){
892            if( options.history && 'pushState' in window.history )
893                window.history.pushState('photobox', doc.title, window.location.pathname + window.location.search);
894        }
895    };
896
897    // Add Photobox special `onpopstate` to the `onpopstate` function
898    window.onpopstate = (function(){
899        var cached = window.onpopstate;
900        return function(event){
901            cached && cached.apply(this, arguments);
902            if( event.state == 'photobox' )
903                history.load();
904        }
905    })();
906
907    // handles all image loading error (if image is dead)
908    function imageError(){
909        overlay.addClass('error');
910        image[0].src = blankImg; // set the source to a blank image
911        preload.onerror = null;
912    }
913
914    // Shows the content (image/video) on the screen
915    function showContent(firstTime){
916        var out, showSaftyTimer;
917        showSaftyTimer = setTimeout(show, 2000);
918
919        // hides the current image and prepare ground for an image change
920        pbLoader.fadeOut(300, function(){
921            overlay.removeClass("pbLoading");
922            pbLoader.removeAttr('style');
923        });
924        overlay.addClass('pbHide');
925
926        image.add(video).removeClass('zoomable'); // while transitioning an image, do not apply the 'zoomable' class
927
928        // check which element needs to transition-out:
929        if( !firstTime && imageLinks[lastActive].rel == 'video' ){
930            out = video;
931            image.addClass('prepare');
932        }
933        else
934            out = image;
935
936        if( firstTime || isOldIE )
937            show();
938        else
939            out.on(transitionend, show);
940
941        // in case the 'transitionend' didn't fire
942        // after hiding the last seen image, show the new one
943        function show(){
944            wrapper.removeAttr('style');
945            clearTimeout(showSaftyTimer);
946            overlay.removeClass('video');
947
948            if( activeType == 'video' ){
949                image.attr('src', blankImg);
950                out.off(transitionend).css({'transition':'none'});
951                video.addClass('prepare');
952                overlay.addClass('video');
953            }
954            else{
955                // create a new image element (cannot modify the same image SRC due to google chrome stupid bugs showing the previous image even after the SRC had changed)
956                image.replaceWith( image = $('<img src="'+ activeURL +'" class="prepare">') )
957            }
958
959            // filthy hack for the transitionend event, but cannot work without it:
960            setTimeout(function(){
961                overlay.removeClass('pbHide next prev');
962                image.add(video).removeAttr('style').removeClass('prepare').on(transitionend, showDone);
963                if(isOldIE) showDone();
964            }, 50);
965        }
966    }
967
968    // a callback whenever a transition of an image or a video is done
969    function showDone(){
970        image.add(video).off(transitionend).addClass('zoomable');
971        if( activeType == 'video' )
972            video.removeClass('pbHide');
973        else{
974            autoplayBtn && options.autoplay && APControl.play();
975        }
976        if( photobox && typeof photobox.callback == 'function' ){
977            photobox.callback(imageLinks[activeImage]);
978        }
979    }
980
981
982    function thumbsResize(e, delta){
983        e.preventDefault();
984        e.stopPropagation(); // stop the event from bubbling up to the Overlay and enlarge the content itself
985        var thumbList = photobox.thumbsList, h;
986        thumbList.css('height', thumbList[0].clientHeight + (delta * 10) );
987        h = caption[0].clientHeight / 2;
988        wrapper[0].style.cssText = "margin-top: -"+ h +"px; padding: "+ h +"px 0;";
989        thumbs.hide().show(0);
990        //thumbs.trigger('mouseenter').trigger('mousemove');
991    }
992
993    function stop(){
994        clearTimeout(APControl.autoPlayTimer);
995        $doc.off('mousemove.photobox');
996        preload.onload = function(){};
997        preload.src = preloadPrev.src = preloadNext.src = activeURL;
998    }
999
1000    function close(){
1001        if( !overlay.hasClass('show') )
1002            return false;
1003
1004        stop();
1005        video.find('iframe').prop('src','').empty();
1006        Photobox.prototype.setup();
1007        history.clear();
1008
1009        overlay.removeClass('on video').addClass('pbHide');
1010        activeImage = -1;
1011
1012        image.on(transitionend, hide);
1013        isOldIE && hide();
1014
1015        // the "photobox" instance might be needed for async transitionEnd functions, so give it some time before clearing it
1016        setTimeout(function(){
1017            photobox = null;
1018        },1000);
1019
1020        function hide(){
1021            if( overlay[0].className == '' ) return; // if already hidden
1022            overlay.removeClass('show pbHide error pbLoading');
1023            image.removeAttr('class').removeAttr('style').off().data('zoom',1);
1024            // a hack to change the image src to nothing, because you can't do that in CHROME
1025            image[0].src = blankImg;
1026
1027            caption.find('.title').empty();
1028
1029            if(noPointerEvents) // pointer-events lack support in IE, so just hide the overlay
1030                setTimeout(function(){ overlay.hide(); }, 200);
1031
1032            options.hideFlash && $('iframe, object, embed').css('visibility', 'visible');
1033        }
1034
1035        // fall-back if the 'transitionend' event didn't fire
1036        setTimeout(hide, 500);
1037        // callback after closing the gallery
1038        if( typeof options.afterClose === 'function' )
1039            options.afterClose(overlay);
1040    }
1041
1042
1043    /**
1044    * jQuery Plugin to add basic "swipe" support on touch-enabled devices
1045    *
1046    * @author Yair Even Or
1047    * @version 1.0.0 (March 20, 2013)
1048    */
1049    $.event.special.swipe = {
1050        setup: function(){
1051            $(this).bind('touchstart', $.event.special.swipe.handler);
1052        },
1053
1054        teardown: function(){
1055            $(this).unbind('touchstart', $.event.special.swipe.handler);
1056        },
1057
1058        handler: function(event){
1059            var args = [].slice.call( arguments, 1 ), // clone arguments array, remove original event from cloned array
1060                touches = event.originalEvent.touches,
1061                startX, startY,
1062                deltaX = 0, deltaY = 0,
1063                that = this;
1064
1065            event = $.event.fix(event);
1066
1067            if( touches.length == 1 ){
1068                startX = touches[0].pageX;
1069                startY = touches[0].pageY;
1070                this.addEventListener('touchmove', onTouchMove, false);
1071            }
1072
1073            function cancelTouch(){
1074                that.removeEventListener('touchmove', onTouchMove);
1075                startX = startY = null;
1076            }
1077
1078            function onTouchMove(e){
1079                e.preventDefault();
1080
1081                var Dx = startX - e.touches[0].pageX,
1082                    Dy = startY - e.touches[0].pageY;
1083
1084                if( Math.abs(Dx) >= 20 ){
1085                    cancelTouch();
1086                    deltaX = (Dx > 0) ? -1 : 1;
1087                }
1088                else if( Math.abs(Dy) >= 20 ){
1089                    cancelTouch();
1090                    deltaY = (Dy > 0) ? 1 : -1;
1091                }
1092
1093                event.type = 'swipe';
1094                args.unshift(event, deltaX, deltaY); // add back the new event to the front of the arguments with the delatas
1095                return ($.event.dispatch || $.event.handle).apply(that, args);
1096            }
1097        }
1098    };
1099
1100    /* MouseWheel plugin
1101     * ! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
1102     * Licensed under the MIT License (LICENSE.txt).
1103     *
1104     * Version: 3.1.11
1105     *
1106     * Requires: jQuery 1.2.2+
1107     */
1108    !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>
vendor: 2,025 bytes, lines 1108-1119
1108n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.11",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b)["offsetParent"in a.fn?"offsetParent":"parent"]();return c.length||(c=a("body")),parseInt(c.css("fontSize"),10)},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
1109
1110    // Expose:
1111    window._photobox = {
1112        DOM      : {
1113            overlay : overlay
1114        },
1115        close    : close,
1116        history  : history,
1117        defaults : defaults
1118    };
1119})(jQuery, document, 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.