Location: PHPKode > projects > ZenTrack - project/bug tracking software > zentrack_2.6.4/www/behavior_js.php
<?
/**
 * This file depends on the $_GET['formset'] value to provide a list
 * of forms that will be managed.  If that list is not
 * provided than this javascript will be of little use.
 *
 * The form names can simply be separated by a space (or a single form name is fine)
 */

  include_once(dirname(__FILE__)."/header.php");  
  $behaviors = $zen->getBehaviorList();
  $groups = $_SESSION['data_groups'];

  /**
   * Generate field info for behaviors
   *
   * @param integer $bid behavior id
   * @param array $fields the fields to generate
   * @param array $fieldMap the map of fields to behaviors
   * @param string $setid identifier used to retrieve these later (defaults to 'default')
   */
   function genBehaviorFields($bid, $fields, &$fieldMap, $setid = 'default') {
     global $zen;
     // create a new set of matches
     foreach($fields as $f) {
       $fkey = $f['field_name'];
       // store the field to behavior mappings for use later
       if( !is_array($fieldMap["$fkey"]) ) { $fieldMap["$fkey"] = array(); }
       if( !in_array($bid, $fieldMap["$fkey"]) ) {
         $fieldMap["$fkey"][] = $bid;
       }
       
       // here we are going to try to parse the field values into
       // a simple integer date that can be used for comparisons
       $val = $f['field_value'];
       if( strpos($f['field_name'], '_date') > 0 ) {
         // can't be the first character, so 0 is not a concern
         $val = $zen->dateParse($val);
       }
       
       // create the behavior fields objects
       print "  behaviorMap['$bid'].addField(";
       print $zen->fixJsVal($f['field_name']);
       print ','.$zen->fixJsVal($f['field_operator']);
       print ','.$zen->fixJsVal($val);
       print ','.$zen->fixJsVal($setid);
       print ");\n";
     }
   }
   
   /**
    * Generate fields for groups
    *
    * @param integer $group_id
    * @param array $values
    * @param string $setid identifier used to retrieve these later (defaults to 'default')
    */
    function genGroupFields( $group_id, $values, $setid = 'default' ) {
      global $zen;
      for($i=0; $i < count($values); $i++) {
        // add all fields used for matching to the group map entry
        $f = $values[$i];
        if( is_array($f) ) {
          $v = $f['field_value'];
          $l = $f['label'];
        }
        else {
          $v = $l = $f;
        }
        print "  groupMap['$group_id'].addField(";
        print $zen->fixJsVal($v);
        print ','.$zen->fixJsVal($l);
        print ','.$zen->fixJsVal($setid);
        print ");\n";
      }      
    }
    
    $mode = $zen->checkAlpha($_GET['mode']);
    if( !$mode ) { $mode = 'view'; }
    $userBins = $zen->getUsersBins($login_id,"level_$mode");  
  
?>
//<pre>

/**
 * Create a behavior map entry
 */
function BehaviorMapEntry(group_id, name, matchall, field, disabled) {
  this.group_id = group_id;
  this.name = name;
  this.matchall = matchall;
  this.field = field;
  this.fields = new Array();
  this.disabled = disabled;
}

BehaviorMapEntry.prototype.addField = function(name, operator, value, setid) {
  if( !setid ) { setid = 'default'; }
  if( !this.fields[setid] ) { this.fields[setid] = new Array(); }
  this.fields[setid][ this.fields[setid].length ] = new BehaviorMapField( name, operator, value );    
}

BehaviorMapEntry.prototype.getFields = function( setid ) {
  if( !setid ) { setid = 'default'; }
  return this.fields[setid]? this.fields[setid] : new Array();
}

/**
 * Create a field in a behavior map
 */
function BehaviorMapField(name, operator, value) {
  this.name = name;
  this.operator = operator;
  // always store value in lower case for case insensitive matching
  this.value = (typeof value == 'string')? value.toLowerCase() : ((typeof value == 'undefined')? null : value);
}

/**
 * Create a group map entry
 */
function GroupMapEntry(id, name, table, evalType, evalText) {
  this.id = id;
  this.name = name;
  this.table = table;
  this.evalType = evalType;
  this.evalText = evalText;
  this.fields = new Array();
}

