﻿/*	ICC-Generator.js : I Ching Connexion stuff

*/

//	now for "Porting over JavaScript 1.6 Array methods"
/*
	* Sugar Arrays (c) Creative Commons 2006
	* http://creativecommons.org/licenses/by-sa/2.5/
	* Author: Dustin Diaz | http://www.dustindiaz.com
	* Reference: http://www.dustindiaz.com/basement/sugar-arrays.html
*/
Function.prototype.method=function(name,fn){this.prototype[name]=fn;return this;};if(!Array.prototype.forEach){Array. method('forEach',function(fn,thisObj){var scope=thisObj||window;for(var i=0,j=this.length;i<j;++i){fn.call(scope,this[i],i,this);}});}
//	I've only extracted the forEach-part out of sugar-arrays-min.js


function getJavaScript( url)
{
	document.write('<script type="text/javascript" src="' + url + '"></scr'+'ipt>');
	//	Note that you should split the closing script tag into two parts.
}	//	getJavaScript

getJavaScript( '/javascripts/AjaxConnexion.js');
getJavaScript( '/javascripts/ICC-Divination.js');


/*	The cells of the grid will be staggered.
	The size of the hexGrid is assumed to be controlled
	by an external css, which defines at least a td.half class.
	The grid cells will hold an image element, 
	whose id equals the coordinate hexgridID, like '3,2' (col 3, diagonal 2).
	TopLeft = '0,0'
	the 'diagonal' line goes top-right vv bottom-left;
*/

var sepCoord = ',';

var init_img = ICC_Hexagrams_imgroot + 'blanc.gif';
// init_img = '';


/*	writeHexGrid constructs a grid within a table like this:
	in 'col,row' coordinates of the hex grid, aka hexgridID
	each cell contains an image with this as its element id.
	
	writeHexGrid( 5, 7) gives:
	note pRow applies to the row in the rectangular table!
	
 	0,0		2,1		4,2
 		1,1		3,2
 	0,1		2,2		4,3
 		1,2		3,3
 	0,2		2,3		4,4
 		1,3		3,4
 	0,3		2,4		4,5
 
*/

function writeHexGrid( pCols, pRows)
{	var iRow, iCol, iDiag;
	var hexgridID, rowMod2, rowDiv2;

	//	first the style
	document.write( '<style type="text/css">table.hexgrid {padding:4px;margin:0px;} .hexgrid tr {vertical-align:top;padding:0px;margin:0px;border:0px;height:15px;} .hexgrid td {padding:0px;margin:0px;width:27px;height:30px;} .hexgrid td.half {height:15px;} .hexgrid img {position:absolute;padding:0px;margin:0px;} div#connexion-name {font-weight:bold;margin:0px;height:5em;display:none;}</style>');

	if (pCols > 0 && pCols > 0)
	{	document.write( '<div id="connexion-name"></div>');
		//	to force enough width for the hexgrid we put it in a containing div
		//	min width = pCols*cellwidth + 2*tablepadding (provided border-spacing=0 and border=0 - use old-style for this, so IE understands it)
		document.write( '<div style="min-width:' + (pCols*27 + 2*4) + 'px;"><table class="hexgrid" cellspacing="0" cellpadding="0">');
	
		//	all the bits depend on the top-left gridcoord, be it 0's or 1's
		for (iRow = 0 ; iRow < pRows ; iRow += 1)
		{
			rowMod2 = (iRow+1) % 2 ;				//	remainder after division by 2
			rowDiv2 = (iRow+1 - rowMod2) / 2;		//	make sure there's no fraction
			document.write( '<tr>');
			for (iCol = 1 - rowMod2, iDiag = rowDiv2 ; iCol < pCols ; iCol += 2, iDiag += 1)
			{
				hexgridID = iCol + sepCoord + iDiag;
				document.write( '<td rowspan="2"><a target="Hexagram meaning"><img src="' + init_img + '" id="' + hexgridID + '" title="" alt="" /></a></td>');
				if (iRow == 0 						//	first row is somewhat different.
					&& pCols - iCol > 1)
				{	document.write( '<td class="half"></td>');	//	half-height in-between cells
				}
			}
			document.write( '</tr>');
		}
		
		//	complete the last row - one half-height cell is enough
		document.write( '<tr><td class="half"></td></tr>');
	
		document.write( '</table></div>');
	}
	
}	//	writeHexGrid


