/* Common Javascript Page 

Version 2.0 (Relies on prototype v1.6)

**Prototype MUST be included BEFORE this script


By Steven Miller

v2.0.1 (10/9/2008)
	-Added autoexec id processing, automatically close windows or redirect to a different url
	-Removed or commented out some old code, hopefully nothing got broken
v2.0.2 (11/15/2009)
	-Bug fixed in typeof test for Effects existance
	
*/

/*
var ua    = navigator.userAgent;
var d     = document;
var w     = window;
var edit  = false;
var start = -1;
var end   = -1;
var len   = -1;
*/

if ( typeof(Effect) == "object" ) {
	Ajax.Responders.register({
		onCreate: function() { $('AJAXSpinner' ).show(); },
		onComplete: function() {
			if (0 == Ajax.activeRequestCount)
				$('AJAXSpinner' ).hide();
		}
	});
}

/*
if (d.selection || (ua.indexOf('Gecko') >= 0 && ua.indexOf('Safari') < 0)) {
	edit = true;
}*/

function openWindow(url, name, width, height) {
  if (height == null) height = 450;
  if (width == null)  width = 500;

	//If Height or width are less then 100, then assume we want percent of screen size
	if ( width < 100 )
		width = screen.width * (width/100);
	if ( height < 100 )
		height = screen.height * (height/100);

	//Pad the windows height and width the url so that the page can work with it
	if ( url.include('?') )
		url = url + '&xww='+width+'&xwh'+height;
	else
		url = url + '?xww='+width+'&xwh'+height;

  var win;
  var winleft = (screen.width - width) / 2;
  var wintop  = (screen.height - height) / 2;
	
  win = window.open(url, name, 'scrollbars=yes,menubar=no,' +
                               'toolbar=no,width=' + width + 
                               ',height='+height +
                               ',top=' + wintop + 
                               ',left=' + winleft + ',resizable=yes'
		    );
  if (parseInt(navigator.appVersion) >= 4) { win.window.focus(); }
	
  return win;
}

