PageSourceSearch

https://protuning-psg.ru/js/theatre/jquery.theatre.js

js protuning-psg.ru collected 2026-10-04 23:55:16 UTC 30,181 bytes, 696 lines download raw bytes

1/**
2 * ELIXON THEATRE
3 * Project Homepage: http://www.webdevelopers.eu/jquery/theatre
4 *
5 * LICENSE
6 *   http://www.webdevelopers.eu/jquery/theatre/license
7 *   Commons Attribution-NonCommercial 3.0
8 *
9 *   Get a commercial license at
10 *   http://www.webdevelopers.eu/jquery/theatre/buy
11 *
12 * DEMO
13 *    http://www.webdevelopers.eu/jquery/theatre/demo
14 *
15 * DOCUMENTATION
16 *    http://www.webdevelopers.eu/jquery/theatre/documentation
17 *
18 * CHANGELOG
19 *  - fires event $stage.trigger('theatreMove', function(idx, actor, theatre)) upon move to allow custom external onMove handlers
20 *  - $stage.theatre('jump', where [, speed]);
21 *  - added support for swipeup, swipedown, swipeleft and swiperight events (e.g.https://github.com/stephband/jquery.event.swipe )
22 *  - honors Actor with .theatre-actor-active pre-created class as first actor automatically.
23 *
24 * @project    Elixon CMS, http://www.webdevelopers.eu/
25 * @package    JQuery
26 * @subpackage Theatre
27 * @author     Daniel Sevcik <[email protected]>
28 * @version    2.6
29 * @copyright  2010 Daniel Sevcik
30 * @since      2010-11-11T22:10:07+0100
31 * @revision   $Revision: 5839 $
32 * @access     public
33 */
34(function($) {
35    var effects={};
36    var methods={};
37    var urlBase;
38
39    $.fn.theatre=function() {
40        var currArguments=arguments;
41        this.css({"visibility": "visible", "display": "block"}); /* if the initial settings are to hide loading images... */
42        (this.length ? this : $(document)).each(function() {
43                methods.initAll.apply($(this), currArguments);
44            });
45        return this;
46    };
47
48    // Examples:
49    // $.fn.theatre.cssSupport("transform");
50    // $.fn.theatre.cssSupport("display", "inline-block");
51    $.fn.theatre.cssSupport=function(propName, propValue) {
52        var i;
53        var ret=false;
54
55        // test Property Name
56        var elStyle=(document.body || document.documentElement).style;
57        var propNameUC=propName.substr(0, 1).toUpperCase()+propName.substr(1);
58        var test=[propName, 'Moz'+propNameUC, 'Webkit'+propNameUC, 'ms'+propNameUC, 'O'+propNameUC];
59
60        for (i=0; i < test.length; i++) {
61            if (elStyle[test[i]] !== undefined) {
62                ret=true;
63                break;
64            }
65        }
66
67        // Test Property Value
68        if (propValue && ret) {
69            var $dummy=$("<div class='theatreCSSCompatDummy' style='width:1px; height: 1px; position: absolute; bottom: 0px; opacity: 0;'></div>").appendTo('body');
70            var cssTest;
71            var ret2=false;
72            elStyle=$dummy.get(0).style;
73
74            for (i=0; i < test.length; i++) {
75                if (elStyle[test[i]] === undefined) {
76                    continue;
77                }
78                cssTest={};
79                cssTest[test[i]]=propValue;
80                $dummy.css(cssTest);
81                if (elStyle[test[i]] == propValue) {
82                    ret2=true;
83                    break;
84                }
85            }
86            $dummy.remove();
87            ret=ret && ret2;
88        }
89
90        return ret;
91    };
92
93    // name:string - 'FILE:EFFECT' or 'EFFECT', one file may register EFFECT or multiple effect prefixed by FILE string
94    methods.loadEffect=function(name) {
95        if (!urlBase) { // Find the locations
96            $('script[src*="jquery.theatre.js"], script[src*="jquery.theatre.min.js"], link[href*="theatre.css"]').first().each(function() {
97                    urlBase=(this.href || this.src).replace(/\/[^\/]*(#.*)?$/, '');
98                });
99        }
100        var url=urlBase+'/effect.'+name.split(':')[0]+'.js';
101        $('head').append('<script type="text/javascript" src="'+url+'"></script>');
102        //$.getScript(url);
103        return effects[name];
104    };
105
106    // Clean actors removed on-the-fly
107    methods.buryDead=function(actors) {
108        return actors.filter(function() {return $(this).get(0).parentNode;});
109    };
110
111    methods.initAll=function(method) {
112        // Init theatre
113        if (typeof method == 'object' || !method || method == 'init') {
114            return this.is(document) || methods.init.apply(this, arguments); // don't init when this is document @see $.fn.theatre()
115        }
116
117        // Register Effect
118        if (method == 'effect') {
119            // Register new effect
120            if (typeof arguments[2] == 'function') {
121                return effects[arguments[1]]=arguments[2];
122            } else {
123                $.error("Elixon Theatre cannot register effect object unless it is a Function.");
124            }
125        } else if (this.is(document)) {
126            return false; // do nothing, document context only for 'effect' function
127        }
128
129        var theatre=this.data('theatre');
130        if (!theatre) return false; // not initialized yet
131
132        var jumpTo=arguments[1];
133        var noStop=false;
134        switch(method) {
135        case 'iterate':
136            noStop=true;
137            method=theatre.settings.playDir;
138        case 'next':
139        case 'prev':
140        case 'redraw':
141            jumpTo=method;
142            // continue with next 'case'
143        case 'jump':
144            var forceRun=0;
145            switch(jumpTo) {
146            case 'redraw': jumpTo=theatre.index; forceRun=1; break;
147            case 'first': jumpTo=0; break;
148            case 'last': jumpTo=theatre.actors.length - 1; break;
149            case 'next': jumpTo=(theatre.index + theatre.actors.length + 1) % theatre.actors.length; break;
150            case 'prev': jumpTo=(theatre.index + theatre.actors.length - 1) % theatre.actors.length; break;
151            default:
152                jumpTo=(parseInt(arguments[1]) - 1) % theatre.actors.length;
153                jumpTo=(Math.abs(Math.floor(jumpTo / theatre.actors.length) * theatre.actors.length) + jumpTo) % theatre.actors.length; //normalize
154            }
155            if (!forceRun && jumpTo == theatre.index) break; // already there
156            if (typeof arguments[2] != 'undefined') {
157                if (typeof theatre.settings.speedOrig == 'undefined') theatre.settings.speedOrig=theatre.settings.speed;
158                theatre.settings.speed=arguments[2];
159            }
160            var lastPos=theatre.index;
161            if (!noStop) this.theatre('stop'); // stop previous animation/play if any
162            while(forceRun || theatre.index != jumpTo) {
163              forceRun=0;
164              jumpTo=methods.onMove.apply(this, [jumpTo]);
165              if (jumpTo == -1) break; // cancelled by user callback
166
167              // console.log('Jumping to ' + jumpTo, this, theatre);
168              if (theatre.effect.jump) {
169                  theatre.index=jumpTo;
170                  theatre.effect.jump.apply(theatre.effect, [lastPos]);
171              } else {
172                  // Which direction is closest to cycle ther using next/prev?
173                  if (theatre.index < jumpTo) {
174                      var dirNext=jumpTo - theatre.index;
175                      var dirPrev=(theatre.index + theatre.actors.length) - jumpTo;
176                  } else {
177                      var dirPrev=theatre.index - jumpTo;
178                      var dirNext=(theatre.actors.length + jumpTo) - theatre.index;
179                  }
180                  var origIndex=theatre.index;
181                  theatre.index=(theatre.index + (dirNext < dirPrev ? 1 : -1) + theatre.actors.length) % theatre.actors.length;
182                  var direction=dirNext < dirPrev || origIndex + 1 == theatre.index ? 'next' : 'prev';
183                  theatre.effect[direction].apply(theatre.effect, [lastPos]);
184              }
185              if (theatre.index == lastPos) break; // movement failed? cancelled by onMove() ? Avoid infinite loop.
186              lastPos=theatre.index;
187              methods.updatePaging.apply(this);
188            }
189            if (typeof theatre.settings.speedOrig != 'undefined') theatre.settings.speed=theatre.settings.speedOrig;
190            this
191                .attr('actors-count', theatre.actors.length)
192                .attr('position-ord', theatre.index + 1)
193                .attr('position', theatre.index + 1 == theatre.actors.length ? 'last' : (theatre.index || 'first'))
194            ;
195            break;
196        case 'play':
197        case 'destroy':
198        case 'stop':
199            methods[method].apply(this);
200            break;
201        default: // Unsupported
202            $.error('Elixon Theatre method "'+method+'" does not exist on jQuery.theatre!');
203        }
204    };
205
206    methods.init=function(options) {
207        methods.destroy.apply(this); // Reset old if any
208
209        // Default settings
210        var settings={
211            selector: '> *:not(.theatre-control)',
212            effect: 'horizontal', // 'horizontal'|'vertical'|'fade'|'show'|'slide'|'3d'|CUSTOM EFFECT NAME|OBJECT with constructor implementing the init,next,prev methods
213            speed: 1000, // Transition speed
214            still: 3000, // Time between transitions
215            autoplay: true,
216            playDir: 'next', // or 'prev'
217            controls: 'horizontal', // display control buttons 'horizontal' or 'vertical' or 'none'
218            itemWidth: false, // width of the item or 'max' or false.
219            itemHeight: false, // height of the item or 'max' or false.
220            width: false, // set the height of the container. By default honors the already set size using CSS
221            height: false, // set the height of the container. By default honors the already set size using CSS
222            onMove: false, // function(index) {}, execution context is the Stage
223            onAfterMove: function() {}, // function(index) {}, execution context is the Stage
224            random: false // random order?
225        };
226        if (options) {
227            $.extend(settings, options);
228        }
229
230        // Random
231        if (settings.random) {
232            var children=$('> *', this).get();
233            while(children.length) {
234                var idx=Math.floor(Math.random() * children.length);
235                var child=children[idx];
236                children.splice(idx, 1);
237                child.parentNode.appendChild(child);
238            }
239        }
240
241        var actors=$(settings.selector, this);
242        var theatre={
243            paging: settings.paging && $(settings.paging),
244            actors: actors,
245            effect: false,
246            settings: settings,
247            interval: false,
248            index: 0,
249            methods: methods
250        };
251
252        // Init effect object
253        var fxAll;
254        switch (typeof settings.effect) {
255        case "string":
256            fxAll=settings.effect.split(/\s+/);
257            break;
258        case "function":
259            fxAll=[settings.effect];
260            break;
261        }
262
263        // Try fx + fallbacks
264        var effect;
265        for (var i=0; i < fxAll.length && !theatre.effect; i++) {
266            settings.effect=fxAll[i];
267            if (typeof settings.effect == 'function') {
268                effect=settings.effect;
269            } else if (effects[settings.effect]) {
270                effect=effects[settings.effect];
271            } else {
272                effect=methods.loadEffect(settings.effect);
273            }
274
275            if (!effect) $.error('Elixon Theatre does not support effect "'+settings.effect+'"!');
276            theatre.effect=new effects[settings.effect](this, actors, settings, theatre);
277
278            // Capabilities
279            if (typeof theatre.effect.capable == 'function') {
280                var answer=theatre.effect.capable();
281                if (answer !== true) {
282                    if (typeof theatre.effect.destroy == 'function') theatre.effect.destroy();
283                    theatre.effect=null;
284                    $(this).addClass('theatre-failover');
285                    if (fxAll.length == 1) { // allow only one automatic fallback otherwise honor only explicitly set fallbacks
286                        fxAll.push(answer || settings.effect);
287                    }
288                }
289            }
290        }
291
292        // Stage
293        this.addClass('theatre').data('theatre', theatre);
294        this.addClass('theatre-'+settings.effect.replace(/[^a-z0-9]+/ig, '-'));
295        if (settings.width) this.css('width', settings.width);
296        if (settings.height) this.css('height', settings.height);
297
298        // Actors - calculate orig width/height
299        var idx=0;
300        actors.each(function(){
301                var $this=$(this);
302                var currIdx=idx++;
303                var getSettings=function() { return {width: $this.width(), height: $this.height(), index: currIdx};};
304                if ($this.is('.theatre-actor-active')) { // preselect first actor
305                    theatre.index=currIdx;
306                }
307                if (!$this.data('theatre')) {$this.data('theatre', getSettings());}
308                $this.load(function() {$this.data('theatre', getSettings());}); // slow image load problem
309            });
310        if (settings.itemWidth || settings.itemHeight) {
311            var thisObj=this;
312            actors.each(function() {
313                    var $this=$(this);
314                    if (settings.itemWidth) {
315                        $this.css('width', settings.itemWidth == 'max' ? (thisObj.width() - $this.outerWidth() + $this.width())+'px' : settings.itemWidth);
316                    }
317                    if (settings.itemHeight) {
318                        $this.css('height', settings.itemHeight == 'max' ? (thisObj.height() - $this.outerHeight() + $this.height())+'px' : settings.itemHeight);
319                    }
320                });
321        }
322
323        this
324            .attr('actors-count', theatre.actors.length)
325            .attr('position-ord', 1)
326            .attr('position', 'first');
327        actors.addClass('theatre-actor').stop(true, true);
328        actors.first().addClass('theatre-actor-first');
329        actors.last().addClass('theatre-actor-last');
330        theatre.effect.init();
331
332        if (settings.autoplay) {
333            methods.play.apply(this, [true]);
334        }
335
336        // Controls
337        methods.appendControls.apply(this);
338        methods.generatePaging.apply(this);
339        methods.onMove.apply(this, [theatre.index]);
340        methods.onAfterMove.apply(this);
341
342        // Swipe gestures
343        methods.swipeSupport.apply(this);
344
345        // Causes problems with validated forms - interferes with background validation
346        // Focus the actor if it is outside of the Stage
347        // Good for multi-page FORM's TAB movement
348        // var $this=$(this);
349        // var mvForward=function() {
350        //	var $actor=$(this)
351        //		.closest('.theatre-actor')
352        //		.add('.theatre-actor', this)
353        //		.add(this)
354        //		.filter('.theatre-actor')
355        //		.first();
356        //	var actor=$actor.data('theatre');
357        //	if (actor && actor.index != theatre.index) {
358        //		// Is visible actor?
359        //		var point=$actor.offset();
360        //		point.left+=$actor.width()/2;
361        //		point.top+=$actor.height()/2;
362        //		var sOffset=$this.offset();
363        //		var inStage;
364        //		inStage=sOffset.left < point.left && point.left < sOffset.left + $this.width();
365        //		inStage=inStage && sOffset.top < point.top && point.top < sOffset.top + $this.height();
366        //		if (!inStage) $this.theatre('jump', actor.index + 1);
367        //	}
368        // };
369        // //.click(mvForward); - onclick="...jump('next')" was reversed by this
370        // // setTimeout is needed because on focus on next invisible page on horizontal/vertical
371        // // browser scrolls automatically so at that moment it is always visible
372        // $('a, *:input, .theatre-actor', $this)
373        //	.focus(function() {var thisObj=this; setTimeout(function() {mvForward.apply(thisObj);}, 100);});
374    };
375
376    methods.onMove=function(nextIdx) {
377        if (isNaN(nextIdx)) return -1;
378        var theatre=this.data('theatre');
379        if (typeof theatre.settings.onMove != 'function') return nextIdx;
380
381        // Set active actor class
382        theatre.actors.not(theatre.actors[nextIdx]).removeClass('theatre-actor-active');
383        $(theatre.actors[nextIdx]).addClass('theatre-actor-active');
384
385        var retVal=theatre.settings.onMove.apply(this, [nextIdx, theatre.actors[nextIdx], theatre]);
386
387        // Ret val is false
388        if (typeof retVal == 'number') {
389            retVal=retVal % theatre.actors.length;
390        } else if (retVal === false) {
391            retVal=-1;
392        } else {
393            retVal=nextIdx;
394        }
395        if (retVal >= 0) this.trigger('theatreMove', [retVal, theatre.actors[retVal], theatre]);
396
397        return retVal;
398    };
399
400    methods.onAfterMove=function() {
401        var theatre=this.data('theatre');
402        if (typeof theatre.settings.onAfterMove != 'function') return false;
403        theatre.settings.onAfterMove.apply(this, [theatre.index, theatre.actors[theatre.index], theatre]);
404        return true;
405    };
406
407    methods.generatePaging=function() {
408        var stage=this;
409        var theatre=this.data('theatre');
410        if (!theatre.paging) return;
411
412        theatre.paging.each(function() {
413                var $this=$(this);
414
415                var templateHTML=$this.data('theatrePagingTemplate');
416                if (!templateHTML) {
417                    $this.data('theatrePagingTemplate', $this.html());
418                } else { // regenerating the paging - restore template
419                    $this.html(templateHTML);
420                }
421
422                var jumpers=[];
423                $('> *', $this).each(function() {jumpers.push($('<div></div>').append(this).html());});
424                var template=jumpers[jumpers.length - 1];
425
426                // Re-generate
427                for(var i=0; i < theatre.actors.length; i++) {
428                    var jumpHTML=jumpers.length < i + 1 ? template : jumpers[i];
429                    (function (pos) {
430                        $this.append(jumpHTML.replace('{#}', pos)); // no "\n" it kills design, designer can always use padding or add space inside element
431                        $this
432                            .children()
433                            .last()
434                            .off(".theatre")
435                            .on("click.theatre", function() {stage.theatre('jump', pos);})
436
437                            // for compatibility reason we need " " between elements and also give designers a way to hide wbr using CSS...
438                            // many designs has alerady design for nested <span> so we use <nobr> that should be quite compatible.
439                            .after(i < theatre.actors.length - 1 ? "<nobr class=\"theatre-spacer\"> </nobr>" : '')
440                        ;
441                    })(i+1);
442                }
443            });
444        methods.updatePaging.apply(this);
445    };
446
447    methods.updatePaging=function() {
448        var theatre=this.data('theatre');
449        // console.log('Updating paging to ' + theatre.index, this, theatre.paging);
450        if (!theatre.paging) return;
451
452        theatre.paging.each(function() {
453                var $this=$(this);
454                $('> *:not(.theatre-spacer)', $this).removeClass('active').eq(theatre.index).addClass('active');
455            });
456    };
457
458    methods.swipeSupport=function() {
459        var thisObj=this;
460        this
461            .on("swipeleft swipeup", function() {thisObj.theatre('next');})
462            .on("swiperight swipedown", function() {thisObj.theatre('prev');})
463        ;
464    };
465
466    methods.appendControls=function() {
467        settings=this.data('theatre').settings;
468        // Controls
469        if (settings.controls == 'horizontal' || settings.controls == 'vertical') {
470            var thisObj=this;
471            this.append('<a class="theatre-control theatre-control-'+settings.controls+'-next theatre-next"><span></span></a>');
472            this.append('<a class="theatre-control theatre-control-'+sett
472ings.controls+'-prev theatre-prev"><span></span></a>');
473            this.append('<a class="theatre-control theatre-control-'+settings.controls+'-play theatre-play"><span></span></a>');
474            this.append('<a class="theatre-control theatre-control-'+settings.controls+'-stop theatre-stop"><span></span></a>');
475            $('.theatre-next', this).click(function() {thisObj.theatre('next');});
476            $('.theatre-prev', this).click(function() {thisObj.theatre('prev');});
477            $('.theatre-play', this).click(function() {thisObj.theatre('play');});
478            $('.theatre-stop', this).click(function() {thisObj.theatre('stop');});
479            this.mouseenter(function() {$('.theatre-control', thisObj).fadeIn();});
480            this.mouseleave(function() {$('.theatre-control', thisObj).fadeOut();});
481            $('.theatre-control', this).fadeOut(0);
482        }
483
484        this.append('<a class="theatre-control theatre-sign" rel="copyright license" style="position: absolute !important; display: none !important;" href="http://www.webdevelopers.eu/jquery/theatre" title="jQuery carousel plugin"><span style="display: none !important;">Elixon Theatre jQuery Plugin</span></a>');
485    };
486
487    methods.destroy=function() {
488        var theatre=this.data('theatre');
489        if (theatre) {
490            clearInterval(theatre.interval);
491            try { // On the fly Actor's removal problem
492                // this.theatre('jump', 0, 0);
493            } catch (x) {
494            }
495            if (typeof theatre.effect.destroy == 'function') theatre.effect.destroy();
496            this.removeClass('theatre-'+theatre.settings.effect.replace(/[^a-z0-9]+/ig, '-'));
497            theatre.actors.each(function(){ // Restore original sizes
498                    var $this=$(this);
499                    var theatre=$this.data('theatre');
500                    try { // @patch try-catch by Yotam Hamiel to solve problem with images added/removed on-the-fly to runnin Theatre
501                        $this.width(theatre.width);
502                        $this.height(theatre.height);
503                    } catch (x) {}
504                });
505        }
506        $('.theatre-control', this).remove();
507    };
508
509    methods.play=function(initDelay) {
510        var theatre=this.data('theatre');
511        var stage=this;
512        //methods.stop.apply(this);
513        //stage.theatre('next');
514        stage.theatre('stop');
515        !initDelay && stage.theatre('iterate');
516        theatre.interval=setInterval(function() {
517            stage.theatre('iterate');
518        }, theatre.settings.speed + theatre.settings.still);
519    };
520
521    methods.stop=function() {
522        var theatre=this.data('theatre');
523        clearInterval(theatre.interval);
524        theatre.interval=false;
525    };
526
527    effects['fade']=
528    effects['slide']=
529    effects['show']=function(stage, actors, settings, theatre) {
530        var onAfterMove=function() {methods.onAfterMove.apply(stage);};
531        var x={
532            fade: {show: 'fadeIn', hide: 'fadeOut', initStyle: {margin: 0, top: 0, left: 0, position: 'absolute', display: 'none'}},
533            slide: {show: 'slideDown', hide: 'slideUp', initStyle: {}},
534            show: {show: 'show', hide:'hide', initStyle: {}}
535        }[settings.effect];
536
537        this.init=function() {
538            actors[x.hide](0).css(x.initStyle).eq(theatre.index)[x.show](0); // actors.fadeOut(0) - does not hide it if stage has height=0 - assigning 'display:none'
539        };
540        this.next=function() {
541            actors.stop(true, true).css('z-index', 0)[x.hide](settings.speed)
542                .eq(theatre.index).css('z-index', 10)[x.show](settings.speed, onAfterMove);
543        };
544        this.prev=function() {
545            actors.stop(true, true).css('z-index', 0)[x.hide](settings.speed)
546                .eq(theatre.index).css('z-index', 10)[x.show](settings.speed, onAfterMove);
547        };
548        this.destroy=function() {
549            actors=methods.buryDead(actors);
550            actors.stop(true, true).css({zIndex: '', top: '', left: '', position: '', margin: ''})[x.show](0);
551        };
552    };
553
554    effects['vertical']=
555    effects['horizontal']=function(stage, actors, settings, theatre) {
556        var onAfterMove=function() {methods.onAfterMove.apply(stage);};
557        var x={
558            horizontal: {'size': 'outerWidth', 'direction': 'left'},
559            vertical: {'size': 'outerHeight', 'direction': 'top'}
560        }[settings.effect];
561
562        this.init=function() {
563            stage.scroll(function() {stage.scrollTop(0).scrollLeft(0);}); // TAB in forms causes automatic scroll
564            actors.fadeOut(0);
565            this.align(0, 0);
566            actors.fadeIn();
567        };
568        this.prev=function() {
569            var curr=$(settings.selector, stage).last();
570            var blk=curr.parentsUntil('.theatre');
571            (blk.length ? blk : curr).last().prependTo(stage);
572            curr.stop(true, true).css(x.direction, -curr[x.size](true));
573            this.align(0);
574        };
575        this.next=function() {
576            var curr=$(settings.selector, stage).first();
577            var offset=this.align(-curr[x.size](true));
578            var blk=curr.parentsUntil('.theatre');
579            blk=(blk.length ? blk : curr).last();
580            blk.appendTo(stage);
581        };
582        this.destroy=function() {
583            actors=methods.buryDead(actors);
584            actors.stop(true, true).css(x.direction, '').css({opacity: '', left: '', top: ''});
585            actors.each(function() {$(this).appendTo(stage);}); // restore order
586        };
587        this.align=function(offset, speed) {
588            stage.scrollLeft(0).scrollTop(0); // with forms the TAB adds scroll
589            $(settings.selector, stage).each(function(idx) {
590                var $this=$(this);
591                var usrCallBack=theatre.index == $this.data('theatre').index ? onAfterMove : function() {};
592                var callBack=usrCallBack;
593                if (offset < 0) {
594                    callBack=function() {$this.css(x.direction, offset); usrCallBack();};
595                }
596                var props={};
597                props[x.direction]=offset;
598                $this.stop(true, true).animate(props, isNaN(speed) ? settings.speed : speed, callBack);
599                offset+=$this[x.size](true);
600            });
601            return offset;
602        };
603    };
604
605    effects['3d']=function(stage, actors, settings, theatre) {
606        // Override custom/default settings
607        var onAfterMove=function() {methods.onAfterMove.apply(stage);};
608
609        settings.resize=false;
610
611        var pivots=[];
612        var maxWidth;
613        var maxHeight;
614        var styleBak={};
615
616        this.init=function() {
617            var thisObj=this;
618
619            styleBak.overflow=stage.css('overflow');
620            stage.css('overflow', 'visible');
621
622            maxWidth=stage.width() * 0.5;
623            maxHeight=stage.height() * 0.8;
624            actors.each(function(pos) {
625                    var rad=(2 * Math.PI * (-pos) / actors.length) + Math.PI / 2;
626                    var x=Math.cos(rad);
627                    var y=Math.sin(rad);
628
629                    /* Bias */
630                    y=1 - Math.sqrt((1 - y) / 2) * 2;
631
632                    var margin=10;
633                    var sizeMin=0.2;
634                    var size=(y + 1) / 2 * (1 - sizeMin) + sizeMin;
635                    var x2=x * (stage.width() - margin) / 2 + stage.width() / 2;
636                    var y2=y * (stage.height() - margin) / 2 + stage.height() / 2;
637
638                    pivots.push({left: x2, top: y2, x: x, y: y, size: size, rad: rad});
639                });
640            this.animate();
641        };
642        this.next=function() {
643            this.animate();
644        };
645        this.prev=function() {
646            this.animate();
647        };
648        this.destroy=function() {
649            actors.stop(true, true).css({'z-index': '', opacity: '', left: '', top: ''});
650            stage.css('overflow', styleBak.overflow);
651        };
652        this.animate=function() {
653            var thisObj=this;
654            actors.stop();
655            actors.each(function(pos) {
656                try {
657                    var pivot=pivots[(pos - theatre.index + actors.length) % actors.length];
658                    var dim=thisObj.calcDim($(this), maxWidth, maxHeight, pivot.size);
659                    var left=Math.round(pivot.left - pivot.x * dim[0] / 2 - dim[0] / 2);
660                    var top=Math.round(pivot.top - pivot.y * dim[1] / 2 - dim[1] / 2);
661                    // use transitions where applicable for skew() or scale() http://www.alistapart.com/articles/understanding-css3-transitions/
662                    // https://github.com/brandonaaron/jquery-cssHooks/blob/master/boxreflect.js for JQuery 1.4.3
663                    $(this).css({'z-index': Math.round(pivot.size * 1000)}).animate({opacity: pivot.size, left: left, top: top, width: dim[0], height: dim[1]}, settings.speed, pos == theatre.index ? onAfterMove : null);
664                } catch (x) {
665                    // Exceptions when adding/removing items on the fly
666                }
667
668            });
669        };
670        this.calcDim=function(obj, maxWidth, maxHeight, size) {
671            var dim=obj.data('theatre');
672            var w=maxWidth;
673            var h=dim.height / dim.width * w;
674            if (h > maxHeight) {
675                w=maxWidth * (maxHeight / h);
676                h=maxHeight;
677            }
678            return [Math.round(w * size), Math.round(h * size)];
679        };
680    };
681
682    // Trial notification
683    var sn="WX-XXXXXX-XXXXXX";
684    if (sn.match(/^W3/)) {
685        var snMsg='%45%6C%69%78%6F%6E%20%54%68%65%61%74%72%65%20%74%72%69%61%6C%20%76%65%72%73%69%6F%6E%20%68%61%73%20%65%78%70%69%72%65%64%21%20%43%6C%69%63%6B%20%4F%4B%20%74%6F%20%6F%62%74%61%69%6E%20%74%68%65%20%66%75%6C%6C%20%76%65%72%73%69%6F%6E%2E';
686        var expDate=new Date(sn.replace(/^W(\d)-(\d{2})(\d{2})(\d{2})-(\d{2})(\d{2})(\d{2})$/, "20$2-$3-$4T$5:$6:$7Z"));
687        var expMethod=["co", "nfi", "rm"];
688        expDate.setTime(expDate.getTime() + 30 * 24 * 3600 * 1000);
689        if (expDate.getTime() &&  expDate.getTime() < (new Date).getTime()) {
690            if (window[expMethod.join('')](unescape(snMsg))) {
691                window.location="http://www.webdevelopers.eu/jquery/theatre/buy?trial=expired";
692            }
693        }
694    }
695
696})(jQuery);

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.