function Hex2TablePos( hexpos)
{	var tabpos = new Array();

	tabpos[ 0] = hexpos[ 0];	//	col stays the same
	tabpos[ 1] = 2*hexpos[ 1] - hexpos[ 0];
	
	return tabpos;

}	//	Hex2TablePos


//	mapping of direction indices to board-offsets
//	######## the same mapping is used in get-hex-connexion.cgi ! #########
//	for each idir, the vector/offset to a neighbour is given as [ col, row]
var idir2brddir =	[	[ -1,  0]		// SW
					,	[  0,  1]		// S
					,	[  1,  1]		// SE
					,	[  1,  0]		// NE
					,	[  0, -1]		// N
					,	[ -1, -1]		// NW
					];

//	map direction index to direction bit
var idir2bitdir = [ 1, 2, 4, 8, 16, 32];


//	returns the position in the given direction idir in relation to startPosition
function posInDir( startPos, idir)
{	var endPos = new Array();

	for (var i = 0 ; i < startPos.length ; i++)
	{	endPos[ i] = startPos[ i]*1 + idir2brddir[ idir][ i];	//	force number (*1) before addition
	}
	
	return endPos;

}	//	posInDir

//	returns true if the given positions are touching or the same
function isNextTo( p1, p2)
{	var isNbr = true;

	for (var i = 0 ; i < p1.length ; i++)
	{	isNbr = isNbr && (Math.abs( p1[ i]*1 - p2[ i]*1) <= 1);	//	force number (*1) before substraction
	}
	
	return isNbr;

}	//	isNextTo


//	returns the opposite direction
function oppositeDir( idir)
{
	return (idir + idir2brddir.length/2) % idir2brddir.length;

}	//	oppositeDir


function setImage( hexgridID, newurl, newtitle, newtarget)
{
	var imgObj = document.getElementById( hexgridID);
	var anchorObj;
	if (imgObj != null)
	{	imgObj.src = (newurl == null ? init_img : newurl);
		imgObj.title = (newtitle == null ? '' : newtitle);
		
		//	note the anchor directly encloses the image
		anchorObj = imgObj.parentNode;
		if (newtarget == null || newtarget == '')
		{	//	remove href attribute
			if (anchorObj.getAttribute( 'href'))
			{	anchorObj.removeAttribute( 'href');
			}
		}
		else
		{	anchorObj.setAttribute( 'href', newtarget);
		}
	}

}	//	setImage

function removeImage( hexgridID)
//	when used as a foreach function, the parameter list will be: element, index, array
{
	setImage( hexgridID);		//	implicit clearing the position
	
}	//	removeImage



/*	=== object Rectangle ===	
	holds top left bottom right coordinates
	defaults to 0 0 0 0
*/
//	constructor
function Rectangle( top, left, bottom, right)
{
	this.top = (top == null ? 0 : top);
	this.left = (left == null ? 0 : left);
	this.bottom = (bottom == null ? 0 : bottom);
	this.right = (right == null ? 0 : right);
	//	methods
	this.Addpt = RectangleAddpt;
}	//	Rectangle

function RectangleAddpt( col, row)
{
	this.top = Math.min( this.top, row);
	this.bottom = Math.max( this.bottom, row);
	this.left = Math.min( this.left, col);
	this.right = Math.max( this.right, col);

}	//	RectangleAddpt

/*	=== end of object Rectangle ===	*/



/*	=== object Connexion ===	
	holds a laid-out Connexion
*/
//	constructor
function Connexion( name)
{	this.name = (name ? name : '');
	this.positions = new Array();
	this.bbox = new Rectangle();
	this.divination = null;
	this.waning = null;		//	array: element holds the line (1-6) a hexagram is waning on
	this.waxing = null;		//	array:											  waxing
	//	methods
	this.Put = ConnexionPut;
	this.SetDivination = ConnexionSetDivination;
	this.Display = ConnexionDisplay;
	this.MoveIntoView = ConnexionMoveIntoView;
	
	this.SetHexagramClass = ConnexionSetHexagramClass;
	this.GetHexImgClass = ConnexionGetHexImgClass;
	
	this.ToCookie = ConnexionToCookie;
	this.FromCookie = ConnexionFromCookie;
}

//	add a hexagram with 
//	binary nr
//	on hexgridID
function ConnexionPut( nr, hexgridID)
{
	this.positions[ nr] = hexgridID; 
	
	var pos = hexgridID.split( sepCoord);	//	yielding [ col, diag ] array
	this.bbox.Addpt( pos[0], pos[1]);

}	//	ConnexionPut



