heitml Source Display

File: /usr/local/httpd/htdocs/heitml2.0/lib/formfields.hei


// heitml Form Fields
// Copyright (C) 2000 by H.E.I. Informationssysteme GmbH and suppliers.
// This file is part of heitml.  All licensing conditions of the
// heitml package apply.  See the license file contained in the heitml
// distribution.
<includeonce>//
<include component.hei>//
<include std.hei>//
<include javasc.hei>//
<include browcomp.hei>//
<include panel.hei>//
<
// 
// Buttons
// The INPUT element uses the value attribute to render the button.
// The BUTTON element uses its contents, which is more flexible.
// Unfortunately, current browsers don't grok the BUTTON element correctly.  To
// faciliate future use of BUTTON, heitml buttons are environments.
// Their attribute value is usable as button label.
// The content is ignored.
// If value is null, contents is used as value for INPUT; it
// should not contain markup.  Caution, this submits an unintended
// value for the button.

// Frontend for button element that currently uses INPUT instead.
defenv 
_allbrowserbutton type=null name=null value=null state=null rest=null contents=null;
  type=default(type,
"submit");
  
if state!="display";
     
assign contents; defbody; /assign;
//   if isempty(contents);
     >
<input type=<? type "P"> <? name "Pn"> <? value "Pn"> <? rest "Pn">><hei
//   else 
//     button element does not work properly in all browsers 
//     ><button <? type "Pn"> <? name "Pn"> <? value "Pn"> <? state "Pn"> <? rest "Pn"> ><
//       ? contents "A"; 
//       ><input type=<? type "P"> <? name "Pn"> <? value "Pn"> <? rest "Pn">><hei
//     ></button><hei
//   /if; 
  
/if;
/defenv;


defenv _heitmlbutton layout type name value form onsubmit rest;
   type=default(type,
"submit");
   l = gl._complevel; 
if gl._complevel &gt; 15; gl._complevel = 15; /if; /* suppress handles inside */
   
if layout=="input" || layout=="button" && gl.bc_dynhtml!="M";
     >
<input type=<? type "P"> <? name "Pn"> <? value "Pn"> <? rest "Pn">><hei
   
elsif layout=="button";
     >
<button type=<? type "P"> <? name "Pn"> <? rest "Pn">><defbody></button><
   
elsif layout=="link" || layout=="compatlink" || layout=="hidden";
     
if tolower(type)!="reset"><input type="hidden" <?name "Pn"> value=""></if;
     
js;
        
let namejs=substall(name,".","_");
        >
function yysubmit_<?namejs>() {<
           
if tolower(type)!="reset"><?form>[<?name "J">].value="t"; window.yybtn=<?name "J">; if (<?onsubmit>(<?form>)) <?form>.submit();<else
                                    >
<?form>.reset();</if>}<
        
if layout!="hidden";
           
if gl._complevel<15;
              >
<jswrite stmt="document.write"><\a href="javascript:yysubmit_<?namejs>();"><defbody><\/a></jswrite><
           
/if;
        
/if;
     
/js;
     
if layout!="hidden" && 15<=gl._complevel;
        >
<\a href="javascript:yysubmit_<?namejs>();"><defbody><\/a><
     
/if;  
     
if layout=="compatlink";
        >
<NOSCRIPT><input <? type "Pn"> <?name "Pn"> <? value "Pn"> <? rest "Pn">></NOSCRIPT><
     
/if;
   
/if;
   gl._complevel=l;
/defenv;


// Form button component.
// NAME is the button name, VALUE its value.
// Contents or VALUE is used to label the button.
// If TYPE is "reset" this is a reset button, else a submit button.
// Optional STATE asks the panel to disable/enable the button.
// We need to know which button was pressed, which is only possible if
// buttons are always successful controls.  Therefore, attribute name
// is mandatory.  If VALUE is null, the button's NAME must be unique
// within its form nest, else the pair NAME, VALUE must be unique.
// EXTEND mandatory name really necessary only for sleform, a sesform
// EXTEND could use stdmakeuniq()
defenv formbutton name layout=null value=null type=null state=null panel=null @rest ...;
  
