/*
	cards.js
	
	v1.32, 2007-12-05, Christian Augustin

*/
/*
	JavaScript 1.3+, W3C DOM Level 1
	
	MSIE 5 (excl. Mac), Netscape 7, Mozilla 1.0, Firefox 1.0, Safari 1.1, Opera 7.0
	
	Caveats
	- Simplified: Spread/stack no longer supported!
	
	ToDo
	- Dynamic card heights.
	(- Dynamic height/width recalculation for font size changes.)*
	- Check for MSIE5/Mac problems.
	
	* Perhaps a higher level "monitoring" will be implemented for this
	that calls updateLayout() when some serious changes are detected.
	Maybe part of layoutctrl.js?
	
	Changes
	2007-12-05 cma: Stopping default behavior on in-page links (preliminary).
	2007-12-04 cma: Capturing in-page anchor links into cards (experimental).
	2007-10-30 cma: CSS enhanded (relative postion for .card).
	2007-10-30 cma: CSS enhanced (float-clearing for .cards, .cardsControl, .card).
	2007-08-21 cma: Bugfixing (.prevPrev -> .showPrev, cardsCard width 100%).
	2007-08-07 cma: Basic formatting added.
	2007-08-07 cma: First preparations to differenciate between MSIE 6 and 7/std.
	2007-03-18 cma: Using "zoom: 1" to fix MSIE bugs (experimental).
	2007-02-19 cma: Changed .findCards to .initElements (new standard).
	2007-02-09 cma: Changing className of documentElement *after* document.write
	                to avoid serious problems with Firefox ...
	2007-02-07 cma: MSIE 7 bug fixed (problem with .card float left).
	2007-02-05 cma: Implementing new updateLayout() functionality.
	2007-02-02 cma: First steps to dynamic usage.
	2006-11-30 cma: Experimental improvements on CSS for better initialization.
	2006-11-24 cma: ".cards, .cardsControl {position: relative}"
	                and other improvements in initial CSS.
	2006-11-22 cma: "html.cardsActive" added.
	2006-11-22 cma: Deleted "page-break-inside: avoid" (cards can be *very* large ...).
	2006-11-22 cma: Using "invisibleOnScreen" to hide cards.
	2006-11-01 cma: printSelectedCardOnly implemented (several changes were necessary).
	2006-06-13 cma: Initial CSS improved.
	2006-05-18 cma: MSIE 6/Win printing problem fixed!
	2006-05-16 cma: Bugfixing.
	2006-05-08 cma: Final investigations on fragment identifier problem. No fix available.
	2006-05-06 cma: Tabs as DIVs, using innerHTML for text in tabs; init bug fixed.
	2006-04-12 cma: CSS finetuning.
	2006-04-10 cma: MSIE dynamic property bug fixed; integrated with conditional comment.
	2006-04-08 cma: Basic CSS included; page layout during init consolidated (.cardsReady).
	2006-04-01 cma: Entity support for card title (XML base + numeric).
	2006-03-13 cma: Local Firefox setTimeout-workaround shifted to pagecomplete.js!
	2006-03-13 cma: Cards.updateLayout() and .setSizes() added.
	2006-03-04 cma: Call to updateLayout() added with .showCard().
	2006-01-31 cma: Bug in isShowCardById() fixed.
	2006-01-29 cma: isShowCardById() returns success, showCardById() returns nothing.
	2006-01-28/29 cma: Cosmetics.
	2006-01-27 cma: Setting width of card only if smaller than cardsContainer!
	2006-01-26 cma: Width-change problem fixed (set width before reading height).
	2006-01-20 cma: cardTitle fixed for MSIE 5.0/Win.
	2006-01-20 cma: cardTitle added (as with slides.js).
	2006-01-20 cma: Stable "pageComplete()" usage, card width "bug" fixed -- declared v1.00!
	2006-01-01 cma: Experimental use of "pageComplete()".
	2005-10-12 cma: Using RegExp to find cards divs.
	2005-08-19 cma: Bug in tag elimination fixed (tab title from headline).
	2005-08-17 cma: Little change in object manipulation order.
	2005-08-16 cma: Fixed MSIE 5.x/Win bug with comment nodes of type 1.
	2005-08-15 cma: Warnings cleanup.
	2005-08-14 cma: Improved initial showCardById (scroll to top).
	2005-08-13 cma: Improved .showCardById (inside out); completed basic API.
	2005-08-12 cma: Reversed cardset order on init (to get inner cardsets first :-).
	2005-08-11 cma: Bug in headline extraction fixed (remove leading/trailing spaces).
	2005-08-09 cma: Initial redesign based on cardset.js and gallery.js
	
*/


