/**
 *  Box v2
 *  PB/Graphico November 2008;
 *
 *  Overlay jQuery plug-in.
 *  Consumes images (jpg and png), html snippets and flash. Height and width must be provided for flash. 
 *  Uses the href attr of the target anchor to provide source content;
 *
 *  Requires:
 *		min. jQuery (1.2.6);
 *		swfObject [http://blog.deconcept.com/swfobject/] for flash inclusion;
 *
 *  Supported browsers Firefox 2 & 3(mac & PC), IE6 & 7, Safari 3(Mac).
 *
 *  Example use;
 *  var options = {
 *      width: 400,
 *      preload: true,
 *      animate: false
 *  }
 *  $('.gallery').box(options);
 *
 *  Supported options;
 *  preload:	Boolean, show preloader, default true;
 *  navigation: Boolean, show navigation , default true;
 *	animate:	Boolean, animate box into position, default true;
 *	close:		Boolean, show close button;
 *	speed:		Number, animation speed;
 *	caption:	Boolean, show caption, only availablr for images, default true;
 *
 *	Overridable methods;
 *	getFlashSize:	Provide default flash dimensions, required;
 *	getImageSize:	Provide default image dimensions, pass though method, optional;	
 *	getFileSize:	Provide dimensions for included html, required;	
 *	getCaption:		Provide source for caption, default: the title attr of the target anchor;	
 *
 *  To do;
 *  All marked with **HACK**;
 *	cross browser (opera);
 *	Open on page load;
 */

