/**
 * Paxwax JavaScript library
 * @author Hallvar Helleseth
 * @copyright 2005 (C) Hallvar Helleseth <hallvar@ii.uib.no>
 */

Paxwax = function() {
   info = new Paxwax.ClientInfo();
}

Paxwax.initialized = false;

Paxwax.addModule =function(type) {
   if(Paxwax.initialized) {
	  type.initialized = false;
	  type.init();
	  type.initialized = true;
   }

   if(type.initialized == true) {
	  return;
   }
   
   if ( typeof window.addEventListener != "undefined" ) {
	  window.addEventListener( "load", type.init, false );
	  window.addEventListener( "unload", type.deinit, false );
   } else if ( typeof window.attachEvent != "undefined" ) {
	  window.attachEvent( "onload", type.init );
	  window.attachEvent( "onunload", type.deinit );
   } else {
	  if ( window.onload != null ) {
		 var oldOnload = window.onload;
		 window.onload = function ( e ) {
			oldOnload( e );
			type.init();
		 };
	  } else {
		 window.onload = type.init;
	  }
	  
	  if ( window.onunload != null ) {
		 var oldOnunload = window.onunload;
		 window.onunload = function ( e ) {
			oldOnunload( e );
			type.deinit();
		 };
	  } else {
		 window.onunload = type.deinit;
	  }
   }
}

/**
 * Load a JavaScript file/module
 */
var tries=0;
var call=null;
Paxwax.loadModule = function(path, name, timed, callback) {
   if(callback) {
	  call = callback;
   }
   if(timed) {
	  try {
		 type = eval(name);
		 //module = new type();
		 if(type.init && type.initialized != true) {
			//alert('init '+name);
			type.init();
		 }
		 if(call) {
			call();
			call=null;
		 }
		 return;
	  } catch (ex) {
		 if(!tries) {
			tries = 0;
		 }
		 if(tries++ > 80) {
			/* abort */
			tries=0;
			//alert('failed to load module '+name);
			//document.title = 'failed to load module '+name;
			return;
		 }
			
		 /* try again */
		 setTimeout('Paxwax.loadModule("'+path+'", "'+name+'", true)', 200);
		 return;
	  }
	  
	  return;
   }

   /* Check if already loaded */
   if(name) {
   try {
	  type = eval(name);
	  return;
   } catch(ex) {
   }
   }

   var head = document.getElementsByTagName("head")[0];
   var script = document.createElement("script");
   script.setAttribute('type', 'text/javascript');
   script.setAttribute('src', path);
   head.appendChild(script);
   if(name) {
	  setTimeout('Paxwax.loadModule("'+path+'", "'+name+'", true)', 500);
   }
}

Paxwax.deinit = function() {
}

Paxwax.init = function() {
   if(document.forms.length > 0) {
	  Paxwax.loadModule('/paxwax/scripts/xforms.js', 'XForms');
	  Paxwax.loadModule('/paxwax/scripts/entity_browser.js', 'ObjectBrowser');

	  if(document.body.getNodes("//*[name()='textarea' and contains(@class,'HTMLArea')]").length > 0) {
		 Paxwax.loadModule('/paxwax/scripts/HTMLArea/HTMLArea.js', 'HTMLArea');
	  }

	  if(document.body.getNodes("//*[name()='table' and @class='BalanceSheet']").length > 0) {
		 Paxwax.loadModule('/paxwax/scripts/Calculation/Calculation.js', 'Calculation');
		 
		 Paxwax.loadModule('/paxwax/scripts/BalanceSheet/BalanceSheet.js', 'BalanceSheet');
	  }
   }

   calendars = document.getElementsByName('Calendar');
   if(calendars.length > 0) {
	  Paxwax.loadModule('/paxwax/scripts/EventCalendar/eventcalendar.js', 'EventCalendar');
   }

   /* TODO: quick print fix for USF */
   p = document.body.getNode("//*[@id='left']//*[@class= 'ornament1']");
   if(p) {
	  p.addEventListener('click', function(e) { window.print(); }, false);
   }

   /* Accessablilty */
   //window.addEventListener('keydown', Paxwax.accessability, false);
   
   new Paxwax();
   Paxwax.initialized = true;
}