/* ===========================
   Constructor function ...
=========================== */

function Cards(obj) {
	if (!obj) return void 0;
	if (!obj.className || !obj.className.match(/(^|\s)cards(\s|$)/)) return obj;
	var that = obj;
	that.cardSet = Cards.cardSets.length;
	Cards.cardSets[that.cardSet] = that;
	that.cards = new Array();
	that.tabs = new Array();
	that.vis = -1;

	that.container = that.insertBefore(document.createElement('div'), that.firstChild);
	that.container.className = 'cardsContainer';

	that.control = document.createElement('div');
	that.control.className = 'cardsControl';
	
	var maxHeight = 0;
	var elm = that.container.nextSibling;
	while (elm) {
		if (elm.nodeType == 1 && elm.tagName != '!') {
			var card = document.createElement('div');
			card.className = 'cardsCard invisibleOnScreen';
			card._set = that;
			card._card = that.cards.length;
			that.cards[card._card] = card;
			that.container.appendChild(card);
			card.appendChild(elm);
			elm.style.visibility = 'inherit';
			//if (that.container.offsetWidth && card.offsetWidth < that.container.offsetWidth)
			//	card.style.width = that.container.offsetWidth + 'px';
			if (card.offsetHeight && (card.offsetHeight > maxHeight)) {
				maxHeight = card.offsetHeight;
				that.container.style.height = maxHeight + 'px';
			}	
			elm = that.container.nextSibling;
		}
		else elm = elm.nextSibling;
	}
	
	for (var c=0; c<that.cards.length; c++) {

		var thisCard = that.cards[c].firstChild;
		var txt = '';
		if (thisCard.title) txt = thisCard.title;
		if (!txt) {
			var elms = (thisCard.all && !window.opera) ? thisCard.all : thisCard.getElementsByTagName('*');
			for (var j=0; j < elms.length; j++) {
				var thisElm = elms[j];
				if (thisElm.className 
				&& (thisElm.className.match(/(^|\s)cardTitle(\s|$)/)) 
				&& thisElm.innerHTML) {
					txt = thisElm.innerHTML;
					break;
				}
			}
		};
		if (!txt) {
			for (var i=1; i <= 6; i++) {
				var elms = thisCard.getElementsByTagName('h' + i);
				if (elms && elms.length && elms[0].innerHTML) {
					txt = elms[0].innerHTML;
					break;
				}
			};
		}
		
		if (!txt) txt = (c + 1) + '';
		
		var tab = document.createElement('div');
		tab.className = 'cardsTab';
		tab._set = this;
		tab._card = c;
		var tablnk = document.createElement('a');
		tablnk.innerHTML = txt;
		tablnk.href = 'javascript:Cards.showCardInSetByIdx('+ c + ',' + that.cardSet + ')';
		tab.appendChild(tablnk);
		that.tabs[c] = tab;
		that.control.appendChild(tab);
	};

	var br = that.control.appendChild(document.createElement('br'));
	br.clear = 'all';
	var bot = that.control.appendChild(document.createElement('div'));
	bot.className = 'cardsControlBottom';

	that.className += ' cardsReady';
	that.insertBefore(that.control, that.firstChild);

	/* Extend obj with prototype methods ... */
	for (var m in Cards.prototype) that[m] = Cards.prototype[m];
	
	that.showIdx(0);
	
	return that;
}



/* =======================
   Instance methods ...
======================= */

Cards.prototype.setSizes = function() {
	if (!(this.cards && this.cards.length)) return;
	var maxHeight = 0;
	var elm = null;
	for (var i=0; i<this.cards.length; i++) {
		elm = this.cards[i];
		if (this.container.offsetWidth && elm.offsetWidth < this.container.offsetWidth)
			elm.style.width = this.container.offsetWidth + 'px';
		if (elm.offsetHeight && (elm.offsetHeight > maxHeight))
			maxHeight = elm.offsetHeight;
	}
	if (maxHeight > 0) this.container.style.height = maxHeight + 'px';
}

Cards.prototype.showIdx = function(c) {
	if ((c < 0) || (c > this.cards.length-1) || c == this.vis) return;
	if (this.vis > -1) {
		this.cards[this.vis].className =
			this.cards[this.vis].className.replace(' cardsCardSelected', ' invisibleOnScreen');
		this.tabs[this.vis].className = 'cardsTab';
	};
	this.vis = c;
	this.cards[c].className =
		this.cards[c].className.replace(' invisibleOnScreen', ' cardsCardSelected');
	this.tabs[c].className = 'cardsTabTop';
	//if (typeof updateLayout == 'function') updateLayout();
	this.updateLayout();
};