GroupMapEntry.prototype.addField = function(value, label, setid) {
  if( !setid ) { setid = 'default'; }
  if( !this.fields[setid] ) { this.fields[setid] = new Array(); }
  if( !label ) { label = value; }
  this.fields[setid][ this.fields[setid].length ] = new GroupMapField( value, label );    
}

GroupMapEntry.prototype.getFields = function( setid ) {
  if( !setid ) { setid = 'default'; }
  behaviorDebug(3, "(GroupMapEntry.getFields("+setid+"): "+this.fields[setid]);
  return this.fields[setid]? this.fields[setid] : new Array();
}

function GroupMapField(value, label) {
  this.value = value;
  this.label = label;
}

<?
  // Generate an array of possible values that can be accessed from Javascript
  $possible_variables=array('login_id',
                            'login_name',
                            'login_language',
                            'login_inits',
                            'username',
                            'view',
                            'id');
  print "var jsVarNames = new Array();\n";
  print "var jsVarValues = new Array();\n";
  foreach ($possible_variables as $vn) {
    $vv=$$vn;
    print "  jsVarNames[ jsVarNames.length ] = ".$zen->fixJsVal( $vn ).";\n";
    print "  jsVarValues[ ".$zen->fixJsVal( $vn )." ] = ".$zen->fixJsVal( $vv ).";\n";
  }
?>
var groupMap = new Array();
var behaviorMap = new Array();
<?
$fieldMap = array();
$groupsLoadedMap = array();
if( is_array($behaviors) ) {
  foreach($behaviors as $b) {
    $bid = $b['behavior_id'];
    
    $group = $_SESSION['data_groups']["{$b['group_id']}"];
    if( !$group ) {
      // ignore behaviors which do not have a valid group specified
      $zen->addDebug('behavior_js.php',"Behavior $bid specified an invalid group ({$b['group_id']}), ignored", 1); 
      continue;
    }
    
    // generate the behaviorMap entry
    print "behaviorMap['$bid'] = new BehaviorMapEntry(";
    print $zen->fixJsVal($b['group_id']);
    print ",".$zen->fixJsVal($b['behavior_name']);
    print ",".($b['match_all']? 'true' : 'false');
    print ",".$zen->fixJsVal($b['field_name']);
    print ",".($b['field_enabled']? 'false' : 'true');
    print ");\n";        
    
    // create the groupMap entry for this element if it has
    // not been loaded yet
    $k = $group['group_id'];
    if( !array_key_exists($k, $groupsLoadedMap) ) {
      print "groupMap['$k'] = new GroupMapEntry($k";
      print ','.$zen->fixJsVal($group['group_name']);
      print ','.$zen->fixJsVal($group['table_name']);
      print ','.$zen->fixJsVal($group['eval_type']);
      // encode the eval text to prevent corrupting
      // the javascript syntax
      print ", '".rawurlencode($group['eval_text'])."'";
      print ");\n";
    }

    if( is_array($b['fields']) ) {
      if( $group && $group['eval_type'] == 'File' ) {
        // this is a file group, we have a more complex match here, since
        // one behavior can map to many sets of matches.
        $sets = $zen->getBehaviorFileSet( $b, $group, $userBins, $mode );
        if( $sets && count($sets) ) {
          foreach($sets as $setid=>$vals) {
            genBehaviorFields($bid, $vals['matches'], $fieldMap, $setid); 
            genGroupFields($group['group_id'], $vals['values'], $setid);
          }
        }
      }
      else {
        // if this is not a file group, then we have a simple match pattern here
        genBehaviorFields($bid, $b['fields'], $fieldMap);
        genGroupFields($group['group_id'], $group['fields']);
      }
    }
  }
}
?>

/**
 * Field map stores a list of behaviors which might be triggered
 * by a given field, so that when the field is edited, we can review
 * and trigger behavior events accordingly.
 */
var fieldMap = new Array();
<?
foreach($fieldMap as $k=>$v) {
  print "fieldMap['{$k}'] = [".join(",",$v)."];\n";
}
?>

// stores a list of fields which have been edited by a given run, so that
// we do not enter a recursive loop
var behaviorFlags = new Array();

// used for debugging this javascript set
var behaviorDebugMessages = new Array();

