includes/clientside/static/acl.js
author Dan
Sun, 04 Jan 2009 00:55:40 -0500
changeset 801 eb8b23f11744
parent 787 b0d0d429d8cf
child 823 4596c40aaa94
permissions -rw-r--r--
Two big commits in one day I know, but redid password storage to use HMAC-SHA1. Consolidated much AES processing to three core methods in session that should handle everything automagically. Installation works; upgrades should. Rebranded as 1.1.6.

// 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;
  
  load_component(['l10n', 'messagebox', 'fadefilter', 'template-compiler', 'jquery', 'jquery-ui', 'autofill']);
  
  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 && ajax.status == 200 )
      {
        var response = String(ajax.responseText + '');
        if ( !check_json_response(response) )
        {
          handle_invalid_json(ajax.responseText);
          return false;
        }
        try {
          var groups = parseJSON(ajax.responseText);
        } catch(e) {
          handle_invalid_json(ajax.responseText);
        }
        __aclBuildWizardWindow();
        if ( groups.mode == 'error' )
        {
          alert(groups.error);
          killACLManager();
          return false;
        }
        aclDataCache = groups;
        __aclBuildSelector(groups);
      }
    }, true);
  return false;
}

function ajaxOpenDirectACLRule(rule_id)
{
  load_component(['l10n', 'messagebox', 'fadefilter', 'template-compiler', 'autofill']);
  
  var params = {
    target_id: rule_id,
    mode: 'seltarget_id'
  };
  params = ajaxEscape(toJSONString(params));
  ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function() {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        var response = String(ajax.responseText + '');
        if ( !check_json_response(response) )
        {
          handle_invalid_json(ajax.responseText);
          return false;
        }
        try
        {
          response = parseJSON(response);
        }
        catch(e)
        {
          handle_invalid_json(response);
        }
        if ( !document.getElementById(aclManagerID) )
        {
          __aclBuildWizardWindow();
          var main = document.getElementById(aclManagerID + '_main');
          main.style.padding = '10px';
        }
        else
        {
          var main = document.getElementById(aclManagerID + '_main');
          main.style.backgroundImage = 'none';
        }
        if ( response.mode == 'error' )
        {
          alert(response.error);
          killACLManager();
          return false;
        }
        aclDataCache = response;
        aclBuildRuleEditor(response, true);
      }
    }, true);
}

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 && ajax.status == 200 )
      {
        document.getElementById(aclManagerID+'_main').innerHTML = '';
        document.getElementById(aclManagerID + '_back').style.display = 'none';
        document.getElementById(aclManagerID + '_next').value = $lang.get('etc_wizard_next');
        groups = parseJSON(ajax.responseText);
        if ( groups.mode == 'error' )
        {
          alert(groups.error);
          killACLManager();
          return false;
        }
        aclDataCache = groups;
        thispage = strToPageID(title);
        groups.page_id = thispage[0];
        groups.namespace = thispage[1];
        __aclBuildSelector(groups);
      }
    }, true);
}

