/*
File: JavaScript functions file to be used with the WPNG Calendar plugin for Wordpress
Plugin URI: http://code.google.com/p/wpng-calendar/

----------------------------------------------------------------------------
LICENSE
----------------------------------------------------------------------------
Copyright 2008  - L1 Jockeys  (email : l1jockeys@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
----------------------------------------------------------------------------
*/

$j=jQuery.noConflict();

/* Loads the Google data API */
google.load("gdata", "1");

/* Loads the Google map API */
//google.load("maps", "2");

/* Set global variables to store the dates being processed */
var firstDate = null;
var lastDate = null;
var geocoder = null;
var entryMap = null;

/**
 * Init the Google data JS client library with an error handler 
 */
function init() {
  /* initialize the error handler */
  google.gdata.client.init(handleGDError);
  /* initialize the Geocoder */
  //geocoder = new GClientGeocoder();
}

/**
 * Setup the Google Calendar service
 */
function setupCalendarService() {
  var calService = new google.gdata.calendar.CalendarService('wpng-calendar-plugin-1');
  return calService;
}

/**
 * Wrapper to pull more entries from the calendar for later dates.
 */
 function getLaterEntries() { 
  /* increment the date by one day before incrementing by the weeks interval */
  lastDate.add(1).days();
  var start = lastDate.clone();
  var end   = lastDate.clone().add(weeks).weeks();
  /* call the loadCalendar function with the new dates) */
  loadCalendar(start,end);
 }
 
/**
 * Wrapper to pull more entries from the calendar for older dates.
 */
 function getOlderEntries() { 
  /* decrement the date by one day before decrementing by the weeks interval */
  firstDate.add(-1).days();
  var end   = firstDate.clone();
  var start = firstDate.clone().add(-1 * weeks).weeks();
  /* call the loadCalendar function with the new dates) */
  loadCalendar(start,end);
 }

/**
 * Wrapper to query the calendar for dates from current through a number of weeks
 */  
function loadCalendarByWeeks() {
  /* default to current date */
  var start = Date.today();
  var end   = Date.today().add(weeks).weeks();
  loadCalendar(start, end);
}

/**
 * Uses Google data JS client library to retrieve a calendar entry from the specified
 * URI.
 *
 * @param {string} entryURI is the URI for the specific entry
 */  
function loadCalendarEntry(entryURI) {
  var service = setupCalendarService();
  service.getEventsEntry(entryURI, listEntry, handleGDError);
}
 
/**
 * Uses Google data JS client library to retrieve a calendar feed from the specified
 * URL.  The feed is controlled by several query parameters and a callback 
 * function is called to process the feed results.
 *
 * @param {start}  start date (JavaScript)
 *        {end}    end date   (JavaScript)
 */  
function loadCalendar(start, end) {
  var service = setupCalendarService();
  var query = new google.gdata.calendar.CalendarEventQuery(calendarURL);
  /* general query settings */
  query.setOrderBy('starttime');
  query.setSortOrder('ascending');
  query.setSingleEvents(true);
  /* pageMaxResults is set dynamically from wpng-calendar.php -> addWPNGSettings */
  query.setMaxResults(pageMaxResults);
  /* convert JS dates to Google GData DateTime */
  var startDateTime = new google.gdata.DateTime(start);
  var endDateTime = new google.gdata.DateTime(end);
  query.setMinimumStartTime(startDateTime);
  query.setMaximumStartTime(endDateTime);

  service.getEventsFeed(query, listEvents, handleGDError);
}

/**
 * Uses Google data JS client library to retrieve a calendar feed from the specified
 * URL.  The feed is controlled by several query parameters and a callback 
 * function is called to process the feed results.
 *
 * @param {size}  # of events to list (JavaScript)
 */  
function loadCalendarWidget() {
  var service = setupCalendarService();
  var query = new google.gdata.calendar.CalendarEventQuery(calendarURL);
  /* general query settings */
  query.setOrderBy('starttime');
  query.setSortOrder('ascending');
  query.setSingleEvents(true);
  query.setMaxResults(widgetListSize);
  /* convert JS dates to Google GData DateTime */
  var startDateTime = new google.gdata.DateTime(Date.today());
  var endDateTime = new google.gdata.DateTime(Date.today().add(6).months());
  query.setMinimumStartTime(startDateTime);
  query.setMaximumStartTime(endDateTime);

  service.getEventsFeed(query, listWidgetEvents, handleGDError);
}

/**
 * Callback function for the Google data JS client library to call with a feed 
 * of events retrieved.
 *
 * Creates a list of events in a human-readable form.  This list of
 * events is added into a div called 'wpng-cal-events'.  
 *
 * @param {json} feedRoot is the root of the feed, containing all entries 
 */ 