Paxwax.accessability = function(e) {
}

/**
 *Get information about the browser and computer and its settings
 */
Paxwax.ClientInfo = function() {
   /* Find browser type and version */
   this.is_ie = ( /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent) );
   this.is_ie5 = ( this.is_ie && /msie 5\.0/i.test(navigator.userAgent) );
   this.is_opera = /opera/i.test(navigator.userAgent);
   this.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
   
   /* Check for cookie support */
   
   /* Get screen resolution and window size */
}

/**
 * Ajax library
 */
var ajax;
Ajax = function() {
   this.queue = new Ajax.RequestQueue();
}

Ajax.init = function() {
   /* Create the RequestQueue */
   ajax = new Ajax();
}

Ajax.deinit = function() {
}

Ajax.Request = function(method, id, parameters, onResponse, statusCB) {
   this.method = method;
   this.id = id;
   this.onResponse = onResponse;
   this.statusCB = statusCB;
   this.url ='/paxwax/eventHandler.php?method='+method+'&id='+id+'&'+parameters;
}

/* Send the request with whatever transport is available from the browser */
Ajax.Request.prototype.send = function() {
   var req = new XMLHttpRequest();
   req.open('GET', this.url, true);
   req.send(null);
   if(this.statusCB) {
	  req.onreadystatechange = this.statusCB;
   }
   req.onload = this.onResponse;
   
   return req;
}

/**
 * insert the request into the RequestQueue
 */
Ajax.Request.prototype.queue = function() {
   ajax.queue.push(this);
}

/**
 * Mainly used for event compression
 * If wanted this function can implement different comparisons for different
 * methods.
*/
Ajax.Request.prototype.compare = function(request) {
   if(request.method == this.method
	  && request.id == this.id
	  && request.onResponse == this.onResponse) {
	  return 0;
   }
   
   return -1;
}

/**
 * RequestQueue is a queue of request where the queue is flushed at a certain
 * interval. It has request compression to remove duplicate requests.
 */
Ajax.RequestQueue = function() {
   this.queue = new Array();
   this.stopFlushThread = false;
   this.started = false;
}

/**
 * Insert a new request into the queue
*/
Ajax.RequestQueue.prototype.push = function(request) {
   this.queue.push(request);
   
   /* Start the flush thread if it hasn't started yet or has been stopped */
   if(!this.started) {
	  this.started = true;
	  this.startFlushThread(100);
   }
}

/**
 * Flush the queue, e.g send all request in the queue. Requests that are
 * compared to be identical, only the last request will be sent.
*/
Ajax.RequestQueue.prototype.flush = function() {
   var prev = null;
   while(request = this.queue.pop()) {
	  /* If there is a previous request matching the current one */
	  if(prev && (request.compare(prev) == 0)) {
		 /* Compressed */
		 continue;
	  }
	  prev = request;
	  
	  request.send();
   }
}

/**
 * Start flushing the queue every 'interval' milliseconds
 */
Ajax.RequestQueue.prototype.startFlushThread = function(interval) {
   this.flush();
   
   if(!this.stopFlushThread) {
	  setTimeout('ajax.queue.startFlushThread('+interval+')', interval);
   } else {
	  /* reset stopFlushThread so we can start the thread again if we want*/
	  this.stopFlushThread = false;
   }
}

/**
 * Stop flushing the requests
*/
Ajax.RequestQueue.prototype.stopFlushThread = function() {
   this.stopFlushThread = true;
}

/**
 * Clipboard
 */
Clipboard = function() {
   this.element = null;
}

Clipboard.prototype.isEmpty = function() {
   if(this.element == null) {
	  return true;
   }
   
   return false;
}

Clipboard.prototype.copy = function(el) {
   this.element = el.cloneNode(true);
}

Clipboard.prototype.paste = function(dst) {
   parent = dst.parentNode;
   parent.replaceChild(this.element, dst);
}

Clipboard.prototype.cut = function(el) {
   this.element = el;
}

/**
 * Load clipboard item from server or a cookie?
 */
Clipboard.init = function() {
}

/**
 * upload clipboard item to server or a cookie?
 */
Clipboard.deinit = function() {
}