// Borrowed from script.aculo.us' effects.js...
Element.addMethods({
  collectTextNodes: function(element) {  
    return $A($(element).childNodes).collect( function(node) {
      return (node.nodeType==3 ? node.nodeValue : 
        (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
    }).flatten().join('');
  }
});


/***
 * Excerpted from "Prototype and script.aculo.us",
 * published by The Pragmatic Bookshelf.
 * Copyrights apply to this code. It may not be used to create training material, 
 * courses, books, articles, and the like. Contact us if you are in doubt.
 * We make no guarantees that this code is fit for any purpose. 
 * Visit http://www.pragmaticprogrammer.com/titles/cppsu for more book information.
***/
var TableSorter = Class.create({
	initialize: function(element,sortSecondary) {
		this.element = $(element);
		this.sortIndex = -1;
		this.sortOrder = 'asc';
		if ( sortSecondary === null ) {
			this.sortSecondary = false;
		} else {
			this.sortSecondary = sortSecondary; //Do we want to sort on TWO columns
			this.sortSecIndex = 0;
		}
		this.activeSortType = "defsort";
		this.activeSecSortType = "defsort";
		this.initDOMReferences();
		this.initEventHandlers();
	}, // initialize
	
  initDOMReferences: function() {
    var head = this.element.down('thead');
    var body = this.element.down('tbody');
    if (!head || !body)
      throw 'Table must have a head and a body to be sortable.';
    this.headers = head.down('tr').childElements(); 
    this.headers.each(function(e, i) { 
      e._colIndex = i;
    });
    this.body = body;
  }, // initDOMReferences

  initEventHandlers: function() {
    this.handler = this.handleHeaderClick.bind(this); 
    this.element.observe('click', this.handler);
  }, // initEventHandlers
	
  handleHeaderClick: function(e) {
    var element = e.element();
    if (!('_colIndex' in element)) {
      element = element.ancestors().find(function(elt) { 
        return '_colIndex' in elt;
      });
      if (!((element) && '_colIndex' in element))
        return;
    }
		//Do not sort columns that have the 'nosort' class in the header
		if (this.headers[element._colIndex].hasClassName('nosort') ) {
			return;
		}
		this.sort(element._colIndex);
  }, // handleHeaderClick

  adjustSortMarkers: function(index) {
		//Remove current sort indicator from table header
		if (this.sortIndex != -1)
	      this.headers[this.sortIndex].removeClassName('sort-' + this.sortOrder);
		
		//User click again on the same column as before, normally this means we sort in the opposite direction, unless column class says otherwise
		if ( this.headers[index].hasClassName('sortdesconly') ) {
	      this.sortOrder = 'desc';
   	   this.sortIndex = index;
		} else if ( this.headers[index].hasClassName('sortasconly') ) {
      	this.sortOrder = 'asc';
      	this.sortIndex = index;			
    } else if (this.sortIndex != index) {
		//User clicked on a new column
      this.sortOrder = (this.headers[index].hasClassName('sortdescfirst') ? 'desc' : 'asc');
      this.sortIndex = index;
    } else {
      this.sortOrder = ('asc' == this.sortOrder ? 'desc' : 'asc');
		}
		//Set header index to indicate sort
    this.headers[index].addClassName('sort-' + this.sortOrder);
  }, // adjustSortMarkers

	getDetector: function(sortindex) {
		var returnSortType = "defsort";
		var rows = this.body.childElements();
		this.detectors.each(function(detector) {		
			//Check everyrow for match, this may be very slow
			switch (detector['testOn']) {
				case "title" :
					var isMatched = true; //This will fail if row count = 0					
					
					rows.each(function(row, index) {
						if ( !detector.re.test( row.childElements()[sortindex].readAttribute('title') ) ) {
							isMatched = false;
							throw $break;
						}
					}.bind(this));
					
					if ( isMatched ) {						
						returnSortType = detector['type'];
						throw $break;
					}
					break;
				default : //text
					var isMatched = true; //This will fail if row count = 0					
					
					rows.each(function(row, index) {
						if ( !detector.re.test( row.childElements()[sortindex].collectTextNodes() ) ) {
							isMatched = false;
							throw $break;
						}
					}.bind(this));
					
					if ( isMatched ) {
						returnSortType = detector['type'];
						throw $break;
					}
					break;
			}
		}.bind(this));
		return returnSortType;
	},

	sort: function(index) {
		this.adjustSortMarkers(index);
		this.doSort(index);
	},
	
	resort: function() {
		this.doSort(this.sortIndex);
	},
	
	doSort: function(index) {
		var rows = this.body.childElements();
 		
		this.activeSortType = this.getDetector(this.sortIndex);

		if ( this.sortTypes[this.activeSortType] )
	 		rows = rows.sortBy(this.sortTypes[this.activeSortType].bind(this,this.sortIndex));
	 	else
	 		throw "Missing sort type named ["+ this.activeSortType +"]";
		
		//Second column sorting
		if ( this.sortSecondary && index != this.sortSecIndex ) {
			this.activeSecSortType = this.getDetector(this.sortSecIndex);
			
			if ( !this.sortTypes[this.activeSecSortType] )
		 		throw "Missing sort type named ["+ this.activeSecSortType +"]";
			
			var groupNum = 0;
			var groupIndex = 0;
			var lastValue = null;
			var rowGroups = [[]];
			
			//We create each MATCHED original sort column into seperate inner arrays, then sort these inner arrays, in the end the orignal array is replaced with the new flattened one
			rows.each( function(row,index) {
				if ( groupNum == 0 && groupIndex == 0 ) { //First run
					lastValue = this.sortTypes[this.activeSortType](this.sortIndex,row);
					rowGroups[groupNum][groupIndex++] = row;
				} else if ( this.sortTypes[this.activeSortType](this.sortIndex,row) == lastValue ) {
					rowGroups[groupNum][groupIndex++] = row;
				} else {
					//Sort
					
					//Sort the inner array and start to make a new one for next matched sort column, however, no need to expense a sort if inner array has only one item
					if ( groupIndex > 0 )
						rowGroups[groupNum] = rowGroups[groupNum].sortBy(this.sortTypes[this.activeSecSortType].bind(this,this.sortSecIndex));
						
					rowGroups[++groupNum] = [];
					groupIndex = 0; //Reset back to zero to have a secondary 0 based array
					rowGroups[groupNum][groupIndex++] = row;
					lastValue = this.sortTypes[this.activeSortType](this.sortIndex,row);
				}
			}.bind(this));
			
			//Sort last group
			rowGroups[groupNum] = rowGroups[groupNum].sortBy(this.sortTypes[this.activeSecSortType].bind(this,this.sortSecIndex));
						
			//Flatten back to original sorted array
			rows = rowGroups.flatten();
		}
		
		if ('desc' == this.sortOrder)
			rows.reverse();
				
		rows.reverse().each(function(row, index) { 
			if (index > 0)
				this.body.insertBefore(row, rows[index - 1]);
		}.bind(this));
	
		//Change classes of rows to alternate
		rows.reverse().each(function(row, index) {
			row.className = (0 == index % 2 ? 'RowA' : 'RowB');
		});
		
		//console.log("Sort end.");
  }, // sort
    
	addDetector: function (name, regexp, testOn) {
		//his.detectors.push([name,regexp,testOn]);
		this.detectors.unshift({re:regexp,type:name,testOn:testOn});
	}, //addDetector
  
	addSortType: function (name, sortFunction) {
		//this.sortTypes[name] = sortFunction;	
		this.sortTypes[name] = sortFunction;
	}, //addSortType
	
	//compare : function(a,b) {
	//	return a < b ? -1 : a == b ? 0 : 1;
	//} //compare
	
	//Built in detectors
	detectors : $A([
		{re: /.*/, type : "defsort", testOn : "text"}
	]),
	
	sortTypes : {
		defsort : function(sortIndex,row) {
			return row.childElements()[sortIndex].collectTextNodes();
		},
		number : function(a,b) {
			// This will grab the first thing that looks like a number from a string, so you can use it to order a column of various srings containing numbers.
			var calc = function(v) {
				v = parseFloat(v.replace(/^.*?([-+]?[\d]*\.?[\d]+(?:[eE][-+]?[\d]+)?).*$/,"$1"));
				return isNaN(v) ? 0 : v;
			}
			return SortableTable.compare(calc(a),calc(b));
		}
	}

}); // TableSorter


//Format a date just like in PHP
/*
format  character  	Description  	Example returned values
Day 	--- 	---
d 	Day of the month, 2 digits with leading zeros 	01 to 31
*D 	A textual representation of a day, three letters 	Mon through Sun
j 	Day of the month without leading zeros 	1 to 31
*l (lowercase 'L') 	A full textual representation of the day of the week 	Sunday through Saturday
*N 	ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) 	1 (for Monday) through 7 (for Sunday)
*S 	English ordinal suffix for the day of the month, 2 characters 	st, nd, rd or th. Works well with j
*w 	Numeric representation of the day of the week 	0 (for Sunday) through 6 (for Saturday)
*z 	The day of the year (starting from 0) 	0 through 365
Week 	--- 	---
*W 	ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0) 	Example: 42 (the 42nd week in the year)
Month 	--- 	---
F 	A full textual representation of a month, such as January or March 	January through December
m 	Numeric representation of a month, with leading zeros 	01 through 12
M 	A short textual representation of a month, three letters 	Jan through Dec
n 	Numeric representation of a month, without leading zeros 	1 through 12
*t 	Number of days in the given month 	28 through 31
Year 	--- 	---
*L 	Whether it's a leap year 	1 if it is a leap year, 0 otherwise.
*o 	ISO-8601 year number. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0) 	Examples: 1999 or 2003
Y 	A full numeric representation of a year, 4 digits 	Examples: 1999 or 2003
y 	A two digit representation of a year 	Examples: 99 or 03
Time 	--- 	---
a 	Lowercase Ante meridiem and Post meridiem 	am or pm
A 	Uppercase Ante meridiem and Post meridiem 	AM or PM
*B 	Swatch Internet time 	000 through 999
g 	12-hour format of an hour without leading zeros 	1 through 12
G 	24-hour format of an hour without leading zeros 	0 through 23
h 	12-hour format of an hour with leading zeros 	01 through 12
H 	24-hour format of an hour with leading zeros 	00 through 23
i 	Minutes with leading zeros 	00 to 59
s 	Seconds, with leading zeros 	00 through 59
*u 	Milliseconds (added in PHP 5.2.2) 	Example: 54321
Timezone 	--- 	---
*e 	Timezone identifier (added in PHP 5.1.0) 	Examples: UTC, GMT, Atlantic/Azores
*I (capital i) 	Whether or not the date is in daylight saving time 	1 if Daylight Saving Time, 0 otherwise.
*O 	Difference to Greenwich time (GMT) in hours 	Example: +0200
*P 	Difference to Greenwich time (GMT) with colon between hours and minutes (added in PHP 5.1.3) 	Example: +02:00
*T 	Timezone abbreviation 	Examples: EST, MDT ...
*Z 	Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. 	-43200 through 50400
Full Date/Time 	--- 	---
*c 	ISO 8601 date (added in PHP 5) 	2004-02-12T15:19:21+00:00
*r 	» RFC 2822 formatted date 	Example: Thu, 21 Dec 2000 16:01:07 +0200
*U 	Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 	See also time()

* = Not yet implemented
*/
function dateFormat(format, dt) {
	if ( !dt )
		dt = new Date();
	
	var retStr = "";
	var fmtArray = format.toArray();
	
	var tmpDPart;
	var curChar;
	for ( var i=0,  l=fmtArray.length; i < l; i++ ) {
		curChars = "";
		switch(fmtArray[i]) {
			//*** DAY ***
			case "d" : //Day of the month, 2 digits with leading zeros, 	01 to 31
				curChars = dt.getDate();
				if ( curChars < 10 )
					curChars= "0" + curChars;
				break;
			case "j" : //Day of the month without leading zeros 	1 to 31
				curChars = dt.getDate();
				break;
			//*** MONTH ***
			case "F" : //A full textual representation of a month, such as January or March 	January through December
			case "M" : //A short textual representation of a month, three letters 	Jan through Dec
				switch (dt.getMonth()) {
					case 0 :
						curChars = "January"; break;
					case 1 :
						curChars = "February"; break;
					case 2 : 
						curChars = "March"; break;
					case 3 :
						curChars = "April"; break;
					case 4 : 
						curChars = "May"; break;
					case 5 :
						curChars = "June"; break;
					case 6 :
						curChars = "July"; break;
					case 7 :
						rcurChars = "August"; break;
					case 8 :
						curChars = "September"; break;
					case 9 :
						curChars = "October"; break;
					case 10 :
						curChars = "November"; break;
					case 11 :
						curChars = "December"; break;
				}
				if ( fmtArray[i] == "M") //Only take first 3 letters if format is M
					curChars = curChars.substr(0,3);
				break;
			case "m" : //Numeric representation of a month, with leading zeros 	01 through 12, 
				curChars = dt.getMonth() + 1;
				if ( curChars < 10 )
					curChars= "0" + curChars;
				break;
			case "n" : //Numeric representation of a month, without leading zeros 	1 through 12
				curChars = dt.getMonth() + 1;
				break;
			//*** YEAR ***
			case "Y" : //A full numeric representation of a year, 4 digits 	Examples: 1999 or 2003
				curChars = dt.getFullYear();
				break;
			case "y" : //A two digit representation of a year 	Examples: 99 or 03
				curChars = dt.getFullYear().toString(); //force to string, prototype function
				curChars = curChars.substr(2);
				break;
			//*** TIME ***
			case "a" : //Lowercase Ante meridiem and Post meridiem 	am or pm
			case "A" : //Uppercase Ante meridiem and Post meridiem 	AM or PM
				if ( dt.getHours()	< 11 )
					curChars = "am";
				else
					curChars = "pm";
				if ( fmtArray[i] == "A") //Make upper
					curChars = curChars.toUpperCase();
				break;
			case "g" : //12-hour format of an hour without leading zeros 	1 through 12
			case "h" : //12-hour format of an hour with leading zeros 	01 through 12
				if ( dt.getHours() > 12 )
					curChars = (dt.getHours() - 12 );
				else
					curChars = dt.getHours();
				if ( fmtArray[i] == 'h' && curChars < 10)
					curChars= "0" + curChars;
				break;
			case "G" : //24-hour format of an hour without leading zeros 	0 through 23
					curChars = dt.getHours();
				break;
			case "H" : //24-hour format of an hour with leading zeros 	00 through 23
				curChars = dt.getHours();
				if ( curChars < 10)
					curChars= "0" + curChars;				
				break;
			case "i" : //Minutes with leading zeros 	00 to 59
				curChars = dt.getMinutes();
				if ( curChars < 10)
					curChars= "0" + curChars;				
				break;
			case "s" : //Seconds, with leading zeros 	00 through 59
				curChars = dt.getSeconds();
				if ( curChars < 10)
					curChars = "0" + curChars;				
				break;
			case "\\" : //Escape character
				if ( i < l - 1 )
					curChars = fmtArray[i+1];
				i++;
				break;
			default :
				curChars = fmtArray[i];
		}
		retStr += curChars;
	}
	//alert(retStr);
	return retStr;
} //dateFormat

//I changed the script.aculo.us version to REMOVE the background style on exit instead
if ( typeof(Effect) == "object" ) {
	Effect.SDMHighlight = Class.create(Effect.Base, {
		initialize: function(element) {
			this.element = $(element);
			if (!this.element) throw(Effect._elementDoesNotExistError);
			var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
			this.start(options);
		},
		setup: function() {
			// Prevent executing on elements not in the layout flow
			if (this.element.getStyle('display')=='none') { this.cancel(); return; }
			// Disable background image during the effect
			if (!this.options.endcolor)
				this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
			// init color calculations
			this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
			this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
		},
		update: function(position) {
			this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
				return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
		},
		finish: function() {
			 this.element.style.removeProperty('background-color');
		}
	});
}

//Add items that effect the HTML should be here
document.observe("dom:loaded", function() {
	
	//Change all anchor items of class window to open in new window
	$$('.openinwindow').each(function(item) {
	  //winW90H90
	  
	  item.observe('click', function(event){
  		//openWindow(item., name, width, height)
  		
  		var wW = 600;
		  var wH = 400;
		  
  		//Get Height and Width from a special formatted class name
		  $w(item.className).each(function(cn) {
		  	if ( cn.startsWith('ws_') ) {
		  		//ws_W90H90
		  		var winDim = cn.match(/ws_W([0-9]*)H([0-9]*)/);
		  		if ( winDim.length == 3 ) {
		  			//Assume we got a valid item
		  			wW = winDim[1];
		  			wH = winDim[2];
		  		}
		  	}
		  });
		  //alert('W='+wW+' H='+wH);
  		openWindow(item.getAttribute('href'),"test",wW,wH);
  		Event.stop(event);
 		});
	});
	
	//Handle the automatic closing of windows after update
	if ( $('autoexit') ) {
		//$('winautoclose').
		if ( $(window.opener) ) {
			//window.opener.location.reload()
			if ( $('autoexit').hasClassName('autoreload') )
				window.opener.location.reload();
			self.close();
		} else {
			if ( $('autoexit').getAttribute('href') == "#" ) {
				history.back();
			} else
				window.location = $('autoexit').getAttribute('href');
		}
	}
	
	$$('.cancelButton').each(function(item) {
		item.observe('click', function(event){
			if ( $(window.opener) )
				self.close();
			else
				history.back();
			Event.stop(event);
		});
	});
	

});

