/*--
Description: Object literal used to hold and return page element references.
ToDo:method error handling
--*/

//Constructor
function JPageElementBase() {
  this.elements   = {};
  this.observers  = {};
};

//Add and element obj reference with an associated label
JPageElementBase.prototype.addElement = function(_label,_value){
  this.elements[_label]=_value;
}

//Add dom object reference that is observing another object with an id = the '_label' argument.
//each "observable" has an array of observers. each 'observer' is composed of a dom node, notification type and optional set of arguments.
//note:_arg param expected as associative array. ex: {optional:false}
//convenient way to associate a set of instructions with a given observer. 
JPageElementBase.prototype.addObserver = function(_label,_value,_type,_args){
  _args=_args||null;
  if(!this.observers[_label]){this.observers[_label]=[];}
  //make observer object literal
  var _obs = {'obs':_value,'type':_type,'args':_args};
  this.observers[_label].push(_obs);
}

//Similar to addObserver but accepts a preformed observer object
JPageElementBase.prototype.addObserverObject = function(_label,_obs){
  if(!this.observers[_label]){this.observers[_label]=[];}
  this.observers[_label].push(_obs);
}

//Given a node id, look through its observers for one subscribed to a given evt type.
//If matching observers are found, attach last one to another (given) node id
JPageElementBase.prototype.borrowObserver = function(_to_label,_from_label,_type){
  var _obs_list = this.getObservers(_from_label,_type);
  if(_obs_list.length>0){
    this.addObserverObject(_to_label,(_obs_list.pop()));
  }
}

//Add a large set of observer relationJips
JPageElementBase.prototype.ingestObservers = function(_obslist){
  for(var _set=0;_set<_obslist.length;_set++){
    this.addObserver(_obslist[_set][0],_obslist[_set][1],_obslist[_set][2]);
  }
}

//Return list of observables observers of a given notification type
JPageElementBase.prototype.getObservers = function(_label,_type){ 
  var _retList=[];
  try{
    for(var i =0;i<this.observers[_label].length;i++){
      if(this.observers[_label][i].type==_type)
        _retList.push(this.observers[_label][i]);
    }
  }catch(e){}
  return _retList;
}

//Return a given observer, return false if not found
JPageElementBase.prototype.hasObserver = function(_label,_type,_id){ 
  var _retVal= false;
  var _obs_list = this.getObservers(_label,_type);
  for(var i=0;i<_obs_list.length;i++){
    if(_obs_list[i].obs.id==_id){
      _retVal = _obs_list[i];
      break;
    }
  }
  return _retVal;
}

//Return list of all an observable's observers regardless of event type
JPageElementBase.prototype.getAllObservers = function(_label){ 
  return this.observers[_label];
}

//Remove an element reference by label
JPageElementBase.prototype.deleteElement = function(_label){
  delete this.elements[_label];
}

//Remove all elements
JPageElementBase.prototype.clear = function(){
  for(var elem in this.elements){
    delete this.elements[elem];
  }
}

//Get the value of an element reference by a label
JPageElementBase.prototype.getElement = function(_label){
  var _r=(this.elements[_label])?this.elements[_label]:false;
  return _r
}

//Get the first element stored in the elements array
JPageElementBase.prototype.getFirstElement = function(){
  for(var i in this.elements){
    return this.elements[i];
  }
}

//Update the value of an existing object reference
JPageElementBase.prototype.setElement = function(_label,_value){
  this.elements[_label]=_value;
}

//Determine the element type
JPageElementBase.prototype.getElementType = function(_label){
  var _retVal = null;
  switch(this.elements[_label].tagName.toLowerCase()){
    case 'input':
      _retVal = this.elements[_label].getAttribute('type');
      break;
    case 'select':
      _retVal = 'select';
      break;
    case 'textarea':
      _retVal = 'textarea';
      break;
  }
  return _retVal;
}

//return a flattened list of node references
//normally instantiated versions of this contructor will return the elements label
JPageElementBase.prototype.flatten = function(){
  return this.elements;
}

//Iterate through the 'elements' associative array performing the 'func' arg on each element.
JPageElementBase.prototype.walk = function(func){
  for(var i in this.elements){
    func(this.elements[i]);
  }
}

//Iterate through a nodeList and add object references with their id as label to the elements obj
JPageElementBase.prototype.ingest = function(nodeList){
  for(var i in nodeList){
    this.addElement(i,nodeList[i]);
  }
}

//Iterate through an array of ids (strings) add object references with their id as label to the elements obj
JPageElementBase.prototype.ingestById = function(idList){
  for(var i =0;i<idList.length;i++){
    this.addElement(idList[i],document.getElementById(idList[i]));
  }
}

//return array of element obj ids that match attribute name
//"parentObjRef" argument is optional - defaults to the body object.
function getElemIdsByTypeAndAttributeValue(attribute,value,elementTypes,parentObjRef){
  parentObjRef = (parentObjRef)?parentObjRef:document.body;
  var retVal = [];
  //look through all requested element types
  for(var x=0;x<elementTypes.length;x++){
    var y = parentObjRef.getElementsByTagName(elementTypes[x]);
    for(var i=0;i<y.length;i++){
      //look for desired val in elements specified attribute
      try{
        if(y[i].getAttribute(attribute).indexOf(value) != -1){
          retVal.push(y[i].id);
        }
      }catch(e){}
    }
  }
  
  return retVal;
}

//return array of element obj ids that match attribute name
//"parentObjRef" argument is optional - defaults to the body object.
function getElemIdsByAttributeName(attribute,elementType,parentObjRef){
  parentObjRef = (parentObjRef)?parentObjRef:document.body;
  var retVal = [];
  var y = parentObjRef.getElementsByTagName(elementType);
  for(var i=0;i<y.length;i++){
    if(y[i].getAttribute(attribute) != null){
      retVal.push(y[i].id);
    }
  }
  return retVal;
}
