﻿	function flvTracer(inVar) {
		// flvTracer is called by the FLV player (player.swf) on any event in the player.
		// for now we're only interested in the user starting to play a video
		if (inVar == "VIEW: PLAY ") {
        	STviewer.projectViewer.clearTimer(); // stop the rotation of image (if playing)
		}
	}


    var STviewer = {
        // viewer with the prime aim of showing images, and other media in a fixed area on the page

        
        // inspiration for handling caching and multimedia comes from Michael J.I. Jackson's Shadowbox.js http://www.mjijackson.com/
        
        
        
        /* version history
            001     Basic investigation, testing Shadowbox elements for preloading
            002     Use of the cache object to monitor loading and cache the images
        	003		???
			004		Modification to allow FLV playback using http://www.jeroenwijering.com/?item=JW_FLV_Media_Player
        
        */
                
        config: {
            displayTime: 500,
            displayArea: "#displayArea",
            autoplayMovies: false,
			showMovieControls: true,
			repeat: false,
			loopOnce: true,
            targetSearch: "a", // if not otherwise set, search for <a> tags - we're using jQuery $ searching
            flvPlayer:          '/global/player4.swf',
			assetURL: "",
            showcaseMode: false,
            cacheAllImages: false,
            fadeTime: "fast",
            loadingGraphic: '<img src="images/loading.gif" width="32" height="32" border="0" alt="loading" />'
        },
        status: {
            currentItem: null,
            playing: null,
			continuePlaying: true
        },
        cache: [],
        
        initFromJS: function (target, cfg) {
            this.utils.detectPlugins();
            this.utils.fileExtREinit();
            this.config = apply(this.config, cfg || {});


            
            if (this.config.showcaseMode) {
                // populate the cache
                //  - insert a reference to the cache in each found element        

                for(var element in this.config.images) {
                    this.config.images[element].cacheKey = STviewer.cacheElementJS(this.config.images[element]);
                };
                
                // load the first image into the page - it really should already be there
//                if (STviewer.cache != []) {
//                    STviewer.utils.imagePreload(STviewer.cache[0], "STviewer.showElement(STviewer.cache[0])" );
//                }
                this.startShowcase();
            } else {
                // projectViewer (pv) mode
            
                // populate the cache
                //for (var section in this.config.
            
            
                this.projectViewer.init();
                
                // hack for until refactoring
                STviewer.config.showcaseConfig = STviewer.projectViewer.config;
            
            }
            
            
        },
        init: function (target, cfg) {
            this.utils.detectPlugins();
            this.utils.fileExtREinit();
            this.config = apply(this.config, cfg || {});

            // populate the cache
            //  - insert a reference to the cache in each found element        
            $(target).find(this.config.targetSearch).each( function () {
                this.cacheKey = STviewer.cacheElement(this);
            });
            // - set an on click event - this really should be elsewhere, or set as an option
            $(target).find(this.config.targetSearch).click( function () {
                if (STviewer.cache[this.cacheKey].loaded) {
                    STviewer.showElement(STviewer.cache[this.cacheKey]);
                } else {
                    STviewer.utils.imagePreload(STviewer.cache[this.cacheKey], "STviewer.showElement(STviewer.cache[" + this.cacheKey + "])" );
                }
                return false;
               });
               
            // load the first image into the page - it really should already be there
//            log("STviewer.cache = " + STviewer.cache.length == 0);
            if (STviewer.cache != []) {
                STviewer.utils.imagePreload(STviewer.cache[0], "STviewer.showElement(STviewer.cache[0])" );
            }
            
            if (this.config.showcaseMode) {
                this.startShowcase();
            }
        },
        showElement: function (imgRef, liveImage) {
            liveImage = (typeof liveImage == "undefined" ? true : liveImage);
            
            if (imgRef.type == "img") {
                if (!imgRef.elementDisplayed) {
                    $(imgRef.imageLayer).html(
                        '<img src="' + imgRef.href + '" height="' + imgRef.imgHeight + '" width="' + imgRef.imgWidth + '" border="0" ' +
                            (imgRef.element.imageCaption + imgRef.element.imageCopyright == "" ? 
                                '' : 'title="' + (imgRef.element.imageCaption == "" ? '' : '' + imgRef.element.imageCaption + ' ') + 
                                (imgRef.element.imageCopyright == "" ? '' : '(c) ' + imgRef.element.imageCopyright)
                                + '"') + ' />'
                    );
                    if (imgRef.element.imageLink) {
                        $(imgRef.imageLayer).html('<a href="#">' + $(imgRef.imageLayer).html() + '</a>');
                        $(imgRef.imageLayer).find("a")[0].imageLink = imgRef.element.imageLink;
                        $(imgRef.imageLayer).find("a").click( function () {
                            window.open(this.imageLink, "_self");
                        });
                    }
                    
                    /* + (typeof this.config.displayInfo == "function" ? this.config.displayInfo(imgRef.element) : "")  */
                    imgRef.elementDisplayed = true;
                 
                    if (this.config.showcaseMode) {
                        this.showcaseViewer.maxDimensions({height: imgRef.imgHeight, width: imgRef.imgWidth});
                    } else {          
                    }
                 
                }
            } else {
//              log("showElement: not an image" + !liveImage);
                if (liveImage) {
                    $(imgRef.imageLayer).html(
                       STviewer.utils.createHTML(STviewer.utils.movieMarkup(imgRef))
                    );
                } // else do nothing
            
            }
            /*            */
            this.status.currentItem =  imgRef.element.cacheKey;
        },
        
        cacheElementPV: function (obj) {
            var o = {
                element:    obj,
                title:      obj.title,
                href:       obj.imageLocation,
                rel:        null,
                group:      obj.parent.sectionName,
                type:       this.getPlayerType(obj.imageLocation),
                loaded:     null,
                imgObj:     null
            }
            o.element.cacheKey = this.cache.length;
            this.cache.push(o);
            return this.cache.length-1;

            
        },
        cacheElementJS: function (obj) {
            var o = {
                element:    obj,
                title:      obj.title,
                href:       obj.imageLocation,
                rel:        null,
                group:      null,
                type:       this.getPlayerType(obj.imageLocation),
                loaded:     null,
                imgObj:     null
            }
            this.cache.push(o);
            return this.cache.length-1;

            
        },
        cacheElement: function (obj) {
            var o = {
                element:    obj,
                title:      obj.title,
                href:       obj.href,
                rel:        obj.rel,
                group:      this.getGroup(obj.rel),
                type:       this.getPlayerType(obj.href),
                loaded:     null,
                imgObj:     null
            }
            this.cache.push(o);
            return this.cache.length-1;
        },
        getGroup: function (str) {
            return str;
        },
        fadeableImage: {
            img: true,
            inline: true,
            swf: false,
            flv: false,
            qt: false,
            wmp: false,
            iframe: true    
        },
        isFadeableImage: function (inImage) {
            // using fadeableImage object above, used to catch any errors
            // first see if we've been sent an image object 
            if (typeof inImage == "object" && typeof inImage.type == "string") inImage = inImage.type;
            return ((typeof this.fadeableImage[inImage]) == "boolean" ? this.fadeableImage[inImage] : false);
        },
        getPlayerType: function (url) {
            // copied wholesale from Shadowbox
            // except plugins refs changed to this.utils.plugins
            // and regex (RE) refs changed to this.utils.RE
            if(RE.img.test(url)) return 'img';
            var match = url.match(this.utils.RE.domain);
            var this_domain = match ? document.domain == match[1] : false;
            if(url.indexOf('#') > -1 && this_domain) return 'inline';
            var q_index = url.indexOf('?');
            if(q_index > -1) url = url.substring(0, q_index); // strip query string for player detection purposes
            if(this.utils.RE.swf.test(url)) return this.utils.plugins.fla ? 'swf' : 'unsupported-swf';
            if(this.utils.RE.flv.test(url)) return this.utils.plugins.fla ? 'flv' : 'unsupported-flv';
            if(this.utils.RE.qt.test(url)) return this.utils.plugins.qt ? 'qt' : 'unsupported-qt';
            if(this.utils.RE.wmp.test(url)){
                if(this.utils.plugins.wmp){
                    return 'wmp';
                }else if(this.utils.plugins.f4m){
                    return 'qt';
                }else{
                    return isMac ? (this.utils.plugins.qt ? 'unsupported-f4m' : 'unsupported-qtf4m') : 'unsupported-wmp';
                }
            }else if(this.utils.RE.qtwmp.test(url)){
                if(this.utils.plugins.qt){
                    return 'qt';
                }else if(this.utils.plugins.wmp){
                    return 'wmp';
                }else{
                    return isMac ? 'unsupported-qt' : 'unsupported-qtwmp';
                }
            }else if(!this_domain || this.utils.RE.iframe.test(url)){
                return 'iframe';
            }
            return 'unsupported';
        },
        utils: {        
            imagePreload: function (obj, callback) {
                if (obj.loaded) return;
                
                if (obj.type == "img") {
                    
                    obj.imgObj = new Image();
                    obj.imgObj.callback = callback;
                    obj.imgObj.onload = function(){
                        // images default to image height and width
                        /*
                        var h = obj.height ? parseInt(obj.height, 10) : preloader.height;
                        var w = obj.width ? parseInt(obj.width, 10) : preloader.width;
                        */
                        obj.imgHeight = obj.height ? parseInt(obj.height, 10) : obj.imgObj.height;
                        obj.imgWidth = obj.width ? parseInt(obj.width, 10) : obj.imgObj.width;
    /*                    resizeContent(h, w, function(dims){
                            showBars(function(){
                                setContent({
                                    tag:    'img',
                                    height: dims.i_height,
                                    width:  dims.i_width,
                                    src:    obj.content,
                                    style:  'position:absolute'
                                });
                                if(dims.enableDrag && options.handleLgImages == 'drag'){
                                    // listen for drag
                                    toggleDrag(true);
                                    SL.setStyle(SL.get('shadowbox_drag_layer'), {
                                        height:     dims.i_height + 'px',
                                        width:      dims.i_width + 'px'
                                    });
                                }
                                finishContent();
                            });
                        });
                            */
                        obj.loaded = true;
                        if (typeof this.callback == "string") {
                            eval(this.callback)
                        } else if (typeof this.callback == "function") {
                            this.callback();
                        }
                        
                        obj.imgObj.onload = function(){}; // clear onload for IE
                        
                        if (STviewer.config.cacheAllImages) {
                            // loop through images - chosen so nothing is missed out
                            for (var cacheCounter = 0; cacheCounter < STviewer.cache.length; cacheCounter++) {
                                if (!STviewer.cache[cacheCounter].loaded) {
//                                    log("okay, I want to preload image " + cacheCounter + " next");
                                    STviewer.utils.imagePreload(STviewer.cache[cacheCounter], 'STviewer.showElement(STviewer.cache['+cacheCounter+'], false);');
                                    break;
                                }
                            }
                            
                        }
                        
                    }    
//                log("image preload starting");
                obj.imgObj.src = obj.href;
                        
                        
                } else {
                    // for now everything other than an image is not preloaded
                    obj.loaded = true;
                    
                    if (typeof callback == "string") {
                        eval(callback)
                    } else if (typeof callback == "function") {
                        callback();
                    } else {
                    

                    }
                }
            },
            preloadNextImage: function () {
//                log("preloadNextImage");
            },
            fileExtensions: {
                    img:        ['png', 'jpg', 'jpeg', 'gif', 'bmp'],
                    qt:         ['dv', 'mov', 'moov', 'movie', 'mp4'],
                    wmp:        ['asf', 'wm', 'wmv'],
                    qtwmp:      ['avi', 'mpg', 'mpeg'],
                    iframe:     ['asp', 'aspx', 'cgi', 'cfm', 'htm', 'html', 'pl', 'php',
                                'php3', 'php4', 'php5', 'phtml', 'rb', 'rhtml', 'shtml',
                                'txt', 'vbs']
                },
            fileExtREinit: function () {
                for (var fileType in this.fileExtensions) {
                    this.RE[fileType] = new RegExp('\.(' + this.fileExtensions[fileType].join('|') + ')\s*$', 'i');
                }
            },
            RE: {
                resize:         /(img|swf|flv)/, // file types to resize
                overlay:        /(img|iframe|html|inline)/, // content types to not use an overlay image for on FF Mac
                swf:            /\.swf\s*$/i, // swf file extension
                flv:            /\.flv\s*$/i, // flv file extension
                domain:         /:\/\/(.*?)[:\/]/, // domain prefix
                inline:         /#(.+)$/, // inline element id
                rel:            /^(light|shadow)box/i, // rel attribute format
                gallery:        /^(light|shadow)box\[(.*?)\]/i, // rel attribute format for gallery link
                unsupported:    /^unsupported-(\w+)/, // unsupported media type
                param:          /\s*([a-z_]*?)\s*=\s*(.+)\s*/, // rel string parameter
                empty:          /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i // elements that don't have children
            },
                    
                    
            /**
             * Generates the markup necessary to embed the movie file with the given
             * link element. This markup will be browser-specific. Useful for generating
             * the media test suite.
             *
             * @param   {HTMLElement}   link        The link to the media file
             * @return  {Object}                    The proper markup to use (see above)
             * @public
             * @static
             */
            movieMarkup: function(obj){
            
                // movies default to 300x300 pixels
                var h = obj.height ? parseInt(obj.height, 10) : 300;
                var w = obj.width ? parseInt(obj.width, 10) : 300;
				
				/* sept08 */
                var h = obj.element.imageHeight ? parseInt(obj.element.imageHeight, 10) : 300;
                var w = obj.element.imageWidth ? parseInt(obj.element.imageWidth, 10) : 300;

                var autoplay = STviewer.config.autoplayMovies;
                var controls = STviewer.config.showMovieControls;
                if(obj.options){
                    if(obj.options.autoplayMovies != null){
                        autoplay = obj.options.autoplayMovies;
                    }
                    if(obj.options.showMovieControls != null){
                        controls = obj.options.showMovieControls;
                    }
                }

                var markup = {
                    tag:    'object',
                    name:   'shadowbox_content'
                };

                switch(obj.type){
                    case 'swf':
                          // dimensions will have to be handled at some point
                    
 //                       var dims = getDimensions(h, w, true);
  //                      h = dims.height;
//                        w = dims.width;
                        markup.type = 'application/x-shockwave-flash';
                        markup.data = obj.href;
                        markup.children = [
 //                           { tag: 'param', name: 'movie', value: obj.content }
                            
                            { tag: 'param', name: 'movie', value: obj.href }
                        ];
                    break;
                    case 'flv':
                        autoplay = autoplay ? 'true' : 'false';
                        var showicons = 'false';
                        var a = h/w; // aspect ratio
                        if(controls){
                            showicons = 'true';
                            h += 20; // height of JW FLV player controller
                        }
//                        var dims = getDimensions(h, h/a, true); // resize
//                        h = dims.height;
//                        w = (h-(controls?20:0))/a; // maintain aspect ratio
                        var flashvars = [
                            'file=' + obj.href,
                            'height=' + h,
                            'width=' + w,
                            'autostart=' + autoplay,
                            'displayheight=' + (h - (controls?20:0)),/**/
                            'showicons=' + showicons,/**/
                            'backcolor=0x000000&amp;frontcolor=0xCCCCCC&amp;lightcolor=0x557722',
							'image=' + obj.href + '.jpg',
							'tracecall=flvTracer',
							'controlbar=bottom'/**/
                        ];
                        markup.type = 'application/x-shockwave-flash';
                        markup.data = STviewer.config.assetURL + STviewer.config.flvPlayer;
						markup.id = "flobject1"; // required by IE when handling flvplayer within a form
                        markup.children = [
                            { tag: 'param', name: 'movie', value: STviewer.config.assetURL + STviewer.config.flvPlayer },
                            { tag: 'param', name: 'flashvars', value: flashvars.join('&') },
                            { tag: 'param', name: 'fullscreen', value: 'true' }
                        ];
                    break;
                    case 'qt':
                        autoplay = autoplay ? 'true' : 'false';
                        if(controls){
                            controls = 'true';
                            h += 16; // height of QuickTime controller
                        }else{
                            controls = 'false';
                        }
                        markup.children = [
                            { tag: 'param', name: 'src', value: obj.content },
                            { tag: 'param', name: 'scale', value: 'aspect' },
                            { tag: 'param', name: 'controller', value: controls },
                            { tag: 'param', name: 'autoplay', value: autoplay }
                        ];
                        if(isIE){
                            markup.classid = 'clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B';
                            markup.codebase = 'http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0';
                        }else{
                            markup.type = 'video/quicktime';
                            markup.data = obj.content;
                        }
                    break;
                    case 'wmp':
                        autoplay = autoplay ? 1 : 0;
                        markup.children = [
                            { tag: 'param', name: 'autostart', value: autoplay }
                        ];
                        if(isIE){
                            if(controls){
                                controls = 'full';
                                h += 70; // height of WMP controller in IE
                            }else{
                                controls = 'none';
                            }
                            // markup.type = 'application/x-oleobject';
                            markup.classid = 'clsid:6BF52A52-394A-11d3-B153-00C04F79FAA6';
                            markup.children[markup.children.length] = { tag: 'param', name: 'url', value: obj.content };
                            markup.children[markup.children.length] = { tag: 'param', name: 'uimode', value: controls };
                        }else{
                            if(controls){
                                controls = 1;
                                h += 45; // height of WMP controller in non-IE
                            }else{
                                controls = 0;
                            }
                            markup.type = 'video/x-ms-wmv';
                            markup.data = obj.content;
                            markup.children[markup.children.length] = { tag: 'param', name: 'showcontrols', value: controls };
                        }
                    break;
                }

                markup.height = h; // new height includes controller
                markup.width = w;


                return markup;
            },

            /**
             * Creates an HTML string from an object representing HTML elements. Based
             * on Ext.DomHelper's createHtml.
             *
             * @param   {Object}    obj     The HTML definition object
             * @return  {String}            An HTML string
             * @public
             * @static
             */
            createHTML: function(obj){
                var html = '<' + obj.tag;
                for(var attr in obj){
                    if(attr == 'tag' || attr == 'html' || attr == 'children') continue;
                    if(attr == 'cls'){
                        html += ' class="' + obj['cls'] + '"';
                    }else{
                        html += ' ' + attr + '="' + obj[attr] + '"';
                    }
                }
                if(RE.empty.test(obj.tag)){
                    html += '/>\n';
                }else{
                    html += '>\n';
                    var cn = obj.children;
                    if(cn){
                        for(var i = 0, len = cn.length; i < len; ++i){
                            html += this.createHTML(cn[i]);
                        }
                    }
                    if(obj.html) html += obj.html;
                    html += '</' + obj.tag + '>\n';
                }
                return html;
            },
                    
            /**
             * Contains plugin support information. Each property of this object is a
             * boolean indicating whether that plugin is supported.
             *
             * - fla: Flash player
             * - qt: QuickTime player
             * - wmp: Windows Media player
             * - f4m: Flip4Mac plugin
             *
             * @property    {Object}    plugins
             * @private
             */
            plugins: null,

            // detect plugin support
            detectPlugins: function () {
                if(navigator.plugins && navigator.plugins.length){
                    var detectPlugin = function(plugin_name){
                        var detected = false;
                        for (var i = 0, len = navigator.plugins.length; i < len; ++i){
                            if(navigator.plugins[i].name.indexOf(plugin_name) > -1){
                                detected = true;
                                break;
                            }
                        }
                        return detected;
                    };
                    var f4m = detectPlugin('Flip4Mac');
                    this.plugins = {
                        fla:    detectPlugin('Shockwave Flash'),
                        qt:     detectPlugin('QuickTime'),
                        wmp:    !f4m && detectPlugin('Windows Media'), // if it's Flip4Mac, it's not really WMP
                        f4m:    f4m
                    };
                }else{
                    var detectPlugin = function(plugin_name){
                        var detected = false;
                        try {
                            var axo = new ActiveXObject(plugin_name);
                            if(axo){
                                detected = true;
                            }
                        } catch (e) {}
                        return detected;
                    };
                    this.plugins = {
                        fla:    detectPlugin('ShockwaveFlash.ShockwaveFlash'),
                        qt:     detectPlugin('QuickTime.QuickTime'),
                        wmp:    detectPlugin('wmplayer.ocx'),
                        f4m:    false
                    };
                }
            }
        }
    
    }

    // quick hack to ensure Shadowbox regexes and plugin values are redirected to STviewer's versions
    var RE = STviewer.utils.RE;
    var plugins = STviewer.utils.plugins;

    jQuery.prototype.initSTviewer = function (cfg) {
        STviewer.init(this, cfg);
    }
    jQuery.prototype.initSTviewerJS = function (cfg) {
        STviewer.initFromJS(this, cfg);
    }
    
    
    STviewer.currentItem = null;

    STviewer.startShowcase = function () {
        this.showcaseViewer.init();    
    }
    
    STviewer.showcaseViewer = {
        layerZindexes: {
            display: 100,
            fadeOut: 101,
            hide: 99
        },
        maxHeight: 0,
        maxDimensions: function (dims) {
            // for now we're taking the height of the latest image and comparing it to the height set in the ul
            // if it's larger, then we change the height in the styles
//log ("maxDimensions: " + dims.height + " displayArea>ul: " + $(STviewer.config.displayArea).find("ul").css("height"));
            
            if (dims.height > this.maxHeight) {

                $(STviewer.config.displayArea).css("height", dims.height + "px").find("ul.liveShowcase").css("height", dims.height + "px").find("li").css("height", dims.height + "px").find(".displayInfo").css("height", dims.height + "px");
                this.maxHeight = dims.height;
                return true;
            }
            return false;
        },
        displayImage: function (nextImage) {
            // exit if we're trying to load the same image
            if (this.currentItem == nextImage.element.cacheKey) {
//                log("showcaseViewer.displayImage: exiting");
                return false;
            }
            
            /*  currentItem    to  nextImage
                fadeable        to  fadeable        - fadedown only
                fadeable        to  non-fadeable    - fadedown only - but delay start
                non-fadeable    to  fadeable        - fadeup        - instant stop
                non-fadeable    to  non-fadeable    - none#
                
            */
            var layerZindexes = {
                display: 100,
                fadeOut: 101,
                hide: 99
                // NOTE: it would be nice to have all the movements and configuration in here
            };
            
            
            var currentRef = STviewer.cache[this.currentItem];
            
            STviewer.showElement(nextImage);
            if (STviewer.isFadeableImage(currentRef)) {
                // move the current image to the top level
                $(currentRef.imageLayer).css("z-index", layerZindexes.fadeOut);
                
                if (STviewer.isFadeableImage(nextImage)) {
                    // ensure it is hidden, but ready to display
                    $(nextImage.imageLayer).css("z-index", layerZindexes.display).show(0);
                    
                    $(currentRef.imageLayer).css("z-index", layerZindexes.fadeOut);
                    $(currentRef.imageLayer).fadeOut(STviewer.config.fadeTime, function () {
                        $(this).css("z-index", STviewer.showcaseViewer.layerZindexes.hide);
                    });

                } else {
                
                    $(nextImage.imageLayer).css("z-index", layerZindexes.display).hide(0);
                    $(currentRef.imageLayer).css("z-index", layerZindexes.fadeOut).fadeOut(STviewer.config.fadeTime, function(){
                        // this is a callback, by now currentItem is what nextImage was
                        $(this).css("z-index", layerZindexes.hide);
                        $(STviewer.cache[STviewer.showcaseViewer.currentItem].imageLayer).show(0);
                    });
                }
            
            } else {
                if (STviewer.isFadeableImage(nextImage)) {
                    // hide the current one (if it exists) and clear the contents
                    if (this.currentItem != undefined) {
                        $(currentRef.imageLayer).css("z-index", layerZindexes.hide).html("");
                    }
                    $(nextImage.imageLayer).css("z-index", layerZindexes.display).hide(0).fadeIn(STviewer.config.fadeTime);
                    
                } else {
                    if (this.currentItem != undefined) {
                        $(currentRef.imageLayer).css("z-index", layerZindexes.hide).html("");
                    }
                    $(nextImage.imageLayer).css("z-index", layerZindexes.display).show(0);
                }
                
            }
            this.setCurrent(nextImage);
        
        },
        setCurrent: function (inItem) {
            if ((typeof inItem) == "integer" || (typeof inItem == "number")) {
                this.currentItem = inItem;
            } else if ((typeof inItem) == "object") {
                this.currentItem = inItem.element.cacheKey;
            } else {
//                log("showcaseViewer.setCurrent failsafe setting this.currentItem to 0 - inItem (" + (typeof inItem) + ") = " + inItem);
                this.currentItem = 0;
            }
        },
        pause: function () {
            // elementary pause
            self.clearInterval(this.thisTimer);
        
        },
        clearTimer: function () {
//            log(typeof this.thisTimer);
            self.clearInterval(this.thisTimer);
        },
        resume: function () {
            // elementary resume
            this.nextTimer();
        },
        timerEvent: function () {
            this.showNextImage({});
            this.nextTimer();        
        },
        nextTimer: function (altTime) {
//            log("nextTimer");
            var nextTimeout = (isNaN(parseInt(altTime)) ? STviewer.config.displayTime : parseInt(altTime));
            this.thisTimer = self.setTimeout('STviewer.showcaseViewer.timerEvent()', nextTimeout);


        },
        thisTimer: null,

        
        previousNextImageNo: function(inRef, inDirection, options) {
            var updownModifier = (inDirection == "previous" ? -1 : +1);
        
            var thisItem, nextItemNo, lookForInRotation, exitLoop, loopcount;
            if ((typeof inRef) == "integer" || (typeof inRef == "number")) {
                thisItem = inRef;
            } else if ((typeof inItem) == "object") {
                thisItem = inItem.element.cacheKey;
            } else return false;
            lookForInRotation = (options && options.inRotation);
            exitLoop = !lookForInRotation;
            loopcount = 0;
            nextItemNo = thisItem;
            do {
                nextItemNo += updownModifier;
                if (nextItemNo < 0) nextItemNo = STviewer.cache.length-1
                else if (nextItemNo >= STviewer.cache.length) nextItemNo = 0;

                if (loopcount++ > 100) {
                    exitLoop = true;
                    nextItemNo = 0;
                }
            } while (!exitLoop);
            return nextItemNo;        
        },
        
        showNextImage: function(options) {
            this.displayImage(STviewer.cache[this.previousNextImageNo(this.currentItem, "next", options)]);
        },
        showPreviousImage: function(options) {
            this.displayImage(STviewer.cache[this.previousNextImageNo(this.currentItem, "previous", options)]);
        },
        
        init: function (cfg) {
            // create HTML for the list of items
            
            // clear the existing image
            var displayList =  $(STviewer.config.displayArea).find("ul")
            $(displayList).html("");
                   
            for (var imageNo in STviewer.config.images) {
                var image = STviewer.config.images[imageNo];
                                
                $(STviewer.config.displayArea).find("ul").append("<li class=\"imageHolder\">" + STviewer.config.loadingGraphic + "</li>");
                var latestItem = $("li.imageHolder:last")[0];
                latestItem.cacheKey = imageNo;
                STviewer.cache[imageNo].imageLayer = latestItem;
            }                            
            $("li.imageHolder").hide(0);
            
            if (STviewer.config.images.length > 1) {
                STviewer.utils.imagePreload(STviewer.cache[0], "STviewer.showcaseViewer.displayImage(STviewer.cache[0]); STviewer.showcaseViewer.nextTimer();" );
            } else { // don't start the timer if we've only got one image
                STviewer.utils.imagePreload(STviewer.cache[0], "STviewer.showcaseViewer.displayImage(STviewer.cache[0]);" );
            }
/*            $(STviewer.config.displayArea).mouseover( function () {
                $(STviewer.config.displayInfoClass).show(0);
                $(STviewer.config.displayInfoBG).fadeTo(0.05, STviewer.config.displayInfoOpacity);
            });
            $(STviewer.config.displayArea).mouseout( function () {
                $(STviewer.config.displayInfoClass).hide(0);
            
            $(STviewer.config.displayArea).find("ul>li").click( function () {
                $(this).find(STviewer.config.displayInfoClass).toggle(0);
            });
            });*/
//            this.nextTimer();
        }
    
    }



    STviewer.projectViewer = {
		pvStatus: {
			continuePlaying: true
		},
        layerZindexes: {
            display: 100,
            fadeOut: 101,
            hide: 99
        },
        displayImage: function (nextImage) {
            // exit if we're trying to load the same image
            if (this.currentItem == nextImage.element.cacheKey) return false;
            
            /*  currentItem    to  nextImage
                fadeable        to  fadeable        - fadedown only
                fadeable        to  non-fadeable    - fadedown only - but delay start
                non-fadeable    to  fadeable        - fadeup        - instant stop
                non-fadeable    to  non-fadeable    - none#
                
            */
            var layerZindexes = {
                display: 100,
                fadeOut: 101,
                hide: 99
                // NOTE: it would be nice to have all the movements and configuration in here
            };
            
            var currentRef = STviewer.cache[this.currentItem];
            STviewer.showElement(nextImage);
            if (STviewer.isFadeableImage(currentRef)) {
                // move the current image to the top level
                $(currentRef.imageLayer).css("z-index", layerZindexes.fadeOut);
                
                if (STviewer.isFadeableImage(nextImage)) {
                    // ensure it is hidden, but ready to display
                    $(nextImage.imageLayer).css("z-index", layerZindexes.display).show(0);
                    
                    $(currentRef.imageLayer).css("z-index", layerZindexes.fadeOut);
                    $(currentRef.imageLayer).fadeOut(STviewer.config.fadeTime, function () {
                        $(this).css("z-index", STviewer.projectViewer.layerZindexes.hide);
                    });

                } else {
                
                    $(nextImage.imageLayer).css("z-index", layerZindexes.display).hide(0);
                    $(currentRef.imageLayer).css("z-index", layerZindexes.fadeOut).fadeOut(STviewer.config.fadeTime, function(){
                        // this is a callback, by now currentItem is what nextImage was
                        $(this).css("z-index", layerZindexes.hide);
                        $(STviewer.cache[STviewer.projectViewer.currentItem].imageLayer).show(0);
                    });
                }
            
            } else {
                if (STviewer.isFadeableImage(nextImage)) {
                    // hide the current one (if it exists) and clear the contents
                    if (this.currentItem != undefined) {
                        $(currentRef.imageLayer).css("z-index", layerZindexes.hide).html("");
                    }
                    $(nextImage.imageLayer).css("z-index", layerZindexes.display).hide(0).fadeIn(STviewer.config.fadeTime);
                    
                } else {
                    if (this.currentItem != undefined) {
                        $(currentRef.imageLayer).css("z-index", layerZindexes.hide).html("");
                    }
                    $(nextImage.imageLayer).css("z-index", layerZindexes.display).show(0);
                }
                
            }
            this.setCurrent(nextImage);
        
        },
        
        setCurrent: function (inItem) {
            var navObjs = $("#pvNavArea>ul>li>ul>li");
            $(navObjs[this.currentItem]).removeClass("selected").parent().parent().removeClass("selected");
            if ((typeof inItem) == "integer" || (typeof inItem == "number")) {
                this.currentItem = inItem;
            } else if ((typeof inItem) == "object") {
                this.currentItem = inItem.element.cacheKey;
            }
            $(navObjs[this.currentItem]).addClass("selected").parent().parent().addClass("selected");
            
            // set the navigation bar too
//            log( $(navObjs[this.currentItem]).offset().left);
            $("#pvNavArea>#horizontalBar>p").css("width",  ($(navObjs[this.currentItem]).offset().left - $("#horizontalBar").offset().left + 3) + "px");          
        },
        pause: function () {
            // elementary pause
            self.clearInterval(this.thisTimer);
        
        },
        clearTimer: function () {
//			log("loop end clearTimer");
            self.clearInterval(this.thisTimer);
        },
        resume: function () {
            // elementary resume
            this.nextTimer();
        },
        timerEvent: function () {   
            this.showNextImage({inRotation: true});
            this.nextTimer(); //if (!this.stopTimerFlag) this.nextTimer();     
        },
        nextTimer: function (altTime) {
//				log("loop end nextTimer");
			if (this.pvStatus.continuePlaying) {
				var nextTimeout = (isNaN(parseInt(altTime)) ? STviewer.config.displayTime : parseInt(altTime));
				this.thisTimer = self.setTimeout('STviewer.projectViewer.timerEvent()', nextTimeout);
			}


        },
		stopTimerFlag: false,
        thisTimer: null,
		repeatLoop: function () {
			if (!STviewer.config.repeat) {
				log("loop end loop");
				this.stopTimerFlag = true;
			} else {
				log("loop do nothing");	
			}
		},
        
        previousNextImageNo: function(inRef, inDirection, options) {
            var updownModifier = (inDirection == "previous" ? -1 : +1);
        
            var thisItem, nextItemNo, lookForInRotation, exitLoop, loopcount;
            if ((typeof inRef) == "integer" || (typeof inRef == "number")) {
                thisItem = inRef;
            } else if ((typeof inItem) == "object") {
                thisItem = inItem.element.cacheKey;
            } else return false;
            lookForInRotation = (options && options.inRotation);
            exitLoop = !lookForInRotation;
            loopcount = 0;
            nextItemNo = thisItem;
			flagRepeatLoop = false;
            do {
                nextItemNo += updownModifier;
                if (nextItemNo < 0) {
					nextItemNo = STviewer.cache.length-1;
					this.pvStatus.continuePlaying = !STviewer.config.loopOnce;
				}
                else if (nextItemNo >= STviewer.cache.length) {
					nextItemNo = 0;
					this.pvStatus.continuePlaying = !STviewer.config.loopOnce;
				}
                
                if (lookForInRotation) {
                    
                    if (STviewer.cache[nextItemNo].element.parent.inRotation) exitLoop = true;
                    if (nextItemNo == thisItem) {
                        exitLoop = true;
                        nextItemNo = 0;
						
//						flagRepeatLoop = true;
                    }
                }
                if (loopcount++ > 100) {
                    exitLoop = true;
                    nextItemNo = 0;
					
//						flagRepeatLoop = true;
                }
            } while (!exitLoop);
			if (flagRepeatLoop) this.repeatLoop();
            return nextItemNo;        
        },
        
        showNextImage: function(options) {
            this.displayImage(STviewer.cache[this.previousNextImageNo(this.currentItem, "next", options)]);
        },
        showPreviousImage: function(options) {
            this.displayImage(STviewer.cache[this.previousNextImageNo(this.currentItem, "previous", options)]);
        },
        
        init: function (cfg) {
            // create HTML for the list of items
            
            // we don't want jQuery to hunt for #pvImageNav every time
            // so put it into a variable
            var pvImageNav = $("#pvNavArea").append("<ul></ul>");
            var pvImageNavList = $(pvImageNav).find("ul:last");
            
            $("#pvNavArea").append('<div id="horizontalBar"><p></p></div>');
            
            // clear the existing image
            $(STviewer.config.displayArea).find("ul").html("");
            
            for (var sectionNo in STviewer.config.sections) {
                var section = STviewer.config.sections[sectionNo];
                var sectionName = section.sectionName;
                $(pvImageNavList).append('<li id="' + selectorText(sectionName) + '" class="'+ (section.inRotation ? 'inRotation' : 'noRotation') +'"><h4><a href="#">' + sectionName + '</a></h4>\n' +
                                '<ul></ul></li>');
                                
                var sectionList =  $(pvImageNavList).find("ul:last")[0];               
                for (var imageNo in section.images) {
                    var image = section.images[imageNo];
                    image.parent = section;
                    
                    $(sectionList).append('<li><a href="#">' + image.imageCaption + '</a><span></span></li>');
                    
                    $(sectionList).find("a:last").each( function () {
                        this.imageObject = image;
                        
                        this.cacheKey = STviewer.cacheElementPV(image);
                        
                        $(STviewer.config.displayArea).find("ul").append("<li class=\"imageHolder\">" + STviewer.config.loadingGraphic + "</li>");
                        var latestItem = $("li.imageHolder:last")[0];
                        latestItem.cacheKey = this.cacheKey;
                        STviewer.cache[this.cacheKey].imageLayer = latestItem;
                        
                        $(this).click(function () {
                            STviewer.projectViewer.displayImage(STviewer.cache[this.cacheKey]);
                            STviewer.projectViewer.clearTimer();
                            return(false);
                        });
                    });

                }                            

                
            }
            // click the section name to go to the first image - if inRotation then start playing again
            if (section.inRotation) {
                $("#pvNavArea>ul>li>h4>a").click( function () {
					STviewer.projectViewer.pvStatus.continuePlaying = true;
                    STviewer.projectViewer.displayImage(STviewer.cache[$(this).parent().parent().find("ul>li>a")[0].cacheKey]);
                    STviewer.projectViewer.clearTimer();
                    STviewer.projectViewer.nextTimer();
                                return(false);
                });
            }
            else {
                $("#pvNavArea>ul>li>h4>a").click( function () {
                    STviewer.projectViewer.displayImage(STviewer.cache[$(this).parent().parent().find("ul>li>a")[0].cacheKey]);
                    STviewer.projectViewer.clearTimer();
                                return(false);
                });
            }
            STviewer.utils.imagePreload(STviewer.cache[0], "STviewer.projectViewer.displayImage(STviewer.cache[0])" );
            
            this.nextTimer();
        }
    
    }