// loads debugging mode from header
var useBehaviorDebug = <?= strlen($Debug_Mode)? $Debug_Mode : false  ?>;

// stores a list of the most recently entered values for a given field
// this prevents updating a list with the values it already contains
// and also prevents inifinite loops
var behaviorHistoryMap = new Array();

/**
 * When a field value is changed, we will run it through
 * the fieldChangedBehavior() method and see if any behaviors
 * should be triggered.
 */
function fieldChangedBehavior( fieldObject ) {
  // debugging
  var fieldName = fieldObject? fieldObject.form.getAttribute('name')+"."+fieldObject.name : "null";
  behaviorDebug(3, "(fieldChangedBehavior)"+fieldName);

  // clear any previously set flags
  clearBehaviorFlags();
  
  // recursively check behaviors for this field
  runFieldBehaviors( fieldObject );

  // output debug
  printBehaviorDebug();
}

/**
 * Checks a field to see if it should be triggered, runs recursively,
 * triggering subsequent events based on fields changed until either
 * no more behaviors can be triggered.
 *
 * The caller is expected to handle the clearing of the field flags
 * after completion, since this method doesn't know at what point
 * it is ok to clear them.
 */
 function runFieldBehaviors( fieldObject ) {
   // generate a useful name for debugging
   var formName = fieldObject? fieldObject.form.getAttribute('name') : "-null-";
   var fieldName = fieldObject? formName+"."+fieldObject.name : "-null-";
   
   // insure that this is a valid field and that it has
   // associated behaviors mapped in the fieldMap
   if( fieldObject && fieldObject.name && fieldMap[ fieldObject.name ] ) {
     // extract the associated behaviors and check each one
     // to see if it should be triggered.
     // We also count on the checkBehaviorStatus() method to
     // prevent infinite recursion.
     var behaviors = fieldMap[ fieldObject.name ];
     behaviorDebug(3, "(runFieldBehaviors)reviewing "+behaviors.length+" behaviors for "+fieldName);
     for(var i=0; i < behaviors.length; i++) {
       var behavior_id = behaviors[i];
       var setid = checkBehaviorStatus(fieldObject.form, behavior_id); 
       if( setid ) {
         // when the method is triggered, the field it changed
         // may trigger a behavior in turn, so we will
         // use recursion to check that field as well
         var fieldAffected = executeBehavior(fieldObject.form, behavior_id, setid);	
         var newFieldName = formName+"."+fieldAffected;
         behaviorDebug(3, "(runFieldBehaviors)updated field: ["+behavior_id+"]"+newFieldName);
         
         // fieldObject.form is a reference to the form object (Read Only)
         // which contains this field
         runFieldBehaviors( fieldObject.form[fieldAffected] );
       }
     }
     return true;
  }
  else {
    // just for debugging
    behaviorDebug(3, "(runFieldBehaviors)field ignored -- no behaviors: "+fieldName);
  }
  return false;
}


/**
 * Execute a behavior.  We flag all fields changed and insure
 * that they cannot be recursively edited by behaviors (to prevent
 * inifinite loops)
 *
 * This method returns the field affected by the behavior.
 */
function executeBehavior( formObj, behaviorId, setid ) {
  if( !setid ) { setid = 'default'; }
  behaviorDebug(3, "(executeBehavior)executing behavior "+behaviorId+"->"+formObj.getAttribute('name')+"[setid="+setid+"]");
  var behavior = behaviorMap[ behaviorId ];
  var group = groupMap[ behavior.group_id ];
  
  if( !behavior || !group ) {
    behaviorDebug(1, "(executeBehavior)Behavior/group invalid: ["+behaviorId+"]");
    return false;
  }

  var fieldObj = formObj[ behavior.field ];
  if( !fieldObj ) {
    behavior.field = behavior.field + "[]";
    fieldObj = formObj[ behavior.field ];
    if( !fieldObj ) {
      behaviorDebug(1, "(executeBehavior)Behavior field invalid: "+formObj.name+"."+behavior.field);
      return false;
    }
  }

  // note that this behavior was executed
  // this may be true even if we don't get
  // a return value from setFormValsUsingGroup
  // because it may already contain the values
  // we attempt to set there
  addBehaviorFlag( behavior.field );

  // store field modified
  var fieldModified = false;

  // only return field if it exists and was changed
  if( setFormValsUsingGroup(fieldObj, group, setid) ) {
    fieldModified = behavior.field;
  }

  // disable/enable field as appropriate
  fieldObj.disabled = behavior.disabled? true : false;

  // disable/enable calendar icon and popup as appropriate
  if (fieldObj.getAttribute('hascalendar')) {
    var calendarIcon=document.getElementById(fieldObj.getAttribute('hascalendar'));
    if (fieldObj.disabled) {
      calendarIcon.src="images/disabled-cal.gif";
      calendarIcon.oldonclick=calendarIcon.onclick;
      calendarIcon.onclick=function() {return false;};
    } else {
      calendarIcon.src="images/cal.gif";
      calendarIcon.onclick=calendarIcon.oldonclick;
    }
  }

  if( fieldObj.disabled ) {
    mClassX(fieldObj, 'inputDisabled');
  }
  else {
    mClassX(fieldObj, 'input');
  }

  return fieldModified;
}