//	sets the class of the hexagram (waning, waxing)
function ConnexionSetHexagramClass( hexval)
{	var hexImgClass = 'normal/';
	var i;
	var hexgridID = this.positions[ hexval];

	if (typeof this.divination == 'object' && this.divination)
	{	//	we have a true divination
		var hexPos = hexgridID.split( sepCoord);
		var nbrPos;
		
		//	the hexagram is waning when next to the actual hexagram
		//	in a direction contained by the mediator
		var considerBits = this.divination.actualVal & this.divination.GetMediatorVal();
		var actualPos = this.positions[ this.divination.actualVal].split( sepCoord);
		if (isNextTo( hexPos, actualPos))
		{	//	otherwise too far away, anyway
			for (i = 0 ; i < idir2bitdir.length ; i++)
			{	if ((idir2bitdir[ i] & considerBits) != 0)
				{	nbrPos = posInDir( actualPos, i);
					if (nbrPos.join( sepCoord) == hexgridID)
					{	this.waning[ hexval] = i + 1;
					}
				}
			}
		}
		
		//	the hexagram is waxing when next to the future hexagram
		//	in a direction contained by the mediator
		considerBits = this.divination.futureVal & this.divination.GetMediatorVal();
		var futurePos = this.positions[ this.divination.futureVal].split( sepCoord);
		if (isNextTo( hexPos, futurePos))
		{	//	otherwise too far away, anyway
			for (i = 0 ; i < idir2bitdir.length ; i++)
			{	if ((idir2bitdir[ i] & considerBits) != 0)
				{	nbrPos = posInDir( futurePos, i);
					if (nbrPos.join( sepCoord) == hexgridID)
					{	this.waxing[ hexval] = i + 1;
					}
				}
			}
		}
	}

}	//	ConnexionSetHexagramClass


//		associate a divination oblect with the Connexion
function ConnexionSetDivination( divination)
{	var hex;

	if (typeof divination == 'object' && divination)
	{	//	looks like a true divination object
		this.divination = divination;
		this.waning = new Array();
		this.waxing = new Array();
		for (hex in this.positions)	
		{	if (!isNaN( hex))	//	make sure it's an index
			{	this.SetHexagramClass( hex);
			}
		}
	}
	else
	{	this.divination = null;
		this.waning = null;
		this.waxing = null;
	}

}	//	ConnexionSetDivination

var _displayConnexion = new Connexion( 'dummy');

function ConnexionDisplay( )
{
	//	first remove current Connexion from the display
	_displayConnexion.positions.forEach( removeImage);
	if (this.divination == null)
	{	this.SetDivination( currentDivination);
	}
	_displayConnexion = this;
	_displayConnexion.MoveIntoView();
	_displayConnexion.positions.forEach( setImageByBinary);

	var cnxName = document.getElementById( "connexion-name");
	cnxName.innerHTML = _displayConnexion.name;
	
	_displayConnexion.ToCookie();
	
}	//	ConnexionDisplay


//	sets the proper hexagram image in the hexgrid
function setImageByBinary( hexgridID, hexVal)
//	when used as a foreach function, the parameter list will be: element, index, array
{	var newimgurl, newtitle;
	var hexgram = Hexagrams[ hexVal];
	var hexStr = '00' + hexVal;
	hexStr = hexStr.substring( hexStr.length - 2);
	
	var hexImgClass = _displayConnexion.GetHexImgClass( hexVal);

	newimgurl = ICC_Hexagrams_imgroot + hexImgClass + hexStr + '.gif';
	
//	newtitle = hexgram.ic_nr + '. ' + hexgram.chin_name + ' - ' + hexgram.eng_name;
	newtitle = hexgram.GetDescription();
	if (_displayConnexion.divination)
	{	if (_displayConnexion.divination.actualVal == hexVal)
		{	newtitle += '\n- Actual hexagram';
		}
		if (_displayConnexion.divination.futureVal == hexVal)
		{	newtitle += '\n- Future hexagram';
		}
		if (_displayConnexion.divination.GetMediatorVal() == hexVal)
		{	newtitle += '\n- Mediator';
		}
		if (_displayConnexion.waning[ hexVal])
		{	newtitle += '\n- Waning influence on line ' + _displayConnexion.waning[ hexVal];
		}
		if (_displayConnexion.waxing[ hexVal])
		{	newtitle += '\n- Waxing influence on line ' + _displayConnexion.waxing[ hexVal];
		}
	}
	
	setImage( hexgridID, newimgurl, newtitle, hexgram.meaning_url);

}	//	setImageByBinary