inherit InlineComponent; definpara; defnowysiwyg; definpara;

  
// Define context H HTML
  
defenv _evalcontext;
    
_setcontext "H"defbody; /_setcontext;
  
/defenv;

  
// Process this button.
  
// PFF is this.panel.ff for convenience
  
// Precondition: all fields have been submitted successfully.
  
// Return string on failure, else whether form has been successfully
  
// submitted (bool).
  
def process pff; return true; /def;

  
defenv jsonsubmit; 
      
assign this.panel.onsubmit
      
// control = form["<? this.panel.control_name (this.name)>"];
      >
if (window.yybtn==<?this.name "P">){
<      defbody>}
<      ? this.panel.onsubmit;
      
/assign;
  
/defenv;

  
// EXTEND need doit because subclasses cannot call inherited body but
  
defenv doit layout panel name value type state rest;
    layout=default(layout,
"input");
    this.panel = default(panel,gl._curpanel);
    this.name = name;
//    if isempty(value); value = name; /if;
//    this.value=value;
    rest.onclick=
"window.yybtn='"+name+"';"+default(rest.onclick,"");
    
if isnull(type); this.type = null; else this.type = tolower (type); /if;
    this.state = this.panel.button_state (this, state);
    this.panel.regbutton (this);
    
// Map all formbutton types except null, "submit" and in the
    
// outermost panel "reset" to HTML button type null.
    
if !isnull(this.type) && (this.type != "submit") && (this.type != "image")
       && (   (this.type != 
"reset")
           || !isnull (this.panel.encpanel)
           || !this.panel.isinitial);
      
if isempty(value);
        
// Client's default button label for type==null is something
        
// like "Submit", no good.
        
// tocapital() would be nice
        value = toupper (substring(type,0,1)) + substring(type,1);
      
/if;
      type = null;
    
/if;
    
if this.state!="display";
       
_heitmlbutton layout type  this.panel.control_name(name) value 
                     
"window.document."+this.panel.form.name this.panel.form.name+"_onsubmit" rest;
          
defbody;
       
/_heitmlbutton;
//    _allbrowserbutton type this.panel.control_name(name) value this.state rest;
//       defbody;
//    /_allbrowserbutton;
    
/if;
  
/defenv;


  
doit layout panel name value type state rest;
     
defbody;
  
/doit;
/defenv


// Button to change input of field FNAME to NEWVALUE.
// Works only if client groks intrinsic events and JavaScript.
defenv fieldchangebutton value=null state=null fname newvalue @rest ...;
  
inherit InlineComponent; definpara; defnowysiwyg;
  panel = gl._curpanel;
  rest.onclick = default (rest.onclick, 
"")
    + 
"this.form['"+panel.control_name (fname)+"'].value='"+newvalue+"';";
  
_allbrowserbutton "button" null value state rest; defbody; /_allbrowserbutton;
/defenv




// 
// Fields

// Restriction: you cannot have multiple fields with a common name,
// except fieldcheckbox.

// EXTEND object controls?
// EXTEND label, legend
// EXTEND document attributes

// Abstract base class for fields.
// Use like this:
//   inherit _fieldbase;
//   regfield (...)
//   clvalue();
// Override check() and toclvalue() to convert between program and
// client representation of the field value.  If your check() can
// fail, you may want to override jscheck(), too.
defclass _fieldbase;
  
inherit object;

  
// Attribute members:
  
//   object panel             panel that contains this field
  
//   string name              field name
  
//   string format            format string for field value,
  
//                              may contain options and format only
  
//   bool mandatory           is field mandatory?
  
//   string descr             description of acceptable field values
  
//   state                    field state: display, disabled, enabled, readonly

  
// Initialize common field attributes and register the field with its form.
  
// String NAME is the field name.
  
// String DESCR describes accepted field values, may be null.
  
