/**
 * A base library for Shadowbox used as a standalone (without another base
 * library/adapter combination).
 *
 * This file is part of Shadowbox.
 *
 * Shadowbox is an online media viewer application that supports all of the
 * web's most popular media publishing formats. Shadowbox is written entirely
 * in JavaScript and CSS and is highly customizable. Using Shadowbox, website
 * authors can showcase a wide assortment of media in all major browsers without
 * navigating users away from the linking page.
 *
 * Shadowbox is released under version 3.0 of the Creative Commons Attribution-
 * Noncommercial-Share Alike license. This means that it is absolutely free
 * for personal, noncommercial use provided that you 1) make attribution to the
 * author and 2) release any derivative work under the same or a similar
 * license.
 *
 * If you wish to use Shadowbox for commercial purposes, licensing information
 * can be found at http://mjijackson.com/shadowbox/.
 *
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 * @copyright   2007-2008 Michael J. I. Jackson
 * @license     http://creativecommons.org/licenses/by-nc-sa/3.0/
 * @version     SVN: $Id: shadowbox-base.js 103 2008-06-27 06:19:21Z mjijackson $
 */

// create the Shadowbox object first
var Shadowbox = {};

Shadowbox.lib = function() {

	// local style camelizing for speed
	var styleCache = {};
	var camelRe = /(-[a-z])/gi;
	var camelFn = function(m, a) {
		return a.charAt(1).toUpperCase();
	};
	var toCamel = function(style) {
		var camel;
		if (!(camel = styleCache[style])) {
			camel = styleCache[style] = style.replace(camelRe, camelFn);
		}
		return camel;
	};

	var view = document.defaultView;
	var alphaRe = /alpha\([^\)]*\)/gi;

	/**
	 * Sets the opacity of the given element to the specified level.
	 *
	 * @param   {HTMLElement}   el          The element
	 * @param   {Number}        opacity     The opacity to use
	 * @return  void
	 * @private
	 * @static
	 */
	var setOpacity = function(el, opacity) {
		var s = el.style;
		if (window.ActiveXObject) { // IE
			s.zoom = 1; // give "layout"
			s.filter = (s.filter || '').replace(alphaRe, '') +
			           (opacity == 1 ? '' : ' alpha(opacity=' + (opacity * 100) + ')');
		} else {
			s.opacity = opacity;
		}
	};

	return {

		adapter: 'standalone',

		/**
		 * Gets the value of the style on the given element. This function
		 * adapted from Ext.Element.getStyle().
		 *
		 * @param   {HTMLElement}   el      The DOM element
		 * @param   {String}        style   The name of the style (e.g. margin-top)
		 * @return  {mixed}                 The value of the given style
		 * @public
		 * @static
		 */
		getStyle: function() {
			return view && view.getComputedStyle
					? function(el, style) {
				var v, cs, camel;
				if (style == 'float') style = 'cssFloat';
				if (v = el.style[style]) return v;
				if (cs = view.getComputedStyle(el, '')) {
					return cs[toCamel(style)];
				}
				return null;
			}
					: function(el, style) {
				var v, cs, camel;
				if (style == 'opacity') {
					if (typeof el.style.filter == 'string') {
						var m = el.style.filter.match(/alpha\(opacity=(.+)\)/i);
						if (m) {
							var fv = parseFloat(m[1]);
							if (!isNaN(fv)) return (fv ? fv / 100 : 0);
						}
					}
					return 1;
				} else if (style == 'float') {
					style = 'styleFloat';
				}
				var camel = toCamel(style);
				if (v = el.style[camel]) return v;
				if (cs = el.currentStyle) return cs[camel];
				return null;
			};
		}(),

		/**
		 * Sets the style on the given element to the given value. May be an
		 * object to specify multiple values. This function adapted from
		 * Ext.Element.setStyle().
		 *
		 * @param   {HTMLElement}   el      The DOM element
		 * @param   {String/Object} style   The name of the style to set if a
		 *                                  string, or an object of name =>
		 *                                  value pairs
		 * @param   {String}        value   The value to set the given style to
		 * @return  void
		 * @public
		 * @static
		 */
		setStyle: function(el, style, value) {
			if (typeof style == 'string') {
				var camel = toCamel(style);
				if (camel == 'opacity') {
					setOpacity(el, value);
				} else {
					el.style[camel] = value;
				}
			} else {
				for (var s in style) {
					this.setStyle(el, s, style[s]);
				}
			}
		},

		/**
		 * Gets a reference to the given element.
		 *
		 * @param   {String/HTMLElement}    el      The element to fetch
		 * @return  {HTMLElement}                   A reference to the element
		 * @public
		 * @static
		 */
		get: function(el) {
			return typeof el == 'string' ? document.getElementById(el) : el;
		},

		/**
		 * Removes an element from the DOM.
		 *
		 * @param   {HTMLElement}       el      The element to remove
		 * @return  void
		 * @public
		 * @static
		 */
		remove: function(el) {
			el.parentNode.removeChild(el);
		},

		/**
		 * Gets the target of the given event. The event object passed will be
		 * the same object that is passed to listeners registered with
		 * addEvent().
		 *
		 * @param   {mixed}             e       The event object
		 * @return  {HTMLElement}               The event's target element
		 * @public
		 * @static
		 */
		getTarget: function(e) {
			var t = e.target ? e.target : e.srcElement;
			return t.nodeType == 3 ? t.parentNode : t;
		},

		/**
		 * Gets the page X/Y coordinates of the mouse event in an [x, y] array.
		 * The page coordinates should be relative to the document, and not the
		 * viewport. The event object provided here will be the same object that
		 * is passed to listeners registered with addEvent().
		 *
		 * @param   {mixed}         e       The event object
		 * @return  {Array}                 The page X/Y coordinates
		 * @public
		 * @static
		 */
		getPageXY: function(e) {
			var x = e.pageX || (e.clientX +
			                    (document.documentElement.scrollLeft || document.body.scrollLeft));
			var y = e.pageY || (e.clientY +
			                    (document.documentElement.scrollTop || document.body.scrollTop));
			return [x, y];
		},

		/**
		 * Prevents the event's default behavior. The event object here will
		 * be the same object that is passed to listeners registered with
		 * addEvent().
		 *
		 * @param   {mixed}             e       The event object
		 * @return  void
		 * @public
		 * @static
		 */
		preventDefault: function(e) {
			if (e.preventDefault) {
				e.preventDefault();
			} else {
				e.returnValue = false;
			}
		},

		/**
		 * Gets the key code of the given event object (keydown). The event
		 * object here will be the same object that is passed to listeners
		 * registered with addEvent().
		 *
		 * @param   {mixed}         e       The event object
		 * @return  {Number}                The key code of the event
		 * @public
		 * @static
		 */
		keyCode: function(e) {
			return e.which ? e.which : e.keyCode;
		},

		/**
		 * Adds an event listener to the given element. It is expected that this
		 * function will be passed the event as its first argument.
		 *
		 * @param   {HTMLElement}   el          The DOM element to listen to
		 * @param   {String}        name        The name of the event to register
		 *                                      (i.e. 'click', 'scroll', etc.)
		 * @param   {Function}      handler     The event handler function
		 * @return  void
		 * @public
		 * @static
		 */
		addEvent: function(el, name, handler) {
			if (el.addEventListener) {
				el.addEventListener(name, handler, false);
			} else if (el.attachEvent) {
				el.attachEvent('on' + name, handler);
			}
		},

		/**
		 * Removes an event listener from the given element.
		 *
		 * @param   {HTMLElement}   el          The DOM element to stop listening to
		 * @param   {String}        name        The name of the event to stop
		 *                                      listening for (i.e. 'click')
		 * @param   {Function}      handler     The event handler function
		 * @return  void
		 * @public
		 * @static
		 */
		removeEvent: function(el, name, handler) {
			if (el.removeEventListener) {
				el.removeEventListener(name, handler, false);
			} else if (el.detachEvent) {
				el.detachEvent('on' + name, handler);
			}
		},

		/**
		 * Appends an HTML fragment to the given element.
		 *
		 * @param   {HTMLElement}       el      The element to append to
		 * @param   {String}            html    The HTML fragment to use
		 * @return  void
		 * @public
		 * @static
		 */
		append: function(el, html) {
			if (el.insertAdjacentHTML) {
				el.insertAdjacentHTML('BeforeEnd', html);
			} else if (el.lastChild) {
				var range = el.ownerDocument.createRange();
				range.setStartAfter(el.lastChild);
				var frag = range.createContextualFragment(html);
				el.appendChild(frag);
			} else {
				el.innerHTML = html;
			}
		}

	};

}();
