includes/clientside/static/acl.js
changeset 1 fe660c52c48f
child 40 723bb7acf914
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/clientside/static/acl.js	Wed Jun 13 16:07:17 2007 -0400
@@ -0,0 +1,696 @@
+// Javascript routines for the ACL editor
+
+var aclManagerID = 'enano_aclmanager_' + Math.floor(Math.random() * 1000000);
+var aclPermList = false;
+var aclDataCache = false;
+
+function ajaxOpenACLManager(page_id, namespace)
+{
+  if(IE)
+    return true;
+  if(!page_id || !namespace)
+  {
+    var data = strToPageID(title);
+    var page_id = data[0];
+    var namespace = data[1];
+  }
+  var params = {
+      'mode' : 'listgroups',
+      'page_id' : page_id,
+      'namespace' : namespace
+    };
+  params = toJSONString(params);
+  params = ajaxEscape(params);
+  ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function() {
+      if(ajax.readyState == 4)
+      {
+        __aclBuildWizardWindow();
+        groups = parseJSON(ajax.responseText);
+        aclDataCache = groups;
+        __aclBuildSelector(groups);
+      }
+    });
+  return false;
+}
+
+function ajaxACLSwitchToSelector()
+{
+  params = {
+      'mode' : 'listgroups'
+    };
+  if ( aclDataCache.page_id && aclDataCache.namespace )
+  {
+    params.page_id   = aclDataCache.page_id;
+    params.namespace = aclDataCache.namespace;
+  }
+  params = toJSONString(params);
+  params = ajaxEscape(params);
+  ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function() {
+      if(ajax.readyState == 4)
+      {
+        document.getElementById(aclManagerID+'_main').innerHTML = '';
+        document.getElementById(aclManagerID + '_back').style.display = 'none';
+        document.getElementById(aclManagerID + '_next').value = 'Next >';
+        groups = parseJSON(ajax.responseText);
+        aclDataCache = groups;
+        thispage = strToPageID(title);
+        groups.page_id = thispage[0];
+        groups.namespace = thispage[1];
+        __aclBuildSelector(groups);
+      }
+    });
+}
+
+function __aclBuildSelector(groups)
+{
+  thispage = strToPageID(title);
+  do_scopesel = ( thispage[0] == groups.page_id && thispage[1] == groups.namespace );
+  
+  seed = Math.floor(Math.random() * 1000000);
+        
+  main = document.getElementById(aclManagerID + '_main');
+  main.style.padding = '10px';
+  
+  selector = document.createElement('div');
+  
+  grpsel = __aclBuildGroupsHTML(groups);
+  grpsel.name = 'group_id';
+  
+  span = document.createElement('div');
+  span.id = "enACL_grpbox_"+seed+"";
+  
+  // Build the selector
+  grpb = document.createElement('input');
+  grpb.type = 'radio';
+  grpb.name  = 'target_type';
+  grpb.value = 1; // ACL_TYPE_GROUP
+  grpb.checked = 'checked';
+  grpb.className = seed;
+  grpb.onclick = function() { seed = this.className; document.getElementById('enACL_grpbox_'+seed).style.display = 'block'; document.getElementById('enACL_usrbox_'+seed).style.display = 'none'; };
+  lbl = document.createElement('label');
+  lbl.appendChild(grpb);
+  lbl.appendChild(document.createTextNode('A usergroup'));
+  lbl.style.display = 'block';
+  span.appendChild(grpsel);
+  
+  usrb = document.createElement('input');
+  usrb.type = 'radio';
+  usrb.name  = 'target_type';
+  usrb.value = 2; // ACL_TYPE_USER
+  usrb.className = seed;
+  usrb.onclick = function() { seed = this.className; document.getElementById('enACL_grpbox_'+seed).style.display = 'none'; document.getElementById('enACL_usrbox_'+seed).style.display = 'block'; };
+  lbl2 = document.createElement('label');
+  lbl2.appendChild(usrb);
+  lbl2.appendChild(document.createTextNode('A specific user'));
+  lbl2.style.display = 'block';
+  
+  usrsel = document.createElement('input');
+  usrsel.type = 'text';
+  usrsel.name = 'username';
+  usrsel.onkeyup = function() { ajaxUserNameComplete(this); };
+  usrsel.id = 'userfield_' + aclManagerID;
+  try {
+    usrsel.setAttribute("autocomplete","off");
+  } catch(e) {};
+  
+  span2 = document.createElement('div');
+  span2.id = "enACL_usrbox_"+seed+"";
+  span2.style.display = 'none';
+  span2.appendChild(usrsel);
+  
+  // Scope selector
+  if(do_scopesel)
+  {
+    scopediv1 = document.createElement('div');
+    scopediv2 = document.createElement('div');
+    scopeRadioPage = document.createElement('input');
+      scopeRadioPage.type = 'radio';
+      scopeRadioPage.name = 'scope';
+      scopeRadioPage.value = 'page';
+      scopeRadioPage.checked = 'checked';
+    scopeRadioGlobal = document.createElement('input');
+      scopeRadioGlobal.type = 'radio';
+      scopeRadioGlobal.name = 'scope';
+      scopeRadioGlobal.value = 'global';
+    lblPage = document.createElement('label');
+      lblPage.style.display = 'block';
+      lblPage.appendChild(scopeRadioPage);
+      lblPage.appendChild(document.createTextNode('Only this page'));
+    lblGlobal = document.createElement('label');
+      lblGlobal.style.display = 'block';
+      lblGlobal.appendChild(scopeRadioGlobal);
+      lblGlobal.appendChild(document.createTextNode('The entire website'));
+    scopediv1.appendChild(lblPage);
+    scopediv2.appendChild(lblGlobal);
+    
+    scopedesc = document.createElement('p');
+    scopedesc.appendChild(document.createTextNode('What should this access rule control?'));
+  }
+  
+  // Styles
+  span.style.marginLeft = '13px';
+  span.style.padding = '5px 0';
+  span2.style.marginLeft = '13px';
+  span2.style.padding = '5px 0';
+  
+  selector.appendChild(lbl);
+  selector.appendChild(span);
+  
+  selector.appendChild(lbl2);
+  selector.appendChild(span2);
+  
+  container = document.createElement('div');
+  container.style.margin = 'auto';
+  container.style.width = '360px';
+  container.style.paddingTop = '70px';
+  
+  head = document.createElement('h2');
+  head.appendChild(document.createTextNode('Manage page access'));
+  
+  desc = document.createElement('p');
+  desc.appendChild(document.createTextNode('Please select who should be affected by this access rule.'));
+  
+  container.appendChild(head);
+  container.appendChild(desc);
+  container.appendChild(selector);
+  
+  if(do_scopesel)
+  {
+    container.appendChild(scopedesc);
+    container.appendChild(scopediv1);
+    container.appendChild(scopediv2);
+  }
+  
+  main.appendChild(container);
+  
+  var mode = document.createElement('input');
+  mode.name = 'mode';
+  mode.type = 'hidden';
+  mode.id = aclManagerID + '_mode';
+  mode.value = 'seltarget';
+  
+  var theform = document.getElementById(aclManagerID + '_formobj_id');
+  if ( !theform.mode )
+  {
+    theform.appendChild(mode);
+  }
+  else
+  {
+    theform.removeChild(theform.mode);
+    theform.appendChild(mode);
+  }
+}
+
+var aclDebugWin = false;
+
+function aclDebug(text)
+{
+  if(!aclDebugWin)
+    aclDebugWin = pseudoWindowOpen("data:text/html;plain,<html><head><title>debug win</title></head><body><h1>Debug window</h1></body></html>", "aclDebugWin");
+    setTimeout(function() {
+  aclDebugWin.pre = aclDebugWin.document.createElement('pre');
+  aclDebugWin.pre.appendChild(aclDebugWin.document.createTextNode(text));
+  aclDebugWin.b = aclDebugWin.document.getElementsByTagName('body')[0];
+    aclDebugWin.b.appendChild(aclDebugWin.pre);}, 1000);
+}
+
+var pseudoWindows = new Object();
+
+function pseudoWindowOpen(url, id)
+{
+  if(pseudoWindows[id])
+  {
+    document.getElementById('pseudowin_ifr_'+id).src = url;
+  }
+  else
+  {
+    win = document.createElement('iframe');
+    win.style.position='fixed';
+    win.style.width = '640px';
+    win.style.height = '480px';
+    win.style.top = '0px';
+    win.style.left = '0px';
+    win.style.zIndex = getHighestZ() + 1;
+    win.style.backgroundColor = '#FFFFFF';
+    win.name = 'pseudo_ifr_'+id;
+    win.id = 'pseudowindow_ifr_'+id;
+    win.src = url;
+    body = document.getElementsByTagName('body')[0];
+    body.appendChild(win);
+  }
+  win_obj = eval("( pseudo_ifr_"+id+" )");
+  return win_obj;
+}
+
+function __aclJSONSubmitAjaxHandler(params)
+{
+  params = toJSONString(params);
+  params = ajaxEscape(params);
+  ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function() {
+      if(ajax.readyState == 4)
+      {
+        try {
+          data = parseJSON(ajax.responseText);
+        } catch(e) {
+          aclDebug(e+"\n\nResponse:\n"+ajax.responseText);
+        }
+        aclDataCache = data;
+        switch(data.mode)
+        {
+          case 'seltarget':
+            
+            // Build the ACL edit form
+            // try {
+              act_desc = ( data.type == 'new' ) ? 'Create access rule' : 'Editing permissions';
+              target_type_t = ( data.target_type == 1 ) ? 'group' : 'user';
+              target_name_t = data.target_name;
+              var scope_type = ( data.page_id == false && data.namespace == false ) ? 'this entire site' : 'this page';
+              html = '<h2>'+act_desc+'</h2><p>This panel allows you to edit what the '+target_type_t+' "<b>'+target_name_t+'</b>" can do on <b>' + scope_type + '</b>. Unless you set a permission to "Deny", these permissions may be overridden by other rules.</p>';
+              parser = new templateParser(data.template.acl_field_begin);
+              html += parser.run();
+              
+              cls = 'row2';
+              for(var i in data.acl_types)
+              {
+                if(typeof(data.acl_types[i]) == 'number')
+                {
+                  cls = ( cls == 'row1' ) ? 'row2' : 'row1';
+                  p = new templateParser(data.template.acl_field_item);
+                  vars = new Object();
+                  vars['FIELD_DESC'] = data.acl_descs[i];
+                  vars['FIELD_DENY_CHECKED'] = '';
+                  vars['FIELD_DISALLOW_CHECKED'] = '';
+                  vars['FIELD_WIKIMODE_CHECKED'] = '';
+                  vars['FIELD_ALLOW_CHECKED'] = '';
+                  vars['FIELD_NAME'] = i;
+                  switch(data.current_perms[i])
+                  {
+                    case 1:
+                      vars['FIELD_DENY_CHECKED'] = 'checked="checked"';
+                      break;
+                    case 2:
+                    default:
+                      vars['FIELD_DISALLOW_CHECKED'] = 'checked="checked"';
+                      break;
+                    case 3:
+                      vars['FIELD_WIKIMODE_CHECKED'] = 'checked="checked"';
+                      break;
+                    case 4:
+                      vars['FIELD_ALLOW_CHECKED'] = 'checked="checked"';
+                      break;
+                  }
+                  vars['ROW_CLASS'] = cls;
+                  p.assign_vars(vars);
+                  html += p.run();
+                }
+              }
+              
+              var parser = new templateParser(data.template.acl_field_end);
+              html += parser.run();
+              
+              if(data.type == 'edit')
+                html += '<p id="'+aclManagerID+'_deletelnk" style="text-align: right;"><a href="#delete_acl_rule" onclick="if(confirm(\'Do you really want to delete this rule?\')) __aclDeleteRule(); return false;" style="color: red;">Delete this rule</a></p>';
+              
+              var main = document.getElementById(aclManagerID + '_main');
+              main.innerHTML = html;
+              
+              var form = document.getElementById(aclManagerID + '_formobj_id');
+              
+              var modeobj = form_fetch_field(form, 'mode');
+              if ( modeobj )
+                modeobj.value = 'save_' + data.type;
+              else
+                alert('modeobj is invalid: '+modeobj);
+              
+              aclPermList = array_keys(data.acl_types);
+              
+              document.getElementById(aclManagerID + '_back').style.display = 'inline';
+              document.getElementById(aclManagerID + '_next').value = 'Save Changes';
+              
+            // } catch(e) { alert(e); aclDebug(ajax.responseText); }
+            
+            break;
+          case 'success':
+            var note = document.createElement('div');
+            note.className = 'info-box';
+            note.style.marginLeft = '0';
+            var b = document.createElement('b');
+            b.appendChild(document.createTextNode('Permissions updated'));
+            note.appendChild(b);
+            note.appendChild(document.createElement('br'));
+            note.appendChild(document.createTextNode('The permissions for '+data.target_name+' on this page have been updated successfully.'));
+            note.appendChild(document.createElement('br'));
+            var a = document.createElement('a');
+            a.href = 'javascript:void(0);';
+            a.onclick = function() { this.parentNode.parentNode.removeChild(this.parentNode); return false; };
+            a.appendChild(document.createTextNode('[ dismiss :'));
+            note.appendChild(a);
+            var a2 = document.createElement('a');
+            a2.href = 'javascript:void(0);';
+            a2.onclick = function() { killACLManager(); return false; };
+            a2.appendChild(document.createTextNode(': close manager ]'));
+            note.appendChild(a2);
+            document.getElementById(aclManagerID + '_main').insertBefore(note, document.getElementById(aclManagerID + '_main').firstChild);
+            if(!document.getElementById(aclManagerID+'_deletelnk'))
+              document.getElementById(aclManagerID + '_main').innerHTML += '<p id="'+aclManagerID+'_deletelnk" style="text-align: right;"><a href="#delete_acl_rule" onclick="if(confirm(\'Do you really want to delete this rule?\')) __aclDeleteRule(); return false;" style="color: red;">Delete this rule</a></p>';
+            //fadeInfoBoxes();
+            break;
+          case 'delete':
+            
+            params = {
+              'mode' : 'listgroups'
+            };
+          params = toJSONString(params);
+          params = ajaxEscape(params);
+          ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function() {
+              if(ajax.readyState == 4)
+              {
+                document.getElementById(aclManagerID+'_main').innerHTML = '';
+                document.getElementById(aclManagerID + '_back').style.display = 'none';
+                document.getElementById(aclManagerID + '_next').value = 'Next >';
+                var thispage = strToPageID(title);
+                groups.page_id = thispage[0];
+                groups.namespace = thispage[1];
+                __aclBuildSelector(groups);
+                
+                note = document.createElement('div');
+                note.className = 'info-box';
+                note.style.marginLeft = '0';
+                b = document.createElement('b');
+                b.appendChild(document.createTextNode('Entry deleted'));
+                note.appendChild(b);
+                note.appendChild(document.createElement('br'));
+                note.appendChild(document.createTextNode('The access rules for '+aclDataCache.target_name+' on this page have been deleted.'));
+                note.appendChild(document.createElement('br'));
+                a = document.createElement('a');
+                a.href = '#';
+                a.onclick = function() { this.parentNode.parentNode.removeChild(this.parentNode); return false; };
+                a.appendChild(document.createTextNode('[ dismiss :'));
+                note.appendChild(a);
+                a = document.createElement('a');
+                a.href = '#';
+                a.onclick = function() { killACLManager(); return false; };
+                a.appendChild(document.createTextNode(': close manager ]'));
+                note.appendChild(a);
+                document.getElementById(aclManagerID + '_main').insertBefore(note, document.getElementById(aclManagerID + '_main').firstChild);
+                //fadeInfoBoxes();
+                
+              }
+            });
+            
+            break;
+          case 'error':
+            alert("Server side processing error:\n"+data.error);
+            break;
+          case 'debug':
+            aclDebug(data.text);
+            break;
+          default:
+            alert("Invalid JSON response from server\nMode: "+data.mode+"\nJSON string: "+ajax.responseText);
+            break;
+        }
+      }
+    });
+}
+
+function __aclBuildGroupsHTML(groups)
+{
+  groups = groups.groups;
+  select = document.createElement('select');
+  for(var i in groups)
+  {
+    if(typeof(groups[i]['name']) == 'string' && i != 'toJSONString')
+    {
+      o = document.createElement('option');
+      o.value = groups[i]['id'];
+      t = document.createTextNode(groups[i]['name']);
+      o.appendChild(t);
+      select.appendChild(o);
+    }
+  }
+  return select;
+}
+
+function __aclBuildWizardWindow()
+{
+  darken();
+  box = document.createElement('div');
+  box.style.width = '640px'
+  box.style.height = '440px';
+  box.style.position = 'fixed';
+  width = getWidth();
+  height = getHeight();
+  box.style.left = ( width / 2 - 320 ) + 'px';
+  box.style.top = ( height / 2 - 250 ) + 'px';
+  box.style.backgroundColor = 'white';
+  box.style.zIndex = getHighestZ() + 1;
+  box.id = aclManagerID;
+  box.style.opacity = '0';
+  box.style.filter = 'alpha(opacity=0)';
+  box.style.display = 'none';
+  
+  mainwin = document.createElement('div');
+  mainwin.id = aclManagerID + '_main';
+  mainwin.style.clip = 'rect(0px,640px,440px,0px)';
+  mainwin.style.overflow = 'auto';
+  mainwin.style.width = '620px';
+  mainwin.style.height = '420px';
+  
+  panel = document.createElement('div');
+  panel.style.width = '620px';
+  panel.style.padding = '10px';
+  panel.style.lineHeight = '40px';
+  panel.style.textAlign = 'right';
+  panel.style.position = 'fixed';
+  panel.style.left = ( width / 2 - 320 ) + 'px';
+  panel.style.top = ( height / 2 + 190 ) + 'px';
+  panel.style.backgroundColor = '#D0D0D0';
+  panel.style.opacity = '0';
+  panel.style.filter = 'alpha(opacity=0)';
+  panel.id = aclManagerID + '_panel';
+  
+  form = document.createElement('form');
+  form.method = 'post';
+  form.action = 'javascript:void(0)';
+  form.onsubmit = function() { if(this.username && !submitAuthorized) return false; __aclSubmitManager(this); return false; };
+  form.name = aclManagerID + '_formobj';
+  form.id   = aclManagerID + '_formobj_id';
+  
+  back = document.createElement('input');
+  back.type = 'button';
+  back.value = '< Back';
+  back.style.fontWeight = 'normal';
+  back.onclick = function() { ajaxACLSwitchToSelector(); return false; };
+  back.style.display = 'none';
+  back.id = aclManagerID + '_back';
+  
+  saver = document.createElement('input');
+  saver.type = 'submit';
+  saver.value = 'Next >';
+  saver.style.fontWeight = 'bold';
+  saver.id = aclManagerID + '_next';
+  
+  closer = document.createElement('input');
+  closer.type = 'button';
+  closer.value = 'Cancel Changes';
+  closer.onclick = function() { if(!confirm('Do you really want to close the ACL manager?')) return false; killACLManager(); return false; }
+  
+  spacer1 = document.createTextNode('  ');
+  spacer2 = document.createTextNode('  ');
+  
+  panel.appendChild(back);
+  panel.appendChild(spacer1);
+  panel.appendChild(saver);
+  panel.appendChild(spacer2);
+  panel.appendChild(closer);
+  form.appendChild(mainwin);
+  form.appendChild(panel);
+  box.appendChild(form);
+  
+  body = document.getElementsByTagName('body')[0];
+  body.appendChild(box);
+  setTimeout("document.getElementById('"+aclManagerID+"').style.display = 'block'; opacity('"+aclManagerID+"', 0, 100, 500); opacity('"+aclManagerID + '_panel'+"', 0, 100, 500);", 1000);
+}
+
+function killACLManager()
+{
+  el = document.getElementById(aclManagerID);
+  if(el)
+  {
+    el.parentNode.removeChild(el);
+    enlighten();
+  }
+}
+
+function __aclSubmitManager(form)
+{
+  var thefrm = document.forms[form.name];
+  var modeobj = form_fetch_field(thefrm, 'mode');
+  if ( typeof(modeobj) == 'object' )
+  {
+    var mode = (thefrm.mode.value) ? thefrm.mode.value : 'cant_get';
+  }
+  else
+  {
+    var mode = '';
+  }
+  switch(mode)
+  {
+    case 'cant_get':
+      alert('BUG: can\'t get the state value from the form field.');
+      break;
+    case 'seltarget':
+      var target_type = parseInt(getRadioState(thefrm, 'target_type'));
+      if(isNaN(target_type))
+      {
+        alert('Please select a target type.');
+        return false;
+      }
+      target_id = ( target_type == 1 ) ? parseInt(thefrm.group_id.value) : thefrm.username.value;
+      
+      obj = { 'mode' : mode, 'target_type' : target_type, 'target_id' : target_id };
+      
+      thispage = strToPageID(title);
+      do_scopesel = ( thispage[0] == aclDataCache.page_id && thispage[1] == aclDataCache.namespace );
+      
+      if(do_scopesel)
+      {
+        scope = getRadioState(thefrm, 'scope');
+        if(scope == 'page')
+        {
+          pageid = strToPageID(title);
+          obj['page_id'] = pageid[0];
+          obj['namespace'] = pageid[1];
+        }
+        else if(scope == 'global')
+        {
+          obj['page_id'] = false;
+          obj['namespace'] = false;
+        }
+        else
+        {
+          alert('Invalid scope');
+          return false;
+        }
+      }
+      else
+      {
+        obj['page_id'] = aclDataCache.page_id;
+        obj['namespace'] = aclDataCache.namespace;
+      }
+      if(target_id == '')
+      {
+        alert('Please enter a username.');
+        return false;
+      }
+      __aclJSONSubmitAjaxHandler(obj);
+      break;
+    case 'save_edit':
+    case 'save_new':
+      var form = document.forms[aclManagerID + '_formobj'];
+      selections = new Object();
+      for(var i in aclPermList)
+      {
+        if(i != 'toJSONString' && i != aclPermList.length-1)
+        {
+          selections[aclPermList[i]] = getRadioState(form, aclPermList[i]);
+          if(!selections[aclPermList[i]])
+          {
+            alert("Invalid return from getRadioState: "+i+": "+selections[i]+" ("+typeof(selections[i])+")");
+            return false;
+          }
+        }
+      }
+      obj = new Object();
+      obj['perms'] = selections;
+      obj['mode'] = mode;
+      obj['target_type'] = aclDataCache.target_type;
+      obj['target_id'] = aclDataCache.target_id;
+      obj['target_name'] = aclDataCache.target_name;
+      obj['page_id'] = aclDataCache.page_id;
+      obj['namespace'] = aclDataCache.namespace;
+      __aclJSONSubmitAjaxHandler(obj);
+      break;
+    default:
+      alert("JSON form submit: invalid mode string "+mode+", stopping execution");
+      return false;
+      break;
+  }
+}
+
+function getRadioState(form, name)
+{
+  inputs = form.getElementsByTagName('input');
+  radios = new Array();
+  for(var i in inputs)
+  {
+    if(inputs[i]) if(inputs[i].type == 'radio')
+      radios.push(inputs[i]);
+  }
+  for(var i in radios)
+  {
+    if(radios[i].checked && radios[i].name == name)
+      return radios[i].value;
+  }
+  return false;
+}
+
+function __aclSetAllRadios(val)
+{
+  val = val+'';
+  form = document.forms[aclManagerID + '_formobj'];
+  if (!form)
+    return false;
+  inputs = form.getElementsByTagName('input');
+  radios = new Array();
+  for(var i in inputs)
+  {
+    if(inputs[i].type == 'radio')
+      radios.push(inputs[i]);
+  }
+  for(var i in radios)
+  {
+    if(radios[i].value == val)
+      radios[i].checked = true;
+    else
+      radios[i].checked = false;
+  }
+}
+
+function __aclDeleteRule()
+{
+  if(!aclDataCache) return false;
+  if(aclDataCache.mode != 'seltarget') return false;
+  parms = {
+    'target_type' : aclDataCache.target_type,
+    'target_id' : aclDataCache.target_id,
+    'target_name' : aclDataCache.target_name,
+    'page_id' : aclDataCache.page_id,
+    'namespace' : aclDataCache.namespace,
+    'mode' : 'delete'
+  };
+  __aclJSONSubmitAjaxHandler(parms);
+}
+
+function array_keys(obj)
+{
+  keys = new Array();
+  for(var i in obj)
+    keys.push(i);
+  return keys;
+}
+
+function form_fetch_field(form, name)
+{
+  var fields = form.getElementsByTagName('input');
+  if ( fields.length < 1 )
+    return false;
+  for ( var i = 0; i < fields.length; i++ )
+  {
+    var field = fields[i];
+    if ( field.name == name )
+      return field;
+  }
+  return false;
+}
+