// If VALUE is not null, it defines the field's initial value.
  
// If FORMAT is not null, it is the `?' format for converting VALUE.
  
// If MANDATORY, entering something into the field is mandatory.
  
// Return the field's initial value.
  
def regfield panel name value format mandatory descr errmsg state;
    this.panel = default(panel,gl._curpanel);
    this.name = name;
    this.format = format;
    this.descr = descr; this.errmsg=errmsg;
    this.mandatory = htmlbool(mandatory);
    this.state = this.panel.field_state (this, state);
    this.jscheck();
    this.panel.regfield (this,value);
  
/def;


  
// Define and return field's clvalue.
  
def clvalue;
    
if isdecl (this.panel.ff[this.name]);
      clff = this.toclvalue (this.panel.ff[this.name]);
    
else
      clff = this.panel.clvalue [this.name];
    
/if;
    
return clff;
//     return this.panel.clvalue (this.name, clff, this.toclvalue (default(this.panel.ivalue[this.name])));
  
/def;


  
// Return string that asks user to fix this field's value.
  
def errormsg;
    
// EXTEND hook to change this per page, and perhaps per form or panel.
    
if !isempty(this.errmsg); return this.errmsg; /if;
    
return "Please enter " + default (this.descr, "something")
         + 
" into field `" + this.name+"'.";
  
/def;

  
// Report bad field value.
  
def error;
    this.panel.error (errormsg(), this.name); 
  
/def;

  
// Register JavaScript contents to run in the onsubmit event handler.
  
// The contents may access the control object in local variable
  
// `control' and may assign an error message to local variable
  
// `error'.  Executing `break' skips any remaning registered
  
// code.
  
defenv regonsubmit;
    
callenv this.panel.form.regonsubmit();
      >
control = form["<? this.panel.control_name (this.name)>"];<hei
      
defbody;
    
/callenv;
  
/defenv;

  
// JavaScript to ensure that submitted value is not empty or not mandatory.
  
// To be run inside of regonsubmit.
  
def js_ensure_mandatory;
    
if this.mandatory>
      if (checkmand && (control.value == "")) {
        error = "<? errormsg()>\n";
      }
    </if;
  
/def;

  
// Register JavaScript field value check.
  
// Caution: your check() MUST NOT RELY on jscheck(), because it only
  
// runs if the user submits with JavaScript enabled.
  
def jscheck;
    
if this.mandatory;          // only to avoid empty regonsubmit
      
regonsubmit;
        
js_ensure_mandatory();
      
/regonsubmit;
    
/if;
  
/def;

  
// Ensure that VALUE is not empty or not mandatory.
  
// Report error if mandatory value is missing.
  
// Handy utility for check().
  
def ensure_mandatory value;
    
if this.panel.field_mandatory(this,this.mandatory) && isempty(value);
      
error();
      
return null;
    
/if;
    
return value;
  
/def;

  
// Callback to check and transform the submitted VALUE.
  
// If VALUE is bad, report a field error.
  
// Return the transformed value.
  
// Note: for most fields VALUE is a string, but for some,
  
// e.g. fieldcheckbox, it is an array of strings.
  
def check value;
    
return ensure_mandatory (value); 
  
/def;


  
// main check interface with form.
  
// fields can overwrite fcheck in special sitations,
  
// normally they overwrite check
  
def fcheck in out clvalue scratch;
     val = check (default (in[this.name]));
     
if !isdecl (this.panel.errmsg[this.name]);
        out[this.name] = val; TuDelField (clvalue[this.name]);
     
else 
        clvalue[this.name]=in[this.name];
        TuDelField (out[this.name]);
     
/if;
  
/def;

  
// Return VALUE transformed into internal field state.
  
// Caution: the result is just a string, so don't do funny escapes
  
// for HTML; use format context "A"!
  
def toclvalue value;
    
assign res;
      
if isnull(this.format); ? value "A"else; ? value "A"+this.format; /if;
    
/assign;
    
return res;
  
/def;
/defclass