(function (jq) {

    jq.fn.box = function (options) {
        var self = this;
        jq.extend(jq.fn.box.defaults, options);

        return this.each(function () {
            var box = new Box();
            // if the current element is a link assume single instance such as one off popup...; 
            if (this.nodeName.toLowerCase() === 'a') {
                // remove nav for single instances;
                box.navigation = false;
                box.bind.apply(box, [0, this]);
            } else {
                // ...else assume collection of links i.e. gallery;
                jq('a', this).each(function (i) {
                    box.navigation = jq.fn.box.defaults.navigation;
                    box.close = jq.fn.box.defaults.close;
                    box.bind.apply(box, arguments);
                });
            };
            box.build();
        });
    };

    jq.fn.box.defaults = {
        preload: true,
        navigation: true,
        close: true,
        speed: 400,
        animate: true,
        caption: false,
        count: 1
    };

    // Box class;
    function Box() {
        var self = this;

        this.group = []; 	    // Array, collection of urls for assets within a group;
        this.navigation = null; // Boolean, instance specific version of jq.fn.box.defaults.navigation;
        this.resize = false;    // Boolean, used to circumnavigate the resize event firing when flash is added; 
        this.close = jq.fn.box.defaults.close; 	// Boolean, instance specific version of jq.fn.box.defaults.close; 
        this.name = "box" + jq.fn.box.defaults.count;
        this.animate = jq.fn.box.defaults.animate;
        jq.fn.box.defaults.count++;
    };

    // Box props & methods;
    Box.prototype = {

        OVERLAY: "<div class=\"overlay\"></div>",

        BOX: "<div class=\"box\" style=\"display: none;\">\n" +
					"<div class=\"clear gutter\">" +
						"<div class=\"clear hd\" style='display: none;'></div>\n" +
						"<div class=\"clear bd\" style=\"display: none;\"></div>\n" +
						"<div class=\"ft clear\" style='display: none;'>" +
							"<div class=\"caption\"></div>" +
						"</div>" +
					"</div>" +
				"</div>",

        PRELOADER: "<div class='ldr'>Loading...</div>",

        CLOSEHTML: '<a href="#" class="close"><img class="close" \
            src="../SiteImages/Common/div-boxClose.jpg" alt="close"/></a>',

        isOpen: false, 	// Boolean, ref declaring if the box is open/visible;
        isFirstPass: true, // Boolean, centers box on first load;
        index: null, 	// Number, ref to position of current asset within collection;

        build: function () {
            var self = this;
            if (jQuery.browser.msie && jQuery.browser.version == "6.0") {
                this.addIframe();
            };
            // build default box;
            jq.each(['overlay', 'box'], function () {
                // there can be only one;			  
                if (jq('.' + arguments[1]).get(0) === undefined) {
                    jq(self[arguments[1].toUpperCase()]).appendTo("body").hide();
                    if (arguments[1] = 'box') {
                        jq(arguments[1]).eq(0).css(self.css[arguments[1]]);
                    };
                }
            });
            jq('.overlay').bind('click', function (e) {
                self.trigger.call(self, e);
            });
            // add options;
            if (jq.fn.box.defaults.preload) {
                jq('body').append(this.getPreloader());
            }
            if (jq.fn.box.defaults.close) {
                jq('.box .hd').append(this.getClose());
            }
            if (this.navigation) {
                jq('.box .ft').append(this.getNav());
            }
            // hide untill asset loaded;
            jq.each(['.box .hd', '.box .ft'], function () {
                jq(arguments[1]).hide();
            });
            // bind browsers events;
            jq.each(['resize', 'scroll'], function () {
                jq(window).bind(arguments[1], function (e) {
                    if (self.isOpen && self.resize !== false) {
                        self.setOverlaySize.call(self);
                        self.adjust.call(self);
                    };
                });
            });
            return this;
        },

        hide: function () {
            this.tidy();
            this.isOpen = false;
            return jq.each(["#iShim", ".overlay", ".box", ".box .hd", ".box .ft"], function () {
                return jq(arguments[1]).stop().hide();
            });
        },

        show: function () {
            this.isOpen = true;
            jq(".ldr").hide();
            jq(".box").show();
            this.adjust();
            return true;
        },

        /**
        * allows for the box to be emptied, but not closed, if nav is present;
        */
        tidy: function () {
            this.unsetCaption();
            this.unsetAsset();
        },

        unsetAsset: function () {
            if (!!(jq(".box .bd").children().get(0))) {
                return jq.each(["hd", "bd", "ft"], function () {
                    if (arguments[1] === "bd") {
                        jq(".box ." + arguments[1]).empty();
                    }
                    jq(".box ." + arguments[1]).hide();
                });
            }
            return false;
        },

        unsetCaption: function () {
            return jq(".box .ft .caption").text('');
        },

        /**
        * handles all box events;
        */
        trigger: function (e) {
            if (e.target.className.indexOf('overlay') != -1 ||
				e.target.className.indexOf('close') != -1) {
                return this.hide();
            } else if (e.target.className.indexOf('prev') != -1 ||
						e.target.className.indexOf('next') != -1) {
                return this.update(e.target);
            }
        },

        update: function (elem) {
            var cmd = elem.className.indexOf('next') != -1 ? 'next' : 'prev';
            if ((cmd === 'next' && this.index + 1 === this.group.length) ||
				(cmd === 'prev' && this.index === 0)) {
                return false;
            } else {
                var url = this.group[this.setIndex(cmd)];
                var tmp = this.getAssetType(url);
                this.tidy();
                return this.loadType(url, tmp, null);
            };
        },

        setIndex: function (cmd) {
            return this.index = (cmd === 'next') ? this.index + 1 : this.index - 1;
        },

        setNavStatus: function () {
            // ** HACK ** curently trigger from complete() 
            // probably should be combined with update() to
            // remove duplication;
            jq('.box .ft a').removeClass('inactive');
            if (this.index + 1 === this.group.length) {
                return jq('.box a.next').addClass('inactive');
            } else if (this.index === 0) {
                return jq('.box a.prev').addClass('inactive');
            };
        },

        getCurrentBox: function () {
            return jq('.box').data('instance');
        },

        getNav: function () {
            var nav = jq('.ft .nav').get(0);
            if (nav === undefined) {
                var self = this;
                nav = jq('<div class="nav clear"></div>').appendTo('.box .ft');
                jq.each(['prev', 'next'], function (i, v) {
                    jq('<a href="#" class="' + v + ' replace">' + v + '<span class="' + v + '"></span></a>').appendTo(nav)
						.bind("click", function (e) {
						    // reset self to current Box instance;
						    self = self.getCurrentBox.call();
						    self.trigger.call(self, e);
						    return false;
						});
                });
                return nav.get(0);
            } else {
                return nav;
            };
        },

        getClose: function () {
            var link = jq('.box .close').get(0);
            if (link === undefined) {
                var self = this;
                return jq(this.CLOSEHTML)
					.bind("click", function (e) {
					    // reset self to current Box instance;
					    self = self.getCurrentBox.call();
					    self.trigger.call(self, e);
					    return false;
					}).get(0);
            } else {
                return link;
            };
        },

        getPreloader: function () {
            var loader = jq('.ldr').get(0);
            if (loader === undefined) {
                return jq(this.PRELOADER).hide().get(0);
            } else {
                return loader;
            }
        },
        /*
        * Allow box to be positioned over select inputs in IE6;
        */
        addIframe: function () {
            var body = document.getElementsByTagName("body")[0];
            body.insertAdjacentHTML("beforeEnd", '<IFRAME style="display: none; width: 0; height: 0;" src="" frameBorder="0" scrolling="no" id="iShim" />');
        },

        showIframe: function (obj) {
            if (obj == undefined) return false;
            jq("#iShim").css({
                position: "absolute",
                top: obj.top,
                left: obj.left,
                width: obj.width,
                height: obj.height
            }).show();
        },

        /**
        * collects required params to load an asset;
        * @param	e, Object			passed though event object;
        * @param	n, Number			ref to position in a collection;
        * @param	elem, HTMLElement	clicked link, null if next prev links;	
        * @return	Object;		
        */
        loadAsset: function (e, n, elem) {
            var type, url = this.group[n];
            try {
                type = this.getAssetType(url);
            } catch (e) {
                alert(e);
                return false;
            }
            this.loadType(url, type, elem);
            return this;
        },

        loadType: function (url, type, elem) {
            type = type.replace(/\w/, function (char) {
                return char.toUpperCase();
            });
            return this["load" + type](url, elem);
        },

        /**
        * set up link bindings;
        * @param   elem, HTMLElement   ref to link;
        * @param   num, Number         position in group of links;
        * @return  jQuery;
        */
        bind: function (num, elem) {
            var self = this;
            // push ref to asset url to group;
            this.group.push(elem.href);
            // store ref to position in the group;
            jq.data(elem, 'index', num);
            return jq(elem).bind('click', function (e) {
                jq('.box').data('instance', self);
                self.index = jq.data(elem, 'index');
                self.setOverlaySize.call(self);
                self.setPreloader.call(self);
                self.loadAsset.call(self, e, jq.data(this, 'index'), this);
                return false;
            });
        },

        getAssetType: function (url) {
            if (url.indexOf('#') != -1) {
                return "fragment";
            } else if (url.toLowerCase().match(/\.jpg$|\.png$/)) {
                return "image";
            } else if (url.match(/\.swf$|\.swf\?[a-zA-Z0-9=]*/)) {
                return "flash";
            } else if (url.match(/\.html$|\.htm$|\.aspx$|\.aspx\?[a-zA-Z0-9=]*$/)) {
                return "file";
            } else {
                throw new Error("getAssetType(): file type not available");
                return false;
            };
        },


        loadFragment: function (url) {
            var el = url.match(/#\w+/g).shift();
            this.setContent(jq(el).get(0));
            return;
        },


        loadImage: function (url, elem, w, h) {
            var self = this;
            if (arguments.length > 2) {
                var size = jq.fn.box.getImageSize(w, h, elem);
                var image = this.createImage(url, size[0], size[1]);
                this.setContent(image);
                return this;
            }
            var image = new Image();
            image.onload = function () {
                self.loadImage.call(self, url, elem, image.width, image.height);
            };
            image.src = url;
        },

        setCaption: function (txt) {
            if (jq.fn.box.defaults.caption) {
                return jq('.box .ft .caption').text(txt);
            }
            return false;
        },

        createImage: function (url, w, h) {
            var image = "<div class='figure' style=''>";
            image += "<img src='" + url + "'";
            image += w > 0 ? " width ='" + w + "'" : '';
            image += h > 0 ? " height ='" + h + "'" : '';
            image += " /></div>";
            return image;
        },

        loadFlash: function (url, elem) {
            var elem, size = jq.fn.box.getFlashSize(elem);
            try {
                elem = this.doSFWObject(url, size[0], size[1]);
            } catch (e) {
                alert(e);
            }
            this.setContent(elem);
            return this;
        },

        doSFWObject: function (url, w, h) {
            //var path = url.split('?xmlPath=');
            //url = path[0];
            //var url = url.toString().replace(/%27/g, '');
            if (typeof SWFObject !== "undefined") {
                this.resize = true;
                var div = jq("<div id='flplayer'></div>").prependTo("body").get(0);
                var so = new SWFObject(url, "flp", w, h, "8", "transparent");
                so.addParam("wmode", "transparent");
                so.addParam("quality", "high");
                so.addParam("allowScriptAccess", "sameDomain");
                so.addParam("allowFullScreen", "false");
                so.addParam("align", "left");
                so.addParam("name", "asset");
                so.addParam("movie", url);
                so.addParam("pluginspage", "http://www.macromedia.com/go/getflashplayer");
                so.write("flplayer");
                return div;
            } else {
                throw new Error("doSFWObject(): swfobject.js missing!");
                return false;
            };
        },

        loadFile: function (url, elem) {
            var self = this;
            if (arguments.length > 2) {
                var size = jq.fn.box.getFileSize(elem);
                var elem = jq(arguments[2]).css({ width: size[0], height: size[1] })
					.appendTo("body").get(0);
                this.setContent(elem);
                return this;
            }
            jq.get((typeof url === "object") ? url.href : url, function (data) {
                return self.loadFile.call(self, url, elem, data);
            });
        },

        setContent: function (asset) {
            if (jq.fn.box.defaults.caption) {
                var html = jq.fn.box.getCaption(this.index + 1, this.group.length);
                jq('.box .ft .caption').append(html);
            } else {
                jq('.box .ft .caption').css("display", "none");
            };
            jq(".box .bd").empty().prepend(asset);
            this.resize = false;
            this.show();
            return this;
        },

        setPreloader: function () {
            if (!jq.fn.box.defaults.preload) {
                return false;
            } else {
                return jq(".ldr").show();
            }
        },

        setOverlaySize: function () {
            var docSize = this.getDocSize();
            return jq(".overlay").show().css({ height: docSize[1] });
        },

        adjust: function () {
            if (this.isOpen === false) {
                return this.hide();
            };
            var size = this.getSize();
            var newxy = this.getXY(size[0], size[1]);
            var dims = { top: newxy[1], left: newxy[0], height: size[1], width: size[0] };
            // add iframe for IE6 hack;
            if (jQuery.browser.msie && jQuery.browser.version == "6.0") {
                this.showIframe(dims);
            };
            return this.move(dims);
        },

        setNavPosition: function () {
            var dims = { box: [], nav: [] };
            // **HACK** show, hide, show untidy. needed as nav dims = 0 if foot not visible;
            jq(".box .ft").show();
            jq.each(['.box', '.box .prev'], function (i, value) {
                var tmp = (i === 0) ? 'box' : 'nav';
                dims[tmp][0] = jq(value).outerWidth();
                dims[tmp][1] = jq(value).outerHeight();
            });
            jq(".box .ft").hide();
            var t = (dims.box[1] - dims.nav[1]) / 2;
            var w = dims.box[0] + (dims.nav[0] * 2);
            jq('.nav').css({ top: t, left: -dims.nav[0], width: w });
            jq(".box .ft").show();
            return true;
        },

        getSize: function () {
            var dims, bxcss = {}, hdcss = {}, bdcss = {}, ftcss = {}, self = this;
            // store current ccs properties for reset later;
            jq.each(["width", "height", "display", "hidden", "position", "visibility"], function (i, val) {
                bxcss[val] = jq(".box").css(val);
                bdcss[val] = jq(".box .bd").css(val);
                hdcss[val] = jq(".box .hd").css(val);
                ftcss[val] = jq(".box .ft").css(val);
            });
            var elems = [".box", ".box .bd", ".box .hd", ".box .ft"];
            // don't inc navigation if not set;
            elems.length = this.navigation ? elems.length : 3;
            // **HACK** don't inc close if not set;
            // elems.length = this.close ? elems.length : 2;
            // normalise css to acurately measure width/height;
            jq.each(elems, function () {
                jq(arguments[1]).css(self.css.reset);
            });
            // width needs to be size via .box .bd because 
            // floated right nav throws it all out;
            dims = [jq(".box .bd").width(), jq(".box").height()];
            // reset old values;
            jq(".box .bd").css(bdcss);
            jq(".box .hd").css(hdcss);
            jq(".box .ft").css(ftcss);
            jq(".box").css(bxcss);
            return dims;
        },

        /**
        * gets the xy position for the box;
        * @param	w, Number	box width;
        * @param	h, Number	box height;
        * @return	Array		xy positions as [x, y];
        */
        getXY: function (w, h) {
            var ws = this.getWindowSize();
            var sp = this.getScroll();
            return jq.map([w, h], function (n, i) {
                return sp[i] + (Math.floor(ws[i] / 2)) - (Math.floor(n / 2));
            });
        },

        move: function (dims) {
            if (this.animate) {
                var self = this;
                // if current x or y position is 0 i.e. first,
                // pass center the box in the current viewport;
                if (this.isFirstPass === true) {
                    var xy = this.getXY(1, 1); // default w, h is 1px;
                    jq(".box").css({ left: xy[0], top: xy[1], width: 1, height: 1 });
                };
                jq(".box").animate({ "top": dims.top, "height": dims.height },
					jq.fn.box.defaults.speed, function () {
					    jq(".box").animate({ "left": dims.left, "width": dims.width },
						this.speed, function () { self.complete.call(self); }
					);
					});
            } else {
                jq(".box").css(dims);
                this.complete();
            };
            this.isFirstPass = false;
        },

        complete: function () {
            // **HACK** remove overflow hidden added by jQuery.animate;
            // jq('.box').css({overflow: 'visible'}); still required??
            // **HACK END**;
            var self = this;
            if (this.close) {
                var b = jq(".box .hd");
                if (jq.browser.opera) {
                    b.css({ width: "100%", height: "100%" });
                };
                b.show();
            };
            jq(".box .bd").fadeIn(600, function () {
                // **HACK** all a bit untidy;
                jq(document).trigger('boxcomplete');
                self.setNavStatus.call(self);
                // move to setNavPosition();
                /*if(self.navigation){
                jq(".box .ft").show();
                };*/
                //if(self.navigation){
                self.setNavPosition.call(self);
                //}

            });

        },
        /**
        * return current page dimensions;
        * @return  Array   in the form [width, height];
        */
        getDocSize: function () {
            // see getWindowSize() for explanation;
            var height = jq.browser.opera && jq.browser.version > "9.5" &&
				jq.fn.jquery <= "1.2.6" ?
				Math.max(document.body.scrollHeight, document.body.offsetHeight) :
				jq(document).height();
            return [jq(document).width(), Math.max(height, this.getWindowSize()[1])];
        },
        /**
        * return current viewport dimensions;
        * @return  Array   in the form [width, height];
        */
        getWindowSize: function () {
            // fix jQuery/Opera bug with the window height, courtesy 
            // of http://www.ericmmartin.com/jquery-browser-issues/
            var height = jq.browser.opera && jq.browser.version > "9.5" &&
				jq.fn.jquery <= "1.2.6" ?
				document.documentElement["clientHeight"] :
				jq(window).height();
            return [jq(window).width(), height];
        },
        /**
        * return current scroll position;
        * @return  Array   in the form [left, top];
        */
        getScroll: function () {
            return [jq(window).scrollLeft(), jq(window).scrollTop()];
        },

        css: {
            box: {
                height: '1px',
                width: '1px'
            },
            reset: {
                display: '',
                visibility: 'hidden',
                position: '',
                height: '',
                width: ''
            }
        }
    }
    /**
    * public methods for custom override;
    */
    jq.fn.box.getFlashSize = function (elem) {
        return [500, 400];
    };
    jq.fn.box.getImageSize = function (w, h, elem) {
        return [w, h];
    };
    jq.fn.box.getFileSize = function (elem) {
        return [400, ""];
    };
    jq.fn.box.getCaption = function (url) {
        return jq('a').filter(function () {
            return this.href == url;
        }).attr('title');
    };
})(jQuery);