function listEvents(feedRoot) {
  var entries = feedRoot.feed.getEntries();
  var eventDiv = document.getElementById('wpng-cal-events');
  /* clear out anything in the current events DIV */
  while (eventDiv.firstChild) {
	  eventDiv.removeChild(eventDiv.firstChild);
  }
  /* loop through the events in the feed and output to the DIV */
  var prevDateString = null;
  var len = entries.length;
  /* the list is displayed in a table, let's create it */
  var table = document.createElement('table');
  var tableBody = document.createElement('tbody');
  table.setAttribute('className','wpng-page-list-table');
  table.setAttribute('class','wpng-page-list-table');
  for (var i = 0; i < len; i++) {
	  var entry = entries[i];
	  var times = entry.getTimes();
	  var startTime = times[0].getStartTime();
	  /* note: using cool functions from DateJS for formatting */
	  var displayTime = startTime.getDate().clone();
	  var dateString = null;
	  if (displayTime.clearTime().equals(Date.today())) {
		  dateString = 'Today';
	  }
	  else if (displayTime.clearTime().equals(Date.today().add(1).days())) {
		  dateString = 'Tomorrow';
	  }
	  else {
		  dateString = displayTime.toString('dddd, MMMM d, yyyy');
	  }
	  /* if the date has changed then output a new header row in the table */
	  if (dateString != prevDateString) {
		  var trHead = document.createElement('tr');
		  var tdHead = document.createElement('td');
		  tdHead.setAttribute('className','wpng-page-list-head');
		  tdHead.setAttribute('class','wpng-page-list-head');
		  tdHead.setAttribute('colSpan','2');
		  tdHead.setAttribute('colspan','2');
		  tdHead.appendChild(document.createTextNode(dateString));
		  trHead.appendChild(tdHead);
		  tableBody.appendChild(trHead);
		  prevDateString = dateString;
	  }
	  /* now display the event itself */
	  var timeString = 'All Day Event';
	  /* if the event has a time, override the default text */
	  if (!startTime.isDateOnly()) {
		  timeString = startTime.getDate().toString("h:mm tt")
	  }
	  /* create an anchor to the ThickBox remote call for the title */
	  var title = entry.getTitle().getText();
	  var uri = entry.getSelfLink().getHref();
	  var anchorTitle = document.createElement('a');
	  anchorTitle.setAttribute('href','javascript:loadCalendarEntry("' + uri + '")');
	  anchorTitle.appendChild(document.createTextNode(title));
	  
	  /* add the event time and title to the table */
	  var trEntry = document.createElement('tr');
	  var tdEntryTime = document.createElement('td');	  
	  var tdEntryTitle = document.createElement('td');	  
	  tdEntryTime.setAttribute('className','wpng-page-list-time');
	  tdEntryTime.setAttribute('class','wpng-page-list-time');
	  tdEntryTitle.setAttribute('className','wpng-page-list-title');
	  tdEntryTitle.setAttribute('class','wpng-page-list-title');
	  tdEntryTime.appendChild(document.createTextNode(timeString));
	  tdEntryTitle.appendChild(anchorTitle);
	  trEntry.appendChild(tdEntryTime);
	  trEntry.appendChild(tdEntryTitle);
	  tableBody.appendChild(trEntry);
	  /* get the date from the first / last entry */
	  if (i == 0) {
		  firstDate = displayTime;
	  }
	  else if (i == (len - 1)) {
	          lastDate = displayTime;
	  }
  }
  /* Append the table body to the table */
  table.appendChild(tableBody);
  
  /* if there were some events, add the table */
  if (len != 0) {
	  /* add the table to the event DIV */
	  eventDiv.appendChild(table);
  }
  else {
	  /* show a default message */
	  eventDiv.appendChild(document.createTextNode('No events to show.'));
  }
  	  
  /* at the end of the list, show the navigation links */
  if (showNav) {
	  eventDiv.appendChild(document.createElement('br'));
	  var navTable = document.createElement('table');
	  var navTableBody = document.createElement('tbody');
	  navTable.setAttribute('className','wpng-page-list-table');
	  navTable.setAttribute('class','wpng-page-list-table');
	  var row = document.createElement('tr');
	  
	  var tdOlder = document.createElement('td');
	  var anchorOlder = document.createElement('a');
	  anchorOlder.setAttribute('href','javascript:getOlderEntries()');
	  anchorOlder.appendChild(document.createTextNode('< Show older events'));
	  tdOlder.appendChild(anchorOlder);
	  row.appendChild(tdOlder);
	  
	  var tdLater = document.createElement('td');
	  tdLater.setAttribute('align','right');
	  var anchorLater = document.createElement('a');
	  anchorLater.setAttribute('href','javascript:getLaterEntries()');
	  anchorLater.appendChild(document.createTextNode('Show later events >'));
	  tdLater.appendChild(anchorLater);
	  row.appendChild(tdLater);
	  
	  navTableBody.appendChild(row);
	  navTable.appendChild(navTableBody);
	  eventDiv.appendChild(navTable);
  }
  
  /* Hide the loading image */
  $j("#wpng-cal-load-page").fadeOut("fast");
  
  /* Animate the display of the list */
  $j("#wpng-cal-events").slideDown("slow");
}

/**
 * Callback function for the Google data JS client library to call with a
 * single entry retreived
 *
 * Displays an event in a human-readable form.  This event is ultimately
 * displayed in a Facebox DIV
 *
 * @param {json} retrievedEntryRoot is the root of the entry
 */ 