// Emit a hidden field in the case of a sleform and display fields.
// Contents displays the field's value.
// Call panel.save_value(FIELD.name, VALUE) if SAVE.
defenv _emit_state field value save rest;
  
if save; field.panel.save_value (field.name, value); /if;
  
defbody;
/defenv



// Text Area Field, like HTML textarea control.
defenv fieldtextarea name rows=5 cols=60 wrap=null mandatory=false descr=null errmsg=null
                      state=null panel=null trim=false empty=false env=null format=
"" maxlength=null @rest ...;
  
inherit InlineComponent, _fieldbase; defnowysiwyg; defautoclose;

  
def errmsg_content_too_long;
    
return "Content of field "+this.name+" too long";
 
/def

  
def check value;
    
if this.trim && !isnull(value); value = trim(value); /if;
    
if !this.empty && value==""; value=null; /if;
    
if !isnull(this.maxlength) && len(default(value,"")) &gt; this.maxlength;
      this.panel.error(this.errmsg_content_too_long(), this.name);
   
/if;
    
return ensure_mandatory (value);
  
/def;

  
def jscheck;
    
regonsubmit;
      
js_ensure_mandatory();
      
if !isnull(this.maxlength);
>

        if (control.value != "") { 
          if (control.value.length > <? this.maxlength>) {
            error = "<? errmsg_content_too_long()>\n";
          }
        }
<    /if;
   
/regonsubmit;
  
/def;

  
assign value;
    
defbody;
  
/assign;
  
if isempty (value); value = null; /if;        // don't override with ""
  this.maxlength = maxlength;
  
regfield (panel, name, value, null, mandatory, descr, errmsg, state);
  value = clvalue();
  this.trim = htmlbool(trim);
  this.empty = htmlbool(empty);

  
if this.state=="display";
    
if !isempty(env)><\<?env>></if;
    
? default (value) format;
    
if !isempty(env)><\/<?env>></if;
    this.panel.save_value (name, value);
  
else;
    >
<textarea name=<? this.panel.control_name (name) "P"> <? rows "Pn"> <? cols "Pn"> <? wrap "Pn"> <? maxlength "Pn"> <? rest "Pn"> <if this.state!="enabled"><?this.state></if>><? value></textarea><hei
  
/if;
/defenv


// Text Field, like HTML text input control.
// If PASSWORD, hide the text using a HTML password input control.
def fieldtext name size=null maxlength=null value=null mandatory=false 
        trim=false empty=false descr=null errmsg=null state=null panel=null password=false @rest ...;
  
inherit  InlineComponent,_fieldbase; definpara;

  
// EXTEND jscheck() that trims, mandatory fields that contains spaces are detected on the server
  
def check value;
    
if this.trim && !isnull(value); value = trim(value); /if;
    
if !this.empty && value==""; value=null; /if;
    
return ensure_mandatory (value);
  
/def;

  
regfield (panel, name, value, null, mandatory, descr, errmsg, state);
  this.trim = htmlbool(trim); this.empty = htmlbool(empty);
  value = clvalue();

  
if this.state=="display"
    
_emit_state this value true rest;
      
if !htmlbool(password); ? value; /if;     // EXTEND print stars?
    
/_emit_state;
  
else;
    >
<\input <if htmlbool(password)>type="password"</if<hei
     >
 name=<? this.panel.control_name (name) "P"> <? size "Pn"> <hei
     >
 <? maxlength "Pn"> <? value "Pn"> <? rest "Pn"><hei
     >
 <if this.state!="enabled"><?this.state></if>><hei
  
/if;
/def


// Hidden Field, like HTML hidden input control.
def fieldhidden name value=null panel=null @rest ...;
  
inherit InlineComponent, _fieldbase;

  
regfield (panel, name, value, null, false, null, null, null);
  value = clvalue();

  >
<input type="hidden" name=<? this.panel.control_name (name) "P"> <? value "Pn"> <? rest "Pn">><hei
/def