/**
 * Set the values of a form field to the list provided
 *
 * The setid is only meaningful to file groups, all other
 * groups can simply ignore this (it passes undefined) and
 * use the defaults.
 */
function setFormValsUsingGroup( fieldObj, group, setid ) {
  if( !setid ) { setid = 'default'; }

  // we keep a history to avoid redundantly setting the values if they
  // already are and to prevent infinite loops
  if( behaviorHistoryMap[ fieldObj.name ] == group.id && group.evalType == 'Matches' ) {
    behaviorDebug(3, "(setFormValsUsingGroup)field "+fieldObj.name
		  +" is already set to "+group.name+" (skipping)");
    return false;
  }

  // record the group_id that we have used to generate the field's
  // current entries
  behaviorHistoryMap[ fieldObj.name ] = group.id;

  // handle javascript eval type
  if( group.evalType == 'Javascript' ) {
    // we encode the eval text so it wont cause erros in js
    // so we unencode it here and replace any runtime variables before we
    // eval the final code
    var s = replaceRuntimeRefs(fieldObj.form.getAttribute('name'), fieldObj.name, unescape(group.evalText));
    var vals = evalJsString( s );

    // clear any existing values from fields array
    group.fields = new Array();
    
    // only bother if vals were returned
    if( vals != null && vals.length > 0 ) {
      for( var i=0; i < vals.length; i++ ) {
         if( vals[i] && typeof vals[i] == 'object' ) {
           // this is a label/value pair
           group.addField( vals[i].value, vals[i].label );
         }
         else {
           // this is just a value
           group.addField( vals[i] );
         }
      }
    }
  }
  
  var fields = group.getFields(setid);
  
  // set the field values
  var v = '';
  for(var i=0; i < fields.length; i++) {
    v += fields[i].label+",";
  }
  behaviorDebug(3, "(setFormValsUsingGroup)updating "+fieldObj.name
		+" using "+group.name+"["+fieldObj.type+"] with setid="+setid+" and values=["+v+"]");
  // To be used for hidden, text, etc: set the oldPos value if the fieldObj.value is in the list
  // (unless we do it, the first element of the list will always be assigned to the field, no matter if the fieldObj.value is in the list)
  var oldPos = -1;
  if ( fieldObj.value != null && typeof fieldObj.value != "undef" ) {
    for(var i=0; i < fields.length && oldPos == -1; i++) {
      if ( fields[i].value == fieldObj.value ) {
        oldPos=i;
      }
    }
  }
  if ( oldPos == -1) {
    for(var i=0; i < fields.length && oldPos == -1; i++) {
      if (fields[i].label == fieldObj.value) {
        oldPos=i;
      }
    }
  }
  if ( oldPos == -1) {
    oldPos=0;
    behaviorDebug(3, "(setFormValsUsingGroup)Did not recognize field value for "+fieldObj.name+" ("+fieldObj.value+")");
  } else {
    behaviorDebug(3, "(setFormValsUsingGroup)Recognized field value for "+fieldObj.name+" as ["+fields[oldPos].value+"]"+fields[oldPos].label);
  }
  switch( fieldObj.type ) {
    case "checkbox":
      if( fields[0].value ) {
        fieldObj.checked = true;
      }
      break;
    case "hidden":
      var labelText = document.getElementById(fieldObj.name+"LabelText");
      if( labelText ) {
        labelText.innerHTML = fields.length>0?fields[oldPos].label : "";
        if (fields.length>0) {
          labelText.innerHTML = fields[oldPos].label;
        } else {
          behaviorDebug(3, "(setFormValsUsingGroup) Empty string forced for "+fieldObj.name+"LabelText.innerHTML");
          labelText.innerHTML = "";
        }
      }
    case "button":
    case "submit":
    case "text":
    case "textarea":
      if (fields.length>0) {
        fieldObj.value = fields[oldPos].value;
      } else {
        behaviorDebug(3, "(setFormValsUsingGroup) Empty string forced for "+fieldObj.name+".value");
        fieldObj.value = "";
      }
      break;
    case "select":
    case "select-one":
      if( fieldObj.selectedIndex != -1 && fieldObj.options && fieldObj.options.length > 0 ) {
        // store the currently selected value and try to reproduce in a minute
        var oldValue = fieldObj.options[ fieldObj.selectedIndex ].value;
        behaviorDebug(3, "(setFormValsUsingGroup)storing oldValue="+oldValue);
      }
      behaviorDebug(3, "(setFormValsUsingGroup)storing oldValue="+oldValue); 
      fieldObj.length = 0;
      for(var i=0; i < fields.length; i++) {
        var f = fields[i];
        behaviorDebug(3, "(setFormValsUsingGroup)Setting option "+i+" to ["+f.value+"]"+f.label);
        fieldObj.options[i] = new Option();
        fieldObj.options[i].text = f.label;
        fieldObj.options[i].value = f.value;
        // try to set to the same value if possible
        if( f.value == oldValue ) {
//          fieldObj.options[i].selected = true;
          fieldObj.selectedIndex=i;
        }
      }
      break;
    case "select-multiple":
      var oldValues = new Array();
      if( fieldObj.options && fieldObj.options.length > 0 ) {
        // store the currently selected value and try to reproduce in a minute
        for (var i=0; i< fieldObj.options.length; i++) {
          if (fieldObj.options[i].selected) {
            oldValues[fieldObj.options[i].value]=1;
            behaviorDebug(3, "(setFormValsUsingGroup)storing oldValues["+fieldObj.options[i].value+"] = 1");
          }
        }
      }

      fieldObj.length = 0;
      for(var i=0; i < fields.length; i++) {
        var f = fields[i];
        behaviorDebug(3, "(setFormValsUsingGroup)Setting option "+i+" to ["+f.value+"]"+f.label);
        fieldObj.options[i] = new Option();
        fieldObj.options[i].text = f.label;
        fieldObj.options[i].value = f.value;
        // try to set to the same value if possible
        if( oldValues[f.value]==1 ) {
          fieldObj.options[i].selected = true;
//          fieldObj.selectedIndex=i;
        }
      }

      var labelText = document.getElementById(fieldObj.name+"LabelText");
      if( labelText ) {
        labelText.innerHTML = fields.length>0?fields[oldPos].label : "";
        if (fields.length>0) {
          if( fieldObj.options && fieldObj.options.length > 0 ) {
            // store the currently selected value and try to reproduce in a minute
            separator = "";
            labelText.innerHTML = "";
            for (var i=0; i< fieldObj.options.length; i++) {
              if (fieldObj.options[i].selected) {
                labelText.innerHTML = labelText.innerHTML + separator + fieldObj.options[i].value;
                separator = "; ";
                behaviorDebug(3, "(setFormValsUsingGroup)added "+fieldObj.options[i].value+"to "+fieldObj.name+"LabelText.innerHTML");
              }
            }
          }
        } else {
          behaviorDebug(3, "(setFormValsUsingGroup) Empty string forced for "+fieldObj.name+"LabelText.innerHTML");
          labelText.innerHTML = "";
        }
      }
      break;
    //case "radio":
    //case "file":
    //case "hidden":
    //case "password":
    //case "reset":
    default:
      behaviorDebug(1, "(setFormValsUsingGroup)Invalid field type: "+fieldObj.name+"["+fieldObj.type+"]");
      return false;
  }

  return true;
}