function listEntry(retrievedEntryRoot) {
  /* get the entry */
  var entry = retrievedEntryRoot.entry;
  
  /* build the entry display for the Thickbox */
  var entryDiv = document.createElement('div');
  entryDiv.setAttribute('id','wpng-tb');
  
  /* get the title */
  var title = document.createElement('h2');
  title.appendChild(document.createTextNode(entry.getTitle().getText()));
  entryDiv.appendChild(title);
  
  /* display the date/time */
  var dateString = 'All Day Event';
  var times = entry.getTimes();
  if (times.length) {
	  /* if the event has a date & time, override the default text */
	  var startTime = times[0].getStartTime();
	  var endTime = times[0].getEndTime();
	  if (!startTime.isDateOnly()) {
		  dateString = startTime.getDate().toString("ddd, MMM d, yyyy h:mm tt");
	  }
	  else {
		  dateString = startTime.getDate().toString("ddd, MMM d, yyyy");
	  }
	  dateString += '   -   ';
	  if (!endTime.isDateOnly()) {
		  dateString += endTime.getDate().toString("ddd, MMM d, yyyy h:mm tt")
	  }
	  else {
		  dateString += endTime.getDate().toString("ddd, MMM d, yyyy");
	  }
  }
  var dateRow = document.createElement('div');
  dateRow.setAttribute('className','wpng-entry-label-row');
  dateRow.setAttribute('class','wpng-entry-label-row');
  
  dateLabel = document.createElement('div');
  dateLabel.appendChild(document.createTextNode('When: '));
  dateLabel.setAttribute('className','wpng-entry-label');
  dateLabel.setAttribute('class','wpng-entry-label');
  dateRow.appendChild(dateLabel);
  
  dateDisplay = document.createElement('div');
  dateDisplay.appendChild(document.createTextNode(dateString));
  dateDisplay.setAttribute('className','wpng-entry-label-text');
  dateDisplay.setAttribute('class','wpng-entry-label-text');
  dateRow.appendChild(dateDisplay);
  
  entryDiv.appendChild(dateRow);
  
  entryDiv.appendChild(document.createElement('br'));
  entryDiv.setAttribute('className','wpng-entry-break');
  entryDiv.setAttribute('class','wpng-entry-break');
  
  /* display the location */
  var locString = 'No location information';
  var locations = entry.getLocations();
  var locTemp = locations[0].getValueString();
  if (locTemp != null) {
	  locString = locTemp
  }
  var locRow = document.createElement('div');
  locRow.setAttribute('className','wpng-entry-label-row');
  locRow.setAttribute('class','wpng-entry-label-row');
  
  locLabel = document.createElement('div');
  locLabel.appendChild(document.createTextNode('Where: '));
  locLabel.setAttribute('className','wpng-entry-label');
  locLabel.setAttribute('class','wpng-entry-label');
  locRow.appendChild(locLabel);
  
  locDisplay = document.createElement('div');
  locDisplay.appendChild(document.createTextNode(locString));
  locDisplay.setAttribute('className','wpng-entry-label-text');
  locDisplay.setAttribute('class','wpng-entry-label-text');
  locRow.appendChild(locDisplay);
  
  entryDiv.appendChild(locRow);
  
  /* add a link to Google map the location, if something was there */
  if (locTemp != null) {
	  /* use browser sniffing to determine if IE or Opera (ugly, but required) */
	  /* thanks to => http://webbugtrack.blogspot.com/2007/10/bug-245-setattribute-style-does-not.html */
	  var isOpera = false;
	  var isIE = false;
	  var agt=navigator.userAgent.toLowerCase();
	  var appVer = navigator.appVersion.toLowerCase();
	  var iePos  = appVer.indexOf('msie');
	  if(typeof(window.opera) != 'undefined'){isOpera = true;}
	  if(!isOpera && iePos !=-1){isIE = true;};
	  
	  var locMapAnchor = document.createElement('a');
	  locMapAnchor.setAttribute('id','wpng-map-link');
	  locMapAnchor.setAttribute('href','http://maps.google.com/maps?hl=en&q=' + locString);
	  locMapAnchor.setAttribute('target','_blank');
	  if(!isIE){
		  /* use the correct DOM Method */
		  locMapAnchor.setAttribute('style','float:right;');
	  } else {
		  /* use the .cssText hack */
		  locMapAnchor.style.setAttribute('cssText', 'float:right;');
	  }
	  locMapAnchor.appendChild(document.createTextNode('Map'));
	  entryDiv.appendChild(locMapAnchor);
  }
  
  entryDiv.appendChild(document.createElement('br'));
  
  /* display the description */
  var descString = 'No description';
  var tempString = entry.getContent().getText();
  if (tempString != null) {
	  descString = tempString;
  }
  var descDisplay = document.createElement('div');
  descDisplay.setAttribute('className','wpng-entry-desc');
  descDisplay.setAttribute('class','wpng-entry-desc');
  // Translate description wikitext into HTML if the option is enabled
  if (parseWiki) {
	  var descHTML = Wiky.toHtml(descString);
	  descDisplay.innerHTML = descHTML;
  }
  else {
	  descDisplay.appendChild(document.createTextNode(descString));
  }
  
  entryDiv.appendChild(descDisplay);
  
  /* add the map div (may get called upon later) */
  /* TODO: Get fancy with it, see showEntryMap below
  var mapDiv = document.createElement('div');
  mapDiv.setAttribute('id','wpng-entry-map');
  mapDiv.setAttribute('style','display:none;height:400px');
  entryDiv.appendChild(mapDiv);
  */
  
  /* add the div to my modified ThickBox function */
  tb_show_inner("",entryDiv.innerHTML,"height=500&width=500");
}