Cards.prototype.showCard = Cards.prototype.showIdx;

Cards.prototype.showNext = function() {
	if (this.vis < (this.cards.length -1)) this.showIdx(this.vis+1);
};

Cards.prototype.nextCard = Cards.prototype.showNext;

Cards.prototype.showPrev = function() {
	if (this.vis > 0) this.showIdx(this.vis-1);
};

Cards.prototype.prevCard = Cards.prototype.showPrev;

Cards.prototype.updateLayout = function() {
	this.setSizes();
	var p = this.parentNode;
	while (p) {
		if (p.updateLayout) {
			p.updateLayout();
			break;
		};
		p = p.parentNode;
	};
}


/* ==========================
   Class properties ...
========================== */

Cards.cardSets = new Array();
Cards.scrollAfterOnload = false;

Cards.oldIE = !!(!window.opera && document.all && !(window.XMLHttpRequest && document.compatMode == 'CSS1Compat'));

Cards.initialCSS = '\
<style type="text/css">\
	.cards, .cardsControl, .card {position: relative;}\
	.cards:before, .cards:after,\
	.cardsControl:before, .cardsControl:after,\
	.card:before, .card:after {\
		content: ".";\
		display: block;\
		height: 0;\
		visibility: hidden;\
		clear: both;\
	}\
	.cardsControlSpacer, .cardsControlBottom {display: none;}\
	.cards>.card/* */ {\
		visibility: hidden;\
		position: absolute;\
		top: 0;\
	}\
	.cards>.card:first-child/* */ {\
		visibility: inherit;\
		position: relative;\
	}\
	.cardsContainer {\
		margin: 0;\
		padding: 0;\
		position: relative;\
	}\
	.cardsCard {\
		margin: 0;\
		padding: 0;\
		position: absolute;\
		top: 0;\
		left: 0;\
		width: 100%;\
	}\
	.noCardsControl>.cardsControl/* */ {\
		display: none;\
	}\
	.cardsTab, .cardsTabTop {\
		display: block;\
		float: left;\
		margin-right: 1em;\
	}\
	.cardsTabTop {background-color: #ebebeb;}\
	.cardsTab a, .cardsTabTop a {\
		display: block;\
		white-space: nowrap;\
	}\
	.cardsTabTop a:link, .cardsTabTop a:visited, .cardsTabTop a:hover {\
		cursor: default;\
	}\
	@media screen {\
		.cards {padding-top: 2em;}\
		.cardsReady {padding-top: 1em}\
		.cardsControlSpacer {display: block; height: 2em;}\
		.invisibleOnScreen {visibility: hidden;}\
		.cardTitle, .hideOnCards {display: none;}\
	}\
	@media print {\
		.cardsControl, .cardsControlSpacer {display: none !important;}\
		.cardsActive .cardsReady {padding-top: 0;}\
		.cards, .cardsContainer, .cardsCard {\
			position: static !important;\
			height: auto !important;\
		}\
		.printSelectedCardOnly .cardsCard {\
			display: none;\
		}\
		.printSelectedCardOnly .cardsCardSelected {\
			display: block;\
		}\
		.showOnCards {display: none;}\
	}\
<\/style>\
';

if (Cards.oldIE) {
	Cards.initialCSS += '\
<!--[if IE]>\
	<style type="text/css">\
		.cards, .cardsControl, .card {zoom: 1;}\
		.card {\
			top: 0;\
			visibility: expression((parentElement.className.match(/(^|\\s)cards(\\s|$)/)) ? ((parentElement.children[0] != this) ? "hidden" : "inherit") : "inherit");\
			position: expression((parentElement.className.match(/(^|\\s)cards(\\s|$)/)) ? ((parentElement.children[0] != this) ? "absolute" : "relative") : "relative");\
		}\
		.cardsControl {\
			display: expression((parentElement.className.match(/noCardsControl/)) ? "none" : "block");\
		}\
	<\/style>\
<![endif]-->\
';
};


/* ====================
   Class methods ...
==================== */