/**
 * Adds a field to the list of fields updated to prevent recursive looping
 */
function addBehaviorFlag( fieldName ) {
  if( fieldName ) {
    behaviorDebug(3, "Set behaviorFlag: "+fieldName);
    behaviorFlags[ fieldName ] = 1;
  }
}

/**
 * Clears out the list of fields that have been updated during a run
 * of behavior triggers
 */
function clearBehaviorFlags() {
  behaviorDebug(3, "(clearBehaviorFlags)all clear");
  behaviorFlags = new Array();
  behaviorHistoryMap = new Array();
}

/**
 * Checks to see if conditions have been met to trigger a behavior
 *
 * Returns the setid matched or false
 */
function checkBehaviorStatus( formObject, behaviorId ) {
  // retrieve the behavior info
  var behavior = behaviorMap[ behaviorId ];

  // just a quick check for integrity
  if( !behavior ) {
    behaviorDebug(1, "(checkBehaviorStatus)Behavior id not found: "+behaviorId);
  }

  // debugging
  var formName = formObject? formObject.getAttribute('name') : "-null-";
  behaviorDebug(3, "(checkBehaviorStatus)Checking status: ["
		+formName+"]"+behavior.name);

  // make sure this behaviors field is not listed in the
  // flagged fields.  If it is, we return false to avoid
  // infinite recursion.
  if( behaviorFlags[ behavior.field ] ) { 
    behaviorDebug(2, "(checkBehaviorStatus)Behavior ignored -- field modified already: ["
		  +behavior.field+"]"+behavior.name);
    return false; 
  }

  // otherwise, we will check the fields and their 
  // match conditions, insuring that we monitor the 
  // "and" or "or" behavior specified.
  // we must iterate over each setid and evaluate it
  // independantly, taking the first successful result
  // that we receive.
  var numSets = 0;
  for( var setid in behavior.fields ) {
    numSets++;
    behaviorDebug(3,"(checkBehaviorStatus)checking setid="+setid);
    var matchedAll = true;
    for(var i=0; i < behavior.fields[setid].length; i++) {
      var f = behavior.fields[setid][i];
      var matched = matchBehaviorCriteria( formObject, f );
      behaviorDebug(3, "(checkBehaviorStatus)"+f.name+" "+f.operator+" '"+f.value+"' ["+matched+"]");
  
      // if matchall is set, then we must match every field
      // to succeed
      if( behavior.matchall && !matched ) {
        matchedAll = false;
        break; 
      }
      // otherwise, any match is a success
      if( behavior.matchall == false && matched ) {
        behaviorDebug(3, "(checkBehaviorStatus)setid="+setid+": matched on 'or' clause");
        return setid; 
      }
    }
    // in the event that we are looking for a matchAll and the flag is still
    // true then we have a match
    if( behavior.matchall && matchedAll ) {
      behaviorDebug(3, "(checkBehaviorStatus)setid="+setid+": all behaviors matched");
      return setid; 
    }
  }
  
  // if there are no fields to match, then the behavior always runs.
  // we cannot simply look at fields.length here, because it returns
  // zero when we have null values in the array!
  if( numSets < 1 ) {
    behaviorDebug(3, "(checkBehaviorStatus)Always true -- no fields: "+behavior.name);
    return true;
  }

  // in the point that we arrive here, nothing has matched so we return false
  behaviorDebug(3, "(checkBehaviorStatus)behavior="+behavior.name+": no matches");  
  return false;
}

