includes/clientside/static/acl.js
author Dan
Thu, 29 Jul 2010 19:30:11 -0400
changeset 1271 77accbee98f5
parent 1227 bdac73ed481e
permissions -rw-r--r--
Pending group memberships no longer alter result in rank alterations

// 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)
{
	// touch these to make them available to child functions
	void(page_id);
	void(namespace);
	
	// require re-auth
	if ( auth_level <= USER_LEVEL_MEMBER )
	{
		load_component(['login', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'crypto', 'messagebox']);
		ajaxDynamicReauth(function(key)
			{
				ajaxOpenACLManager(page_id, namespace);
			}, user_level);
		
		return false;
	}
	
	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(ajax) {
			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(ajax) {
			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(ajax) {
			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 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');
	
	var 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(ajax) {
			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') )
						{
							var p = document.createElement('p');
							p.innerHTML = '<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.id = aclManagerID + '_deletelnk';
							p.style.textAlign = 'right';
							
							document.getElementById(aclManagerID + '_main').appendChild(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(ajax) {
							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');
								ajaxACLSwitchToSelector();
								
								// note
								var note = document.createElement('div');
								note.className = 'info-box-mini';
								note.appendChild(document.createTextNode($lang.get('acl_lbl_delete_success')));
								
								// button: dismiss note
								var a_dismiss = document.createElement('a');
								a_dismiss.href = '#';
								a_dismiss.onclick = function()
								{
									var p = this.parentNode;
									domOpacity(p, 100, 0, 500);
									window.setTimeout(function()
										{
											p.parentNode.removeChild(p);
										}, 600);
									return false;
								}
								a_dismiss.appendChild(document.createTextNode($lang.get('acl_btn_success_dismiss')));
								note.appendChild(a_dismiss);
								// add a space
								note.appendChild(document.createTextNode(' / '));
								
								// button: dismiss note
								var a_close = document.createElement('a');
								a_close.href = '#';
								a_close.onclick = function()
								{
									killACLManager();
									return false;
								}
								a_close.appendChild(document.createTextNode($lang.get('acl_btn_success_close')));
								note.appendChild(a_close);
								
								// style note
								domObjChangeOpac(note, 0);
								note.style.position = 'absolute';
								// icon padding L + icon padding R + icon width + right padding + border width L + border width R
								note.style.width = ($dynano(aclManagerID + '_main').Width() - ( 5 + 5 + 16 + 4 + 1 + 1 )) + 'px';
								
								// make tangible, then calculate height and position right above button panel
								var panel = document.getElementById(aclManagerID + '_panel');
								panel.parentNode.parentNode.appendChild(note);
								note.style.top = '401px';
								note.style.left = '0px';
								
								opacity(note, 0, 100, 500);
							}
						}, 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)
{
	var 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');
	var box = document.createElement('div');
	box.style.width = '640px'
	box.style.height = IE ? '500px' : '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';
	
	var 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';
	
	var panel = document.createElement('div');
	panel.style.width = '620px';
	panel.style.padding = '10px';
	panel.style.lineHeight = '40px';
	panel.style.textAlign = 'right';
	panel.style.position = 'fixed';
	if ( IE )
	{
		panel.style.left = '0px';
		panel.style.top = '440px';
	}
	else
	{
		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';
	
	var 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';
	
	var 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';
	
	var saver = document.createElement('input');
	saver.type = 'submit';
	saver.value = $lang.get('etc_wizard_next');
	saver.style.fontWeight = 'bold';
	saver.id = aclManagerID + '_next';
	
	var 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;
	}
	
	var spacer1 = document.createTextNode('  ');
	var 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);
	
	var 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, 250); opacity('"+aclManagerID + '_panel'+"', 0, 100, 250);", 500);
	}
	
	console.debug(panel);
}

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)
{
	console.debug(form);
	var thefrm = 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[aclManagerID + '_formobj'];
	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(ajax)
		{
			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(ajax)
		{
			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;
}