// These are a set of utilitarian javascript functions for providing various window and form functionality
// In order to remain independent of the prototype library, lovely though it is, we're creating the class
// instances here in the form of two variables: windowHandler and formHandler.
// NoneTheLess, if prototype is loaded, then two classes are also created, by the names of WindowHandler and FormHandler
// which can be used in various instance creations
// 

var navHandler = {
  // Return the parameter in a given URL - if no url is given, it uses the current window location
  getParam: function( name, url )
  {
    name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
    var regexS = "[\\?&]"+name+"=([^&#]*)";
    var regex = new RegExp( regexS );
    var results = regex.exec( url || window.location.href );
    if( results == null )
      return "";
    else
      return results[1];
  }  
}

// Handler for managing windows
var windowHandler = {
  // Return the window size
  windowSize: function() {
    var myWidth = 0, myHeight = 0;
    if( typeof( window.innerWidth ) == 'number' ) {
      //Non-IE
      myWidth = window.innerWidth;
      myHeight = window.innerHeight;
    } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
      //IE 6+ in 'standards compliant mode'
      myWidth = document.documentElement.clientWidth;
      myHeight = document.documentElement.clientHeight;
    } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
      //IE 4 compatible
      myWidth = document.body.clientWidth;
      myHeight = document.body.clientHeight;
    }
  
    // rect = new Object ; 
    // rect.width = myWidth;
    // rect.height = myHeight;
    // return it as an array
    return  {width: myWidth, height: myHeight};
  },

  scrollXY: function() {
    var scrOfX = 0, scrOfY = 0;
    if( typeof( window.pageYOffset ) == 'number' ) {
      //Netscape compliant
      scrOfY = window.pageYOffset;
      scrOfX = window.pageXOffset;
    } else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
      //DOM compliant
      scrOfY = document.body.scrollTop;
      scrOfX = document.body.scrollLeft;
    } else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
      //IE6 standards compliant mode
      scrOfY = document.documentElement.scrollTop;
      scrOfX = document.documentElement.scrollLeft;
    }
    return { xpos: scrOfX, ypos: scrOfY };
  },
  
  windowCoordinates: function() {
    var scroll = this.scrollXY();
    var windowSize = this.windowSize();
    return {left: scroll.xpos, right:windowSize.width + scroll.xpos, top:  scroll.ypos, bottom: windowSize.height + scroll.ypos}
  },
  
  documentDimensions: function() {
    var docwidth,docheight;
    if (window.innerWidth || window.innerHeight){ 
      docwidth = window.innerWidth; 
      docheight = window.innerHeight; 
    } 
    //IE Mozilla 
    if (document.body.clientWidth || document.body.clientHeight){ 
      docwidth = document.body.clientWidth; 
      docheight = document.body.clientHeight; 
    }
    return {width: docwidth,height:docheight}
  },

