/**
 * Colflow
 */

;(function($){

var $$;


/**
 * 
 * @desc Convert images from a simple html <ul> into a thumbnail gallery
 * @author David Hellsing
 * @version 1.0
 *
 * @name Galleria
 * @type jQuery
 *
 * @cat plugins/Media
 * 
 * @example $('ul.gallery').colflow({options});
 * @desc Create a a gallery from an unordered list of images with thumbnails
 * @options
 *   insert:   (selector string) by default, Galleria will create a container div before your ul that holds the image.
 *             You can, however, specify a selector where the image will be placed instead (f.ex '#main_img')
 *   history:  Boolean for setting the history object in action with enabled back button, bookmarking etc.
 *   onImage:  (function) a function that gets fired when the image is displayed and brings the jQuery image object.
 *             You can use it to add click functionality and effects.
 *             f.ex onImage(image) { image.css('display','none').fadeIn(); } will fadeIn each image that is displayed
 *   onThumb:  (function) a function that gets fired when the thumbnail is displayed and brings the jQuery thumb object.
 *             Works the same as onImage except it targets the thumbnail after it's loaded.
 *
**/

$$ = $.fn.colflow = function($options) {

	// check for basic CSS support
	if (!$$.hasCSS()) { return false; }
	// init the modified history object
	$.historyInit($$.onPageLoad);
		
	// set default options
	var $defaults = {
		insert      : '.colflow-container',
		chapterId   : 0,
		storyId		: 0,
		fontsize	: 12,
		basefontsizestring : "12px",
		fontsize : 12,
		lineheight : 12,
		history     : true,
		clickNext   : true,
		onImage     : function(image,caption,thumb) {},
		onThumb     : function(thumb) {}
	};


	// extend the options
	var $opts = $.extend($defaults, $options);
	
	//$.colflow.opts = $opts;
	
	// bring the options to the colflow object
	for (var i in $opts) {
		$.colflow[i]  = $opts[i];
	}

	//alert("insert "+$.colflow["insert"]);
	
	// if no insert selector, create a new division and insert it before the ul
	var _insert = ( $($.colflow["insert"]).is($.colflow["insert"]) ) ? 
		$($.colflow["insert"]) : 
		jQuery(document.createElement('div')).insertBefore(this);
		
	// create a wrapping div for the image
	var _div2 = $(document.createElement('div')).addClass('caption');

	var _div = $(document.createElement('span')).addClass('colflow-wrapper').attr("id","colflow-wrapper");
	
	// create a caption span
	//var _span = $(document.createElement('span')).addClass('caption');
	//NB Brian MacMillan edit
	// create a caption div
	
	// inject the wrapper in in the insert selector
	_insert.addClass('colflow-container').append(_div2).append(_div);
	//-------------
	return this.each(function(){

		// add the Galleria class
		$(this).addClass('colflow');

		// loop through list
		$(this).children('li').each(function(i) {

			// bring the scope
			var _container = $(this);

			// build element specific options
			//var _o = $.meta ? $.extend({}, $opts, _container.data()) : $opts;

			// remove the clickNext if image is only child
			//NB Brian disabled during debugging//
			//_o.clickNext = $(this).is(':only-child') ? false : _o.clickNext;

			// try to fetch an anchor
			var _a = $(this).find('a').is('a') ? $(this).find('a') : false;

			// reference the original image as a variable and hide it
			var _img = $(this).children('img').css('display','none');

			// extract the original source
			var _src = _a ? _a.attr('href') : _img.attr('src');

			// find a title
			var _title = _a ? _a.attr('title') : _img.attr('title');

			// create loader image            
			var _loader = new Image();

			// check url and activate container if match
			//if (_o.history && (window.location.hash && window.location.hash.replace(/\#/,'') == _src)) {
			if ($.colflow["history"] && (window.location.hash && window.location.hash.replace(/\#/,'') == _src)) {
				_container.siblings('.active').removeClass('active');
				_container.addClass('active');
			}

			// begin loader
			//alert("img "+$(_img).attr("src"));
			$(_loader).load(function () {

				// try to bring the alt
				$(this).attr('alt',_img.attr('alt'));

				//-----------------------------------------------------------------
				// the image is loaded, let's create the thumbnail

				var _thumb = _a ? 
					_a.find('img').addClass('thumb scale').css('display','none') :
					_img.clone(true).addClass('thumb').css('display','none');

				if (_a) { _a.replaceWith(_thumb); }
				/* NB Brian - scaling wasn't working.Should work though. Code below is almost certainly correct.
				   there's just something wrong with input params or the environment
				if (!_thumb.hasClass('noscale')) { // scaled tumbnails!
					//alert("scale thumbnails");
					var w = Math.ceil( _img.width() / _img.height() * _container.height() );
					var h = Math.ceil( _img.height() / _img.width() * _container.width() );
					if (w < h) {
						_thumb.css({ height: 'auto', width: _container.width(), marginTop: -(h-_container.height())/2 });
					} else {
						_thumb.css({ width: 'auto', height: _container.height(), marginLeft: -(w-_container.width())/2 });
					}
				} else { // Center thumbnails.
					// a tiny timer fixed the width/height
					window.setTimeout(function() {
						_thumb.css({
							marginLeft: -( _thumb.width() - _container.width() )/2, 
							marginTop:  -( _thumb.height() - _container.height() )/2
						});
					}, 1);
				}
				*/
				// a tiny timer fixed the width/height
				//NB Brian change - scaling wasn't working.
				window.setTimeout(function() {
					_thumb.css({
						marginLeft: -( _thumb.width() - _container.width() )/2, 
						marginTop:  -( _thumb.height() - _container.height() )/2
					});
				}, 1);

				// add the rel attribute
				_thumb.attr('rel',_src);

				// add the title attribute
				_thumb.attr('title',_title);

				// add the click functionality to the _thumb
				_thumb.click(function() {
					$.colflow.activate(_src);
				});

				// hover classes for IE6
				_thumb.hover(
					function() { $(this).addClass('hover'); },
					function() { $(this).removeClass('hover'); }
				);
				_container.hover(
					function() { _container.addClass('hover'); },
					function() { _container.removeClass('hover'); }
				);

				// prepend the thumbnail in the container
				_container.prepend(_thumb);

				// show the thumbnail
				//_thumb.css('display','block');
				_thumb.css('display','none'); //default to hidden - NB Brian change

				// call the onThumb function
				//_o.onThumb(jQuery(_thumb));
				$.colflow.onThumb(jQuery(_thumb));

				// check active class and activate image if match
				if (_container.hasClass('active')) {
					$.colflow.activate(_src);
					//_span.text(_title);
				}

				//-----------------------------------------------------------------

				// finally delete the original image
				_img.remove();

			}).error(function () {

				// Error handling
			    _container.html('<span class="error" style="color:red">Error loading image: '+_src+'</span>');			
			}).attr('src', _src);
		});
	});
};


/**
 *
 * @name NextSelector
 *
 * @desc Returns the sibling sibling, or the first one
 *
**/
$$.nextSelector = function(selector) {
	return $(selector).is(':last-child') ?
		   $(selector).siblings(':first-child') :
    	   $(selector).next();
    	   
};

/**
 *
 * @name previousSelector
 *
 * @desc Returns the previous sibling, or the last one
 *
**/

$$.previousSelector = function(selector) {
	return $(selector).is(':first-child') ?
		   $(selector).siblings(':last-child') :
    	   $(selector).prev();
    	   
};

/**
 *
 * @name hasCSS
 *
 * @desc Checks for CSS support and returns a boolean value
 *
**/

$$.hasCSS = function()  {
	$('body').append(
		$(document.createElement('div')).attr('id','css_test')
		.css({ width:'1px', height:'1px', display:'none' })
	);
	var _v = ($('#css_test').width() != 1) ? false : true;
	$('#css_test').remove();
	return _v;
};

/**
 *
 * @name onPageLoad
 *
 * @desc The function that displays the image and alters the active classes
 *
 * Note: This function gets called when:
 * 1. after calling $.historyInit();
 * 2. after calling $.historyLoad();
 * 3. after pushing "Go Back" button of a browser
 *
**/

$$.onPageLoad = function(_src) {	
	if (!_src){
		//alert("return");
		return;
	}
	//NB Brian - i've disabled the code for blank source.
	
	// get the wrapper
	var _wrapper = $('.colflow-wrapper');
	
	// get the thumb
	var _thumb = $('.colflow img[@rel="'+_src+'"]');

	//alert("page load event "+_src);
	if (_src) {
		
		// new hash location
		if ($.colflow.history) {
			window.location = window.location.href.replace(/\#.*/,'') + '#' + _src;
		}
		
		// alter the active classes
		_thumb.parents('li').siblings('.active').removeClass('active');
		_thumb.parents('li').addClass('active');
	
		// define a new image
		var _img   = $(new Image()).attr('src',_src).addClass('replaced').attr('id','replaced');

		// empty the wrapper and insert the new image
		_wrapper.empty().append(_img);

		// insert the caption
		_wrapper.siblings('.caption').text(_thumb.attr('title'));
		
		// fire the onImage function to customize the loaded image's features
		$.colflow.onImage(_img,_wrapper.siblings('.caption'),_thumb);

		//NB Added by Brian MacMillan
		// fire the onThumb function to customize the loaded thumbnail's features
		// doesn't do anything at present
		$.colflow.onThumb(_img);
		
		// add clickable image helper
		if($.colflow.clickNext) {
			_img.css('cursor','pointer').click(function() { $.colflow.nextImage(); })
		}
		$.colflow.current = _src;
	} else {
		
		// clean up the container if none are active
		_wrapper.siblings().andSelf().empty();
		
		// remove active classes
		$('.colflow li.active').removeClass('active');
	}

}
/**
 *
 * @name jQuery.colflow
 *
 * @desc The global colflow object holds four constant variables and four public methods:
 *       $.colflow.history = a boolean for setting the history object in action with named URLs
 *       $.colflow.current = is the current source that's being viewed.
 *       $.colflow.clickNext = boolean helper for adding a clickable image that leads to the next one in line
 *       $.colflow.nextImage() = displays the next image in line, returns to first image after the last.
 *       $.colflow.previousImage() = displays the previous image in line, returns to last image after the first.
 *       $.colflow.activate(_src) = displays an image from _src in the colflow container.
 *       $.colflow.onImage(image,caption) = gets fired when the image is displayed.
 *
**/

$.extend({colflow : {
	current : 'images/india/600px/guy_with_bucket_600px.jpg', //current image from galleria
	colorscheme : 'WHITE',
	template : "STORIES",
	title : "When we all Have Brains and Other Stories",
	albumid : "#india-400px",
	image : new Array(),
	imagenumber :  new Array(), 
	bookid : 0,
	bookcount : 2,
	chapterid : 0,
	//Note that this is a two dimensional array and therefore requires exception handling
	page : new Array(), 
	pagecount : new Array(),
	// Another two dimensional array, contains the heights of every paragraph of
	// every chapter in a book
	paracount : new Array(),
	paraheight : new Array(),
	chaptercount : new Array(),
	chapter : $("#book-0").children("div[name='c0']"),
	basefontSize : 12,
	fontsize : 12,
	lineheight : 18,
	columnheight :100,
	columnheightstring :"100px",
	columnwidth : 50,
	columnwidthstring : "50px",
	contentheight : 800,
	//contentheightstring: "100px",
	contentwidth : 400,
	contentwidthstring: "100px:",
	aggrparaheight : new Array(),
	topmargin : new Array(),
	parabreak : new Array(),
	overlaplines : new Array(),
	overlapheight : new Array(),
	nonoverlaplines : new Array(),
	nonoverlapheight : new Array(),
	overlapparalines : new Array(),
	colmasktop : new Array(),
	colmaskheight : new Array(),
	firstparagraph : new Array(),
	firstparagraphprev : 0,  //used to change page during a resize event.
	lasttab : "Help",

	//overlapheight : new Array(),
	//
	newImageAlbum : function(psAlbumId) {
		$.colflow.albumid = psAlbumId;
		$.colflow.imagenumber[$.colflow.albumid]=0; //trying to avoid using numbers. 
		$.colflow.image[$.colflow.albumid]=$($.colflow.albumid).children("li").eq(0).children("img").attr("src");
	},
	//Note the functions below always apply to the current book and chapter.
	//which is why they are not passed as arguments. This keeps the calling code clean.
	newBook : function(piBook) {
		//fired when a new book is loaded.
		//page[][]: record current page/image for each chapter/story/poem/image in a book, hence two dimensions
		//for inter webpage navigation. Can some of this be handed off to the browser
		//navigation?
		try{
			var iChapterCount = $("#book-"+piBook).children("div").length;
			var iParaLimit = 0;

			$.colflow.bookid = piBook;
			$.colflow.chapterid = 0;
			//Make page two dimensional.
			$.colflow.nonoverlapheight[piBook] = new Array();
			$.colflow.nonoverlaplines[piBook] = new Array();
			$.colflow.overlapheight[piBook] = new Array();
			$.colflow.overlaplines[piBook] = new Array();
			$.colflow.overlapparalines[piBook] = new Array();
			$.colflow.page[piBook] = new Array();
			$.colflow.pagecount[piBook] = new Array();
			$.colflow.topmargin[piBook] = new Array();
			//Make pageLimit and paraCount two dimensional, paraHeight three dimensional
			$.colflow.paracount[piBook] = new Array();
			$.colflow.paraheight[piBook] = new Array();
			$.colflow.aggrparaheight[piBook] = new Array();
			$.colflow.parabreak[piBook] = new Array();
			$.colflow.colmasktop[piBook] = new Array();
			$.colflow.colmaskheight[piBook] = new Array();
			$.colflow.firstparagraph[piBook] = new Array();
			for (i=0;i<iChapterCount;i++){
				$.colflow.page[piBook][i] = 0;
				$.colflow.topmargin[piBook][i] = 0;
				$.colflow.pagecount[piBook][i]=0;
				$.colflow.page[piBook][i]=0;
				$.colflow.pagecount[piBook][i] = 0;
				
				$.colflow.aggrparaheight[piBook][i] = new Array(); //make three dimensional
				$.colflow.paraheight[piBook][i] = new Array();
				$.colflow.firstparagraph[piBook][i] = new Array();
				$.colflow.nonoverlaplines[piBook][i] = new Array();
				$.colflow.nonoverlapheight[piBook][i] = new Array();
				$.colflow.overlaplines[piBook][i] = new Array();
				$.colflow.overlapheight[piBook][i] = new Array();
				$.colflow.overlapparalines[piBook][i] = new Array();
				$.colflow.parabreak[piBook][i] = new Array();
				$.colflow.paracount[piBook][i] = $("#book-"+$.colflow.bookid).children("div[name='c"+i+"'] p").length;
				$.colflow.topmargin[piBook][i] = new Array();
				$.colflow.colmasktop[piBook][i] = new Array();
				$.colflow.colmaskheight[piBook][i] = new Array();
				//Alternative way to handle height calcs - figure out height in advance
				//upside is its a little faster to run, but has more overhead in terms of stored info.
				//for (j=0;j< $.colflow.paracount[piBook][i];j++){
				//	$.colflow.paraheight[piBook][i][j]=$("#book-"+$colflow.bookid).children("div[name='c"+i+"']").children("p").eq(j).height();
				//	$.colflow.aggrparaheight[piBook][i][j] = 0; //make three dimensional
				//}
			}
		} catch (err)
		{
			alert("error in newbook "+err.message);
		}
		$.colflow.chaptercount[piBook]=iChapterCount;
		//NB $.colflow.chapter is not returning an object in bm_stories, event though
		//the expression below evaluates correctly
		//This needs to be fixed in order to stop absurdly long statements in bm_stories.
		$.colflow.chapter = $("#book-"+piBook).children("div[name='c"+$.colflow.chapterid+"']");
	},
	firstParagraph : function(piPage){
		// FirstParagraph - which paragraph starts the current page
		// it is used by redraw function to move the current page to a sensible location
		// after a redraw event. the idea is that after a redraw, the current page includes the first paragraph 
		// from the page as it was before the resize event. 
		return $.colflow.firstparagraph[$.colflow.bookid][$.colflow.chapterid][piPage];
	},
		setFirstParagraph : function(piPage,piValue){
			$.colflow.firstparagraph[$.colflow.bookid][$.colflow.chapterid][piPage]=piValue;
		},
	getPageFromParagraph : function(piParagraph){
		//Used to move to the page that the first paragraph of the (formerly) current
		//page was on after a resize event.
		//Loop through $.colflow.firstParagraph() until finding a result > piParagraph
		//then return the previous page.
		for (var i=0;i<$.colflow.paracount[$.colflow.bookid][$.colflow.chapterid];i++)
		{
			if ($.colflow.firstparagraph[$.colflow.bookid][$.colflow.chapterid][i]>piParagraph)
			{
				return i-1;
			}
		}
		return i;
	},
	// MASK Comments
	// the bottom and top of each column has a mask that covers the partial line
	// that may occur at the bottom of the text and before the bottom of the column 
	// (this is true even if overflow set to false.). Should be able to eliminate the top mask and 
	// dynamically calc overlap height.
	colMaskTop : function(piPage){
		return $.colflow.colmasktop[$.colflow.bookid][$.colflow.chapterid][piPage];
	},
		setColMaskTop : function(piPage,piValue){
			$.colflow.colmasktop[$.colflow.bookid][$.colflow.chapterid][piPage]=piValue;
		},
	colMaskHeight : function(piPage){
		return $.colflow.colmaskheight[$.colflow.bookid][$.colflow.chapterid][piPage];
	},
		setColMaskHeight : function(piPage,piValue){
			$.colflow.colmaskheight[$.colflow.bookid][$.colflow.chapterid][piPage]=piValue;
		},
	nonOverlapHeight : function(piPage){
		return $.colflow.nonoverlapheight[$.colflow.bookid][$.colflow.chapterid][piPage];
	},
		setNonOverlapHeight : function(piPage,piValue){
			$.colflow.nonoverlapheight[$.colflow.bookid][$.colflow.chapterid][piPage]=piValue;
		},
	nonOverlapLines : function(piPage){
		return $.colflow.nonoverlaplines[$.colflow.bookid][$.colflow.chapterid][piPage];
	},
		setNonOverlapLines : function(piPage,piValue){
			$.colflow.nonoverlaplines[$.colflow.bookid][$.colflow.chapterid][piPage]=piValue;
		},
	overlapHeight : function(piPage){
		return $.colflow.overlapheight[$.colflow.bookid][$.colflow.chapterid][piPage];
	},
		setOverlapHeight : function(piPage,piValue){
			try{
				//alert("in set overlap height "+piPage+" " +piValue);
				$.colflow.overlapheight[$.colflow.bookid][$.colflow.chapterid][piPage]=piValue;
			} catch (err)
			{
				alert("Error in setOverlapHeight "+err.message);
			}
		},
	overlapLines : function(piPage){
		// OverlapParaLines - the total number of lines of the paragraph which overlaps between each page.
		// One array element per page. Recalculated on page resize and on page load.
		return $.colflow.overlaplines[$.colflow.bookid][$.colflow.chapterid][piPage];
	},
		setOverlapLines : function(piPage,piValue){
			$.colflow.overlaplines[$.colflow.bookid][$.colflow.chapterid][piPage]=piValue;
		},
	overlapParaLines : function(piPage){
		return $.colflow.overlapparalines[$.colflow.bookid][$.colflow.chapterid][piPage];
	},
		setOverlapParaLines : function(piPage,piValue){
			$.colflow.overlapparalines[$.colflow.bookid][$.colflow.chapterid][piPage]=piValue;
		},
	paraBreak : function(piPage){
		return $.colflow.parabreak[$.colflow.bookid][$.colflow.chapterid][piPage];
	},
		setParaBreak : function(piPage,piValue){
			$.colflow.parabreak[$.colflow.bookid][$.colflow.chapterid][piPage]=piValue;
		},
	setChapter : function(){
		//This function allows a short hand reference in order to clean up code in container program.
		$.colflow.chapter = $("#book-"+$.colflow.bookid).children("div[name='c"+$.colflow.chapterid+"']");
	},
	topMargin : function(piPage) {
		sReturn = $.colflow.topmargin[$.colflow.bookid][$.colflow.chapterid][piPage];
		if (sReturn.toString().indexOf(".")>0)
		{
			//alert("$.colflow.topmargin "); //+$.colflow.topmargin[$.colflow.bookid][$.colflow.chapterid][piPage]);
			sReturn=sReturn.toString().substring(0,sReturn.toString().indexOf("."));
		}
		return sReturn;
		//return sReturn+".px";
	},
	setTopMargin : function(piPage,piParagraph,piMarginBottom)
	{
		// To understand this function you need to understand the architecture of this program.
		// Each content column can have overlap text at the top and the bottom that reflects the situation
		// where a paragraph breaks between columns. 
		// sTopMargin is a negative pixel value which pulls the top paragraph 
		// up to the point where only the lines spilling over from the previous page are visible.
		try
		  {
			//Set top margin for piPage - assumes this is second page or higher.
			//Note that the overlaplines from the previous column are being referenced!
			//if (gaiNonOverlapLines[piPage-1] <= 0)

			if ($.colflow.nonOverlapLines(piPage-1) <= 0)
			{
				$.colflow.setTopMarginDetail(piPage,piMarginBottom); //iParaSpacing+gaiTopMarginAdjustment;	

			}
			//else if (gaiOverlapLines[piPage-1] == 0)
			else if ($.colflow.overlapLines(piPage-1) == 0)
			{
				//gaiTopMargin[piPage] = 0; //iParaSpacing;
				$.colflow.setTopMarginDetail(piPage,piMarginBottom); //iParaSpacing;

			} else
			{
				//$.colflow.setTopMargin(piPage,Number(-1 * (Number(gaiNonOverlapLines[piPage-1]) * Number($.colflow.lineheight)))); // + Number(iParaSpacing)+gaiTopMarginAdjustment;
				$.colflow.setTopMarginDetail(piPage,Number(-1 * (Number($.colflow.nonOverlapLines(piPage-1)) * Number($.colflow.lineheight)))) + Number(piMarginBottom); //+gaiTopMarginAdjustment;
				// To do: remove
			}
			if(piParagraph<7)
			{
				//alert("top margin for page "+piPage+" "+$.colflow.topMargin(piPage));
				//alert("1c "+gaiTopMargin[piPage]+" "+gaiNonOverlapLines[piPage-1]);
			}

		}
		catch(err)
		{
			errorHandler("Error in colflow.setTopMargin: ",err);
		}
	},
	setTopMarginDetail : function(piPage,piValue){
		$.colflow.topmargin[$.colflow.bookid][$.colflow.chapterid][piPage]=Number(piValue);
	},
	aggrParaHeight : function(piPage){
		return Number($.colflow.aggrparaheight[$.colflow.bookid][$.colflow.chapterid][piPage]);
	},
		setAggrParaHeight : function(piPage,piValueToAppend) {
			//alert("in setAggrParaHeight "+piPage+" "+piValueToAppend);
			//NB Brian To do: overload so that the set and get functions are the same.
			var sPlusEquals=Number($.colflow.aggrparaheight[$.colflow.bookid][$.colflow.chapterid][piPage])+Number(piValueToAppend);
			$.colflow.aggrparaheight[$.colflow.bookid][$.colflow.chapterid][piPage]=sPlusEquals;
			//Note that this appends to the current value (+= didn't work)
			if ($.colflow.bookid==1)
			{
				//alert("piPage "+piPage+"\n aggrHeight-"+$.colflow.aggrparaheight[$.colflow.bookid][$.colflow.chapterid][piPage]+"\n Content Height-"+$.colflow.contentheight);
			}
		},
		resetAggrParaHeight : function(piPage,piValue) {
			//set the aggrparaheight to piValue. No aggretion, unlike with setAggrParaHeight
			$.colflow.aggrparaheight[$.colflow.bookid][$.colflow.chapterid][piPage]=Number(piValue);
			//alert("reset " +$.colflow.aggrparaheight[$.colflow.bookid][$.colflow.chapterid][piPage]);
		},
	paraHeight : function(piParaNumber){
		//substring(0,gasParaHeight[i].indexOf("px"));
		//NB Note that paraheight is only available for visible paragraphs, so zIndex and masks are required.
		var iParaId = "#c"+$.colflow.chapterid+"p"+piParaNumber;
		return $("#book-"+$.colflow.bookid+" div[name='c"+$.colflow.chapterid+"'] "+iParaId).height();
	},
	image : function(){
		return $.colflow.image[$.colflow.albumid];
	},
	imageNumber : function(){
		$.colflow.imagenumber[$.colflow.albumid];
	},
	page : function(){
		return $.colflow.page[$.colflow.bookid][$.colflow.chapterid];
	},
		setPage : function(piValue){
			$.colflow.page[$.colflow.bookid][$.colflow.chapterid] = piValue;
		},
	paraCount : function() {
		// paragraphCount - number of paragraphs in current story
		//NB Brian -1 because result is zero based. 
		return $("#book-"+$.colflow.bookid).children("div[name='c"+$.colflow.chapterid+"']").children("p").length-1;
	},
	pageCount : function(){
		// pageCount - number of pages in current chapter. 
		// could be calculated dynamically.
		return $.colflow.pagecount[$.colflow.bookid][$.colflow.chapterid];
	},
		setPageCount : function(piValue){
			$.colflow.pagecount[$.colflow.bookid][$.colflow.chapterid] = piValue;
		},
	chapterCount : function(){
		return $.colflow.chaptercount[$.colflow.bookid];
	},
	chapterTitle : function(){
		return $("#book-"+$.colflow.bookid).children("div[name='c"+$.colflow.chapterid+"']").attr("title");
	},
	bookTitle : function(){
		//return $.colflow.bookid;
		//return $("#book-"+psStoryId +".c-"+psChapterid).attr("title");
		return $("#book-"+$.colflow.bookid).attr("title");
	},
	onImage : function(){

	},
	onThumb : function(){
		//alert("thumb clicked");
	},
	setDefaultImage : function(psContainer){
		//alert("in set di "+$(psContainer+" li").siblings(':first-child').children("img").attr("src"));//children("li").children("img").attr("src"));
 		$.colflow.activate($(psContainer+" li").siblings(':first-child').children("img").attr("src"));
	},
	activate : function(_src) { 
		if ($.colflow.history) {
			$.historyLoad(_src);
		} else {
			$$.onPageLoad(_src);
		}
		
		//NB Added by BM to capture where in each slideshow the user is.
		$.colflow.image[$.colflow.albumid]=_src;
		
		//find the index of the current image. not really necessary - can just move up and down the DOM.
		//there must be a property that makes this loop unnecessary.
		$($.colflow.albumid+' li').each(function(i) {
			if ($(this).children('img').attr('rel')==_src){
				$.colflow.imagenumber[$.colflow.albumid]=i;
				//alert("setting index in activate "+i+" "+_src);
				//what is the break command in qQuery?
			};
		})

		$.colflow.valignPicture();
		
		//NB Brian - this code snippet returns the id of the current image. Alternate to above code.
		//alert($('.colflow img[@rel="'+$.colflow.current+'"]').parents('li').find('img').attr('id'));
		//document.getElementsByClassName(".caption").style.display="block";
		//$(".caption").show("slow");
	},
	next : function()
	{
		if ($.colflow.template=="PHOTOS"){$.colflow.nextImage();}
		if ($.colflow.template=="STORIES"){$.colflow.nextPage();}
		if ($.colflow.template=="POEMS"){$.colflow.nextPoem();}
	},
	previous : function()
	{
		if ($.colflow.template=="PHOTOS"){$.colflow.previousImage();}
		if ($.colflow.template=="STORIES"){$.colflow.previousPage();}
		if ($.colflow.template=="POEMS"){$.colflow.previousPoem();}
	},
	nextPage : function() {
		try
		{
			if ($.colflow.page()<($.colflow.pageCount()-1))
			{
				$.colflow.setPage($.colflow.page()+2);
			} else
			{
				$.colflow.setPage(0);
			}
			//var x = $.colflow.page();
			$.colflow.redraw();
			$.colflow.drawContentHeader();
		} catch(err)
		{
			errorHandler("Next",err);
		}
	},
	//Set page counter and then redraw
	previousPage : function()
	{
		var iModulus;
		if ($.colflow.page()>1)
		{
			$.colflow.setPage($.colflow.page()-2);
		} else
		{
			iModulus = ($.colflow.pageCount() % 2);
			if (iModulus==0) 
			{
				$.colflow.setPage($.colflow.pageCount());
			} else
			{
				$.colflow.setPage($.colflow.pageCount()-1);
			}

		}
		$.colflow.redraw();
		$.colflow.drawContentHeader();
	},
	nextImage : function() {
		//alert($$.nextSelector($($.colflow.albumid+' img[@rel="'+$.colflow.image()+'"]'));
		//alert($.colflow.albumid+' img[@rel="'+$.colflow.image()+'"]');
		//var _next = $($$.nextSelector($($.colflow.albumid+' img[@rel="'+$.colflow.image()+'"]').parents('li'))).find('img').attr('rel');
		var _next = $$.nextSelector($($.colflow.albumid+' img[@rel="'+$.colflow.image()+'"]').parents("li"));
		$.colflow.activate($(_next).children("img").attr("src"));
	},
	previousImage : function() {
		//alert($.colflow.albumid+' img[@rel="'+$.colflow.image()+'"]');
		//var _prev = $($$.previousSelector($($.colflow.albumid+' img[@rel="'+$.colflow.image()+'"]').parents('li'))).find('img').attr('rel');
		//alert(""+$($.colflow.albumid+' img[@rel="'+$.colflow.image()+'"]').parents("li").children("img").attr("src"));
		var _prev = $$.previousSelector($($.colflow.albumid+' img[@rel="'+$.colflow.image()+'"]').parents("li"));
		$.colflow.activate($(_prev).children("img").attr("src"));

	},
	redrawFlag : true, // stops multiple redraws for photographs.
	valignPicture : function() {
		//v-align middle wasn't working so now i calculate. This is a much worse alternative.
		var iMarginTop = Number($("#main-image").height()-$("#replaced").height())/2;//;
		//alert("height "+$("#main-image").height());
		//adjust for proceeding div, the navigation controls. NB make controls float.
		var iMarginAdjustment = 10; //tuning should be calc'd from a screen element.
		iMarginTop = iMarginTop-Number($("#content-images-inner").height())+iMarginAdjustment; //.css({marginTop:iMarginTop});
		$("#replaced").css({marginTop:iMarginTop})
		//alert($("#replaced").css("marginTop"));

	},
	refresh : function() {
		//alert("in refresh - use colflow.activate instead.");
		if ($.colflow.current=="")
			{
				//alert("refresh current is blank");
				$.colflow.current = 'images/india/600px/guy_with_bucket_600px.jpg';
				//alert("current "+$.colflow.current);
				$.colflow.nextImage();
				return;
			};
		$.colflow.activate($.colflow.image());
	},
	createImageObjects : function(poThis)
	{
		//pass a UL that is the parent of an image list.
		$(poThis).each(function(){
			//alert("cio 1");
			// add the Galleria class
			//alert("poThis "+poThis);
			$(poThis).addClass('colflow');

			// loop through list
			$(poThis).children('li').each(function(i) {
				//alert("cio 2");
				// bring the scope
				var _container = $(this);

				// build element specific options
				//var _o = $.meta ? $.extend({}, $opts, _container.data()) : $opts;

				// remove the clickNext if image is only child
				//NB Brian disabled during debugging//
				//_o.clickNext = $(this).is(':only-child') ? false : _o.clickNext;

				// try to fetch an anchor
				var _a = $(this).find('a').is('a') ? $(this).find('a') : false;

				//alert("_a "+_a);
				
				// reference the original image as a variable and hide it
				var _img = $(this).children('img').css('display','none');

				// extract the original source
				var _src = _a ? _a.attr('href') : _img.attr('src');

				// find a title
				var _title = _a ? _a.attr('title') : _img.attr('title');

				// create loader image            
				var _loader = new Image();

				// check url and activate container if match
				//if (_o.history && (window.location.hash && window.location.hash.replace(/\#/,'') == _src)) {
				if ($.colflow["history"] && (window.location.hash && window.location.hash.replace(/\#/,'') == _src)) {
					_container.siblings('.active').removeClass('active');
					_container.addClass('active');
				}
				//alert("in createImageObjects img "+$(_img).attr("src"));
				//alert("before loader");
				// begin loader
				$(_loader).load(function () {
					//alert("in loader");
					// try to bring the alt
					$(this).attr('alt',_img.attr('alt'));

					//-----------------------------------------------------------------
					// the image is loaded, let's create the thumbnail

					var _thumb = _a ? 
						_a.find('img').addClass('thumb scale').css('display','none') :
						_img.clone(true).addClass('thumb').css('display','none');

					if (_a) { _a.replaceWith(_thumb); }
					/* NB Brian - scaling wasn't working.Should work though. Code below is almost certainly correct.
					   there's just something wrong with input params or the environment
					if (!_thumb.hasClass('noscale')) { // scaled tumbnails!
						//alert("scale thumbnails");
						var w = Math.ceil( _img.width() / _img.height() * _container.height() );
						var h = Math.ceil( _img.height() / _img.width() * _container.width() );
						if (w < h) {
							_thumb.css({ height: 'auto', width: _container.width(), marginTop: -(h-_container.height())/2 });
						} else {
							_thumb.css({ width: 'auto', height: _container.height(), marginLeft: -(w-_container.width())/2 });
						}
					} else { // Center thumbnails.
						// a tiny timer fixed the width/height
						window.setTimeout(function() {
							_thumb.css({
								marginLeft: -( _thumb.width() - _container.width() )/2, 
								marginTop:  -( _thumb.height() - _container.height() )/2
							});
						}, 1);
					}
					*/
					// a tiny timer fixed the width/height
					//NB Brian change - scaling wasn't working.
					window.setTimeout(function() {
						_thumb.css({
							marginLeft: -( _thumb.width() - _container.width() )/2, 
							marginTop:  -( _thumb.height() - _container.height() )/2
						});
					}, 1);

					// add the rel attribute
					_thumb.attr('rel',_src);

					// add the title attribute
					_thumb.attr('title',_title);

					// add the click functionality to the _thumb
					_thumb.click(function() {
						$.colflow.activate(_src);
					});

					// hover classes for IE6
					_thumb.hover(
						function() { $(poThis).addClass('hover'); },
						function() { $(poThis).removeClass('hover'); }
					);
					_container.hover(
						function() { _container.addClass('hover'); },
						function() { _container.removeClass('hover'); }
					);

					// prepend the thumbnail in the container
					_container.prepend(_thumb);

					// show the thumbnail
					//_thumb.css('display','block');
					_thumb.css('display','none'); //default to hidden - NB Brian change

					// call the onThumb function
					//_o.onThumb(jQuery(_thumb));
					$.colflow.onThumb(jQuery(_thumb));

					// check active class and activate image if match
					if (_container.hasClass('active')) {
						$.colflow.activate(_src);
						//_span.text(_title);
					}

					//-----------------------------------------------------------------

					// finally delete the original image
					_img.remove();

				}).error(function () {

					// Error handling
				    _container.html('<span class="error" style="color:red">Error loading image: '+_src+'</span>');			
				}).attr('src', _src);
			});
		});
	},
	drawContentHeader : function()
	{
		// The header is divided into four containers 
		// upper left center left, upper right and center right.
		// These are referred to as left-left left-right right-left and right-right
		// To do: read header information directly from story file.	
		try
		{
			var e;
			//alert("page "+$.colflow.page());
			document.getElementById("content-header-left-pagenumber").innerHTML =  Number($.colflow.page())+1;

			document.getElementById("content-header-left-right").innerHTML =  "   "+$.colflow.chapterTitle()+" by Brian MacMillan";

			document.getElementById("content-header-right-left").innerHTML = "    "+$.colflow.bookTitle();		

			e = document.getElementById("content-header-right-pagenumber");
			if ($.colflow.page()+2>$.colflow.pageCount())
			{
				//element.innerHTML = "\u00A0"+"&nbsp;"+"&nbsp;"+" Fin";	//NB Trying to put a space in front nothing is working.	perhaps just switch the csvs at this point to text-indent: 1em.
				e.innerHTML = "Fin";	//NB Trying to put a space in front nothing is working.	perhaps just switch the csvs at this point to text-indent: 1em.
			} else
			{
				e.innerHTML = Number($.colflow.page()+2); //+" of "+Number(giPageLimit+1);
			}
			if ($.colflow.page() < 10)
			{
				document.getElementById("content-header-left-next").style.marginRight="0px";
			}
			if ($.colflow.page() >= 10 && $.colflow.page() <99)
			{
				document.getElementById("content-header-left-next").style.marginRight="5px";
			}
			if ($.colflow.page() > 99)
			{
				document.getElementById("content-header-left-next").style.marginRight="10px";
			}
		} catch(err)
		{
			errorHandler("drawContentHeader",err);
		}
	},
	drawPage : function(psEvent)
	{
		// drawPage() - (re)draw story content
		// notice execution order
		// 1. set globals
		// 2. putTextIntoColumns
		// 3. redraw
		// 4. draw header
		try
		{		
			//if (giStoryLoaded[$.colflow.chapterid]==0) {return}
			if (($.colflow.paraCount()==undefined) || ($.colflow.paraCount()==0))
			{
				alert("Error in drawPage - incorrect paragraph count for chapter "+$.colflow.chapterid);
			}
			//To do: speed up by only resetting values during resize events and on load
			//To do - default view does not work in IE is there another option?
			//Also note that this code depends on the sId elements being visible!!!!

			/* set calculation variables (formerly setGlobals)*/
			var sPlaceholder ="";
			//var bIE = false;
			var eColumnOne = document.getElementById("column-one");
			var eContent = document.getElementById("content-inner");

			var eTextResizeControl = document.getElementById("content-invisible-story");
			var styleColumn, styleContent, styleResize;

			if( window.getComputedStyle) 
			{
				styleColumn = document.defaultView.getComputedStyle(eColumnOne,sPlaceholder);
				styleContent =  document.defaultView.getComputedStyle(eContent,sPlaceholder);
				//styleResize =  document.defaultView.getComputedStyle(eTextResizeControl,sPlaceholder);
			} else
			{
				styleContent = eContent.currentStyle;
				styleColumn = eColumnOne.currentStyle;
				//styleResize =  eTextResizeControl.currentStyle;
			}
			$.colflow.basefontsizestring = $("#column-one").css("fontSize");
			$.colflow.fontsize = stripPX($.colflow.basefontsizestring);
			$.colflow.lineheight = stripPX(styleColumn.lineHeight);

			var eaParagraph = new Array();
			var saParagraphStyle = new Array(); 
			$.colflow.setFirstParagraph(0,0);  
			if (window.getComputedStyle)
			{	
				var sTempHeight = styleContent.height;
				var sTempWidth = styleContent.width;

				$.colflow.contentheight = stripPX(sTempHeight);
				$.colflow.contentwidth = stripPX(sTempWidth);

				/* if $.colflow.columnheightstring is a percentage */
				if ($.colflow.columnheightstring.indexOf("%")>0)
				{
					$.colflow.columnheight = ($.colflow.contentheight * $.colflow.columnheightstring.substring(0,$.colflow.columnheightstring.indexOf("%"))/100);
				} else {
					$.colflow.columnheight = $.colflow.columnheightstring.substring(0,$.colflow.columnheightstring.indexOf("px"));
				}
			} else
			{
				$.colflow.columnheightstring = styleColumn.height;	
				$.colflow.columnheight = eColumnOne.clientHeight;
				$.colflow.contentheight = eContent.clientHeight;
				$.colflow.contentwidth = eContent.clientWidth;
			}
			/* End initialization of calc variables */

			$.colflow.putTextIntoColumns();
			// NB Brian - To do: redraw logic
			// 1. determine the first paragraph of the current page - gaiParaBreak[$.colflow.page()]
			// 2. recalc all globals related to screen display by running - putTextIntoColumns and subs 
			// 3. change current page to ensure that it includes the first paragraph
			//    that was visible when the resize event occurred.  
			if (psEvent=="resize")
			{
				//NB Handle which page/para is active after a resize event
				//My thought is to get the first paragraph of the current page ($.colflow.firstparagraphprev = $.colflow.firstParagraph($.colflow.page()) 
				//then find out what page that paragraph is one after the resize event.
				//I think that this may need to happen in two spots, before putTextIntoColumns and after.
				//alert("DrawPage resize first paragraph "+$.colflow.firstparagraphprev);
				$.colflow.setPage($.colflow.getPageFromParagraph($.colflow.firstparagraphprev));
				//$.colflow.redraw();
			}
			$.colflow.redraw();
			$.colflow.drawContentHeader();
			$.colflow.redrawFlag=true;
		} catch(err)
		{
			//errorHandler("Error in drawPage: '" + err.message+"'")
			errorHandler("DrawPage: ",err);
		}
	},
	putTextIntoColumns : function()
	{
		// Step one: find out which paragraph should wrap.
		// Variables you need to understand in order to use this function:
		// $.colflow.lineheight - this is the line height for this font size in pixels for this browser.
		// $.colflow.overlapHeight the number of pixels overlap between col 1 and col2.
		// msg is used to aggregate system state information that can display when debug mode is on.
		var msg="Content Height "+$.colflow.contentheight+String.fromCharCode(10); //debug messages
	  try
	  {
		var iCurPage = 0; //current column (-or page? (ed))
		var iCurPara = 0; //current paragraph
		var bNewPage = true; //flag to handle margins and overlaps at top of page.
		var bColumnFullIgnoreBottomParaPadding = false;
		var bMultiPagePara = false; //exception handling for multi-page paragraphs
		var iDebugAssist = 0;
		//if (giStoryLoaded==0) {return}

		// Loop through all of the nodes (p elements) in the story and display them in consecutive columns.
		//var iMarginBottom = Number(stripPX($(".body-text").css("margin-bottom")));
		var iMarginBottom = parseInt($(".body-text").css("margin-bottom"));
		
		//alert("NB to fix - remove reference to .body-text iMarginBottom "+iMarginBottom);
		
		//initialize top margin on first page to zero.
		$.colflow.setTopMarginDetail(0,0);

		var sDebug="";
		//for (iCurPara = 0;iCurPara<=$.colflow.paraCount();iCurPara++)
		$("#book-"+$.colflow.bookid).children("div[name='c"+$.colflow.chapterid+"']").children("p").each(function(i)
		{
			//exception handling for first paragraph on first page 
			if (iCurPage==0 && bNewPage)
			{
				$.colflow.resetAggrParaHeight(0,0); //args are page and value
				$.colflow.setFirstParagraph(0,0);
				bNewPage = false;	
			}
			//exception handling for top page margins for top of all pages except the first one
			iDebugAssist = 1;
			//alert(" aph para i"+i+" "+Number($.colflow.paraHeight(iCurPara-1))+" top margin "+$.colflow.topMargin(iCurPage)+" bottom margin "+Number(iMarginBottom));
			if (iCurPage>0 && bNewPage)
			{
				$.colflow.resetAggrParaHeight(iCurPage,Number($.colflow.topMargin(iCurPage)));
				//if (iCurPara<5) {alert("Page reset "+$.colflow.aggrParaHeight(iCurPage)+" page "+iCurPage)}
				$.colflow.setAggrParaHeight(iCurPage,Number($.colflow.paraHeight(iCurPara-1)) + Number(iMarginBottom)); //
				$.colflow.setFirstParagraph(iCurPage,iCurPara);
				bNewPage = false;	
			}
			iDebugAssist = 2;
			$.colflow.setAggrParaHeight(iCurPage,$.colflow.paraHeight(iCurPara)); //height of current paragraph
			if ($.colflow.aggrParaHeight(iCurPage) <= $.colflow.contentheight)  
			{
				if (Number($.colflow.aggrParaHeight(iCurPage))+Number(iMarginBottom) >= $.colflow.contentheight)  
				{
					//exclude padding after paragraph.
					bColumnFullIgnoreBottomParaPadding=true;
				}
				else 
				{
					//account for interparagraph spacing (marginBottom for p element)
					$.colflow.setAggrParaHeight(iCurPage,Number(iMarginBottom)); //aggregate plus padding after paragraph
				}
			}
			if ($.colflow.bookid==1 && iCurPara<6) {
				//alert("PTIC book-"+$.colflow.bookid+" chapter-"+$.colflow.chapterid+" pages-"+$.colflow.pageCount()+"\nContent Height "+$.colflow.contentheight+"\n AggrParaHeight "+$.colflow.aggrParaHeight());
			}
			iDebugAssist = 3;
			// Is column full of text? 
			// Note that the situation where column fits exactly requires exception handling.
			// In that situation we want to exclude the padding that follows our paragraph from our calculations.
			if (($.colflow.aggrParaHeight(iCurPage) >= $.colflow.contentheight) || (bColumnFullIgnoreBottomParaPadding))
			{
				//reset flag used in above if statement.
				bColumnFullIgnoreBottomParaPadding = false;

				iDebugAssist = 31;
				iCurPara = $.colflow.setMarginsAndBreaks(iCurPage,iCurPara,iMarginBottom);

				iCurPage++;
				//flag to handle top margin - see top of for loop
				bNewPage = true;

				//if (iCurPara<5) {alert("AggrPara > contentHeight "+$.colflow.aggrParaHeight(iCurPage)+" paragraph "+iCurPara)}

				//Trap for situation where paragraph is more than one page long
				while ($.colflow.overlapHeight(iCurPage-1)>$.colflow.contentheight)
				{
					iDebugAssist = 32;
					$.colflow.resetAggrParaHeight(iCurPage,Number($.colflow.paraHeight(iCurPara))-Number($.colflow.nonOverlapHeight(iCurPage-1)));
					iCurPara = $.colflow.setMarginsAndBreaks(iCurPage,iCurPara);
					iCurPage++;
					iDebugAssist = 33;
				}
			}
			iDebugAssist = 4;
			iCurPara++;
		});	
		//Exception handling for partial last page. 
		if ($.colflow.aggrParaHeight(iCurPage)<$.colflow.contentheight)
		{ 
			$.colflow.setParaBreak(iCurPage,$.colflow.paraCount());
		}
		//alert("setting page count "+ iCurPage);
		$.colflow.setPageCount(iCurPage);
	  }
	  catch(err)
	  {
		errorHandler("colflow.putTextIntoColumns "+iDebugAssist,err);
	  }
	},
	//redraw content based on current page number. Called by nextPage(), previousPage()
	redraw : function()
	{
		try
		{
			$.colflow.removeAndAddNodes($.colflow.page(),"column-one",false);
			//exception handling for situation where last page is
			//on the left hand column
			//the third argument, bRemoveFlag, is set to true, 
			//this removes the text nodes in the second column but doesn't attempt to draw text.
			if ($.colflow.page()+1 > $.colflow.pageCount())
			{
				$.colflow.removeAndAddNodes($.colflow.page()+1,"column-two",true);
			}
			else
			{
				$.colflow.removeAndAddNodes($.colflow.page()+1,"column-two",false);
			}
			$.colflow.setMasks($.colflow.page());
			$.colflow.setMasks($.colflow.page()+1);
			$.colflow.drawMasks($.colflow.page());
		}
		catch(err)
		{
			errorHandler("Redraw: ",err);
		}
	},
	//Calculate the position of the masks which cover the partial line(s) at the bottom of 
	//each column.
	setMasks : function(piPage)
	{
		try
		{
			if ((piPage)<$.colflow.pageCount())
			{
				if (window.getComputedStyle)
				{
					//gaiColMaskTop[piPage]=(Number(gafAggrParaHeight[piPage]) - Number(gafParaHeight[gaiParaBreak[piPage]])) + Number(gaiNonOverlapHeight[piPage]);
					$.colflow.setColMaskTop(piPage,$.colflow.aggrParaHeight(piPage) - $("#book-"+$.colflow.bookid).children("div[name='c"+$.colflow.chapterid+"']").children("p").eq($.colflow.paraBreak(piPage)).height() + $.colflow.nonOverlapHeight(piPage));
				} else
				{
					//alert("not getcomputed");
					//gaiColMaskTop[piPage]=(Number(gafAggrParaHeight[piPage]) - Number(gafParaHeight[gaiParaBreak[piPage]])) + Number(gaiNonOverlapHeight[piPage]);
					$.colflow.setColMaskTop(piPage,$.colflow.aggrParaHeight(piPage) - $("#book-"+$.colflow.bookid).children("div[name='c"+$.colflow.chapterid+"']").children("p").eq($.colflow.paraBreak(piPage)).height()+ $.colflow.nonOverlapHeight(piPage));
				}
				// NB - IE exception perhaps
				if (piPage == 0)
				{
					//gaiColMaskTop[piPage] + 16; // -= giFirstLineAdjustmentIE;
				}
			} else
			{
				//alert("para "+$.colflow.aggrParaHeight(piPage));
				$.colflow.setColMaskTop(piPage,$.colflow.aggrParaHeight(piPage)+1);
			}
			//alert(" content height "+$.colflow.colMaskTop(piPage));
			$.colflow.setColMaskHeight(piPage,Number($.colflow.contentheight)-Number($.colflow.colMaskTop(piPage)));
		}
		catch(err)
		{
			errorHandler("setMasks: '",err);
		}
	},
	drawMasks : function(piPage)
	{
		var iDebugAssist = 0;  // used to determine in which statement an IE bug occurs.
		try{
			var eColumnOneMask = document.getElementById("column-one-mask");
			var eColumnTwoMask = document.getElementById("column-two-mask");

			if ($.colflow.overlapLines(piPage)==0)
			{
				iDebugAssist = 1;
				eColumnOneMask.style.display="none";
			} else
			{
				eColumnOneMask.style.display="block";
				//eColumnOneMask.style.top = gaiColMaskTop[piPage]+"px";
				//eColumnOneMask.style.height= gaiColMaskHeight[piPage]+"px";
				eColumnOneMask.style.top = $.colflow.colMaskTop(piPage)+"px";
				eColumnOneMask.style.height= $.colflow.colMaskHeight(piPage)+"px";
			}

			if (piPage<=$.colflow.pageCount())
			{
				iDebugAssist = 10;

				if ($.colflow.overlapLines(piPage+1)==0)
				{
					iDebugAssist = 11;
					eColumnTwoMask.style.display="none";
				} else		
				{
					iDebugAssist = 12;
					eColumnTwoMask.style.display="block";
					//NB exception handling for IE7 bug where gaiColMaskTop/Height are evaluating to NaN
					//when there is no text on the right column of the final page.
					//if (gaiColMaskTop[piPage+1]== Number.NaN)
					if ($.colflow.colMaskTop(piPage+1)== Number.NaN)
					{
						iDebugAssist = 14;
						eColumnTwoMask.style.top = $.colflow.contentheight;
					} else
					{
						iDebugAssist = 13;
						//eColumnTwoMask.style.top = gaiColMaskTop[piPage+1]+"px";
						eColumnTwoMask.style.top = $.colflow.colMaskTop(piPage+1)+"px";
					}
					//if (gaiColMaskHeight[piPage+1]==Number.NaN)
					if ($.colflow.colMaskHeight(piPage+1)==Number.NaN)
					{
						iDebugAssist = 16;
						eColumnTwoMask.style.height = 0;
					} else
					{
						iDebugAssist = 15;
						//eColumnTwoMask.style.height= gaiColMaskHeight[piPage+1]+"px";
						eColumnTwoMask.style.height= $.colflow.colMaskHeight(piPage+1)+"px";
					}
				}	
			} else
			{
				// mask = column height
				iDebugAssist = 100;
				eColumnTwoMask.style.display="block";
				eColumnTwoMask.style.top = $.colflow.colMaskTop(piPage+1)+"px";
				eColumnTwoMask.style.height= $.colflow.colMaskHeight(piPage+1)+"px";			
			}		
		}
		catch(err)
		{
			//errorHandler("Error in drawMasks: '"err+iDebugAssist+" "+gaiColMaskHeight[piPage+1],err);
			alert("Error in DrawMasks: '"+iDebugAssist+" "+$.colflow.colMaskHeight(piPage+1)+" "+err.message);
		}
	},
	removeAndAddNodes : function(piPage,psElementId, pbRemoveOnlyFlag)
	{
		// removeAndAddNodes deletes the paragraphs currently being displayed and replaces them
		// with the nodes from the next or previous page. 
		// An alternate architecture involves drawing all columns up front and simply hiding and displaying.
		// This version is lighter and quite fast.	
		// Note: when the last page is on the left hand column
		// pbRemoveOnlyFlag is set to true so that the right hand column is erased.
		try
		{
			//alert("remove and add nodes begin");
			var eNewParagraph; 
			var eOldParagraph;
			var bFirstTime = true;
			var eColumn = document.getElementById(psElementId);
			var iParaStart = 0;
			var iParaEnd = 0;
			var j; //counter
			var sId;
			var sClassName;
			var sSelector;
			//remove child paragraph nodes from column, if they exist, before adding them
			if (eColumn.hasChildNodes() )
			{
			    while (eColumn.childNodes.length >= 1 )
			    {
			        eColumn.removeChild(eColumn.firstChild );
			    } 
			}
			if (piPage>0)
			{
				iParaStart = $.colflow.paraBreak(piPage-1);
			}
			j = iParaStart;

			if (!pbRemoveOnlyFlag)
			{
				// NB Can the entire node be assigned rather than just the html attr eg nodeNew=nodeOrig?
				//for (j=iParaStart;j<=gaiParaBreak[piPage];j++)
				//var sSelector = "#book-"+$.colflow.bookid+" p[id='c"+$.colflow.chapterid+"p3']";
				//alert("classname "+$(sSelector).attr("id"));
				//alert("Remove and add nodes piPage"+piPage+" parabreak "+$.colflow.paraBreak(piPage));
				for (j=iParaStart;j<=$.colflow.paraBreak(piPage);j++)
				{
					eNewParagraph = document.createElement('p');
					//NB Brian need to simplify. This selector is ridiculous. Its also fast.
					//sClassName = $("#book-"+$.colflow.bookid+" p[id='c"+$.colflow.chapterid+"p"+j+"']").attr("class");
					sSelector =  "#book-"+$.colflow.bookid+" p[id='c"+$.colflow.chapterid+"p"+j+"']";
					sClassName = $(sSelector).attr("class");
					//if (j==6) {alert("classname "+sClassName);}
					eNewParagraph.setAttribute('class',sClassName);
					eNewParagraph.setAttribute('className',sClassName);
					//To css
					if (bFirstTime)
					{
						//eNewParagraph.style.marginTop = gasTopMargin[piPage];
						eNewParagraph.style.marginTop = ($.colflow.topMargin(piPage)+"px");
						//alert("first time "+piPage+" mt-"+$.colflow.topMargin(piPage)+" mtnew-"+eNewParagraph.style.marginTop);
						bFirstTime = false;
					}
					eNewParagraph.innerHTML = $(sSelector).html();
					eColumn.appendChild(eNewParagraph);
				}
			}
		}
		catch(err)
		{
			//NB Brian To do: there is a counter issue here related to column-two and the paragraph count.
			//The error results in a blank last page
	 		if (err.message = "eOldParagraph is null")
			{
			//	alert("This error is related to the unnecessarily blank last page.");
			} else
			{
				errorHandler("$.colflow.RemoveAndAddNodes",err);
			}
		}
	},
	setMarginsAndBreaks : function(piCurPage,piCurPara,piMarginBottom)
	{
		//determine the number of lines which overlap between this page and the next
		$.colflow.setColFormatingVars(piCurPage,piCurPara);	
		//adjust for situation where overlap lines = 0 (paragraphs fit on current col with no overlap)
		if ($.colflow.overlapLines(piCurPage)==0)
		{
			piCurPara++;
		}
		//setParaBreak is the paragraph that starts the next column.
		//so if the first page ends on paragraph 3 exactly, 
		//the second page starts on paragraph 4 (gaiParaBreak[0]=4)  
		//if the first page ends part way through paragraph 4
		//the second page also starts on paragraph 4 and gaiParaBreak[0]=4

		$.colflow.setParaBreak(piCurPage,piCurPara);

		//set margin of next page based on overlap from previous page.
		//wrapper to colflow
		$.colflow.setTopMargin(piCurPage+1,piCurPara,piMarginBottom);

		return piCurPara;
	},
	setColFormatingVars : function(piPage,piPara)
	{
		// Remember that the current architecture assumes that breaking paragraphs are printed twice.
		// once at the bottom of the first column with overlap lines hidden and once at the top
		// of the second column with a negative top margin that causes the lines that have already
		// been printed to be clipped.
		// Having access to reliable font metrics will make this type of approach unnecessary.
		try
		{
			// populate second column ...
			$.colflow.setOverlapHeight(piPage,$.colflow.aggrParaHeight(piPage)-$.colflow.contentheight);
			//number of lines in overlap paragraph ...
			$.colflow.setOverlapParaLines(piPage,$("#book-"+$.colflow.bookid).children("div[name='c"+$.colflow.chapterid+"']").children("p").eq(piPara).height() / Number($.colflow.lineheight)); 	

			// ... of which iOverLapLines print on next column ...
			//there is a question as to whether to round or floor or neither. that's a margin/padding issue.
			//I have chosen to use ceil rather than Math.round()
			$.colflow.setOverlapLines(piPage,Math.ceil(Number($.colflow.overlapHeight(piPage))/$.colflow.lineheight));
			// and iNonOverLapLines print on current column
			$.colflow.setNonOverlapLines(piPage,Number($.colflow.overlapParaLines(piPage))-Number($.colflow.overlapLines(piPage)));
			// need to mask partial lines. Mask starts 1 pixel below the bottom of the
			// last nonOverlapLine 

			//gaiNonOverlapHeight[piPage] = gaiNonOverlapLines[piPage]*$.colflow.lineheight;
			//$.colflow.setNonOverlapHeight(piPage,gaiNonOverlapLines[piPage]*$.colflow.lineheight);
			$.colflow.setNonOverlapHeight(piPage,$.colflow.nonOverlapLines(piPage)*$.colflow.lineheight);

			//Handle exceptions where 
			// 1. only a partial line appears at the end of the column and is rounded to 0 lines.
			// 2. A paragraph is complete and there is less than the interparagraph space below it. 
			//    This creates the negative 1 value because of Math.ceil, above.
			//if (gaiNonOverlapLines[piPage]==-1) {
			if ($.colflow.nonOverlapLines(piPage)==-1) {
				//gaiOverlapLines[piPage]=0;
				$.colflow.setOverlapLines(piPage,0);
			}
		}
		catch(err)
		{
			errorHandler("Error in setColFormatingVars: '",err);
		}
	}
}
});

})(jQuery);


/**
 *
 * Packed history extension for jQuery
 * Credits to http://www.mikage.to/
 *
**/


jQuery.extend({historyCurrentHash:undefined,historyCallback:undefined,
historyInit:function(callback)
{
	jQuery.historyCallback=callback;
	var current_hash=location.hash;jQuery.historyCurrentHash=current_hash;if(jQuery.browser.msie){if(jQuery.historyCurrentHash==''){jQuery.historyCurrentHash='#'}$("body").prepend('<iframe id="jQuery_history" style="display: none;"></iframe>');
	var ihistory=$("#jQuery_history")[0];
	var iframe=ihistory.contentWindow.document;
	iframe.open();iframe.close();iframe.location.hash=current_hash}else if($.browser.safari){jQuery.historyBackStack=[];
		jQuery.historyBackStack.length=history.length;
		jQuery.historyForwardStack=[];
		jQuery.isFirst=true}jQuery.historyCallback(current_hash.replace(/^#/,''));
		setInterval(jQuery.historyCheck,100)},historyAddHistory:function(hash){jQuery.historyBackStack.push(hash);
		jQuery.historyForwardStack.length=0;this.isFirst=true
},
historyCheck:function(){if(jQuery.browser.msie){var ihistory=$("#jQuery_history")[0];
var iframe=ihistory.contentDocument||ihistory.contentWindow.document;
var current_hash=iframe.location.hash;if(current_hash!=jQuery.historyCurrentHash){location.hash=current_hash;
jQuery.historyCurrentHash=current_hash;
jQuery.historyCallback(current_hash.replace(/^#/,''))}}else if($.browser.safari){if(!jQuery.dontCheck){var historyDelta=history.length-jQuery.historyBackStack.length;
if(historyDelta){jQuery.isFirst=false;
if(historyDelta<0){for(var i=0;i<Math.abs(historyDelta);i++)jQuery.historyForwardStack.unshift(jQuery.historyBackStack.pop())}else{for(var i=0;i<historyDelta;i++)jQuery.historyBackStack.push(jQuery.historyForwardStack.shift())}
var cachedHash=jQuery.historyBackStack[jQuery.historyBackStack.length-1];
if(cachedHash!=undefined){jQuery.historyCurrentHash=location.hash;
	jQuery.historyCallback(cachedHash)}}else if(jQuery.historyBackStack[jQuery.historyBackStack.length-1]==undefined&&!jQuery.isFirst){if(document.URL.indexOf('#')>=0){jQuery.historyCallback(document.URL.split('#')[1])}else{var current_hash=location.hash;jQuery.historyCallback('')}jQuery.isFirst=true}}}else{var current_hash=location.hash;
	if(current_hash!=jQuery.historyCurrentHash){jQuery.historyCurrentHash=current_hash;
jQuery.historyCallback(current_hash.replace(/^#/,''))}}},
historyLoad:function(hash)
{
	var newhash;

	if(jQuery.browser.safari){newhash=hash}else{newhash='#'+hash;location.hash=newhash}
	jQuery.historyCurrentHash=newhash;
	if(jQuery.browser.msie){var ihistory=$("#jQuery_history")[0];
	var iframe=ihistory.contentWindow.document;
	iframe.open();iframe.close();iframe.location.hash=newhash;
	jQuery.historyCallback(hash)
	}else if(jQuery.browser.safari){jQuery.dontCheck=true;this.historyAddHistory(hash);
	var fn=function(){jQuery.dontCheck=false};
	window.setTimeout(fn,200);jQuery.historyCallback(hash);
	location.hash=newhash}else{jQuery.historyCallback(hash)}
	//alert("in history load end "+hash);
	}
 });

