1(function($)
2{
3    var imageflow_plugin = function(root) {
4
5        var _self = this;
6
7        this.root = $(root);
8
9        this.widthfactor = 0.51;
10        this.reflectionHeight = 0.5;
11        this.bottomLine = this.root.width() * 0.33; // Or false
12
13        this.imagesroot = null;
14        this.checkedImages = new Array();
15        this.imageflowDone = false;
16
17        this.current = 0;
18        this.xstep = 150; // pixel to move;
19        this.xstepWidthScaleFactor = 0.80; // scale the width of the images
20        this.xstepHeightScaleFactor = 0.60; // scale the height of the images
21        this.focusedElememtsPerSide = 5;
22
23        this.maxheight = this.root.width() * this.widthfactor;
24        this.timeOutAction = null;
25        this.whereToMoveQueue = new Array();
26
27        this.images = null;
28        this.caption = null;
29
30        // Loader
31        this.loadingbar = null;
32        this.loadingbartext = null;
33        this.loadingbarloader = null;
34
35        // Scroller
36        this.scrollbar = null;
37        this.scrollbarwidth = this.root.width() * 0.6;
38        this.scrollbarSliderOffsetLeft = 38;
39        this.scrollbarSliderOffsetRight = 54;
40        this.scroller = null;
41
42        this.dragStartPosition = false;
43        this.scrollerStartDragOffset = false;
44        this.scrollerDragOffset = 0;
45        this.scrollerIsDragging = false;
46        this.scrollerDownIntervall = false;
47        this.mouseDownByDrag = false;
48
49        this.intermediateImageSrc = DOKU_BASE + 'lib/plugins/imageflow/images/intermediate.png';
50
51        this.debug = false;
52
53        this.buildBasicStructure = function() {
54
55            if ( this.loadingbar !== null || this.images !== null ) { return false; }
56
57            var arVersion = navigator.appVersion.split("MSIE");
58            var version = parseFloat(arVersion[1]);
59
60            var newRoot = $('<div/>').addClass('imageflow_root').appendTo(this.root);
61
62            // Start Build Loader
63            this.loadingbar = $('<div/>')
64                                .addClass('imageflow_loadingbar_container')
65                                .css({top: (this.root.height()/2 -8), left: (this.root.width()/2 -100)}).appendTo(newRoot);
66
67
68            this.loadingbarloader = $('<div/>').addClass('imageflow_loader').appendTo(this.loadingbar);
69            this.loadingbartext = $('<p/>').text('Loading in progress.').appendTo(this.loadingbarloader);
70            // End Build Loader
71
72            // Start Build Images Container
73            this.images = $('<div/>').addClass('imageflow_images').height(this.root.width()*0.338).appendTo(newRoot);
74            // End Build Images Container
75
76            var splitter = $('<div/>').addClass('imageflow_no_scroller').appendTo(newRoot);
77
78            // Start Build Scroller Container
79            this.scrollbar = $('<div/>').addClass('imageflow_scrollbar').width(this.scrollbarwidth).css({left:((this.root.width() - (this.scrollbarwidth + this.scrollbarSliderOffsetLeft + this.scrollbarSliderOffsetRight)) / 2), 'margin-top': this.root.width() * 0.02 + 30}).click(_self.scrollerClick).appendTo(newRoot);
80
81            /* Set slider attributes */
82            /* Mousedown instead of click for holding it there */
83            $('<div/>').addClass('imageflow_slider_cap_left').appendTo(this.scrollbar).bind('mousedown', { direction: -1 }, _self.scrollerSideDown);
84            this.scroller = $('<div/>').addClass('imageflow_slider').appendTo(this.scrollbar).bind('mousedown', _self.dragStart);
85            $('<div/>').addClass('imageflow_slider_cap_right').appendTo(this.scrollbar).bind('mousedown', { direction: 1 }, _self.scrollerSideDown);
86
87            $(document).bind('mouseup', _self.scrollerSideUp);
88            // End Build Scroller Container
89
90            // Start Build Caption Container
91            this.caption = $('<div/>').addClass('imageflow_caption').width(this.root.width() * 0.5).css({left:this.root.width() * 0.25}).appendTo(newRoot);
92            // End Build Caption Container
93        };
94
95        this.dbgMessage = function(message) {
96            if ( _self.debug ) {
97                console.log(message);
98            }
99        };
100
101        this.loadImageEvent = function (src, eventFkt) {
102            var loadImage = new Image();
103            loadImage.bind('load', eventFkt);
104            loadImage.src = src;
105        };
106
107        // Set Timeout for clusores
108        this.timeOut = function(fkt, time) {
109            setTimeout(function(e) {
110                _self[fkt](e, this);
111            }, time);
112        };
113
114        this.init = function() {
115
116            this.root.addClass('scripting_active').html("").height(this.maxheight + 20);
117            this.buildBasicStructure();
118
119            // Loader Image Cascade
120            // Prepare Loader images - they have to be shown very first.
121            var loadImage = $(new Image());
122            loadImage.bind('load', function() {
123
124                _self.loadingbar.show(); // Show loader Bar
125                var loadImage = $(new Image());
126                loadImage.bind('load', function() {
127                    _self.timeOut('initImages', 500);
128                });
129
130                loadImage.attr('src', DOKU_BASE + 'lib/plugins/imageflow/images/loader_bg.gif');
131            });
132
133            loadImage.attr('src', DOKU_BASE + 'lib/plugins/imageflow/images/loader.gif');
134
135            var intermediate = new Image();
136            intermediate.src = this.intermediateImageSrc;
137        };
138
139        this.initImages = function () {
140
141            // get images from JSINFO var
142            $(JSINFO['relation']['imageflow'][this.root.attr('id')]).each(function(){
143                var imgRep = new imageRepresentation();
144                imgRep.init(this);
145                _self.checkedImages.push(imgRep);
146            });
147
148            this.timeOut('checkForImagesReady', 50);
149        };
150
151        /*
152         * END OF ALL INIT THINGS
153         */
154
155
156        /*
157         * Represents an image with special functions inside
158         */
159        var imageRepresentation = function() {
160
161            var __self = this;
162
163            this.imgData = null;
164            this.image = null;
165            this.id = null;
166
167            this.counter = 300;
168            this.isFinished = false;
169
170            this.x_pos = 0;
171            this.width = 0;
172            this.height = 0;
173            this.pc = 0;
174
175            this.popupData = {};
176
177            this.isImageOk = function() {
178
179                var img = this.image.get(0);
180                if (!img.complete) { return false; }
181                if (typeof img.naturalWidth != "undefined" && img.naturalWidth == 0) { return false; }
182
183                // No Way to determine
184                return true;
185            };
186
187            this.intermediateFinish = function() {
188
189                this.counter = 10;
190                this.imgData.intermediateImage = this.image;
191                this.image = $(new Image());
192
193                this.imgData.intermediateImage.bind('load', function() {
194
195                    // reset the image
196                    __self.image.replaceWith(__self.imgData.intermediateImage);
197                    __self.image = __self.imgData.intermediateImage;
198                    __self.finish();
199
200                    _self.moveTo(_self.current); // Force repaint
201                });
202
203                this.image.bind('load', __self.finish);
204                this.image.attr('src', _self.intermediateImageSrc);
205            };
206
207            this.finish = function() {
208
209                __self.counter = 0;
210                __self.width = __self.image.naturalWidth();
211                __self.height = __self.image.naturalHeight(); // - ( this.image.height * this.reflectionHeight ); // Height w/o reflection
212
213                /* Check source image format. Get image height minus reflection height! */
214                __self.pc = _self.xstep * (((__self.width + 1) > (__self.height / (_self.reflectionHeight + 1))) ? _self.xstepWidthScaleFactor : _self.xstepHeightScaleFactor);
215            };
216
217            this.checkFinished = function() {
218                var isOK = this.isImageOk();
219                if ( !isOK && this.counter > 0 ) {
220                    this.counter--;
221                    return false;
222                } else if ( !isOK && this.counter == 0) {
223                    this.intermediateFinish();
224                }
225
226                this.isFinished = true;
227                this.finish();
228                return true;
229            };
230
231            this.popupClick = function(itemNr, direction) {
232
233                if ( !itemNr && !direction ) { return; }
234
235                _self.addMoveElementToQueue(itemNr);
236                var imageElement = _self.checkedImages[itemNr];
237                _self.specialClick.call(imageElement.image, {target: imageElement.image});
238
239                return true;
240            };
241
242            this.init = function(imgData) {
243
244                this.imgData = imgData;
245                this.popupData = imgData.popupData || {};
246                this.popupData.id = this.popupData.id || this.imgData.id;
247                this.popupData.call = this.popupData.call || this.imgData.call;
248                this.popupData.isImage = this.popupData.isImage || this.imgData.isImage || false;
249
250                // Reset SRC if this is not an image, because we want the default source then.
251                this.popupData.src = this.popupData.isImage ? this.popupData.src || this.imgData.src : null;
252                this.popupData.hasNextPrevious = true; // Allways display the next and previous handles.
253
254                this.image = $(new Image());
255                var src = this.imgData.src;
256                if ( imgData.params ) {
257                    for ( var key in imgData.params) {
258                        if ( typeof key == 'string' && (typeof imgData.params[key] == 'string' || typeof imgData.params[key] == 'number') ) {
259                            src += (src.indexOf('?') > 0 ? '&' : '?') + escape(key) + "=" + escape(imgData.params[key]);
260                        }
261                    }
262                }
263
264                this.image.attr({
265                    src: src,
266                    id: this.imgData.id
267                }).hide();
268
269                this.id = this.imgData.id;
270
271                this.image.bind('load', __self.finish);
272            };
273        };
274
275        this.loadingStatus = function() {
276
277            var completed = 0; var total = 0;
278
279            for ( var img = 0; img < this.checkedImages.length; img++ ) {
280                if ( this.checkedImages[img].isFinished || this.checkedImages[img].checkFinished() ) { completed++; }
281                total ++;
282            }
283
284            var finished = Math.round((completed/total)*100);
285            if ( finished >= 100 ) { finished = 100; }
286
287            this.loadingbarloader.width(finished+'%');
288            this.loadingbar.show();
289
290            this.loadingbartext.text('Loading Images ' + completed + '/' + total);
291
292            return finished;
293        };
294
295        this.checkForImagesReady = function() {
296
297            if ( this.imageflowDone ) { return; }
298            if ( this.loadingStatus() < 100 ) {
299                this.timeOut('checkForImagesReady', 50);
300                return;
301            } else {
302                this.imageflowDone = true;
303            }
304
305            this.refreshImageFlow();
306            this.addGlobalEvents();
307            this.checkForPopUp();
308        };
309
310        this.addGlobalEvents = function () {
311
312            this.root.bind({
313                'mousewheel': _self.globalEvent,
314                'mousemove': _self.drag
315            });
316
317            $(document).bind({
318                'keydown': _self.globalEvent,
319                'mouseup': _self.dragStop
320            });
321        };
322
323        /*
324         * BEGINN OF ALL MOVEMENT THINGS
325         */
326        this.refreshImageFlow = function() {
327
328            var img = 0;
329            for ( img; img < this.checkedImages.length; img++ ) {
330                var imageElement = this.checkedImages[img];
331
332                this.images.append(imageElement.image);
333
334                imageElement.image.show();
335                imageElement.image.css('cursor', 'pointer');
336
337                imageElement.image.click(_self.elementClick);
338                imageElement.image.dblclick(_self.specialClick);
339                imageElement.image.bind('mousedown', _self.dragStart);
340            }
341
342            if ( img < 1 ) {
343                this.loadingbartext.text('Ups. There are no Images.');
344                this.loadingbartext.css('color', "#a00");
345
346                return;
347            }
348
349            this.loadingbar.hide();
350            this.scrollbar.show();
351            this.moveTo(this.current, this.current);
352            this.glideTo(this.current);
353        };
354
355        this.moveTo = function(whereToMove, origWhereToMoveIndex) {
356
357            //this.current = whereToMove;
358            var zIndex = _self.checkedImages.length;
359            var size = _self.root.width() * 0.5;
360            var images_top = 50; // Offset from top
361
362            for ( var img = 0; img < _self.checkedImages.length; img++ ) {
363                var imageElement = _self.checkedImages[img];
364
365                // Hide Elements outside of our viewport
366                if ( img < _self.current - _self.focusedElememtsPerSide || img > _self.current + _self.focusedElememtsPerSide ) {
367                    imageElement.image.hide();
368                    continue;
369                }
370
371                var movement = (img - _self.current) * _self.xstep;
372                var z = Math.sqrt(10000 + movement * movement) + 100;
373                var xs = movement / z * size + size;
374
375                /* Still hide images until they are processed, but set display style to block */
376                imageElement.image.show();
377
378                /* Process new image height and image width */
379                var new_img_h = (imageElement.height / imageElement.width * imageElement.pc) / z * size;
380                var new_img_w = imageElement.pc / z * size;
381
382                if ( new_img_h > _self.maxheight ) {
383                    new_img_h = _self.maxheight;
384                    new_img_w = imageElement.width * new_img_h / imageElement.height;
385                }
386
387                var new_img_top = ((new_img_h / (_self.reflectionHeight + 1)) * _self.reflectionHeight);
388                if ( _self.bottomLine !== false ) { new_img_top += (_self.bottomLine - new_img_h); }
389
390                var new_img_left = xs - (imageElement.pc / 2) / z * size;
391
392                imageElement.image.css({left: new_img_left, top: new_img_top }).width(new_img_w).height(new_img_h);
393
394                _self.dbgMessage(imageElement.image.offset().left + "|" + imageElement.image.offset().top + " " + imageElement.image.width() + "x" + imageElement.image.height());
395
396                // imageElement.image.style.visibility = 'visible';
397
398                /* Set image layer through zIndex */
399                if ((img - _self.current) < 0) {
400                    zIndex++;
401                } else {
402                    zIndex--;
403                }
404
405                // register new handles
406                if ( img == origWhereToMoveIndex ) {
407                    zIndex++;
408
409                    imageElement.image.unbind('click').click(_self.specialClick);
410                } else {
411                    imageElement.image.unbind('click').click(_self.elementClick);
412                }
413
414                imageElement.image.css('z-index', zIndex);
415            }
416
417            _self.current = whereToMove;
418
419            // Set Caption, though its not performaing best here
420            if (_self.checkedImages[origWhereToMoveIndex]) { _self.buildCaptionForElement(_self.checkedImages[origWhereToMoveIndex].imgData); }
421            _self.setSliderPosition();
422        };
423
424        this.buildCaptionForElement = function(imgData) {
425
426            // Remove old Caption
427            this.caption.html('');
428
429            // Create new Caption if title or caption given
430            if ( imgData.title ) {
431                this.caption.append($('<h3/>').text(imgData.title));
432            }
433
434            if ( imgData.desc ) {
435                this.caption.append($('<p/>').text(imgData.desc));
436            }
437        };
438
439        this.setSliderPosition = function(override) {
440            var new_slider_pos = (_self.scrollbarwidth * (_self.current/(_self.checkedImages.length-1)));
441            if ( new_slider_pos >= 0 &&  new_slider_pos <= _self.scrollbarwidth && ( _self.scrollerStartDragOffset === false || override === true ) ) {
442                _self.dbgMessage('slider-margin-left:' + (new_slider_pos - (_self.scroller.width() / 2)));
443                _self.scroller.css('margin-left', new_slider_pos - (_self.scroller.width() / 2));
444            }
445        };
446
447        this.glideTo = function(whereToMove) {
448
449            if ( whereToMove < 0 ) { whereToMove = 0; }
450            if ( whereToMove >= _self.checkedImages.length ) { whereToMove = _self.checkedImages.length-1; }
451
452            // Animate gliding to new position
453            // If current position is not the desired one
454            var devident = (_self.whereToMoveQueue.length > 1 ? 1 : _self.xstep); // check distance
455            if ( whereToMove < _self.current - 1/devident || whereToMove > _self.current + 1/devident )
456            {
457                _self.moveTo(_self.current + (whereToMove-_self.current)/3, whereToMove); // move in three steps
458                _self.timeOutAction = setTimeout(function(){ _self.glideTo(whereToMove); }, 50);
459                return;
460            }
461
462            _self.current = whereToMove;
463
464            if ( _self.whereToMoveQueue.length > 1 ) { // Asume, the first entry is the first moving step
465                _self.timeOutAction = setTimeout(function() {
466                    _self.glideTo(parseInt(_self.whereToMoveQueue.splice(1,1))); }, 50);
467            } else {
468
469                // Display new caption
470                _self.moveTo(_self.current, _self.current); // If the above got interrupted, set the new distance
471
472                _self.timeOutAction = null; // Reset Timeout
473                _self.whereToMoveQueue = new Array();
474            }
475        };
476
477        this.addMoveElementToQueue = function (whereToMove) {
478            if ( whereToMove < 0 ) { whereToMove = 0; } // Already the first
479            if ( whereToMove >= _self.checkedImages.length ) { whereToMove = _self.checkedImages.length-1; } // This is already the last
480            if ( _self.whereToMoveQueue[_self.whereToMoveQueue.length -1] != whereToMove )_self.whereToMoveQueue.push(whereToMove);
481            _self.glideTo(whereToMove);
482
483            return true;
484        };
485
486        this.handle = function(delta) {
487
488            var whereToMove = _self.current;
489            if ( _self.timeOutAction ) {
490                clearTimeout(this.timeOutAction);
491                _self.timeOutAction = null;
492                whereToMove = parseInt(_self.whereToMoveQueue[_self.whereToMoveQueue.length - 1]);
493                _self.whereToMoveQueue = new Array();
494            }
495
496            whereToMove += delta;
497            if ( _self.addMoveElementToQueue(whereToMove) ) { return whereToMove; }
498        };
499
500        this.drag = function(e) {
501
502            if ( !_self.dragStartPosition ) { return; } // Dragging not inited
503            e.stopPropagation();
504
505            var direction = _self.scrollerIsDragging ? 1 : -1;
506            var posx = e.clientX;
507            var move =  direction * (posx - _self.dragStartPosition);
508
509            if ( !_self.scrollerIsDragging ) { // Dragging at the image
510
511                _self.dbgMessage("dragging image");
512
513                if ( _self.scrollerStartDragOffset === false || isNaN(_self.scrollerStartDragOffset.target) || isNaN(_self.scrollerStartDragOffset.current) ) {
514                    _self.scrollerStartDragOffset = {};
515                    _self.scrollerStartDragOffset.target = _self.getClickImage(e);
516                    _self.scrollerStartDragOffset.current = _self.current;
517
518                    _self.dbgMessage("setting StartDragOffset (" + _self.scrollerStartDragOffset.target + " / " + _self.scrollerStartDragOffset.current + ")");
519                }
520
521                var s = _self.images.width()/2;
522                var movement = (_self.scrollerStartDragOffset.target - _self.scrollerStartDragOffset.current) * _self.xstep;
523                var z = Math.sqrt(10000 + movement * movement) + 100;
524                var xs = movement / z * s;
525
526                _self.dbgMessage("move1: " + move);
527                move += direction * (xs);
528                _self.dbgMessage("move2: " + move);
529
530                xs = move + s;
531                if ( xs < 0 ) { xs = 0; }
532                if ( xs > 2*s ) { xs = 2*s - 1; }
533                movement = (200 * s * ( xs -s ) / ( (2*s - xs) * xs )) / _self.xstep;
534                _self.dbgMessage("movement1: " + movement);
535                _self.dbgMessage("xs: " + xs);
536
537                // Maximum movement to either side
538                if ( movement > _self.focusedElememtsPerSide ) { movement = _self.focusedElememtsPerSide; }
539                if ( movement < -_self.focusedElememtsPerSide ) { movement = -_self.focusedElememtsPerSide; }
540
541                movement += _self.scrollerStartDragOffset.current + (_self.scrollerStartDragOffset.target - _self.scrollerStartDragOffset.current);
542
543                _self.dbgMessage("movement2: " + movement);
544                _self.moveTo(movement, Math.round(movement));
545                _self.mouseDownByDrag = true;
546
547
548                // // this.debug.innerHTML = movement + " " + this.current;
549
550            } else {
551                _self.dbgMessage("dragging scroller");
552
553                var deltaPercent = move * 100 / _self.scrollbarwidth;
554                var delta = Math.round(_self.checkedImages.length / 100 * deltaPercent);
555
556                if (_self.scrollerDragOffset - delta != 0) {
557                    _self.handle(delta - _self.scrollerDragOffset);
558                    _self.scrollerDragOffset = delta;
559                }
560            }
561
562            _self.setSliderPosition(true);
563
564            return false;
565        };
566
567        this.dragStart = function(e) {
568
569            if ( _self.timeOutAction !== null ) { return; }
570
571            _self.dbgMessage("Drag Start " + e.target);
572            e.stopPropagation();
573
574            _self.dragStartPosition = e.clientX;
575            _self.scrollerIsDragging = e.target == _self.scroller.get(0);
576            _self.scrollerStartDragOffset = _self.scrollerIsDragging ? _self.scroller.offset().left : false;
577        };
578
579        this.dragStop = function(e) {
580
581            if ( _self.dragStartPosition === false ) { return; }
582
583            e.stopPropagation();
584
585            _self.dbgMessage("Drag Stop " + e.target);
586            if ( _self.scrollerIsDragging === false && typeof _self.scrollerStartDragOffset == 'object' && isFinite(parseInt(_self.scrollerStartDragOffset.target)) ) {
587                // Snap to Element
588                _self.addMoveElementToQueue(Math.round(_self.current));
589            }
590
591            _self.dragStartPosition = false;
592            _self.scrollerStartDragOffset = false;
593            _self.scrollerIsDragging = false;
594            _self.scrollerDragOffset = 0;
595            _self.setSliderPosition();
596            setTimeout( function() { _self.mouseDownByDrag = false; }, 100);
597        };
598
599        this.scrollerClick = function(e) {
600
601            if ( _self.mouseDownByDrag ) { return; }
602            if ( ((e.target) ? e.target : e.srcElement) != _self.scroller.parentNode) { return; }
603            // this.debug.innerHTML = "Scroller Click " + ((e.target) ? e.target : e.srcElement).id;
604
605            _self.mouseDownByDrag = true;
606
607            _self.dragStartPosition = _self.scroller.offset().left + _self.scroller.width()/2;
608            _self.scrollerStartDragOffset = false;
609            _self.scrollerIsDragging = true;
610
611            _self.drag(e);
612            _self.dragStop(e);
613            _self.mouseDownByDrag = false;
614
615
616        };
617
618        this.scrollerSideDown = function(e) {
619
620            if ( _self.scrollerDownIntervall !== false ) { return; }
621            _self.dbgMessage("Side Down");
622
623            _self.handle(e.data.direction);
624            _self.scrollerDownIntervall = window.setInterval( function() { _self.handle(e.data.direction); }, 500);
625        };
626
627        this.scrollerSideUp = function() {
628            if ( _self.scrollerDownIntervall === false ) { return; }
629            _self.dbgMessage("Side Up");
630
631            window.clearInterval(_self.scrollerDownIntervall);
632            _self.scrollerDownIntervall = false;
633        };
634
635        this.specialClick = function(e) {
636
637            if ( _self.mouseDownByDrag ) { return; }
638
639            // If we have the popupviewer, lets do some action!
640            if ( !jQuery.popupviewer ) {
641                return;
642            }
643
644            if ( (whereToMove = _self.getClickImage(e)) === false ) { return; }
645
646            var whereToImage = _self.checkedImages[whereToMove];
647            jQuery.popupviewer().init(_self.checkedImages).presentViewerWithContent.call(whereToImage);
648        };
649
650        this.elementClick = function(e) {
651
652            if ( _self.mouseDownByDrag ) { return; }
653            if ( (whereToMove = _self.getClickImage(e)) === false ) { return; }
654
655            if ( _self.timeOutAction ) {
656                clearTimeout(_self.timeOutAction);
657                _self.timeOutAction = null;
658                _self.whereToMoveQueue = new Array();
659            }
660
661            _self.addMoveElementToQueue(whereToMove);
662        };
663
664        this.getClickImage = function(e) {
665
666            var whereToMove = 0;
667            $.grep(_self.checkedImages, function(elem, index){
668                if ( elem.image.first().is(e.target) ) {
669                    whereToMove = index;
670                    return false;
671                }
672            });
673
674            return whereToMove;
675        };
676
677        this.checkForPopUp = function() {
678            if ( document.location.href.indexOf('#') < 0 ) { return; }
679            var extend = document.location.href.substr(document.location.href.indexOf('#'));
680            if ( typeof extend == "undefined" || this.root.find(extend).size() <= 0 ) { return; }
681
682            var e = {};
683            e.target = $(extend);
684            this.elementClick(e);
685            this.specialClick(e);
686        };
687
688        this.globalEvent = function(e) {
689
690            var delta = false;
691
692            if (e.keyCode) {
693                switch (e.keyCode) {
694                /* Right arrow key */
695                case 39:
696                    delta = 1;
697                    break;
698
699                /* Left arrow key */
700                case 37:
701                    delta = -1;
702                    break;
703                }
704            } else if (e.originalEvent.wheelDelta) {
705                delta = -e.originalEvent.wheelDelta;
706            }
707
708            if ( delta ) {
709                e.stopPropagation();
710                _self.handle(delta  > 0 ? 1 : -1);
711                return false;
712            }
713        };
714    };
715
716    $(function(){
717
718        if ( typeof $.fn.naturalWidth != 'undefined' && typeof $.fn.naturalHeight != 'undefined' ) { return; }
719
720        function img(url) { var i = new Image(); i.src = url; return i; }
721        if ('naturalWidth' in (new Image())) {
722            $.fn.naturalWidth  = function() { return this[0].naturalWidth; };
723            $.fn.naturalHeight = function() { return this[0].naturalHeight; };
724            return;
725        }
726
727        $.fn.naturalWidth  = function() { return img(this.src).width; };
728        $.fn.naturalHeight = function() { return img(this.src).height; };
729    });
730
731
732    $(function(){
733        $('div.imageflow_wrapper').each(function(){
734            (new imageflow_plugin(this)).init();
735        });
736    });
737})(jQuery);
738