/**
 * Callback function for the Google data JS client library to call with a feed 
 * of events retrieved.
 *
 * Creates a list of events in a human-readable form. This list of
 * events is intended to be shown in a Wordpress sidebar widget
 *
 * @param {json} feedRoot is the root of the feed, containing all entries 
 */ 
function listWidgetEvents(feedRoot) {
  var entries = feedRoot.feed.getEntries();
  var eventDiv = document.getElementById('wpng-cal-widget-events');
  /* clear out anything in the current events DIV */
  while (eventDiv.firstChild) {
	  eventDiv.removeChild(eventDiv.firstChild);
  }
  /* loop through the events in the feed and output to the DIV */
  var prevDateString = null;
  var len = entries.length;
  var ulist = null;
  for (var i = 0; i < len; i++) {
	  var entry = entries[i];
	  var times = entry.getTimes();
	  var startTime = times[0].getStartTime();
	  /* note: using cool functions from DateJS for formatting */
	  var displayTime = startTime.getDate().clone();
	  var dateString = null;
	  if (displayTime.clearTime().equals(Date.today())) {
		  dateString = 'Today';
	  }
	  else if (displayTime.clearTime().equals(Date.today().add(1).days())) {
		  dateString = 'Tomorrow';
	  }
	  else {
		  dateString = displayTime.toString('MMM dd');
	  }
	  /* if the date has changed then output a new title and start a new list */
	  if (dateString != prevDateString) {
		  /* append the list to the event DIV, unless this is the first one */
		  if (ulist != null) {
			  eventDiv.appendChild(ulist);
		  }
		  var titleDiv = document.createElement('div');
		  titleDiv.setAttribute('className','wpng-widget-date-title');
		  titleDiv.setAttribute('class','wpng-widget-date-title');
		  titleDiv.appendChild(document.createTextNode(dateString));
		  eventDiv.appendChild(titleDiv);
		  prevDateString = dateString;
		  /* the events are displayed in an undefined list, let's create it */
		  ulist = document.createElement('ul');
	  }
	  /* now display the event itself as an item in the list */
	  
	  /* create an anchor to the Facebox remote call for the title */
	  var title = entry.getTitle().getText();
	  var uri = entry.getSelfLink().getHref();
	  var anchorTitle = document.createElement('a');
	  anchorTitle.setAttribute('className','thickbox');
	  anchorTitle.setAttribute('class','thickbox');
	  anchorTitle.setAttribute('href','javascript:loadCalendarEntry("' + uri + '")');
	  anchorTitle.appendChild(document.createTextNode(title));
	  
	  /* add the event to the list */
	  var item = document.createElement('li');
	  item.appendChild(anchorTitle);
	  ulist.appendChild(item);
  }
  
  /* append the last list to the event DIV */
  if (ulist != null) {
	  eventDiv.appendChild(ulist);
  }
  
  /* if there were not any events, display a default message */
  if (len == 0) {
	  /* show a default message */
	  eventDiv.appendChild(document.createTextNode('No events to show.'));
  }
  
  /* Hide the loading image */
  $j("#wpng-cal-load-widget").fadeOut("fast");
  
  /* Animate the display of the list */
  $j("#wpng-cal-widget-events").slideDown("slow");
  
}

/**
 * Show a map using the description information using the Google Map API
 * TODO: Add a AJAX grabbed map to the Facebox using this function
 */
 
/*
function showEntryMap(address) { 
  entryMap = new google.maps.Map2(document.getElementById("wpng-entry-map"));
  geocoder.getLatLng(
    address,
    function(point) {
      if (!point) {
        alert(address + " not found");
      } else {
        entryMap.setCenter(point, 13);
        var marker = new GMarker(point);
        entryMap.addOverlay(marker);
        marker.openInfoWindowHtml(address);
      }
    }
  );
  var test = jQuery('#wpng-entry-map').slideDown();
}
*/

/**
 * Callback function for the Google data JS client library to call when an error
 * occurs during the retrieval of the feed.  Details available depend partly
 * on the web browser, but this shows a few basic examples. In the case of
 * a privileged environment using ClientLogin authentication, there may also
 * be an e.type attribute in some cases.
 *
 * @param {Error} e is an instance of an Error 
 */