// Return the mouse click location
  clickPoint: function(e) {
    var posx = 0;
    var posy = 0;
    if (!e) var e = window.event;
    if (e.pageX || e.pageY)
    {
      posx = e.pageX;
      posy = e.pageY;
    }
    else if (e.clientX || e.clientY)
    {
      posx = e.clientX + document.body.scrollLeft;
      posy = e.clientY + document.body.scrollTop;
    }
    return { xpos: posx,ypos: posy};
  },

  // Make sure the indicated object does not extend beyond the window
  fitToWindow: function(objectId,padding) {
    // objectId = objectId.id
    if (!padding) padding = 20;

    var width = parseInt($(objectId).offsetWidth);
    var height = parseInt($(objectId).offsetHeight);
    var xpos =  parseInt($(objectId).style.left);
    var ypos =  parseInt($(objectId).style.top);

    w = this.windowCoordinates();

    // console.log ("point (x,y)=("+xpos+","+ypos+"), width="+width + ',height=' + height + ', wind left=' + w.left + ', right=' + w.right + ', top=' + w.top + ', bottom=' + w.bottom + ", right edge=" + (xpos+width+padding) + ", bottom="+(ypos + height + padding));
    if ( xpos + width + padding > w.right ) $(objectId).style.left = w.right - width - padding + 'px';
    if ( xpos < w.left ) $(objectId).style.left = w.left + padding + 'px';
    if ( ypos + height + padding > w.bottom ) $(objectId).style.top = w.bottom - height - padding + 'px';
    if ( ypos < w.top ) $(objectId).style.top = w.top + padding + 'px';
  },

  centerInWindow: function(objectId) {
    var width = parseInt($(objectId).offsetWidth);
    var height = parseInt($(objectId).offsetHeight);  
    var w = this.windowSize();
    var xoffset = parseInt((w.width - width)/2) + this.scrollXY().xpos;
    var yoffset = parseInt((w.height - height)/2) + this.scrollXY().ypos;
    if ( xoffset > 0 ) {
      $(objectId).style.left = xoffset + 'px';
    }
    if ( yoffset > 0 ) {
      $(objectId).style.top = yoffset + 'px';
    }
    // console.log("object info("+width+','+height+')'+"xoffset="+xoffset + "yoffset=" + yoffset + "window info("+w.width+','+w.height+')')
  },

  // Return the element's absolute offset relative to the page
  elementAbsoluteOffsets: function(element) {
    element = $(element);
    var left = 0;
    var top = 0;
    do {
      left += element.offsetLeft;
      top += element.offsetTop;
    } while (element = element.offsetParent);
    return {xpos: left, ypos: top}
  },
  
  getParentOffset: function(element) {
    element = $(element);

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var offset = Position.cumulativeOffset(element.offsetParent || document.body);
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {xpos: offset[0], ypos: offset[1]};
  },
  

  showUrlHere: function(url,objectId,event,options) {
    new Ajax.Request( url,
    {
      method: 'get',
      evalScripts: true,
      onSuccess: function(transport) { 
        $(objectId).innerHTML = transport.responseText;
        this.showHere(objectId,event,options);
       }.bind(this)
    });    
  },

  // This positions an object at a given point (defined as {xpos: value, ypos:value} where
  // values are in pixels)
  // The options is a hash that has the following elements:
  // placement: { xpos: 'left|right|center', ypos:'top|bottom|center' } defaulting to { xpos: left, ypos: top}
  // offset: offset from mouse position in pixels
  // NOTE: this does not change the display settings for the element
  position: function(objectId,point,options) {
    var m = $(objectId);
    dimensions = Element.getDimensions(m);
    // console.log("point="+point.xpos + ','+point.ypos)
    if ( !options ) options = { placement: null, offset: null} 
    if ( !options.offset )
      options.offset = { xpos: 0, ypos: 0 }

    point.xpos += options.offset.xpos
    point.ypos += options.offset.ypos
    // console.log("point.xpos="+point.xpos);
    if ( !options.placement ) 
      options.placement = { xpos: 'left', ypos: 'top' }
      
    if ( options.placement.xpos == 'left')
      m.style.left = point.xpos + 'px';
    else if ( options.placement.xpos == 'right')
      m.style.left = point.xpos - dimensions.width + 'px';
    else 
      m.style.left = point.xpos - parseInt(dimensions.width/2) + 'px';

    if ( options.placement.ypos == 'top' )
      m.style.top = point.ypos + 'px';
    else if ( options.placement.ypos == 'bottom')
      m.style.top = point.ypos - dimensions.height + 'px';
    else 
      m.style.top = point.ypos - parseInt(dimensions.height/2) + 'px';   
  },
  
  positionRelativeToPoint: function(objectId,point,options) {
    if (!options) options = {};
    if (options.fitToWindow == null) options.fitToWindow = true;
    var m = $(objectId);
    this.position(m,point,options);
    if ( options && options.removeOnClick ) 
      this.manageEventHandling();
    m.style.display='block';
    if (options.fitToWindow) this.fitToWindow(objectId);       
    this.showingElement = m;
  },
  
  // Position an object relative to another objet
  // Note that the position property for the positioned object really 
  // should be "absolute"
  positionRelativeToElement: function(objectId,anchorId,options) {
    var point = this.elementAbsoluteOffsets(anchorId);
    this.positionRelativeToPoint(objectId,point,options);
  },
  
  // The objectId is the ID for the element that we are placing
  // The event is the mouse event
  // The options is a hash that has the following elements:
  // placement: { xpos: 'left|right|center', ypos:'top|bottom|center' } defaulting to { xpos: left, ypos: top}
  // offset: offset from mouse position in pixels
  // removeOnClick: capture clicks, and remove this
  positionRelativeToClick: function(objectId,event,options) {
    var m = $(objectId);
    stopEventPropagation(event);
    
    // Double-clicking toggles it off
    if ( m.style.display=='block') {
      m.style.display= 'none';
      return;
    }
    
    var point = this.clickPoint(event);
    // console.log("point="+point.xpos + ','+point.ypos)
    parentOffset = this.getParentOffset(m)
    // console.log("parent offset ="+parentOffset.xpos + ','+parentOffset.ypos)
    
    point.xpos += -parentOffset.xpos;
    point.ypos += -parentOffset.ypos;

    this.positionRelativeToPoint(objectId,point,options);    
  },

  // For backwards compatibility
  showHere: function(objectId,event,options) {
    this.positionRelativeToClick(objectId,event,options)
  },
  
  
  manageEventHandling: function(event) {
    if ( this.showingElement ) 
      this.hideAndStopObserving(event);
    this.stopObserver = this.hideAndStopObserving.bindAsEventListener(this);
    Event.observe(document,'click', this.stopObserver, false )    
  },
  
  hideAndStopObserving: function(event) {
    if ( event ) stopEventPropagation(event);
    if (this.showingElement) Element.hide(this.showingElement);
    this.showingElement = null;
    Event.stopObserving(document,'click',this.stopObserver)
  },
  
  // Passing of panel untested... don't use
  dimWindow: function(divId,panel) {
    var veil = $(divId);
    // console.log(veil);
    panel = $(panel) ;
    // console.log(panel);
    if ( panel ) {
      veil.style.height=panel.offsetHeight;
      veil.style.width=panel.offsetWidth;
      var position = Position.cumulativeOffset(panel);
      veil.style.left=position[0];
      veil.style.top=position[1];
    } else {
      var docD = this.documentDimensions();
      veil.style.width = docD.width;
      veil.style.height = docD.height;
      veil.style.left=0;
      veil.style.top=0;
    }
    veil.style.display = 'block';
  },
  
  undimWindow: function(divId) {
    Element.hide(divId)
  },
  
  scrollDiv: function(id) {
    var e = document.getElementById(id);
    if ( e ) 
      e.scrollTop=e.scrollHeight-e.offsetHeight;
  }
  

}


