/**
* Utility functions
** Copyright (c) 2006, FineTooth
* All rights reserved.
* 
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* 
* *       Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* *       Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* *       Neither the name of the FineTooth nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
**/

// instance
Utility = {};

// disable the context menu for starters
document.oncontextmenu = function() { return false; }


// Handwire junk to be removed
Object.prototype.mixin = function( obj )
{
  for ( var p in obj )
  {
    this.prototype[p] = obj[p];
  }
}



function concat()
{
  var result = [];
  for ( var i = 0; i < arguments.length; i++ )
    for ( var j = 0; j < arguments[i].length; j++ )
      result.push( arguments[i][j] );
  return result;
}
function withoutFirst( sequence )
{
  result = [];
  for ( var i = 1; i < sequence.length; i++ )
    result.push(sequence[i]);
  return result;
}
function cons( element, sequence )
{
  return concat( [element], sequence );
}



// Handwire event handling
addEvent = function(obj, evType, fn, bubble )
{
  bubble = bubble ? true : false;
  if (obj.addEventListener) {
    obj.addEventListener( evType, fn, bubble );
    return true;
  } else if (obj.attachEvent) {
    var r = obj.attachEvent( "on" + evType, fn );
    return r;
  } else {
    return false;
  }
}
removeEvent = function( obj, evType, fn, bubble )
{
  bubble = bubble ? true : false;
  if (obj.removeEventListener) {
    obj.removeEventListener( evType, fn, bubble );
    return true;
  } else if (obj.detachEvent) {
    var r = obj.detachEvent( "on" + evType, fn );
    return r;
  } else {
    return false;
  }
}



// Handwire observer pattern...
Object.prototype.addObserver = function( o )
{
  var observers = this.getObservers();
  observers.push( o );
}
Object.prototype.removeObserver = function( o )
{
  var observers = this.getObservers();
  for ( var i = 0; i < observers.length; i++ )
  {
    if ( observers[i] == o ) observers[i] = undefined;
  }
}
Object.prototype.callObservers = function( strCallbackName )
{
  this.notifyObservers( { callback: strCallbackName, caller: this } );
},
Object.prototype.notifyObservers = function( e )
{
  var observers = this.getObservers();
  for ( var i = 0; i < observers.length; i++ )
  {
    if ( observers[i] != undefined )
    {
      observers[i].notify( e );
    }
  }
}
Object.prototype.getObservers = function()
{
  if ( !this._hshObservers ) this._hshObservers = [];
  return this._hshObservers;
}

Object.prototype.notify = function( e )
{
  if ( e.callback && this[e.callback] && (typeof this[e.callback] == "function") ) this[e.callback]( e );
}
Object.prototype.bindObservers = function( o )
{
  this.addObserver( o );
  o.addObserver( this );
}




/**** Handwire AJAX FUNCTIONS ****/
function xml_request( url, handler )
{
	var req;
	
	req = xml_http_request_object();
  
  if ( req )
  {
    req.open( "GET", url, true );
    req.onreadystatechange = handler;
    req.send( null );
  }

}
function xml_http_request_object()
{
   var req = false;
   
   try {
     req = new ActiveXObject("Msxml2.XMLHTTP");
   } catch (e) {
     try {
       req = new ActiveXObject("Microsoft.XMLHTTP");
     } catch (E) {
       req = false;
     }
   }
 
   if (!req && typeof XMLHttpRequest!='undefined')
   {
     req = new XMLHttpRequest();
   }
 
   return req;
}


/**
 * URL STATIC CLASS 
 * I took out a + "/" + before window.location.pathname and also 
 * added in a conditional on the port thing to make sure it's set, preventing an unnecessary colon.
 * -lsimon
 */
Url = function() {}
Url.Basename = window.location.protocol
                 + "//" + 
  window.location.hostname
  + ((window.location.port && (window.location.port != 80 && window.location.port != 443)) ? ":" + window.location.port : "")
               + window.location.pathname;
Url.Host = window.location.protocol + "//" + window.location.hostname + "/";

// used in Ajax functions
Url.lastPiece = window.location.pathname.match( /\w+\.\w{3}$/ );


/** 
* Below here all added by Lindsey Simon
*/

// Fixes the scroll noticing in prototype
Position.includeScrollOffsets = true;