//	map flag bits to img class
//		flag		bit
//		divine		1
//		mediator	2
//		waning		3
//		waxing		4
var Bits2ImgClass = [ 'nolines/', 'divine/', 'normal/', 'divinorm/'
					, 'waning/', 'divinwan/', 'normwan/', 'divinormwan/'
					, 'waxing/', 'divinwax/', 'normwax/', 'divinormwax/'
					, 'wanwaxing/', 'divinwanwax/', 'normwanwax/', 'divinormwanwax/'
					];

//	returns the subdirectory relating to the class of the hexagram (normal, waning, waxing)
function ConnexionGetHexImgClass( hexval)
{	var hexImgClass = 'normal/';

	if (this.divination)
	{	var flagBits = 0;
	
		Bits2ImgClass[ 0] = showAllHexagrams ? 'normal/' : 'nolines/';

		if (this.divination.actualVal == hexval
			|| this.divination.futureVal == hexval
		   )
		{	flagBits |= 1;		//	divine bit
		}

		if (this.divination.GetMediatorVal() == hexval)
		{	flagBits |= 2;		//	mediator bit
		}

		if (this.waning[ hexval])
		{	flagBits |= 4;		//	waning bit
		}

		if (this.waxing[ hexval])
		{	flagBits |= 8;		//	waxing bit
		}
		
		hexImgClass = Bits2ImgClass[ flagBits];
	}
	
	return hexImgClass;

}	//	ConnexionGetHexImgClass


//	make sure every position is visible on the grid
//	although the cgi-script will return all-positive coordinates
//	a positive diagonal coordinate might be out of rectangular view
function ConnexionMoveIntoView()
{	var minRectRow = 0;		//	we'll be looking for the top only
	var rrow, hex, vpos, hexgridID;


	for (hex in this.positions)		//	steps through each property of the positions array (= hex value and more)
	{
		if (!isNaN( hex))		//	so restict to index property only, i.e. for a property-'name' that's a number
		{	//	toString() is overdone, but IE doesn't think it's a string otherwise
			vpos = Hex2TablePos( this.positions[ hex].toString().split( sepCoord));	//	yielding [ col, tabrow ] array
	
			minRectRow = Math.min( minRectRow, vpos[1]);		//	rrow = 2*diag - col
		}
	}
	if (minRectRow < 0)
	{	// shift it all up
		upshift = Math.ceil( (-minRectRow)/2);
		for (hex in this.positions)	
		{
			if (!isNaN( hex))
			{	//	strangely enough, now IE allows a split on positions[ hex] ?!?
				vpos = this.positions[ hex].split( sepCoord);	//	yielding [ col, diag ] array
				vpos[ 1] = vpos[ 1]*1 + upshift;		//	force number before addition
				this.positions[ hex] = vpos.join( sepCoord);
			}
		}
	}

}	//	ConnexionMoveIntoView


var connexionCookieKey = 'ICConnexion';
var connexionCookieKey_OLD = 'ICC_connexion';	//	used before cookie domain was generalized to "mindsport.nl"
							//	which was until 4-10-2008, so it'll be obsolete completely after 4-10-2009

function ConnexionToCookie()
{
	//	see /cms/Joomla15/media/system/js/mootools-uncompressed.js
	
	Cookie.set( connexionCookieKey
				, Json.toString( { positions: this.positions		//	we forget the name
								})
				, ICC_CookieOptions()		//	see ICC-Divination.js
			  );

}	//	ConnexionToCookie

//	returns false if cookie didn't exist
function ConnexionFromCookie()
{
	//	see /cms/Joomla15/media/system/js/mootools-uncompressed.js
	
	var cnxCookie = Cookie.get( connexionCookieKey) || Cookie.get( connexionCookieKey_OLD);
	
	if (cnxCookie)
	{	var cnxObj = Json.evaluate( cnxCookie);
		this.positions = cnxObj.positions;
	}
	
	return cnxCookie != false;

}	//	ConnexionFromCookie


/*	=== end of object Connexion ===	*/



//	display/page preferences
var showAllHexagrams = false;

function SetShowAllHexagrams( on)
{
	if (showAllHexagrams != on)
	{	showAllHexagrams = on;
		_displayConnexion.Display();
	}
}	//	SetShowAllHexagrams

function ToggleShowAllHexagrams()
{
	SetShowAllHexagrams( !showAllHexagrams);
	
}	//	ToggleShowAllHexagrams



