PageSourceSearch

https://www.bourboncountycitizen.com/wp-content/plugins/webads-dis…/public/js/jquery.sudoSlider.js?ver=1.2.3

js bourboncountycitizen.com collected 2026-09-26 19:28:45 UTC 121,069 bytes, 3,134 lines download raw bytes

1/**@preserve
2 * Sudo Slider version 3.4.9 - jQuery plugin
3 * Written by Erik Krogh Kristensen [email protected].
4 * http://webbies.dk/SudoSlider/
5 *
6 * Dual licensed under the MIT and GPL licenses.
7 *
8 * Based on EasySlider http://cssglobe.com/easy-slider-17-numeric-navigation-jquery-slider/
9 * But bear little resemblance at this point.
10 *
11 * Built for jQuery library
12 * http://jquery.com
13 *
14 */
15(function(factory) {
16    // window/root object.
17    var root = (typeof self == 'object' && self.self == self && self) ||
18        (typeof global == 'object' && global.global == global && global);
19
20    // AMD
21    if (typeof define === 'function' && define.amd) {
22        define(['jquery'], function($) {
23            factory($, root);
24        });
25
26        // CommonJS
27    } else if (typeof exports !== 'undefined') {
28        var $ = require('jquery');
29        factory($, root);
30
31        // Finally, as a browser global.
32    } else {
33        factory(root.jQuery || root.$, root);
34    }
35
36})(function ($, win) {
37    // Saves space in the minified version.
38    var undefined; // Makes sure that undefined really is undefined within this scope.
39    var FALSE = false;
40    var TRUE = true;
41
42    // Constants
43    var PAGES_MARKER_STRING = "pages";
44    var NEXT_STRING = "next";
45    var PREV_STRING = "prev";
46    var LAST_STRING = "last";
47    var FIRST_STRING = "first";
48    var ABSOLUTE_STRING = "absolute";
49    var RELATIVE_STRING = "relative";
50    var HIDDEN_STRING = "hidden";
51    var SWING = "swing";
52    var EMPTY_FUNCTION = function () { };
53    var ANIMATION_CLONE_MARKER_CLASS = "sudo-box";
54    var DIV_TAG = "<div>";
55    var CSSVendorPrefix = getCSSVendorPrefix();
56    var jWin = jQuery(win);
57    var doc = jQuery(document);
58
59    var TOUCHSTART = "touchstart";
60    var TOUCHMOVE = "touchmove";
61    var TOUCHCANCEL = "touchcancel";
62    var TOUCHEND = "touchend";
63    var MOUSEDOWN = "mousedown";
64    var MOUSEMOVE = "mousemove";
65    var MOUSEUP = "mouseup";
66    var SLIDES_CONTAINER = "slidesContainer";
67    var SUDO_DRAGGING_CLASS = "sudoSlider-dragging";
68
69    jQuery.fn.sudoSlider = function (optionsOrg) {
70        // Defining the base element.
71        var baseSlider = this;
72
73        optionsOrg = $.extend(objectToLowercase(getDefaultOptions()), objectToLowercase(optionsOrg));
74        if (CSSVendorPrefix === FALSE || !minJQueryVersion([1, 8, 0])) {
75            optionsOrg.usecss = FALSE;
76        }
77
78        return this.each(function () {
79            var init,
80                fullyInitialized = FALSE,
81                isSlideContainerUl,
82                slidesContainer,
83                slides,
84                imagesInSlidesLoaded,
85                totalSlides,
86                currentSlide,
87                previousSlide,
88                clickable,
89                currentlyAnimatingTowards,
90                numericControls,
91                numericContainer,
92                destroyed,
93                slideNumberBeforeDestroy = FALSE,
94                controls,
95                nextbutton,
96                prevbutton,
97                autoTimeout,
98                autoOn,
99                numberOfVisibleSlides,
100                asyncDelayedSlideLoadTimeout,
101                obj = $(this),
102                finishedAdjustingTo = FALSE, // This variable tells if the slider is currently adjusted (height and width) to any specific slide. This is usefull when ajax-loading stuff.
103                adjustingTo, // This one tells what slide we are adjusting to, to make sure that we do not adjust to something we shouldn't.
104                adjustTargetTime = 0, // This one holds the time that the autoadjust animation should complete.
105                currentlyAnimating = FALSE,
106                currentAnimation,
107                currentAnimationCallback,
108                currentAnimationObject,
109                runAfterAnimationCallbacks,
110                awaitingAjaxCallbacks,
111                startedAjaxLoads,
112                finishedAjaxLoads,
113                animateToAfterCompletion = FALSE,
114                animateToAfterCompletionClicked,
115                animateToAfterCompletionSpeed,
116                slideContainerCreated = FALSE,
117                option = [],
118                options = $.extend(TRUE, {}, optionsOrg),
119                currentSliderPositionTop,
120                currentSliderPositionLeft,
121                unBindCallbacks = [],
122                autoStartedWithPause = FALSE,
123                animationWasInterrupted = FALSE;
124
125            // The call to the init function is after the definition of all the functions.
126            function initSudoSlider() {
127                // Storing the public options in an array.
128                var optionIndex = 0;
129                for (var key in options) {
130                    option[optionIndex] = options[key];
131                    optionIndex++;
132                }
133
134                init = TRUE;
135                fullyInitialized = FALSE;
136
137                imagesInSlidesLoaded = [];
138                runAfterAnimationCallbacks = [];
139                awaitingAjaxCallbacks = [];
140                startedAjaxLoads = [];
141                finishedAjaxLoads = [];
142
143                // Fix for nested list items
144                slidesContainer = childrenNotAnimationClones(obj);
145
146                // Is the ul element there?
147                var ulLength = slidesContainer.length;
148                var newUl = $(DIV_TAG);
149                if (!ulLength) {
150                    obj.append(slidesContainer = newUl);
151                    isSlideContainerUl = FALSE;
152                } else if (!((isSlideContainerUl = slidesContainer.is("ul")) || slidesContainer.hasClass(SLIDES_CONTAINER)) && !slideContainerCreated) {
153                    newUl.append(slidesContainer);
154                    obj.append(slidesContainer = newUl);
155                }
156                slideContainerCreated = TRUE;
157
158                var slidesJquery = childrenNotAnimationClones(slidesContainer);
159
160                slides = [];
161
162                totalSlides = slidesJquery.length;
163
164                slidesJquery.each(function (index, elem) {
165                    var that = $(elem);
166                    slides[index] = that;
167                    that.css({position: RELATIVE_STRING});
168                    if (that.css("display") == "none") {
169                        that.css("display", "inline");
170                    }
171                });
172
173                // Adding CSS classes
174                slidesContainer.addClass(SLIDES_CONTAINER);
175
176                slidesJquery.addClass("slide");
177
178                slidesJquery.each(function (index, elem) {
179                    $(elem).attr("data-slide", index + 1);
180                });
181
182                // Now we are going to fix the document, if it's 'broken'. (No <li>).
183                // I assume that it's can only be broken, if ajax is enabled. If it's broken without Ajax being enabled, the script doesn't have anything to fill the holes.
184                if (option[30]/*ajax*/) {
185                    // Do we have enough list elements to fill out all the ajax documents.
186                    var numerOfAjaxUrls = option[30]/*ajax*/.length;
187                    if (numerOfAjaxUrls > totalSlides) {
188                        for (var a = 1; a <= numerOfAjaxUrls - totalSlides; a++) {
189                            var tag;
190                            if (isSlideContainerUl) {
191                                tag = "li";
192                            } else {
193                                tag = "div";
194                            }
195                            var slide = $("<" + tag + ">" + option[32]/*loadingtext*/ + "</" + tag + ">");
196                            slidesContainer.append(slide);
197                            slides[totalSlides + (a - 1)] = slide;
198                        }
199                        slidesJquery = childrenNotAnimationClones(slidesContainer);
200                        totalSlides = numerOfAjaxUrls;
201                    }
202                }
203
204                slidesJquery.each(function (index, elem) {
205                    imagesInSlidesLoaded[index] = FALSE;
206                    runOnImagesLoaded($(elem), TRUE, function () {
207                        imagesInSlidesLoaded[index] = TRUE;
208                    });
209                });
210
211                if (slideNumberBeforeDestroy === FALSE) {
212                    currentSlide = 0;
213                } else {
214                    currentSlide = slideNumberBeforeDestroy;
215                }
216                currentSlide = currentSlide || 0;
217
218                previousSlide = currentSlide;
219
220                clickable = TRUE;
221                numericControls = [];
222                destroyed = FALSE;
223
224
225                // Set obj overflow to hidden (and position to relative <strike>, if fade is enabled. </strike>)
226                obj.css({overflow: HIDDEN_STRING});
227                if (obj.css("position") == "static") obj.css({position: RELATIVE_STRING}); // Fixed a lot of IE6 + IE7 bugs.
228
229                // Float items to the left, and make sure that all elements are shown.
230                slidesJquery.css({"float": "left", listStyle: "none"});
231                // The last CSS to make it work.
232                slidesContainer.add(slidesJquery).css({display: "block", position: RELATIVE_STRING, margin: "0"});
233
234                adjustPositionToPosition(0, 0, TRUE);
235
236                option[7]/*slidecount*/ = parseInt10(option[7]/*slidecount*/);
237
238                // Lets just redefine slidecount
239                numberOfVisibleSlides = option[7]/*slidecount*/;
240
241                option[7]/*slidecount*/ += option[8]/*movecount*/ - 1;
242
243                // startslide can only be a number (and not 0). Converting from 1 index to 0 index.
244                // unless it is the string "random".
245                if (typeof option[9]/*startslide*/ == "string" && option[9]/*startslide*/.toLowerCase() == "random") {
246                    option[9]/*startslide*/ = (Math.random() * totalSlides) | 0;
247                }
248                option[9]/*startslide*/ = parseInt10(option[9]/*startslide*/) - 1 || 0;
249
250                option[0]/*effect*/ = getEffectMethod(option[0]/*effect*/);
251
252                // Cloning numericText, so the array same array can be modifed outside SudoSlider without doing weird things.
253                option[18]/*numerictext*/ = option[18]/*numerictext*/.slice(0);
254
255                for (var a = 0; a < totalSlides; a++) {
256                    if (!option[18]/*numerictext*/[a] && option[18]/*numerictext*/[a] != "") {
257                        option[18]/*numerictext*/[a] = (a + 1);
258                    }
259                    if (option[30]/*ajax*/) {
260                        option[30]/*ajax*/[a] = option[30]/*ajax*/[a] || FALSE;
261                    }
262                }
263
264                option[4]/*controlsfade*/ = option[4]/*controlsfade*/ && !option[15]/*continuous*/;
265
266                if (option[10]/*responsive*/) {
267                    adjustResponsiveLayout(TRUE);
268                }
269
270                // Making sure that i have enough room in the <ul> (Through testing, i found out that the max supported size (height or width) in Firefox is 17895697px, Chrome supports up to 134217726px, and i didn't find any limits in IE (6/7/8/9)).
271                // 9000000px gives room for about 12500 slides of 700px each (and works in every browser i tested). Down to 9000000 from 10000000 because the later might not work perfectly in FireFox on OSX.
272                slidesContainer[option[6]/*vertical*/ ? "height" : "width"](9000000)[option[6]/*vertical*/ ? "width" : "height"]("100%");
273
274                // If responsive is turned on, autowidth doesn't work.
275                option[28]/*autowidth*/ = option[28]/*autowidth*/ && !option[10]/*responsive*/;
276
277                if (option[10]/*responsive*/) {
278                    bindAndRegisterOff(jWin, "resize focus", adjustResponsiveLayout, "");
279                }
280
281                if (option[17]/*numeric*/ || option[16]/*prevnext*/) {
282                    controls = $("<span " + option[35]/*controlsattr*/ + "></span>");
283                    obj[option[5]/*insertafter*/ ? "after" : "before"](controls);
284
285                    if (option[17]/*numeric*/) {
286                        numericContainer = $("<ol " + option[36]/*numericattr*/ + "></ol>");
287                        controls.prepend(numericContainer);
288                        var usePages = option[17]/*numeric*/ == PAGES_MARKER_STRING;
289                        var distanceBetweenPages = usePages ? numberOfVisibleSlides : 1;
290                        for (var a = 0; a < totalSlides - ((option[15]/*continuous*/ || usePages) ? 1 : numberOfVisibleSlides) + 1; a += distanceBetweenPages) {
291                            numericControls[a] = $("<li data-target=\"" + (a + 1) + "\"><a href=\"#\"><span>" + option[18]/*numerictext*/[a] + "</span></a></li>")
292                                .appendTo(numericContainer)
293                                .click(function () {
294                                    enqueueAnimation(getTargetAttribute(this) - 1, TRUE);
295                                    return FALSE;
296                                });
297                        }
298                    }
299                    if (option[16]/*prevnext*/) {
300                        nextbutton = makecontrol(option[34]/*nexthtml*/, NEXT_STRING);
301                        prevbutton = makecontrol(option[33]/*prevhtml*/, PREV_STRING);
302                    }
303                    if (option[4]/*controlsfade*/) {
304                        fadeControls(currentSlide, 0);
305                    }
306                }
307
308
309                // Lets make those fast/normal/fast into some numbers we can make calculations with.
310                var optionsToConvert = [4/*controlsfadespeed*/, 1/*speed*/, 14/*pause*/];
311                for (var i = 0; i < optionsToConvert.length; i++) {
312                    option[optionsToConvert[i]] = textSpeedToNumber(option[optionsToConvert[i]]);
313                }
314
315                if (option[2]/*customlink*/) {
316                    var filter = stringTrim(option[2]/*customlink*/);
317                    if (filter.charAt(0) == ">") {
318                        bindAndRegisterOff(obj, "click", customLinkClickHandler, filter.substr(1, filter.length));
319                    } else {
320                        bindAndRegisterOff(doc, "click", customLinkClickHandler, filter);
321                    }
322                }
323
324                runOnImagesLoaded(getSlides(option[9]/*startslide*/, option[7]/*slidecount*/), TRUE, function () {
325                    if (slideNumberBeforeDestroy !== FALSE) {
326                        goToSlide(slideNumberBeforeDestroy, FALSE);
327                    } else if (option[26]/*history*/) {
328                        // I support the jquery.address plugin, Ben Alman's hashchange plugin and Ben Alman's jQuery.BBQ.
329                        var hashPlugin;
330                        if (hashPlugin = jWin.hashchange) {
331                            hashPlugin(URLChange);
332                        } else if (hashPlugin = $.address) {
333                            hashPlugin.change(URLChange);
334                        } else {
335                            // This assumes that jQuery BBQ is included. If not, stuff won't work in old browsers.
336                            bindAndRegisterOff(jWin, "hashchange", URLChange);
337                        }
338                        URLChange();
339
340                    } else {
341                        goToSlide(option[9]/*startslide*/, FALSE);
342                    }
343
344                    setCurrent(currentSlide);
345                });
346                if (option[30]/*ajax*/[option[9]/*startslide*/]) {
347                    ajaxLoad(option[9]/*startslide*/);
348                }
349                if (option[31]/*preloadajax*/ === TRUE) {
350                    for (var i = 0; i < totalSlides; i++) {
351                        if (option[30]/*ajax*/[i] && option[9]/*startslide*/ != i) {
352                            ajaxLoad(i);
353                        }
354                    }
355                } else {
356                    startAsyncDelayedLoad();
357                }
358            }
359
360            /*
361             * The functions do the magic.
362             */
363
364            function customLinkClickHandler() {
365                var target;
366                if (target = getTargetAttribute(this)) {
367                    if (target == 'stop') {
368                        option[12]/*auto*/ = FALSE;
369                        stopAuto();
370                    } else if (target == "start") {
371                        startAuto();
372                        option[12]/*auto*/ = TRUE;
373                    } else if (target == 'block') {
374                        clickable = FALSE;
375                    } else if (target == 'unblock') {
376                        clickable = TRUE;
377                    } else {
378                        enqueueAnimation((target == parseInt10(target)) ? target - 1 : target, TRUE);
379                    }
380                }
381                return FALSE;
382            }
383
384            // Adjusts the slider when a change in layout has happened.
385            var previousAdjustedResponsiveWidth = -1;
386
387            function adjustResponsiveLayout(forced) {
388                function doTheAdjustment() {
389                    if ((cantDoAdjustments() && !(forced === TRUE)) || totalSlides == 0) {
390                        return;
391                    }
392
393                    var subPixelNewWidth = getResponsiveWidth();
394                    var newWidth = parseInt10(subPixelNewWidth);
395                    var addAPixelEvery = 1 / (subPixelNewWidth - newWidth);
396
397                    if (previousAdjustedResponsiveWidth != newWidth || (forced === TRUE)) {
398                        previousAdjustedResponsiveWidth = newWidth;
399
400                        var slideCounter = 1;
401                        for (var i = 0; i < totalSlides; i++) {
402                            slideCounter++;
403                            if (slideCounter >= addAPixelEvery) {
404                                slideCounter = 0;
405                                slides[i].width(newWidth + 1);
406                            } else {
407                                slides[i].width(newWidth);
408                            }
409                        }
410                        if (autoStartedWithPause !== FALSE) {
411                            startAuto(autoStartedWithPause);
412                        }
413
414                        stopAnimation();
415                        ensureSliderContainerCSSDurationReset();
416                        adjustPositionTo(currentSlide);
417                        autoadjust(currentSlide, 0);
418                    }
419                }
420
421                doTheAdjustment();
422                callAsync(doTheAdjustment); // Fixing invisible scrollbar.
423                schedule(doTheAdjustment, 20);
424            }
425
426            // Returns the width of a single <li> if the page layout is responsive.
427            function getResponsiveWidth() {
428                var width = obj.width();
429                if (option[6]/*vertical*/) {
430                    return width;
431                } else {
432                    return width / numberOfVisibleSlides;
433                }
434            }
435
436            // For backwards compability, the rel attribute is used as a fallback.
437            function getTargetAttribute(that) {
438                that = $(that);
439                return that.attr("data-target") || that.attr("rel");
440            }
441
442            // Triggered when the URL changes.
443            function URLChange() {
444                var target = getUrlHashTarget();
445                if (init) {
446                    goToSlide(target, FALSE);
447                } else {
448                    enqueueAnimation(target, FALSE);
449                }
450            }
451
452            function startAsyncDelayedLoad() {
453                if (option[31]/*preloadajax*/ !== FALSE) {
454                    var preloadAjaxTime = parseInt10(option[31]/*preloadajax*/);
455                    if (option[30]/*ajax*/) {
456                        for (var i = 0; i < option[30]/*ajax*/.length; i++) {
457                            if (option[30]/*ajax*/[i]) {
458                                clearTimeout(asyncDelayedSlideLoadTimeout);
459                                asyncDelayedSlideLoadTimeout = schedule(function () {
460                                    if (option[30]/*ajax*/[i]) {
461                                        ajaxLoad(i);
462                                    } else {
463                                        startAsyncDelayedLoad();
464                                    }
465                                }, preloadAjaxTime);
466
467                                break;
468                            }
469                        }
470                    }
471                }
472            }
473
474            function startAuto(pause) {
475                if (pause === undefined) {
476                    var dataPause = slides[currentSlide].attr("data-pause");
477                    if (dataPause !== undefined) {
478                        pause = parseInt10(dataPause);
479                    } else {
480                        pause = option[13]/*pause*/;
481                    }
482                }
483                if (animationWasInterrupted) {
484                    pause = mathMax(pause, 100);
485                }
486                stopAuto();
487                autoOn = TRUE;
488                autoStartedWithPause = pause;
489                autoTimeout = schedule(function () {
490                    if (autoOn && !currentlyAnimating) {
491                        enqueueAnimation(NEXT_STRING, FALSE);
492                        autoStartedWithPause = FALSE;
493                    }
494                }, pause);
495            }
496
497            function stopAuto(autoPossibleStillOn) {
498                if (autoTimeout) {
499                    clearTimeout(autoTimeout);
500                }
501                if (!autoPossibleStillOn) autoOn = FALSE;
502            }
503
504            function textSpeedToNumber(speed) {
505                if (parseInt10(speed) || speed == 0) {
506                    return parseInt10(speed);
507                }
508                if (speed == "fast") {
509                    return 200;
510                }
511                if (speed == "normal" || speed == "medium") {
512                    return 400;
513                }
514                if (speed == "slow") {
515                    return 600;
516                }
517                return 600;
518            }
519
520            function makecontrol(html, action) {
521                return $(html).prependTo(controls).click(function () {
522                    enqueueAnimation(action, TRUE);
523                    return FALSE;
524                });
525            }
526
527            function enqueueAnimation(direction, clicked, speed) {
528                if (clickable && !init) {
529                    // Stopping, because if its needed then its restarted after the end of the animation.
530                    stopAuto(TRUE);
531
532                    if (!destroyed) {
533                        loadSlidesAndAnimate(direction, clicked, speed);
534                    }
535                } else {
536                    if (option[37]/*interruptible*/ && currentlyAnimating) {
537                        if (getRealPos(filterDir(direction)) !== currentlyAnimatingTowards) {
538                            stopAnimation();
539                            enqueueAnimation(direction, clicked, speed);
540                        }
541                    } else {
542                        animateToAfterCompletion = direction;
543                        animateToAfterCompletionClicked = clicked;
544                        animateToAfterCompletionSpeed = speed;
545
546                        // I can just as well start the ajax loads
547                        if (option[30]/*ajax*/) {
548                            var targetSlide = filterDir(direction);
549                            for (var loadSlide = targetSlide; loadSlide < targetSlide + numberOfVisibleSlides; loadSlide++) {
550                                if (option[30]/*ajax*/[loadSlide]) {
551                                    ajaxLoad(getRealPos(loadSlide));
552                                }
553                            }
554                        }
555                    }
556                }
557            }
558
559            // It may not sound like it, but the variable fadeOpacity is only for TRUE/FALSE.
560            function fadeControl(fadeOpacity, fadetime, nextcontrol) {
561                fadeOpacity = fadeOpacity ? 1 : 0;
562                var fadeElement = $();
563
564                if (option[16]/*prevnext*/) {
565                    fadeElement = nextcontrol ? nextbutton : prevbutton;
566                }
567
568                if (option[2]/*customlink*/) {
569                    var customLink = $(option[2]/*customlink*/);
570                    var filterString = "=\"" + (nextcontrol ? NEXT_STRING : PREV_STRING) + "\"]";
571                    var filtered = customLink.filter("[rel" + filterString + ", [data-target" + filterString + "");
572                    fadeElement = fadeElement.add(filtered);
573                }
574
575                var adjustObject = {opacity: fadeOpacity};
576
577                function callback() {
578                    if (!fadeOpacity && fadeElement.css("opacity") == 0) {
579                        fadeElement.css({visibility: HIDDEN_STRING});
580                    }
581                }
582
583                if (fadeOpacity) {
584                    fadeElement.css({visibility: "visible"});
585                }
586
587                if (option[38]/*useCSS*/) {
588                    animate(fadeElement, adjustObject, fadetime, option[46]/*CSSease*/, callback);
589                } else {
590                    fadeElement.animate(
591                        adjustObject,
592                        {
593                            queue: FALSE,
594                            duration: fadetime,
595                            easing: option[11]/*ease*/,
596                            callback: callback
597                        }
598                    );
599                }
600            }
601
602            // Fade the controls, if we are at the end of the slide.
603            // It's all the different kind of controls.
604            function fadeControls(slide, fadetime) {
605                fadeControl(slide, fadetime, FALSE); // abusing that the number 0 == FALSE.
606                // The new way of doing it.
607                fadeControl(slide < totalSlides - numberOfVisibleSlides, fadetime, TRUE);
608            }
609
610            // Updating the 'current' class
611            function setCurrent(i) {
612                i = getRealPos(i) + 1;
613
614                // Fixing that the last numeric control isn't marked when we are at the last possible position.
615                if (option[17]/*numeric*/ == PAGES_MARKER_STRING && i == totalSlides - numberOfVisibleSlides + 1 && !option[15]/*continuous*/) {
616                    i = totalSlides;
617                }
618
619                if (option[17]/*numeric*/) {
620                    for (var control = 0; control < numericControls.length; ++control) {
621                        var element = numericControls[control];
622                        setCurrentElement(element, i)
623                    }
624                }
625                if (option[2]/*customlink*/) setCurrentElement($(option[2]/*customlink*/), i);
626            }
627
628            function setCurrentElement(element, i) {
629                if (element && element.filter) {
630                    element
631                        .filter(".current")
632                        .removeClass("current");
633
634                    element
635                        .filter(function () {
636                            var elementTarget = getTargetAttribute(this);
637                            if (option[17]/*numeric*/ == PAGES_MARKER_STRING) {
638                                for (var a = numberOfVisibleSlides - 1; a >= 0; a--) {
639                                    if (elementTarget == i - a) {
640                                        return TRUE;
641                                    }
642                                }
643                            } else {
644                                return elementTarget == i;
645                            }
646                            return FALSE;
647                        })
648                        .addClass("current");
649                }
650            }
651
652            function getUrlHashTarget() {
653                var hashString = location.hash.substr(1)
654                for (var i = 0; i < option[18]/*numerictext*/.length; i++) {
655                    if (option[18]/*numerictext*/[i] == hashString) {
656                        return i;
657                    }
658                }
659                if (hashString && !init) {
660                    return currentSlide;
661                } else {
662                    return option[9]/*startslide*/;
663                }
664            }
665
666            function autoadjust(i, speed) {
667                i = getRealPos(i); // I assume that the continuous clones, and the original element is the same height. So i always adjust according to the original element.
668
669                adjustingTo = i;
670                adjustTargetTime = getTimeInMillis() + speed;
671
672                if (speed == 0) {
673                    finishedAdjustingTo = i;
674                } else {
675                    finishedAdjustingTo = FALSE;
676                }
677
678                if (option[27]/*autoheight*/ || option[28]/*autowidth*/) {
679                    autoHeightWidth(i);
680                }
681            }
682
683
684            function autoHeightWidth(i) {
685                obj.ready(function () {
686                    adjustHeightWidth(i);                   
687                    runOnImagesLoaded(slides[i], FALSE, makeCallback(adjustHeightWidth, [i]));
688                });
689            }
690
691            // Axis: TRUE == height, FALSE == width.
692            function getSliderDimensions(fromSlide, vertical) {
693                var pixels = 0;
694                for (var slide = fromSlide; slide < fromSlide + numberOfVisibleSlides; slide++) {
695                    var targetSlide = slides[getRealPos(slide)];
696                    if (targetSlide) {
697                        var targetPixels = targetSlide['outer' + (vertical ? "Height" : "Width")](TRUE);
698                        if (vertical == option[6]/*vertical*/) {
699                            pixels += targetPixels;
700                        } else {
701                            pixels = mathMax(targetPixels, pixels);
702                        }
703                    }
704                }
705                return pixels;
706            }
707
708            var prevHeightWidthAdjustObject  = {};
709
710            function adjustHeightWidth(i) {
711                if (i != adjustingTo || cantDoAdjustments()) {
712                    return;
713                }
714
715                var speed = adjustTargetTime - getTimeInMillis();
716                speed = mathMax(speed, 0);
717                var adjustObject = {};
718                if (option[27]/*autoheight*/) {
719                    adjustObject.height = getSliderDimensions(i, TRUE) || 1; // Making it completely invisible gives trouble.
720                }
721                if (option[28]/*autowidth*/) {
722                    adjustObject.width = getSliderDimensions(i, FALSE) || 1;
723                }
724
725
726                if (simpleObjectEquals(prevHeightWidthAdjustObject, adjustObject)) {
727                    return;
728                } else {
729                    prevHeightWidthAdjustObject = adjustObject;
730                }
731
732                if (option[38]/*useCSS*/) {
733                    animate(obj, adjustObject, speed, option[46]/*CSSease*/)
734                } else {
735                    if (speed == 0) {
736                        // Doing CSS if speed == 0, 1: its faster. 2: it fixes bugs.
737                        obj.stop().css(adjustObject);
738                    } else {
739                        obj.animate(
740                            adjustObject,
741                            {
742                                queue: FALSE,
743                                duration: speed,
744                                easing: option[11]/*ease*/
745                            }
746                        );
747                    }
748                }
749            }
750
751            function adjustPositionTo(slide) {
752                var left = getSlidePosition(slide, FALSE);
753                var top = getSlidePosition(slide, TRUE);
754
755                adjustPositionToPosition(left, top);
756            }
757
758            function adjustPositionToPosition(left, top, both) {
759                currentSliderPositionLeft = left;
760                currentSliderPositionTop = top;
761
762                if (option[38]/*useCSS*/ || both) {
763                    slidesContainer.css({transform: "translate(" + left + "px, " + top + "px)"});
764                }
765                if (!option[38]/*useCSS*/ || both) {
766                    function setMargins(left, top) {
767                        slidesContainer.css({
768                            marginLeft: left,
769                            marginTop: top
770                        });
771                    }
772
773                    setMargins(0, 0);
774                    setMargins(left, top);
775                }
776            }
777
778            function getSlidePosition(slide, vertical) {
779                if (vertical == undefined) {
780                    vertical = option[6]/*vertical*/;
781                }
782                var targetSlide = slides[getRealPos(slide)];
783                return (targetSlide && targetSlide.length) ? -targetSlide.position()[vertical ? "top" : "left"] : 0;
784            }
785
786            function callQueuedAnimation() {
787                if (animateToAfterCompletion !== FALSE) {
788                    var animateTo = animateToAfterCompletion;
789                    animateToAfterCompletion = FALSE;
790                    callAsync(makeCallback(enqueueAnimation, [animateTo, animateToAfterCompletionClicked, animateToAfterCompletionSpeed]));
791                }
792            }
793
794            function adjust(clicked) {
795                ensureSliderContainerCSSDurationReset();
796
797                autoadjust(currentSlide, 0);
798                currentSlide = getRealPos(currentSlide); // Going to the real slide, away from the clone.
799                if (!option[29]/*updateBefore*/) setCurrent(currentSlide);
800                adjustPositionTo(currentSlide);
801                clickable = TRUE;
802                currentlyAnimatingTowards = FALSE;
803
804                if (option[12]/*auto*/) {
805                    // Stopping auto if clicked. And also continuing after X seconds of inactivity.
806                    if (clicked) {
807                        stopAuto();
808                        if (option[14]/*resumepause*/) startAuto(option[14]/*resumepause*/);
809                    } else if (!init) {
810                        startAuto();
811                    }
812                }
813
814                callQueuedAnimation();
815            }
816
817            // This function is called when i need a callback on the current element and it's continuous clones (if they are there).
818            // after:  TRUE == afteranimation : FALSE == beforeanimation;
819            function aniCall(i, after, synchronous, speed) {
820                i = getRealPos(i);
821                // Wierd fix to let IE accept the existance of the sudoSlider object.
822                var func = makeCallback(after ? afterAniCall : beforeAniCall, [slides[i], i + 1, speed]);
823                if (synchronous) {
824                    func();
825                } else {
826                    callAsync(func);
827                }
828            }
829
830            function afterAniCall(el, a) {
831                option[25]/*afteranimation*/.call(el, a, baseSlider);
832            }
833
834            function beforeAniCall(el, a, speed) {
835                option[24]/*beforeanimation*/.call(el, a, baseSlider, speed);
836            }
837
838            // Convert the direction into a usefull number.
839            function filterDir(dir) {
840                if (dir == NEXT_STRING) {
841                    return limitDir(currentSlide + option[8]/*movecount*/, dir);
842                } else if (dir == PREV_STRING) {
843                    return limitDir(currentSlide - option[8]/*movecount*/, dir);
844                } else if (dir == FIRST_STRING) {
845                    return 0;
846                } else if (dir == LAST_STRING) {
847                    return totalSlides - 1;
848                } else {
849                    return limitDir(parseInt10(dir), dir);
850                }
851            }
852
853            // If continuous is off, we sometimes do not want to move to far.
854            // This method was added in 2.1.8, se the changelog as to why.
855            function limitDir(i, dir) {
856                if (option[15]/*continuous*/) {
857                    if (dir == NEXT_STRING || dir == PREV_STRING) {
858                        return i;
859                    } else {
860                        return getRealPos(i);
861                    }
862                } else {
863                    var maxSlide = totalSlides - numberOfVisibleSlides;
864                    if (i > maxSlide) {
865                        if (currentSlide == maxSlide && dir == NEXT_STRING) {
866                            return 0;
867                        } else {
868                            return maxSlide;
869                        }
870                    } else if (i < 0) {
871                        if (currentSlide == 0 && dir == PREV_STRING) {
872                            return maxSlide;
873                        } else {
874                            return 0;
875                        }
876                    } else {
877                        return i;
878                    }
879                }
880            }
881
882            // Load a ajax document (or image) into a slide.
883            // If testing this locally (loading everything from a harddisk instead of the internet), it may not work.
884            // But then try to upload it to a server, and see it shine.
885            function ajaxLoad(slide, ajaxCallBack) {
886                if (ajaxCallBack) {
887                    var callbackList = awaitingAjaxCallbacks[slide];
888                    if (!callbackList) {
889                        callbackList = awaitingAjaxCallbacks[slide] = [];
890                    }
891                    callbackList.push(ajaxCallBack);
892                }
893
894                if (finishedAjaxLoads[slide]) {
895                    if (ajaxCallBack) {
896                        runOnImagesLoaded(slides[slide], TRUE, makeCallback(callAsync, [ajaxCallBack]));
897                    }
898                    return;
899                }
900
901                if (startedAjaxLoads[slide]) {
902                    return;
903                }
904                startedAjaxLoads[slide] = TRUE;
905
906                var target = option[30]/*ajax*/[slide];
907                if (!target) {
908                    callAsync(ajaxCallBack);
909                    return;
910                }
911
912                if (asyncDelayedSlideLoadTimeout) clearTimeout(asyncDelayedSlideLoadTimeout);// I dont want it to run to often.
913
914                var targetslide = slides[slide];
915
916                // Loads the url as an image, either if it is an image, or if everything else failed.
917                function loadImage() {
918                    var image = new Image();
919                    image.src = target;
920                    var thatImage = $(image);
921                    runOnImagesLoaded(thatImage, TRUE, function () {
922                        targetslide.empty().append(image);
923
924                        ajaxAdjust(slide, TRUE);
925                    });
926                }
927
928                // It is loaded, we dont need to do that again.
929                option[30]/*ajax*/[slide] = FALSE;
930                // It is the only option that i need to change for good.
931                options.ajax[slide] = FALSE;
932
933                if (option[47]/*AjaxHasHTML*/) {
934                    var succesRan = FALSE;
935                    $.ajax({
936                        url: target,
937                        success: function (data, textStatus, jqXHR) {
938                            succesRan = TRUE;
939                            runWhenNotAnimating(function () {
940                                var type = jqXHR.getResponseHeader('Content-Type');
941                                if (type && type.substr(0, 1) != "i") {
942                                    targetslide.html(data);
943                                    ajaxAdjust(slide, FALSE);
944                                } else {
945                                    loadImage();
946                                }
947                            });
948                        },
949                        complete: function () {
950                            // Some browsers wont load images this way, so i treat an error as an image.
951                            // There is no stable way of determining if it's a real error or if i tried to load an image in a old browser, so i do it this way.
952                            if (!succesRan) {
953                                loadImage();
954                            }
955                        }
956                    });
957                } else {
958                    loadImage();
959                }
960            }
961
962            // Performs the callback immediately if no animation is running.
963            // Otherwise waits for the animation to complete in a FIFO queue.
964            function runWhenNotAnimating(completeFunction) {
965                if (currentlyAnimating) {
966                    runAfterAnimationCallbacks.push(completeFunction);
967                } else {
968                    callAsync(completeFunction);
969                }
970            }
971
972            function ajaxAdjust(i, img) {
973                var target = slides[i];
974
975                if (!currentlyAnimating) {
976                    adjustPositionTo(currentSlide);
977                    autoadjust(currentSlide, 0);
978                }
979
980                runOnImagesLoaded(target, TRUE, makeCallback(runWhenNotAnimating, [
981                    function () {
982                        adjustPositionTo(currentSlide);
983                        autoadjust(currentSlide, 0);
984
985                        finishedAjaxLoads[i] = TRUE;
986
987                        performCallbacks(awaitingAjaxCallbacks[i]);
988
989                        startAsyncDelayedLoad();
990
991                        callAsync(function () {
992                            option[23]/*ajaxload*/.call(slides[i], i + 1, img, baseSlider);
993                        });
994
995                        if (init) {
996                            init = FALSE;
997                            callAsync(performInitCallback);
998                        }
999                    }
1000                ]));
1001            }
1002
1003            function performInitCallback() {
1004                fullyInitialized = TRUE;
1005                if (option[15]/*continuous*/) {
1006                    centerTargetSlideAfter(currentSlide);
1007                }
1008
1009                autoadjust(currentSlide, 0);
1010                adjustPositionTo(currentSlide);
1011                callQueuedAnimation();
1012                if (option[10]/*responsive*/) {
1013                    adjustResponsiveLayout();
1014                }
1015                if (option[12]/*auto*/) {
1016                    startAuto();
1017                }
1018                option[22]/*initCallback*/.call(baseSlider);
1019
1020                if (option[41]/*touch*/) {
1021                    setUpTouch();
1022                }
1023
1024                // Fixing once and for all that the wrong slide is shown on init.
1025                runOnImagesLoaded(getSlides(currentSlide, totalSlides), FALSE, makeCallback(runWhenNotAnimating, [
1026                    function () {
1027                        autoadjust(currentSlide, 0);
1028                        adjustPositionTo(currentSlide);
1029                    }
1030                ]));
1031            }
1032
1033            function setUpTouch() {
1034                var body = $("body");
1035                var easingToUse;
1036                var runningTouchEffect = FALSE;
1037
1038                var prevEffect = option[0]/*effect*/;
1039
1040                option[0]/*effect*/ = function (obj) {
1041                    if (runningTouchEffect) {
1042                        runningTouchEffect = FALSE;
1043                        var options = obj.options;
1044                        options.ease = easingToUse;
1045                        options.cssease = easingToUse;
1046                        return slide(obj);
1047                    } else {
1048                        return prevEffect(obj);
1049                    }
1050                };
1051                var initialOffsetLeft;
1052                var initialOffsetTop;
1053
1054                var startTime;
1055                var lastTime;
1056                var lastDistance;
1057
1058                var bufferSize = 3;
1059                var positionsBuffer = [];
1060                var timeBuffer = [];
1061                var bufferIndex = 0;
1062
1063                var currentTouchSlide;
1064                var baseDistance;
1065
1066                function touchStart(x, y) {
1067                    body.addClass(SUDO_DRAGGING_CLASS);
1068                    currentTouchSlide = currentSlide;
1069                    baseDistance = 0;
1070
1071                    currentlyAnimating = TRUE;
1072                    ensureSliderContainerCSSDurationReset();
1073                    initialOffsetTop = currentSliderPositionTop;
1074                    initialOffsetLeft = currentSliderPositionLeft;
1075
1076                    var distance;
1077                    if (option[6]/*vertical*/) {
1078                        distance = mathAbs(y);
1079                    } else {
1080                        distance = mathAbs(x);
1081                    }
1082
1083                    lastDistance = distance;
1084                    startTime = getTimeInMillis();
1085                    lastTime = startTime;
1086
1087                    var currentSlidePosition = getSlidePosition(currentTouchSlide);
1088                    distanceToRightSlide = getSlidePosition(currentTouchSlide + 1) - currentSlidePosition;
1089
1090                    correctionWhenCalculatingWhereToMoveTouchDistance = FALSE;
1091                }
1092
1093                var distanceToRightSlide;
1094                var correctionWhenCalculatingWhereToMoveTouchDistance;
1095
1096                function touchMove(x, y) {
1097                    var distance;
1098                    if (option[6]/*vertical*/) {
1099                        distance = y;
1100                    } else {
1101                        distance = x;
1102                    }
1103                    var distanceAbs = mathAbs(distance);
1104                    positionsBuffer[bufferIndex] = distanceAbs - lastDistance;
1105                    var newTime = getTimeInMillis();
1106                    timeBuffer[bufferIndex] = newTime - lastTime;
1107                    bufferIndex = (bufferIndex + 1) % bufferSize;
1108
1109                    lastTime = newTime;
1110                    lastDistance = distanceAbs;
1111
1112                    if (option[15]/*continuous*/ && totalSlides >= numberOfVisibleSlides + 1) {
1113                        // Moving everything to the right
1114                        var direction = 0;
1115                        if ((distance - baseDistance) < distanceToRightSlide) {
1116                            correctionWhenCalculatingWhereToMoveTouchDistance = FALSE;
1117
1118                            baseDistance += distanceToRightSlide;
1119                            direction = 1;
1120
1121                            var rightSlidePositionBefore = getSlidePosition(currentTouchSlide + direction);
1122
1123                            centerTargetSlideAfter(currentTouchSlide + direction);
1124                            var rightSlidePositionAfter = getSlidePosition(currentTouchSlide + direction);
1125
1126                        }
1127
1128                        // Moving everything to the left.
1129                        if ((distance - baseDistance) > 0) {
1130                            direction = -1;
1131
1132                            var rightSlidePositionBefore = getSlidePosition(currentTouchSlide);
1133
1134                            centerTargetSlideAfter(currentTouchSlide + direction);
1135                            rightSlidePositionAfter = getSlidePosition(currentTouchSlide);
1136
1137                            var moveBaseDistance = getSlidePosition(currentTouchSlide - 1) - rightSlidePositionAfter;
1138                            baseDistance += moveBaseDistance;
1139
1140                            correctionWhenCalculatingWhereToMoveTouchDistance = -moveBaseDistance;
1141                        }
1142
1143                        if (direction != 0) {
1144                            currentTouchSlide += direction;
1145
1146                            if (option[6]/*vertical*/) {
1147                                initialOffsetTop -= rightSlidePositionBefore - rightSlidePositionAfter;
1148                            } else {
1149                                initialOffsetLeft -= rightSlidePositionBefore - rightSlidePositionAfter;
1150                            }
1151
1152                            distanceToRightSlide = getSlidePosition(currentTouchSlide + 1) - getSlidePosition(currentTouchSlide);
1153                        }
1154                    }
1155
1156                    if (option[6]/*vertical*/) {
1157                        x = 0;
1158                    } else {
1159                        y = 0;
1160                    }
1161                    adjustPositionToPosition(initialOffsetLeft + x, initialOffsetTop + y);
1162                }
1163
1164                function touchEnd(x, y) {
1165                    body.removeClass(SUDO_DRAGGING_CLASS);
1166                    var distance;
1167                    if (option[6]/*vertical*/) {
1168                        distance = y;
1169                    } else {
1170                        distance = x;
1171                    }
1172                    distance -= baseDistance;
1173
1174                    if (correctionWhenCalculatingWhereToMoveTouchDistance) {
1175                        distance -= correctionWhenCalculatingWhereToMoveTouchDistance;
1176                        currentTouchSlide++;
1177                    }
1178
1179                    var distanceAbs = mathAbs(distance);
1180                    var currentTime = getTimeInMillis();
1181                    var time = 0;
1182                    var bufferDistance = 0;
1183                    for (var i = 0; i < bufferSize; i++) {
1184                        var thisTime = timeBuffer[i];
1185                        if (thisTime + 100 < currentTime) {
1186                            time += thisTime;
1187                            bufferDistance += positionsBuffer[i];
1188                        }
1189                    }
1190                    var slideDimensions;
1191                    if (option[6]/*vertical*/) {
1192                        slideDimensions = obj.height();
1193                    } else {
1194                        slideDimensions = obj.width();
1195                    }
1196                    // This is in pixels pr. ms.
1197                    var speed = mathAbs(bufferDistance) / time;
1198
1199                    var goToAnotherSlide = speed >= 0.2 || distanceAbs >= slideDimensions / 2;
1200
1201                    if ((bufferDistance > 0 && distanceAbs < 0) || (bufferDistance < 0 && distanceAbs > 0) || distanceAbs <= 10) {
1202                        goToAnotherSlide = FALSE;
1203                    }
1204
1205
1206                    var direction = distance < 0 ? NEXT_STRING : PREV_STRING;
1207
1208                    if (!option[15]/*continuous*/) {
1209                        if (currentSlide + 1 == totalSlides) {
1210                            if (direction == NEXT_STRING) {
1211                                goToAnotherSlide = FALSE;
1212                            }
1213                        } else if (currentSlide == 0) {
1214                            if (direction == PREV_STRING) {
1215                                goToAnotherSlide = FALSE;
1216                            }
1217                        }
1218                    }
1219
1220
1221                    direction = direction == NEXT_STRING ? currentTouchSlide + 1 : currentTouchSlide - 1;
1222                    direction = mod(direction, totalSlides);
1223
1224                    var distanceLeft;
1225                    if (goToAnotherSlide) {
1226                        distanceLeft = slideDimensions - distanceAbs;
1227                    } else {
1228                        distanceLeft = distanceAbs;
1229                    }
1230
1231                    var timeFromSpeed = (distanceLeft / speed) * 1.3;
1232                    var timeFromDistance = mathMax((option[1]/*speed*/) * (slideDimensions / distanceLeft), (option[1]/*speed*/) / 4);
1233
1234                    var timeLeft;
1235                    if (timeFromSpeed < timeFromDistance) {
1236                        timeLeft = mathMin(timeFromSpeed, (option[1]/*speed*/));
1237                    } else {
1238                        timeLeft = mathMin(timeFromDistance, (option[1]/*speed*/));
1239                    }
1240
1241                    var cubicBezierY = (speed * timeLeft) / (distanceLeft + speed * timeLeft);
1242                    var cubicBezierX = 1 - cubicBezierY;//distanceLeft/(distanceLeft + speed*timeLeft);
1243
1244                    if (option[38]/*useCSS*/) {
1245                        easingToUse = "cubic-bezier(" + cubicBezierX + "," + cubicBezierY + ",0.3,1)";
1246                    } else {
1247                        easingToUse = makeBezier([cubicBezierX || 0, cubicBezierY || 0, 0.3, 1]);
1248                    }
1249
1250                    clickable = FALSE;
1251                    runningTouchEffect = TRUE;
1252                    if (goToAnotherSlide) {
1253                        performAnimation(direction, timeLeft, TRUE, TRUE, TRUE);
1254                    } else {
1255                        performAnimation(currentTouchSlide, timeLeft, TRUE, TRUE, TRUE);
1256                    }
1257                }
1258
1259                {
1260                    var startedTouch = FALSE;
1261                    var startX = 0;
1262                    var startY = 0;
1263                    var prevX = 0;
1264                    var prevY = 0;
1265
1266                    var dragFunction = function (event) {
1267                        if (!clickable) {
1268                            return;
1269                        }
1270                        var type = event.type;
1271                        var startEvent;
1272                        var endEvent1;
1273                        var endEvent2;
1274                        var isMouseEvent = type.substr(0, 1) == "m";
1275                        if (isMouseEvent) {
1276                            startEvent = MOUSEDOWN;
1277                            endEvent1 = MOUSEUP;
1278                            endEvent2 = "";
1279                        } else {
1280                            startEvent = TOUCHSTART;
1281                            endEvent1 = TOUCHEND;
1282                            endEvent2 = TOUCHCANCEL;
1283                            event = event.originalEvent;
1284                        }
1285
1286
1287                        if (!startedTouch) {
1288                            if (type != startEvent) {
1289                                return;
1290                            }
1291
1292                            var eventTarget = event.target;
1293                            var target = $(eventTarget);
1294                            if (!option[42]/*touchHandle*/) {
1295                                target = target.parents().add(eventTarget);
1296                            }
1297                            var filter = option[42]/*touchHandle*/ || obj;
1298                            if (typeof filter === "string") {
1299                                filter = stringTrim(filter);
1300                                if (filter.charAt(0) == ">") {
1301                                    filter = $(filter.substr(1, filter.length), obj);
1302                                }
1303                            }
1304
1305                            var isTarget = target.filter(filter).length;
1306
1307                            if (!isTarget) {
1308                                return;
1309                            } else {
1310                                startedTouch = TRUE;
1311                            }
1312                        }
1313
1314                        if (type != endEvent1 && type != endEvent2) {
1315                            var x;
1316                            var y;
1317                            if (isMouseEvent) {
1318                                x = event.pageX;
1319                                y = event.pageY;
1320                            } else {
1321                                var touch = event.touches[0];
1322                                x = touch.pageX;
1323                                y = touch.pageY;
1324                            }
1325
1326
1327                            if (type == startEvent) {
1328                                startX = x;
1329                                startY = y;
1330                                touchStart(x - startX, y - startY);
1331                                event.preventDefault();
1332                            } else {
1333                                touchMove(x - startX, y - startY);
1334
1335                                if (!allowScroll(isMouseEvent, x - startX, y - startY)) {
1336                                    event.preventDefault();
1337                                }
1338                            }
1339
1340                            prevX = x - startX;
1341                            prevY = y - startY;
1342                        } else {
1343                            touchEnd(prevX, prevY);
1344                            startedTouch = FALSE;
1345                        }
1346                    };
1347                    bindAndRegisterOff(doc, [TOUCHSTART, TOUCHMOVE, TOUCHEND, TOUCHCANCEL].join(" "), dragFunction);
1348                    if (option[44]/*mouseTouch*/) {
1349                        bindAndRegisterOff(doc, [MOUSEDOWN, MOUSEMOVE, MOUSEUP].join(" "), dragFunction);
1350                    }
1351
1352                }
1353
1354                function allowScroll(isMouseEvent, x, y) {
1355                    var isGeneralDirectionVertical = mathAbs(y) > mathAbs(x);
1356                    if (isMouseEvent) {
1357                        // If the user drags vertically, prevent the (horizontal) scroll event
1358                        return FALSE;
1359                    }
1360                    if (!option[45]/*allowScroll*/) {
1361                        return FALSE;
1362                    }
1363                    if (option[6]/*vertical*/) {
1364                        return !isGeneralDirectionVertical;
1365                    } else {
1366                        return isGeneralDirectionVertical;
1367                    }
1368                }
1369            }
1370
1371            function performCallbacks(callbacks) {
1372                while (callbacks && callbacks.length) {
1373                    // Removing and running the first, so we maintain FIFO.
1374                    callbacks.splice(0, 1)[0]();
1375                }
1376            }
1377
1378            function isContentInSlideReady(slide) {
1379                if (!imagesInSlidesLoaded[slide]) {
1380                    return FALSE;
1381                }
1382                if (!option[30]/*ajax*/) {
1383                    return TRUE;
1384                } else {
1385                    if (option[30]/*ajax*/[slide]) {
1386                        return FALSE;
1387                    }
1388                    return !(startedAjaxLoads[slide] && !finishedAjaxLoads[slide]);
1389                }
1390            }
1391
1392            function loadSlidesAndAnimate(i, clicked, speed) {
1393                var dir = filterDir(i);
1394                var prevNext = i == NEXT_STRING || i == PREV_STRING;
1395
1396                var targetSlide = getRealPos(dir);
1397                if (targetSlide == currentSlide) {
1398                    return;
1399                }
1400                clickable = FALSE;
1401                currentlyAnimatingTowards = targetSlide;
1402
1403                if (option[30]/*ajax*/) {
1404                    var waitCounter = 0;
1405                    for (var loadSlide = targetSlide; loadSlide < targetSlide + numberOfVisibleSlides; loadSlide++) {
1406                        var realLoadSlide = getRealPos(loadSlide);
1407                        if (!isContentInSlideReady(realLoadSlide)) {
1408                            waitCounter++;
1409                            ajaxLoad(realLoadSlide, function () {
1410                                // This runs aync, so every callback is placed before the first is run. Therefore this works.
1411                                waitCounter--;
1412                                if (waitCounter == 0) {
1413                                    option[40]/*loadFinish*/.call(baseSlider, dir + 1);
1414                                    performAnimation(dir, speed, clicked, prevNext);
1415                                }
1416                            });
1417                        }
1418                    }
1419                    if (waitCounter == 0) {
1420                        performAnimation(dir, speed, clicked, prevNext);
1421                    } else {
1422                        option[39]/*loadStart*/.call(baseSlider, dir + 1);
1423                    }
1424                } else {
1425                    performAnimation(dir, speed, clicked, prevNext);
1426                }
1427            }
1428
1429            function ensureSliderContainerCSSDurationReset() {
1430                if (option[38]/*useCSS*/) {
1431                    slidesContainer.css(CSSVendorPrefix + "transition-duration", "");
1432                }
1433            }
1434
1435            var reorderedSlidesToStartFromSlide = 0;
1436
1437            function reorderSlides(slide) {
1438                slide = getRealPos(slide);
1439                if (slide == reorderedSlidesToStartFromSlide) {
1440                    return;
1441                }
1442                reorderedSlidesToStartFromSlide = slide;
1443                ensureSliderContainerCSSDurationReset();
1444                for (var i = 0; i < totalSlides; i++) {
1445                    var slideToInsert = slides[getRealPos((slide + i))];
1446                    slidesContainer.append(slideToInsert);
1447                }
1448                adjustPositionTo(currentSlide);
1449            }
1450
1451            function centerTargetSlideAdjusted(targetSlide, extraSpace) {
1452                var offset = mathMax(parseInt10((totalSlides - extraSpace - numberOfVisibleSlides) / 2), 0);
1453                targetSlide = mod(targetSlide - offset, totalSlides);
1454                reorderSlides(targetSlide);
1455            }
1456
1457            function centerTargetSlideAfter(targetSlide) {
1458                centerTargetSlideAdjusted(targetSlide, 0);
1459            }
1460
1461            function centerTargetSlideBefore(targetSlide) {
1462                var startSlide = mathMin(targetSlide, currentSlide);
1463                var adjustment = mathAbs(targetSlide - currentSlide);
1464
1465                centerTargetSlideAdjusted(startSlide, adjustment);
1466            }
1467
1468
1469            function performAnimation(dir, speed, clicked, prevNext, skipPreCenterTargetSlide) {
1470                if (option[29]/*updateBefore*/) setCurrent(dir);
1471
1472                if (option[26]/*history*/ && clicked) win.location.hash = option[18]/*numerictext*/[dir];
1473
1474                if (option[4]/*controlsfade*/) fadeControls(dir, option[3]/*controlsfadespeed*/);
1475
1476                var fromSlides = $();
1477                var toSlides = $();
1478                for (var a = 0; a < numberOfVisibleSlides; a++) {
1479                    fromSlides = fromSlides.add(slides[getRealPos(currentSlide + a)]);
1480                    toSlides = toSlides.add(slides[getRealPos(dir + a)]);
1481                }
1482
1483
1484                // Finding a "shortcut", used for calculating the offsets.
1485                var diff = dir - currentSlide;
1486                var targetSlide;
1487                if (option[15]/*continuous*/ && !prevNext) {
1488                    var diffAbs = mathAbs(diff);
1489                    targetSlide = dir;
1490
1491                    var newDiff = -currentSlide + dir + totalSlides;
1492                    if (mathAbs(newDiff) < diffAbs) {
1493                        targetSlide = dir + totalSlides;
1494                        diff = newDiff;
1495                        diffAbs = mathAbs(diff);
1496                    }
1497
1498                    newDiff = -currentSlide + dir - totalSlides;
1499                    if (mathAbs(newDiff) < diffAbs) {
1500                        targetSlide = dir - totalSlides;
1501                        diff = newDiff;
1502                    }
1503                } else {
1504                    targetSlide = dir;
1505                }
1506
1507                if (option[15]/*continuous*/ && !skipPreCenterTarget
1507Slide) {
1508                    centerTargetSlideBefore(targetSlide);
1509                }
1510
1511                var leftTarget = getSlidePosition(targetSlide, FALSE);
1512                var topTarget = getSlidePosition(targetSlide, TRUE);
1513
1514                var targetLi = slides[getRealPos(dir)];
1515                var callOptions = $.extend(TRUE, {}, options); // Making a copy, to enforce read-only.
1516                var overwritingSpeed = option[1]/*speed*/;
1517                var attributeSpeed = targetLi.attr("data-speed");
1518                if (attributeSpeed != undefined) {
1519                    overwritingSpeed = parseInt10(attributeSpeed);
1520                }
1521                if (speed != undefined) {
1522                    overwritingSpeed = parseInt10(speed);
1523                }
1524                callOptions.speed = overwritingSpeed;
1525
1526                var effect = option[0]/*effect*/;
1527
1528                var specificEffectAttrName = "data-effect";
1529                var slideSpecificEffect = targetLi.attr(specificEffectAttrName);
1530                if (slideSpecificEffect) {
1531                    effect = getEffectMethod(slideSpecificEffect);
1532                }
1533
1534                var slideOutSlide = slides[currentSlide];
1535                if (slideOutSlide) {
1536                    var slideOutSpecificEffect = slides[currentSlide].attr(specificEffectAttrName + "out")
1537                    if (slideOutSpecificEffect) {
1538                        effect = getEffectMethod(slideOutSpecificEffect);
1539                    }
1540                }
1541
1542                currentlyAnimating = TRUE;
1543                currentAnimation = effect;
1544
1545                var callbackHasYetToRun = TRUE;
1546                currentAnimationCallback = function () {
1547                    currentlyAnimating = FALSE;
1548                    callbackHasYetToRun = FALSE;
1549
1550                    goToSlide(dir, clicked);
1551                    fixClearType(toSlides);
1552
1553                    if (option[15]/*continuous*/) {
1554                        centerTargetSlideAfter(targetSlide);
1555                    }
1556
1557                    // afteranimation
1558                    aniCall(dir, TRUE);
1559                    if (option[10]/*responsive*/) {
1560                        adjustResponsiveLayout();
1561                    }
1562
1563                    performCallbacks(runAfterAnimationCallbacks);
1564                };
1565                currentAnimationObject = {
1566                    fromSlides: fromSlides,
1567                    toSlides: toSlides,
1568                    slider: obj,
1569                    container: slidesContainer,
1570                    options: callOptions,
1571                    to: dir + 1,
1572                    from: currentSlide + 1,
1573                    diff: diff,
1574                    target: {
1575                        left: leftTarget,
1576                        top: topTarget
1577                    },
1578                    stopCallbacks: [],
1579                    callback: function () {
1580                        if (callbackHasYetToRun) {
1581                            callbackHasYetToRun = FALSE;
1582                            stopAnimation();
1583                        }
1584                    },
1585                    goToNext: function () {
1586                        if (callbackHasYetToRun) {
1587                            // Only moving after there is content ready to replace the previous.
1588                            runOnImagesLoaded($("." + ANIMATION_CLONE_MARKER_CLASS, obj), TRUE, makeCallback(adjustPositionTo, [dir]));
1589                        }
1590                    }
1591                };
1592
1593                autoadjust(dir, overwritingSpeed);
1594                callAsync(function () {
1595                    // beforeanimation
1596                    aniCall(dir, FALSE, TRUE, overwritingSpeed);
1597
1598                    effect.call(baseSlider, currentAnimationObject);
1599                });
1600            }
1601
1602            function stopAnimation() {
1603                if (currentlyAnimating) {
1604                    //noinspection JSUnusedAssignment
1605                    animationWasInterrupted = TRUE;
1606                    // Doing it in this order isn't a problem in relation to the user-callbacks, since they are run in a setTimeout(callback, 0) anyway.
1607                    if (currentAnimationCallback) {
1608                        currentAnimationCallback();
1609                    }
1610
1611                    performCallbacks(currentAnimationObject.stopCallbacks);
1612
1613
1614                    var stopFunction = currentAnimation.stop;
1615                    if (stopFunction) {
1616                        stopFunction();
1617                    } else {
1618                        defaultStopFunction();
1619                    }
1620                    autoadjust(currentSlide, 0);
1621                    adjustPositionTo(currentSlide);
1622                    animationWasInterrupted = FALSE;
1623                }
1624            }
1625
1626            function bindAndRegisterOff(element, events, handler, selector) {
1627                element.on(events, selector, handler);
1628                unBindCallbacks.push(function () {
1629                    element.off(events, selector, handler);
1630                });
1631            }
1632
1633            function cantDoAdjustments() {
1634                return !obj.is(":visible") || init || destroyed;
1635            }
1636
1637            function defaultStopFunction() {
1638                $("." + ANIMATION_CLONE_MARKER_CLASS, obj).remove();
1639                slidesContainer.stop();
1640            }
1641
1642            function goToSlide(slide, clicked) {
1643                clickable = !clicked && !option[12]/*auto*/;
1644                previousSlide = currentSlide;
1645                currentSlide = slide;
1646
1647                adjust(clicked);
1648
1649                if (option[4]/*controlsfade*/ && init) {
1650                    fadeControls(currentSlide, 0);
1651                }
1652                // This is handles in AjaxAdjust, if something is loading.
1653                if (init && !option[30]/*ajax*/[currentSlide] && !startedAjaxLoads[currentSlide]) {
1654                    init = FALSE;
1655                    callAsync(performInitCallback);
1656                }
1657            }
1658
1659            function getSlides(from, count) {
1660                var visibleSlides = $();
1661                for (var i = 0; i < count; i++) {
1662                    visibleSlides = visibleSlides.add(slides[getRealPos(from + i)]);
1663                }
1664                return visibleSlides;
1665            }
1666
1667            function getRealPos(a) {
1668                return mod(a, totalSlides);
1669            }
1670
1671            function fixClearType(element) {
1672                if (screen.fontSmoothingEnabled && element.style) {
1673                    try {
1674                        element.style.removeAttribute("filter"); // Fix cleartype
1675                    } catch (ignored) { }
1676                }
1677            }
1678
1679            /*
1680             * Public methods.
1681             */
1682
1683            // First i just define those i use more than one. Then i just add the others as anonymous functions.
1684            function publicDestroy() {
1685                stopAuto();
1686                stopAnimation();
1687                autoadjust(currentSlide, 0);
1688                destroyed = TRUE;
1689
1690                slideNumberBeforeDestroy = currentSlide;
1691
1692                performCallbacks(unBindCallbacks);
1693
1694                ensureSliderContainerCSSDurationReset();
1695                if (controls) {
1696                    controls.remove();
1697                }
1698
1699
1700                reorderSlides(0);
1701                adjustPositionTo(currentSlide);
1702
1703                option[43]/*destroyCallback*/.call(baseSlider);
1704            }
1705
1706            baseSlider.destroy = publicDestroy;
1707
1708            function publicInit() {
1709                if (destroyed) {
1710                    initSudoSlider();
1711                }
1712            }
1713
1714            function runOnDestroyedSlider(func) {
1715                return function foo() {
1716                    var reinit = !destroyed;
1717                    if (!init && !fullyInitialized) {
1718                        var args = arguments;
1719                        callAsync(function () {
1720                            foo.apply(undefined, args);
1721                        }); // Fixing a very special, special case
1722                        return;
1723                    }
1724                    publicDestroy();
1725
1726                    func.apply(this, arguments);
1727
1728                    if (reinit) {
1729                        publicInit();
1730                    }
1731                }
1732            }
1733
1734            baseSlider.init = publicInit;
1735
1736            baseSlider.getOption = function (a) {
1737                return options[a.toLowerCase()];
1738            };
1739
1740            baseSlider.setOption = runOnDestroyedSlider(function (key, val) {
1741                options[key.toLowerCase()] = val;
1742            });
1743
1744            baseSlider.setOptions = runOnDestroyedSlider(function (newOptions) {
1745                for (var key in newOptions) {
1746                    options[key.toLowerCase()] = newOptions[key];
1747                }
1748            });
1749
1750            baseSlider.runWhenNotAnimating = runWhenNotAnimating;
1751
1752            baseSlider.insertSlide = runOnDestroyedSlider(function (html, pos, numtext, goToSlide) {
1753                // pos = 0 means before everything else.
1754                // pos = 1 means after the first slide.
1755                // if pos is negative, then we count from the right instead.
1756                if (pos < 0) {
1757                    pos = totalSlides - mod(-pos - 1, totalSlides + 1);
1758                } else {
1759                    pos = mod(pos, totalSlides + 1);
1760                }
1761
1762                html = $(html || DIV_TAG);
1763                if (isSlideContainerUl) {
1764                    html = $("<li>").prepend(html);
1765                } else {
1766                    if (html.length != 1) {
1767                        html = $(DIV_TAG).prepend(html);
1768                    } else {
1769                        // Inserting as is.
1770                    }
1771                }
1772
1773                if (!pos || pos == 0) {
1774                    slidesContainer.prepend(html);
1775                } else {
1776                    slides[pos - 1].after(html);
1777                }
1778
1779                // Finally, we make it work again.
1780                if (goToSlide) {
1781                    slideNumberBeforeDestroy = goToSlide - 1;
1782                } else if (pos <= slideNumberBeforeDestroy || (!pos || pos == 0)) {
1783                    slideNumberBeforeDestroy++;
1784                }
1785
1786                if (option[18]/*numerictext*/.length < pos) {
1787                    option[18]/*numerictext*/.length = pos;
1788                }
1789
1790                option[18]/*numerictext*/.splice(pos, 0, numtext || parseInt10(pos) + 1);
1791            });
1792
1793            baseSlider.removeSlide = runOnDestroyedSlider(function (pos) {
1794                pos = mathMax(0, parseInt10(pos) - 1); // 1 == the first.
1795
1796                slides[mathMin(pos, totalSlides - 1)].remove();
1797                option[18]/*numerictext*/.splice(pos, 1);
1798                if (pos < slideNumberBeforeDestroy) {
1799                    slideNumberBeforeDestroy--;
1800                }
1801            });
1802
1803            baseSlider.goToSlide = function (a, speed) {
1804                var parsedDirection = (a == parseInt10(a)) ? a - 1 : a;
1805                callAsync(makeCallback(enqueueAnimation, [parsedDirection, TRUE, speed]));
1806            };
1807
1808            baseSlider.block = function () {
1809                clickable = FALSE;
1810            };
1811
1812            baseSlider.unblock = function () {
1813                clickable = TRUE;
1814            };
1815
1816            baseSlider.startAuto = function () {
1817                option[12]/*auto*/ = TRUE;
1818                startAuto();
1819            };
1820
1821            baseSlider.stopAuto = function () {
1822                option[12]/*auto*/ = FALSE;
1823                stopAuto();
1824            };
1825
1826            baseSlider.adjust = function adjustMethod(repeat) {
1827                var autoAdjustSpeed = mathMax(adjustTargetTime - getTimeInMillis(), 0);
1828                autoadjust(currentSlide, autoAdjustSpeed);
1829                if (!currentlyAnimating) {
1830                    adjustPositionTo(currentSlide);
1831                }
1832                if (!repeat) {
1833                    callAsync(makeCallback(adjustMethod, [true]));
1834                }
1835            };
1836
1837            baseSlider.getValue = function (a) {
1838                return {
1839                    currentslide: currentSlide + 1,
1840                    totalslides: totalSlides,
1841                    clickable: clickable,
1842                    destroyed: destroyed,
1843                    autoanimation: autoOn
1844                }[a.toLowerCase()];
1845            };
1846
1847            baseSlider.getSlide = function (number) {
1848                return slides[getRealPos(parseInt10(number) - 1)];
1849            };
1850
1851            baseSlider.stopAnimation = stopAnimation;
1852
1853            // Done, now initialize.
1854            initSudoSlider();
1855        });
1856    };
1857
1858    /*
1859     * End generic slider.
1860     * Factory method for default options (no accidental writes!).
1861    */
1862    function getDefaultOptions() {
1863        return {
1864            effect: "slide", /*option[0]/*effect*/
1865            speed: 1500, /*  option[1]/*speed*/
1866            customLink: FALSE, /* option[2]/*customlink*/
1867            controlsFadeSpeed: 400, /*   option[3]/*controlsfadespeed*/
1868            controlsFade: TRUE, /*  option[4]/*controlsfade*/
1869            insertAfter: TRUE, /*  option[5]/*insertafter*/
1870            vertical: FALSE, /* option[6]/*vertical*/
1871            slideCount: 1, /*     option[7]/*slidecount*/
1872            moveCount: 1, /*     option[8]/*movecount*/
1873            startSlide: 1, /*     option[9]/*startslide*/
1874            responsive: TRUE, /* option[10]/*responsive*/
1875            ease: SWING, /* option[11]/*ease*/
1876            auto: FALSE, /* option[12]/*auto*/
1877            pause: 2000, /*  option[13]/*pause*/
1878            resumePause: FALSE, /* option[14]/*resumepause*/
1879            continuous: FALSE, /* option[15]/*continuous*/
1880            prevNext: TRUE, /*  option[16]/*prevnext*/
1881            numeric: FALSE, /* option[17]/*numeric*/
1882            numericText: [], /*    option[18]/*numerictext*/
1883            slices: 15, /*   option[19]/*slices*/
1884            boxCols: 8, /*    option[20]/*boxCols*/
1885            boxRows: 4, /*    option[21]/*boxRows*/
1886            initCallback: EMPTY_FUNCTION, /* option[22]/*initCallback*/
1887            ajaxLoad: EMPTY_FUNCTION, /* option[23]/*ajaxload*/
1888            beforeAnimation: EMPTY_FUNCTION, /* option[24]/*beforeanimation*/
1889            afterAnimation: EMPTY_FUNCTION, /* option[25]/*afteranimation*/
1890            history: FALSE, /* option[26]/*history*/
1891            autoHeight: TRUE, /*  option[27]/*autoheight*/
1892            autoWidth: TRUE, /*  option[28]/*autowidth*/
1893            updateBefore: FALSE, /* option[29]/*updateBefore*/
1894            ajax: FALSE, /* option[30]/*ajax*/
1895            preloadAjax: 100, /*   option[31]/*preloadajax*/
1896            loadingText: "", /*    option[32]/*loadingtext*/
1897            prevHtml: '<a href="#" class="prevBtn"> previous </a>', /* option[33]/*prevhtml*/
1898            nextHtml: '<a href="#" class="nextBtn"> next </a>', /* option[34]/*nexthtml*/
1899            controlsAttr: 'class="controls"', /* option[35]/*controlsattr*/
1900            numericAttr: 'class="numericControls"', /* option[36]/*numericattr*/
1901            interruptible: FALSE, /* option[37]/*interruptible*/
1902            useCSS: TRUE, /* option[38]/*useCSS*/
1903            loadStart: EMPTY_FUNCTION, /* option[39]/*loadStart*/
1904            loadFinish: EMPTY_FUNCTION,  /* option[40]/*loadFinish*/
1905            touch: FALSE,  /* option[41]/*touch*/
1906            touchHandle: FALSE, /* option[42]/*touchHandle*/
1907            destroyCallback: EMPTY_FUNCTION,  /* option[43]/*destroyCallback*/
1908            mouseTouch: TRUE, /* option[44]/*mouseTouch*/
1909            allowScroll: TRUE, /* option[45]/*allowScroll*/
1910            CSSease: SWING, /* option[46]/*CSSease*/
1911            ajaxHasHTML: FALSE /* option[47]/*AjaxHasHTML*/
1912
1913        };
1914    }
1915    jQuery.fn.sudoSlider.getDefaultOptions = getDefaultOptions;
1916
1917    /* Start animations.
1918     * A lot of the code here is an if-else-elseif nightmare. This is because it is smaller in JavaScript, and this thing needs to be small (when minimized).
1919     */
1920
1921    var GROW_IN = "GrowIn";
1922    var GROW_OUT = "GrowOut";
1923    var ROUNDED = "Rounded";
1924    var FLY_IN = "FlyIn";
1925    var FLY_OUT = "FlyOut";
1926
1927    // Start by defining everything, the implementations are below
1928    var normalEffectsPrefixObject = {
1929        box: {
1930            Random: [
1931                "",
1932                GROW_IN,
1933                GROW_IN + ROUNDED,
1934                GROW_OUT,
1935                GROW_OUT + ROUNDED,
1936                FLY_IN,
1937                FLY_OUT,
1938                boxRandomTemplate
1939            ],
1940            Rain: [
1941                "",
1942                GROW_IN,
1943                GROW_IN + ROUNDED,
1944                GROW_OUT,
1945                GROW_OUT + ROUNDED,
1946                FLY_IN,
1947                FLY_OUT,
1948                [
1949                    "UpLeft",
1950                    "DownLeft",
1951                    "DownRight",
1952                    "UpRight",
1953                    boxRainTemplate
1954                ]
1955            ],
1956            Spiral: [
1957                "InWards",
1958                "OutWards",
1959                {
1960                    "": boxSpiralTemplate,
1961                    Grow: [
1962                        "In",
1963                        "Out",
1964                        [
1965                            "",
1966                            "Rounded",
1967                            boxSpiralGrowTemplate
1968                        ]
1969                    ]
1970                }
1971            ]
1972        },
1973        fade: {
1974            "": fade,
1975            OutIn: fadeOutIn
1976        },
1977        foldRandom: [
1978            "Horizontal",
1979            "Vertical",
1980            foldRandom
1981        ],
1982        slide: slide,
1983        stack: [
1984            "Up",
1985            "Right",
1986            "Down",
1987            "Left",
1988            [
1989                "",
1990                "Reverse",
1991                stackTemplate
1992            ]
1993        ]
1994        // TODO: Name!
1995        /*unnamed: [
1996            "",
1997            "Reveal",
1998            [
1999                "",
2000                "Vertical",
2001                [
2002                    "",
2003                    "Reverse",
2004                    "Random",
2005                    testAnimation
2006                ]
2007            ]
2008        ]*/
2009    };
2010
2011    // Generic effects needs to have a "dir" attribute as their last argument.
2012    var genericEffectsPrefixObject = {
2013        blinds: [
2014            "1",
2015            "2",
2016            blinds
2017        ],
2018        fold: fold,
2019        push: [
2020            "Out",
2021            "In",
2022            pushTemplate
2023        ],
2024        reveal: revealTemplate,
2025        slice: {
2026            "": [
2027                "",
2028                "Reveal",
2029                [
2030                    "",
2031                    "Reverse",
2032                    "Random",
2033                    slice
2034                ]
2035            ],
2036            Fade: slicesFade
2037        },
2038        zip: zip,
2039        unzip: unzip
2040    };
2041
2042
2043    function parsePrefixedEffects(resultObject, effectsObject, prefix, generic, argumentsStack) {
2044        if (isFunction(effectsObject)) {
2045            if (generic) {
2046                // Parsing the value 0, as a hack to make generic effects work, see the below else case.
2047                parsePrefixedEffects(resultObject, ["", "Up", "Right", "Down", "Left", effectsObject], prefix, 0, argumentsStack);
2048            } else {
2049                resultObject[prefix] = function (obj) {
2050                    var argumentArray = [obj].concat(argumentsStack);
2051
2052                    // Ugly hack, to make "generic" functions to work.
2053                    var genericArgumentIndex = argumentArray.length - 1;
2054                    if (generic === 0 && argumentArray[genericArgumentIndex] == 0) {
2055                        argumentArray[genericArgumentIndex] = getDirFromAnimationObject(obj);
2056                    }
2057
2058                    effectsObject.apply(this, argumentArray);
2059                }
2060            }
2061        } else if (isArray(effectsObject)) {
2062            var effectIndex = effectsObject.length - 1;
2063            var effect = effectsObject[effectIndex];
2064            for (var i = 0; i < effectIndex; i++) {
2065                var newArgumentStack = cloneArray(argumentsStack);
2066                newArgumentStack.push(i);
2067                var name = effectsObject[i];
2068                parsePrefixedEffects(resultObject, effect, prefix + name, generic, newArgumentStack);
2069            }
2070        } else {
2071            jQuery.each(effectsObject, function (name, effect) {
2072                parsePrefixedEffects(resultObject, effect, prefix + name, generic, argumentsStack);
2073            });
2074        }
2075    }
2076
2077
2078    var allEffects = {};
2079    parsePrefixedEffects(allEffects, genericEffectsPrefixObject, "", TRUE, []);
2080    parsePrefixedEffects(allEffects, normalEffectsPrefixObject, "", FALSE, []);
2081
2082    allEffects.random = makeRandomEffect(allEffects);
2083
2084    // Saving it.
2085    jQuery.fn.sudoSlider.effects = allEffects;
2086
2087
2088    // The implementations
2089    // dir: 0: UpRight, 1: DownRight: 2: DownLeft, 3: UpLeft
2090    // effect: 0: none, 1: growIn, 2: growRoundedIn, 3: growOut, 4: growRoundedOut, 5: flyIn, 6: flyOut
2091    function boxRainTemplate(obj, effect, dir) {
2092        var reverseRows = dir == 1 || dir == 3;
2093        var reverse = dir == 0 || dir == 3;
2094        var grow = effect >= 1 && effect <= 4;
2095        var flyIn = effect == 5 || effect == 6;
2096        var reveal = effect == 6 || effect == 3 || effect == 4;
2097        var roundedGrow = effect == 2 || effect == 4;
2098        boxTemplate(obj, reverse, reverseRows, grow, FALSE, 1, flyIn, reveal, roundedGrow);
2099    }
2100
2101    function boxSpiralTemplate(obj, direction) {
2102        boxTemplate(obj, direction, FALSE, FALSE, FALSE, 2, FALSE, FALSE, FALSE);
2103    }
2104
2105    function boxSpiralGrowTemplate(obj, direction, reveal, rounded) {
2106        boxTemplate(obj, direction, FALSE, TRUE, FALSE, 2, FALSE, reveal, rounded);
2107    }
2108
2109    // effect: 0: no grow, 1: growIn: 2: growInRounded, 3: growOut, 4: growOutRounded, 5: flyIn, 6: flyOut
2110    function boxRandomTemplate(obj, effect) {
2111        var reveal = effect == 3 || effect == 4 || effect == 6;
2112        var roundedGrow = effect == 2 || effect == 4;
2113        var flyIn = effect == 5 || effect == 6;
2114        boxTemplate(obj, FALSE, FALSE, effect > 0 && effect < 5, TRUE, 0, flyIn, reveal, roundedGrow);
2115    }
2116
2117    // SelectionAlgorithm: 0: Standard selection, 1: rain, 2: spiral
2118    function boxTemplate(obj, reverse, reverseRows, grow, randomize, selectionAlgorithm, flyIn, reveal, roundedGrow) {
2119        var options = obj.options;
2120        var boxRows = options.boxrows;
2121        var boxCols = options.boxcols;
2122        var totalBoxes = boxRows * boxCols;
2123        var speed = options.speed / (totalBoxes == 1 ? 1 : 2.5); // To make the actual time spent equal to options.speed.
2124        var boxes = createLazyBoxes(obj, boxCols, boxRows, !reveal);
2125        var timeBuff = 0;
2126        var rowIndex = 0;
2127        var colIndex = 0;
2128        var box2DArr = [];
2129        box2DArr[rowIndex] = [];
2130        if (reverse) {
2131            reverseArray(boxes);
2132        }
2133        if (randomize) {
2134            shuffle(boxes);
2135        }
2136
2137        for (var i = 0; i < boxes.length; i++) {
2138            var element = boxes[i];
2139            box2DArr[rowIndex][colIndex] = element;
2140            colIndex++;
2141            if (colIndex == boxCols) {
2142                if (reverseRows) {
2143                    reverseArray(box2DArr[rowIndex]);
2144                }
2145                rowIndex++;
2146                colIndex = 0;
2147                box2DArr[rowIndex] = [];
2148            }
2149        }
2150
2151        var boxesResult = [];
2152        if (selectionAlgorithm == 1) {
2153            // Rain
2154            for (var cols = 0; cols < (boxCols * 2) + 1; cols++) {
2155                var prevCol = cols;
2156                var boxesResultLine = [];
2157                for (var rows = 0; rows < boxRows; rows++) {
2158                    if (prevCol >= 0 && prevCol < boxCols) {
2159                        var rawBox = box2DArr[rows][prevCol];
2160                        if (!rawBox) {
2161                            return;
2162                        }
2163                        boxesResultLine.push(rawBox);
2164                    }
2165                    prevCol--;
2166                }
2167                if (boxesResultLine.length != 0) {
2168                    boxesResult.push(boxesResultLine);
2169                }
2170            }
2171        } else if (selectionAlgorithm == 2) {
2172            // Spiral
2173            // Algorithm borrowed from the Camera plugin by Pixedelic.com
2174            var rows2 = boxRows / 2, x, y, z, n = reverse ? totalBoxes : -1;
2175            var negative = reverse ? -1 : 1;
2176            for (z = 0; z < rows2; z++) {
2177                y = z;
2178                for (x = z; x < boxCols - z - 1; x++) {
2179                    boxesResult[n += negative] = boxes[y * boxCols + x];
2180                }
2181                x = boxCols - z - 1;
2182                for (y = z; y < boxRows - z - 1; y++) {
2183                    boxesResult[n += negative] = boxes[y * boxCols + x];
2184                }
2185                y = boxRows - z - 1;
2186                for (x = boxCols - z - 1; x > z; x--) {
2187                    boxesResult[n += negative] = boxes[y * boxCols + x];
2188                }
2189                x = z;
2190                for (y = boxRows - z - 1; y > z; y--) {
2191                    boxesResult[n += negative] = boxes[y * boxCols + x];
2192                }
2193            }
2194        } else {
2195            for (var row = 0; row < boxRows; row++) {
2196                for (var col = 0; col < boxCols; col++) {
2197                    boxesResult.push([box2DArr[row][col]]);
2198                }
2199            }
2200        }
2201
2202        if (reveal) {
2203            obj.goToNext();
2204        }
2205
2206        var count = 0;
2207        for (var i = 0; i < boxesResult.length; i++) {
2208            var boxLine = boxesResult[i];
2209
2210            if (!isArray(boxLine)) {
2211                boxLine = [boxLine];
2212            }
2213
2214            for (var j = 0; j < boxLine.length; j++) {
2215                var lazyBox = boxLine[j];
2216                (function (lazyBox, delay) {
2217                    function boxAnimationFunction(delay) {
2218                        var box = lazyBox();
2219                        var boxChildren = box.children();
2220                        var orgWidth = box.width();
2221                        var orgHeight = box.height();
2222                        var goToWidth = orgWidth;
2223                        var goToHeight = orgHeight;
2224                        var orgLeft = parseNumber(box.css("left"));
2225                        var orgTop = parseNumber(box.css("top"));
2226                        var goToLeft = orgLeft;
2227                        var goToTop = orgTop;
2228
2229                        var childOrgLeft = parseNumber(boxChildren.css("left"));
2230                        var childOrgTop = parseNumber(boxChildren.css("top"));
2231                        var childGoToLeft = childOrgLeft;
2232                        var childGoToTop = childOrgTop;
2233
2234                        if (flyIn) {
2235                            var adjustTop;
2236                            var adjustLeft;
2237
2238                            if (randomize) {
2239                                adjustLeft = pickRandomValue([-goToWidth, goToWidth]);
2240                                adjustTop = pickRandomValue([-goToHeight, goToHeight]);
2241                            } else {
2242                                adjustLeft = reverse != reverseRows ? -goToWidth : goToWidth;
2243                                adjustTop = reverse ? -goToHeight : goToHeight;
2244                            }
2245
2246
2247                            var flyDistanceFactor = 1.5;
2248
2249                            if (reveal) {
2250                                goToLeft -= adjustLeft * flyDistanceFactor;
2251                                goToTop -= adjustTop * flyDistanceFactor;
2252                            } else {
2253                                box.css({
2254                                    left: orgLeft + adjustLeft * flyDistanceFactor,
2255                                    top: orgTop + adjustTop * flyDistanceFactor
2256                                });
2257                            }
2258                        }
2259
2260                        if (grow) {
2261                            if (reveal) {
2262                                childGoToLeft -= goToWidth / 2;
2263                                goToLeft += goToWidth / 2;
2264                                childGoToTop -= goToHeight / 2;
2265                                goToTop += goToHeight / 2;
2266
2267                                goToHeight = goToWidth = 0;
2268                            } else {
2269                                box.css({left: orgLeft + (goToWidth / 2), top: orgTop + (goToHeight / 2)});
2270                                boxChildren.css({
2271                                    left: childOrgLeft - goToWidth / 2,
2272                                    top: childOrgTop - goToHeight / 2
2273                                });
2274
2275                                box.width(0).height(0);
2276                                if (roundedGrow) {
2277                                    box.css({borderRadius: mathMax(orgHeight, orgWidth)});
2278                                }
2279                            }
2280                        }
2281
2282
2283                        if (reveal) {
2284                            box.css({opacity: 1});
2285                        }
2286                        count++;
2287                        schedule(function () {
2288                            doc.ready(function () {
2289                                animate(boxChildren,
2289 {left: childGoToLeft, top: childGoToTop}, speed, FALSE, FALSE, obj);
2290                                animate(box, {
2291                                    opacity: reveal ? 0 : 1,
2292                                    width: goToWidth,
2293                                    height: goToHeight,
2294                                    left: goToLeft,
2295                                    top: goToTop,
2296                                    borderRadius: grow && reveal && roundedGrow ? mathMax(orgHeight, orgWidth) : 0
2297                                }, speed, FALSE, function () {
2298                                    count--;
2299                                    if (count == 0) {
2300                                        obj.callback();
2301                                    }
2302                                }, obj);
2303                            })
2304                        }, delay);
2305                    }
2306
2307                    var minWaitTime = 150;
2308                    if (reveal || delay < minWaitTime) {
2309                        boxAnimationFunction(delay);
2310                    } else {
2311                        schedule(makeCallback(boxAnimationFunction, [minWaitTime]), delay - minWaitTime);
2312                    }
2313                })(lazyBox, timeBuff);
2314            }
2315            timeBuff += (speed / boxesResult.length) * 1.5;
2316        }
2317    }
2318
2319    function slicesFade(obj, dir) {
2320        var vertical = dir == 2 || dir == 4;
2321        var negative = dir == 1 || dir == 4;
2322        foldTemplate(obj, vertical, negative, FALSE, TRUE);
2323    }
2324
2325    function fold(obj, dir) {
2326        var vertical = dir == 2 || dir == 4;
2327        var negative = dir == 1 || dir == 4;
2328        foldTemplate(obj, vertical, negative);
2329    }
2330
2331    function foldRandom(obj, vertical) {
2332        foldTemplate(obj, vertical, FALSE, TRUE);
2333    }
2334
2335    function blinds(obj, blindsEffect, dir) {
2336        blindsEffect++;
2337        var vertical = dir == 2 || dir == 4;
2338        var negative = dir == 1 || dir == 4;
2339        foldTemplate(obj, vertical, negative, FALSE, FALSE, blindsEffect);
2340    }
2341
2342    function slice(obj, reveal, reverse, dir) {
2343        var random = reverse == 2;
2344        var vertical = dir == 1 || dir == 3;
2345        var negative = dir == 1 || dir == 4;
2346        foldTemplate(obj, vertical, reverse, random, FALSE, 0, negative ? 1 : 2, reveal);
2347    }
2348
2349    function testAnimation(obj, reveal, vertical, reverseArg) {
2350        var reverse = reverseArg == 1;
2351        var random = reverseArg == 2;
2352
2353        foldTemplate(obj, vertical, reverse, random, FALSE, 0, 1, reveal);
2354        foldTemplate(obj, vertical, reverse, random, FALSE, 0, 2, reveal);
2355    }
2356
2357    function zip(obj, dir) {
2358        var vertical = dir == 2 || dir == 4;
2359        var negative = dir == 1 || dir == 4;
2360        foldTemplate(obj, vertical, negative, FALSE, FALSE, 0, 3);
2361    }
2362
2363    function unzip(obj, dir) {
2364        var vertical = dir == 2 || dir == 4;
2365        var negative = dir == 1 || dir == 4;
2366        foldTemplate(obj, vertical, negative, FALSE, FALSE, 0, 3, TRUE);
2367    }
2368
2369    function foldTemplate(obj, vertical, reverse, randomize, onlyFade, curtainEffect, upDownEffect, reveal) {
2370        var options = obj.options;
2371        var slides = options.slices;
2372        var speed = options.speed / 2; // To make the actual time spent be equal to options.speed
2373        var objSlider = obj.slider;
2374        var lazySlides = createLazyBoxes(obj, vertical ? slides : 1, vertical ? 1 : slides, !reveal);
2375        var slicesElement = $();
2376        for (var i = 0; i < lazySlides.length; i++) {
2377            slicesElement = slicesElement.add(lazySlides[i]());
2378        }
2379        var count = 0;
2380        var upDownAlternator = FALSE;
2381        if (reverse) {
2382            reverseArray(slicesElement);
2383        } else {
2384            $(reverseArray(slicesElement.get())).appendTo(objSlider);
2385        }
2386        if (randomize) {
2387            shuffle(slicesElement);
2388        }
2389        slicesElement.each(function (i) {
2390            var timeout = ((speed / slides) * i);
2391            var slice = $(this);
2392            var orgWidth = slice.width();
2393            var orgHeight = slice.height();
2394            var goToLeft = slice.css("left");
2395            var goToTop = slice.css("top");
2396            var startPosition = vertical ? goToLeft : goToTop;
2397
2398            var innerBox = slice.children();
2399            var startAdjustment = innerBox[vertical ? "width" : "height"]();
2400            if (curtainEffect == 1) {
2401                startPosition = 0
2402            } else if (curtainEffect == 2) {
2403                startPosition = startAdjustment / 2;
2404            }
2405            if (reverse) {
2406                startPosition = startAdjustment - startPosition;
2407            }
2408            if (vertical) {
2409                slice.css({
2410                    width: (onlyFade || upDownEffect ? orgWidth : 0),
2411                    left: startPosition
2412                });
2413            } else {
2414                slice.css({
2415                    height: (onlyFade || upDownEffect ? orgHeight : 0),
2416                    top: startPosition
2417                });
2418            }
2419
2420            if (reveal) {
2421                var negative = upDownEffect == 1 ? -1 : 1;
2422                slice.css({
2423                    top: goToTop,
2424                    left: goToLeft,
2425                    width: orgWidth,
2426                    height: orgHeight,
2427                    opacity: 1
2428                });
2429                if (vertical) {
2430                    goToTop = negative * orgHeight;
2431                } else {
2432                    goToLeft = negative * orgWidth;
2433                }
2434            }
2435
2436            if (upDownEffect) {
2437                var bottom = TRUE;
2438                if (upDownEffect == 3) {
2439                    if (upDownAlternator) {
2440                        bottom = FALSE;
2441                        upDownAlternator = FALSE;
2442                    } else {
2443                        upDownAlternator = TRUE;
2444                    }
2445                } else if (upDownEffect == 2) {
2446                    bottom = FALSE;
2447                }
2448                if (vertical) {
2449                    if (reveal) {
2450                        goToTop = (bottom ? -1 : 1) * orgHeight;
2451                    } else {
2452                        slice.css({
2453                            bottom: bottom ? 0 : orgHeight,
2454                            top: bottom ? orgHeight : 0,
2455                            height: reveal ? orgHeight : 0
2456                        });
2457                    }
2458                } else {
2459                    if (reveal) {
2460                        goToLeft = (bottom ? -1 : 1) * orgWidth;
2461                    } else {
2462                        slice.css({
2463                            right: bottom ? 0 : orgWidth,
2464                            left: bottom ? orgWidth : 0,
2465                            width: reveal ? orgWidth : 0
2466                        });
2467                    }
2468                }
2469            }
2470
2471
2472            count++;
2473            schedule(makeCallback(animate, [
2474                    slice, {
2475                        width: orgWidth,
2476                        height: orgHeight,
2477                        opacity: reveal ? 0 : 1,
2478                        left: goToLeft,
2479                        top: goToTop
2480                    }, speed, FALSE, function () {
2481                        count--;
2482                        if (count == 0) {
2483                            obj.callback();
2484                        }
2485                    }, obj])
2486                , timeout);
2487        });
2488        if (reveal) {
2489            obj.goToNext();
2490        }
2491    }
2492
2493    function stackTemplate(obj, dir, reverse) {
2494        var pushIn = obj.diff > 0;
2495        if (reverse) {
2496            pushIn = !pushIn;
2497        }
2498
2499        pushTemplate(obj, pushIn, ++dir);
2500    }
2501
2502    // 1: up, 2: right, 3: down, 4, left:
2503    function pushTemplate(obj, pushIn, dir) {
2504        var vertical = dir == 2 || dir == 4;
2505        var negative = (dir == 2 || dir == 3) ? 1 : -1;
2506        var options = obj.options;
2507        var speed = options.speed;
2508        var callback = obj.callback;
2509        if (pushIn) {
2510            var fromSlides = obj.fromSlides;
2511            var toSlides = makeClone(obj, TRUE).hide();
2512            obj.slider.append(toSlides);
2513            var height = mathMax(toSlides.height(), fromSlides.height());
2514            var width = mathMax(toSlides.width(), fromSlides.width());
2515            toSlides.css(vertical ? {left: negative * width} : {top: negative * height}).show();
2516            animate(toSlides, {left: 0, top: 0}, speed, FALSE, callback, obj);
2517        } else {
2518            var fromSlides = makeClone(obj, FALSE);
2519            obj.slider.append(fromSlides);
2520            obj.goToNext();
2521            var toSlides = obj.toSlides;
2522
2523            var measurementSlides = negative == -1 ? fromSlides : toSlides;
2524            var height = measurementSlides.height();
2525            var width = measurementSlides.width();
2526
2527            animate(fromSlides, vertical ? {left: negative * width} : {top: negative * height}, speed, FALSE, callback, obj);
2528        }
2529    }
2530
2531    function revealTemplate(obj, dir) {
2532        var vertical = dir == 1 || dir == 3;
2533        var options = obj.options;
2534        var speed = options.speed;
2535        var innerBox = makeClone(obj, TRUE);
2536        var width = innerBox.width();
2537        var height = innerBox.height();
2538        var box = createBox(innerBox, 0, 0, 0, 0)
2539            .css({opacity: 1})
2540            .appendTo(obj.slider);
2541        var both = box.add(innerBox);
2542        both.hide(); // FF css animation fix
2543        if (vertical) {
2544            box.css({width: width});
2545            if (dir == 1) {
2546                innerBox.css({top: -height});
2547                box.css({bottom: 0, top: "auto"});
2548            }
2549        } else {
2550            box.css({height: height});
2551            if (dir == 4) {
2552                innerBox.css({left: -width});
2553                box.css({right: 0, left: "auto"});
2554            }
2555        }
2556        // <FF css animation fix>
2557        both.show();
2558        if (vertical) {
2559            both.width(width);
2560        } else {
2561            both.height(height);
2562        }
2563        // </FF css animation fix>
2564        animate(innerBox, {left: 0, top: 0}, speed, FALSE, EMPTY_FUNCTION, obj);
2565        animate(box, {width: width, height: height}, speed, FALSE, obj.callback, obj);
2566    }
2567
2568    function slide(obj) {
2569        var ul = childrenNotAnimationClones(obj.slider);
2570        var options = obj.options;
2571        var speed = options.speed;
2572        var target = obj.target;
2573
2574        var left = target.left;
2575        var top = target.top;
2576
2577        if (obj.options.usecss) {
2578            animate(ul, {transform: "translate(" + left + "px, " + top + "px)"}, speed, FALSE, obj.callback, obj, TRUE);
2579        } else {
2580            animate(ul, {marginTop: top, marginLeft: left}, speed, FALSE, obj.callback, obj);
2581        }
2582    }
2583
2584    function animate(elem, properties, speed, ease, callback, obj, doNotResetCss) {
2585        var usecss = !obj || obj.options.usecss;
2586        if (CSSVendorPrefix === FALSE || !usecss) {
2587            if (!ease) {
2588                ease = obj.options.ease;
2589            }
2590            elem.animate(properties, speed, ease, callback);
2591            return;
2592        }
2593        if (!ease) {
2594            ease = obj.options.cssease;
2595        }
2596
2597        var CSSObject = {};
2598        var transitionProperty = CSSVendorPrefix + "transition";
2599        var keys = getKeys(properties);
2600        // Adding vendor prefix, because sometimes it's needed.
2601        CSSObject[transitionProperty] = keys.join(" ") + (CSSVendorPrefix == "" ? "" : " " + CSSVendorPrefix + keys.join(" " + CSSVendorPrefix));
2602
2603        var transitionTiming = transitionProperty + "-duration";
2604        CSSObject[transitionTiming] = speed + "ms";
2605
2606        var transitionEase = transitionProperty + "-timing-function";
2607        if (ease == SWING) {
2608            ease = "ease-in-out";
2609        }
2610        CSSObject[transitionEase] = ease;
2611
2612        function resetCSS() {
2613            if (!doNotResetCss) {
2614                var cssObject = {};
2615                cssObject[transitionTiming] = "0s";
2616                cssObject[transitionEase] = "";
2617                cssObject[transitionProperty] = "";
2618                elem.css(cssObject);
2619            }
2620        }
2621
2622        if (obj) {
2623            obj.stopCallbacks.push(resetCSS);
2624        }
2625
2626        var eventsVendorPrefix = CSSVendorPrefix.replace(/\-/g, ""); // replaces all "-" with "";
2627        var eventsSuffix = (eventsVendorPrefix ? "T" : "t") + "ransitionend";
2628        var events = eventsVendorPrefix + eventsSuffix + " t" + "ransitionend";
2629
2630        var called = FALSE;
2631        var callbackFunction = function () {
2632            if (!called) {
2633                called = TRUE;
2634                elem.unbind(events);
2635                resetCSS();
2636                if (callback) {
2637                    callback();
2638                }
2639            }
2640        };
2641
2642        if (speed < 20) { // If instant animation
2643            elem.css(properties);
2644            callbackFunction();
2645            return;
2646        }
2647
2648        callAsync(function () {
2649            elem.css(CSSObject);
2650            callAsync(function () {
2651                elem.css(properties);
2652                var startTime = getTimeInMillis();
2653
2654                elem.on(events, function eventHandler(event) {
2655                    if (elem.is(event.target) && (getTimeInMillis() - startTime) - speed > -100) {
2656                        elem.off(events, eventHandler);
2657                        callbackFunction();
2658                    }
2659                });
2660                // If the animation doesn't do anything, the bind will never be triggered, so this is a fallback.
2661                schedule(callbackFunction, speed + 100);
2662            });
2663        });
2664        return callbackFunction
2665    }
2666
2667    function fadeOutIn(obj) {
2668        var options = obj.options;
2669        var fadeSpeed = options.speed;
2670
2671        var fadeinspeed = parseInt10(fadeSpeed * (3 / 5));
2672        var fadeoutspeed = fadeSpeed - fadeinspeed;
2673
2674        obj.stopCallbacks.push(function () {
2675            obj.fromSlides.stop().css({opacity: 1});
2676        });
2677
2678        animate(obj.fromSlides, {opacity: 0.0001}, fadeoutspeed, FALSE, makeCallback(finishFadeAnimation, [obj, fadeSpeed]), obj);
2679    }
2680
2681
2682    function fade(obj) {
2683        finishFadeAnimation(obj, obj.options.speed);
2684    }
2685
2686    function finishFadeAnimation(obj, speed) {
2687        var options = obj.options;
2688        options.boxcols = 1;
2689        options.boxrows = 1;
2690        options.speed = speed;
2691        boxTemplate(obj, FALSE);
2692    }
2693
2694    // 1: up, 2: right, 3: down, 4, left:
2695    function getDirFromAnimationObject(obj) {
2696        var vertical = obj.options.vertical;
2697        var diff = obj.diff;
2698        var dir;
2699        if (vertical) {
2700            if (diff < 0) {
2701                dir = 1;
2702            } else {
2703                dir = 3;
2704            }
2705        } else {
2706            if (diff < 0) {
2707                dir = 2;
2708            } else {
2709                dir = 4;
2710            }
2711        }
2712        return dir;
2713    }
2714
2715
2716    function createLazyBoxes(obj, numberOfCols, numberOfRows, useToSlides) {
2717        var slider = obj.slider;
2718        var vertical = obj.options.vertical;
2719        var result = [];
2720        var width;
2721        if (!vertical) {
2722            width = 0;
2723        } else {
2724            if (useToSlides) {
2725                width = obj.toSlides.width();
2726            } else {
2727                width = obj.slider.width();
2728            }
2729        }
2730
2731        var height;
2732        if (vertical) {
2733            height = 0;
2734        } else {
2735            if (useToSlides) {
2736                height = obj.toSlides.height();
2737            } else {
2738                height = obj.slider.height();
2739            }
2740        }
2741
2742        obj.toSlides.each(function () {
2743            var that = $(this);
2744            if (vertical) {
2745                height += that.height();
2746            } else {
2747                width += that.width();
2748            }
2749        });
2750
2751        var boxWidth = Math.ceil(width / numberOfCols);
2752        var boxHeight = Math.ceil(height / numberOfRows);
2753        for (var row = 0; row < numberOfRows; row++) {
2754            for (var col = 0; col < numberOfCols; col++) {
2755                doStuff(row, col);
2756            }
2757        }
2758        function doStuff(row, col) {
2759            result.push(function () {
2760                var innerBox = makeClone(obj, useToSlides);
2761
2762                var box = createBox(innerBox, boxHeight * row, boxWidth * col, boxHeight, boxWidth);
2763                slider.append(box);
2764                return box;
2765            });
2766        }
2767
2768        return result;
2769    }
2770
2771    function createBox(innerBox, top, left, height, width) {
2772        innerBox.css({
2773            width: innerBox.width(),
2774            height: innerBox.height(),
2775            display: "block",
2776            top: -top,
2777            left: -left
2778        });
2779        return $(DIV_TAG).css({
2780            left: left,
2781            top: top,
2782            width: width,
2783            height: height,
2784            opacity: 0,
2785            overflow: HIDDEN_STRING,
2786            position: ABSOLUTE_STRING
2787        }).append(innerBox).addClass(ANIMATION_CLONE_MARKER_CLASS);
2788    }
2789
2790    // Makes a single box that contains clones of the toSlides/fromSlides. Positioned correctly relative to each other. And the returned box has the correct height and width.
2791    function makeClone(obj, useToSlides) {
2792        var slides = useToSlides ? obj.toSlides : obj.fromSlides;
2793        var firstSlidePosition = slides.eq(0).position();
2794        var orgLeft = firstSlidePosition.left;
2795        var orgTop = firstSlidePosition.top;
2796        var height = 0;
2797        var width = 0;
2798        var result = $(DIV_TAG).css({
2799            position: ABSOLUTE_STRING,
2800            top: 0,
2801            left: 0
2802        }).addClass(ANIMATION_CLONE_MARKER_CLASS);
2803        slides.each(function (index, elem) {
2804            var that = $(elem);
2805            var cloneWidth = that.outerWidth(TRUE);
2806            var cloneHeight = that.outerHeight(TRUE);
2807            var clone = that.clone();
2808            var position = that.position();
2809            var left = position.left - orgLeft;
2810            var top = position.top - orgTop;
2811            clone.css({position: ABSOLUTE_STRING, left: left, top: top, opacity: 1});
2812            height = mathMax(height, top + cloneHeight);
2813            width = mathMax(width, left + cloneWidth);
2814            result.append(clone);
2815        });
2816        result.width(width).height(height);
2817        return result;
2818    }
2819
2820    /*
2821     * Util scripts.
2822     */
2823
2824    // This is not deep equals, and only using ==
2825    function simpleObjectEquals(a, b) {
2826        if (getKeys(a).length != getKeys(b).length) {
2827            return FALSE;
2828        }
2829        for (var key in a) {
2830            if (a[key] != b[key]) {
2831                return FALSE;
2832            }
2833        }
2834        return TRUE;
2835    }
2836
2837    function makeCallback(func, args) {
2838        return function () {
2839            func.apply(undefined, args);
2840        }
2841    }
2842
2843    function runOnImagesLoaded(target, waitForAllImages, callback) {
2844        if (!target) {
2845            callback();
2846            return;
2847        }
2848        var elems = target.add(target.find("img")).filter("img");
2849        var numberOfRemainingImages = elems.length;
2850        if (!numberOfRemainingImages) {
2851            callback();
2852            return;
2853        }
2854
2855        elems.each(function () {
2856            var that = this;
2857            var jQueryThat = $(that);
2858            var events = "load error";
2859            var loadFunction = function () {
2860                jQueryThat.off(events, loadFunction);
2861                numberOfRemainingImages--;
2862                if (waitForAllImages) {
2863                    if (numberOfRemainingImages == 0) {
2864                        callback();
2865                    }
2866                } else {
2867                    callback();
2868                }
2869            };
2870            jQueryThat.on(events, loadFunction);
2871            /*
2872             * Start ugly working IE fix.
2873             */
2874            if (that.readyState == "complete") {
2875                jQueryThat.trigger("load");
2876            } else if (that.readyState) {
2877                // Sometimes IE doesn't fire the readystatechange, even though the readystate has been changed to complete. AARRGHH!! I HATE IE, I HATE IT, I HATE IE!
2878                that.src = that.src;
2878 // Do not ask me why this works, ask the IE team!
2879            }
2880            /*
2881             * End ugly working IE fix.
2882             */
2883            else if (that.complete) {
2884                jQueryThat.trigger("load");
2885            }
2886            else if (that.complete === undefined) {
2887                var src = that.src;
2888                // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f
2889                // data uri bypasses webkit log warning (thx doug jones)
2890                that.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
2891                that.src = src;
2892            }
2893        });
2894    }
2895
2896    // The minVersion is specified in an array, like [1, 8, 0] for 1.8.0
2897    // Partially copy-pasted from: https://gist.github.com/dshaw/652870
2898    function minJQueryVersion(minVersion) {
2899        var version = $.fn.jquery.split(".");
2900        var length = version.length
2901        for (var a = 0; a < length; a++) {
2902            if (minVersion[a] && +version[a] < +minVersion[a]) {
2903                return FALSE;
2904            }
2905        }
2906        return TRUE;
2907    }
2908
2909    function getVendorPrefixedProperty(property, searchElement) {
2910        for (var name in searchElement) {
2911            if (endsWith(name.toLowerCase(), property.toLowerCase())) {
2912                return name;
2913            }
2914        }
2915        return FALSE;
2916    }
2917
2918    function getCSSVendorPrefix() {
2919        var property = "transition";
2920        var styleName = getVendorPrefixedProperty(property, jQuery(DIV_TAG)[0].style);
2921        if (styleName === FALSE) {
2922            return FALSE;
2923        }
2924        var prefix = styleName.slice(0, styleName.length - property.length);
2925        if (prefix.length != 0) {
2926            return "-" + prefix + "-";
2927        }
2928        return "";
2929    }
2930
2931    function stringTrim(str) {
2932        return str.replace(/^\s+|\s+$/g, '');
2933    }
2934
2935    function endsWith(string, suffix) {
2936        return string.indexOf(suffix, string.length - suffix.length) !== -1;
2937    }
2938
2939    function getKeys(obj) {
2940        var keys = [];
2941        for (var key in obj) {
2942            keys.push(key);
2943        }
2944        return keys;
2945    }
2946
2947    // Puts the specified function in a setTimeout([function], 0);
2948    function callAsync(func) {
2949        schedule(func, 0);
2950    }
2951
2952    function schedule(func, time) {
2953        return setTimeout(func, time);
2954    }
2955
2956    function matches(string, patternWithWildCards) {
2957        return !!string.match(new RegExp("^" + patternWithWildCards.split("*").join(".*") + "$", "g"));
2958    }
2959
2960    function startsWith(string, prefix) {
2961        return string.indexOf(prefix) == 0;
2962    }
2963
2964    function cloneArray(arrayToClone) {
2965        return arrayToClone.slice();
2966    }
2967
2968    // This mutates the given array, so that it is reversed.
2969    // It also returns it.
2970    function reverseArray(array) {
2971        return [].reverse.call(array);
2972    }
2973
2974    function childrenNotAnimationClones(obj) {
2975        return obj.children().not("." + ANIMATION_CLONE_MARKER_CLASS);
2976    }
2977
2978    function objectToLowercase(obj) {
2979        var ret = {};
2980        for (var key in obj)
2981            ret[key.toLowerCase()] = obj[key];
2982        return ret;
2983    }
2984
2985    // Mutates and returns the array.
2986    function shuffle(array) {
2987        for (var j, x, i = array.length; i; j = (Math.random() * i) | 0, x = array[--i], array[i] = array[j], array[j] = x) {
2988        }
2989        return array;
2990    }
2991
2992    function isFunction(func) {
2993        return jQuery.isFunction(func);
2994    }
2995
2996    function isArray(object) {
2997        return jQuery.isArray(object);
2998    }
2999
3000    function parseInt10(num) {
3001        return parseInt(num, 10);
3002    }
3003
3004    function parseNumber(num) {
3005        return parseFloat(num);
3006    }
3007
3008    function getTimeInMillis() {
3009        return +new Date();
3010    }
3011
3012    // Actual modulo, not remainder. From here: http://stackoverflow.com/questions/4467539/javascript-modulo-not-behaving
3013    // Added a or zero, to ensure it returns a number.
3014    function mod(a, n) {
3015        return (((a % n) + n) % n) || 0;
3016    }
3017
3018    function mathAbs(number) {
3019        return number < 0 ? -number : number;
3020    }
3021
3022    function mathMax(a, b) {
3023        return a > b ? a : b;
3024    }
3025
3026    function mathMin(a, b) {
3027        return a < b ? a : b;
3028    }
3029
3030    function getEffectMethod(inputEffect) {
3031        if (isArray(inputEffect)) {
3032            return makeRandomEffect(inputEffect);
3033        } else if (isFunction(inputEffect)) {
3034            return inputEffect
3035        } else /* if (typeof inputEffect === "string") */{
3036            inputEffect = stringTrim(inputEffect);
3037            if (inputEffect.indexOf(",") != -1) {
3038                var array = inputEffect.split(",");
3039                return makeRandomEffect(array);
3040            } else {
3041                var effects = objectToLowercase(allEffects);
3042                var effectName = stringTrim(inputEffect.toLowerCase());
3043                var result = effects[effectName];
3044                if (result) {
3045                    return result;
3046                } else {
3047                    var array = [];
3048                    for (var name in effects) {
3049                        if (matches(name, effectName)) {
3050                            array.push(effects[name]);
3051                        }
3052                    }
3053                    if (!array.length) {
3054                        return slide;
3055                    }
3056                    return makeRandomEffect(array);
3057                }
3058            }
3059        }
3060    }
3061
3062    function makeRandomEffect(array) {
3063        return function (obj) {
3064            var effect = pickRandomValue(array);
3065            return getEffectMethod(effect)(obj);
3066        }
3067    }
3068
3069    function pickRandomValue(obj) {
3070        return obj[shuffle(getKeys(obj))[0]];
3071    }
3072
3073    var requestAnimationFrame = function(callback, frameCount){
3074        var equestAnimationFrame = "equestAnimationFrame";
3075        var requestWindow = win["r" + equestAnimationFrame] ||
3076            win["webkitR" + equestAnimationFrame] ||
3077            win["mozR" + equestAnimationFrame] ||
3078            win["oR" + equestAnimationFrame] ||
3079            win["msR" + equestAnimationFrame] ||
3080            function (callback) {
3081                schedule(callback, 1000 / 60);
3082            };
3083        if (frameCount) {
3084            requestWindow(makeCallback(recursive, [callback, frameCount - 1]));
3085        } else {
3086            requestWindow(callback);
3087        }
3088    };
3089
3090    // From this guy: https://github.com/rdallasgray/bez
3091    // Inlined into my own script to make it shorter.
3092    function makeBezier(coOrdArray) {
3093        var encodedFuncName = "bez_" + coOrdArray.join("_").replace(/\./g, "p");
3094        var jqueryEasing = $.easing;
3095        if (!isFunction(jqueryEasing[encodedFuncName])) {
3096            var polyBez = function (p1, p2) {
3097                var A = [0, 0];
3098                var B = [0, 0];
3099                var C = [0, 0];
3100
3101                function bezCoOrd(t, ax) {
3102                    C[ax] = 3 * p1[ax], B[ax] = 3 * (p2[ax] - p1[ax]) - C[ax], A[ax] = 1 - C[ax] - B[ax];
3103                    return t * (C[ax] + t * (B[ax] + t * A[ax]));
3104                }
3105
3106                function xDeriv(t) {
3107                    return C[0] + t * (2 * B[0] + 3 * A[0] * t);
3108                }
3109
3110                function xForT(t) {
3111                    var x = t, i = 0, z;
3112                    while (++i < 14) {
3113                        z = bezCoOrd(x, 0) - t;
3114                        if (mathAbs(z) < 1e-3) break;
3115                        x -= z / xDeriv(x);
3116                    }
3117                    return x;
3118                }
3119
3120                return function (t) {
3121                    return bezCoOrd(xForT(t), 1);
3122                }
3123            };
3124            jqueryEasing[encodedFuncName] = function (x, t, b, c, d) {
3125                return c * polyBez([coOrdArray[0], coOrdArray[1]], [coOrdArray[2], coOrdArray[3]])(t / d) + b;
3126            }
3127        }
3128        return encodedFuncName;
3129    }
3130
3131});
3132// If you did just read the entire code, congrats.
3133// Did you find a bug? I didn't, so plz tell me if you did. (https://github.com/webbiesdk/SudoSlider/issues)
3134// You can use this fiddle: http://jsfiddle.net/32m7bhtn/ as a starting point if you want to create a snall test-case for me.

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.