// Integer Field.
def fieldint name size=null value=null format=null mandatory=false min=null max=null 
    descr=null errmsg=null state=null panel=null @rest ...;
  
inherit InlineComponent, _fieldbase; definpara;

  
def jscheck;
    
regonsubmit>
      <js_ensure_mandatory>
      if (control.value != "") { 
        value = parseInt (control.value, 10);
        //  In JavaScript 1.0, parseInt() may return 0 instead of
        //  NaN, and isNaN() may be missing.  Code for 1.1:
        //  if (isNaN (value)
        //      <if !isnull (this.min)>|| (value &lt; <? this.min>)</if>
        //      <if !isnull (this.max)>|| (value &gt; <? this.min>)</if>) {
        //    error = "<? errormsg()>\n";
        //  }
        // once Javascript 1.0 is totally dead this code can be replaced
        if ((parseInt ("foo") == 0) && (value == 0)) {
          // broken parseInt(), can't tell 0 from NaN, have to skip range check
          break;
        }
        if ((value != value)<   // look ma, it's NaN
            
if !isnull (this.min)>|| (value <\ <? this.min>)</if;
            
if !isnull (this.max)>|| (value >  <? this.max>)</if>) {
          error = "<? errormsg()>\n";
        }
      }
    </regonsubmit;
  
/def;

  
def check value;
    value = ensure_mandatory (value);
    
if isempty(value); return null; /if;
    res = integer (value);
    
if isnull (res)
       || !isnull (this.min) && res < this.min
       || !isnull (this.max) && this.max < res;
      
error();
    
/if;
    
return res;
  
/def;

  
if isnull (descr);
    descr = 
"an integral number";
    
if isnull(min);
      
if !isnull(max);
        descr = descr + 
" less than " + max;
      
/if;
    
elsif isnull(max);
      descr = descr + 
" greater than " + min;
    
else;
      descr = descr + 
" between " + min + " and " + max;
    
/if;
  
/if;
  this.min = integer(min); this.max = integer(max); value=integer(value);
  
regfield (panel, name, value, format, mandatory, descr, errmsg, state);
  value = clvalue();

  
if this.state=="display"
    
_emit_state this value true rest; ? value; /_emit_state;
  
else;
    >
<input name=<? this.panel.control_name (name) "P"> <? size "Pn"> <? value "Pn"> <? rest "Pn"> <if this.state!="enabled"><?this.state></if>><hei
  
/if;
/def


// Real number field.
def fieldreal name size=null value=null format=null mandatory=false min=null max=null 
            prec=null descr=null errmsg=null state=null panel=null @rest ...;
  
inherit InlineComponent, _fieldbase; definpara;

  
def jscheck;
    
regonsubmit>
      <js_ensure_mandatory>
      if (control.value != "") {
        value = parseFloat (control.value);
        //  In JavaScript 1.0, parseFloat() may return 0 instead of
        //  NaN, and isNaN() may be missing.  Code for 1.1:
        //  if (isNaN (value)
        //         <if !isnull (this.min)>|| (value &lt; <? this.min>)</if>
        //         <if !isnull (this.max)>|| (value &gt; <? this.min>)</if>) {
        //       error = "<? errormsg()>\n";
        //  }
        // When Javascript 1.0 is totally dead this code can be replaced
        if ((parseFloat ("foo") == 0) && (value == 0)) {
          // broken parseFloat(), can't tell 0 from NaN, have to skip range check
          break;
        }
        if ((value != value)<   // look ma, it's NaN
            
if !isnull (this.min)>|| (value <\ <? this.min>)</if;
            
if !isnull (this.max)>|| (value >  <? this.max>)</if>) {
          error = "<? errormsg()>\n";
        }
      }
    </regonsubmit;
  
/def;

  
def check value;
    value = ensure_mandatory (value);
    
if isempty(value); return null; /if;
    res = real (value);
    
if isnull (res)
       || !isnull (this.min) && res < this.min
       || !isnull (this.max) && this.max < res;
      