function __aclBuildSelector(groups)
{
  thispage = strToPageID(title);
  do_scopesel = ( thispage[0] == groups.page_id && thispage[1] == groups.namespace );
  
  document.getElementById(aclManagerID + '_next').style.display = 'inline';
  
  seed = Math.floor(Math.random() * 1000000);
        
  main = document.getElementById(aclManagerID + '_main');
  main.style.padding = '10px';
  main.style.backgroundImage = 'none';
  
  // the "edit existing" button
  var editbtn_wrapper = document.createElement('div');
  editbtn_wrapper.style.styleFloat = 'right';
  editbtn_wrapper.style.cssFloat = 'right';
  editbtn_wrapper.style.fontSize = 'smaller';
  var editbtn = document.createElement('a');
  editbtn.href = '#';
  editbtn.innerHTML = $lang.get('acl_btn_show_existing');
  editbtn_wrapper.appendChild(editbtn);
  
  // tracer button
  var tracebtn = document.createElement('a');
  tracebtn.href = '#';
  tracebtn.innerHTML = $lang.get('acl_btn_view_effective');
  editbtn_wrapper.appendChild(document.createElement('br'));
  editbtn_wrapper.appendChild(tracebtn);
  
  main.appendChild(editbtn_wrapper);
  
  editbtn.onclick = function()
  {
    aclSetViewListExisting();
    return false;
  }
  
  tracebtn.onclick = function()
  {
    aclSetViewDebugTools();
    return false;
  }
  
  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($lang.get('acl_radio_usergroup')));
  lbl.style.display = 'block';
  span.appendChild(grpsel);
  
  anoninfo = document.createElement('div');
  anoninfo.className = 'info-box-mini';
  anoninfo.appendChild(document.createTextNode($lang.get('acl_msg_guest_howto')));
  span.appendChild(document.createElement('br'));
  span.appendChild(anoninfo);
  
  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($lang.get('acl_radio_user')));
  lbl2.style.display = 'block';
  
  usrsel = document.createElement('input');
  usrsel.type = 'text';
  usrsel.name = 'username';
  usrsel.className = 'autofill username';
  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');
    scopediv3 = document.createElement('div');
    scopeRadioPage = document.createElement('input');
      scopeRadioPage.type = 'radio';
      scopeRadioPage.name = 'scope';
      scopeRadioPage.value = 'page';
      scopeRadioPage.checked = 'checked';
      scopeRadioPage.className = '1048576';
      if ( groups.page_groups.length > 0 ) scopeRadioPage.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'none'; };
    scopeRadioGlobal = document.createElement('input');
      scopeRadioGlobal.type = 'radio';
      scopeRadioGlobal.name = 'scope';
      scopeRadioGlobal.value = 'global';
      scopeRadioGlobal.className = '1048576';
      if ( groups.page_groups.length > 0 ) scopeRadioGlobal.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'none'; };
    scopeRadioGroup = document.createElement('input');
      scopeRadioGroup.type = 'radio';
      scopeRadioGroup.name = 'scope';
      scopeRadioGroup.value = 'group';
      scopeRadioGroup.className = '1048576';
      if ( groups.page_groups.length > 0 ) scopeRadioGroup.onclick = function() { var id = 'enACL_pgsel_' + this.className; document.getElementById(id).style.display = 'block'; };
    lblPage = document.createElement('label');
      lblPage.style.display = 'block';
      lblPage.appendChild(scopeRadioPage);
      lblPage.appendChild(document.createTextNode($lang.get('acl_radio_scope_thispage')));
    lblGlobal = document.createElement('label');
      lblGlobal.style.display = 'block';
      lblGlobal.appendChild(scopeRadioGlobal);
      lblGlobal.appendChild(document.createTextNode($lang.get('acl_radio_scope_wholesite')));
    lblGroup = document.createElement('label');
      lblGroup.style.display = 'block';
      lblGroup.appendChild(scopeRadioGroup);
      lblGroup.appendChild(document.createTextNode($lang.get('acl_radio_scope_pagegroup')));
    scopediv1.appendChild(lblPage);
    scopediv2.appendChild(lblGroup);
    scopediv3.appendChild(lblGlobal);
    
    scopedesc = document.createElement('p');
    scopedesc.appendChild(document.createTextNode($lang.get('acl_lbl_scope')));
    
    scopePGrp = document.createElement('select');
    scopePGrp.style.marginLeft = '13px';
    scopePGrp.style.display = 'none';
    scopePGrp.id = "enACL_pgsel_1048576";
    
    var opt;
    for ( var i = 0; i < groups.page_groups.length; i++ )
    {
      opt = document.createElement('option');
      opt.value = groups.page_groups[i].id;
      opt.appendChild(document.createTextNode(groups.page_groups[i].name));
      scopePGrp.appendChild(opt);
    }
    
    scopediv2.appendChild(scopePGrp);
    
  }
  
  // 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 = '50px';
  
  head = document.createElement('h2');
  head.appendChild(document.createTextNode($lang.get('acl_lbl_welcome_title')));
  
  desc = document.createElement('p');
  desc.appendChild(document.createTextNode($lang.get('acl_lbl_welcome_body')));
  
  container.appendChild(head);
  container.appendChild(desc);
  container.appendChild(selector);
  
  if(do_scopesel)
  {
    container.appendChild(scopedesc);
    container.appendChild(scopediv1);
    if ( groups.page_groups.length > 0 )
    {
      container.appendChild(scopediv2);
    }
    container.appendChild(scopediv3);
  }
  
  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);
  }
  
  autofill_init_element(usrsel, {
      allow_anon: true
    });
}

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 && ajax.status == 200 )
      {
        var response = String(ajax.responseText + '');
        if ( !check_json_response(response) )
        {
          handle_invalid_json(ajax.responseText);
          return false;
        }
        try
        {
          var data = parseJSON(ajax.responseText);
        }
        catch(e)
        {
          handle_invalid_json(ajax.responseText);
          return false;
        }
        aclDataCache = data;
        switch(data.mode)
        {
          case 'seltarget':
            
            // Build the ACL edit form
            aclBuildRuleEditor(data);
            
            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($lang.get('acl_lbl_save_success_title')));
            note.appendChild(b);
            note.appendChild(document.createElement('br'));
            note.appendChild(document.createTextNode($lang.get('acl_lbl_save_success_body', { target_name: data.target_name })));
            note.appendChild(document.createElement('br'));
            
            /*
            var a = document.createElement('a');
            a.href = '#';
            a.id = aclManagerID + '_btn_dismiss';
            a.appendChild(document.createTextNode('[ ' + $lang.get('acl_btn_success_dismiss') + ' :'));
            note.appendChild(a);
            var a2 = document.createElement('a');
            a2.href = '#';
            a.id = aclManagerID + '_btn_close';
            a2.appendChild(document.createTextNode(': ' + $lang.get('acl_btn_success_close') + ' ]'));
            note.appendChild(a2);
            */
            
            var a_dismiss = document.createElement('a');
            a_dismiss.href = '#';
            a_dismiss.appendChild(document.createTextNode('[ ' + $lang.get('acl_btn_success_dismiss') + ' :'));
            note.appendChild(a_dismiss);
            
            var a_close = document.createElement('a');
            a_close.href = '#';
            a_close.appendChild(document.createTextNode(': ' + $lang.get('acl_btn_success_close') + ' ]'));
            note.appendChild(a_close);
            
            document.getElementById(aclManagerID + '_main').insertBefore(note, document.getElementById(aclManagerID + '_main').firstChild);
            
            a_dismiss.setAttribute('onclick', 'var parent = this.parentNode.parentNode; parent.removeChild(this.parentNode); return false;');
            a_close.setAttribute('onclick', 'killACLManager(); return false;');
            
            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(\'' + $lang.get('acl_msg_deleterule_confirm') + '\')) __aclDeleteRule(); return false;" style="color: red;">' + $lang.get('acl_lbl_deleterule') + '</a></p>';
            
            document.getElementById(aclManagerID+'_main').scrollTop = 0;
            document.getElementById(aclManagerID+'_main').style.backgroundImage = 'none';
                        
            aclDataCache.mode = 'save_edit';
            break;
          case 'delete':
            
            params = {
              'mode' : 'listgroups'
            };
          params = toJSONString(params);
          params = ajaxEscape(params);
          ajaxPost(stdAjaxPrefix+'&_mode=acljson', 'acl_params='+params, function() {
              if ( ajax.readyState == 4 && ajax.status == 200 )
              {
                document.getElementById(aclManagerID+'_main').innerHTML = '';
                document.getElementById(aclManagerID + '_back').style.display = 'none';
                document.getElementById(aclManagerID + '_next').value = $lang.get('etc_wizard_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';
                note.style.position = 'absolute';
                note.style.width = '558px';
                note.id = 'aclSuccessNotice_' + Math.floor(Math.random() * 100000);
                b = document.createElement('b');
                b.appendChild(document.createTextNode($lang.get('acl_lbl_delete_success_title')));
                note.appendChild(b);
                note.appendChild(document.createElement('br'));
                note.appendChild(document.createTextNode($lang.get('acl_lbl_delete_success_body', { target_name: aclDataCache.target_name })));
                note.appendChild(document.createElement('br'));
                a = document.createElement('a');
                a.href = '#';
                a.onclick = function() { opacity(this.parentNode.id, 100, 0, 1000); setTimeout('var div = document.getElementById("' + this.parentNode.id + '"); div.parentNode.removeChild(div);', 1100); return false; };
                a.appendChild(document.createTextNode('[ ' + $lang.get('acl_btn_success_dismiss') + ' :'));
                note.appendChild(a);
                a = document.createElement('a');
                a.href = '#';
                a.onclick = function() { killACLManager(); return false; };
                a.appendChild(document.createTextNode(': ' + $lang.get('acl_btn_success_close') + ' ]'));
                note.appendChild(a);
                document.getElementById(aclManagerID + '_main').insertBefore(note, document.getElementById(aclManagerID + '_main').firstChild);
                //fadeInfoBoxes();
                
              }
            }, true);
            
            break;
          case 'error':
            alert("Server side processing error:\n"+data.error);
            break;
          case 'debug':
            aclDebug(data.text);
            break;
          case 'list_existing':
            aclSetViewListExistingRespond(data);
            break;
          case 'trace':
            aclDrawTraceWrapper(data);
            break;
          default:
            handle_invalid_json(ajax.responseText);
            break;
        }
      }
    }, true);
}

