/**
 * LightBox - Builds and manages a set of elements that makeup a lightbox
 *
 * Options (if no default is specified, then there is none):
 * box					mixed		Element id or object that holds the content
 * overlay				mixed		Element id or object that acts as the overlay
 * closeButton			mixed		Element id or object that acts as a close button
 * boxClass				string		CSS class that can be added to the box
 * overlayClass			string		CSS class that can be added to the overlay
 * closeClass			string		CSS class that can be added to the close button (default: lbClose)
 * imgClass				string		CSS class used to identify images to preload
 * contentClass			string		CSS class used for content wrapper (default: lbContentWrapper)
 * frameClass			string		CSS class used for iframe wrapper (default: lbContentFrame)
 * autoRegisterEvents	boolean		Determines if click events for displaying images should be automatically created (default: true)
 * makeCloseButton		boolean		Determines if a close button is created if one is not specified (default: true)
 * onshow				function	Function to execute when the lightbox is shown
 * onhide				function	Function to execute when the lightbox is hidden
 * pinTop				boolean		Determines if the lightbox should pin it's top position in place (default: false)
 * pinLeft				boolean		Determines if the lightbox should pin it's left position in place (default: false)
 * autopin				boolean		Determines if the lightbox will automatically pin itself when it is larger than the viewable area. (default: true)
 *
 * @param	options		object		optional;Object specifying options
 * @return				LightBox	New LightBox instance
 */