/**
 * Extend regular DOM Element with some new and useful functions
 * Some parts taken from the Prototype JavaScript library
 */
Object.extend = function(destination, source) {
   for (property in source) {
	  destination[property] = source[property];
   }
   return destination;
}

Object.prototype.extend = function(object) {
   return Object.extend.apply(this, [this, object]);
}

Object.prototype.extend = function(object) {
   for (property in object) {
	  this[property] = object[property]
   }
   return this;
}

Function.prototype.bind = function(object) {
   var __method = this;
   return function() {
	  __method.apply(object, arguments);
   }
}

Node.prototype.extend({
   getX1: function() {
	  if(!this) return 0;
	  x1 = this.offsetLeft;
	  if(this.offsetParent && this.offsetParent != document.body) {
		 x1 = x1 + this.offsetParent.getX1();
	  }
	  return x1;
   },

   getX2: function() {
	  return this.getX1() + this.offsetWidth;
   },

   getY1: function() {
	  return this.offsetTop;
   },

   getY2: function() {
	  return this.offsetHeight + this.offsetTop;
   },

   addClassName: function(className) {
	  if(this.className && this.className.search(className) < 0) {
		 this.className += ' '+className;
	  } else if(!this.className) {
		 this.className = className;
	  }
   },

   removeClassName: function(className) {
	  if (this.className) {
		 var cls = this.className.split(' ');
		 var ar = new Array();
		 for (var i = 0;i <  cls.length; i++) {
			if (cls[i] != className) {
			   ar[ar.length] = cls[i];
			}
		 }
		 this.className = ar.join(' ');
	  }
   },

   getType: function() {
	  if(this.className && this.className.match(/([A-za-z]+)$/)) {
		 return RegExp.$1;
	  }

	  return null;
   },

   getEntity: function() {
	  if(this.id) {
		 str = new String(this.id);
		 if(str.match(/id[0-9]+/)) {
			return this;
		 }
	  }
	  
	  parent = this.parentNode;
	  if(parent && parent.id) {
		 str = new String(parent.id);
		 if(str.match(/id[0-9]+/)) {
			return parent;
		 }
	  }
	  
	  if(parent && parent.parentNode) {
		 return parent.getEntity();
	  }

	  return null;
   },

   getClassName: function() {
	  if(this.id && this.className) {
		 names = this.className.split(" ");
		 name = names[names.length-1];
		 return name;
	  }

	  return null;
   },
   
   getParentType: function() {
	  parent = this.parentNode;
	  if(parent && parent.id) {
		 str = new String(parent.id)
		 if(str.match(/id[0-9]+/)) {
			names = parent.className.split(" ");
			entity = names[names.length-1];
			return entity;
		 }
	  }
	  
	  if(parent && parent.parentNode) {
		 return parent.getParentType();
	  }

	  return null;
   },

   getId: function() {
	  if(this.id && this.id.match(/id([0-9]+)/)) {
		 return RegExp.$1;
	  } else if(this.parentNode) {
		 return this.parentNode.getId();
	  }
	  
	  return null;
   },

   addTooltip: function(text) {
	  this.onmouseover = function(e) {
		 oldtip = document.getElementById('tooltip');
		 if(oldtip) {
			oldtip.parentNode.removeChild(oldtip);
		 }

		 tip = document.createElement('div');
		 tip.id = 'tooltip';

		 tip.style.position = 'absolute';
		 tip.style.zIndex = '9';
		 var x = e.clientX + document.documentElement.scrollLeft;
		 var y = e.clientY + document.documentElement.scrollTop;
		 dx = x + tip.offsetWidth - window.innerWidth + 4;
		 dy = y + tip.offsetHeight - window.innerHeight + 4;
		 if(dx > 0) x -= dx;
		 if(dy > 0) x -= dy;

		 tip.style.top = (y + 14) +'px';
		 tip.style.left = (x + 14) +'px';
		 tip.style.zindex = 99;

		 tip.appendChild(document.createTextNode(text));
		 document.body.appendChild(tip);
		 tip.fadeIn();
	  }

	  this.onmousemove = function(e) {
		 var x = e.clientX + document.documentElement.scrollLeft;
		 var y = e.clientY + document.documentElement.scrollTop;
		 dx = x + tip.offsetWidth - window.innerWidth + 4;
		 dy = y + tip.offsetHeight - window.innerHeight + 4;
		 if(dx > 0) x -= dx;
		 if(dy > 0) x -= dy;

		 tip.style.top = (y + 14) +'px';
		 tip.style.left = (x +14) +'px';
	  }

	  this.onmouseout = function(e) {
		 tip = document.getElementById('tooltip');
		 if(tip) {
			tip = document.body.removeChild(tip);
		 }
	  }
   },

   clearTooltip: function() {
	  tip = document.getElementById('tooltip');
	  if(tip) {
		 tip = document.body.removeChild(tip);
	  }
   },
   
   /**
    * Effects 
	*/
   fadeIn: function() {
	  duration = 0.3;
	  fps = 30;
	  start = 0;
	  end = 0.8;
	  
	  frames = fps * duration;
	  timeout = 1 / fps;
	  change = (end - start) / frames;
	  
	  if(!this.style.MozOpacity) {
		 this.style.MozOpacity = 0;
	  }

	  this.style.MozOpacity = new Number(this.style.MozOpacity) + change;

	  if(new Number(this.style.MozOpacity) < 0.8) { 
		 setTimeout(this.fadeIn.bind(this), timeout * 1000);
	  }
   },
   
   fadeOut: function(start, end, time) {
	  if(!this.style.MozOpacity) {
		 this.style.MozOpacity = start;
	  }

	  element.style.MozOpacity = new Number(this.style.MozOpacity) - 0.1;

	  if(new Number(this.style.MozOpacity) > 0) {
		 setTimeout(this.fadeOut.bind(this), 40);
	  }
   },

   memberOf: function(className) {
	  if(this.className && this.className.indexOf(className) >= 0) {
		 return true;
	  } else if(!this.parentNode) {
		 return false;
	  }

	  return this.parentNode.memberOf(className);
   },

   childOf: function(element) {
	  if(!this.parentNode) {
		 return false;
	  }
	  if(!this.parentNode.id) {
		 return this.parentNode.childOf(element);
	  }
	  if(element && element.id && element.id == this.parentNode.id) {
		 return true;
	  }
	  return false;
   },

   remove: function() {
	  this.parentNode.removeChild(this);
	  
	  if(tooltip = document.getElementById('tooltip')) {
		 tooltip.parentNode.removeChild(tooltip);
	  }
   },

   getNode: function(expr) {
	  nodes = this.getNodes(expr);
	  if(nodes.length >= 1) {
		 return nodes[0];
	  }
   },

   getNodes: function(expr) {
	  var iterator = document.evaluate(expr, this, NSResolver, XPathResult.ANY_TYPE, null);
	  ar = new Array();
	  while(item = iterator.iterateNext()) {
		 ar.push(item);
	  }

	  return ar;
   }
});