function handleGDError(e) {
  document.getElementById('jsSourceFinal').setAttribute('style', 
      'display:none');
  if (e instanceof Error) {
    /* alert with the error line number, file and message */
    alert('Error at line ' + e.lineNumber +
          ' in ' + e.fileName + '\n' +
          'Message: ' + e.message);
    /* if available, output HTTP error code and status text */
    if (e.cause) {
      var status = e.cause.status;
      var statusText = e.cause.statusText;
      alert('Root cause: HTTP error ' + status + ' with status text of: ' + 
            statusText);
    }
  } else {
    alert(e.toString());
  }
}

/* initialize the Google error handler */
google.setOnLoadCallback(init);

//console.log("Initialized");

// -------------------------------------------------------------------
// DHTML Window Widget- By Dynamic Drive, available at: http://www.dynamicdrive.com
// v1.0: Script created Feb 15th, 07'
// v1.01: Feb 21th, 07' (see changelog.txt)
// v1.02: March 26th, 07' (see changelog.txt)
// v1.03: May 5th, 07' (see changelog.txt)
// -------------------------------------------------------------------

var dhtmlwindow={
imagefiles:['/wp-content/themes/options/images/min.gif', '/wp-content/themes/options/images/close.gif', '/wp-content/themes/options/images/restore.gif', '/wp-content/themes/options/images/resize.gif'], //Path to 4 images used by script, in that order
ajaxbustcache: true, //Bust caching when fetching a file via Ajax?
ajaxloadinghtml: '<b>Loading Page. Please wait...</b>', //HTML to show while window fetches Ajax Content?

minimizeorder: 0,
zIndexvalue:100,
tobjects: [], //object to contain references to dhtml window divs, for cleanup purposes
lastactivet: {}, //reference to last active DHTML window

init:function(t){
	var domwindow=document.createElement("div") //create dhtml window div
	domwindow.id=t
	domwindow.className="dhtmlwindow"
	var domwindowdata=''
	domwindowdata='<div class="drag-handle">'
	domwindowdata+='DHTML Window <div class="drag-controls"><img src="'+this.imagefiles[0]+'" title="Minimize" /><img src="'+this.imagefiles[1]+'" title="Close" /></div>'
	domwindowdata+='</div>'
	domwindowdata+='<div class="drag-contentarea"></div>'
	domwindowdata+='<div class="drag-statusarea"><div class="drag-resizearea" style="background: transparent url('+this.imagefiles[3]+') top right no-repeat;">&nbsp;</div></div>'
	domwindowdata+='</div>'
	domwindow.innerHTML=domwindowdata
	document.getElementById("dhtmlwindowholder").appendChild(domwindow)
	//this.zIndexvalue=(this.zIndexvalue)? this.zIndexvalue+1 : 100 //z-index value for DHTML window: starts at 0, increments whenever a window has focus
	var t=document.getElementById(t)
	var divs=t.getElementsByTagName("div")
	for (var i=0; i<divs.length; i++){ //go through divs inside dhtml window and extract all those with class="drag-" prefix
		if (/drag-/.test(divs[i].className))
			t[divs[i].className.replace(/drag-/, "")]=divs[i] //take out the "drag-" prefix for shorter access by name
	}
	//t.style.zIndex=this.zIndexvalue //set z-index of this dhtml window
	t.handle._parent=t //store back reference to dhtml window
	t.resizearea._parent=t //same
	t.controls._parent=t //same
	t.onclose=function(){return true} //custom event handler "onclose"
	t.onmousedown=function(){dhtmlwindow.setfocus(this)} //Increase z-index of window when focus is on it
	t.handle.onmousedown=dhtmlwindow.setupdrag //set up drag behavior when mouse down on handle div
	t.resizearea.onmousedown=dhtmlwindow.setupdrag //set up drag behavior when mouse down on resize div
	t.controls.onclick=dhtmlwindow.enablecontrols
	t.show=function(){dhtmlwindow.show(this)} //public function for showing dhtml window
	t.hide=function(){dhtmlwindow.hide(this)} //public function for hiding dhtml window
	t.close=function(){dhtmlwindow.close(this)} //public function for closing dhtml window (also empties DHTML window content)
	t.setSize=function(w, h){dhtmlwindow.setSize(this, w, h)} //public function for setting window dimensions
	t.moveTo=function(x, y){dhtmlwindow.moveTo(this, x, y)} //public function for moving dhtml window (relative to viewpoint)
	t.isResize=function(bol){dhtmlwindow.isResize(this, bol)} //public function for specifying if window is resizable
	t.isScrolling=function(bol){dhtmlwindow.isScrolling(this, bol)} //public function for specifying if window content contains scrollbars
	t.load=function(contenttype, contentsource, title){dhtmlwindow.load(this, contenttype, contentsource, title)} //public function for loading content into window
	this.tobjects[this.tobjects.length]=t
	return t //return reference to dhtml window div
},

open:function(t, contenttype, contentsource, title, attr, recalonload){
	var d=dhtmlwindow //reference dhtml window object
	function getValue(Name){
		var config=new RegExp(Name+"=([^,]+)", "i") //get name/value config pair (ie: width=400px,)
		return (config.test(attr))? parseInt(RegExp.$1) : 0 //return value portion (int), or 0 (false) if none found
	}
	if (document.getElementById(t)==null) //if window doesn't exist yet, create it
		t=this.init(t) //return reference to dhtml window div
	else
		t=document.getElementById(t)
	this.setfocus(t)
	t.setSize(getValue(("width")), (getValue("height"))) //Set dimensions of window
	var xpos=getValue("center")? "middle" : getValue("left") //Get x coord of window
	var ypos=getValue("center")? "middle" : getValue("top") //Get y coord of window
	//t.moveTo(xpos, ypos) //Position window
	if (typeof recalonload!="undefined" && recalonload=="recal" && this.scroll_top==0){ //reposition window when page fully loads with updated window viewpoints?
		if (window.attachEvent && !window.opera) //In IE, add another 400 milisecs on page load (viewpoint properties may return 0 b4 then)
			this.addEvent(window, function(){setTimeout(function(){t.moveTo(xpos, ypos)}, 400)}, "load")
		else
			this.addEvent(window, function(){t.moveTo(xpos, ypos)}, "load")
	}
	t.isResize(getValue("resize")) //Set whether window is resizable
	t.isScrolling(getValue("scrolling")) //Set whether window should contain scrollbars
	t.style.visibility="visible"
	t.style.display="block"
	t.contentarea.style.display="block"
	t.moveTo(xpos, ypos) //Position window
	t.load(contenttype, contentsource, title)
	if (t.state=="minimized" && t.controls.firstChild.title=="Restore"){ //If window exists and is currently minimized?
		t.controls.firstChild.setAttribute("src", dhtmlwindow.imagefiles[0]) //Change "restore" icon within window interface to "minimize" icon
		t.controls.firstChild.setAttribute("title", "Minimize")
		t.state="fullview" //indicate the state of the window as being "fullview"
	}
	return t
},

setSize:function(t, w, h){ //set window size (min is 150px wide by 100px tall)
	t.style.width=Math.max(parseInt(w), 150)+"px"
	t.contentarea.style.height=Math.max(parseInt(h), 100)+"px"
},

moveTo:function(t, x, y){ //move window. Position includes current viewpoint of document
	this.getviewpoint() //Get current viewpoint numbers
	t.style.left=(x=="middle")? this.scroll_left+(this.docwidth-t.offsetWidth)/2+"px" : this.scroll_left+parseInt(x)+"px"
	t.style.top=(y=="middle")? this.scroll_top+(this.docheight-t.offsetHeight)/2+"px" : this.scroll_top+parseInt(y)+"px"
},

isResize:function(t, bol){ //show or hide resize inteface (part of the status bar)
	t.statusarea.style.display=(bol)? "block" : "none"
	t.resizeBool=(bol)? 1 : 0
},

isScrolling:function(t, bol){ //set whether loaded content contains scrollbars
	t.contentarea.style.overflow=(bol)? "auto" : "hidden"
},

load:function(t, contenttype, contentsource, title){ //loads content into window plus set its title (3 content types: "inline", "iframe", or "ajax")
	if (t.isClosed){
		alert("DHTML Window has been closed, so no window to load contents into. Open/Create the window again.")
		return
	}
	var contenttype=contenttype.toLowerCase() //convert string to lower case
	if (typeof title!="undefined")
		t.handle.firstChild.nodeValue=title
	if (contenttype=="inline")
		t.contentarea.innerHTML=contentsource
	else if (contenttype=="div"){
		var inlinedivref=document.getElementById(contentsource)
		t.contentarea.innerHTML=(inlinedivref.defaultHTML || inlinedivref.innerHTML) //Populate window with contents of inline div on page
		if (!inlinedivref.defaultHTML)
			inlinedivref.defaultHTML=inlinedivref.innerHTML //save HTML within inline DIV
		inlinedivref.innerHTML="" //then, remove HTML within inline DIV (to prevent duplicate IDs, NAME attributes etc in contents of DHTML window
		inlinedivref.style.display="none" //hide that div
	}
	else if (contenttype=="iframe"){
		t.contentarea.style.overflow="hidden" //disable window scrollbars, as iframe already contains scrollbars
		if (!t.contentarea.firstChild || t.contentarea.firstChild.tagName!="IFRAME") //If iframe tag doesn't exist already, create it first
			t.contentarea.innerHTML='<iframe src="" style="margin:0; padding:0; width:100%; height: 100%" name="_iframe-'+t.id+'"></iframe>'
		window.frames["_iframe-"+t.id].location.replace(contentsource) //set location of iframe window to specified URL
		}
	else if (contenttype=="ajax"){
		this.ajax_connect(contentsource, t) //populate window with external contents fetched via Ajax
	}
	t.contentarea.datatype=contenttype //store contenttype of current window for future reference
},

setupdrag:function(e){
	var d=dhtmlwindow //reference dhtml window object
	var t=this._parent //reference dhtml window div
	d.etarget=this //remember div mouse is currently held down on ("handle" or "resize" div)
	var e=window.event || e
	d.initmousex=e.clientX //store x position of mouse onmousedown
	d.initmousey=e.clientY
	d.initx=parseInt(t.offsetLeft) //store offset x of window div onmousedown
	d.inity=parseInt(t.offsetTop)
	d.width=parseInt(t.offsetWidth) //store width of window div
	d.contentheight=parseInt(t.contentarea.offsetHeight) //store height of window div's content div
	if (t.contentarea.datatype=="iframe"){ //if content of this window div is "iframe"
		t.style.backgroundColor="#F8F8F8" //colorize and hide content div (while window is being dragged)
		t.contentarea.style.visibility="hidden"
	}
	document.onmousemove=d.getdistance //get distance travelled by mouse as it moves
	document.onmouseup=function(){
		if (t.contentarea.datatype=="iframe"){ //restore color and visibility of content div onmouseup
			t.contentarea.style.backgroundColor="white"
			t.contentarea.style.visibility="visible"
		}
		d.stop()
	}
	return false
},

getdistance:function(e){
	var d=dhtmlwindow
	var etarget=d.etarget
	var e=window.event || e
	d.distancex=e.clientX-d.initmousex //horizontal distance travelled relative to starting point
	d.distancey=e.clientY-d.initmousey
	if (etarget.className=="drag-handle") //if target element is "handle" div
		d.move(etarget._parent, e)
	else if (etarget.className=="drag-resizearea") //if target element is "resize" div
		d.resize(etarget._parent, e)
	return false //cancel default dragging behavior
},

getviewpoint:function(){ //get window viewpoint numbers
	var ie=document.all && !window.opera
	var domclientWidth=document.documentElement && parseInt(document.documentElement.clientWidth) || 100000 //Preliminary doc width in non IE browsers
	this.standardbody=(document.compatMode=="CSS1Compat")? document.documentElement : document.body //create reference to common "body" across doctypes
	this.scroll_top=(ie)? this.standardbody.scrollTop : window.pageYOffset
	this.scroll_left=(ie)? this.standardbody.scrollLeft : window.pageXOffset
	this.docwidth=(ie)? this.standardbody.clientWidth : (/Safari/i.test(navigator.userAgent))? window.innerWidth : Math.min(domclientWidth, window.innerWidth-16)
	this.docheight=(ie)? this.standardbody.clientHeight: window.innerHeight
},

rememberattrs:function(t){ //remember certain attributes of the window when it's minimized or closed, such as dimensions, position on page
	this.getviewpoint() //Get current window viewpoint numbers
	t.lastx=parseInt((t.style.left || t.offsetLeft))-dhtmlwindow.scroll_left //store last known x coord of window just before minimizing
	t.lasty=parseInt((t.style.top || t.offsetTop))-dhtmlwindow.scroll_top
	t.lastwidth=parseInt(t.style.width) //store last known width of window just before minimizing/ closing
},

move:function(t, e){
	t.style.left=dhtmlwindow.distancex+dhtmlwindow.initx+"px"
	t.style.top=dhtmlwindow.distancey+dhtmlwindow.inity+"px"
},

resize:function(t, e){
	t.style.width=Math.max(dhtmlwindow.width+dhtmlwindow.distancex, 150)+"px"
	t.contentarea.style.height=Math.max(dhtmlwindow.contentheight+dhtmlwindow.distancey, 100)+"px"
},

enablecontrols:function(e){
	var d=dhtmlwindow
	var sourceobj=window.event? window.event.srcElement : e.target //Get element within "handle" div mouse is currently on (the controls)
	if (/Minimize/i.test(sourceobj.getAttribute("title"))) //if this is the "minimize" control
		d.minimize(sourceobj, this._parent)
	else if (/Restore/i.test(sourceobj.getAttribute("title"))) //if this is the "restore" control
		d.restore(sourceobj, this._parent)
	else if (/Close/i.test(sourceobj.getAttribute("title"))) //if this is the "close" control
		d.close(this._parent)
	return false
},

minimize:function(button, t){
	dhtmlwindow.rememberattrs(t)
	button.setAttribute("src", dhtmlwindow.imagefiles[2])
	button.setAttribute("title", "Restore")
	t.state="minimized" //indicate the state of the window as being "minimized"
	t.contentarea.style.display="none"
	t.statusarea.style.display="none"
	if (typeof t.minimizeorder=="undefined"){ //stack order of minmized window on screen relative to any other minimized windows
		dhtmlwindow.minimizeorder++ //increment order
		t.minimizeorder=dhtmlwindow.minimizeorder
	}
	t.style.left="10px" //left coord of minmized window
	t.style.width="200px"
	var windowspacing=t.minimizeorder*10 //spacing (gap) between each minmized window(s)
	t.style.top=dhtmlwindow.scroll_top+dhtmlwindow.docheight-(t.handle.offsetHeight*t.minimizeorder)-windowspacing+"px"
},

restore:function(button, t){
	dhtmlwindow.getviewpoint()
	button.setAttribute("src", dhtmlwindow.imagefiles[0])
	button.setAttribute("title", "Minimize")
	t.state="fullview" //indicate the state of the window as being "fullview"
	t.style.display="block"
	t.contentarea.style.display="block"
	if (t.resizeBool) //if this window is resizable, enable the resize icon
		t.statusarea.style.display="block"
	t.style.left=parseInt(t.lastx)+dhtmlwindow.scroll_left+"px" //position window to last known x coord just before minimizing
	t.style.top=parseInt(t.lasty)+dhtmlwindow.scroll_top+"px"
	t.style.width=parseInt(t.lastwidth)+"px"
},


close:function(t){
	try{
		var closewinbol=t.onclose()
	}
	catch(err){ //In non IE browsers, all errors are caught, so just run the below
		var closewinbol=true
 }
	finally{ //In IE, not all errors are caught, so check if variable isn't defined in IE in those cases
		if (typeof closewinbol=="undefined"){
			alert("An error has occured somwhere inside your \"onclose\" event handler")
			var closewinbol=true
		}
	}
	if (closewinbol){ //if custom event handler function returns true
		if (t.state!="minimized") //if this window isn't currently minimized
			dhtmlwindow.rememberattrs(t) //remember window's dimensions/position on the page before closing
		if (window.frames["_iframe-"+t.id]) //if this is an IFRAME DHTML window
			window.frames["_iframe-"+t.id].location.replace("about:blank")
		else
			t.contentarea.innerHTML=""
		t.style.display="none"
		t.isClosed=true //tell script this window is closed (for detection in t.show())
	}
	return closewinbol
},


setopacity:function(targetobject, value){ //Sets the opacity of targetobject based on the passed in value setting (0 to 1 and in between)
	if (!targetobject)
		return
	if (targetobject.filters && targetobject.filters[0]){ //IE syntax
		if (typeof targetobject.filters[0].opacity=="number") //IE6
			targetobject.filters[0].opacity=value*100
		else //IE 5.5
			targetobject.style.filter="alpha(opacity="+value*100+")"
		}
	else if (typeof targetobject.style.MozOpacity!="undefined") //Old Mozilla syntax
		targetobject.style.MozOpacity=value
	else if (typeof targetobject.style.opacity!="undefined") //Standard opacity syntax
		targetobject.style.opacity=value
},

setfocus:function(t){ //Sets focus to the currently active window
	this.zIndexvalue++
	t.style.zIndex=this.zIndexvalue
	t.isClosed=false //tell script this window isn't closed (for detection in t.show())
	this.setopacity(this.lastactivet.handle, 0.5) //unfocus last active window
	this.setopacity(t.handle, 1) //focus currently active window
	this.lastactivet=t //remember last active window
},


show:function(t){
	if (t.isClosed){
		alert("DHTML Window has been closed, so nothing to show. Open/Create the window again.")
		return
	}
	if (t.lastx) //If there exists previously stored information such as last x position on window attributes (meaning it's been minimized or closed)
		dhtmlwindow.restore(t.controls.firstChild, t) //restore the window using that info
	else
		t.style.display="block"
	this.setfocus(t)
	t.state="fullview" //indicate the state of the window as being "fullview"
},

hide:function(t){
	t.style.display="none"
},

ajax_connect:function(url, t){
	var page_request = false
	var bustcacheparameter=""
	if (window.XMLHttpRequest) // if Mozilla, IE7, Safari etc
		page_request = new XMLHttpRequest()
	else if (window.ActiveXObject){ // if IE6 or below
		try {
		page_request = new ActiveXObject("Msxml2.XMLHTTP")
		} 
		catch (e){
			try{
			page_request = new ActiveXObject("Microsoft.XMLHTTP")
			}
			catch (e){}
		}
	}
	else
		return false
	t.contentarea.innerHTML=this.ajaxloadinghtml
	page_request.onreadystatechange=function(){dhtmlwindow.ajax_loadpage(page_request, t)}
	if (this.ajaxbustcache) //if bust caching of external page
		bustcacheparameter=(url.indexOf("?")!=-1)? "&"+new Date().getTime() : "?"+new Date().getTime()
	page_request.open('GET', url+bustcacheparameter, true)
	page_request.send(null)
},

ajax_loadpage:function(page_request, t){
	if (page_request.readyState == 4 && (page_request.status==200 || window.location.href.indexOf("http")==-1)){
	t.contentarea.innerHTML=page_request.responseText
	}
},


stop:function(){
	dhtmlwindow.etarget=null //clean up
	document.onmousemove=null
	document.onmouseup=null
},

addEvent:function(target, functionref, tasktype){ //assign a function to execute to an event handler (ie: onunload)
	var tasktype=(window.addEventListener)? tasktype : "on"+tasktype
	if (target.addEventListener)
		target.addEventListener(tasktype, functionref, false)
	else if (target.attachEvent)
		target.attachEvent(tasktype, functionref)
},

cleanup:function(){
	for (var i=0; i<dhtmlwindow.tobjects.length; i++){
		dhtmlwindow.tobjects[i].handle._parent=dhtmlwindow.tobjects[i].resizearea._parent=dhtmlwindow.tobjects[i].controls._parent=null
	}
	window.onload=null
}

} //End dhtmlwindow object

document.write('<div id="dhtmlwindowholder"><span style="display:none">.</span></div>') //container that holds all dhtml window divs on page
window.onunload=dhtmlwindow.cleanup