function LightBox(options) {
	//internals
	this.isShowing = false;
	this.initialized = false;
	this.isIE = false;
	this.regexFilename = /^.*\/([^\/]*)$/i;
	this.regexFullpath = /^(?:https?:\/\/|(\/))?(.*)$/i;
	this.images = {};
	
	//options and objects
	this.box = null;
	this.overlay = null;
	this.closeButton = null;
	this.options = {
		boxClass: '',
		overlayClass: '',
		closeClass: 'lbClose',
		imgClass: '',
		contentClass: 'lbContentWrapper',
		frameClass: 'lbContentFrame',
		makeCloseButton: true,
		autoRegisterEvents: true,
		pinTop: false,
		pinLeft: false,
		autopin: true
	};
	this.events = {
		onshow: null,
		onhide: null
	};
	
	options = options || {};
	
	domLoader.register(function initialize(){
		this.isIE = window.ActiveXObject ? (window.XMLHttpRequest ? 7 : 6) : false;
		
		for(var k in options) {
			if(options.hasOwnProperty(k) && k in this.options) {
				this.options[k] = options[k];
			}
		}
		
		var docbody = dbt('body')[0];
		
		function isString(str) {
			return str && (typeof str == 'string' || (typeof str == 'object' && typeof str.valueOf() == 'string'));
		}
		
		var box_id = 'lightbox';
		if(options['box'] && isString(options['box'])) {
			this.box = did(options['box']);
			box_id = options['box'];
		} else if(options['box'] && typeof options['box'] == 'object') {
			this.box = options['box'];
		}
		
		if(!this.box) {
			this.box = createElement('div');
			this.box.id = box_id;
			docbody.insertBefore(this.box, first(docbody));
		}
		addClass(this.box, this.options.boxClass);
		this.box.style.display = 'none';
		
		var close_id = 'lbClose';
		if(options['closeButton'] && isString(options['closeButton'])) {
			this.closeButton = did(options['closeButton']);
			close_id = options['closeButton'];
		} else {
			this.closeButton = options['closeButton'];
		}
		
		if(!this.closeButton && this.options.makeCloseButton) {
			this.closeButton = createElement('a');
			this.closeButton.id = close_id;
			this.options.closeClass = this.options.closeClass.trim();
			this.closeButton.innerHTML = 'Close X';
			if(this.options.closeClass) {
				addClass(this.closeButton, this.options.closeClass);
			}
			this.box.appendChild(this.closeButton);
		}
		
		var overlay_id = 'overlay';
		if(options['overlay'] && isString(options['overlay'])) {
			this.overlay = did(options['overlay']);
			overlay_id = options['overlay'];
		} else if(options['overlay'] && typeof options['overlay'] == 'object') {
			this.overlay = options['overlay'];
		}
		
		if(!this.overlay) {
			this.overlay = createElement('div');
			this.overlay.id = overlay_id;
			docbody.insertBefore(this.overlay, this.box);
		}
		addClass(this.overlay, this.options.overlayClass);
		this.overlay.style.display = 'none';
				
		if(this.isIE) {
			this.shim = createElement('iframe');
			this.shim.style.cssText = 'display: none;position: absolute;top: 0px;left: 0px;filter:progid:DXImageTransform.Microsoft.alpha(opacity=0);z-index:'+(getStyle(this.overlay, 'zIndex')-1);
			this.shim.frameBorder = 0;
			this.shim.scrolling = 'no';
			this.shim.src = dbt('base')[0].href + 'blankpage';
			docbody.insertBefore(this.shim, first(docbody));
		}
		
		registerEvent(window, 'resize', this.createCaller(this, this.resize));
		registerEvent(window, 'scroll', this.createCaller(this, this.resize));
		registerEvent(this.overlay, 'click', this.createCaller(this, this.hide));
		if(this.closeButton) {
			registerEvent(this.closeButton, 'click', this.createCaller(this, this.hide));
		}
		
		for(var eName in this.events) {
			if(this.events.hasOwnProperty(eName) && typeof options[eName] == 'function') {
				this.events[eName] = options[eName];
				this.listen(eName, this.events[eName]);
			}
		}
		
		this.preloadImages();
		this.initialized = true;
	}, this);
}
LightBox.prototype = {
	preloadImages: function preloadImages(className) {
		className = (className ? String(className) : this.options.imgClass).trim();
		if(className == '') {
			return false;
		}
		var nodes = slice(dbc(className)), tagname='', imgsrc='';
		nodes.forEach(this.preloadImage, this);
	},
	preloadImage: function preloadImage(src, fn) {
		var node;
		if(typeof src.valueOf() == 'object' && src.nodeType == 1) {
			node = src;
			var tag = node.tagName.toLowerCase();
			if(tag == 'a' && node.href != '') {
				src = node.href;
			} else if(tagname == 'img' && node.src != '') {
				src = node.src;
			}
		}
		
		src = (src ? String(src) : '').trim();
		var fname = src.replace(this.regexFullpath, '$1$2');
		if(!src || !fname) {
			return null;
		}
		
		if(!this.images[fname]) {
			this.images[fname] = new Image();
			if(fn && typeof fn == 'function') {
				var self = this;
				registerEvent(this.images[fname], 'load', function imgLoad(e){
					e = fixEvent(e);
					if(!e.target) {
						e = {target: self.images[fname]};
					}
					unregisterEvent(e.target, 'load', arguments.callee);
					fn.call(self, e);
				});
			}
			this.images[fname].src = src;
		} else if(typeof fn == 'function') {
			fn.call(this, {target: this.images[fname]});
		} else {
		}
		if(this.options.autoRegisterEvents && node) {
			registerEvent(node, 'click', this.createCaller(this, this.showIMG, true, fname, '', '', ''));
		}
		return (fname in this.images) ? this.images[fname] : null;
	},
	createWrapper: function createWrapper(html) {
		if(!this.initialized){
			return false;
		}
		if(this.frame){ this.frame.style.display = 'none'; }
		if(!this.content) {
			this.options.contentClass = this.options.contentClass.trim() || 'lbContentWrapper';
			this.content = createElement('div');
			this.content.className = this.options.contentClass;
			this.box.appendChild(this.content);
		}
		html = html || '';
		if(html != '') {
			this.content.innerHTML = String(html);
		}
		this.content.style.display = '';
	},
	showHTML: function showHTML(html) {
		if(!this.initialized){
			return false;
		}
		this.createWrapper(html);
		this.show();
		return this;
	},
	showDOM: function showDOM(node, clone) {
		if(!this.initialized){
			return false;
		}
		this.createWrapper();
		clone = clone == null ? true : clone;
		if(node && node.nodeType == 1 && !contains(this.content, node)) {
			if(clone){
				var nodeClone = node.cloneNode(true);
				this.content.appendChild(nodeClone);
			} else {
				this.content.appendChild(node);
			}
		}
		this.show();
		this.resize();
		return this;
	},
	showIMG: function showIMG(src, width, height, alt) {
		if(!this.initialized){
			return false;
		}
		this.createWrapper('<div>Loading Image...</div>');
		
		this.preloadImage(src, function displayImage(e) {
			var img = e.target;
			var attributes = {'width':(width || img.width), 'height':(height || img.height), 'alt':(alt || '')};
			for(var atr in attributes) {
				if(attributes.hasOwnProperty(atr)) {
					img.setAttribute(atr, attributes[atr]);
				}
			}
			this.content.innerHTML = '';
			this.showDOM(img, false);
		});
		return this;
	},
	showURL: function showURL(url) {
		if(!this.initialized){
			return false;
		}
		if(this.content){
			this.content.style.display = 'none';
		}
		if(!this.frame) {
			this.options.frameClass = this.options.frameClass.trim() || 'lbContentFrame';
			this.frame = createElement('iframe');
			this.frame.className = this.options.frameClass;
			this.frame.frameBorder = 0;
			this.frame.allowTransparency = true;
			this.box.appendChild(this.frame);
		}
		
		this.frame.src = url;
		this.frame.style.display = '';
		this.show();
		return this;
	},
	clear: function clear() {
		this.content.innerHTML = '';
		return this;
	},
	show: function show() {
		this.resize();
		this.box.style.display = '';
		this.overlay.style.display = '';
		if(this.shim) {
			this.shim.style.display = '';
		}
		if(!this.isShowing) {
			triggerEvent(this.box, 'focus', false, false);
		}
		this.isShowing = true;
		return this;
	},
	hide: function hide() {
		this.box.style.display = 'none';
		this.overlay.style.display = 'none';
		if(this.shim) {
			this.shim.style.display = 'none';
		}
		if(this.isShowing) {
			triggerEvent(this.box, 'blur', false, false);
		}
		this.isShowing = false;
		return this;
	},
	toggle: function toggle() {
		var fn = this.isShowing ? 'hide':'show';
		this[fn]();
	},
	pin: function pin() {
		var val = slice(arguments).shift();
		switch(val) {
			case 'top':
				this.options.pinTop = true;
				break;
			case 'left':
				this.options.pinLeft = true;
				break;
			default:
				this.options.pinTop = true;
				this.options.pinLeft = true;
		}
	},
	unpin: function unpin() {
		var val = slice(arguments).shift();
		switch(val) {
			case 'top':
				this.options.pinTop = false;
				break;
			case 'left':
				this.options.pinLeft = false;
				break;
			default:
				this.options.pinTop = false;
				this.options.pinLeft = false;
		}
	},
	resize: function resize() {
		var max = getMaxDocSize();
		['overlay','shim'].forEach(function(item){
			if(this[item]) {
				this[item].style.width = max['width']+'px';
				this[item].style.height = max['height']+'px';
			}
		}, this);
		
		var docsize = getDocSize(), scroll = getScrollXY();
		if(!this.isShowing) {
			this.box.style.visibility = 'hidden';
			this.box.style.display = '';
		}
		//var boxWidth = this.isIE ? this.box.offsetWidth : getStyle(this.box,'width')
		//var boxHeight = this.isIE ? this.box.offsetHeight : getStyle(this.box,'height');
		var boxWidth = this.box.offsetWidth;
		var boxHeight = this.box.offsetHeight;
		if(!this.isShowing) {
			this.box.style.display = 'none';
			this.box.style.visibility = 'visible';
		}
		boxWidth = isNaN(boxWidth) ? 0 : boxWidth;
		boxHeight = isNaN(boxHeight) ? 0 : boxHeight;
		var topleft = {
			'x':Math.round((docsize['width']-boxWidth)/2)+scroll['x'],
			'y':Math.round((docsize['height']-boxHeight)/2)+scroll['y']
		};
		if(boxWidth > docsize['width']) {
			topleft.x = 0;
		}
		var pintop = this.options.autopin ? (boxHeight > docsize['height']) : false;
		var pinleft = this.options.autopin ? (boxWidth > docsize['width']) : false;
		this.setPosition(topleft.x, topleft.y, pinleft, pintop);
		return this;		 
	},
	setPosition: function setPosition(x, y, pinx, piny) {
		var pin = false;
		if(this.options.autopin && !(pinx && piny)) {
			pin = (pinx ? 'top' : (piny ? 'left' : false));
			this.unpin(pin);
		}
		x = (x || 0) < 0 ? 0 : x;
		y = (y || 0) < 0 ? 0 : y; 
		if(!this.options.pinLeft && x != 'auto') {
			this.box.style.left = parseInt(x)+'px';
		}
		if(!this.options.pinTop && y != 'auto') {
			this.box.style.top = parseInt(y)+'px';
		}
		if(this.options.autopin && (pinx || piny)) {
			pin = (pinx && piny) ? true : (pinx ? 'left' : (piny ? 'top': false));
			this.pin(pin);
		}
	},
	listen: function listen(event, fn) {
		if(typeof fn != 'function') {
			return false;
		}
		var self = this;
		function eventWrapper(func, stopDefault, stopBubble) {
			return function wrapping(e) {
				e = fixEvent(e);
				if(stopDefault) {
					e.preventDefault();
				}
				if(stopBubble) {
					e.stopPropagation();
				}
				func.call(self, e);
			}
		}
		switch(event) {
			case 'onshow':
				registerEvent(this.box, 'focus', eventWrapper(fn, true, true));
				break;
			case 'onhide':
				registerEvent(this.box, 'blur', eventWrapper(fn, true, true));
				break;
			default:
				break;
		}
	},
	createCaller: function createCaller(obj, fn, stopDefault){
		var args = slice(arguments, 3);
		return function callMethod(e) {
			e = fixEvent(e);
			if(stopDefault) {
				e.preventDefault();
			}
			fn.apply(obj, args.concat([e]));
		}
	},
	constructor: LightBox
};