/**
 * Behaviour v1.0 by Ben Nolan, June 2005. BSD Licensed by him.
 * More information: http://ripcord.co.nz/behaviour/
 * Modified by Hallvar to use XPath instead of CSS selectors.
 */
var Behaviour = {
   list : new Array,
   ctxNodes : new Array,

   register : function(sheet, ctxNode) {
	  Behaviour.list.push(sheet);
	  Behaviour.ctxNodes.push(ctxNode);
   },

   apply : function () {
	  var sheet;
	  for(var i=0;sheet = Behaviour.list[i];i++) {
		 ctxNode = Behaviour.ctxNodes[i];
		 for(selector in sheet) {
			//alert(ctxNode+' '+selector);
			list = ctxNode.getNodes(selector);
			
			if(!list) {
			   continue;
			}
			
			for(var j=0;element=list[j];j++) {
			   sheet[selector](element, ctxNode);
			}
		 }
	  }
   }
}

NSResolver = function(prefix) {
   if(prefix == 'html') {
	  return 'http://www.w3.org/1999/xhtml';
   } else if(prefix == 'px') {
	  return 'http://paxwax.org/paxml/'
   } else {
	  /* Should never ever happen */
	  return null;
   }
}

Paxwax.addModule(Paxwax);
Paxwax.addModule(Ajax);
Paxwax.addModule(Clipboard);
