source: extensions/iPiwigo/www/extensions/jqt.photo.js @ 9188

Last change on this file since 9188 was 9188, checked in by Polly, 13 years ago

Adding the Phonegap www folder needed to compile.

  • Property svn:executable set to *
File size: 71.4 KB
Line 
1/*!*
2 *    Provides event handling for iPhone like photo gallery (without thumbnail portion)
3 *
4 *    @author Sam Shull <http://www.google.com/profiles/brickysam26>
5 *    @copyright 2010 Sam Shull <http://samshull.blogspot.com/>
6 *
7 *    Special Thanks to Steve Simitzis <http://saturn5.com>
8 *
9 *    @license <http://www.opensource.org/licenses/mit-license.html>
10 *
11 *    Permission is hereby granted, free of charge, to any person obtaining a copy
12 *    of this software and associated documentation files (the "Software"), to deal
13 *    in the Software without restriction, including without limitation the rights
14 *    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 *    copies of the Software, and to permit persons to whom the Software is
16 *    furnished to do so, subject to the following conditions:
17 *   
18 *    The above copyright notice and this permission notice shall be included in
19 *    all copies or substantial portions of the Software.
20 *   
21 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 *    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 *    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 *    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 *    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 *    THE SOFTWARE.
28 *
29 *    Custom Events handled by each gallery:
30 *
31 *        jqt-photo-slideto            - slide to a given index (e, [index = int [, options = Object [, slides = jQuery ] ] ] )
32 *        jqt-photo-goto            - jump to a given index (e, [index = int [, options = Object [, slides = jQuery ] ] ] )
33 *        jqt-photo-play                - start the slideshow from the given point
34 *        jqt-photo-pause                - stop the slideshow
35 *        jqt-photo-prev                - go to the previous slide
36 *        jqt-photo-next                - go to the next slide
37 *        jqt-photo-hide-toolbars        - hide the visibility of the toolbars
38 *        jqt-photo-show-toolbars        - show the visibility of the toolbars
39 *        jqt-photo-toggle-toolbars    - toggle the visibility of the toolbars
40 */
41(function($){
42    //we need this in the algorithm
43    if (!"WebKitCSSMatrix" in this) {
44        return null;
45    }
46   
47    /**
48     *
49     *
50     *
51     */
52    var undefined, 
53       
54        /**
55         *    keep track of the registered galleries for updates on resize
56         *
57         *    @var Array<Element>
58         */
59        galleries = [],
60       
61        /**
62         *    Does the browser support Touch
63         *
64         *    @var Boolean
65         */
66        supportsTouch = "Touch" in this,
67       
68        /**
69         *    event types
70         *
71         *    @var Object
72         */
73        events = supportsTouch ? {
74            start: "touchstart",
75            move: "touchmove",
76            end: "touchend"
77        } : {
78            start: "mousedown",
79            move: "mousemove",
80            end: "mouseup"
81        },
82       
83        controlPoints,
84       
85        /**
86         *    keep track of whether or not the parsed class rule has been inserted
87         *
88         *    @var Boolean
89         */
90        parseRuleSet = false,
91       
92        //bring into context
93       
94        /**
95         *   
96         *
97         *    @var Window
98         */
99        window = this,
100       
101        /**
102         *   
103         *
104         *    @var Document
105         */
106        document = window.document,
107       
108        /**
109         *   
110         *
111         *    @var Object
112         */
113        Math = window.Math,
114       
115        //simplify access to these functions
116       
117        /**
118         *   
119         *
120         *    @var Function
121         */
122        min = Math.min,
123       
124        /**
125         *   
126         *
127         *    @var Function
128         */
129        floor = Math.floor,
130       
131        /**
132         *   
133         *
134         *    @var Function
135         */
136        sqrt = Math.sqrt,
137       
138        /**
139         *   
140         *
141         *    @var Function
142         */
143        pow = Math.pow,
144       
145        /**
146         *   
147         *
148         *    @var Function
149         */
150        abs = Math.abs,
151       
152        /**
153         *    current orientation
154         *
155         *    @var String
156         */
157        orientation = abs(window.orientation) == 90 ? "landscape" : "portrait",
158       
159        /**
160         *    The default options
161         *
162         *    @var Object
163         */
164        defaults = {
165            /**
166             *    A list of the images to be displayed in the gallery
167             *
168             *    @var Array<Object>
169             */
170            data: [],
171           
172            /**
173             *    displays in title bar
174             *    - supports format
175             *        {0} = current index
176             *        {1} = total number of slides
177             *
178             *    @var String
179             */
180            galleryName: "{0} of {1}",
181           
182            /**
183             *    where to start the slides in the gallery - reset on pageAnimation out
184             *
185             *    @var Number
186             */
187            defaultIndex: 0,
188           
189            /**
190             *    properties to animate
191             *
192             *    @var Number
193             */
194            transitionProperty: "-webkit-transform",
195           
196            /**
197             *    timing function
198             *
199             *    @var String
200             */
201            timingFunction: "cubic-bezier(0,0,.25,1)",
202           
203            /**
204             *    the reset transform of the table
205             *
206             *    @var String
207             */
208            transform: "translate3d({0}px,0,0)",
209           
210            /**
211             *    The template to be used for transforming the IMG tag
212             *
213             *    @var String
214             */
215            imageTransform: "scale({0}) translate3d({1}px, {2}px, 0)",
216           
217            /**
218             *    Maximum number of slides to keep in DOM at on time - before current slide
219             *
220             *    @var Number
221             */
222            maxSlidesBefore: 2,
223           
224            /**
225             *    Maximum number of slides to keep in DOM at on time - after current slide
226             *
227             *    @var Number
228             */
229            maxSlidesAfter: 2,
230           
231            /**
232             *    default delay between transitions for slideshow
233             *
234             *    @var Number
235             */
236            slideDelay: 5000,
237           
238            /**
239             *    default speed for slide change
240             *
241             *    @var Number
242             */
243            scrollSpeed: 500,
244           
245            /**
246             *    default speed for scale resizing - used on scaleEnd
247             *
248             *    @var Number
249             */
250            scaleSpeed: 500,
251           
252            /**
253             *    should we try to dynamically render the CSS rules
254             *
255             *    @var Boolean
256             */
257            useDynamicStyleSheet: true,
258           
259            /**
260             *    should a slideshow repeat by default
261             *
262             *    @var Boolean
263             */
264            repeatSlideShow: false,
265           
266            /**
267             *    selector for the parent of the gallery
268             *
269             *    @var String
270             */
271            appendTo: "#jqt",
272           
273            /**
274             *    the attribute name for storing the index of the slide in the data
275             *
276             *    @var String
277             */
278            dataAttribute: "data-index",
279           
280            /**
281             *    tags to ignore
282             *
283             *    @var Array
284             */
285            tagNames: "A,INPUT,SELECT,TEXTAREA",
286           
287            //only used by template generator
288           
289            /**
290             *    link to return
291             *
292             *    @var String
293             */
294            backLink: '<a class="back">Back</a>',
295           
296            /**
297             *    a blank image tag's template
298             *
299             *    @var String
300             */
301            blankImage: '<img class="jqt-photo-img"/>',
302           
303            /**
304             *    an individual slides template
305             *
306             *    @var String
307             */
308            slideTemplate: '<td class="jqt-photo-image-slide">\
309                                <div class="jqt-photo-not-loaded">\
310                                    <img class="jqt-photo-img"/>\
311                                    <div class="jqt-photo-caption"></div>\
312                                </div>\
313                            </td>',
314           
315            /**
316             *    CSS Rule for removing the max-width/max-height properties from the images
317             *
318             *    @var String
319             */
320            parsedClassTemplate: "#jqt .jqt-photo .jqt-photo-image-slide > .{0}{max-width:auto;max-height:auto;}",
321           
322            /**
323             *    the overall psuedo page template of a gallery
324             *
325             *    @var String
326             */
327            galleryTemplate: '<div class="jqt-photo">\
328                                    <div class="toolbar toolbar-top"><\/div>\
329                                    <table class="jqt-photo-table">\
330                                        <tr class="jqt-photo-slide-list"><\/tr>\
331                                    <\/table>\
332                                    <div class="toolbar toolbar-bottom">\
333                                        <div class="jqt-photo-prev"></div>\
334                                        <div class="jqt-photo-pause"></div>\
335                                        <div class="jqt-photo-play"></div>\
336                                        <div class="jqt-photo-next"></div>\
337                                    <\/div>\
338                                <\/div>',
339           
340            //psuedo events
341           
342            /**
343             *    Handle the pageAnimationEnd event fired by jQTouch
344             *    Resets the positions and scales
345             *
346             *    @var Function
347             */
348            pageAnimationEnd: pageAnimationEnd,
349           
350            /**
351             *    Handle the pageAnimationStart event fired by jQTouch
352             *    Resets the positions and scales
353             *
354             *    @var Function
355             */
356            pageAnimationStart: pageAnimationStart,
357           
358            /**
359             *    Handle the transition of slides in a gallery
360             *
361             *    @var Function
362             */
363            slideTo: slideTo,
364           
365            /**
366             *    Begin a slideshow on a gallery
367             *
368             *    @var Function
369             */
370            play: play,
371           
372            /**
373             *    End a slideshow on a gallery
374             *
375             *    @var Function
376             */
377            pause: pause,
378           
379            /**
380             *    Go to the next slide in the list
381             *
382             *    @var Function
383             */
384            next: next,
385           
386            /**
387             *    Go to the previous slide in the list
388             *
389             *    @var Function
390             */
391            prev: prev,
392           
393            /**
394             *    Go to a specific or implied slide
395             *
396             *    @var Function
397             */
398            goTo: goTo,
399           
400            /**
401             *    Reveal the toolbars
402             *
403             *    @var Function
404             */
405            showToolbars: showToolbars,
406           
407            /**
408             *    Hide the toolbars
409             *
410             *    @var Function
411             */
412            hideToolbars: hideToolbars,
413           
414            /**
415             *    Show/Hide the toolbars where appropriate
416             *
417             *    @var Function
418             */
419            toggleToolbars: toggleToolbars,
420           
421            //mouse events
422           
423            /**
424             *    Handle a touchstart event on the gallery
425             *
426             *    @var Function
427             */
428            touchStart: touchStart,
429           
430            /**
431             *    Handle a drag start event (triggered by touchstart) - one-finger
432             *
433             *    @var Function
434             */
435            dragStart: dragStart,
436           
437            /**
438             *    Event handler - move - one-finger
439             *
440             *    @var Function
441             */
442            drag: drag,
443           
444            /**
445             *    Event handler - end - one-finger
446             *
447             *    @var Function
448             */
449            dragEnd: dragEnd,
450           
451            /**
452             *    Event handler - start - two-fingers
453             *
454             *    @var Function
455             */
456            scaleStart: scaleStart,
457           
458            /**
459             *    Event handler - move - two-fingers
460             *
461             *    @var Function
462             */
463            scale: scale,
464           
465            /**
466             *    Event handler - end - two-fingers
467             *
468             *    @var Function
469             */
470            scaleEnd: scaleEnd,
471           
472            /**
473             *    An event handler for the IMG load event
474             *
475             *    @var Function
476             */
477            loader: loader,
478           
479            /**
480             *    Function provided for generating the slides
481             *
482             *    @var Function
483             */
484            createSlide: createSlide,
485           
486            /**
487             *    Function provided for maintaining the sliding window of slides
488             *
489             *    @var Function
490             */
491            rearrange: rearrange,
492           
493            //classes
494           
495            /**
496             *    Keep track of preloaded images
497             *
498             *    @var String
499             */
500            presizedClass: "jqt-photo-presized",
501           
502            /**
503             *    keep track of images that have data
504             *
505             *    @var String
506             */
507            parsedClass: "jqt-photo-parsed",
508           
509            /**
510             *    Class name for identifying the current slide
511             *
512             *    @var String
513             */
514            currentClass: "jqt-photo-current",
515           
516            /**
517             *    Class name for designating that a slideshow is in progress
518             *    (and cause pause icon to show, not play icon)
519             *
520             *    @var String
521             */
522            playingClass: "jqt-photo-playing",
523           
524            /**
525             *    Class name for designating that an image is not currently loaded
526             *
527             *    @var String
528             */
529            notLoadedClass: "jqt-photo-not-loaded",
530           
531            /**
532             *    Class name for initiating an animation to show the toolbars
533             *
534             *    @var String
535             */
536            toolbarAnimationInClass: "jqt-photo-toolbar-animation-in",
537           
538            /**
539             *    Class name for initiating an animation to hide the toolbars
540             *
541             *    @var String
542             */
543            toolbarAnimationOutClass: "jqt-photo-toolbar-animation-out",
544           
545            /**
546             *    Class name for hiding toolbars - applied outside of animation
547             *
548             *    @var String
549             */
550            toolbarHideClass: "jqt-photo-toolbar-hidden",
551           
552            //selectors
553           
554            /**
555             *    The selector for the container of the slides
556             *
557             *    @var String
558             */
559            tableSelector: ".jqt-photo-table",
560           
561            /**
562             *    The selector for the caption of a slide
563             *
564             *    @var String
565             */
566            captionSelector: ".jqt-photo-caption",
567           
568            /**
569             *    The selector of a slide container
570             *
571             *    @var String
572             */
573            slideSelector: ".jqt-photo-image-slide",
574           
575            /**
576             *    The selector of the scalable element, relative to a slide
577             *
578             *    @var String
579             */
580            imageSelector: "img.jqt-photo-img",
581           
582            /**
583             *    The selector of the title element
584             *
585             *    @var String
586             */
587            titleSelector: ".toolbar-top h1",
588           
589            /**
590             *    The selector of row that contains the images
591             *
592             *    @var String
593             */
594            listSelector: ".jqt-photo-slide-list",
595           
596            /**
597             *    The selector of the Play Icon
598             *
599             *    @var String
600             */
601            playSelector: ".jqt-photo-play",
602           
603            /**
604             *    The selector of the Pause Icon
605             *
606             *    @var String
607             */
608            pauseSelector: ".jqt-photo-pause",
609           
610            /**
611             *    The selector of the Next Icon
612             *
613             *    @var String
614             */
615            nextSelector: ".jqt-photo-next",
616           
617            /**
618             *    The selector of the Previous Icon
619             *
620             *    @var String
621             */
622            prevSelector: ".jqt-photo-prev",
623           
624            /**
625             *    The selector of a gallery element
626             *
627             *    @var String
628             */
629            gallerySelector: ".jqt-photo"
630        },
631       
632        /**
633         *    Calculate the desired position of the caption elements within the galleries
634         *
635         *    @var Function
636         */
637        caption = function(vars){return (window.innerHeight - vars.caption) + "px";},
638       
639        /**
640         *    Calculate the desired height of the galleries and the slides
641         *
642         *    @var Function
643         */
644        height = function(){return window.innerHeight + "px";},
645       
646        /**
647         *    Calculate the desired width of the galleries and the slides
648         *
649         *    @var Function
650         */
651        width = function(){return window.innerWidth + "px"},
652       
653        /**
654         *    Calculate the desired position of the bottom toolbar elements within the galleries
655         *
656         *    @var Function
657         */
658        toolbar = function(vars){return (window.innerHeight - vars.toolbar) + "px";},
659       
660        /**
661         *    The default CSS rules for dynamic insertion
662         *
663         *    @var Object
664         */
665        cssRules = {
666       
667            /**
668             *    Variables used by the CSS "macros" for dynamically calculating positions and dimensions
669             *
670             *    @var Object
671             */
672            variables : {
673                caption: 90,
674                toolbar: 45
675            },
676       
677            /**
678             *    Default CSS rules
679             *
680             *    @var Object
681             */
682            defaults: {
683                "#jqt .jqt-photo .toolbar-bottom" : {
684                    top: toolbar
685                },
686                "#jqt .jqt-photo .jqt-photo-image-slide > div" : {
687                    width: width,
688                    height: height,
689                    "line-height": height
690                },
691                "#jqt .jqt-photo .jqt-photo-image-slide .jqt-photo-caption" : {
692                    top: caption,
693                }
694            },
695       
696            /**
697             *    Portrait view CSS rules
698             *
699             *    @var Object
700             */
701            portrait: {
702                "#jqt.portrait .jqt-photo" : {
703                    height: height,
704                    width: width,
705                },
706                "#jqt.portrait .jqt-photo .toolbar-bottom" : {
707                    top: toolbar
708                },
709                "#jqt.portrait .jqt-photo .jqt-photo-image-slide > div" : {
710                    width: width,
711                    height: height,
712                    "line-height": height,
713                },
714                "#jqt .jqt-photo .jqt-photo-image-slide .jqt-photo-caption" : {
715                    top: caption,
716                }
717            },
718       
719            /**
720             *    Landscape view CSS rules
721             *
722             *    @var Object
723             */
724            landscape: {
725                "#jqt.landscape .jqt-photo" : {
726                    height: height,
727                    width: width,
728                },
729                "#jqt.landscape .jqt-photo .toolbar-bottom" : {
730                    top: toolbar,
731                },
732                "#jqt.landscape .jqt-photo .jqt-photo-table" : {
733                    height: height,
734                },
735                "#jqt.landscape .jqt-photo .jqt-photo-image-slide > div" : {
736                    height: height,
737                    width: width,
738                    "line-height": height,
739                },
740                "#jqt.landscape .jqt-photo .jqt-photo-image-slide .jqt-photo-caption" : {
741                    top: caption,
742                }
743            }
744        };
745   
746    /*
747     *    jQTouch extension
748     */
749    if ($.jQTouch) {
750        //bind the extension
751        $.jQTouch.addExtension(function(jqt) {   
752            /*/event handler
753            function binder (event, info) {
754                if (info.page.is(defaults.gallerySelector)) {
755                    info.page.jqtPhoto();
756                }
757            }
758             
759            //attach
760            $(document.body).bind("pageInserted", binder);
761           
762            //attach to appropriate containers on DOMREADY
763            $(function() {
764                $(defaults.gallerySelector)
765                    .each(function() {
766                        binder(null, {page: $(this)});
767                    });
768            });
769            */
770            return {
771                generateGallery: generateGallery, 
772                /**
773                 *    Jump to the index of a slide in a gallery
774                 *
775                 *    @param jQuery | String (selector) | Element gallery
776                 *    @param Number index
777                 *    @param String | Object animation
778                 *    @param Boolean reverse
779                 *    @return jQuery
780                 */
781                goToSlide: function (gallery, index, animation, reverse) {
782                    //trigger this first
783                    var g = $(gallery), options = g.data("jqt-photo-options"),slides = g.find(options.slideSelector);
784                     
785                    if (slides.index("."+options.currentClass) != index) {
786                        slides.removeClass(options.currentClass).eq(index).addClass(options.currentClass);
787                    }
788                   
789                    if (!g.hasClass("current")) {
790                        jqt.goTo(g, animation || "slide", reverse);
791                    }
792                   
793                    g.triggerHandler("jqt-photo-show-toolbars");
794                   
795                    return g;
796                }
797            };
798        });
799    }
800   
801    /**
802     *    Generate a gallery and attach it to the #jqt element,
803     *    additionally updates scaling data, so you don't have to
804     *
805     *    <code>
806     *        images = [{src:"/somewhere1.jpg",caption:"Not Yet Implemented",width:200,height:200},
807     *                  {src:"/somewhere2.jpg",caption:"Not Yet Implemented"}];
808     *    </code>
809     *
810     *    @param String id
811     *    @param Array<Object> images
812     *    @param Object options - @see defaults
813     *    @return jQuery
814     */
815    function generateGallery (id, images, options) {
816        options = $.extend({}, defaults, options);
817       
818        options.data = images;
819       
820        if (!parseRuleSet) {
821            parseRuleSet = true;
822            var sheet = document.styleSheets[document.styleSheets.length-1];
823                        sheet.insertRule(
824                format(options.parsedClassTemplate, options.parsedClass), 
825                sheet.cssRules.length
826            );
827        }
828       
829        options.gallery = $(options.galleryTemplate).appendTo(options.appendTo);
830       
831        var list = options.list = options.gallery.find(options.listSelector),
832            lower = options.defaultIndex - options.maxSlidesBefore,
833            upper = options.defaultIndex + options.maxSlidesAfter,
834            toolbar = options.gallery.attr("id", id)
835                        .find(".toolbar-top")
836                        .append(
837                            $("<h1></h1>").html(
838                                format(options.galleryName, options.defaultIndex + 1, images.length)
839                            )
840                        );
841       
842        if (options.backLink) {
843            toolbar.append(options.backLink);
844        }
845       
846        $.each(images, function(i, data) {
847            list.append(options.createSlide(data, i, options, i >= lower && i <= upper));
848        });
849       
850        return jqtPhoto(options.gallery[0], options);
851    }
852   
853    /*
854     *
855     *    jQuery Extensions
856     *
857     */
858   
859    /**
860     *    A jQuery prototype extension for enabling the photo gallery
861     *    on a given set of elements
862     *
863     *    @param Object options
864     *    @return jQuery
865     */
866    $.fn.jqtPhoto = function(options) {
867        options = $.extend({}, defaults, options || {});
868        return this.each(function(){jqtPhoto(this, options)});
869    };
870   
871    /**
872     *    A static jQuery extension for setting and retrieving the jQT-Photo defaults
873     *
874     *   
875     */
876    $.jqtPhoto = {
877        generateGallery: generateGallery,
878   
879        /**
880         *    Change and/or retrieve the default options
881         *
882         *    @param Object options - optional
883         *    @return Object
884         */
885        defaults: function(options) {
886            if (options) {
887                defaults = $.extend(defaults, options);
888            }
889           
890            return $.extend({}, defaults);
891        },
892   
893        /**
894         *    Change and/or retrieve the default CSS
895         *
896         *    @param Object options - optional
897         *    @return Object
898         */
899        defaultCSS: function(options) {
900            if (options !== undefined) {
901                cssRules = $.extend(true, cssRules, options);
902            }
903           
904            return $.extend({}, cssRules);
905        }
906    };
907   
908    /*
909     *
910     *    Functions
911     *
912     */
913   
914    /**
915     *    Intialize the photo gallery
916     *
917     *    @param Element | String | jQuery element
918     *    @param Object options
919     *    @return jQuery
920     */
921    function jqtPhoto (element, options) {
922        var $elem = $(element),
923            slides = attachEvents($elem, options).find(options.slideSelector),
924            images = slides.find(options.imageSelector);
925       
926        options.list = $elem.find(options.listSelector);
927       
928        options.table = tableData($elem.data("jqt-photo-options", options), options);
929       
930        options.blankImage = parseImageData($elem, $(options.blankImage).load(options.loader), options);
931       
932        if (!slides.filter("."+options.currentClass).length) {
933            if (!slides.filter(format("[{0}={1}]", options.dataAttribute, options.defaultIndex)).addClass(options.currentClass).length) {
934                slides.eq(0).addClass(options.currentClass);
935            }
936        }
937       
938        $elem.find(options.tableSelector).css({
939            webkitTransitionProperty: options.transitionProperty,
940            webkitTransitionTimingFunction: options.timingFunction,
941            webkitTransitionDuration: options.defaultDuration + "s",
942            webkitTransform: format(options.transform, -slides.filter("."+options.currentClass).attr("offsetLeft") || 0)
943        });
944       
945        parseImageData($elem, images, options);
946       
947        galleries = galleries.concat($.makeArray($elem));
948       
949        return $elem;
950    }
951   
952    /**
953     *    Record the initial start position of the image list element
954     *
955     *    @param jQuery target
956     *    @param Object options
957     *    @return null
958     */
959    function tableData (target, options) {
960        var table = target.find(options.tableSelector),
961            transform = window.getComputedStyle(table[0]).webkitTransform,
962            matrix = new WebKitCSSMatrix(transform);
963       
964        return table.data("jqt-photo-position", {x: Number(matrix.m41 || 0), gallery: target});
965    }
966   
967    /**
968     *    Attach event listeners to the gallery
969     *
970     *    To Do: refractor event listeners into delegate
971     *
972     *    @param jQuery target
973     *    @param Object options
974     *    @return jQuery
975     */
976    function attachEvents (target, options) {
977       
978        return target
979                .each(function(){this.addEventListener(events.start, options.touchStart, false)})
980                .bind("jqt-photo-slideto", options.slideTo)
981                .bind("jqt-photo-goto", options.goTo)
982                .bind("jqt-photo-play", options.play)
983                .bind("jqt-photo-pause", options.pause)
984                .bind("jqt-photo-prev", options.prev)
985                .bind("jqt-photo-next", options.next)
986                .bind("jqt-photo-hide-toolbars", options.hideToolbars)
987                .bind("jqt-photo-show-toolbars", options.showToolbars)
988                .bind("jqt-photo-toggle-toolbars", options.toggleToolbars)
989                .bind("pageAnimationEnd", options.pageAnimationEnd)
990                .bind("pageAnimationStart", options.pageAnimationStart);
991    }
992   
993    /**
994     *    Record initial slide data
995     *
996     *    @param jQuery images
997     *    @param Object options
998     *    @return jQuery
999     */
1000    function parseImageData (target, images, options) {
1001        return images.each(function() {
1002            var t = $(this).addClass(options.parsedClass),
1003                matrix = new WebKitCSSMatrix(window.getComputedStyle(this).webkitTransform),
1004                scale = Number(matrix.m11),
1005                data = t.data("jqt-photo-info") || {}, 
1006                c = {
1007                    scale: scale,
1008                    top: Number(matrix.m42),
1009                    left: Number(matrix.m41),
1010                    width: t.width() * scale,
1011                    height: t.height() * scale,
1012                    parent: t.parent()
1013                };
1014           
1015            data.galleryOptions = options;
1016            data.gallery = data.gallery || target;
1017           
1018            data[orientation] = {current: c, original: $.extend({}, c)};
1019           
1020            t.data("jqt-photo-info", data);
1021        });
1022    }
1023   
1024    /**
1025     *    Distributive handling for touchstart events
1026     *
1027     *    To Do: refractor to use all event based triggers
1028     *
1029     *    @context The gallery element
1030     *
1031     *    @param Event event
1032     *    @return Boolean
1033     */
1034    function touchStart (event) {
1035        var target = $(event.target.nodeType == 3 ? event.target.parentNode : event.target), 
1036            $this = $(this),
1037            options = $this.data("jqt-photo-options"),
1038            tt = supportsTouch ? event.targetTouches : {length: !event.ctrlKey ? 1 : 2};
1039       
1040        if (target.is(options.tagNames)) {
1041            return null;
1042        }
1043       
1044        if (target.is(options.playSelector)) {
1045            if (!$this.hasClass(options.playingClass) && options.play) {
1046                return options.play.call(this, event);
1047            }
1048           
1049            return true;
1050           
1051        } else if (target.is(options.pauseSelector)) {
1052            if ($this.hasClass(options.playingClass) && options.pause) {
1053                return options.pause.call(this, event);
1054            }
1055           
1056            return true;
1057           
1058        } else if (target.is(options.nextSelector) && options.next) {
1059            return options.next.call(this, event);
1060           
1061        } else if (target.is(options.prevSelector) && options.prev) {
1062            return options.prev.call(this, event);
1063           
1064        } else if (tt.length == 2 && target.closest(options.imageSelector).length && options.scaleStart) {
1065            if ($this.hasClass(options.playingClass) && options.pause) {
1066                options.pause.call(this);
1067            }
1068           
1069            return options.scaleStart.call(this, event);
1070           
1071        } else if (tt.length == 1 && options.dragStart) {
1072            if ($this.hasClass(options.playingClass) && options.pause) {
1073                options.pause.call(this, event);
1074            }
1075           
1076            return options.dragStart.call(this, event);
1077        }
1078       
1079        return true;
1080    }
1081       
1082    /**
1083     *    Initialize a drag event
1084     *
1085     *
1086     *    @context The gallery element
1087     *
1088     *    @param Event event
1089     *    @return Boolean
1090     */
1091    function dragStart (event) {
1092        event.preventDefault();
1093        window.scrollTo(0,0);
1094       
1095        var $this = $(this),
1096            options = $this.data("jqt-photo-options"),
1097            table = $this.find(options.tableSelector).css("webkitTransitionDuration", "0s"),
1098            position = table.data("jqt-photo-position"),
1099            target = $(event.target).closest(options.imageSelector),
1100            data = target.data("jqt-photo-info") || false,
1101            offset = !!data && data[orientation] || false,
1102            current = !!offset && offset.current || {},
1103            tt = supportsTouch ? event.targetTouches[0] : event;
1104       
1105        this.addEventListener(events.move, options.drag, false);
1106        this.addEventListener(events.end, options.dragEnd, false);
1107       
1108        $this
1109            .data("jqt-photo-event", {
1110                table: table,
1111                position: {current: position.x, original: position.x},
1112                target: target.css("webkitTransitionDuration", "0s"),
1113                options: options,
1114                slides: table.find(options.slideSelector),
1115                moved: false,
1116                x: tt.pageX,
1117                y: tt.pageY,
1118                left: current.left || 0,
1119                top: current.top || 0,
1120                timeStamp: +new Date
1121            });
1122           
1123        return true;
1124    }
1125   
1126    /**
1127     *    Perform a drag event
1128     *
1129     *
1130     *    @context The gallery element
1131     *
1132     *    @param Event event
1133     *    @return Boolean
1134     */
1135    function drag (event) {
1136        event.preventDefault();
1137       
1138        var $this = $(this),
1139            original = $this.data("jqt-photo-event"),
1140            table = original.table,
1141            position = original.position,
1142            target = original.target,
1143            info = target.data("jqt-photo-info"),
1144            data = !!info && info[orientation] || false,
1145            current = data.current || false,
1146            tt = supportsTouch ? event.targetTouches[0] : event,
1147            distanceX = original.x - tt.pageX,
1148            distanceY = original.y - tt.pageY,
1149            w, h, x, y, l, t, d, 
1150            s = current.scale;
1151       
1152        original.moved = true;
1153       
1154        //image
1155        if (data) {
1156            $this.triggerHandler("jqt-photo-hide-toolbars", [original.options]);
1157           
1158            if (distanceX) {
1159                x = target.width() * s;
1160                w = current.parent.width();
1161                l = original.left;
1162               
1163                if (x > w) {
1164                    l -= distanceX / s;
1165                    distanceX = 0;
1166                    d = ((x - w) / s) / 2;
1167                   
1168                    if (l > d) {
1169                        distanceX = d - l;
1170                        l = d;
1171                    } else if (l < -d) {
1172                        distanceX = -(l + d);
1173                        l = -d;
1174                    }
1175                }
1176               
1177                current.left = floor(l);
1178            }
1179           
1180            if (distanceY) {
1181                y = target.height() * s;
1182                h = current.parent.height(),
1183                t = original.top;
1184               
1185                if (y > h) {
1186                    t -= distanceY / s;
1187                }
1188               
1189                current.top = floor(t);
1190            }
1191           
1192            target.css({
1193                webkitTransitionDuration: "0s",
1194                webkitTransform: format(original.options.imageTransform, s, current.left, current.top)
1195            });
1196        }
1197       
1198        //table
1199        position.current = position.original - distanceX;
1200        table.css({
1201                webkitTransitionDuration: "0s",
1202                webkitTransform: format(original.options.transform, position.current)
1203        });
1204       
1205        return true;
1206    }
1207   
1208    /**
1209     *    End and clean up a drag event
1210     *
1211     *
1212     *
1213     *    @context The gallery element
1214     *
1215     *    @param Event event
1216     *    @return Boolean
1217     */
1218    function dragEnd (event) {
1219        var $this = $(this),
1220            original = $this.data("jqt-photo-event"),
1221            table = original.table,
1222            tablePosition = table.data("jqt-photo-position"),
1223            position = original.position,
1224            target = original.target,
1225            info = target.data("jqt-photo-info"),
1226            data = !!info && info[orientation] || false,
1227            current = data.current || false,
1228            slides = original.slides,
1229            h, x, y, t, d, n, w,
1230            s = current.scale;
1231       
1232        this.removeEventListener(events.move, original.options.drag, false);
1233        this.removeEventListener(events.end, original.options.dragEnd, false);
1234       
1235        if (!original.moved) {
1236            $this.trigger("jqt-photo-toggle-toolbars");
1237            return dispatchClick(event.target);
1238        }
1239       
1240        event.preventDefault();
1241       
1242        //image
1243        if (data) {
1244            y = target.height() * s;
1245            h = current.parent.height(),
1246            t = current.top;
1247           
1248            if (y > h) {
1249                d = ((y - h) / s) / 2;
1250               
1251                if (t > d) {
1252                    t = d;
1253                } else if (t < -d) {
1254                    t = -d;
1255                }
1256            }
1257           
1258            current.top = floor(t);
1259           
1260            target.css({
1261                webkitTransitionDuration: original.options.scaleSpeed + "ms",
1262                webkitTransform: format(original.options.imageTransform, s, current.left, current.top)
1263            });
1264        }
1265       
1266        d = (+new Date() - original.timeStamp);
1267       
1268        //table
1269        if (position.current > 0) {
1270            tablePosition.x = 0;
1271           
1272        } else if (position.current < (slides.eq(slides.length-1).width() - table.outerWidth())) {
1273            tablePosition.x = (slides.eq(slides.length-1).width() - table.outerWidth());
1274           
1275        } else {
1276            n = tablePosition.x < position.current ? -1 : 1;
1277            x = slides.filter("."+original.options.currentClass);
1278            t = abs(x.attr("offsetLeft") + position.current);
1279            w = x.width() / 3;
1280           
1281            if (t > w) {
1282                $this.triggerHandler("jqt-photo-slideto", [slides.index(x[0]) + n, (d > 1000 ? 1000 : d), original.options, slides]);
1283                return true;
1284            }
1285        }
1286       
1287        table.css({
1288            webkitTransitionDuration: (d > 1000 ? 1000 : d) + "ms",
1289            webkitTransform: format(original.options.transform, tablePosition.x)
1290        });
1291       
1292        return true;
1293    }
1294   
1295    /**
1296     *    Initialize a scaling event
1297     *
1298     *
1299     *    @context The gallery element
1300     *
1301     *    @param Event event
1302     *    @return Boolean
1303     */
1304    function scaleStart (event) {
1305        event.preventDefault();
1306        window.scrollTo(0,0);
1307       
1308        var $this = $(this),
1309            options = $this.data("jqt-photo-options"),
1310            tt = supportsTouch ? 
1311                    event.targetTouches : 
1312                    [
1313                        {pageX: controlPoints.x - event.pageX, pageY: controlPoints.y - event.pageY},
1314                        event
1315                    ];
1316       
1317        this.addEventListener(events.move, options.scale, false);
1318        this.addEventListener(events.end, options.scaleEnd, false);
1319       
1320        try{
1321            this.removeEventListener(events.move, options.drag, false);
1322            this.removeEventListener(events.end, options.dragEnd, false);
1323        }catch(e){}
1324       
1325        $this.data("jqt-photo-event", {
1326                target: $(event.target).closest(options.imageSelector).css("webkitTransitionDuration", "0s"),
1327                options: options,
1328                distance: sqrt(
1329                    pow((tt[1].pageX - tt[0].pageX), 2)
1330                    + pow((tt[1].pageY - tt[0].pageY), 2)
1331                )
1332            })
1333            .find(".image-list")
1334            .css("webkitTransitionDuration", "0s");//end any transform on this table
1335           
1336        return true;
1337    }
1338   
1339    /**
1340     *    Perform a scaling event
1341     *
1342     *
1343     *    @context The gallery element
1344     *
1345     *    @param Event event
1346     *    @return Boolean
1347     */
1348    function scale (event) {
1349        if (event.targetTouches.length != 2) {
1350            return true;
1351        }
1352       
1353        event.preventDefault();
1354       
1355        var $this = $(this),
1356            original = $this.data("jqt-photo-event"),
1357            target = original.target,
1358            info = target.data("jqt-photo-info"),
1359            data = !!info && info[orientation] || false,
1360            tt = event.targetTouches,
1361            distance = sqrt(
1362                pow((tt[1].pageX - tt[0].pageX), 2) +
1363                pow((tt[1].pageY - tt[0].pageY), 2)
1364            ),
1365            difference = distance - original.distance,
1366            percentChange = (difference / original.distance) / 2,
1367            current = data.current,
1368            transform;
1369       
1370        if (!current) {
1371            parseImageData($this, target, original.options);
1372            info = target.data("jqt-photo-info");
1373            data = !!info && info[orientation] || false;
1374            current = data.current;
1375        }
1376       
1377        $this.triggerHandler("jqt-photo-hide-toolbars", [original.options]);
1378       
1379        transform = format(original.options.imageTransform, 
1380                        (current.scale = (current.scale + (current.scale * percentChange))), 
1381                        current.left, 
1382                        current.top                       
1383                   );
1384       
1385        original.distance = distance;
1386           
1387        target.css({webkitTransitionDuration: "0s", webkitTransform: transform});
1388       
1389        return true;
1390    }
1391   
1392    /**
1393     *    End a scaling event
1394     *
1395     *
1396     *    @context The gallery element
1397     *
1398     *    @param Event event
1399     *    @return Boolean
1400     */
1401    function scaleEnd (event) {
1402        event.preventDefault();
1403       
1404        var $this = $(this),
1405            original = $this.data("jqt-photo-event"),
1406            info = original.target.data("jqt-photo-info"),
1407            data = !!info && info[orientation] || false,
1408            current = data.current;
1409       
1410        this.removeEventListener(events.move, original.options.scale, false);
1411        this.removeEventListener(events.end, original.options.scaleEnd, false);
1412       
1413        if (!current) {
1414            parseImageData($this, original.target, original.options);
1415            info = original.target.data("jqt-photo-info");
1416            data = !!info && info[orientation] || false;
1417            current = data.current;
1418        }
1419       
1420        if (current.scale < data.original.scale) {
1421            current.scale = data.original.scale;
1422            current.left = data.original.left;
1423            current.top = data.original.top;
1424        }
1425       
1426        original.target.css({
1427            webkitTransitionDuration: original.options.scaleSpeed + "ms",
1428            webkitTransform: format(original.options.imageTransform, current.scale, current.left, current.top)
1429        });
1430       
1431        return true;
1432    }
1433   
1434    /**
1435     *    Dispatch a click event on a given target, in the event
1436     *    a single-target touch event did not result in movement
1437     *
1438     *    @param Element target
1439     *    @return Boolean
1440     */
1441    function dispatchClick (target) {
1442        var theEvent = target.ownerDocument.createEvent("MouseEvents");
1443        theEvent.initEvent("click", true, true);
1444        target.dispatchEvent(theEvent);
1445        return true;
1446    }
1447   
1448    /**
1449     *    Begin a slideshow
1450     *
1451     *
1452     *    @context The gallery element
1453     *
1454     *    @param Event event
1455     *    @param Number index
1456     *    @return Boolean
1457     */
1458    function play (event, index) {
1459        event.preventDefault && event.preventDefault();
1460       
1461        var $this = $(this),
1462            options = $this.data("jqt-photo-options"),
1463            slides = $this.find(options.slideSelector),
1464            n = index === undefined || index < 0 
1465                ? Number(slides.filter("."+options.currentClass).attr(options.dataAttribute))
1466                : abs(index);
1467               
1468        addHover($this.addClass(options.playingClass), $this.find(options.pauseSelector)[0]);
1469       
1470        if (n != index) {
1471            $this.triggerHandler("jqt-photo-goto", [n, 0, options, slides]);
1472        }
1473       
1474        $this.data("jqt-photo-slide-timer", setInterval(function(){slideInterval($this, slides, options)}, options.slideDelay));
1475            //.triggerHandler("jqt-photo-hide-toolbars", [options]);
1476       
1477        return true;
1478    }
1479   
1480    /**
1481     *    Handler for slideshow intervals
1482     *
1483     *
1484     *
1485     *    @param jQuery target
1486     *    @param jQuery slides
1487     *    @param Object options
1488     *    @return null
1489     */
1490    function slideInterval (target, slides, options) {
1491        //current index + 1
1492        var index = Number(target.find(options.slideSelector).filter("."+options.currentClass).attr(options.dataAttribute)) + 1,
1493            func = "jqt-photo-slideto";//"slideTo";
1494       
1495        if (index === options.data.length) {
1496            if (!options.repeatSlideShow) {
1497                target.triggerHandler("jqt-photo-pause");
1498                return null;
1499            }
1500            index = 0;
1501            func = "jqt-photo-goto";//"goTo";
1502        }
1503       
1504        //options[func].call(target[0], {}, index, options.scrollSpeed, options);
1505        target.triggerHandler(func, [index, options.scrollSpeed, options, slides]);
1506       
1507        target.triggerHandler("jqt-photo-hide-toolbars", [options]);
1508       
1509        return null;
1510    }
1511   
1512    /**
1513     *    End a slideshow
1514     *
1515     *
1516     *    @context The gallery element
1517     *
1518     *    @param Event event
1519     *    @return Boolean
1520     */
1521    function pause (event) {
1522        event.preventDefault && event.preventDefault();
1523       
1524        var $this = $(this), 
1525            timer = $this.data("jqt-photo-slide-timer"), 
1526            options = $this.data("jqt-photo-options");
1527           
1528        addHover($this, $this.find(options.playSelector)[0]);
1529       
1530        clearInterval(timer);
1531        $this.removeClass(options.playingClass)
1532            .data("jqt-photo-slide-timer", null)
1533            .triggerHandler("jqt-photoshow-toolbars");
1534       
1535        return true;
1536    }
1537   
1538    /**
1539     *    Perform a slide to a given, or inferred next slide, using the data provided
1540     *
1541     *
1542     *
1543     *    @context The gallery element
1544     *
1545     *    @param Event event
1546     *    @param Number index
1547     *    @param Number duration
1548     *    @param Object options
1549     *    @param jQuery slides - optional;
1550     *    @return Boolean
1551     */
1552    function slideTo (event, index, duration, options, slides) {
1553        event.preventDefault && event.preventDefault();
1554       
1555        var $this = $(this), table, current, title, last, img;
1556           
1557        options = options || $this.data("jqt-photo-options");
1558       
1559        slides = slides || $this.find(options.slideSelector);
1560       
1561        table = options.table;
1562       
1563        index = Number(index || 0);
1564        //how long should this particular transition take?
1565        duration = duration === undefined || duration < 0 ? options.scrollSpeed : (Number(duration) || 0);
1566       
1567        if (index >= options.data.length) {
1568            $this.triggerHandler("jqt-photo-goto", [0, duration, options, slides]);
1569            return true;
1570        }
1571       
1572        if (index < 0) {
1573            index = options.data.length + index;
1574        }
1575       
1576        last = slides.filter("."+options.currentClass);
1577       
1578        //new current
1579        current = slides.removeClass(options.currentClass).filter(format("[{0}={1}]", options.dataAttribute, index));
1580       
1581        img = current.find(options.imageSelector);
1582       
1583        if (img.attr("src") != options.data[index].src) {
1584            img.parent().addClass(options.notLoadedClass);
1585            img.attr("src", options.data[index].src);
1586        }
1587       
1588        //new position
1589        position = -current.addClass(options.currentClass).attr("offsetLeft");
1590   
1591        if (position > 0) {
1592            position = 0;
1593        } else if (position < -(table.width()-current.width())) {
1594            position = -(table.width()-current.width());
1595        }
1596       
1597        //trigger slide change event
1598        $this.trigger("jqt-photo-slide-change", [index, duration, position]);
1599        //save the position
1600        table.data("jqt-photo-position").x = position;
1601        //atach animation end listener, and set necessary css properties
1602         table.one("webkitTransitionEnd", function() {
1603            options.rearrange(current, last, options);
1604        })
1605        .css({
1606            webkitTransitionDuration: duration+"ms",
1607            webkitTransform: format(options.transform, position)
1608        });
1609       
1610        //update the title of the gallery
1611        title = $this.find(options.titleSelector);
1612        title.html(format(options.galleryName || title.html(), Number(index) + 1, options.data.length));
1613       
1614        return true;
1615    }
1616   
1617    /**
1618     *    Jump to a given, or inferred next slide, using the data provided
1619     *
1620     *
1621     *
1622     *    @context The gallery element
1623     *
1624     *    @param Event event
1625     *    @param Number index    - optional; index of the slide to go to
1626     *    @param Number duration - optional; duration of the transition
1627     *    @param Object options - optional; the options associated with the gallery
1628     *    @param jQuery slides - optional;
1629     *    @return Boolean
1630     */
1631    function goTo (event, index, duration, options, slides) {
1632        event.preventDefault && event.preventDefault();
1633       
1634        var $this = $(this), table, current, title, last, img;
1635       
1636        options = options || $this.data("jqt-photo-options");
1637       
1638        slides = slides || $this.find(options.slideSelector);
1639       
1640        table = options.table;
1641       
1642        index = Number(index || 0);
1643        //how long should this particular transition take?
1644        duration = duration === undefined || duration < 0 ? options.scrollSpeed : (Number(duration) || 0);
1645       
1646        if (index >= options.data.length) {
1647            index = 0;
1648        }
1649       
1650        if (index < 0) {
1651            index = options.data.length + index;
1652        }
1653       
1654        last = slides.filter("."+options.currentClass);
1655       
1656        //new current
1657        current = slides.removeClass(options.currentClass).filter(format("[{0}={1}]", options.dataAttribute, index));
1658       
1659        img = current.find(options.imageSelector);
1660       
1661        if (img.attr("src") != options.data[index].src) {
1662            img.parent().addClass(options.notLoadedClass);
1663            img.attr("src", options.data[index].src);
1664        }
1665       
1666        //position of the new current
1667        position = -current.addClass(options.currentClass).attr("offsetLeft");
1668       
1669        if (position > 0) {
1670            position = 0;
1671        } else if (position < -(table.width()-current.width())) {
1672            position = -(table.width()-current.width());
1673        }
1674       
1675        //save the position
1676        table.data("jqt-photo-position").x = position;
1677        //reset scales after slide and set css to cause the slide
1678        table.css({
1679            webkitTransitionDuration: "0s",
1680            webkitTransform: format(options.transform, position)
1681        });
1682       
1683        //update the title
1684        //title element
1685        title = $this.find(options.titleSelector);
1686        title.html(format(options.galleryName || title.html(), Number(index) + 1, options.data.length));
1687       
1688        options.rearrange(current, last, options);
1689       
1690        return true;
1691    }
1692   
1693    /**
1694     *    Reassign the img tags with src attributes in order to maintain the
1695     *    memory consumption
1696     *
1697     *    @param jQuery current - the current slide
1698     *    @param jQuery last - the previous slide
1699     *    @param Object options - the gallery options
1700     *    @return Boolean
1701     */
1702    function rearrange (current, last, options) {
1703        var next = current.next()
1704            prev = current.prev();
1705           
1706        next.length && next.find(options.imageSelector).attr("src", options.data[next.attr(options.dataAttribute)].src);
1707           
1708        prev.length && prev.find(options.imageSelector).attr("src", options.data[prev.attr(options.dataAttribute)].src);
1709       
1710        current.prevAll().slice(options.maxSlidesBefore)
1711            .find(options.imageSelector)
1712            .filter("[src]")
1713            .each(function() {
1714                var clone = options.blankImage.clone(true),
1715                    $this = $(this);
1716               
1717                $this.parent().addClass(options.notLoadedClass);
1718                $this.replaceWith(clone);
1719               
1720                parseImageData($this, clone, options);
1721            });
1722           
1723        current.nextAll().slice(options.maxSlidesAfter)
1724            .find(options.imageSelector)
1725            .filter("[src]")
1726            .each(function() {
1727                var clone = options.blankImage.clone(true),
1728                    $this = $(this);
1729               
1730                $this.parent().addClass(options.notLoadedClass);
1731                $this.replaceWith(clone);
1732               
1733                parseImageData($this, clone, options);
1734            });
1735       
1736        if (last[0] !== current[0]) {
1737            resetDimensions(last.find(options.imageSelector));
1738        }
1739       
1740        return true;
1741    }
1742   
1743    /**
1744     *    Transition to previous slide (or last slide) in gallery
1745     *
1746     *
1747     *    @context The gallery element
1748     *
1749     *    @param Event event
1750     *    @return Boolean
1751     */
1752    function prev (event) {
1753        event.preventDefault && event.preventDefault();
1754       
1755        var $this = $(this),
1756            options = $this.data("jqt-photo-options"),
1757            slides = $this.find(options.slideSelector),
1758            index = Number(slides.filter("."+options.currentClass).attr(options.dataAttribute));
1759           
1760        addHover($this, event.target);
1761           
1762        $this.triggerHandler("jqt-photo-goto", [index - 1, 0, options, slides]);
1763       
1764        return true;
1765    }
1766   
1767    /**
1768     *    Transition to next slide (or first slide) in gallery
1769     *
1770     *
1771     *    @context The gallery element
1772     *
1773     *    @param Event event
1774     *    @return Boolean
1775     */
1776    function next (event) {
1777        event.preventDefault && event.preventDefault();
1778       
1779        var $this = $(this),
1780            options = $this.data("jqt-photo-options"),
1781            slides = $this.find(options.slideSelector),
1782            index = Number(slides.filter("."+options.currentClass).attr(options.dataAttribute));
1783           
1784        addHover($this, event.target);
1785           
1786        $this.triggerHandler("jqt-photo-goto", [index + 1, 0, options, slides]);
1787       
1788        return true;
1789    }
1790   
1791    /**
1792     *    Trigger an animation to slide up/slide down the toobars on a given gallery
1793     *    using a predetermined class
1794     *
1795     *
1796     *    @context The gallery element
1797     *
1798     *    @param Event event
1799     *    @param Object options
1800     *    @return null
1801     */
1802    function toggleToolbars (event, options) {
1803        if (event && event.target && event.target.tagName === "A") {
1804            return null;
1805        }
1806       
1807        var gallery = $(this);
1808       
1809        options = options || gallery.data("jqt-photo-options");
1810       
1811        if (gallery.hasClass(options.toolbarHideClass)) {
1812            gallery.triggerHandler("jqt-photo-show-toolbars", [options]);
1813           
1814        } else {
1815            gallery.triggerHandler("jqt-photo-hide-toolbars", [options]);
1816        }
1817        return null;
1818    }
1819   
1820    /**
1821     *    Trigger an animation to slide up the toobars on a given gallery
1822     *    using a predetermined class
1823     *
1824     *
1825     *    @context The gallery element
1826     *
1827     *    @param Event event
1828     *    @param Object options
1829     *    @return null
1830     */
1831    function hideToolbars (event, options) {
1832        var gallery = $(this);
1833       
1834        options = options || gallery.data("jqt-photo-options");
1835       
1836        if (!gallery.hasClass(options.toolbarHideClass)) {
1837            gallery.one("webkitTransitionEnd",function() {
1838                gallery.addClass(options.toolbarHideClass)
1839                    .removeClass(options.toolbarAnimationOutClass);
1840            })
1841            .addClass(options.toolbarAnimationOutClass);
1842        }
1843        return null;
1844    }
1845   
1846    /**
1847     *    Trigger an animation to slide down the toobars on a given gallery
1848     *    using a predetermined class
1849     *
1850     *
1851     *    @context The gallery element
1852     *
1853     *    @param Event event
1854     *    @param Object options
1855     *    @return null
1856     */
1857    function showToolbars (event, options) {
1858        var gallery = $(this);
1859       
1860        options = options || gallery.data("jqt-photo-options");
1861       
1862        if (gallery.hasClass(options.toolbarHideClass)) {
1863            gallery.one("webkitTransitionEnd",function() {
1864                gallery.removeClass(options.toolbarAnimationInClass);
1865            })
1866            .addClass(options.toolbarAnimationInClass)
1867            .removeClass(options.toolbarHideClass);
1868        }
1869        return null;
1870    }
1871   
1872    /**
1873     *    The jQTouch animation start event
1874     *
1875     *    @context The gallery element
1876     *
1877     *    @param jQuery.Event event
1878     *    @param Object info
1879     *    @return null
1880     */
1881    function pageAnimationStart (event, info) {
1882        if (info && info.direction == "in") {
1883            var $this = $(this),
1884                options = $this.data("jqt-photo-options"),
1885                slides = $this.find(options.slideSelector),
1886                index = Number(slides.filter("."+options.currentClass).attr(options.dataAttribute)),
1887                n = index > -1 ? index : (options.defaultIndex > -1 ? options.defaultIndex : 0);
1888                //n = (options.defaultIndex > -1 ? options.defaultIndex : 0);
1889                     
1890            if (slides.index("."+options.currentClass) != n) {
1891                $this.triggerHandler("jqt-photo-goto", [n, 0, options, slides]);
1892                $this.triggerHandler("jqt-photo-show-toolbars");
1893            }
1894        }
1895        return null;
1896    }
1897   
1898    /**
1899     *    The jQTouch animation end event
1900     *
1901     *    @context The gallery element
1902     *
1903     *    @param jQuery.Event event
1904     *    @param Object info
1905     *    @return null
1906     */
1907    function pageAnimationEnd (event, info) {
1908        var $this = $(this),
1909            options = $this.data("jqt-photo-options"),
1910            slides = $this.find(options.slideSelector),
1911            index,n;
1912            //n = (options.defaultIndex > -1 ? options.defaultIndex : 0);
1913               
1914        if (info && info.direction == "out") {
1915            $this.triggerHandler("jqt-photo-goto", [options.defaultIndex, 0, options, slides]);
1916            $this.triggerHandler("jqt-photo-show-toolbars");
1917        } else {
1918            index = Number(slides.filter("."+options.currentClass).attr(options.dataAttribute));
1919            n = index > -1 ? index : (options.defaultIndex > -1 ? options.defaultIndex : 0)
1920            $this.triggerHandler("jqt-photo-goto", [n, 0, options, slides]);
1921        }
1922        return null;
1923    }
1924   
1925    /**
1926     *    Create a slide for insertion using the provided information
1927     *
1928     *    @param Object data - the information about the slide
1929     *    @param Number index - index of the data in the list
1930     *    @param Object options - the information about the gallery
1931     *    @param Boolean inWindow - in the slide window
1932     */
1933    function createSlide (data, index, options, inWindow) {
1934        var slide = $(data.template || options.slideTemplate).attr(options.dataAttribute, index),
1935            img = slide.find(options.imageSelector);
1936       
1937        parseImageData(options.gallery, img.load(options.loader), options);
1938       
1939        if (inWindow) {
1940            img.attr("src", data.src);
1941        }
1942       
1943        if (data.caption) {
1944            slide.find(options.captionSelector).html(data.caption);
1945        }
1946                       
1947        return slide;               
1948    }
1949   
1950    /**
1951     *    Reset the scale and position of the given images
1952     *
1953     *    @param jQuery images
1954     *    @return jQuery
1955     */
1956    function resetDimensions (images) {
1957        return images.each(function(i) {
1958            var t = $(this),
1959                data = t.data("jqt-photo-info"),
1960                d = data[orientation];//,p;
1961           
1962            /*if (!d) {
1963                p = t.parent(defaults.gallerySelector);
1964                parseImageData(p, t, p.data("jqt-photo-options"));
1965                d = data[orientation];
1966            }*/
1967           
1968            //image should be resized?
1969            if (d && d.current.scale != 1 && d.current.scale != d.original.scale) {
1970                t.css({
1971                    webkitTransitionDuration: "0s", 
1972                    webkitTransform: format(data.galleryOptions.imageTransform, 
1973                                            d.current.scale = d.original.scale,
1974                                            d.current.left = d.original.left,
1975                                            d.current.top = d.original.top
1976                                     )
1977                });
1978               
1979                d.current.width = d.original.width;
1980                d.current.height = d.original.height;
1981            }
1982            return null;
1983        });
1984    }
1985   
1986    /**
1987     *    Add the hover class to the given element on touchstart
1988     *
1989     *    @param jQuery gallery
1990     *    @param DOMElement | jQuery element
1991     *    @return null
1992     */
1993    function addHover (gallery, element) {
1994        $(element).addClass("hover")
1995        gallery.one("touchend", function(){$(element).removeClass("hover")});
1996        return null;
1997    }
1998   
1999    /**
2000     *    Format a String followed by a set of arguments using the format
2001     *    {0} is replaced with arguments[1]
2002     *
2003     *    @param String s
2004     *    @param Object arg1 ... argN
2005     *    @return String
2006     */
2007    function format (s) {
2008        var args = arguments;
2009        return s.replace(/\{(\d+)\}/g, function(a,b){return args[Number(b)+1] + ""});
2010    }
2011   
2012    /**
2013     *    window.onorientationchange event handler updates all slide data for the new orientation
2014     *    (if it doesn't already exist) and resets the scale of the images
2015     *
2016     *    @return null
2017     */
2018    function onorientationchange () {
2019        orientation = abs(window.orientation) == 90 ? "landscape" : "portrait";
2020       
2021        //effectively this shouldn't be necessary, but it appears to be
2022        $("#jqt").removeClass("portrait landscape").addClass(orientation);
2023       
2024        //make sure the slides line up appropriately, use delay to ensure repaint has passed
2025        setTimeout(function(){
2026            var playing = [],
2027                loop = function() {
2028                    var $this = $(this), 
2029                        options = $this.data("jqt-photo-options"),
2030                        slides = $this.find(options.slideSelector),
2031                        images =  slides.find(options.imageSelector);
2032                   
2033                    //if ($this.is(":visible")) {
2034                        alignSlides($this, slides, options);
2035                    //}
2036                   
2037                    if ($this.hasClass(options.playingClass)) {
2038                        playing.push(this);
2039                        $this.triggerHandler("jqt-photo-pause");
2040                    }
2041                   
2042                    //if (!images.data("jqt-photo-info")[orientation]) {
2043                    //    parseImageData($this, images, defaults);
2044                    //}
2045                   
2046                    resetDimensions(images.filter("[src]"));
2047                };
2048           
2049            $(galleries).filter(":visible").each(loop).end().filter(":not(:visible)").each(loop);
2050           
2051            $(playing).triggerHandler("jqt-photo-play");
2052           
2053        }, 10);
2054           
2055        controlPoints = {x: window.innerWidth/2, y: window.innerHeight/2};
2056       
2057        return null;
2058    }
2059   
2060    /**
2061     *    Reset the transform after an orientation change
2062     *    to ensure that the slides are correctly aligned
2063     *
2064     *
2065     *
2066     *    @param jQuery galleries
2067     *    @return jQuery
2068     */
2069    function alignSlides ($this, slides, options) {
2070        table = $this.find(options.tableSelector),
2071        position = -slides.filter("."+options.currentClass).attr("offsetLeft");
2072               
2073        table.data("jqt-photo-position").x = position;
2074       
2075        table.css({
2076            webkitTransitionDuration: "0s",
2077            webkitTransform: format(options.transform, position)
2078        });
2079       
2080        return null;
2081    }
2082   
2083    /**
2084     *    image onload event handler
2085     *
2086     *    @context An IMG tag
2087     *
2088     *    @return null
2089     */
2090    function loader () {
2091        var innerWidth = window.innerWidth,
2092            innerHeight = window.innerHeight,
2093            $this = $(this),
2094            w = $this.width(),
2095            h = $this.height(),
2096            top, left,
2097            scale = min(min(w, innerWidth)/w, min(h, innerHeight)/h),
2098            width = w * scale,
2099            height = h * scale,
2100            data = $this.data("jqt-photo-info"),
2101            options = data.galleryOptions;
2102       
2103        $this.parent().removeClass(options.notLoadedClass);
2104       
2105        if (!$this.is(":visible")) {
2106            data.gallery.one("pageAnimationEnd", function(){parseImageData(data.gallery, $this, data.galleryOptions)});
2107           
2108        } else if (width > innerWidth || height > innerHeight) {
2109           
2110            left = -floor(((width - innerWidth) / scale) / 2);
2111           
2112            top = -floor(((height - innerHeight) / scale) / 2);
2113           
2114            $this.css({
2115                 webkitTransitionDuration: "0s",
2116                 webkitTransform: format(options.imageTransform, scale, left, top)
2117            })
2118            .addClass(options.presizedClass);
2119           
2120            data = data[orientation];
2121            data.current.scale = data.original.scale = scale;
2122            data.current.left = data.original.left = left;
2123            data.current.top = data.original.top = top;
2124            data.current.width = data.original.width = width * scale;
2125            data.current.height = data.original.height = height * scale;
2126        } 
2127       
2128        return null;
2129    }
2130   
2131    //load the css on dom ready
2132    $(function() {
2133        window.scrollTo(0,0);
2134        controlPoints = {x: window.innerWidth/2, y: window.innerHeight/2};
2135       
2136        if (defaults.useDynamicStyleSheet) {
2137            var stringRules = "", 
2138                rules = cssRules, 
2139                o = window.innerHeight > window.innerWidth ? "portrait" : "landscape",
2140                buildProperties = function(name, value) {
2141                    stringRules += name + ":" + ($.isFunction(value) ? value(rules.variables) : value) + ";";
2142                },
2143                buildRules = function(name, properties) {
2144                    stringRules += name + "{";
2145                   
2146                    $.each(properties, buildProperties);
2147                   
2148                    stringRules += "}";
2149                };
2150           
2151            $.each(rules.defaults, buildRules);
2152            $.each(rules[o], buildRules);
2153           
2154           
2155            $(document.createElement("style"))
2156                .attr({type:"text/css",media:"screen"})
2157                .html(stringRules)
2158                .appendTo("head");
2159           
2160            //make sure the css is correct on orientationchange
2161            $(window).one("orientationchange", function() {
2162                //ensure repaint
2163                //setTimeout(function() {
2164                    window.scrollTo(0,0);
2165                   
2166                    orientation = abs(window.orientation) == 90 ? "landscape" : "portrait";
2167       
2168                    //effectively this shouldn't be necessary, but it appears to be
2169                    $("#jqt").removeClass("portrait landscape").addClass(orientation);
2170                   
2171                    stringRules = "";
2172                   
2173                    $.each(rules[orientation], buildRules);
2174                   
2175                    $(document.createElement("style"))
2176                        .attr({type:"text/css",media:"screen"})
2177                        .html(stringRules)
2178                        .appendTo("head");
2179                //}, 10);
2180            });
2181        }
2182        //Orientation event listener bind
2183        $(window).bind("orientationchange", onorientationchange);
2184        //http://cubiq.org/iscroll-3-3-beta-2
2185        //use resize instead of orientationchange
2186        //$(window).bind("resize", onorientationchange);
2187    });
2188   
2189})(jQuery);
Note: See TracBrowser for help on using the repository browser.