/**
 * Matches a single field value against behavior criteria
 */
function matchBehaviorCriteria( formObject, behaviorMapField ) {
  var formField = formObject[ behaviorMapField.name ];

  // if the field does not exist, it did not match
  if( !formField ) { 
    behaviorDebug(1, "(matchBehaviorCriteria)Field not found in form: "
		  +behaviorMapField.name);
    return false; 
  }

  fieldVal = getFormFieldValue(formField,behaviorMapField);
  if( fieldVal && fieldVal.toLowerCase ) { fieldVal = fieldVal.toLowerCase(); }

  // store result
  var res = false;

  // otherwise, evaluate the match and deal accordingly
  switch( behaviorMapField.operator ) {
  case "eq":
    // equals
    res = behaviorMapField.value == fieldVal;
    break;
  case "ne":
    // not equal
    res = behaviorMapField.value != fieldVal;
    break;
  case "co":
    // contains
    res = fieldVal.indexOf(behaviorMapField.value) > -1;
    break;
  case "nc":
    // does not contain
    res = fieldVal.indexOf(behaviorMapField.value) < 0;
    break;
  case "sw":
    // starts with
    res = fieldVal.indexOf(behaviorMapField.value) == 0;
    break;
  case "ew":
    // ends with
    var len1 = behaviorMapField.value.length;
    var len2 = fieldVal.length;
    var len3 = len2-len1;
    res = len3 >= 0 &&
      fieldVal.substr(len3,len1) == behaviorMapField.value;
    break;
  case "gt":
    // greater than
    res = fieldVal > behaviorMapField.value;
    break;
  case "lt":
    // less than
    res = fieldVal < behaviorMapField.value;
    break;
  case "ge":
    // greater than or equal
    res = fieldVal >= behaviorMapField.value;
    break;
  case "le":
    // less than or equal
    res = fieldVal <= behaviorMapField.value;
    break;
  case "js":
    // replace runtime variables and then evaluate js code
    var s = replaceRuntimeRefs(formField.form.getAttribute('name'), formField.name, behaviorMapField.value);
    res = eval(s);
    break;
  default:
    behaviorDebug(1, "Invalid comparator: "+behaviorMapField.operator);
    res = false;
    break;
  }

  behaviorDebug(3, "(matchBehaviorCriteria) "
	+"'"+behaviorMapField.value+"' "
	+behaviorMapField.operator+" "
	+"'"+fieldVal+"' "
	+" ["+res+"]");
  
  return res;
}