Object.extend(String.prototype, {
   
      
   trim: function() {
      var s = this;
      while ( s.substring(0,1) == ' ' ) {
         s = s.substring( 1, s.length );
      }
      while ( s.substring( s.length - 1, s.length ) == ' ' ) {
         s = s.substring( 0, s.length - 1 );
      }
      return s;
   },
   
   // our friend from PHP
   addslashes: function() {
      return this.replace( /\'/g, "\\'");
   }
  
});


/**
* Return the mouse position relative to the document - lsimon
* @param e eventobj
*/
function getMouseEventPosition( e ) {
	var pos = {};
	if ( !e ) var e = window.event;
	if ( e.pageX || e.pageY ) {
		pos.x = e.pageX;
		pos.y = e.pageY;
	}
	else if ( e.clientX || e.clientY ) {
		pos.x = e.clientX + document.body.scrollLeft;
		pos.y = e.clientY + document.body.scrollTop;
	}
	return pos;
}



/**
* For some reason prototype doesn't have this
* probably because it's unreliable or gheigh
*/
// added by lsimon
Object.extend(Event, {	
   isRightClick: function(event) {
      return (((event.which) && (event.which == 3)) || ((event.button) && (event.button == 2)));
   }
});



/**
* Since onSuccess called before onComplete, we can test for errors here
* @param {obj} transport
* @param {obj} json
*/
Ajax.onSuccess = function( transport, json ) {
   //alert("Ajax.onSuccess:"+transport+":"+json);
   
   // test JSON
   if ( json && !json.success )
      // if we have an error_code or error_id from FWLoad, then json is an exception object
      if ( json.error_code  || json.error_id ) {
         throw json;
      }
      // otherwise things are aok

   return true;     
}

/**
* onFailure generic Ajax problem
* @param {obj} transport
* @param {obj} json
*/
Ajax.onFailure = function( transport, json) {
   throw "Ajax.onFailure"; 
}

/**
* onException Ajax
* @param {obj} Ajax.Request
* @param {obj} exception from prototype
*/
Ajax.onException = function( request, exception ) {
   //alert("Ajax.onException:" + exception);
   
   if ( exception.error_id ) {
      top.location.href = "?module=error&error_id=" + exception.error_id;
   }
   else {
      // fix for a FF bug that throws an exception if the listener is gone away && exception.name.match( /80040111/ )
      if ( exception && exception.message && exception.message.match( /80040111/ ) )
         return;
      
      // #TODO uncomment once done testing
      //alert("caught exception");return;
      
      var error_code = exception.error_code ? exception.error_code : "2033";
      var error_message = exception.error_message ? escape(exception.error_message) : escape("Exception thrown during Ajax transaction");
      // maybe we have details?
      // or else in IE we don't have a lot
      // in FF we get lineNumber and fileName
      var error_details = exception.error_details ? escape(exception.error_details) : escape( exception.description ? exception.description : exception.message +", on line " + exception.lineNumber + " in file " + exception.fileName );
      var error_backtrace = exception.error_backtrace ? escape(exception.error_backtrace) : escape( "Parameters: " + request.options.parameters + '\nMethod: ' + request.options.method + '\nFFStack:' + exception.stack );
      var error_referrer = exception.error_referrer ? escape(exception.error_referrer) : "index.php";
      
      //for (var piece in exception)
      //   alert(piece);
      //alert( 'exc:'+ exception.message+":"+exception ); return;
      
      // error form
      top.location.href = "?module=error&error_code=" + error_code + "&error_message=" + error_message + "&error_details=" + error_details + "&error_backtrace=" + error_backtrace + "&error_referrer=" + error_referrer;
   }
}

/**
* Simle AJAX requests for json success using prototype
* param {obj} obj
*/
var AjaxRequest = function( obj )
{
   if ( !(obj.parameters) ){
      var exception = {};
      exception.error_code = '2031';
      exception.error_message = escape("AjaxRequest failed to initialize");
      throw exception;
      return;
   }
   
   // we add on a parameter to break cache and so we know we're dealing with AJAX on the serverside
   var random_num = Math.round( ( Math.random() * 666 ) );
   
   // method
   var method = obj.method ? obj.method : "get";
   
   // if not specified url, then use our current url
   var url = obj.url ? obj.url : Url.lastPiece + (method == "get" ? "?" : "");
  
   new Ajax.Request( url, {
      asynchronous: obj.asynchronous ? obj.asynchronous : true,
      method: method,
      parameters: obj.parameters + "&AJAX=Ajax.Request_"+random_num,
      onLoading: obj.onLoading ? obj.onLoading : Prototype.emptyFunction,
      onSuccess: Ajax.onSuccess,
      onComplete: obj.onComplete,
      reload: true,
      onFailure: Ajax.onFailure,
      onException: Ajax.onException
   } );
}
/**
* Ajax updater stuffs innerHTML into a div with server returned xhtml 
* param {obj} obj
*/
var AjaxUpdater = function( element_id, obj )
{
   if ( !obj.parameters ){
      var exception = {};
      exception.error_code = '2032';
      exception.error_message = escape("AjaxUpdater failed to initialize");
      throw exception;
      return;
   }
   
   // we add on a parameter to both break cache and so we know we're dealing with AJAX serverside
   var random_num = Math.round( ( Math.random() * 666 ) );
   
   // determine url and method
   var method = obj.method ? obj.method : "get";
   var url = Url.lastPiece + (method == "get" ? "?" : "");
   
   new Ajax.Updater( element_id, url, {
      asynchronous: obj.asynchronous ? obj.asynchronous : true,
      method: method,
      parameters: obj.parameters + "&AJAX=Ajax.Updater_"+random_num,
      onLoading: obj.onLoading ? obj.onLoading : Prototype.emptyFunction,
      onSuccess: Ajax.onSuccess,
      onComplete: obj.onComplete,
      onFailure: Ajax.onFailure,
      onException: Ajax.onException,
      evalScripts: true
   } );
   
}



// for the toggle plus template
togglePlus = function ( element_id, img )
{
	if ( Element.visible( element_id ) ) {
		Element.hide( element_id );
		img.src = 'weblib/interface_images/plus.gif';
		img.onmouseout =function() { this.src = 'weblib/interface_images/plus.gif' };
		img.onmouseover = function() { this.src = 'weblib/interface_images/plus-active.gif' };
	}
	else {
		Element.show( element_id );
		img.src = 'weblib/interface_images/minus.gif';
		img.onmouseout = function() { this.src = 'weblib/interface_images/minus.gif' };
		img.onmouseover = function() { this.src = 'weblib/interface_images/minus-active.gif' };
	}
}

/**
* hide selects - temporarily disable any selects since their z-index is evil on IE
* @param {string} element_id optional
*/
hideSelects = function( element_id ) {
   if ( element_id )
      var scope = $( element_id );
	var selects = (scope || document).getElementsByTagName( 'select' );
	for (var i=0, n=selects.length; i<n; i++)
		selects[i].style.visibility = "hidden";	
}

/**
* hide selects 
* @param {string} element_id optional
*/
unhideSelects = function( element_id ) {
   
   // conditional for popup
   if ( !element_id && $( 'popupIframeWrapper' ) && $( 'popupIframeWrapper' ).style.display == "block" ) 
      return;
   
   if ( element_id )
      var scope = $( element_id );
	var selects = (scope || document).getElementsByTagName( 'select' );
	for (var i=0, n=selects.length; i<n; i++)
		selects[i].style.visibility = "visible";	
}


/**
* Get an iframe document object - specific to browsers
* @param {string} element_id
* @return {obj} 
*/
getIframeDocument = function( element_id ) {
   var iframe = $( element_id );
   var iframe_document;
   
   // For FF, etc ...
   if ( iframe.contentDocument )
      iframe_document = iframe.contentDocument; 
   // For IE5.5 and IE6
   else if (iframe.contentWindow)
      iframe_document = iframe.contentWindow.document;
   // For IE5
   else if (iframe.document)
      iframe_document = iframe.document;
   return iframe_document;
}




/**
* Dynamically Load a JavaScript file one time into memory
*/
Utility.loadJavaScriptFileOnce = function( filename ) {
   var scripts = document.getElementsByTagName( 'script' );
   var loaded = $A( scripts ).find( function( element ) {
	   return element.src.match( filename );
	});
   if ( loaded ) return;
   
   var script = document.createElement( 'script' );
   script.type = 'text/javascript';
   script.src = filename; 
   document.getElementsByTagName( 'head' )[0].appendChild( script );
}

/**
* My own version of serializing the Sortable
* @param {string} element
*/
Sortable.serializeToString = function(element) {
   element = $(element);
   var options = Object.extend(Sortable.options(element), arguments[1] || {});
   var name = encodeURIComponent(
      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
   
   return Sortable.sequence(element, arguments[1]).map( function(item) {
      return encodeURIComponent(item);
   }).join(',');
}

/**
* debugLog 
* @param {string} txt
*/
Utility.createLogTextArea = function() {
   Utility.logTextArea = document.createElement( 'textarea' );
   Utility.logTextArea.cols = 50;
   Utility.logTextArea.rows = 10;
   Utility.logTextArea.style.position = "absolute";
   Utility.logTextArea.style.zIndex = "9999";
   Utility.logTextArea.style.top = "400px";
   Utility.logTextArea.style.left = "50px";
   Utility.logTextArea.value = "Initialising Log\n";
   document.body.appendChild( Utility.logTextArea );
}

/**
* debugLog 
* @param {string} txt
*/
Utility.debugLog = function( txt ) {
   if ( !Utility.logTextArea ) Utility.createLogTextArea();
   Utility.logTextArea.value = txt + "\n" + Utility.logTextArea.value;
}