Cards.initElements = function(obj) {
	if (!document.getElementsByTagName) return;
	var divs = (obj||document).getElementsByTagName('div');
	var cardSets = new Array();
	if (divs) {
		for (var i=0; i<divs.length; i++) {
			var n = divs[i];
			if (n.className && n.className.match(/(^|\s)cards(\s|$)/)) cardSets[cardSets.length] = n;
		}
	};
	if (cardSets.length) {
		for (var j=0; j<cardSets.length; j++) new Cards(cardSets[j]);
	};
	
	if (document.addEventListener)
		document.addEventListener("click", Cards.checkId, false)
	else if (document.attachEvent)
		document.attachEvent("onclick", Cards.checkId);
}

Cards.findCards = Cards.initElements;

Cards.updateLayout = function() {
	//for (var i=0; i<Cards.cardSets.length; i++) Cards.cardSets[i].updateLayout();
}

Cards.cardSetByIdx = function(n) {
	if ((n < 0) || (n > Cards.cardSets.length-1)) return null;
	return Cards.cardSets[n];
}

Cards.showCardInSetByIdx = function(c, n) {
	var set = Cards.cardSetByIdx(n);
	if (!set) return;
	set.showIdx(c);
}

Cards.cardSetById = function(id) {
	if (!document.getElementById) return null;
	var elm = document.getElementById(id);
	return (elm && elm.cards) ? elm : null;
}

Cards.prevCardById = function(id) {
	if (!document.getElementById) return;
	var elm = document.getElementById(id);
	if (elm && elm.cards) elm.prevCard();
}

Cards.nextCardById = function(id) {
	if (!document.getElementById) return;
	var elm = document.getElementById(id);
	if (elm && elm.cards) elm.nextCard();
}

Cards.isShowCardById = function(id) {
	if (!document.getElementById) return false;
	var elm = document.getElementById(id);
	if (!elm) return false;
	if (elm._set) elm._set.showCard(elm._card);
	var par = elm.parentNode;
	while (par) {
		if (par._set && typeof par._card != 'undefined') par._set.showCard(par._card);
		par = par.parentNode;
	}
	return true;
}

Cards.showCardById = function(id) {
	Cards.isShowCardById(id);
	return void 0;
}

Cards.checkId = function(evt) {
	evt = (evt) ? evt : ((event) ? event : null); if (!evt) return true;
	var elm = evt.target || evt.srcElement; if (!elm) return true;
	if (!elm.tagName || !elm.tagName.match(/^a$/i) || !elm.href) return true;
	
	// Get target ID ...
	var id = elm.href.match(/#[^#]+$/); if (!id) return true;
	id = id[0]; id = id.substring(1); if (!id) return true;
	
	// Check for same page ...
	var tpage = elm.href.match(/^[^#]+/); if (tpage) tpage = tpage[0];
	var apage = document.location.href.match(/^[^#]+/); if (apage) apage = apage[0];
	if (!(tpage && (tpage == apage))) return true;

	if (Cards.isShowCardById(id) && document.getElementById) {
		if (evt.preventDefault) evt.preventDefault();
		if (typeof evt.cancelBubble != 'undefined') evt.cancelBubble = true;
		if (typeof evt.returnValue != 'undefined') evt.returnValue = false;
		return false;
	}
	
	return true;
}


/* =================
   Initialize ...
================= */

if (document.getElementsByTagName 
	&& document.createElement
	&& (!window.opera || document.createDocumentFragment)
	&& !(!window.opera && navigator.userAgent.match(/MSIE 5\..*Mac/))) {

	Cards.initComplete = false;

	document.write(Cards.initialCSS);
	
	Cards.docElm = document.documentElement;
	if (Cards.docElm) {
		if (!Cards.docElm.className) {
			Cards.docElm.className = 'cardsActive';
		} else if (!Cards.docElm.className.match('cardsActive')) {
			Cards.docElm.className += ' cardsActive';
		};
	};
	
	Cards.init = function() {

		Cards.initElements();

		if (document.location.hash) {
			var id = document.location.hash;
			id = id.replace('#', '');
			if (Cards.isShowCardById(id) && window.scrollTo) window.scrollTo(0, 0);
		}

		Cards.initComplete = true;
		return true;
	}
	
	if (window.onDomReady) {
		window.onDomReady(Cards.init);
	} else if (typeof pageComplete == 'function') {
		Cards.oldPageComplete = pageComplete || function(){};
		pageComplete = function() {
			Cards.oldPageComplete();
			Cards.init();
		};
	} else {
		Cards.oldOnload = window.onload || function(){};
		window.onload = function() {
			Cards.oldOnload();
			Cards.init();
		};
	};


}

/* End of cardset.js */