/**
 * Replaces special reference names with runtime values
 */
function replaceRuntimeRefs( formName, fieldName, jsText ) {
    var myReplaceText;
    var myRegExp;
    for (var i=0; i < jsVarNames.length; i++) {
      myReplaceText = '\{'+jsVarNames[i]+'\}';
      myRegExp = new RegExp(myReplaceText, "ig")  
      jsText = jsText.replace( myRegExp, jsVarValues[jsVarNames[i]] );
    }
    var form = 'window.document.forms["'+formName+'"]';
    var field = form+".elements['"+fieldName+'"]';
    jsText = jsText.replace(/{form}/ig, form);
    jsText = jsText.replace(/{field}/ig, field);
    return jsText;
}

/**
 * Creates a debug message for behaviors
 */
function behaviorDebug( errorLevel, str ) {
  if( useBehaviorDebug >= errorLevel ) {
    str = "["+makeTimeString()+"] "+str;
    behaviorDebugMessages[ behaviorDebugMessages.length ] = [errorLevel, str];
  }
}

/**
 * Prints out debug messages
 */
function printBehaviorDebug() {
  var divBox = document.getElementById? document.getElementById("behaviorDebugDiv") : null;
  if( divBox ) {
    // generate or append our debug info to be placed in the div layer.
    var msg = divBox.innerHTML;
    if( !msg ) { msg = "<p>-----BEHAVIORS -----</p>\n"; }
    for(var i=0; i < behaviorDebugMessages.length; i++) {
      var m = behaviorDebugMessages[i];
      var lvl;
      if( divBox ) {
	switch(m[0]) {
	case 1:
	  lvl = "error";
	  break;
	case 2:
	  lvl = "";
	  break;
	default:
	  lvl = "note";
	}
	msg += "<span class='"+lvl+"'>"+m[1]+"</span>";
      }
      msg += "<br>\n";
    }
    divBox.innerHTML = msg;
  }
  behaviorDebugMessages = new Array();
}

/**
 * When the page loads, we want to check to see if any behaviors
 * should be run based on the loaded values and load corresponding
 * behavior info.
 */