function aclBuildRuleEditor(data, from_direct)
{
  var act_desc = ( data.type == 'new' ) ? $lang.get('acl_lbl_editwin_title_create') : $lang.get('acl_lbl_editwin_title_edit');
  var target_type_t = ( data.target_type == 1 ) ? $lang.get('acl_target_type_group') : $lang.get('acl_target_type_user');
  var target_name_t = data.target_name;
  var scope_type = ( data.page_id == false && data.namespace == false ) ? $lang.get('acl_scope_type_wholesite') : ( data.namespace == '__PageGroup' ) ? $lang.get('acl_scope_type_pagegroup') : $lang.get('acl_scope_type_thispage');
  
  document.getElementById(aclManagerID + '_next').style.display = 'inline';
  
  html = '<h2>'+act_desc+'</h2>';
  html += '<p>' + $lang.get('acl_lbl_editwin_body', { target_type: target_type_t, target: target_name_t, scope_type: scope_type }) + '</p>';
  
  // preset management
  var load_flags = 'href="#" onclick="aclShowPresetLoader(); return false;"';
  var save_flags = 'href="#" onclick="aclShowPresetSave(); return false;"';
  html += '<div style="float: right;">';
  html += $lang.get('acl_btn_edit_presets', { load_flags: load_flags, save_flags: save_flags });
  html += '</div>';
  html += '<div style="clear: both;"></div>';
  
  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();
      if ( data.acl_descs[i].match(/^([a-z0-9_]+)$/) )
      {
        vars['FIELD_DESC'] = $lang.get(data.acl_descs[i]);
      }
      else
      {
        vars['FIELD_DESC'] = data.acl_descs[i];
      }
      vars['FIELD_INHERIT_CHECKED'] = '';
      vars['FIELD_DENY_CHECKED'] = '';
      vars['FIELD_DISALLOW_CHECKED'] = '';
      vars['FIELD_WIKIMODE_CHECKED'] = '';
      vars['FIELD_ALLOW_CHECKED'] = '';
      vars['FIELD_NAME'] = i;
      if ( !data.current_perms[i] )
      {
        data.current_perms[i] = 'i';
      }
      switch(data.current_perms[i])
      {
        case 'i':
        default:
          vars['FIELD_INHERIT_CHECKED'] = 'checked="checked"';
          break;
        case 1:
          vars['FIELD_DENY_CHECKED'] = 'checked="checked"';
          break;
        case 2:
          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(\'' + $lang.get('acl_msg_deleterule_confirm') + '\')) __aclDeleteRule(); return false;" style="color: red;">' + $lang.get('acl_lbl_deleterule') + '</a></p>';
  
  var main = document.getElementById(aclManagerID + '_main');
  main.innerHTML = html;
  
  var form = document.getElementById(aclManagerID + '_formobj_id');
  
  if ( from_direct )
  {
    var modeobj = document.getElementById(aclManagerID + '_mode');
    modeobj.value = 'save_edit';
  }
  else
  {
    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 = $lang.get('etc_save_changes');
}

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(aclDisableTransitionFX, 70, 'acldarkener');
  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 = $lang.get('etc_wizard_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 = $lang.get('etc_wizard_next');
  saver.style.fontWeight = 'bold';
  saver.id = aclManagerID + '_next';
  
  closer = document.createElement('input');
  closer.type = 'button';
  closer.value = $lang.get('etc_cancel_changes');
  closer.onclick = function()
  {
    miniPromptMessage({
      title: $lang.get('acl_msg_closeacl_confirm_title'),
      message: $lang.get('acl_msg_closeacl_confirm_body'),
      buttons: [
        {
          text: $lang.get('acl_btn_close'),
          color: 'red',
          style: {
            fontWeight: 'bold'
          },
          onclick: function(e)
          {
            killACLManager();
            miniPromptDestroy(this);
          }
        },
        {
          text: $lang.get('etc_cancel'),
          onclick: function(e)
          {
            miniPromptDestroy(this);
          }
        }
      ]
    });
    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);
  if ( aclDisableTransitionFX )
  {
    document.getElementById(aclManagerID).style.display = 'block';
    changeOpac(100, aclManagerID);
    changeOpac(100, aclManagerID + '_panel');
  }
  else
  {
    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)
  {
    if ( aclDisableTransitionFX )
    {
      enlighten(true, 'acldarkener');
      el.parentNode.removeChild(el);
    }
    else
    {
      opacity(aclManagerID, 100, 0, 500);
      setTimeout('var el = document.getElementById(aclManagerID); el.parentNode.removeChild(el); enlighten(false, "acldarkener");', 750);
    }
  }
}

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', ['1', '2']));
      if(isNaN(target_type))
      {
        alert($lang.get('acl_err_pleaseselect_targettype'));
        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', ['page', 'group', 'global']);
        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 if(scope == 'group')
        {
          obj['page_id'] = document.getElementById('enACL_pgsel_1048576').value;
          obj['namespace'] = '__PageGroup';
        }
        else
        {
          alert('Invalid scope');
          return false;
        }
      }
      else
      {
        obj['page_id'] = aclDataCache.page_id;
        obj['namespace'] = aclDataCache.namespace;
      }
      if(target_id == '')
      {
        alert($lang.get('acl_err_pleaseselect_username'));
        return false;
      }
      __aclJSONSubmitAjaxHandler(obj);
      break;
    case 'save_edit':
    case 'save_new':
      var form = document.forms[aclManagerID + '_formobj'];
      selections = new Object();
      var dbg = '';
      var warned_everyone = false;
      for(var i in aclPermList)
      {
        selections[aclPermList[i]] = getRadioState(form, aclPermList[i], ['i', 1, 2, 3, 4]);
        // If we're editing permissions for everyone on the entire site and the
        // admin selected to deny privileges, give a stern warning about it.
        if ( selections[aclPermList[i]] == 1 && aclDataCache.target_type == 1 /* ACL_TYPE_GROUP */ && aclDataCache.target_id == 1 && !warned_everyone )
        {
          warned_everyone = true;
          if ( !confirm($lang.get('acl_msg_deny_everyone_confirm')) )
          {
            return false;
          }
        }
        dbg += aclPermList[i] + ': ' + selections[aclPermList[i]] + "\n";
        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;
    case 'trace':
      var params = {
        mode: 'trace',
        user: document.getElementById(aclManagerID + 'trace_user').value,
        page: document.getElementById(aclManagerID + 'trace_page').value
      };
      __aclJSONSubmitAjaxHandler(params);
      break;
    default:
      alert("JSON form submit: invalid mode string "+mode+", stopping execution");
      return false;
      break;
  }
}

function getRadioState(form, name, valArray)
{
  // Konqueror/Safari fix
  if ( form[name] )
  {
    var formitem = form[name];
    if ( String(formitem) == '[object DOMNamedNodesCollection]' || is_Safari )
    {
      var i = 0;
      var radios = new Array();
      var radioids = new Array();
      while(true)
      {
        var elem = formitem[i];
        if ( !elem )
          break;
        radios.push(elem);
        if ( !elem.id )
        {
          elem.id = 'autoRadioBtn_' + Math.floor(Math.random() * 1000000);
        }
        radioids.push(elem.id);
        i++;
      }
      var cr;
      for ( var i = 0; i < radios.length; i++ )
      {
        cr = document.getElementById(radioids[i]);
        if ( cr.value == 'on' || cr.checked == true )
        {
          try {
            return ( typeof ( valArray[i] ) != 'undefined' ) ? valArray[i] : false;
          } catch(e) {
            // alert('Didn\'t get value for index: ' + i);
            return false;
          }
        }
      }
      return false;
    }
  }
  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, valArray)
{
  val = String(val);
  var form = document.forms[aclManagerID + '_formobj'];
  if (!form)
  {
    return false;
  }
  var inputs = form.getElementsByTagName('input');
  var radios = new Array();
  var dbg = '';
  for(var i = 0; i < inputs.length; i++)
  {
    dbg += String(inputs[i]) + "\n";
    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) 
  {
    if ( window.console )
    {
      try{ console.error('ACL editor: can\'t load data cache on delete'); } catch(e) {};
    }
    return false;
  }
  if(aclDataCache.mode != 'seltarget' && aclDataCache.mode != 'save_new' && aclDataCache.mode != 'save_edit')
  {
    if ( window.console )
    {
      try{ console.error('ACL editor: wrong mode on aclDataCache: ' + aclDataCache.mode); } catch(e) {};
    }
    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 aclSetViewListExisting()
{
  if ( !document.getElementById(aclManagerID) )
  {
    return false;
  }
  
  var main = document.getElementById(aclManagerID + '_main');
  main.innerHTML = '';
  main.style.backgroundImage = 'url(' + scriptPath + '/images/loading-big.gif)';
  main.style.backgroundRepeat = 'no-repeat';
  main.style.backgroundPosition = 'center center';
  
  var parms = {
    'mode' : 'list_existing'
  };
  __aclJSONSubmitAjaxHandler(parms);
}

function aclSetViewListExistingRespond(data)
{
  var main = document.getElementById(aclManagerID + '_main');
  main.style.padding = '10px';
  main.innerHTML = '';
  
  var heading = document.createElement('h3');
  heading.appendChild(document.createTextNode($lang.get('acl_msg_scale_intro_title')));
  main.appendChild(heading);
  
  var p = document.createElement('p');
  p.appendChild(document.createTextNode($lang.get('acl_msg_scale_intro_body')));
  main.appendChild(p);
  
  
  main.innerHTML += data.key;
  main.style.backgroundImage = 'none';
  
  document.getElementById(aclManagerID + '_back').style.display = 'inline';
  document.getElementById(aclManagerID + '_next').style.display = 'none';
  
  for ( var i = 0; i < data.rules.length; i++ )
  {
    var rule = data.rules[i];
    // build the rule, this is just more boring DOM crap.
    var div = document.createElement('div');
    div.style.padding = '5px 3px';
    div.style.backgroundColor = '#' + rule.color;
    div.style.cursor = 'pointer';
    div.rule_id = rule.rule_id;
    div.onclick = function()
    {
      var main = document.getElementById(aclManagerID + '_main');
      main.innerHTML = '';
      main.style.backgroundImage = 'url(' + scriptPath + '/images/loading-big.gif)';
      ajaxOpenDirectACLRule(parseInt(this.rule_id));
    }
    div.innerHTML = rule.score_string;
    main.appendChild(div);
  }
}

function aclSetViewDebugTools()
{
  // selection window for viewing effective permissions
  var main = document.getElementById(aclManagerID + '_main');
  main.innerHTML = '';
 
  // set the submission handler to trace
  var thefrm = document.forms[form.name];
  var modeobj = form_fetch_field(thefrm, 'mode');
  modeobj.value = 'trace';
  
  // show the back button
  document.getElementById(aclManagerID + '_back').style.display = 'inline';
  
  //
  // start building
  //
  
  // selection interface
  var selector = document.createElement('div');
  
    var table = document.createElement('table');
    
    // username
    var tr_user = document.createElement('tr');
    var td_user_l = document.createElement('td');
    var lbl_user = document.createElement('label');
    lbl_user.setAttribute('for', aclManagerID + 'trace_user');
    lbl_user.appendChild(document.createTextNode($lang.get('acl_lbl_trace_user')));
    td_user_l.appendChild(lbl_user);
    tr_user.appendChild(td_user_l);
    
    var td_user_i = document.createElement('td');
    var i_user = document.createElement('input');
    i_user.type = 'text';
    i_user.id = aclManagerID + 'trace_user';
    i_user.onkeyup = function() { new AutofillUsername(this, true); };
    i_user.size = '20';
    td_user_i.appendChild(i_user);
    tr_user.appendChild(td_user_i);
    
    table.appendChild(tr_user);
    
    // page
    var tr_page = document.createElement('tr');
    var td_page_l = document.createElement('td');
    var lbl_page = document.createElement('label');
    lbl_page.setAttribute('for', aclManagerID + 'trace_page');
    lbl_page.appendChild(document.createTextNode($lang.get('acl_lbl_trace_page')));
    td_page_l.appendChild(lbl_page);
    tr_page.appendChild(td_page_l);
    
    var td_page_i = document.createElement('td');
    var i_page = document.createElement('input');
    i_page.type = 'text';
    i_page.id = aclManagerID + 'trace_page';
    i_page.onkeyup = function() { new AutofillPage(this); };
    i_page.size = '20';
    td_page_i.appendChild(i_page);
    tr_page.appendChild(td_page_i);
    
    table.appendChild(tr_page);
    
    selector.appendChild(table);
  
  // wrapper
  
  var container = document.createElement('div');
  
    container.style.margin = 'auto';
    container.style.width = '360px';
    container.style.paddingTop = '90px';
    
    var head = document.createElement('h2');
    head.appendChild(document.createTextNode($lang.get('acl_lbl_trace_title')));
    
    var desc = document.createElement('p');
    desc.innerHTML = $lang.get('acl_lbl_trace_body');
    
    container.appendChild(head);
    container.appendChild(desc);
    container.appendChild(selector);
  
  main.appendChild(container);
}

function aclTraceKey()
{
  var div = document.createElement('div');
  $(div).addClass('tblholder');
  var table = document.createElement('table');
  $(table).attr('cellspacing', '1').attr('cellpadding', '4');
  
  var inherit_list = ['enano_default', 'global_everyone', 'global_group', 'global_user', 'pg_everyone', 'pg_group', 'pg_user', 'local_everyone', 'local_group', 'local_user'];
  for ( var i = 0; i < inherit_list.length; i++ )
  {
    var t = inherit_list[i];
    var tr = document.createElement('tr');
    var td_key = document.createElement('td');
    $(td_key).addClass('acl_' + t).addClass('acl_inherit_key');
    tr.appendChild(td_key);
    var td_explain = document.createElement('td');
    $(td_explain).addClass(i % 2 == 0 ? 'row1' : 'row2');
    td_explain.appendChild(document.createTextNode($lang.get('acl_inherit_key_' + t)));
    tr.appendChild(td_explain);
    table.appendChild(tr);
  }
  div.appendChild(table);
  return div;
}

function aclTraceModalKey()
{
  load_component('messagebox');
  miniPrompt(function(parent)
    {
      // heading
      var h3 = document.createElement('h3');
      h3.appendChild(document.createTextNode($lang.get('acl_msg_trace_key')));
      parent.appendChild(h3);
      
      var key = aclTraceKey();
      parent.appendChild(key);
      
      var p = document.createElement('p');
      $(p).css('text-align', 'center');
      
      var closer = document.createElement('a');
      $(closer).addClass('abutton').addClass('abutton_red').css('font-weight', 'bold');
      closer.appendChild(document.createTextNode($lang.get('etc_close')));
      closer.href = '#';
      $(closer).click(function(e)
        {
          miniPromptDestroy(this);
          return false;
        });
      
      p.appendChild(closer);
      parent.appendChild(p);
    });
}

function aclDrawTraceWrapper(data)
{
  // hide the next button
  document.getElementById(aclManagerID + '_next').style.display = 'none';
  
  var trace_by_perm = aclDrawTraceByPerm(data);
  var trace_by_rule = aclDrawTraceByRule(data);
  
  trace_by_perm.id = 'aclDebugTraceViewPerm';
  trace_by_rule.id = 'aclDebugTraceViewRule';
  
  var start_with_rule = ( readCookie('acl_trace_view') == 'rule' );
  
  if ( start_with_rule )
  {
    trace_by_perm.style.display = 'none';
  }
  else
  {
    trace_by_rule.style.display = 'none';
  }
  
  // selection window for viewing effective permissions
  var main = document.getElementById(aclManagerID + '_main');
  main.innerHTML = '';
  
  var wrapper = document.createElement('div');
  $(wrapper).css('padding-bottom', '20px');
  
  var floatlink = document.createElement('div');
  $(floatlink).css('float', 'right').css('margin-left', '20px').css('margin-bottom', '20px').css('text-align', 'right');
  var a_toggle = document.createElement('a');
  $(a_toggle).attr('id', 'aclDebugTraceViewToggle');
  a_toggle.innerHTML = '&raquo; ';
  a_toggle.innerHTML += start_with_rule ? $lang.get('acl_btn_sort_perm') : $lang.get('acl_btn_sort_rule');
  a_toggle.href = '#';
  floatlink.appendChild(a_toggle);
  floatlink.appendChild(document.createElement('br'));
  var a_key = document.createElement('a');
  $(a_key).css('font-size', 'smaller');
  a_key.innerHTML = '&raquo; ';
  a_key.innerHTML += $lang.get('acl_btn_view_key');
  a_key.href = '#';
  floatlink.appendChild(a_key);
  wrapper.appendChild(floatlink);
  
  var h3 = document.createElement('h3');
  h3.appendChild(document.createTextNode($lang.get('acl_msg_debug_main_title')));
  wrapper.appendChild(h3);
  var p = document.createElement('p');
  p.appendChild(document.createTextNode($lang.get('acl_msg_debug_main_body')));
  wrapper.appendChild(p);
  
  wrapper.appendChild(trace_by_perm);
  wrapper.appendChild(trace_by_rule);
  
  main.appendChild(wrapper);
  
  $(a_toggle).click(function(e)
    {
      aclTraceToggleViews();
      return false;
    });
  
  $(a_key).click(function(e)
    {
      aclTraceModalKey();
      return false;
    });
}

function aclTraceToggleViews()
{
  var trace_by_perm = document.getElementById('aclDebugTraceViewPerm');
  var trace_by_rule = document.getElementById('aclDebugTraceViewRule');
  
  var toggler = document.getElementById('aclDebugTraceViewToggle');
  var newtext;
  
  if ( trace_by_perm.style.display == 'none' )
  {
    newtext = $lang.get('acl_btn_sort_rule');
    $(trace_by_rule).hide('blind', {}, 750, function()
      {
        $(trace_by_perm).show('blind', {}, 750);
      });
    createCookie('acl_trace_view', 'perm');
  }
  else
  {
    newtext = $lang.get('acl_btn_sort_perm');
    $(trace_by_perm).hide('blind', {}, 750, function()
      {
        $(trace_by_rule).show('blind', {}, 750);
      });
    createCookie('acl_trace_view', 'rule');
  }
  $(toggler).fadeOut(500, function()
    {
      this.innerHTML = '&raquo; ' + newtext;
      $(this).fadeIn(500);
    });
}

function aclDrawTraceByPerm(data)
{
  var wrapper = document.createElement('div');
  // wrapper.style.display = 'none';
  
  // temporarily append wrapper to body to allow onclick to work
  // var body = document.getElementsByTagName('body')[0];
  // body.appendChild(wrapper);  
  
  for ( var i in data.perms )
  {
    var perm = data.perms[i];
    var item = document.createElement('div');
    item.className = perm.divclass;
    
    // first row - permission name + current setting
    // use innerHTML here to allow for HTML in localized permission types
    item.innerHTML += '<b>' + perm.perm_name + ' - ' + perm.perm_value + '</b>';
    item.appendChild(document.createElement('br'));
    
    // second row - permission localized name + rule ID
    var sm = document.createElement('small');
    sm.innerHTML = perm.perm_src;
    
    item.appendChild(sm);
    
    wrapper.appendChild(item);
    
    // whole row is now in the document
    if ( perm.rule_id != -1 )
    {
      sm.innerHTML += ' [';
      // rule is editable
      var editlink = document.createElement('a');
      editlink.href = 'javascript:ajaxOpenDirectACLRule(' + perm.rule_id + ');';
      editlink.appendChild(document.createTextNode($lang.get('acl_btn_edit_rule')));
      sm.appendChild(editlink);
      sm.innerHTML += ']';
    }
    
    if ( perm.bad_deps.length > 0 )
    {
      var bd = document.createElement('span');
      $(bd).addClass('acl_failed_deps');
      var failed_deps = '';
      for ( var i = 0; i < perm.bad_deps.length; i++ )
      {
        if ( i > 0 )
          failed_deps += ', ';
        failed_deps += data.perms[perm.bad_deps[i]].perm_name;
      }
      var title = document.createElement('span');
      $(title).addClass('title');
      title.appendChild(document.createTextNode($lang.get('acl_msg_failed_deps')));
      bd.appendChild(title);
      bd.appendChild(document.createTextNode(failed_deps));
      
      item.appendChild(document.createElement('br'));
      item.appendChild(bd);
    }
  }
  
  // var ret = wrapper.cloneNode(true);
  // body.removeChild(wrapper);
  // wrapper = false;
  // ret.style.display = 'block';
  // console.debug(ret);
  // return ret;
  return wrapper;
}

function aclDrawTraceByRule(data)
{
  var wrapper = document.createElement('div');
  var groupdata = {};
  
  for ( var i in data.perms )
  {
    var perm = data.perms[i];
    if ( !groupdata[perm['rule_id']] )
    {
      groupdata[perm['rule_id']] = {
        meta: {
          divclass: perm.divclass,
          perm_src: perm.perm_src,
          rule_id: perm.rule_id
        },
        rules: {}
      };
    }
    groupdata[perm['rule_id']]['rules'][i] = perm;
  }
  
  for ( var i in groupdata )
  {
    var group = groupdata[i];
    var grp = document.createElement('div');
    var head = document.createElement('div');
    head.className = group.meta.divclass;
    var span = document.createElement('span');
    span.style.fontSize = 'larger';
    span.appendChild(document.createTextNode(group.meta.perm_src));
    head.appendChild(span);
    if ( group.meta.rule_id != -1 )
    {
      head.innerHTML += ' [';
      // rule is editable
      var editlink = document.createElement('a');
      editlink.href = 'javascript:ajaxOpenDirectACLRule(' + group.meta.rule_id + ');';
      editlink.appendChild(document.createTextNode($lang.get('acl_btn_edit_rule')));
      head.appendChild(editlink);
      head.innerHTML += ']';
    }
    grp.appendChild(head);
    for ( var i in group.rules )
    {
      var rule = group.rules[i];
      var rulediv = document.createElement('div');
      rulediv.style.padding = '3px 12px';
      rulediv.innerHTML += rule.perm_name + ': ';
      var b = document.createElement('strong');
      b.appendChild(document.createTextNode(rule.perm_value));
      rulediv.appendChild(b);
      grp.appendChild(rulediv);
      
      if ( rule.bad_deps.length > 0 )
      {
        var bd = document.createElement('span');
        $(bd).addClass('acl_failed_deps');
        var failed_deps = '';
        for ( var i = 0; i < rule.bad_deps.length; i++ )
        {
          if ( i > 0 )
            failed_deps += ', ';
          failed_deps += data.perms[rule.bad_deps[i]].perm_name;
        }
        var title = document.createElement('span');
        $(title).addClass('title');
        title.appendChild(document.createTextNode($lang.get('acl_msg_failed_deps')));
        bd.appendChild(title);
        bd.appendChild(document.createTextNode(failed_deps));
        
        rulediv.appendChild(document.createElement('br'));
        rulediv.appendChild(bd);
      }
    }
    wrapper.appendChild(grp);
  }
  
  return wrapper;
}

function aclShowPresetLoader()
{
  var prompt = miniPrompt(function(parent)
    {
      parent.innerHTML = '<img style="display: block; margin: 0 auto;" src="' + cdnPath + '/images/loading-big.gif" />';
    });
  var request = toJSONString({
      mode: 'list_presets'
    });
  ajaxPost(stdAjaxPrefix + '&_mode=acljson', 'acl_params=' + ajaxEscape(request), function()
    {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        if ( !check_json_response(ajax.responseText) )
        {
          miniPromptDestroy(prompt);
          return handle_invalid_json(ajax.responseText);
        }
        var response = parseJSON(ajax.responseText);
        if ( response.mode == 'error' )
        {
          alert(response.error);
          miniPromptDestroy(prompt);
          return false;
        }
        prompt = prompt.firstChild.nextSibling;
        prompt.style.textAlign = 'center';
        prompt.innerHTML = '<h3>' + $lang.get('acl_lbl_preset_load_title') + '</h3>';
        
        if ( response.presets.length > 0 )
        {
          // selection box
          var para = document.createElement('p');
          var select = document.createElement('select');
          
          var option = document.createElement('option');
          option.value = '0';
          option.appendChild(document.createTextNode($lang.get('acl_lbl_preset_load')));
          select.appendChild(option);
          
          for ( var i = 0; i < response.presets.length; i++ )
          {
            var preset = response.presets[i];
            var option = document.createElement('option');
            option.value = preset.rule_id;
            option.preset_data = preset;
            option.appendChild(document.createTextNode($lang.get(preset.preset_name)));
            select.appendChild(option);
          }
          
          para.appendChild(select);
          prompt.appendChild(para);
          
          // buttons
          var buttons = document.createElement('p');
          
          // load button
          var btn_load = document.createElement('a');
          btn_load.className = 'abutton abutton_green';
          btn_load.style.fontWeight = 'bold';
          btn_load.appendChild(document.createTextNode($lang.get('acl_btn_load_preset')));
          btn_load.selectobj = select;
          btn_load.onclick = function()
          {
            if ( this.selectobj.value == '0' )
            {
              alert($lang.get('acl_err_select_preset'));
              return false;
            }
            // retrieve preset data
            for ( var i = 0; i < this.selectobj.childNodes.length; i++ )
            {
              if ( this.selectobj.childNodes[i].tagName == 'OPTION' )
              {
                var node = this.selectobj.childNodes[i];
                if ( node.value == this.selectobj.value )
                {
                  aclSetRulesAbsolute(node.preset_data.rules);
                  break;
                }
              }
            }
            miniPromptDestroy(this);
            return false;
          }
          btn_load.href = '#';
          buttons.appendChild(btn_load);
          
          buttons.appendChild(document.createTextNode(' '));
          
          // cancel button
          var btn_cancel = document.createElement('a');
          btn_cancel.className = 'abutton';
          btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
          btn_cancel.onclick = function()
          {
            miniPromptDestroy(this);
            return false;
          }
          btn_cancel.href = '#';
          buttons.appendChild(btn_cancel);
          
          prompt.appendChild(buttons);
        }
        else
        {
          // "no presets"
          prompt.innerHTML += '<p>' + $lang.get('acl_msg_no_presets', { close_flags: 'href="#" onclick="miniPromptDestroy(this); return false;"' }) + '</p>';
        }
      }
    });
}

function aclSetRulesAbsolute(rules)
{
  __aclSetAllRadios('i');
  
  var form = document.forms[aclManagerID + '_formobj'];
  if (!form)
  {
    return false;
  }
  var inputs = form.getElementsByTagName('input');
  var radios = new Array();
  var dbg = '';
  for(var i = 0; i < inputs.length; i++)
  {
    if(inputs[i].type == 'radio')
      radios.push(inputs[i]);
  }
  for(var i in radios)
  {
    if ( typeof(rules[ radios[i]['name'] ]) == 'number' )
    {
      radios[i].checked = ( rules[radios[i]['name']] == radios[i].value );
    }
  }
}

function aclShowPresetSave()
{
  miniPrompt(function(parent)
    {
      parent.style.textAlign = 'center';
      
      parent.innerHTML = '<h3>' + $lang.get('acl_lbl_preset_save_title') + '</h3>';
      var input = document.createElement('input');
      input.id = aclManagerID + '_preset_save';
      input.type = 'text';
      input.size = '30';
      input.onkeypress = function(e)
      {
        // javascript sucks. IE and several others throw myriad errors unless it's done this way.
        if ( e )
        if ( e.keyCode )
        if ( e.keyCode == 13 )
        {
          if ( aclSavePreset() )
          {
            if ( window.opera )
            {
              // damn weird opera bug.
              var input = this;
              setTimeout(function()
                {
                  miniPromptDestroy(input);
                }, 10);
            }
            else
            {
              miniPromptDestroy(this);
            }
          }
        }
        else if ( e.keyCode == 27 )
        {
          miniPromptDestroy(this);
        }
      }
      var para = document.createElement('p');
      para.appendChild(input);
      
      parent.appendChild(para);
      
      // buttons
      var buttons = document.createElement('p');
      
      // save button
      var btn_save = document.createElement('a');
      btn_save.className = 'abutton abutton_green';
      btn_save.style.fontWeight = 'bold';
      btn_save.appendChild(document.createTextNode($lang.get('acl_btn_save_preset')));
      btn_save.selectobj = select;
      btn_save.onclick = function()
      {
        if ( aclSavePreset() )
        {
          miniPromptDestroy(this);
        }
        return false;
      }
      btn_save.href = '#';
      buttons.appendChild(btn_save);
      
      buttons.appendChild(document.createTextNode(' '));
      
      // cancel button
      var btn_cancel = document.createElement('a');
      btn_cancel.className = 'abutton';
      btn_cancel.appendChild(document.createTextNode($lang.get('etc_cancel')));
      btn_cancel.onclick = function()
      {
        miniPromptDestroy(this);
        return false;
      }
      btn_cancel.href = '#';
      buttons.appendChild(btn_cancel);
      
      parent.appendChild(buttons);
      
      var timeout = ( aclDisableTransitionFX ) ? 10 : 1000;
      setTimeout(function()
        {
          input.focus();
        }, timeout);
    });
}

function aclSavePreset()
{
  var input = document.getElementById(aclManagerID + '_preset_save');
  if ( trim(input.value) == '' )
  {
    alert($lang.get('acl_err_preset_name_empty'));
    return false;
  }
  var form = document.forms[aclManagerID + '_formobj'], selections = {};
  var dbg = '';
  var warned_everyone = false;
  for(var i in aclPermList)
  {
    selections[aclPermList[i]] = getRadioState(form, aclPermList[i], ['i', 1, 2, 3, 4]);
    // If we're editing permissions for everyone on the entire site and the
    // admin selected to deny privileges, give a stern warning about it.
    if ( selections[aclPermList[i]] == 1 && aclDataCache.target_type == 1 /* ACL_TYPE_GROUP */ && aclDataCache.target_id == 1 && !warned_everyone )
    {
      warned_everyone = true;
      if ( !confirm($lang.get('acl_msg_deny_everyone_confirm')) )
      {
        return false;
      }
    }
    dbg += aclPermList[i] + ': ' + selections[aclPermList[i]] + "\n";
    if(!selections[aclPermList[i]])
    {
      alert("Invalid return from getRadioState: "+i+": "+selections[i]+" ("+typeof(selections[i])+")");
      return false;
    }
  }
  
  var packet = toJSONString({
      mode: 'save_preset',
      preset_name: input.value,
      perms: selections
    });
  
  var whitey = whiteOutElement(document.getElementById(aclManagerID));
  
  ajaxPost(stdAjaxPrefix + '&_mode=acljson', 'acl_params=' + ajaxEscape(packet), function()
    {
      if ( ajax.readyState == 4 && ajax.status == 200 )
      {
        if ( !check_json_response(ajax.responseText) )
        {
          whitey.parentNode.removeChild(whitey);
          return handle_invalid_json(ajax.responseText);
        }
        var response = parseJSON(ajax.responseText);
        if ( response.mode == 'error' )
        {
          whitey.parentNode.removeChild(whitey);
          alert(response.error);
          return false;
        }
        whiteOutReportSuccess(whitey);
      }
    });
  
  return true;
}

function array_keys(obj)
{
  keys = new Array();
  for(var i in obj)
    keys.push(i);
  return keys;
}