error();
    
/if;
    
return res;
    
// EXTEND  obey prec
  
/def;

  
if isnull(descr);
    descr = 
"a real number";
    
if isnull(min);
      
if !isnull(max);
        descr = descr + 
" less than " + max;
      
/if;
    
elsif isnull(max);
      descr = descr + 
" greater than " + min;
    
else;
      descr = descr + 
" between " + min + " and " + max;
    
/if;
  
/if;
  this.min = real(min);
  this.max = real(max);
  this.prec = prec;
  value = real(value);
  
regfield (panel, name, value, format, mandatory, descr, errmsg, state);
  value = clvalue();

  
// EXTEND obey prec
  
if this.state=="display"
    
_emit_state this value true rest; ? value; /_emit_state;
  
else;
    >
<input name=<? this.panel.control_name (name) "P"> <? size "Pn"> <? value "Pn"> <? rest "Pn"> <if this.state!="enabled"><? this.state></if>><hei
  
/if;
/def

// Emit a checkbox or radio control for FIELD.
// TYPE is either "checkbox" or "radio".
// VALUE, CHECKED, and STATE are as in HTML.
def _emit_checkbox_or_radio field type value checked multiple state rest;
  
if state=="display"
    
_emit_state field value checked rest;
      
if checked; ? "x"/if;
    
/_emit_state;
  
else
   n=field.panel.control_name (field.name);
   
if multiple; n=n+".";/if;
    >
<input <? type "Pn"> name=<? n "P"> <? value "Pn"> <? checked "Pn"> <? rest "Pn"> <if state!="enabled"><?state></if>><
  
/if;
/def


// Field with two values.
def fieldcheckbox name onvalue="t" offvalue="" checked=null state=null panel=null multiple=null 
        multisep=
"" @rest ...;
  
inherit InlineComponent, _fieldbase; definpara;

  
def check value;
    
if isnull (value); return this.offvalue; /if;
    
return this.onvalue;
  
/def;

  
def fcheck in out clvalue scratch;
    v=in[this.name]; 
    
if !this.multiple;
       
if v==this.clval;out [this.name] = this.onvalue; else 
                        out [this.name] = this.offvalue;      
/if; 
    
else
       
if isempty(scratch[this.name]);
          out[this.name]= 
""; scratch[this.name]=true;
       
/if;
       
if (istuple(v) && !isnull (arsearch (v, this.clval)));
                                                
// This one checked? 
          
if !isempty(out [this.name]);
            out [this.name] = out [this.name] + this.multisep + this.onvalue; 
// Yes, add
          
else 
            out [this.name] = this.onvalue;     
// Yes, return its value.
          
/if;
       
/if;
    
/if;
  
/def;

  
def toclvalue value;
    
if htmlbool(this.multiple);
        
if  contains(value,this.onvalue+this.multisep) || 
            contains(value,this.multisep+this.onvalue); 
return "t"/if;
    
/if;
    
if value == this.onvalue; return "t"/if;
    
return null;
  
/def;

  
if isnull (checked);
    value = null;
  
elsif htmlbool(checked);
    value = onvalue;
  
else;
    value = offvalue;
  
/if;
  this.multiple = htmlbool(multiple); this.multisep = multisep;
  
if this.multiple; onvalue=onvalue+""/if;
  this.onvalue = onvalue;
  this.offvalue = offvalue;
  
regfield (panel, name, value, null, false, null, null, state);
  value = clvalue();
  
if isstring (onvalue); this.clval=onvalue; else this.clval="t"/if;
  
_emit_checkbox_or_radio (this, "checkbox", this.clval, !isnull (value), htmlbool(multiple), this.state, rest);
/def


defenv condenabled  negate=null; inherit component; deftranspara;
   
_marker;
   
if ((gl._curpanel.state=="display")&&htmlbool(negate) || 
       (gl._curpanel.state!=
"display")&&!htmlbool(negate));
      
defbody;
   
/if;
   
/_marker;
/defenv

>