function pageLoadedBehavior() {
  // debug output
  behaviorDebug(3, "(pageLoadedBehavior)running");
  
  // clear the behavior flags from any previous uses
  clearBehaviorFlags();


  var behaviorFormSet = new Array(<?
    if( $_GET['formset'] ) {
      $sep = false;
      foreach(explode(',',$_GET['formset']) as $b) {
      if( $sep ) { print ","; }
        print "window.document.".$zen->checkAlphaNum($b,'_');
        $sep = true;
      }
    }
  ?>);

  // iterate over form elements and check for behaviors
  for( var x=0; x < behaviorFormSet.length; x++ ) {
    if( !behaviorFormSet[x] || !behaviorFormSet[x].elements ) {
      behaviorDebug(1, "(pageLoadedBehavior)invalid form: "+(behaviorFormSet[x]? behaviorFormSet[x].getAttribute('name') : 'undefined'));
      continue;
    }
    behaviorDebug(3, "(pageLoadedBehavior)loading form: "+behaviorFormSet[x].getAttribute('name'));
    for( var i=0; i < behaviorFormSet[x].elements.length; i++ ) {
      if( fieldMap[ behaviorFormSet[x].elements[i].name ] ) {
        setBehaviorOnChange( behaviorFormSet[x].elements[i] );
      }
      runFieldBehaviors( behaviorFormSet[x].elements[i] );
    }
  }

  // output debug
  printBehaviorDebug();
}

/**
 * Generate a function to handle onchange events for us
 */
function setBehaviorOnChange( formElement ) {
  if( formElement.type == 'checkbox' ) {
    var oldFun = formElement.onclick;
    formElement.onclick = genBehaviorFunction( formElement, oldFun );
  }
  else {
    var oldFun = formElement.onchange;
    formElement.onchange = genBehaviorFunction( formElement, oldFun );
  }
}

/**
 * This extra method is needed to handle scoping problems with
 * creating anonymous functions
 */
function genBehaviorFunction( fieldObject, oldFunction ) {
  return function() {
    var x = null;
    if( oldFunction ) {
      x = oldFunction();
    }
    fieldChangedBehavior(fieldObject);
    if (typeof(x) == 'boolean') { return x; }
  }
}

/**
 * Return a form field value by detrmining the field type and extracting value
 */
function getFormFieldValue( formField, behaviorMapField ) {
  switch( formField.type ) {
  case "checkbox":
    return formField.checked? 1 : 0;
  case "radio":
    for(var i=0; i < formField.length; i++) {
      if( formField[i].checked ) { return formField[i].value; }
    }
    return null;
  case "select":
  case "select-one":
  case "select-multiple":
    if( formField.selectedIndex < 0 ) { return ''; }
    if( probablyNumericComparator(behaviorMapField) ) {
      return formField[ formField.selectedIndex ].value;
    }
    else {
      return formField[ formField.selectedIndex ].text;
    }
  default:
    // test for date values and try to do something sensible
    // with these so that they are useful
    if( behaviorMapField.name.indexOf("_date") > 0 ) {
      //can't begin with this, so zero is not a concern
      if( formField.value.match(/[^0-9]/) >= 0 ) {
        var val = Date.parse(formField.value);
        if( val > 0 ) { return val; }
      }
    }
    return formField.value? formField.value : '';
  }
}

/**
 * Determine if a field should be matched on the numeric value or the string label
 */
function probablyNumericComparator( behaviorMapField ) {
  if( !isIntegerValue(behaviorMapField.value) ) { return false; }
  var op = behaviorMapField.operator;
  if( op == "eq" || op == "ne" || op == "gt" || op == "lt" || op == "ge" || op == "le" ) {   
    return true; 
  }
  return false;
}

/**
 * Determine if a value contains only numbers and is probably a valid id
 */
function isIntegerValue( val ) {
  try {
    if( typeof val == 'string' ) {
      return val.search(/[^0-9]/) < 0 && parseInt(val) > 0;
    }
    return parseInt(val)+"" == val;
  }
  catch( e ) {
    return false;
  }
}


/**
 * Evaluate js code and return results
 */
function evalJsString( s ) {
  eval( s );
  return x;
}


//window.onload = mergeFunctions( window.onload, pageLoadedBehavior );
addToOnload(pageLoadedBehavior);

<?
  // print out debugging info if specified as such
  if( array_key_exists('behavior_debug', $_GET) ) {
    print "//  ------ DEBUG OUTPUT FOR BEHAVIOR_JS ------- \n";
    $zen->printJsFriendlyDebug();
    print "//  ------ END DEBUG OUTPUT FOR BEHAVIOR_JS ------- \n";
  }
?>

//</pre>
Return current item: ZenTrack - project/bug tracking software