(function( window, document, req_version, callback, $, script, done, readystate ){
  
  // If jQuery isn't loaded, or is a lower version than specified, load the
  // specified version and call the callback, otherwise just call the callback.
  if ( !($ = window.jQuery) || req_version > $.fn.jquery || callback( $, null, window ) ) {

    // Create a script element.
    script = document.createElement( 'script' );
    script.type = 'text/javascript';
    
    // Load the specified jQuery from the Google AJAX API server (minified).
    script.src = 'http://ajax.googleapis.com/ajax/libs/jquery/' + req_version + '/jquery.min.js';
    
    // When the script is loaded, remove it, execute jQuery.noConflict( true )
    // on the newly-loaded jQuery (thus reverting any previous version to its
    // original state), and call the callback with the newly-loaded jQuery.
    script.onload = script.onreadystatechange = function() {
      if ( !done && ( !( readystate = this.readyState )
        || readystate == 'loaded' || readystate == 'complete' ) ) {

        callback( ($ = window.jQuery).noConflict(1), done = 1, window );
        
        $( script ).remove();
      }
    };
    
    // Add the script element to either the head or body, it doesn't matter.
    document.documentElement.childNodes[0].appendChild( script );
  }
  
})( window, document,
  
  // Minimum jQuery version required. Change this as-needed.
  '1.3.2',
  
  // Your jQuery code goes inside this callback. $ refers to the jQuery object,
  // and L is a boolean that indicates whether or not an external jQuery file
  // was just "L"oaded.
  function( $, L, window ) {
    '$:nomunge, L:nomunge'; // Used by YUI compressor.

     /*
      * FancyBox - jQuery Plugin
      * Simple and fancy lightbox alternative
      *
      * Examples and documentation at: http://fancybox.net
      * 
      * Copyright (c) 2008 - 2010 Janis Skarnelis
      *
      * Version: 1.3.1 (05/03/2010)
      * Requires: jQuery v1.3+
      *
      * Dual licensed under the MIT and GPL licenses:
      *   http://www.opensource.org/licenses/mit-license.php
      *   http://www.gnu.org/licenses/gpl.html
      */

     (function($) {

     	var tmp, loading, overlay, wrap, outer, inner, close, nav_left, nav_right,

     		selectedIndex = 0, selectedOpts = {}, selectedArray = [], currentIndex = 0, currentOpts = {}, currentArray = [],

     		ajaxLoader = null, imgPreloader = new Image(), imgRegExp = /\.(jpg|gif|png|bmp|jpeg)(.*)?$/i, swfRegExp = /[^\.]\.(swf)\s*$/i,

     		loadingTimer, loadingFrame = 1,

     		start_pos, final_pos, busy = false, shadow = 20, fx = $.extend($('<div/>')[0], { prop: 0 }), titleh = 0, 

     		isIE6 = !$.support.opacity && !window.XMLHttpRequest,

     		/*
     		 * Private methods 
     		 */

     		fancybox_abort = function() {
     			loading.hide();

     			imgPreloader.onerror = imgPreloader.onload = null;

     			if (ajaxLoader) {
     				ajaxLoader.abort();
     			}

     			tmp.empty();
     		},

     		fancybox_error = function() {
     			$.fancybox('<p id="fancybox_error">The requested content cannot be loaded.<br />Please try again later.</p>', {
     				'scrolling'		: 'no',
     				'padding'		: 20,
     				'transitionIn'	: 'none',
     				'transitionOut'	: 'none'
     			});
     		},

     		fancybox_get_viewport = function() {
     			return [ $(window).width(), $(window).height(), $(document).scrollLeft(), $(document).scrollTop() ];
     		},

     		fancybox_get_zoom_to = function () {
     			var view	= fancybox_get_viewport(),
     				to		= {},

     				margin = currentOpts.margin,
     				resize = currentOpts.autoScale,

     				horizontal_space	= (shadow + margin) * 2,
     				vertical_space		= (shadow + margin) * 2,
     				double_padding		= (currentOpts.padding * 2),

     				ratio;

     			if (currentOpts.width.toString().indexOf('%') > -1) {
     				to.width = ((view[0] * parseFloat(currentOpts.width)) / 100) - (shadow * 2) ;
     				resize = false;

     			} else {
     				to.width = currentOpts.width + double_padding;
     			}

     			if (currentOpts.height.toString().indexOf('%') > -1) {
     				to.height = ((view[1] * parseFloat(currentOpts.height)) / 100) - (shadow * 2);
     				resize = false;

     			} else {
     				to.height = currentOpts.height + double_padding;
     			}

     			if (resize && (to.width > (view[0] - horizontal_space) || to.height > (view[1] - vertical_space))) {
     				if (selectedOpts.type == 'image' || selectedOpts.type == 'swf') {
     					horizontal_space	+= double_padding;
     					vertical_space		+= double_padding;

     					ratio = Math.min(Math.min( view[0] - horizontal_space, currentOpts.width) / currentOpts.width, Math.min( view[1] - vertical_space, currentOpts.height) / currentOpts.height);

     					to.width	= Math.round(ratio * (to.width	- double_padding)) + double_padding;
     					to.height	= Math.round(ratio * (to.height	- double_padding)) + double_padding;

     				} else {
     					to.width	= Math.min(to.width,	(view[0] - horizontal_space));
     					to.height	= Math.min(to.height,	(view[1] - vertical_space));
     				}
     			}

     			to.top	= view[3] + ((view[1] - (to.height	+ (shadow * 2 ))) * 0.5);
     			to.left	= view[2] + ((view[0] - (to.width	+ (shadow * 2 ))) * 0.5);

     			if (currentOpts.autoScale === false) {
     				to.top	= Math.max(view[3] + margin, to.top);
     				to.left	= Math.max(view[2] + margin, to.left);
     			}

     			return to;
     		},

     		fancybox_format_title = function(title) {
     			if (title && title.length) {
     				switch (currentOpts.titlePosition) {
     					case 'inside':
     						return title;
     					case 'over':
     						return '<span id="fancybox-title-over">' + title + '</span>';
     					default:
     						return '<span id="fancybox-title-wrap"><span id="fancybox-title-left"></span><span id="fancybox-title-main">' + title + '</span><span id="fancybox-title-right"></span></span>';
     				}
     			}

     			return false;
     		},

     		fancybox_process_title = function() {
     			var title	= currentOpts.title,
     				width	= final_pos.width - (currentOpts.padding * 2),
     				titlec	= 'fancybox-title-' + currentOpts.titlePosition;

     			$('#fancybox-title').remove();

     			titleh = 0;

     			if (currentOpts.titleShow === false) {
     				return;
     			}

     			title = $.isFunction(currentOpts.titleFormat) ? currentOpts.titleFormat(title, currentArray, currentIndex, currentOpts) : fancybox_format_title(title);

     			if (!title || title === '') {
     				return;
     			}

     			$('<div id="fancybox-title" class="' + titlec + '" />').css({
     				'width'			: width,
     				'paddingLeft'	: currentOpts.padding,
     				'paddingRight'	: currentOpts.padding,
     				'height'        : '40px'
     			}).html(title).appendTo('body');

     			switch (currentOpts.titlePosition) {
     				case 'inside':
     					titleh = $("#fancybox-title").outerHeight(true) - currentOpts.padding;
     					final_pos.height += titleh;
     				break;

     				case 'over':
     					$('#fancybox-title').css('bottom', currentOpts.padding);
     				break;

     				default:
     					$('#fancybox-title').css('top', $("#fancybox-title").outerHeight(true) * -1);
     				break;
     			}

     			$('#fancybox-title').appendTo( outer ).hide();
     		},

     		fancybox_set_navigation = function() {
     			$(document).unbind('keydown.fb').bind('keydown.fb', function(e) {
     				if (e.keyCode == 27 && currentOpts.enableEscapeButton) {
     					e.preventDefault();
     					$.fancybox.close();

     				} else if (e.keyCode == 37) {
     					e.preventDefault();
     					$.fancybox.prev();

     				} else if (e.keyCode == 39) {
     					e.preventDefault();
     					$.fancybox.next();
     				}
     			});

     			if ($.fn.mousewheel) {
     				wrap.unbind('mousewheel.fb');

     				if (currentArray.length > 1) {
     					wrap.bind('mousewheel.fb', function(e, delta) {
     						e.preventDefault();

     						if (busy || delta === 0) {
     							return;
     						}

     						if (delta > 0) {
     							$.fancybox.prev();
     						} else {
     							$.fancybox.next();
     						}
     					});
     				}
     			}

     			if (!currentOpts.showNavArrows) { return; }

     			if ((currentOpts.cyclic && currentArray.length > 1) || currentIndex !== 0) {
     				nav_left.show();
     			}

     			if ((currentOpts.cyclic && currentArray.length > 1) || currentIndex != (currentArray.length -1)) {
     				nav_right.show();
     			}
     		},

     		fancybox_preload_images = function() {
     			var href, 
     				objNext;

     			if ((currentArray.length -1) > currentIndex) {
     				href = currentArray[ currentIndex + 1 ].href;

     				if (typeof href !== 'undefined' && href.match(imgRegExp)) {
     					objNext = new Image();
     					objNext.src = href;
     				}
     			}

     			if (currentIndex > 0) {
     				href = currentArray[ currentIndex - 1 ].href;

     				if (typeof href !== 'undefined' && href.match(imgRegExp)) {
     					objNext = new Image();
     					objNext.src = href;
     				}
     			}
     		},

     		_finish = function () {
     			inner.css('overflow', (currentOpts.scrolling == 'auto' ? (currentOpts.type == 'image' || currentOpts.type == 'iframe' || currentOpts.type == 'swf' ? 'hidden' : 'auto') : (currentOpts.scrolling == 'yes' ? 'auto' : 'visible')));

     			if (!$.support.opacity) {
     				inner.get(0).style.removeAttribute('filter');
     				wrap.get(0).style.removeAttribute('filter');
     			}

     			$('#fancybox-title').show();

     			if (currentOpts.hideOnContentClick)	{
     				inner.one('click', $.fancybox.close);
     			}
     			if (currentOpts.hideOnOverlayClick)	{
     				overlay.one('click', $.fancybox.close);
     			}

     			if (currentOpts.showCloseButton) {
     				close.show();
     			}

     			fancybox_set_navigation();

     			$(window).bind("resize.fb", $.fancybox.center);

     			if (currentOpts.centerOnScroll) {
     				$(window).bind("scroll.fb", $.fancybox.center);
     			} else {
     				$(window).unbind("scroll.fb");
     			}

     			if ($.isFunction(currentOpts.onComplete)) {
     				currentOpts.onComplete(currentArray, currentIndex, currentOpts);
     			}

     			busy = false;

     			fancybox_preload_images();
     		},

     		fancybox_draw = function(pos) {
     			var width	= Math.round(start_pos.width	+ (final_pos.width	- start_pos.width)	* pos),
     				height	= Math.round(start_pos.height	+ (final_pos.height	- start_pos.height)	* pos),

     				top		= Math.round(start_pos.top	+ (final_pos.top	- start_pos.top)	* pos),
     				left	= Math.round(start_pos.left	+ (final_pos.left	- start_pos.left)	* pos);

     			wrap.css({
     				'width'		: width		+ 'px',
     				'height'	: height	+ 'px',
     				'top'		: top		+ 'px',
     				'left'		: left		+ 'px'
     			});

     			width	= Math.max(width - currentOpts.padding * 2, 0);
     			height	= Math.max(height - (currentOpts.padding * 2 + (titleh * pos)), 0);

     			inner.css({
     				'width'		: width		+ 'px',
     				'height'	: height	+ 'px'
     			});

     			if (typeof final_pos.opacity !== 'undefined') {
     				wrap.css('opacity', (pos < 0.5 ? 0.5 : pos));
     			}
     		},

     		fancybox_get_obj_pos = function(obj) {
     			var pos		= obj.offset();

     			pos.top		+= parseFloat( obj.css('paddingTop') )	|| 0;
     			pos.left	+= parseFloat( obj.css('paddingLeft') )	|| 0;

     			pos.top		+= parseFloat( obj.css('border-top-width') )	|| 0;
     			pos.left	+= parseFloat( obj.css('border-left-width') )	|| 0;

     			pos.width	= obj.width();
     			pos.height	= obj.height();

     			return pos;
     		},

     		fancybox_get_zoom_from = function() {
     			var orig = selectedOpts.orig ? $(selectedOpts.orig) : false,
     				from = {},
     				pos,
     				view;

     			if (orig && orig.length) {
     				pos = fancybox_get_obj_pos(orig);

     				from = {
     					width	: (pos.width	+ (currentOpts.padding * 2)),
     					height	: (pos.height	+ (currentOpts.padding * 2)),
     					top		: (pos.top		- currentOpts.padding - shadow),
     					left	: (pos.left		- currentOpts.padding - shadow)
     				};

     			} else {
     				view = fancybox_get_viewport();

     				from = {
     					width	: 1,
     					height	: 1,
     					top		: view[3] + view[1] * 0.5,
     					left	: view[2] + view[0] * 0.5
     				};
     			}

     			return from;
     		},

     		fancybox_show = function() {
     			loading.hide();

     			if (wrap.is(":visible") && $.isFunction(currentOpts.onCleanup)) {
     				if (currentOpts.onCleanup(currentArray, currentIndex, currentOpts) === false) {
     					$.event.trigger('fancybox-cancel');

     					busy = false;
     					return;
     				}
     			}

     			currentArray	= selectedArray;
     			currentIndex	= selectedIndex;
     			currentOpts		= selectedOpts;

     			inner.get(0).scrollTop	= 0;
     			inner.get(0).scrollLeft	= 0;

     			if (currentOpts.overlayShow) {
     				if (isIE6) {
     					$('select:not(#fancybox-tmp select)').filter(function() {
     						return this.style.visibility !== 'hidden';
     					}).css({'visibility':'hidden'}).one('fancybox-cleanup', function() {
     						this.style.visibility = 'inherit';
     					});
     				}

     				overlay.css({
     					'background-color'	: currentOpts.overlayColor,
     					'opacity'			: currentOpts.overlayOpacity
     				}).unbind().show();
     			}

     			final_pos = fancybox_get_zoom_to();

     			fancybox_process_title();

     			if (wrap.is(":visible")) {
     				$( close.add( nav_left ).add( nav_right ) ).hide();

     				var pos = wrap.position(),
     					equal;

     				start_pos = {
     					top		:	pos.top ,
     					left	:	pos.left,
     					width	:	wrap.width(),
     					height	:	wrap.height()
     				};

     				equal = (start_pos.width == final_pos.width && start_pos.height == final_pos.height);

     				inner.fadeOut(currentOpts.changeFade, function() {
     					var finish_resizing = function() {
     						inner.html( tmp.contents() ).fadeIn(currentOpts.changeFade, _finish);
     					};

     					$.event.trigger('fancybox-change');

     					inner.empty().css('overflow', 'hidden');

     					if (equal) {
     						inner.css({
     							top			: currentOpts.padding,
     							left		: currentOpts.padding,
     							width		: Math.max(final_pos.width	- (currentOpts.padding * 2), 1),
     							height		: Math.max(final_pos.height	- (currentOpts.padding * 2) - titleh, 1)
     						});

     						finish_resizing();

     					} else {
     						inner.css({
     							top			: currentOpts.padding,
     							left		: currentOpts.padding,
     							width		: Math.max(start_pos.width	- (currentOpts.padding * 2), 1),
     							height		: Math.max(start_pos.height	- (currentOpts.padding * 2), 1)
     						});

     						fx.prop = 0;

     						$(fx).animate({ prop: 1 }, {
     							 duration	: currentOpts.changeSpeed,
     							 easing		: currentOpts.easingChange,
     							 step		: fancybox_draw,
     							 complete	: finish_resizing
     						});
     					}
     				});

     				return;
     			}

     			wrap.css('opacity', 1);

     			if (currentOpts.transitionIn == 'elastic') {
     				start_pos = fancybox_get_zoom_from();

     				inner.css({
     						top			: currentOpts.padding,
     						left		: currentOpts.padding,
     						width		: Math.max(start_pos.width	- (currentOpts.padding * 2), 1),
     						height		: Math.max(start_pos.height	- (currentOpts.padding * 2), 1)
     					})
     					.html( tmp.contents() );

     				wrap.css(start_pos).show();

     				if (currentOpts.opacity) {
     					final_pos.opacity = 0;
     				}

     				fx.prop = 0;

     				$(fx).animate({ prop: 1 }, {
     					 duration	: currentOpts.speedIn,
     					 easing		: currentOpts.easingIn,
     					 step		: fancybox_draw,
     					 complete	: _finish
     				});

     			} else {
     				inner.css({
     						top			: currentOpts.padding,
     						left		: currentOpts.padding,
     						width		: Math.max(final_pos.width	- (currentOpts.padding * 2), 1),
     						height		: Math.max(final_pos.height	- (currentOpts.padding * 2) - titleh, 1)
     					})
     					.html( tmp.contents() );

     				wrap.css( final_pos ).fadeIn( currentOpts.transitionIn == 'none' ? 0 : currentOpts.speedIn, _finish );
     			}
     		},

     		fancybox_process_inline = function() {
     			tmp.width(	selectedOpts.width );
     			tmp.height(	selectedOpts.height );

     			if (selectedOpts.width	== 'auto') {
     				selectedOpts.width = tmp.width();
     			}
     			if (selectedOpts.height	== 'auto') {
     				selectedOpts.height	= tmp.height();
     			}

     			fancybox_show();
     		},

     		fancybox_process_image = function() {
     			busy = true;

     			selectedOpts.width	= imgPreloader.width;
     			selectedOpts.height	= imgPreloader.height;

     			$("<img />").attr({
     				'id'	: 'fancybox-img',
     				'src'	: imgPreloader.src,
     				'alt'	: selectedOpts.title
     			}).appendTo( tmp );

     			fancybox_show();
     		},

     		fancybox_start = function() {
     			fancybox_abort();

     			var obj	= selectedArray[ selectedIndex ],
     				href, 
     				type, 
     				title,
     				str,
     				emb,
     				selector,
     				data;

     			selectedOpts = $.extend({}, $.fn.fancybox.defaults, (typeof $(obj).data('fancybox') == 'undefined' ? selectedOpts : $(obj).data('fancybox')));
     			title = obj.title || $(obj).title || selectedOpts.title || '';

     			if (obj.nodeName && !selectedOpts.orig) {
     				selectedOpts.orig = $(obj).children("img:first").length ? $(obj).children("img:first") : $(obj);
     			}

     			if (title === '' && selectedOpts.orig) {
     				title = selectedOpts.orig.attr('alt');
     			}

     			if (obj.nodeName && (/^(?:javascript|#)/i).test(obj.href)) {
     				href = selectedOpts.href || null;
     			} else {
     				href = selectedOpts.href || obj.href || null;
     			}

     			if (selectedOpts.type) {
     				type = selectedOpts.type;

     				if (!href) {
     					href = selectedOpts.content;
     				}

     			} else if (selectedOpts.content) {
     				type	= 'html';

     			} else if (href) {
     				if (href.match(imgRegExp)) {
     					type = 'image';

     				} else if (href.match(swfRegExp)) {
     					type = 'swf';

     				} else if ($(obj).hasClass("iframe")) {
     					type = 'iframe';

     				} else if (href.match(/#/)) {
     					obj = href.substr(href.indexOf("#"));

     					type = $(obj).length > 0 ? 'inline' : 'ajax';
     				} else {
     					type = 'ajax';
     				}
     			} else {
     				type = 'inline';
     			}

     			selectedOpts.type	= type;
     			selectedOpts.href	= href;
     			selectedOpts.title	= title;

     			if (selectedOpts.autoDimensions && selectedOpts.type !== 'iframe' && selectedOpts.type !== 'swf') {
     				selectedOpts.width		= 'auto';
     				selectedOpts.height		= 'auto';
     			}

     			if (selectedOpts.modal) {
     				selectedOpts.overlayShow		= true;
     				selectedOpts.hideOnOverlayClick	= false;
     				selectedOpts.hideOnContentClick	= false;
     				selectedOpts.enableEscapeButton	= false;
     				selectedOpts.showCloseButton	= false;
     			}

     			if ($.isFunction(selectedOpts.onStart)) {
     				if (selectedOpts.onStart(selectedArray, selectedIndex, selectedOpts) === false) {
     					busy = false;
     					return;
     				}
     			}

     			tmp.css('padding', (shadow + selectedOpts.padding + selectedOpts.margin));

     			$('.fancybox-inline-tmp').unbind('fancybox-cancel').bind('fancybox-change', function() {
     				$(this).replaceWith(inner.children());
     			});

     			switch (type) {
     				case 'html' :
     					tmp.html( selectedOpts.content );
     					fancybox_process_inline();
     				break;

     				case 'inline' :
     					$('<div class="fancybox-inline-tmp" />').hide().insertBefore( $(obj) ).bind('fancybox-cleanup', function() {
     						$(this).replaceWith(inner.children());
     					}).bind('fancybox-cancel', function() {
     						$(this).replaceWith(tmp.children());
     					});

     					$(obj).appendTo(tmp);

     					fancybox_process_inline();
     				break;

     				case 'image':
     					busy = false;

     					$.fancybox.showActivity();

     					imgPreloader = new Image();

     					imgPreloader.onerror = function() {
     						fancybox_error();
     					};

     					imgPreloader.onload = function() {
     						imgPreloader.onerror = null;
     						imgPreloader.onload = null;
     						fancybox_process_image();
     					};

     					imgPreloader.src = href;

     				break;

     				case 'swf':
     					str = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="' + selectedOpts.width + '" height="' + selectedOpts.height + '"><param name="movie" value="' + href + '"></param>';
     					emb = '';

     					$.each(selectedOpts.swf, function(name, val) {
     						str += '<param name="' + name + '" value="' + val + '"></param>';
     						emb += ' ' + name + '="' + val + '"';
     					});

     					str += '<embed src="' + href + '" type="application/x-shockwave-flash" width="' + selectedOpts.width + '" height="' + selectedOpts.height + '"' + emb + '></embed></object>';

     					tmp.html(str);

     					fancybox_process_inline();
     				break;

     				case 'ajax':
     					selector	= href.split('#', 2);
     					data		= selectedOpts.ajax.data || {};

     					if (selector.length > 1) {
     						href = selector[0];

     						if (typeof data == "string") {
     							data += '&selector=' + selector[1];
     						} else {
     							data.selector = selector[1];
     						}
     					}

     					busy = false;
     					$.fancybox.showActivity();

     					ajaxLoader = $.ajax($.extend(selectedOpts.ajax, {
     						url		: href,
     						data	: data,
     						error	: fancybox_error,
     						success : function(data, textStatus, XMLHttpRequest) {
     							if (ajaxLoader.status == 200) {
     								tmp.html( data );
     								fancybox_process_inline();
     							}
     						}
     					}));

     				break;

     				case 'iframe' :
     					$('<iframe id="fancybox-frame" name="fancybox-frame' + new Date().getTime() + '" frameborder="0" hspace="0" scrolling="' + selectedOpts.scrolling + '" src="' + selectedOpts.href + '"></iframe>').appendTo(tmp);
     					fancybox_show();
     				break;
     			}
     		},

     		fancybox_animate_loading = function() {
     			if (!loading.is(':visible')){
     				clearInterval(loadingTimer);
     				return;
     			}

     			$('div', loading).css('top', (loadingFrame * -40) + 'px');

     			loadingFrame = (loadingFrame + 1) % 12;
     		},

     		fancybox_init = function() {
     			if ($("#fancybox-wrap").length) {
     				return;
     			}

     			$('body').append(
     				tmp			= $('<div id="fancybox-tmp"></div>'),
     				loading		= $('<div id="fancybox-loading"><div></div></div>'),
     				overlay		= $('<div id="fancybox-overlay"></div>'),
     				wrap		= $('<div id="fancybox-wrap"></div>')
     			);

     			if (!$.support.opacity) {
     				wrap.addClass('fancybox-ie');
     				loading.addClass('fancybox-ie');
     			}

     			outer = $('<div id="fancybox-outer"></div>')
     				.append('<div class="fancy-bg" id="fancy-bg-n"></div><div class="fancy-bg" id="fancy-bg-ne"></div><div class="fancy-bg" id="fancy-bg-e"></div><div class="fancy-bg" id="fancy-bg-se"></div><div class="fancy-bg" id="fancy-bg-s"></div><div class="fancy-bg" id="fancy-bg-sw"></div><div class="fancy-bg" id="fancy-bg-w"></div><div class="fancy-bg" id="fancy-bg-nw"></div>')
     				.appendTo( wrap );

     			outer.append(
     				inner		= $('<div id="fancybox-inner"></div>'),
     				close		= $('<a id="fancybox-close"></a>'),

     				nav_left	= $('<a href="javascript:;" id="fancybox-left"><span class="fancy-ico" id="fancybox-left-ico"></span></a>'),
     				nav_right	= $('<a href="javascript:;" id="fancybox-right"><span class="fancy-ico" id="fancybox-right-ico"></span></a>')
     			);

     			close.click($.fancybox.close);
     			loading.click($.fancybox.cancel);

     			nav_left.click(function(e) {
     				e.preventDefault();
     				$.fancybox.prev();
     			});

     			nav_right.click(function(e) {
     				e.preventDefault();
     				$.fancybox.next();
     			});

     			if (isIE6) {
     				overlay.get(0).style.setExpression('height',	"document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'");
     				loading.get(0).style.setExpression('top',		"(-20 + (document.documentElement.clientHeight ? document.documentElement.clientHeight/2 : document.body.clientHeight/2 ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop )) + 'px'");

     				outer.prepend('<iframe id="fancybox-hide-sel-frame" src="javascript:\'\';" scrolling="no" frameborder="0" ></iframe>');
     			}
     		};

     	/*
     	 * Public methods 
     	 */

     	$.fn.fancybox = function(options) {
     		$(this)
     			.data('fancybox', $.extend({}, options, ($.metadata ? $(this).metadata() : {})))
     			.unbind('click.fb').bind('click.fb', function(e) {
     				e.preventDefault();

     				if (busy) {
     					return;
     				}

     				busy = true;

     				$(this).blur();

     				selectedArray	= [];
     				selectedIndex	= 0;

     				var rel = $(this).attr('rel') || '';

     				if (!rel || rel == '' || rel === 'nofollow') {
     					selectedArray.push(this);

     				} else {
     					selectedArray	= $("a[rel=" + rel + "], area[rel=" + rel + "]");
     					selectedIndex	= selectedArray.index( this );
     				}

     				fancybox_start();

     				return false;
     			});

     		return this;
     	};

     	$.fancybox = function(obj) {
     //		if (busy) {
     //			return;
     //		}

     		busy = true;

     		var opts = typeof arguments[1] !== 'undefined' ? arguments[1] : {};

     		selectedArray	= [];
     		selectedIndex	= opts.index || 0;

     		if ($.isArray(obj)) {
     			for (var i = 0, j = obj.length; i < j; i++) {
     				if (typeof obj[i] == 'object') {
     					$(obj[i]).data('fancybox', $.extend({}, opts, obj[i]));
     				} else {
     					obj[i] = $({}).data('fancybox', $.extend({content : obj[i]}, opts));
     				}
     			}

     			selectedArray = jQuery.merge(selectedArray, obj);

     		} else {
     			if (typeof obj == 'object') {
     				$(obj).data('fancybox', $.extend({}, opts, obj));
     			} else {
     				obj = $({}).data('fancybox', $.extend({content : obj}, opts));
     			}

     			selectedArray.push(obj);
     		}

     		if (selectedIndex > selectedArray.length || selectedIndex < 0) {
     			selectedIndex = 0;
     		}

     		fancybox_start();
     	};

     	$.fancybox.showActivity = function() {
     		clearInterval(loadingTimer);

     		loading.show();
     		loadingTimer = setInterval(fancybox_animate_loading, 66);
     	};

     	$.fancybox.hideActivity = function() {
     		loading.hide();
     	};

     	$.fancybox.next = function() {
     		return $.fancybox.pos( currentIndex + 1);
     	};

     	$.fancybox.prev = function() {
     		return $.fancybox.pos( currentIndex - 1);
     	};

     	$.fancybox.pos = function(pos) {
     		if (busy) {
     			return;
     		}

     		pos = parseInt(pos, 10);

     		if (pos > -1 && currentArray.length > pos) {
     			selectedIndex = pos;
     			fancybox_start();
     		}

     		if (currentOpts.cyclic && currentArray.length > 1 && pos < 0) {
     			selectedIndex = currentArray.length - 1;
     			fancybox_start();
     		}

     		if (currentOpts.cyclic && currentArray.length > 1 && pos >= currentArray.length) {
     			selectedIndex = 0;
     			fancybox_start();
     		}

     		return;
     	};

     	$.fancybox.cancel = function() {
     		if (busy) {
     			return;
     		}

     		busy = true;

     		$.event.trigger('fancybox-cancel');

     		fancybox_abort();

     		if (selectedOpts && $.isFunction(selectedOpts.onCancel)) {
     			selectedOpts.onCancel(selectedArray, selectedIndex, selectedOpts);
     		}

     		busy = false;
     	};

     	// Note: within an iframe use - parent.$.fancybox.close();
     	$.fancybox.close = function() {
     		if (busy || wrap.is(':hidden')) {
     			return;
     		}

     		busy = true;

     		if (currentOpts && $.isFunction(currentOpts.onCleanup)) {
     			if (currentOpts.onCleanup(currentArray, currentIndex, currentOpts) === false) {
     				busy = false;
     				return;
     			}
     		}

     		fancybox_abort();

     		$(close.add( nav_left ).add( nav_right )).hide();

     		$('#fancybox-title').remove();

     		wrap.add(inner).add(overlay).unbind();

     		$(window).unbind("resize.fb scroll.fb");
     		$(document).unbind('keydown.fb');

     		function _cleanup() {
     			overlay.fadeOut('fast');

     			wrap.hide();

     			$.event.trigger('fancybox-cleanup');

     			inner.empty();

     			if ($.isFunction(currentOpts.onClosed)) {
     				currentOpts.onClosed(currentArray, currentIndex, currentOpts);
     			}

     			currentArray	= selectedOpts	= [];
     			currentIndex	= selectedIndex	= 0;
     			currentOpts		= selectedOpts	= {};

     			busy = false;
     		}

     		inner.css('overflow', 'hidden');

     		if (currentOpts.transitionOut == 'elastic') {
     			start_pos = fancybox_get_zoom_from();

     			var pos = wrap.position();

     			final_pos = {
     				top		:	pos.top ,
     				left	:	pos.left,
     				width	:	wrap.width(),
     				height	:	wrap.height()
     			};

     			if (currentOpts.opacity) {
     				final_pos.opacity = 1;
     			}

     			fx.prop = 1;

     			$(fx).animate({ prop: 0 }, {
     				 duration	: currentOpts.speedOut,
     				 easing		: currentOpts.easingOut,
     				 step		: fancybox_draw,
     				 complete	: _cleanup
     			});

     		} else {
     			wrap.fadeOut( currentOpts.transitionOut == 'none' ? 0 : currentOpts.speedOut, _cleanup);
     		}
     	};

     	$.fancybox.resize = function() {
     		var c, h;

     		if (busy || wrap.is(':hidden')) {
     			return;
     		}

     		busy = true;

     		c = inner.wrapInner("<div style='overflow:auto'></div>").children();
     		h = c.height();

     		wrap.css({height:	h + (currentOpts.padding * 2) + titleh});
     		inner.css({height:	h});

     		c.replaceWith(c.children());

     		$.fancybox.center();
     	};

     	$.fancybox.center = function() {
     		busy = true;

     		var view	= fancybox_get_viewport(),
     			margin	= currentOpts.margin,
     			to		= {};

     		to.top	= view[3] + ((view[1] - ((wrap.height() - titleh) + (shadow * 2 ))) * 0.5);
     		to.left	= view[2] + ((view[0] - (wrap.width() + (shadow * 2 ))) * 0.5);

     		to.top	= Math.max(view[3] + margin, to.top);
     		to.left	= Math.max(view[2] + margin, to.left);

     		wrap.css(to);

     		busy = false;
     	};

     	$.fn.fancybox.defaults = {
     		padding				:	10,
     		margin				:	20,
     		opacity				:	false,
     		modal				:	false,
     		cyclic				:	false,
     		scrolling			:	'auto',	// 'auto', 'yes' or 'no'

     		width				:	560,
     		height				:	340,

     		autoScale			:	true,
     		autoDimensions		:	true,
     		centerOnScroll		:	false,

     		ajax				:	{},
     		swf					:	{ wmode: 'transparent' },

     		hideOnOverlayClick	:	true,
     		hideOnContentClick	:	false,

     		overlayShow			:	true,
     		overlayOpacity		:	0.3,
     		overlayColor		:	'#666',

     		titleShow			:	true,
     		titlePosition		:	'outside',	// 'outside', 'inside' or 'over'
     		titleFormat			:	null,

     		transitionIn		:	'fade',	// 'elastic', 'fade' or 'none'
     		transitionOut		:	'fade',	// 'elastic', 'fade' or 'none'

     		speedIn				:	300,
     		speedOut			:	300,

     		changeSpeed			:	300,
     		changeFade			:	'fast',

     		easingIn			:	'swing',
     		easingOut			:	'swing',

     		showCloseButton		:	true,
     		showNavArrows		:	true,
     		enableEscapeButton	:	true,

     		onStart				:	null,
     		onCancel			:	null,
     		onComplete			:	null,
     		onCleanup			:	null,
     		onClosed			:	null
     	};

 		fancybox_init();

     })($);
     
    /************************************************************************************************
     * Grabability
     */
     
     var dbg = function(s) {
     	if(typeof console !== 'undefined')
     		console.log("Grabability: " + s);
     };

     /**
      * Grabability
      *
      * Based on the wonderful Readability project:
      *
      * Readability. An Arc90 Lab Experiment. 
      * Website: http://lab.arc90.com/experiments/grabability
      * Source:  http://code.google.com/p/arc90labs-grabability
      *
      * Grabability is licensed under the Apache License, Version 2.0.
      *
      * Copyright (c) 2009 Arc90 Inc
      * Readability is licensed under the Apache License, Version 2.0.
      *
     **/
     var Grabability = {
     	version:     '0.1',
     	iframeLoads: 0,
     	bodyCache:  null,   /* Cache the body HTML in case we need to re-use it later */

     	/**
     	 * All of the regular expressions in use within grabability.
     	 * Defined up here so we don't instantiate them repeatedly in loops.
     	 **/
     	regexps: {
     		unlikelyCandidatesRe:   /combx|comment|disqus|foot|header|menu|meta|nav|rss|shoutbox|sidebar|sponsor/i,
     		okMaybeItsACandidateRe: /and|article|body|column|main/i,
     		positiveRe:             /article|body|content|entry|hentry|page|pagination|post|text/i,
     		negativeRe:             /combx|comment|contact|foot|footer|footnote|link|media|meta|promo|related|scroll|shoutbox|sponsor|tags|widget/i,
     		divToPElementsRe:       /<(a|blockquote|dl|div|img|ol|p|pre|table|ul)/i,
     		replaceBrsRe:           /(<br[^>]*>[ \n\r\t]*){2,}/gi,
     		replaceFontsRe:         /<(\/?)font[^>]*>/gi,
     		trimRe:                 /^\s+|\s+$/g,
     		normalizeRe:            /\s{2,}/g,
     		killBreaksRe:           /(<br\s*\/?>(\s|&nbsp;?)*){1,}/g,
     		videoRe:                /http:\/\/(www\.)?(youtube|vimeo)\.com/i
     	},

     	grab: function(el) {
     		// Get the element scope - we only run grabability within this scope, defaults to body
     	    this.scope = el ? $(el) : $('body');

     	    this.workarea        = $("<div style='display:none'></div>");
             this.article_content = $("<div style='display:none'></div>");

             $('body').append(this.workarea).append(this.article_content);

     	    // Copy the whole scoped content into the work area, sanitizing it
             this.copyContentToWorkarea();

     	    // Run the main readability logic
     		var out = this.run();

     		// Cleanup
     		this.workarea.remove();
     		this.article_content.remove();
     		this.bodyCache = null;

     		return out;
     	},

     	/**
     	 * Workflow:
     	 *  1. Prep the document by removing script tags, css, etc.
     	 *  2. Build grabability's DOM tree.
     	 *  3. Grab the article content from the current dom tree.
     	 *
     	 * @return void
     	 **/
     	run: function(preserveUnlikelyCandidates) {
     		preserveUnlikelyCandidates = (typeof preserveUnlikelyCandidates == 'undefined') ? false : preserveUnlikelyCandidates;

     		if(!this.bodyCache) this.bodyCache = this.workarea[0].innerHTML;

     		this.prepDocument();
     		this.grabArticle(preserveUnlikelyCandidates);

     		/**
     		 * If we attempted to strip unlikely candidates on the first run through, and we ended up with no content,
     		 * that may mean we stripped out the actual content so we couldn't parse it. So re-run init while preserving
     		 * unlikely candidates to have a better shot at getting our content out properly.
     		**/
     		if(this.getInnerText(this.article_content[0], false) == "")
     		{
     			if(!preserveUnlikelyCandidates) {
     				this.workarea[0].innerHTML = this.bodyCache;
     				return this.run(true);				
     			} else {
     				this.article_content.empty();
     			}
     		}		

     		return this.article_content[0].innerHTML;
     	},

     	/**
     	 * Determines whether a node is within the scope
     	 */
     	withinWorkarea: function(node) {
     	    return $(node).parents().index(this.workarea) >= 0;
     	},

     	/**
     	 * Copies and sanitizes content into the working area
     	 */
     	copyContentToWorkarea: function() {
     	    var sanitize = function(content) {
                 // script tags
                 content = content.replace(new RegExp('<script[^>]*>([\\S\\s]*?)<\/script>', 'img'), '');

                 // iframes
                 content = content.replace(new RegExp('<iframe[^>]*>([\\S\\s]*?)<\/iframe>', 'img'), '');
                 content = content.replace(new RegExp('<iframe[^>]*>', 'img'), '');

                 return content;
             };

             this.workarea[0].innerHTML = sanitize(this.scope[0].innerHTML);
     	},

     	/**
     	 * Prepare the HTML document for grabability to scrape it.
     	 * This includes things like stripping javascript, CSS, and handling terrible markup.
     	 * 
     	 * @return void
     	 **/
     	prepDocument: function () {
     		/* remove all scripts that are not grabability */
     		var scripts = this.workarea.find('script');
     		for(i = scripts.length-1; i >= 0; i--)
     		{
     			if(typeof(scripts[i].src) == "undefined" || scripts[i].src.indexOf('grabability') == -1)
     			{
     				scripts[i].parentNode.removeChild(scripts[i]);			
     			}
     		}

     		/* Remove all style tags in head (not doing this on IE) - TODO: Why not? */
     		var styleTags = this.workarea.find("style");
     		for (var j=0;j < styleTags.length; j++)
     			styleTags[j].textContent = "";

     		/* Turn all double br's into p's */
     		/* Note, this is pretty costly as far as processing goes. Maybe optimize later. */

     		this.workarea[0].innerHTML = this.workarea[0].innerHTML.replace(this.regexps.replaceBrsRe, '</p><p>').replace(this.regexps.replaceFontsRe, '<$1span>');
     	},

     	/**
     	 * Prepare the article node for display. Clean out any inline styles,
     	 * iframes, forms, strip extraneous <p> tags, etc.
     	 *
     	 * @param Element
     	 * @return void
     	 **/
     	prepArticle: function () {
     		this.cleanStyles(this.article_content[0]);
     		this.killBreaks(this.article_content[0]);

     		/* Clean out junk from the article content */
     		this.clean(this.article_content[0], "form");
     		this.clean(this.article_content[0], "object");
     		this.clean(this.article_content[0], "h1");
     		/**
     		 * If there is only one h2, they are probably using it
     		 * as a header and not a subheader, so remove it since we already have a header.
     		***/
     		if(this.article_content[0].getElementsByTagName('h2').length == 1)
     			this.clean(this.article_content[0], "h2");
     		this.clean(this.article_content[0], "iframe");

     		this.cleanHeaders(this.article_content[0]);

     		/* Do these last as the previous stuff may have removed junk that will affect these */
     		this.cleanConditionally(this.article_content[0], "table");
     		this.cleanConditionally(this.article_content[0], "ul");
     		this.cleanConditionally(this.article_content[0], "div");

     		/* Remove extra paragraphs */
     		var articleParagraphs = this.article_content[0].getElementsByTagName('p');
     		for(i = articleParagraphs.length-1; i >= 0; i--)
     		{
     			var imgCount    = articleParagraphs[i].getElementsByTagName('img').length;
     			var embedCount  = articleParagraphs[i].getElementsByTagName('embed').length;
     			var objectCount = articleParagraphs[i].getElementsByTagName('object').length;

     			if(imgCount == 0 && embedCount == 0 && objectCount == 0 && this.getInnerText(articleParagraphs[i], false) == '')
     			{
     				articleParagraphs[i].parentNode.removeChild(articleParagraphs[i]);
     			}
     		}

     		try {
     			this.article_content[0].innerHTML = this.article_content[0].innerHTML.replace(/<br[^>]*>\s*<p/gi, '<p');		
     		}
     		catch (e) {
     			dbg("Cleaning innerHTML of breaks failed. This is an IE strict-block-elements bug. Ignoring.");
     		}
     	},

     	/**
     	 * Initialize a node with the grabability object. Also checks the
     	 * className/id for special names to add to its score.
     	 *
     	 * @param Element
     	 * @return void
     	**/
     	initializeNode: function (node) {
     		node.grabability = {"contentScore": 0};			

     		switch(node.tagName) {
     			case 'DIV':
     				node.grabability.contentScore += 5;
     				break;

     			case 'PRE':
     			case 'TD':
     			case 'BLOCKQUOTE':
     				node.grabability.contentScore += 3;
     				break;

     			case 'ADDRESS':
     			case 'OL':
     			case 'UL':
     			case 'DL':
     			case 'DD':
     			case 'DT':
     			case 'LI':
     			case 'FORM':
     				node.grabability.contentScore -= 3;
     				break;

     			case 'H1':
     			case 'H2':
     			case 'H3':
     			case 'H4':
     			case 'H5':
     			case 'H6':
     			case 'TH':
     				node.grabability.contentScore -= 5;
     				break;
     		}

     		node.grabability.contentScore += this.getClassWeight(node);
     	},

     	/***
     	 * grabArticle - Using a variety of metrics (content score, classname, element types), find the content that is
     	 *               most likely to be the stuff a user wants to read. Then return it wrapped up in a div.
     	 *
     	 * @return Element
     	**/
     	grabArticle: function (preserveUnlikelyCandidates) {
     		/**
     		 * First, node prepping. Trash nodes that look cruddy (like ones with the class name "comment", etc), and turn divs
     		 * into P tags where they have been used inappropriately (as in, where they contain no other block level elements.)
     		 *
     		 * Note: Assignment from index for performance. See http://www.peachpit.com/articles/article.aspx?p=31567&seqNum=5
     		 * TODO: Shouldn't this be a reverse traversal?
     		**/
     		for(var nodeIndex = 0; (node = document.getElementsByTagName('*')[nodeIndex]); nodeIndex++)
     		{
     		    if(!this.withinWorkarea(node)) continue;

     			/* Remove unlikely candidates */
     			if (!preserveUnlikelyCandidates) {
     				var unlikelyMatchString = node.className + node.id;
     				if (unlikelyMatchString.search(this.regexps.unlikelyCandidatesRe) !== -1 &&
     				    unlikelyMatchString.search(this.regexps.okMaybeItsACandidateRe) == -1 &&
     					node.tagName !== "BODY")
     				{
     					dbg("Removing unlikely candidate - " + unlikelyMatchString);
     					node.parentNode.removeChild(node);
     					nodeIndex--;
     					continue;
     				}				
     			}

     			/* Turn all divs that don't have children block level elements into p's */
     			if (node.tagName === "DIV") {
     				if (node.innerHTML.search(this.regexps.divToPElementsRe) === -1)	{
     					dbg("Altering div to p");
     					var newNode = document.createElement('p');
     					try {
     						newNode.innerHTML = node.innerHTML;				
     						node.parentNode.replaceChild(newNode, node);
     						nodeIndex--;
     					}
     					catch(e)
     					{
     						dbg("Could not alter div to p, probably an IE restriction, reverting back to div.");
     					}
     				}
     				else
     				{
     					/* EXPERIMENTAL */
     					/*for(var i = 0, il = node.childNodes.length; i < il; i++) {
     						var childNode = node.childNodes[i];
     						if(childNode.nodeType == Node.TEXT_NODE) {
     							dbg("replacing text node with a p tag with the same content.");
     							var p = document.createElement('p');
     							p.innerHTML = childNode.nodeValue;
     							p.style.display = 'inline';
     							p.className = 'grabability-styled';
     							childNode.parentNode.replaceChild(p, childNode);
     						}
     					}*/
     				}
     			} 
     		}

     		/**
     		 * Loop through all paragraphs, and assign a score to them based on how content-y they look.
     		 * Then add their score to their parent node.
     		 *
     		 * A score is determined by things like number of commas, class names, etc. Maybe eventually link density.
     		**/
     		var allParagraphs = document.getElementsByTagName("p");
     		var candidates    = [];

     		for (var j=0; j	< allParagraphs.length; j++) {
     		    if(!this.withinWorkarea(allParagraphs[j])) continue;

     			var parentNode      = allParagraphs[j].parentNode;
     			var grandParentNode = parentNode.parentNode;
     			var innerText       = this.getInnerText(allParagraphs[j]);

     			/* If this paragraph is less than 25 characters, don't even count it. */
     			if(innerText.length < 25) continue;

     			/* Initialize grabability data for the parent. */
     			if(this.withinWorkarea(parentNode)) {
         			if(typeof parentNode.grabability == 'undefined')
         			{
         				this.initializeNode(parentNode);
         				candidates.push(parentNode);
         			}
     			}

     			/* Initialize grabability data for the grandparent. */
     			if(this.withinWorkarea(grandParentNode)) {
         			if(typeof grandParentNode.grabability == 'undefined')
         			{
         				this.initializeNode(grandParentNode);
         				candidates.push(grandParentNode);
         			}
     			}

     			var contentScore = 0;

     			/* Add a point for the paragraph itself as a base. */
     			contentScore++;

     			/* Add points for any commas within this paragraph */
     			contentScore += innerText.split(',').length;

     			/* For every 100 characters in this paragraph, add another point. Up to 3 points. */
     			contentScore += Math.min(Math.floor(innerText.length / 100), 3);

     			/* Add the score to the parent. The grandparent gets half. */
     			if(this.withinWorkarea(parentNode)) parentNode.grabability.contentScore += contentScore;
     			if(this.withinWorkarea(grandParentNode)) grandParentNode.grabability.contentScore += contentScore/2;
     		}

     		/**
     		 * After we've calculated scores, loop through all of the possible candidate nodes we found
     		 * and find the one with the highest score.
     		**/
     		var topCandidate = null;
     		for(var i=0, il=candidates.length; i < il; i++)
     		{
     			/**
     			 * Scale the final candidates score based on link density. Good content should have a
     			 * relatively small link density (5% or less) and be mostly unaffected by this operation.
     			**/
     			candidates[i].grabability.contentScore = candidates[i].grabability.contentScore * (1-this.getLinkDensity(candidates[i]));

     			dbg('Candidate: ' + candidates[i] + " (" + candidates[i].className + ":" + candidates[i].id + ") with score " + candidates[i].grabability.contentScore);

     			if(!topCandidate || candidates[i].grabability.contentScore > topCandidate.grabability.contentScore)
     				topCandidate = candidates[i];
     		}

     		if(topCandidate) {
         		/**
         		 * Now that we have the top candidate, look through its siblings for content that might also be related.
         		 * Things like preambles, content split by ads that we removed, etc.
         		**/
         		var siblingScoreThreshold = Math.max(10, topCandidate.grabability.contentScore * 0.2);
         		var siblingNodes          = topCandidate.parentNode.childNodes;

         		for(var i=0, il=siblingNodes.length; i < il; i++)
         		{
         			var siblingNode = siblingNodes[i];
         			if(!this.withinWorkarea(siblingNode)) continue;

         			var append = false;

         			dbg("Looking at sibling node: " + siblingNode + " (" + siblingNode.className + ":" + siblingNode.id + ")" + ((typeof siblingNode.grabability != 'undefined') ? (" with score " + siblingNode.grabability.contentScore) : ''));
         			dbg("Sibling has score " + (siblingNode.grabability ? siblingNode.grabability.contentScore : 'Unknown'));

         			if(siblingNode === topCandidate)
         			{
         				append = true;
         			}

         			if(typeof siblingNode.grabability != 'undefined' && siblingNode.grabability.contentScore >= siblingScoreThreshold)
         			{
         				append = true;
         			}

         			if(siblingNode.nodeName == "P") {
         				var linkDensity = this.getLinkDensity(siblingNode);
         				var nodeContent = this.getInnerText(siblingNode);
         				var nodeLength  = nodeContent.length;

         				if(nodeLength > 80 && linkDensity < 0.25)
         				{
         					append = true;
         				}
         				else if(nodeLength < 80 && linkDensity == 0 && nodeContent.search(/\.( |$)/) !== -1)
         				{
         					append = true;
         				}
         			}

         			if(append)
         			{
         				dbg("Appending sibling node: " );

         				/* Append sibling and subtract from our list because it removes the node when you append to another node */
         				this.article_content[0].innerHTML += siblingNode.innerHTML;
         			}
         		}				

         		/**
         		 * So we have all of the content that we need. Now we clean it up for presentation.
         		**/
         		this.prepArticle();
     		}
     	},

     	stripScripts: function(html) {
     	    return html.replace(new RegExp('<script[^>]*>([\\S\\s]*?)<\/script>', 'img'), '');
     	},

     	/**
     	 * Get the inner text of a node - cross browser compatibly.
     	 * This also strips out any excess whitespace to be found.
     	 *
     	 * @param Element
     	 * @return string
     	**/
     	getInnerText: function (e, normalizeSpaces) {
     		var textContent    = "";

     		normalizeSpaces = (typeof normalizeSpaces == 'undefined') ? true : normalizeSpaces;

     		if (navigator.appName == "Microsoft Internet Explorer")
     			textContent = e.innerText.replace( this.regexps.trimRe, "" );
     		else
     			textContent = e.textContent.replace( this.regexps.trimRe, "" );

     		if(normalizeSpaces)
     			return textContent.replace( this.regexps.normalizeRe, " ");
     		else
     			return textContent;
     	},

     	/**
     	 * Get the number of times a string s appears in the node e.
     	 *
     	 * @param Element
     	 * @param string - what to split on. Default is ","
     	 * @return number (integer)
     	**/
     	getCharCount: function (e,s) {
     	    s = s || ",";
     		return this.getInnerText(e).split(s).length;
     	},

     	/**
     	 * Remove the style attribute on every e and under.
     	 * TODO: Test if getElementsByTagName(*) is faster.
     	 *
     	 * @param Element
     	 * @return void
     	**/
     	cleanStyles: function (e) {
     	    e = e || document;
     	    var cur = e.firstChild;

     		if(!e)
     			return;

     		// Remove any root styles, if we're able.
     		if(typeof e.removeAttribute == 'function' && e.className != 'grabability-styled')
     			e.removeAttribute('style');

     	    // Go until there are no more child nodes
     	    while ( cur != null ) {
     			if ( cur.nodeType == 1 ) {
     				// Remove style attribute(s) :
     				if(cur.className != "grabability-styled") {
     					cur.removeAttribute("style");					
     				}
     				this.cleanStyles( cur );
     			}
     			cur = cur.nextSibling;
     		}			
     	},

     	/**
     	 * Get the density of links as a percentage of the content
     	 * This is the amount of text that is inside a link divided by the total text in the node.
     	 * 
     	 * @param Element
     	 * @return number (float)
     	**/
     	getLinkDensity: function (e) {
     		var links      = e.getElementsByTagName("a");
     		var textLength = this.getInnerText(e).length;
     		var linkLength = 0;
     		for(var i=0, il=links.length; i<il;i++)
     		{
     			linkLength += this.getInnerText(links[i]).length;
     		}		

     		return linkLength / textLength;
     	},

     	/**
     	 * Get an elements class/id weight. Uses regular expressions to tell if this 
     	 * element looks good or bad.
     	 *
     	 * @param Element
     	 * @return number (Integer)
     	**/
     	getClassWeight: function (e) {
     		var weight = 0;

     		/* Look for a special classname */
     		if (e.className != "")
     		{
     			if(e.className.search(this.regexps.negativeRe) !== -1)
     				weight -= 25;

     			if(e.className.search(this.regexps.positiveRe) !== -1)
     				weight += 25;				
     		}

     		/* Look for a special ID */
     		if (typeof(e.id) == 'string' && e.id != "")
     		{
     			if(e.id.search(this.regexps.negativeRe) !== -1)
     				weight -= 25;

     			if(e.id.search(this.regexps.positiveRe) !== -1)
     				weight += 25;				
     		}

     		return weight;
     	},

     	/**
     	 * Remove extraneous break tags from a node.
     	 *
     	 * @param Element
     	 * @return void
     	 **/
     	killBreaks: function (e) {
     		try {
     			e.innerHTML = e.innerHTML.replace(this.regexps.killBreaksRe,'<br />');		
     		}
     		catch (e) {
     			dbg("KillBreaks failed - this is an IE bug. Ignoring.");
     		}
     	},

     	/**
     	 * Clean a node of all elements of type "tag".
     	 * (Unless it's a youtube/vimeo video. People love movies.)
     	 *
     	 * @param Element
     	 * @param string tag to clean
     	 * @return void
     	 **/
     	clean: function (e, tag) {
     		var targetList = e.getElementsByTagName( tag );
     		var isEmbed    = (tag == 'object' || tag == 'embed');

     		for (var y=targetList.length-1; y >= 0; y--) {
     			/* Allow youtube and vimeo videos through as people usually want to see those. */
     			if(isEmbed && targetList[y].innerHTML.search(this.regexps.videoRe) !== -1)
     			{
     				continue;
     			}

     			targetList[y].parentNode.removeChild(targetList[y]);
     		}
     	},

     	/**
     	 * Clean an element of all tags of type "tag" if they look fishy.
     	 * "Fishy" is an algorithm based on content length, classnames, link density, number of images & embeds, etc.
     	 *
     	 * @return void
     	 **/
     	cleanConditionally: function (e, tag) {
     		var tagsList      = e.getElementsByTagName(tag);
     		var curTagsLength = tagsList.length;

     		/**
     		 * Gather counts for other typical elements embedded within.
     		 * Traverse backwards so we can remove nodes at the same time without effecting the traversal.
     		 *
     		 * TODO: Consider taking into account original contentScore here.
     		**/
     		for (var i=curTagsLength-1; i >= 0; i--) {
     			var weight = this.getClassWeight(tagsList[i]);

     			dbg("Cleaning Conditionally " + tagsList[i] + " (" + tagsList[i].className + ":" + tagsList[i].id + ")" + ((typeof tagsList[i].grabability != 'undefined') ? (" with score " + tagsList[i].grabability.contentScore) : ''));

     			if(weight < 0)
     			{
     				tagsList[i].parentNode.removeChild(tagsList[i]);
     			}
     			else if ( this.getCharCount(tagsList[i],',') < 10) {
     				/**
     				 * If there are not very many commas, and the number of
     				 * non-paragraph elements is more than paragraphs or other ominous signs, remove the element.
     				**/

     				var p      = tagsList[i].getElementsByTagName("p").length;
     				var img    = tagsList[i].getElementsByTagName("img").length;
     				var li     = tagsList[i].getElementsByTagName("li").length-100;
     				var input  = tagsList[i].getElementsByTagName("input").length;

     				var embedCount = 0;
     				var embeds     = tagsList[i].getElementsByTagName("embed");
     				for(var ei=0,il=embeds.length; ei < il; ei++) {
     					if (embeds[ei].src.search(this.regexps.videoRe) == -1) {
     					  embedCount++;	
     					}
     				}

     				var linkDensity   = this.getLinkDensity(tagsList[i]);
     				var contentLength = this.getInnerText(tagsList[i]).length;
     				var toRemove      = false;

     				if ( img > p ) {
     				 	toRemove = true;
     				} else if(li > p && tag != "ul" && tag != "ol") {
     					toRemove = true;
     				} else if( input > Math.floor(p/3) ) {
     				 	toRemove = true; 
     				} else if(contentLength < 25 && (img == 0 || img > 2) ) {
     					toRemove = true;
     				} else if(weight < 25 && linkDensity > .2) {
     					toRemove = true;
     				} else if(weight >= 25 && linkDensity > .5) {
     					toRemove = true;
     				} else if((embedCount == 1 && contentLength < 75) || embedCount > 1) {
     					toRemove = true;
     				}

     				if(toRemove) {
     					tagsList[i].parentNode.removeChild(tagsList[i]);
     				}
     			}
     		}
     	},

     	/**
     	 * Clean out spurious headers from an Element. Checks things like classnames and link density.
     	 *
     	 * @param Element
     	 * @return void
     	**/
     	cleanHeaders: function (e) {
     		for (var headerIndex = 1; headerIndex < 7; headerIndex++) {
     			var headers = e.getElementsByTagName('h' + headerIndex);
     			for (var i=headers.length-1; i >=0; i--) {
     				if (this.getClassWeight(headers[i]) < 0 || this.getLinkDensity(headers[i]) > 0.33) {
     					headers[i].parentNode.removeChild(headers[i]);
     				}
     			}
     		}
     	},

     	htmlspecialchars: function (s) {
     		if (typeof(s) == "string") {
     			s = s.replace(/&/g, "&amp;");
     			s = s.replace(/"/g, "&quot;");
     			s = s.replace(/'/g, "&#039;");
     			s = s.replace(/</g, "&lt;");
     			s = s.replace(/>/g, "&gt;");
     		}

     		return s;
     	}

     };
     
     /**
      * jQuery.ajax mid - CROSS DOMAIN AJAX 
      * ---
      * @author James Padolsey (http://james.padolsey.com)
      * @version 0.11
      * @updated 12-JAN-10
      * ---
      * Note: Read the README!
      * ---
      * @info http://james.padolsey.com/javascript/cross-domain-requests-with-jquery/
      */

     $.ajax = (function(_ajax){

         var protocol = location.protocol,
             hostname = location.hostname,
             exRegex = RegExp(protocol + '//' + hostname),
             YQL = 'http' + (/^https/.test(protocol)?'s':'') + '://query.yahooapis.com/v1/public/yql?callback=?',
             query = 'select * from html where url="{URL}" and xpath="*"';

         function isExternal(url) {
             return !exRegex.test(url) && /:\/\//.test(url);
         }

         return function(o) {

             var url = o.url;

             if ( /get/i.test(o.type) && !/json/i.test(o.dataType) && isExternal(url) ) {

                 // Manipulate options so that JSONP-x request is made to YQL

                 o.url = YQL;
                 o.dataType = 'json';

                 o.data = {
                     q: query.replace(
                         '{URL}',
                         url + (o.data ?
                             (/\?/.test(url) ? '&' : '?') + jQuery.param(o.data)
                         : '')
                     ),
                     format: 'xml'
                 };

                 // Since it's a JSONP request
                 // complete === success
                 if (!o.success && o.complete) {
                     o.success = o.complete;
                     delete o.complete;
                 }

                 o.success = (function(_success){
                     return function(data) {

                         if (_success) {
                             // Fake XHR callback.

                             r = data.results[0] ? data.results[0] : '';

                             _success.call(this, {
                                 responseText: r
                                     // YQL screws with <script>s
                                     // Get rid of them
                                     .replace(/<script[^>]+?\/>|<script(.|\s)*?\/script>/gi, '')
                             }, 'success');
                         }

                     };
                 })(o.success);

             }

             return _ajax.apply(this, arguments);

         };

     })($.ajax);
    
    var QuickRead = {

        titleRE: /<title>((.|\n|\r)*?)<\/title>/,
        bodyRE: '<body[^>]*>([\\S\\s]*?)<\/body>',
        appliedToLinks: false,
        
        /**
         * Applies QuickRead to links on the page, based on some rules
         */
        
        applyToLinks: function() {
            var self = this;
            if(!this.appliedToLinks) {
                this.track();
                
                if(this.filter) {
                    var els = $(this.filter);
                } else {
                    var els = $('a');
                }
                
                els.each(function(i,el) {
                    var fancybox = $('#fancybox-wrap');
                    var el = $(el);
                    if(
                        el.parents().index(fancybox) == -1 &&
                        document.location.host != self.domain(el[0].href) &&
                        self.isUrl(el[0].href)
                    ) {
                        var link = $('<span class="quickread_link">&nbsp;<a href="' + el[0].href + '"><img src="http://www.readshout.com/images/quickread/button.gif" /></a></span>');
                        el.after(link);
                        link.click(function(e) {
                            e.stopPropagation();
                            self.open(el[0].href);
                            return false;
                        });
                    }
                });
                this.appliedToLinks = true;
            }
        },

        open: function(url) {
            var self = this;

            this.contentarea = $('<div style="display:none"></div>');
            $('body').append(this.contentarea);

            $.fancybox('<div class="quickread_loading"><img src="http://www.readshout.com/images/spinner.gif" /> Loading</div>');

            // Cross domain GET to get the url content
            $.get(url, function(d) {
                var text = d.responseText;
                var body = new RegExp(self.bodyRE, 'img').exec(text);
                var title = self.titleRE.exec(text);
                var contentLoaded = false;
                var content = '';

                if(body && body[1] && title) {
                    var bodyText = self.sanitize(body[1]);

                    self.contentarea.html(bodyText);

                    // Run Grabability, scoped within the content we just pulled
                    content = Grabability.grab(self.contentarea);
                    title = title[1];
                    contentLoaded = true;
                }

                // Open it in the lightbox for easy viewing
                if(!contentLoaded || content.length < 200) {
                    $.fancybox(url, { 
                       'autoScale'          : false, 
                       'transitionIn'       : 'none', 
                       'transitionOut'      : 'none', 
                       'width'              : 990, 
                       'height'             : 750, 
                       'overlayOpacity'     : 0.7,
                       'padding'            : 0, 
                       'margin'             : 0, 
                       'type'               : 'iframe',
                       'href'               : url
                   });
                } else {

                    var lb_title = "<h1>" + title + "</h1>";
                    var lb_main = "<div class='quickread'>" + content + "</div>";
                    var lb_header = "<div class='quickread_header'><div class='quickread_left'><a href='" + url + "'>View Original on " + self.domain(url) + "</a></div><div class='quickread_right'>QuickRead by <a href='http://www.readshout.com/'>Readshout</a> | <a href='http://www.twitter.com/readshout'>Follow Us</a></div><div style='clear:both'></div></div>";
                    var lb_content = "<div class='quickread_content'>" + lb_header + lb_title + lb_main + "</div>";

                    $.fancybox(lb_content, {
                        title: 'QuickRead',
                        titlePosition: 'outside',
                        scrolling: 'no',
                        autoScale: false,
                        width: 720,
                        height: 'auto',
                        padding: 20,
                        autoDimensions: false,
                        overlayOpacity: 0.7
                    });
                }

                self.contentarea.remove();
            });
        },

        // We only want the raw text from the body to pass into readability. We need to strip out
        // anything that would load assets that could affect the page.

        sanitize: function(content) {
            // script tags
            content = content.replace(new RegExp('<script[^>]*>([\\S\\s]*?)<\/script>', 'img'), '');

            // iframes
            content = content.replace(new RegExp('<iframe[^>]*>([\\S\\s]*?)<\/iframe>', 'img'), '');
            content = content.replace(new RegExp('<iframe[^>]*>', 'img'), '');

            // weird xml issue with yql (need to strip out <div />'s )
            content = content.replace(new RegExp('<div[^>]*/>', 'img'), '');

            return content;
        },

        domain: function(url) {
            var matches = /\/\/(.*?)\//.exec(url);
            return matches ? matches[1] : null;
        },
        
        loadStyles: function() {
            var url = window.__quickread_development ? 'http://localhost:10000/bookmarklets/quickread/quickread.css' : 'http://www.readshout.com/bookmarklets/quickread/quickread.css';
            
            var headID = document.getElementsByTagName("head")[0];         
            var cssNode = document.createElement('link');
            cssNode.type = 'text/css';
            cssNode.rel = 'stylesheet';
            cssNode.href = url;
            cssNode.media = 'screen';
            headID.appendChild(cssNode);
        },
        
        setFilter: function(f) {
            this.filter = f;
        },
        
        isUrl: function(s) {
        	var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
        	return regexp.test(s);
        },
        
        track: function() {
            var src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
            
            $.getScript(src, function() {
                if(window['_gat']) {
                    var pageTracker = _gat._getTracker('UA-17855423-1');
            		pageTracker._initData();
            		pageTracker._trackEvent('Bookmarklets', 'QuickRead', window.location.href);
                }
            });
        }
    };
    
    window.QuickRead = QuickRead;
    
    QuickRead.loadStyles();
    
    // automatically apply if it's the bookmarklet
    if(window.__quickread_bookmarklet)
        QuickRead.applyToLinks();
    
  }
);