// Handler for managing forms and form elements
var formHandler = {
  
  validateEmail: function(strValue) {
    var objRegExp = /^([\w\.\-\+]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
    return objRegExp.test(strValue);
  },
  
  findElementByName: function(formName,name) {
    var e = null;
    if ( document.forms[formName] ) {
      var elements = document.forms[formName].elements
      for (var i = 0 ; i < elements.length; i++ ) {
        if ( elements[i].name == name ) {
          e= elements[i];
          break;
        }
      }
    }
    return e;
  },
  
  getRadioValue: function(formName, fieldName) {
    var form;
    if ( (form = document.forms[formName]) == null ) 
      return;
    var radioObj = eval("form."+fieldName);
    if ( !radioObj ) alert("Unable to find field name "+fieldName);
    var radioLength = radioObj.length;
    if(radioLength == undefined) {
      if(radioObj.checked)
        return radioObj.value;
      else
        return "";
    } else {
      for(var i = 0; i < radioLength; i++) {
        if(radioObj[i].checked) 
          return radioObj[i].value;
      }
    }
    return "";    
  },
  
  getSelectValue: function(fieldId) {
    $(fieldId).options[$(fieldId).selectedIndex].value;
  },
  
  setRadioValue: function(formName,fieldName,value) {
    var form;
    if ( (form = document.forms[formName]) == null ) 
      return;
    var radioObj = eval("form."+fieldName);
    if ( !radioObj ) alert("Unable to find field name "+fieldName);
    var radioLength = radioObj.length;
    if(radioLength == undefined) {
      if(radioObj.value == value )
         radioObj.checked = true;
    } else {
      for(var i = 0; i < radioLength; i++) {
        if(radioObj[i].value == value )
           radioObj[i].checked = true;
         else 
          radioObj[i].checked = false;
      }
    }
  },
  
  formFocus: function(formName,name) {
    // console.log('Focus on form '+formName + ' entry name ='+name);
    if ( document.forms[formName] == null ) 
      return;
    var elements = document.forms[formName].elements
    for (var i = 0 ; i < elements.length; i++ ) {
      if ( name ) {
        if ( elements[i].name == name ) {
          elements[i].focus();
          break;
        }
      } else if ( elements[i].tagName.match(/textarea/i) ||
          (elements[i].tagName.match(/input/i) && 
              !elements[i].getAttribute('type').match(/(hidden|button|submit|reset|image)/i) ) ) {
        elements[i].focus();
        break;
      }
    }
  },

  // Return the field of defined name in the form
  getFormField: function(formName, fieldName) {
    if ( document.forms[formName] == null || fieldName == null ) 
      return null;
    var elements = document.forms[formName].elements
    for (var i = 0 ; i < elements.length; i++ ) 
      if ( elements[i].name == fieldName ) 
        return elements[i]
    return null;
  },
  
  // Duplicate the indicated block, presumably containing the item identified by currItem
  // The block is only duplicated if the item has some value, that is, is not empty
  // You can also pass a value for maxUnfilled (default 1 if not passed).  This will make
  // sure that the number of empty fields with name equal to the name of currItem is not greater
  // than what has been passed.  This feature is provided to simplify the logic that the caller
  // must implement to determine if the block should be duplicated or not.
  // Returns true if a new field was added
  duplicateFields: function (block,currItem,maxUnfilled) {
    // console.log(currItem)
    var emptyFields = 0;
    if ( ! isDefined('maxUnfilled') )  maxUnfilled = 1;
    if (!currItem || (currItem && currItem.value)) {
      if ( currItem ) {
        var parent = block.parentNode;
        names = document.getElementsByName(currItem.name);
        for ( i = 0 ; i < names.length; i++ ) {
          if ( ! names[i].value || names[i].value.match(/^\s*$/ )) emptyFields++;
        }
        // console.log("found items of name" + currItem.name + " = " + names.length + "of which these are empty: "+ emptyFields)
      }
      if ( emptyFields < maxUnfilled ) {
        var newBlock = block.cloneNode(true);
        this.clearChildrenInputValues(newBlock);
        block.parentNode.appendChild(newBlock)
        return true;
      } else {
        return false;
      }
      
    }
  },
  /*
  * Multifile selection by:
  *   Class by Stickman -- http://www.the-stickman.com
  *      with thanks to:
  *      [for Safari fixes]
  *         Luis Torrefranca -- http://www.law.pitt.edu
  *         and
  *         Shawn Parker & John Pennypacker -- http://www.fuzzycoconut.com
  *      [for duplicate name bug]
  *         'neal'
  *  If you specify a preview Image Div ID, then if any links are 
  *  provided, then the image will be show in the preview div, and
  *  the individual images links will be selectable 
  */
  MultiSelector: function( list_target, max, previewImageDiv ){
    
    this.list_target = list_target;
    this.count = 0;
    this.id = 0;
    if( max ){
      this.max = max;
    } else {
      this.max = -1;
    };
    
    this.addElement = function( element, runAfter ){
      if( element.tagName == 'INPUT' ){
        element.multi_selector = this;
        element.onchange = function(){
          var new_element = document.createElement( 'input' );
          new_element.type = element.type;
          new_element.name = element.name;
          this.parentNode.insertBefore( new_element, this );
          this.multi_selector.addElement( new_element );
          this.multi_selector.addListRow( this );

          // Hide this: we can't use display:none because Safari doesn't like it
          this.style.position = 'absolute';
          this.style.left = '-1000px';
          if ( typeof runAfter == 'function' )
            runAfter(element);

        };
        // If we've reached maximum number, disable input element
        if( this.max != -1 && this.count >= this.max ){
          element.disabled = true;
        };

        this.count++;
        this.current_element = element;

      } else {
        alert( 'Error: not a file input element' );
      };

    };

    /**
     * Add a new row to the list of files
     */
    this.addListRow = function( element ){
      if ( ! element.value ) return;
      
      var new_row = document.createElement( 'div' );
      var new_row_button = document.createElement( 'input' );
      new_row_button.type = 'button';
      new_row_button.value = 'Delete';
      //new_row_button.style = 'margin:100px';  //GARF: Farhad you need to make formatting of the output generic
      new_row.element = element;
      // If this is selected and previewImages is on, then we show the appropriate image
      // in the preview position
      previewImageDiv = $(previewImageDiv);
      if ( previewImageDiv ) {
        this.previewImage(element);  // show it right now
        // new_row.onclick = function() {alert("hello");}
        new_row.onclick = function() { this.previewImage(element) ; }.bind(this);
      }
      // if the delete button is selected, remove the images
      new_row_button.onclick= function(){
        this.parentNode.element.multi_selector.cleanImage(this.parentNode.element);
        this.parentNode.element.parentNode.removeChild( this.parentNode.element );
        this.parentNode.parentNode.removeChild( this.parentNode );
        this.parentNode.element.multi_selector.count--;
        this.parentNode.element.multi_selector.current_element.disabled = false;
        // Appease Safari
        //    without it Safari wants to reload the browser window
        //    which nixes your already queued uploads
        return false;
      };

      new_row.innerHTML = element.value;
      new_row.appendChild( new_row_button );
      this.list_target.appendChild( new_row );

    };

    this.cleanImage = function(element) {
      var preview = document.getElementById('multiselector_image_preview');
      if ( preview && preview.src == element.value ) {
        preview.src = "";
        preview.style.visibility = 'hidden';
      }
      
    };
    
    this.previewImage = function(element) {
      var preview = document.getElementById('multiselector_image_preview');
      if ( imageHandler.isImageLink(element.value) ) {
        if ( !preview ) {
          preview = new Image();
          preview.setAttribute('id','multiselector_image_preview');
          previewImageDiv.appendChild(preview)
        }
        if ( preview.src != element.value ) { //  not currently display
          preview.style.visibility = "visible";
          preview.src = element.value;
          imageHandler.resizeImage(preview,previewImageDiv.offsetWidth,previewImageDiv.offsetHeight);
        } 
      } else {
        if ( preview )
          preview.style.visibility = "hidden";
      }     
    }     

  },

  // given a node representing a "block" (can be anything though), it 
  // clears all the input children
  clearChildrenInputValues :function(block) {
    var blockChildren = block.childNodes;
    if ( blockChildren ) {
      for ( var i = 0 ; i < blockChildren.length ; i++ ) {
        var child = blockChildren[i];
        // We only care about elements
        if ( child.nodeType == 1 ) 
          child.tagName == 'INPUT' ? child.value = '' : this.clearChildrenInputValues(child)
      }
    }
  },
  
  // Enables <cr> to submit forms.
  submitOnEnter: function(myfield,e) {
    var keycode;
    if (window.event) keycode = window.event.keyCode;
    else if (e) keycode = e.which;
    else return true;
    
    // This is the keycode for the 
    if (keycode == 13)
    {
      myfield.form.submit();
      return false;
    }
    else
    return true;
  },
  
  // WARNING - requires prototype
  instructInField: function(field,defaultClass,setClass) {
    var field = document.getElementById(field);
    if ( field ) {
      this.setClassOnValue(field,defaultClass,setClass);
      Event.observe(field,'focus', function(event) { this.instructFieldFocus(field); this.setClassOnValue(field,defaultClass,setClass) }.bind(this))
      Event.observe(field,'blur', function(event) { this.instructFieldBlur(field); this.setClassOnValue(field,defaultClass,setClass) }.bind(this))
    }
  },
  
  instructFieldFocus: function(field) {
    if ( field.value == field.defaultValue)
      field.value='';
  },
  
  instructFieldBlur: function(field) {
    if ( field.value == '' )
      field.value = field.defaultValue;
  },
  
  setClassOnValue: function(field,defaultClass,setClass) {
    if ( field.value==field.defaultValue)
      this.replaceClassName(field,setClass,defaultClass);
    else
      this.replaceClassName(field,defaultClass,setClass);
  },
  
  // clear the default value associated with an input field
  clearDefaultValue: function(my) {
    if(my.value==my.defaultValue) my.value='';
  },

  resetDefaultValue: function(my) {
    if (my.value=='') my.value = my.defaultValue;
  },

  // code to provide focus highlighting for IE
  // courtesy of suckerfish: http://www.htmldog.com/articles/suckerfish/focus/
  highlightOnFocus: function() {
    var inputs = document.getElementsByTagName("INPUT");
    this.addHighlight(inputs);
    inputs = document.getElementsByTagName("TEXTAREA");
    this.addHighlight(inputs);  
  },

  addClassName: function(item,className) {
    if ( !item.className.match(RegExp("\\b"+className+"\\b")) )  {
      item.className += " " + className;
    } 
  },
  
  removeClassName: function(item,className) {
    item.className = item.className.replace(new RegExp("\\b"+className + "\\b"),'');
  },
  
  replaceClassName: function(item,originalClassName, newClassName) {
    this.removeClassName(item,originalClassName);
    this.addClassName(item,newClassName);
  },
  
  // Note: Ideally, this function would maintain the functionality associatd with
  // any function, and simply do the highlighting as an extra, but it doesn't work!
  addHighlight: function(inputs) {
    for (var i=0; i<inputs.length; i++) {
      if ( isDefined("Prototype") ) {
        Event.observe(inputs[i], 'focus', function() {this.addClassName(inputs[i],"focused")}, false);
        Event.observe(inputs[i], 'blur', function(){this.removeClassName(inputs[i],"focused")}, false);
      } else {
        inputs[i].addEventListener('onfocus', function(){addClassName(inputs[i],"focused")}, false);
        inputs[i].addEventListener('onblur', function(){removeClassName(inputs[i],"focused")}, false);
      }
    }
  }
  
}

// WARNING - if the html has already scaled the image, then the image.height and image.width numbers
// are not going to be accurate.  Generally, the html scales the image proportionately, but only 
// the dimension that is explicitly scaled is changed when queried in the DOM.
var imageHandler= {
  resizeImage: function(image,maxWidth,maxHeight) {
    var scale;
    if ( !maxWidth ) maxWidth = 100;
    if ( !maxHeight ) maxHeight = maxWidth;
    // console.log("image width ="+image.width + " height="+image.height);
    var wscale = maxWidth/image.width;
    var hscale = maxHeight/image.height;
    // In case there is some problem
    if ( image.width == 0 || image.height == 0)
      return;

    // console.log("width scale ="+wscale + " height scale="+hscale);
    // Scale by the smaller amount
    if ( hscale > wscale )
      image.width = image.width * wscale;
    else
      image.height = image.height * hscale;
  },
  
  // check if the link represents an image
  isImageLink: function(val) {
    return val.match(/\.(jpg|jpeg|png|gif)$/i) ? true : false
  }  
}

function isDefined(variable,object) {
  object = object || window;
  return (typeof(eval(object)[variable]) != 'undefined');
}

function gotoURL(url) {
  window.location = url;
}

function stopEventPropagation(e) {
  if (!e) var e = window.event;
  e.cancelBubble = true;
  if (e.stopPropagation) e.stopPropagation();
}


// If we are using Prototype, then we create the class and set its protoype to be 
// what we defined above
if ( isDefined("Prototype") ) {
  var WindowHandler = Class.create();
  WindowHandler.prototype = windowHandler;
  var FormHandler = Class.create();
  FormHandler.prototype = formHandler;
  var ImageHandler = Class.create();
  ImageHandler.prototype = imageHandler;
  var NavHandler = Class.create();
  NavHandler.prototype = navHandler;
}

