// DMS2.0 :: Dust-Me Selectors
//***************************************************************************
// Copyright 2007 SitePoint Pty Ltd -- http://www.sitepoint.com/
// Written by brothercake -- http://www.brothercake.com/
// MIT License -- http://opensource.org/licenses/mit-license.php
//***************************************************************************



//dust-me selectors browser object
DustMeSelectors_browser = new Object();

//have the extension events been initialised yet this session
DustMeSelectors_browser.beeninitialised = false;



//when the chrome has loaded
window.addEventListener('load', function()
{
	//if the show in status preferene is true
	//show the status bar overlay
	if(DustMeSelectors_preferences.getPreference('boolean', 'showinstatus') == true)
	{
		DustMeSelectors_browser.setUIState('statusoverlay', false);
	}

	//check the preferences for a version number
	//if the value is empty, or it's an older version
	var version = DustMeSelectors_preferences.getPreference('string', 'version');
	if(version == '' || version != DustMeSelectors_preferences.version)
	{
		//open the post-install landing page and set the version preference
		document.getElementById('content').addTab('http://www.sitepoint.com/dustmeselectors/welcome/?v='
			+ DustMeSelectors_preferences.version, null)
		DustMeSelectors_preferences.setPreference('string', 'version', DustMeSelectors_preferences.version)
	}

	//initialize the data saving options
	DustMeSelectors_tools.initialiseDataSavingOptions();

	//get the autorun preference and set the autorun menu items
	var autorun = DustMeSelectors_preferences.getPreference('boolean', 'autorun');
	DustMeSelectors_browser.setUIState('autorun', !autorun);

	//get the disable preference and set the status and menu items
	var disable = DustMeSelectors_preferences.getPreference('boolean', 'disable');
	DustMeSelectors_browser.setUIState('disable', disable);

	//if there's no data in the hosts index, disabled the view and clear menu items
	if(DustMeSelectors_tools.getHostsIndex().length == 0)
	{
		DustMeSelectors_browser.setUIState('view-clearmenu', true);
	}

	//if the extension is not disabled,
	if(!disable)
	{
		//define the openview value that means the view window can open
		DustMeSelectors_browser.openview = true;

		//initialize the extension event listeners
		//passing the true argument to say this came from chrome load
		DustMeSelectors_browser.initialiseEventListeners(true);
	}

}, false);



//bind an unload listener for window management
window.addEventListener('unload', function()
{
	//dialogs windows to check
	var dialogs = ['prefdialog', 'spiderdialog', 'viewdialog'];

	//iterate through the dialogs, and in each case
	//if the dialog is present and open, close it
	for(var i=0; i<dialogs.length; i++)
	{
		if(DustMeSelectors_browser[dialogs[i]] && !DustMeSelectors_browser[dialogs[i]].closed)
		{
			DustMeSelectors_browser[dialogs[i]].close();
		}
	}

	//send a stop command to abandon any running find operation
	DustMeSelectors_browser.stop();

}, false);




//initialise the extension event listeners
DustMeSelectors_browser.initialiseEventListeners = function(chromeload)
{
	//if the extension hasn't yet been initialized this session
	if(!DustMeSelectors_browser.beeninitialised)
	{
		//set the command key property according to platform
		//which is the control key for windows and linux
		//or the command (apple) key for macs
		//we'll be using this for selection handling in the view selectors dialog
		DustMeSelectors_browser.command = 'ctrlKey';
		if(/^mac/i.test(navigator.platform))
		{
			DustMeSelectors_browser.command = 'metaKey';
		}

		//bind a mutation event listener to the content document to listen for the dom ready event
		document.getElementById('appcontent').addEventListener('DOMContentLoaded', function()
		{
			//abandon any running find operation
			DustMeSelectors_browser.stop();

			//if autorun is enabled,
			if(DustMeSelectors_preferences.getPreference('boolean', 'autorun'))
			{
				//kick off a find operation, passing the isautorun parameter
				//to identify that this is an automatic run
				DustMeSelectors_browser.findRedundentSelectors(true);

				//if the mutation preference is enabled
				//tie additional listeners into key DOM mutation events
				if(DustMeSelectors_preferences.getPreference('boolean', 'mutation'))
				{
					window.content.document.addEventListener('DOMNodeInserted', function(e)
						{ DustMeSelectors_tools.handleContentMutation(e.relatedNode); }, false);
					window.content.document.addEventListener('DOMNodeRemoved', function(e)
						{ DustMeSelectors_tools.handleContentMutation(e.relatedNode); }, false);
				}
			}

		}, false);

		//record that the the extension has been initialized this session
		//which prevnts the subsequent addition of multiple listeners
		//when disabling and re-enabling mid session
		DustMeSelectors_browser.beeninitialised = true;
	}
};



//update the control settings
DustMeSelectors_browser.updateControlSettings = function()
{
	//store the content document location object
	this.location = window.content.document.location;

	//store the selectors data preference name
	this.prefname = DustMeSelectors_tools.getSelectorsDataPreferenceName(this.location);
};




//menu command function: confirm set autorun preference
DustMeSelectors_browser.setAutorunPreference = function()
{
	//invert and set the autorun preference
	this.doSetAutorunPreference(true);
};



//actually set autorun preference
DustMeSelectors_browser.doSetAutorunPreference = function(invert)
{
	//get the autorun preference
	var autorun = DustMeSelectors_preferences.getPreference('boolean', 'autorun');

	//if the invert flag is true, set it to the inverse of its current value
	if(invert) { autorun = !autorun; }

	//re-save the preference
	DustMeSelectors_preferences.setPreference('boolean', 'autorun', autorun);

	//set the autorun menu items checked value
	DustMeSelectors_browser.setUIState('autorun', !autorun);
};



//set the disabled preference
DustMeSelectors_browser.setDisabledPreference = function(invert)
{
	//get the disable preference
	var disable = DustMeSelectors_preferences.getPreference('boolean', 'disable');

	//if the invert flag is true, set it to the inverse of its current value
	if(invert) { disable = !disable; }

	//re-save the preference
	DustMeSelectors_preferences.setPreference('boolean', 'disable', disable);

	//set the UI accordingly
	DustMeSelectors_browser.setUIState('disable', disable);

	//if the extension is now enabled, initialize the event listeners
	//passing false as the chrome load argument
	//so that full initialisation happens as though
	//the content document mutation events were already in place
	if(!disable)
	{
		DustMeSelectors_browser.initialiseEventListeners(false);
	}
};




//menu command function: open the preferences window
DustMeSelectors_browser.openPreferencesDialog = function()
{
	//open the preferences dialog
	this.prefdialog = window.openDialog('chrome://dustmeselectors/content/preferences.xul', 'prefwindow', 'centerscreen,resizable=no');
};



//menu command function: confirm clear saved selectors
DustMeSelectors_browser.clearSavedSelectors = function()
{
	//if the clearconfirm preference is true
	//open the clear confirm dialog
	if(DustMeSelectors_preferences.getPreference('boolean', 'clearconfirm'))
	{
		window.openDialog('chrome://dustmeselectors/content/clearconfirm.xul', 'clearconfirm', 'chrome,centerscreen,modal,resizable=no');
	}

	//otherwise just call clear the selectors straight away
	//passing the false argument to say this didn't come from the view dialog
	else
	{
		this.doClearSavedSelectors(false, null);
	}
};



//menu command function: confirm clear saved selectors for selected host
DustMeSelectors_browser.clearSavedSelectorsForSelectedHost = function(targetwindow)
{
	//if the clearconfirm preference is true
	//open the clear confirm for selected host dialog
	//as a child of the specified target window
	if(DustMeSelectors_preferences.getPreference('boolean', 'clearconfirm'))
	{
		targetwindow.openDialog('chrome://dustmeselectors/content/clearconfirmselected.xul', 'clearconfirmselected', 'chrome,centerscreen,modal,resizable=no');
	}

	//otherwise just call clear the selectors straight away
	//passing the true argument to say this came from the view dialog
	else
	{
		this.doClearSavedSelectors(true, null);
	}
};



//menu command function: confirm clear saved selectors for ALL hosts
DustMeSelectors_browser.clearAllSavedSelectors = function()
{
	//open the clearall confirm dialog
	window.openDialog('chrome://dustmeselectors/content/clearallconfirm.xul', 'clearallconfirm', 'chrome,centerscreen,modal,resizable=no');
};



//actually clear saved selectors
DustMeSelectors_browser.doClearSavedSelectors = function(fromdialog, windowobject)
{
	//if this request came from the view dialog
	//we need to clear the object referred to by the hosts menu
	if(fromdialog)
	{
		var prefname = this.viewdialog.document.getElementById('dms-hostsindexmenu')
			.selectedItem.getAttribute('value');
	}

	//otherwise get the current host information
	//and store the prefname locally
	else
	{
		this.updateControlSettings();
		prefname = this.prefname;
	}

	//try to move all files to trash and delete this host from the hostsindex
	//** this isn't ideal - we can't easily enable/disable the host-specific clear menu
	//** because of all the window loading / tab-handling problems
	//** but that means someone could go to delete data that isn't there
	//** and that will be handled, but there's no feedback that nothing happened
	DustMeSelectors_filesystem.moveToTrash(prefname + '.json');
	DustMeSelectors_filesystem.moveToTrash(prefname.replace('selectors', 'log') + '.json');
	DustMeSelectors_filesystem.moveToTrash(prefname.replace('selectors', 'used') + '.json');
	DustMeSelectors_tools.updateHostsIndex(prefname, false)

	//if the viewdialog is present,
	if(this.viewdialog && !this.viewdialog.closed)
	{
		//if the preference name matches the the currently viewing data
		//clear the appropriate UI components
		if(prefname == this.viewdialog.document.getElementById('dms-hostsindexmenu').selectedItem.getAttribute('value'))
		{
			this.clearViewDialog(this.viewdialog.document);
		}

		//otherwise just update the hosts index selector
		else
		{
			this.populateHostsIndexMenu(this.viewdialog.document, false);
		}
	}

	//if this request didnt' come from the view dialog
	//or it did, but the selected host is this host
	if(!fromdialog || prefname == this.prefname)
	{
		//hide the status label
		//** this probably isn't necessary, it should already be hidden
		this.hideStatusLabel();

		//send a stop command in case the find is running when you do this
		this.stop();
	}

	//if we have a window object it means this is clearconfirm dialog with a checkbox
	//so in that case get the checked value and set the clearconfirm preference accordingly
	if(windowobject !== null)
	{
		DustMeSelectors_preferences.setPreference('boolean', 'clearconfirm',
			(windowobject.document.getElementById('dms-clearconfirm-clearconfirm')
				.getAttribute('checked') == 'true' ? true : false));
	}
};



//actually clear all saved selectors
DustMeSelectors_browser.doClearAllSavedSelectors = function()
{
	//move all files to trash
	for(var i=0; i<DustMeSelectors_tools.hostsindex.length; i++)
	{
		DustMeSelectors_filesystem.moveToTrash(DustMeSelectors_tools.hostsindex[i] + '.json');
		DustMeSelectors_filesystem.moveToTrash(DustMeSelectors_tools.hostsindex[i].replace('selectors.', 'log.') + '.json');
		DustMeSelectors_filesystem.moveToTrash(DustMeSelectors_tools.hostsindex[i].replace('selectors.', 'used.') + '.json');
	}

	//empty the hosts array and save empty back to index file
	//we don't want to delete the hosts index, we want to keep it but be empty
	DustMeSelectors_tools.hostsindex = [];
	DustMeSelectors_filesystem.dumpToFile('hostsindex', '');

	//if the viewdialog is present, clear the appropriate UI components
	if(this.viewdialog && !this.viewdialog.closed)
	{
		this.clearViewDialog(this.viewdialog.document);
	}

	//disable the view item and clear menu
	this.setUIState('view-clearmenu', true);

	//hide the status label
	//** this probably isn't necessary, it should already be hidden
	this.hideStatusLabel();

	//send a stop command in case the find is running when you do this
	this.stop();
};



//menu command function: open spider dialog
DustMeSelectors_browser.openSpiderDialog = function()
{
	//disable and suppress the main UI
	//(suppress means disable the disable and preferences items too
	// so that re-enabling manually is impossible)
	this.setUIState('disable', true);
	this.setUIState('suppress', true);

	//set the suppressed flag so that you can't click the icon for a find operation
	//which would otherwise be possible, because it appears that
	//image elements can't be disabled (it just looks that way because it's a gray image)
	this.suppressed = true;

	//open the spider dialog
	this.spiderdialog = window.openDialog('chrome://dustmeselectors/content/spider.xul',
		'spiderdialog', 'width=326,height=130,resizable=no,dialog=no');

	//apply any saved dialog properties
	DustMeSelectors_tools.applyDialogProps(this.spiderdialog, 'spider');
};


//spider dialog closed
DustMeSelectors_browser.spiderDialogClosed = function()
{
	//re-enable and un-suppress the main UI
	this.setUIState('disable', false);
	this.setUIState('suppress', false);

	//and clear the suppressed flag
	this.suppressed = false;
};


//menu command function: view saved selectors
DustMeSelectors_browser.viewSavedSelectors = function(prefname, forcetab, direct)
{
	//if no input preference name is specified or it's null
	//update the control settings and copy the prefname to local value
	if(typeof prefname == 'undefined' || prefname === null)
	{
		this.updateControlSettings();
		var prefname = this.prefname;
	}

	//if no forcetab argument is specified or it's null, default it to false
	if(typeof forcetab == 'undefined' || forcetab === null) { var forcetab = false; }

	//if not direct argument is specified or it's null, default it to false
	if(typeof direct == 'undefined' || direct === null) { var direct = false; }

	//get the selectors object for the selected host
	var selectors = DustMeSelectors_tools.getDataObject(prefname, 'selectors', {});

	//count the number of groups in the selectors object
	var groupcount = 0;
	for(var i in selectors)
	{
		if(!selectors.hasOwnProperty(i)) { continue; }
		groupcount++;
	}

	//if there are no members then we have no stylesheets
	if(groupcount == 0)
	{
		//hide the status label
		//** this probably isn't necessary, it should already be hidden
		this.hideStatusLabel();
	}

	//if we already have an open view dialog
	if(this.viewdialog && !this.viewdialog.closed)
	{
		//call the view dialog loaded method directly
		this.viewDialogLoaded(selectors, groupcount, prefname, forcetab);

		//if this is a direct command, focus the window
		//so it will come to view if requested and hidden
		//but not if called programatticaly
		if(direct) { this.viewdialog.focus(); }
	}

	//otherwise if the openview flag is true we want to open it
	else if(this.openview)
	{
		//define hasevents properties to prevent multiple listeners being added
		this.hasevents = { 'selectors' : false, 'used' : false };

		//open the view selectors dialog
		this.viewdialog = window.openDialog('chrome://dustmeselectors/content/viewdata.xul',
			'viewdialog', 'width=400,height=450,resizable=yes,dialog=no');

		//apply any saved dialog properties
		DustMeSelectors_tools.applyDialogProps(this.viewdialog, 'viewselectors');

		//when the window has loaded, pass the selectors object
		//group count and preference name and forcetab argument to the view dialog loaded method
		this.viewdialog.addEventListener('load', function()
		{
			DustMeSelectors_browser.viewDialogLoaded(selectors, groupcount, prefname, forcetab);

		}, false);
	}

	//set the openview flag to true
	//so that subsequent manual open operations work
	this.openview = true;
};



//view selectors dialog window loaded handler
DustMeSelectors_browser.viewDialogLoaded = function(selectors, groupcount, prefname, forcetab)
{
	//save a reference to the viewdialog document
	//and a reference to the content document in the viewselectors browser
	var viewdoc = this.viewdialog.document;
	var doc = viewdoc.getElementById('dms-viewselectorsbrowser').contentDocument;

	//clear the dialog browsers
	this.clearBrowserDocument('dms-viewselectorsbrowser');
	this.clearBrowserDocument('dms-viewusedbrowser');
	this.clearBrowserDocument('dms-viewlogbrowser');

	//if the forcetab argument is true, preset the selectors tab
	//this will apply during a regular find operation
	//to save the pointlessness of getting log information
	//but during a spider operation it will be false
	//so that a log view is not interrupted when it might be relevant
	if(forcetab) { viewdoc.getElementById('dms-viewtabs').selectedIndex = 0; }

	//populate and preselect the hosts index menu
	this.populateHostsIndexMenu(viewdoc, true);

	//iterate through the hostsindex array
	//and if the prefname matches the currently viewing host
	for(var i=0; i<DustMeSelectors_tools.hostsindex.length; i++)
	{
		if(DustMeSelectors_tools.hostsindex[i] == prefname)
		{
			//pass the document references, the selectors object and its meta-data,
			//a fixed datatype value ('selectors') and the update status flag
			//to the populate selectors information method
			this.populateSelectorsInformation(viewdoc, doc, selectors, groupcount, 'selectors', true);

			//and we're done
			return;
		}
	}

	//if we're still going then this host doesn't exist
	//so disable the save and clear buttons
	viewdoc.getElementById('dms-viewactions-save').setAttribute('disabled', true);
	viewdoc.getElementById('dms-viewactions-clear').setAttribute('disabled', true);

	//and deselect the host selector menu
	viewdoc.getElementById('dms-hostsindexmenu').selectedIndex = -1;
};



//populate the hosts index menu
DustMeSelectors_browser.populateHostsIndexMenu = function(doc, preselect)
{
	//get a reference to the menupopup element
	var menupopup = doc.getElementById('dms-hostsindexmenu').getElementsByTagName('menupopup').item(0);

	//clear all existing items
	while(menupopup.childNodes.length > 0)
	{
		menupopup.removeChild(menupopup.firstChild);
	}

	//sort the hosts array alphabetically
	DustMeSelectors_tools.hostsindex.sort();

	//to compile the menu we need to do several discrete iterations
	//so that we can put the global and local items at the top
	//irrespective of their position in the hostsindex array

	//we also needto remember whether we had either
	//so we know whether to draw a divider before the other items
	var haveglobalorlocal = false;

	//first add a menu item for the global preference, if it exists
	for(var i=0; i<DustMeSelectors_tools.hostsindex.length; i++)
	{
		//if the value is just "selectors" then this is the global preference data
		//stored when per-host saving is off, or from a legacy version
		if(DustMeSelectors_tools.hostsindex[i] === 'selectors')
		{
			//create the menu item and set its value and label
			//using the globalprefs string
			var menuitem = menupopup.appendChild(doc.createElement('menuitem'));
			menuitem.setAttribute('value', DustMeSelectors_tools.hostsindex[i]);
			menuitem.setAttribute('label', DustMeSelectors_tools.bundle.getString('view.globalprefs'));

			//remember that we have a global or local value
			haveglobalorlocal = true;

			//we're done now
			break;
		}
	}

	//then add an item for local file data, if its exists
	for(i=0; i<DustMeSelectors_tools.hostsindex.length; i++)
	{
		//if the value is "@local" then this is local files data
		if(DustMeSelectors_tools.hostsindex[i] === 'selectors.@local')
		{
			//create the menu item and set its value and label
			//using the localfiles string
			var menuitem = menupopup.appendChild(doc.createElement('menuitem'));
			menuitem.setAttribute('value', DustMeSelectors_tools.hostsindex[i]);
			menuitem.setAttribute('label', DustMeSelectors_tools.bundle.getString('view.localfiles'));

			//remember that we have a global or local value
			haveglobalorlocal = true;

			//we're done now
			break;
		}
	}

	//then add a new menu item for each of the subsequent hosts in the hostsindex
	for(i=0; i<DustMeSelectors_tools.hostsindex.length; i++)
	{
		//ignore global and local data in this main iteration
		if(DustMeSelectors_tools.hostsindex[i] === 'selectors'
			|| DustMeSelectors_tools.hostsindex[i] === 'selectors.@local') { continue; }

		//if we haven't already done so, and if we have a global or local value, add a divider
		if(typeof separator == 'undefined' && haveglobalorlocal)
		{
			var separator = menupopup.appendChild(doc.createElement('menuseparator'));
		}

		//create the menu item and set its value and label
		//from the data in the hostsindex array
		var menuitem = menupopup.appendChild(doc.createElement('menuitem'));
		menuitem.setAttribute('value', DustMeSelectors_tools.hostsindex[i]);
		menuitem.setAttribute('label', DustMeSelectors_tools.hostsindex[i]
				.replace('selectors.', '').replace(/\#([0-9]+)/, ':$1'));
	}

	//finally, if we're preselecting, iterate through the created menu
	//to look for the item to preselect
	if(preselect)
	{
		var found = false, items = menupopup.getElementsByTagName('menuitem');
		for(i=0; i<items.length; i++)
		{
			if(items[i].getAttribute('value') == this.prefname)
			{
				menupopup.parentNode.selectedItem = items[i];
				found = true;
				break;
			}
		}
	}
};



//menu command function: respond to view dialog tab selection
//and pass the event to view saved data for host
DustMeSelectors_browser.tabSelect = function(host)
{
	this.viewSavedDataForHost(host, false);
};


//menu command function: view saved data for a stored host
DustMeSelectors_browser.viewSavedDataForHost = function(host, isnew)
{
	//get a reference to the dialog document
	var viewdoc = this.viewdialog.document;

	//if this is a new instance, clear the dialog browsers
	if(isnew)
	{
		this.clearBrowserDocument('dms-viewselectorsbrowser');
		this.clearBrowserDocument('dms-viewusedbrowser');
		this.clearBrowserDocument('dms-viewlogbrowser');
	}

	//get the selected tab in the tabbrowser
	var tab = viewdoc.getElementById('dms-viewtabs').selectedItem;

	//now switch according to its id (which tab is selected)
	switch(tab.id)
	{
		//the selectors tab
		case 'dms-viewtab-selectors' :

			//view the saved selectors for this host
			this.viewSavedSelectorsForHost(host, 'selectors');
			break;

		//the used selectors tab
		case 'dms-viewtab-used' :

			//view the used selectors for this host
			this.viewSavedSelectorsForHost(host, 'used');
			break;

		//the spider log tab
		case 'dms-viewtab-spider' :

			//view the spider log for this host
			this.viewSpiderLogForHost(host);
			break;
	}
};


//view saved selectors for a stored host
DustMeSelectors_browser.viewSavedSelectorsForHost = function(host, datatype)
{
	//get a reference to the dialog document, and the content document in the dialog's browser
	var viewdoc = this.viewdialog.document;
	var doc = viewdoc.getElementById('dms-view' + datatype + 'browser').contentDocument;

	//get the selectors or used object for the selected host
	var data = DustMeSelectors_tools.getDataObject(host, datatype, {});

	//count the data and update the view sumary element
	var totals = { 'groups' : 0, 'sheets' : 0, 'rules' : 0 };
	for(i in data)
	{
		if(!data.hasOwnProperty(i)) { continue; }
		totals.groups++;
		if(typeof data[i] == 'object')
		{
			totals.sheets++;
			for(j in data[i])
			{
				if(!data[i].hasOwnProperty(j)) { continue; }
				if(data[i][j].indexOf('{D}') != -1) { continue; }
				totals.rules++;
			}
		}
	}

	//if the data is not empty update with this information
	//otherwise update with the "no stylesheets" message
	if(totals.sheets > 0)
	{
		this.updateViewSummary(totals.rules, totals.sheets, datatype, null, null);
	}
	else
	{
		this.updateViewSummary(0, 0, datatype, null, DustMeSelectors_tools.bundle.getString('view.none'));
	}

	//get a reference to the document body
	//and if it has any existing child nodes, we're done
	var body = doc.getElementsByTagName('body').item(0);
	if(body.childNodes.length > 0) { return; }

	//pass the document references, the selectors/used object and its meta-data,
	//the datatype and a false update status flag
	//to the populate selectors information method
	this.populateSelectorsInformation(viewdoc, doc, data, totals.groups, datatype, false);
};



//clear the selectors and log dialogs for the selected host
//in response to a "clear this data" command
DustMeSelectors_browser.clearViewDialog = function(doc)
{
	//clear the hosts index menu selected index
	doc.getElementById('dms-hostsindexmenu').selectedIndex = -1;

	//disable the save and clear buttons
	doc.getElementById('dms-viewactions-save').setAttribute('disabled', true);
	doc.getElementById('dms-viewactions-clear').setAttribute('disabled', true);

	//re-populate the hosts index menu
	//but don't preselect any item
	this.populateHostsIndexMenu(doc, false);

	//clear any existing data from the selectors and log information browsers
	this.clearBrowserDocument('dms-viewusedbrowser');
	this.clearBrowserDocument('dms-viewselectorsbrowser');
	this.clearBrowserDocument('dms-viewlogbrowser');

	//clear the view summary element
	this.updateViewSummary(null, null, null, null, null);
};




//view spider log for a stored host
DustMeSelectors_browser.viewSpiderLogForHost = function(host)
{
	//get a reference to the dialog document, and the content document in the dialog's browser
	var viewdoc = this.viewdialog.document;
	var doc = viewdoc.getElementById('dms-viewlogbrowser').contentDocument;

	//get the log object for the selected host
	var log = DustMeSelectors_tools.getDataObject(host, 'log', null);

	//if the log exists, update the view sumary element with its data
	//otherwise update it with the "not spidered" message
	if(log != null)
	{
		this.updateViewSummary(log.pages, log.files, 'log', log.summary, null);
	}
	else
	{
		this.updateViewSummary(0, 0, 'log', null, DustMeSelectors_tools.bundle.getString('spider.nolog'));
	}

	//get a reference to the document body
	//and if it has any existing child nodes, do nothing
	var body = doc.getElementsByTagName('body').item(0);
	if(body.childNodes.length > 0) { return; }

	//pass the references and the log object
	//to the populate log information method
	this.populateLogInformation(viewdoc, doc, log);
};


//begin to populate the selectors information iframe
DustMeSelectors_browser.populateLogInformation = function(viewdoc, doc, log)
{
	//get a reference to the document body
	var body = doc.getElementsByTagName('body').item(0);

	//if it has any existing child nodes, do nothing
	if(body.childNodes.length > 0) { return; }

	//otherwise show the progress dialog
	viewdoc.getElementById('dms-viewprogress').setAttribute('hidden', false);

	//wait a decent moment so the UI has a chance to draw
	//then actually populate the log information view
	window.setTimeout(function()
	{
		DustMeSelectors_browser.doPopulateLogInformation(viewdoc, doc, body, log);

	}, 100);

};


//actually populate the log information iframe
DustMeSelectors_browser.doPopulateLogInformation = function(viewdoc, doc, body, log)
{
	//clear the browser document
	this.clearBrowserDocument('dms-viewlogbrowser');

	//enable the save and clear buttons
	viewdoc.getElementById('dms-viewactions-save').setAttribute('disabled', false);
	viewdoc.getElementById('dms-viewactions-clear').setAttribute('disabled', false);

	//if the log object is null then this site has not been spidered
	if(log == null)
	{
		//so output a message to say so
		var h2 = body.appendChild(doc.createElement('h2'));
		h2.appendChild(doc.createTextNode(DustMeSelectors_tools.bundle.getString('spider.nolog')));
		body.setAttribute('class', 'full');

		//and hide the progress dialog
		viewdoc.getElementById('dms-viewprogress').setAttribute('hidden', true);
	}

	//otherwise we have log data
	else
	{
		//output the original sitemap URL plus summary message if applicable
		var h2 = body.appendChild(doc.createElement('h2'));
		h2.appendChild(
			doc.createTextNode(log.baseurl
				+ (log.summary == 'incomplete'
					? (' ' + DustMeSelectors_tools.bundle.getString('spider.incomplete'))
					: '')
					));

		//output the selectors information
		body.setAttribute('class', 'full');
		var container = body.appendChild(doc.createElement('div'));
		container.setAttribute('class', 'spiderlog');
		var list = container.appendChild(doc.createElement('ol'));
		for(var i in log)
		{
			if(!log.hasOwnProperty(i) || /^(pages|files|summary|baseurl)$/.test(i)) { continue; }
			var item = list.appendChild(doc.createElement('li'));
			item.setAttribute('class', log[i].status);
			var span = item.appendChild(doc.createElement('span'));
			span.appendChild(doc.createTextNode(i));
			if(log[i].message)
			{
				var q = item.appendChild(doc.createElement('q'));
				q.appendChild(doc.createTextNode(log[i].message));
			}
		}
	}

	//either way, now hide the progress dialog
	viewdoc.getElementById('dms-viewprogress').setAttribute('hidden', true);
};


//clear the log or selectors information page
DustMeSelectors_browser.clearBrowserDocument = function(id)
{
	//get a reference to the document body
	//of the document in the dialog's browser
	var body = this.viewdialog.document.getElementById(id).contentDocument
		.getElementsByTagName('body').item(0);

	//clear any existing data
	while(body.childNodes.length > 0)
	{
		body.removeChild(body.firstChild);
	}
	body.setAttribute('class', '');
};


//begin to populate the selectors information iframe
DustMeSelectors_browser.populateSelectorsInformation = function(viewdoc, doc, selectors, groupcount, datatype, updatestatus)
{
	//get a reference to the document body
	var body = doc.getElementsByTagName('body').item(0);

	//if it has any existing child nodes, do nothing
	if(body.childNodes.length > 0) { return; }

	//otherwise show the progress dialog
	viewdoc.getElementById('dms-viewprogress').setAttribute('hidden', false);

	//wait a decent moment so the UI has a chance to draw
	//then actually populate the selectors information view
	window.setTimeout(function()
	{
		DustMeSelectors_browser.doPopulateSelectorsInformation(viewdoc, doc, selectors, groupcount, datatype, updatestatus);

	}, 100);
};


//actually populate the selectors information iframe
DustMeSelectors_browser.doPopulateSelectorsInformation = function(viewdoc, doc, selectors, groupcount, datatype, updatestatus)
{
	//get a reference to the document body
	var body = doc.getElementsByTagName('body').item(0);

	//clear the browser document
	this.clearBrowserDocument('dms-view' + datatype + 'browser');

	//enable the save and clear buttons
	viewdoc.getElementById('dms-viewactions-save').setAttribute('disabled', false);
	viewdoc.getElementById('dms-viewactions-clear').setAttribute('disabled', false);

	//if we have no groups then there are no stylesheets
	if(groupcount == 0)
	{
		//so output a message to say so
		var h2 = body.appendChild(doc.createElement('h2'));
		h2.appendChild(doc.createTextNode(DustMeSelectors_tools.bundle.getString('view.none')));
		body.setAttribute('class', 'full');
	}

	//otherwise we have selectors data
	else
	{
		//output the selectors information
		//along the way compiling the data we need for the viewsummary element
		body.setAttribute('class', 'full');
		var totals = { 'sheets' : 0, 'rules' : 0 };
		for(var i in selectors)
		{
			if(!selectors.hasOwnProperty(i)) { continue; }

			//URL header
			h2 = body.appendChild(doc.createElement('h2'));
			h2.appendChild(doc.createTextNode(i));
			h2.setAttribute('tabindex', '0');

			//group container
			var group = body.appendChild(doc.createElement('div'));

			//populate selectors group, returning the number of rules and sheets
			//with which to update the view summary element
			var subtotals = this.populateSelectorsGroup(doc, selectors, group, i, datatype);
			totals.sheets += subtotals.sheets;
			totals.rules += subtotals.rules;
		}

		//update the summary description element
		this.updateViewSummary(totals.rules, totals.sheets, datatype, null, null);

		//define the lastselection array which will store
		//object references to the items that are selected
		//and a selectiongroup property that store the group containing the selections
		//which is used to prevent selections across groups
		this.lastselection = [];
		this.selectiongroup = null;

		//define a property for storing the currently focused element
		//which we'll use for manipulating focus when deleting items with the keyboard
		this.hasfocus = null;

		//define a propery for storing whether the context menu was keyboard opened
		//which we'll use for preventing multiple actions when pressing enter on it
		//because that enter also fires on the underlying target
		this.keymenuopen = false;

		//defining a property for storing whether drag selection is enabled
		this.dragselect = false;

		//if these listeners have not been defined
		if(this.hasevents[datatype] == false)
		{
			this.hasevents[datatype] = true;

			//bind a mousedown handler to the document to handle click selection
			//and to enabled drag selection (if the mousedown happens inside a selectors list)
			doc.addEventListener('mousedown', function(e)
			{
				DustMeSelectors_browser.viewSelectionHandler(e, doc, viewdoc, datatype);
			}, false);

			//bind a mouseover handler to handle drag selection (if enabled)
			doc.addEventListener('mouseover', function(e)
			{
				if(DustMeSelectors_browser.dragselect == true)
				{
					DustMeSelectors_browser.viewSelectionHandler(e, doc, viewdoc, datatype);
				}
			}, false);

			//bind a mouseup and handler to disable drag selection
			doc.addEventListener('mouseup', function(e)
			{
				DustMeSelectors_browser.dragselect = false;
			}, false);

			//and a mouseout handler when the mouse leaves the document, likewise
			doc.addEventListener('mouseout', function(e)
			{
				if(e.relatedTarget == null)
				{
					DustMeSelectors_browser.dragselect = false;
				}
			}, false);

			//bind a contextmenu listener to the document to handle the context menu
			doc.addEventListener('contextmenu', function(e)
			{
				DustMeSelectors_browser.viewContextHandler(e, doc, viewdoc, datatype);
			}, false);

			//bind a keyup listener to handle the tab and arrow keys, for navigation and selection
			doc.addEventListener('keyup', function(e)
			{
				DustMeSelectors_browser.viewSelectionHandler(e, doc, viewdoc, datatype);
			}, false);
		}
	}

	//either way, now hide the progress dialog
	viewdoc.getElementById('dms-viewprogress').setAttribute('hidden', true);
};

//populate a single group within the view document
DustMeSelectors_browser.populateSelectorsGroup = function(doc, selectors, group, i, datatype)
{
	//count the data in this group
	var count = 0, totals = { 'sheets' : 0, 'rules' : 0 };

	//a group containing 0 or more selectors
	if(typeof selectors[i] == 'object')
	{
		totals.sheets++;
		var havedeleted = false;
		for(var j in selectors[i])
		{
			if(!selectors[i].hasOwnProperty(j)) { continue; }

			//don't include items marked deleted
			//but remember we had some so we can add the star mark
			if(selectors[i][j].indexOf('{D}') != -1)
			{
				havedeleted = true;
				continue;
			}

			totals.rules++;
			count++;
		}
		if(count == 0)
		{
			group.className = 'group ' + (datatype == 'used' ? 'warning' : 'okay');
			var h3 = group.appendChild(doc.createElement('h3'));
			h3.className = datatype == 'used' ? 'warning' : 'okay';
			h3.appendChild(doc.createTextNode(DustMeSelectors_tools.bundle
				.getString('view.' + (datatype == 'used' ? 'noused' : 'okay'))));

			//add the mark class if this group is empty
			//because it has deleted selectors
			if(havedeleted == true) { group.previousSibling.className = 'mark'; }
		}
		else
		{
			group.className = 'group ' + (datatype == 'used' ? 'okay' : 'warning');
			h3 = group.appendChild(doc.createElement('h3'));
			h3.className = datatype == 'used' ? 'okay' : 'warning';
			h3.setAttribute('tabindex', '0');
			h3.appendChild(doc.createTextNode(DustMeSelectors_tools.bundle
				.getString('view.' + (datatype == 'used' ? 'used' : 'warning'))
				.replace('%1', count)
				.replace('%P1', (count == 1 ? '' : DustMeSelectors_tools.bundle.getString('view.plural')))
				));
			var list = group.appendChild(doc.createElement('ul'));
			for(var j in selectors[i])
			{
				if(!selectors[i].hasOwnProperty(j)) { continue; }

				//don't include deleted items, but if we find any
				//add the mark class to the heading
				if(selectors[i][j].indexOf('{D}') != -1)
				{
					group.previousSibling.className = 'mark';
					continue;
				}

				var item = list.appendChild(doc.createElement('li'));
				item.setAttribute('tabindex', '0');
				var thisselector = selectors[i][j].split('{');
				//make the selector a <q>, which is relevant semantics i think
				//but particularly useful here because we can use
				//its cite attribute to store the URI of the relating stylesheet
				//(its index in the selectors object)
				//and its class attribute to store its index in the group object
				var span = item.appendChild(doc.createElement('q'));
				span.setAttribute('cite', i);
				span.className = 'j' + j;
				span.appendChild(doc.createTextNode(thisselector[0]));
				//add a line number <cite> if we have the data
				if(thisselector.length > 1 && /^([0-9]+\})/.test(thisselector[1]))
				{
					var line = item.appendChild(doc.createElement('cite'));
					line.appendChild(doc.createTextNode(DustMeSelectors_tools.bundle.getString('view.line')
						.replace('%1', parseInt(thisselector[1], 10))
						));
				}
			}

			//bind the toggle class name for indicator
			h3.className += ' toggle';
		}
	}
	//an error message
	else
	{
		group.className += ' error';
		h3 = group.appendChild(doc.createElement('h3'));
		h3.className = 'error';
		h3.appendChild(doc.createTextNode(selectors[i]));
	}

	//return the totals object
	return totals;
};


//toggle group display
DustMeSelectors_browser.toggleGroupDisplay = function(heading)
{
	//get a reference to the group list, if any,
	//and hide or show it as applicable
	var list = heading.nextSibling;
	if(list)
	{
		if(list.style.display == 'none')
		{
			list.style.display = 'block';
			heading.className = heading.className.replace(/( closed)/g, '');
		}
		else
		{
			list.style.display = 'none';
			heading.className += ' closed';
		}
	}
};


//view dialog selection handler
DustMeSelectors_browser.viewSelectionHandler = function(e, doc, viewdoc, datatype)
{
	//save the target node
	var target = e.target;

	//save a reference to the context menu popup
	var popup = viewdoc.getElementById('dms-view' + datatype + 'context');

	//if this is a keyup event
	if(e.type == 'keyup')
	{
		//if this is tab key
		if(e.keyCode == 9)
		{
			//clear all existing selections fully
			DustMeSelectors_browser.clearAllSelectedTargets(false);

			//if the target is a list item, select or unselect it as applicable
			if(e.target.nodeName.toLowerCase() == 'li')
			{
				if(target.className.indexOf('selected') == -1)
				{
					DustMeSelectors_browser.selectTarget(target.firstChild, 'selected');
				}
				else
				{
					DustMeSelectors_browser.clearSelectedTarget(target.firstChild);
				}
			}

			//store this item as current focus
			DustMeSelectors_browser.hasfocus = target;

			//clear the open flag
			DustMeSelectors_browser.keymenuopen = false;
		}

		//if this is the up or down arrow key and the shift key is pressed
		if((e.keyCode == 38 || e.keyCode == 40) && e.shiftKey)
		{
			//select the next node to move to, either the previous sibling for up arrow
			//or the next sibling for down arraw, which will be null if there isn't one
			if(e.keyCode == 38)
			{
				var sibling = DustMeSelectors_browser
					.lastselection[DustMeSelectors_browser.lastselection.length - 1].parentNode.previousSibling;
			}
			else
			{
				sibling = DustMeSelectors_browser
					.lastselection[DustMeSelectors_browser.lastselection.length - 1].parentNode.nextSibling;
			}

			//if we have a sibling
			if(sibling != null)
			{
				//if we don't have a stored selection group, or it matches this target
				if(DustMeSelectors_browser.selectiongroup == null
					|| DustMeSelectors_browser.selectiongroup == sibling.parentNode.parentNode)
				{
					//move focus to it so that we can continue to tab from it
					sibling.focus();

					//save its first child selector as view target
					DustMeSelectors_browser.viewtarget = sibling.firstChild;

					//select or unselect this node as appropriate
					if(sibling.className.indexOf('selected') == -1)
					{
						DustMeSelectors_browser.selectTarget(sibling.firstChild, 'selected');
					}
					else
					{
						DustMeSelectors_browser.clearSelectedTarget(sibling.firstChild);
					}
				}
			}

			//prevent default action to stop text range selection
			//** not convinced this actually works, but it doesn't predictably not work either!
			e.preventDefault();
		}

		//if this is enter key
		if(e.keyCode == 13)
		{
			//if the menu is already open, do nothing here
			if(DustMeSelectors_browser.keymenuopen == true) { return true; }

			//if the target is a group heading, fire its toggle method and we're done
			//so it will open and close as though clicked on, and we're done
			if(target.nodeName.toLowerCase() == 'h3')
			{
				DustMeSelectors_browser.toggleGroupDisplay(target);
				return true;
			}

			//get a reference to this browser element and its current screen position
			var browser = viewdoc.getElementById('dms-view' + datatype + 'browser');
			var browserpos = { 'left' : browser.boxObject.screenX, 'top' : browser.boxObject.screenY };

			//get the position of the target relative to its containing document
			var targetpos = { 'left' : target.offsetLeft, 'top' : target.offsetTop };
			var tmp = target.offsetParent;
			while(tmp)
			{
				targetpos.left += tmp.offsetLeft;
				targetpos.top += tmp.offsetTop;
				tmp = tmp.offsetParent;
			}

			//add the target height to its top position
			//so that the popup will be anchored underneath it
			targetpos.top += target.offsetHeight;

			//subtract the browser window scrolling offsets
			targetpos.left -= browser.contentWindow.pageXOffset;
			targetpos.top -= browser.contentWindow.pageYOffset;

			//open the context menu popup at the computed position
			popup.showPopup(browser, (browserpos.left + targetpos.left), (browserpos.top + targetpos.top));

			//set the open flag
			DustMeSelectors_browser.keymenuopen = true;

			//save the view target, dialog document and browser document properties
			DustMeSelectors_browser.viewtarget = target.firstChild;
			DustMeSelectors_browser.viewdocument = viewdoc;
			DustMeSelectors_browser.document = doc;

			//if the event target was a list items
			if(target.nodeName.toLowerCase() == 'li')
			{
				//enable the delete item for all selectors, and disable the restore item
				DustMeSelectors_browser.toggleViewContextItems('mark', false, datatype);
				DustMeSelectors_browser.toggleViewContextItems('restore', true, datatype);

				//then enable the restore item if we have deleted selectors in this group
				if(DustMeSelectors_browser.haveDeletedSelectors(DustMeSelectors_browser.viewtarget.getAttribute('cite')))
				{
					DustMeSelectors_browser.toggleViewContextItems('restore', false, datatype);
				}
			}

			//otherwise it must be a URL heading
			else
			{
				//save the heading as the new target
				DustMeSelectors_browser.viewtarget = target;

				//half-select all the items in the group
				//which is the same as selected programatically, but visually different via CSS
				var items = DustMeSelectors_browser.viewtarget.nextSibling.getElementsByTagName('li');
				for(var i=0; i<items.length; i++)
				{
					DustMeSelectors_browser.selectTarget(items[i].firstChild, 'half-selected');
				}

				//disable all items
				DustMeSelectors_browser.toggleViewContextItems('mark,restore', true, datatype);

				//enable the delete item for all selectors if this group is not empty
				if(items.length > 0)
				{
					DustMeSelectors_browser.toggleViewContextItems('markall', false, datatype);
				}

				//enable the restore item if we have deleted selectors in this group
				if(DustMeSelectors_browser.haveDeletedSelectors(DustMeSelectors_browser.viewtarget.firstChild.nodeValue))
				{
					DustMeSelectors_browser.toggleViewContextItems('restore', false, datatype);
				}
			}

			//DustMeSelectors_browser.markSelector(datatype);
		}

		//if this is the delete key
		if(e.keyCode == 8)
		{
			//if the event target was a list items and its selected
			if(target.nodeName.toLowerCase() == 'li' && target.className.indexOf('selected') != -1)
			{
				//save the view target, dialog document and browser document properties
				DustMeSelectors_browser.viewtarget = target.firstChild;
				DustMeSelectors_browser.viewdocument = viewdoc;
				DustMeSelectors_browser.document = doc;

				//initiate the mark-un|selected action
				DustMeSelectors_browser.markSelector(datatype);
			}
		}
	}

	//or if this is a mouseover event
	else if(e.type == 'mouseover')
	{
		//ignore events leaving <cite> line numbers or <q> selectors back onto list item
		if(/^(q|cite)$/i.test(e.relatedTarget.nodeName)) { return true; }

		//whenever the event target is a list-item
		//select the unselected, and unselect the selected
		if(target.nodeName.toLowerCase() == 'li')
		{
			//if we don't have a stored selection group, or it matches this target
			if(DustMeSelectors_browser.selectiongroup == null
				|| DustMeSelectors_browser.selectiongroup == target.parentNode.parentNode)
			{
				if(target.className.indexOf('selected') == -1)
				{
					DustMeSelectors_browser.selectTarget(target.firstChild, 'selected');
				}
				else
				{
					DustMeSelectors_browser.clearSelectedTarget(target.firstChild);
				}
			}
		}

		//and we're done
		return true;
	}

	//or if this is a mousedown event on the left button
	else if(e.type == 'mousedown' && e.which == 1)
	{
		//if the target is a group header
		//fire the open/close toggle, and we're done
		if(target.nodeName.toLowerCase() == 'h3')
		{
			DustMeSelectors_browser.toggleGroupDisplay(target);
			return false;
		}

		//enable drag selection if this target is inside a list
		var node = target;
		while(node.nodeName.toLowerCase() != 'ul')
		{
			if(!node.parentNode) { break; }
			else { node = node.parentNode; }
		}
		if(node.nodeName.toLowerCase() == 'ul')
		{
			DustMeSelectors_browser.dragselect = true;
		}

		//store the event target property, converted to target <q> selector
		//if we don't get a node back just clear selected targets fully and we're done
		DustMeSelectors_browser.viewtarget = DustMeSelectors_browser.getTargetSelector(target);
		if(DustMeSelectors_browser.viewtarget == null)
		{
			DustMeSelectors_browser.clearAllSelectedTargets(false);
			return false;
		}

		//detect option modifiers - shift for range selection
		//and ctrl/cmd for multiple selection, as per platform
		//the double-if is to that cmd takes precedence if both are pressed
		var option = 'none';
		if(e.shiftKey) { option = 'range'; }
		if(e[DustMeSelectors_browser.command]) { option = 'multiple'; }

		//if we're not using an option, clear all selected targets fully
		if(option == 'none')
		{
			DustMeSelectors_browser.clearAllSelectedTargets(false);
		}

		//if we are using an option, only clear half-selections
		else
		{
			DustMeSelectors_browser.clearAllSelectedTargets(true);
		}

		//if the parent list item is already highlighted
		if(DustMeSelectors_browser.viewtarget.parentNode.className.indexOf('selected') != -1)
		{
			//if the "multiple" option is in use
			//de-highlight the parent list-item
			//and remove this target from the lastselection array
			if(option == 'multiple')
			{
				DustMeSelectors_browser.clearSelectedTarget(DustMeSelectors_browser.viewtarget);
			}
		}

		//if it's not already highlighted
		else
		{
			//if we don't have a stored selection group, or it matches this target
			if(DustMeSelectors_browser.selectiongroup == null
				|| DustMeSelectors_browser.selectiongroup == DustMeSelectors_browser.viewtarget.parentNode.parentNode.parentNode)
			{
				//highlight the parent list-item
				//and add this target to the lastselection array
				DustMeSelectors_browser.selectTarget(DustMeSelectors_browser.viewtarget, 'selected');
			}
		}

		//if the "range" option is in use
		if(option == 'range')
		{
			//if we don't have a stored selection group, or it matches this target
			if(DustMeSelectors_browser.selectiongroup == null
				|| DustMeSelectors_browser.selectiongroup == DustMeSelectors_browser.viewtarget.parentNode.parentNode.parentNode)
			{
				//iterate to find the extent of items that are now|already selected
				//(ie. between this selection and the previous one, but they might be in reverse order
				//so we need to sort once we have the indexes)
				var nowselected = [-1, -1], list = DustMeSelectors_browser.viewtarget.parentNode.parentNode;
				var items = list.getElementsByTagName('li');
				for(i=0; i<items.length; i++)
				{
					if(items[i].firstChild == DustMeSelectors_browser.lastselection[DustMeSelectors_browser.lastselection.length - 2])
					{
						nowselected[0] = i;
					}
					if(items[i].firstChild == DustMeSelectors_browser.viewtarget)
					{
						nowselected[1] = i;
					}
				}
				nowselected.sort(function(a, b) { return a - b; });

				//then iterate between those items and select the unselected ones
				for(i=nowselected[0]; i<=nowselected[1]; i++)
				{
					if(items[i].className.indexOf('selected') == -1)
					{
						DustMeSelectors_browser.selectTarget(items[i].firstChild, 'selected');
					}
				}
			}
		}
	}
};


//view dialog context menu handler
DustMeSelectors_browser.viewContextHandler = function(e, doc, viewdoc, datatype)
{
	//if the target is a URL heading, store it
	if(e.target.nodeName.toLowerCase() == 'h2')
	{
		DustMeSelectors_browser.viewtarget = e.target;
	}

	//otherwise store the event target property, converted to target <q> selector
	//if we don't get a node back, clear all targets fully, suppress the default action and we're done
	else
	{
		DustMeSelectors_browser.viewtarget = DustMeSelectors_browser.getTargetSelector(e.target);
		if(DustMeSelectors_browser.viewtarget == null)
		{
			DustMeSelectors_browser.clearAllSelectedTargets(false);
			e.preventDefault();
			return false;
		}
	}

	//if we're still going, also save the dialog document and inner content document
	//for use by the browser element's actual context menu that subsequently appears
	DustMeSelectors_browser.viewdocument = viewdoc;
	DustMeSelectors_browser.document = doc;

	//if the view target is a selector,
	if(DustMeSelectors_browser.viewtarget.nodeName.toLowerCase() == 'q')
	{
		//if this item is not already selected, this is a primary single selection
		//ie. to select an item and immediately act on
		if(DustMeSelectors_browser.viewtarget.parentNode.className.indexOf('selected') == -1)
		{
			//clear all existing selections fully
			DustMeSelectors_browser.clearAllSelectedTargets(false);

			//select this item
			DustMeSelectors_browser.selectTarget(DustMeSelectors_browser.viewtarget, 'selected');
		}

		//enable the delete item and disable the restore item
		DustMeSelectors_browser.toggleViewContextItems('mark', false, datatype);
		DustMeSelectors_browser.toggleViewContextItems('restore', true, datatype);

		//then enable the restore item if we have deleted selectors in this group
		if(DustMeSelectors_browser.haveDeletedSelectors(DustMeSelectors_browser.viewtarget.getAttribute('cite')))
		{
			DustMeSelectors_browser.toggleViewContextItems('restore', false, datatype);
		}
	}

	//or if the target is a URL heading
	else if(DustMeSelectors_browser.viewtarget.nodeName.toLowerCase() == 'h2')
	{
		//half-select all the items in the group that aren't already fully selected
		//half-selected is the same as selected programatically, but visually different via CSS
		var items = DustMeSelectors_browser.viewtarget.nextSibling.getElementsByTagName('li');
		for(var i=0; i<items.length; i++)
		{
			if(items[i].className != 'selected')
			{
				DustMeSelectors_browser.selectTarget(items[i].firstChild, 'half-selected');
			}
		}

		//disable all items
		DustMeSelectors_browser.toggleViewContextItems('mark,restore', true, datatype);

		//enable the delete item for all selectors if this group is not empty
		if(items.length > 0)
		{
			DustMeSelectors_browser.toggleViewContextItems('markall', false, datatype);
		}

		//enable the restore item if we have deleted selectors in this group
		if(DustMeSelectors_browser.haveDeletedSelectors(DustMeSelectors_browser.viewtarget.firstChild.nodeValue))
		{
			DustMeSelectors_browser.toggleViewContextItems('restore', false, datatype);
		}
	}
};

//get a target <q> selector from view click and context actions
DustMeSelectors_browser.getTargetSelector = function(target)
{
	//convert clicks on <cite> (line number) or list-item to the <q> element
	if(/^(li)$/i.test(target.nodeName))
	{
		target = target.firstChild;
	}
	if(/^(cite)$/i.test(target.nodeName))
	{
		target = target.previousSibling;
	}

	//if the view target is a selector, return it
	if(/^(q)$/i.test(target.nodeName)) { return target; }

	//otherwise return null
	return null;
};

//select a single target
DustMeSelectors_browser.selectTarget = function(target, classname)
{
	target.parentNode.className = classname;
	DustMeSelectors_browser.lastselection.push(target);
	DustMeSelectors_browser.selectiongroup = target.parentNode.parentNode.parentNode;
};

//clear a single selected target
DustMeSelectors_browser.clearSelectedTarget = function(target)
{
	target.parentNode.className = '';
	for(i=0; i<DustMeSelectors_browser.lastselection.length; i++)
	{
		if(DustMeSelectors_browser.lastselection[i] == target)
		{
			DustMeSelectors_browser.lastselection.splice(i, 1);
			break;
		}
	}
	if(DustMeSelectors_browser.lastselection.length == 0)
	{
		DustMeSelectors_browser.selectiongroup = null;
	}
};

//clear all selected targets; if the halfonly argument is true
//only clear half-selected items, and ignore fully selected ones
DustMeSelectors_browser.clearAllSelectedTargets = function(halfonly)
{
	for(var i=0; i<DustMeSelectors_browser.lastselection.length; i++)
	{
		if(halfonly == false || DustMeSelectors_browser.lastselection[i].parentNode.className == 'half-selected')
		{
			DustMeSelectors_browser.lastselection[i].parentNode.className = '';
			DustMeSelectors_browser.lastselection.splice(i--, 1);
		}
	}
	if(DustMeSelectors_browser.lastselection.length == 0)
	{
		DustMeSelectors_browser.selectiongroup = null;
	}
};


//toggle the view context items' disabled property
DustMeSelectors_browser.toggleViewContextItems = function(items, isdisabled, datatype)
{
	items = items.split(',');
	for(var i=0; i<items.length; i++)
	{
		var markall = items[i] == 'markall';
		items[i] = items[i].replace('markall', 'mark');
		var menuitem = this.viewdialog.document.getElementById('dms-view' + datatype + 'context-' + items[i]);
		menuitem.setAttribute('disabled', isdisabled);
		if(items[i] == 'mark')
		{
			if(markall == true)
			{
				menuitem.setAttribute('label', DustMeSelectors_tools.bundle
					.getString('view.context.' + (datatype == 'used' ? 'markallunused' : 'markallused'))
					);
			}
			else
			{
				menuitem.setAttribute('label', DustMeSelectors_tools.bundle
					.getString('view.context.' + (datatype == 'used' ? 'markunused' : 'markused'))
					.replace('%P1', (DustMeSelectors_browser.lastselection.length > 1
						? DustMeSelectors_tools.bundle.getString('view.plural')
						: '')));
			}
		}
	}
};

//see if we have deleted selectors or used selectors in a group
DustMeSelectors_browser.haveDeletedSelectors = function(key, datatype)
{
	//get the host name referred to by the select host menu
	var prefname = this.viewdialog.document.getElementById('dms-hostsindexmenu')
		.selectedItem.getAttribute('value');

	//get the both data objects for the selected host
	var data = {
		'selectors' : DustMeSelectors_tools.getDataObject(prefname, 'selectors', {}),
		'used' : DustMeSelectors_tools.getDataObject(prefname, 'used', {})
		};

	//if the members we're interested in aren't objects, just return false
	if(typeof data.selectors[key] != 'object' || typeof data.used[key] != 'object') { return false; }

	//convert the members we're interested in to JSON
	//so we can do a simple string test to look for deleted selectors
	data.selectors[key] = DustMeSelectors_tools.Object_toJSONString(data.selectors[key]);
	data.used[key] = DustMeSelectors_tools.Object_toJSONString(data.used[key]);

	//if either group contains any deletion marks, return true, otherwise return false
	return (data.selectors[key].indexOf('{D}') != -1 || data.used[key].indexOf('{D}') != -1);
};


//menu command function: mark a selector from the view list and selectors or used object
DustMeSelectors_browser.markSelector = function(datatype)
{
	//get the host name referred to by the select host menu
	var prefname = this.viewdialog.document.getElementById('dms-hostsindexmenu')
		.selectedItem.getAttribute('value');

	//get both selectors and used objects for the selected host
	var data = {
		'selectors' : DustMeSelectors_tools.getDataObject(prefname, 'selectors', {}),
		'used' : DustMeSelectors_tools.getDataObject(prefname, 'used', {})
		};

	//save the inverse reference to the data type
	var notdatatype = datatype == 'used' ? 'selectors' : 'used';

	//count the number of rules and sheets in the data we're using
	var sheets = 0, rules = 0;
	for(var i in data[datatype])
	{
		if(!data[datatype].hasOwnProperty(i)) { continue; }
		if(typeof data[datatype][i] == 'object')
		{
			sheets++;
			for(var j in data[datatype][i])
			{
				if(!data[datatype][i].hasOwnProperty(j)) { continue; }
				if(data[datatype][i][j].indexOf('{D}') != -1) { continue; }
				rules++;
			}
		}
	}

	//get a reference to the heading for this group
	if(this.viewtarget.nodeName.toLowerCase() == 'h2')
	{
		var heading = this.viewtarget.nextSibling.firstChild;
	}
	else
	{
		heading = this.viewtarget.parentNode.parentNode.previousSibling;
	}

	//add the mark class
	heading.parentNode.previousSibling.className = 'mark';

	//if we have a focused element this was keyboard initiated
	//so in that case move the focus to the group URL header, then nullify it
	if(this.hasfocus != null)
	{
		//the target is a list item
		if(this.hasfocus.nodeName.toLowerCase() == 'li')
		{
			this.hasfocus.parentNode.parentNode.previousSibling.focus();
		}
		//the target is already a URL header
		else
		{
			this.hasfocus.focus();
		}
		this.hasfocus = null;
	}

	//count the lastselection array so we know the total difference
	var diff = this.lastselection.length;

	//then for each of the stored targets
	//(that is, while we still have, because we're going to remove them as we go along)
	while(this.lastselection.length > 0)
	{
		//get the selectors group and selector reference from attributes
		i = this.lastselection[0].getAttribute('cite');
		j = this.lastselection[0].getAttribute('class').replace('j', '');

		//add this selector to the other group
		data[notdatatype][i][j] = data[datatype][i][j];

		//mark it as deleted in this group
		data[datatype][i][j] += '{D}';

		//remove this item from the list
		//and remove it from the selection group
		this.lastselection[0].parentNode.parentNode.removeChild(this.lastselection[0].parentNode);
		this.lastselection.splice(0, 1);
	}

	//reset the selection group property
	DustMeSelectors_browser.selectiongroup = null;

	//count the object as it is now
	var count = 0;
	for(var k in data[datatype][i])
	{
		if(!data[datatype][i].hasOwnProperty(k)
			|| data[datatype][i][k].indexOf('{D}') != -1) { continue; }
		count++;
	}

	//if there are still selectors in this group update the heading text
	if(count > 0)
	{
		heading.setAttribute('tabindex', '0');
		heading.removeChild(heading.firstChild);
		heading.insertBefore(this.viewdialog.document.createTextNode(DustMeSelectors_tools.bundle
			.getString('view.' + (datatype == 'used' ? 'used' : 'warning'))
			.replace('%1', count)
			.replace('%P1', (count == 1 ? '' : DustMeSelectors_tools.bundle.getString('view.plural')))
			), heading.firstChild);
	}
	//otherwise it's empty
	else
	{
		//schange the class and message,
		//remove the list and change the parent group class
		heading.removeAttribute('tabindex');
		while(heading.childNodes.length > 0) { heading.removeChild(heading.firstChild); }
		heading.className =  (datatype == 'used' ? 'warning' : 'okay');
		heading.appendChild(this.viewdialog.document.createTextNode(DustMeSelectors_tools.bundle
			.getString('view.' + (datatype == 'used' ? 'noused' : 'okay'))));
		heading.parentNode.className = 'group ' + (datatype == 'used' ? 'warning' : 'okay');
		heading.parentNode.removeChild(heading.nextSibling);
	}

	//decrease the total rules count
	rules -= diff;

	//update the summary description element
	this.updateViewSummary(rules, sheets, datatype, null, null);

	//clear the other browser so its forced to recompile next view
	this.clearBrowserDocument('dms-view' + notdatatype + 'browser');

	//convert selectors and used objects to JSON and save back to file
	//and add this host to the hostsindex, if it doesn't exist already
	DustMeSelectors_filesystem.dumpToFile(prefname + '.json', DustMeSelectors_tools.Object_toJSONString(data['selectors']));
	DustMeSelectors_filesystem.dumpToFile(prefname.replace('selectors', 'used') + '.json', DustMeSelectors_tools.Object_toJSONString(data['used']));
	DustMeSelectors_tools.updateHostsIndex(prefname, true);

	//wait a moment then clear the menu open flag
	//we do this pause so that the enter command doens't fire the event on the underlying target
	//without the pause this would happen before the subsequent event
	window.setTimeout(function() { DustMeSelectors_browser.keymenuopen = false; }, 500);
};


//menu command function: restore all marked selectors to a group
DustMeSelectors_browser.restoreMarkedSelectors = function(datatype)
{
	//if the view target is a <q> element, iterate to find the group heading
	if(this.viewtarget.nodeName.toLowerCase() == 'q')
	{
		this.viewtarget = this.viewtarget
			.parentNode.parentNode.parentNode.previousSibling;
	}

	//now we have (or if we already had) a heading,
	//the index reference will be in the first child text node of the viewtarget element
	index = this.viewtarget.firstChild.nodeValue;

	//get the host name referred to by the select host menu
	var prefname = this.viewdialog.document.getElementById('dms-hostsindexmenu')
		.selectedItem.getAttribute('value');

	//get both selectors and used objects for the selected host
	var data = {
		'selectors' : DustMeSelectors_tools.getDataObject(prefname, 'selectors', {}),
		'used' : DustMeSelectors_tools.getDataObject(prefname, 'used', {})
		};

	//iterate through the members of each of the relevant objects for the selected host
	for(var j in data)
	{
		if(!data.hasOwnProperty(j)) { continue; }

		//save the inverse reference
		var notj = j == 'used' ? 'selectors' : 'used';

		for(var i in data[j][index])
		{
			if(!data[j][index].hasOwnProperty(i)) { continue; }

			//for each one that has a deleted token
			if(data[j][index][i].indexOf('{D}') != -1)
			{
				//delete it from the other object
				delete data[notj][index][i];

				//remove its deletion token from this object
				data[j][index][i] = data[j][index][i].replace('{D}');
			}
		}
	}

	//clear all selections fully
	DustMeSelectors_browser.clearAllSelectedTargets(false);

	//convert selectors and used objects to JSON and save back to file
	//and add this host to the hostsindex, if it doesn't exist already
	DustMeSelectors_filesystem.dumpToFile(prefname + '.json', DustMeSelectors_tools.Object_toJSONString(data['selectors']));
	DustMeSelectors_filesystem.dumpToFile(prefname.replace('selectors', 'used') + '.json', DustMeSelectors_tools.Object_toJSONString(data['used']));
	DustMeSelectors_tools.updateHostsIndex(prefname, true);

	//get a refernece to the group div (the next sibling of the current target header)
	//and clear any existing content from the group
	var group = this.viewtarget.nextSibling;
	while(group.childNodes.length > 0)
	{
		group.removeChild(group.firstChild);
	}

	//save a reference to the browser's content document
	var doc = this.viewdialog.document.getElementById('dms-view' + datatype + 'browser').contentDocument;

	//re-populate the data in this group
	this.populateSelectorsGroup(
		doc,
		data[datatype],
		group,
		index,
		datatype
		);

	//remove the mark class
	group.previousSibling.className = '';

	//clear the other browser so it's forced to recompile next view
	//this is still less than 100% ideal, but it saves a lot of computation
	//finding the group reference in the other document,
	//and knowing whether it's already been rendered
	this.clearBrowserDocument('dms-view' + (datatype == 'used' ? 'selectors' : 'used') + 'browser');

	//re-count the data and update the view sumary element
	var totals = { 'sheets' : 0, 'rules' : 0 };
	for(i in data[datatype])
	{
		if(!data[datatype].hasOwnProperty(i)) { continue; }
		if(typeof data[datatype][i] == 'object')
		{
			totals.sheets++;
			for(j in data[datatype][i])
			{
				if(!data[datatype][i].hasOwnProperty(j)) { continue; }
				if(data[datatype][i][j].indexOf('{D}') != -1) { continue; }
				totals.rules++;
			}
		}
	}
	this.updateViewSummary(totals.rules, totals.sheets, datatype, null, null);

	/*
	//clear both browsers so they're forced to recompile next view
	this.clearBrowserDocument('dms-viewselectorsbrowser');
	this.clearBrowserDocument('dms-viewusedbrowser');

	//count the number of groups in this object
	var groupcount = 0;
	for(var i in data[datatype])
	{
		if(!data[datatype].hasOwnProperty(i)) { continue; }
		groupcount++;
	}

	//re-populate this browser
	this.populateSelectorsInformation(this.viewdocument,
		this.document, data[datatype], groupcount, datatype, true);
	*/

	//wait a moment then clear the menu open flag
	//we do this pause so that the enter command doens't fire the event on the underlying target
	//without the pause this would happen before the subsequent event
	window.setTimeout(function() { DustMeSelectors_browser.keymenuopen = false; }, 500);
}



//update the summary description element
DustMeSelectors_browser.updateViewSummary = function(rules, sheets, datatype, nb, forcemessage)
{
	//get the summary description element and clear any existing content
	var description = this.viewdialog.document.getElementById('dms-viewsummary');
	while(description.childNodes.length > 0)
	{
		description.removeChild(description.firstChild);
	}

	//if the rules and sheets are both not null
	if(rules !== null && sheets !== null)
	{
		//if the forcemessage argument is not null
		//just write that value directly into the description
		if(forcemessage != null)
		{
			description.appendChild(this.viewdialog.document.createTextNode(forcemessage));
		}

		//otherwise if the datatype is log
		else if(datatype == 'log')
		{
			//write a new summary node with the information
			//the value for rules is actually the number of pages
			//and the number for sheets is the number of files
			description.appendChild(
				this.viewdialog.document.createTextNode(DustMeSelectors_tools.bundle
					.getString('spider.logsummary')
					.replace('%1', rules)
					.replace('%2', sheets - rules)
					.replace('%P1', (rules == 1 ? '' : DustMeSelectors_tools.bundle.getString('view.plural')))
					.replace('%P2', (sheets - rules == 1 ? '' : DustMeSelectors_tools.bundle.getString('view.plural')))
				+ (nb == 'incomplete'
					? (' ' + DustMeSelectors_tools.bundle.getString('spider.incomplete'))
					: '')
					));
		}

		//otherwise it's used or selectors
		else
		{
			//write a new summary node with the information
			description.appendChild(this.viewdialog.document.createTextNode(DustMeSelectors_tools.bundle
				.getString('view.' + (datatype == 'used' ? 'usedsummary' : 'summary'))
				.replace('%1', rules)
				.replace('%P1', (rules == 1 ? '' : DustMeSelectors_tools.bundle.getString('view.plural')))
				.replace('%2', sheets)
				.replace('%P2', (sheets == 1 ? '' : DustMeSelectors_tools.bundle.getString('view.plural')))
				));
		}
	}
};



//menu command function: find redundent selectors
DustMeSelectors_browser.findRedundentSelectors = function(isautorun)
{
	//if the extension is disabled or the suppressed flag is strictly true, do nothing
	if(DustMeSelectors_preferences.getPreference('boolean', 'disable')
		|| DustMeSelectors_browser.suppressed === true) { return; }

	//if the isautorun paramer is set this is an autorun initiation
	//so in that case, check for the autorun preference
	//and return if it's false so this doesn't happen
	//we're filtering here because removing the event listener mid session didn't work
	//we could have always put a DOMContentLoaded listener on the content document
	//but this is more efficient, so it's only added if autorun is on at chrome load time
	if(typeof isautorun != 'undefined' && isautorun === true)
	{
		if(DustMeSelectors_preferences.getPreference('boolean', 'autorun') === false)
		{
			return;
		}
	}

	//update the control settings
	this.updateControlSettings();

	//set the status icon to its busy state
	this.setUIState('busy');

	//set the status icon activity icon
	//this.updateStatusLabel(
	//	'background:url(chrome://dustmeselectors/content/activity.gif) no-repeat 100% 0;padding-right:22px;',
	//	null,
	//	null
	//	);

	//set the stop items to enabled and the find items to disabled
	this.setUIState('find-stop', false);

	//if we have an isautorun flag then this is an autorun initiation
	//so set the openview parameter according to the openafterautorun preference
	//otherwise set it according to the openafterfind preference
	this.openview = DustMeSelectors_preferences
		.getPreference('boolean', (typeof isautorun != 'undefined' && isautorun === true)
			? 'openafterautorun' : 'openafterfind');

	//set the running property to true
	DustMeSelectors.running = true;

	//we need a property to count the number of remote stylesheets
	//from which we're still waiting for selectors data
	//(because the requests we'll be making for those stylesheets are asynchronous)
	DustMeSelectors.waiting = 0;

	//now wait a brief moment, then proceed get all selectors for the content document
	//in all the stylesheets on the page we're viewing
	//the pause is a threading tactic to give the UI time to update
	//on pages where there's a lot of stylesheets/rules to parse
	window.setTimeout(function()
	{
		DustMeSelectors.getAllSelectors(DustMeSelectors_browser.prefname, window.content.document);

	}, 10);
};




//save and output the results of the evaluation
DustMeSelectors_browser.saveAndOutput = function(selectors, used)
{
	//convert selectors and used objects to JSON and save to file
	//and add this host to the hostsindex, if it doesn't exist already
	DustMeSelectors_filesystem.dumpToFile(this.prefname + '.json', DustMeSelectors_tools.Object_toJSONString(selectors));
	DustMeSelectors_filesystem.dumpToFile(this.prefname.replace('selectors', 'used') + '.json', DustMeSelectors_tools.Object_toJSONString(used));
	DustMeSelectors_tools.updateHostsIndex(this.prefname, true);

	//if the running flag is true
	if(DustMeSelectors.running)
	{
		//set the status icon back to its default state
		this.setUIState('status');

		//hide the status label
		this.hideStatusLabel();

		//view the saved selectors, forcing the selectors tab but not forcing focus
		this.viewSavedSelectors(null, true, false);
	}

	//send a stop command to disable the stop menu items
	//and reset the running property to false
	this.stop();
};




//menu command function: stop
DustMeSelectors_browser.stop = function()
{
	//set the running property to false
	DustMeSelectors.running = false;

	//set the stop items to disabled and the find items to enable
	this.setUIState('find-stop', true);

	//set the status icon back to its default state
	this.setUIState('status');

	//hide the status label
	this.hideStatusLabel();
};


//restart a find operation on the same page in response to a mutation event
DustMeSelectors_browser.restart = function()
{
	//set the running property to false
	DustMeSelectors.running = false;

	//reset the openview parameter according to the openafterautorun preference
	DustMeSelectors_browser.openview = DustMeSelectors_preferences.getPreference('boolean', 'openafterautorun');

	//wait to give the stop time to happen, then restart the find operation
	window.setTimeout(function()
	{
		//set the running property to true
		DustMeSelectors.running = true;

		//reset the waiting property
		DustMeSelectors.waiting = 0;

		//redo the find operation
		DustMeSelectors.getAllSelectors(
			DustMeSelectors_browser.prefname,
			window.content.document
		);

	}, 300);
};




//set the UI state of menu and status items
DustMeSelectors_browser.setUIState = function(type, arg)
{
	switch(type)
	{
		case 'busy' :

			document.getElementById('dms-statusicon').setAttribute('src',
				'chrome://dustmeselectors/content/statusicon_busy.gif');
			document.getElementById('dms-statusicon').setAttribute('tooltiptext',
				DustMeSelectors_tools.bundle.getString('menu.stop'));

			break;

		case 'view-clearmenu' :

			document.getElementById('dms-mainmenu-view').setAttribute('disabled', arg);
			document.getElementById('dms-statuspopup-view').setAttribute('disabled', arg);
			document.getElementById('dms-mainmenu-clearmenu').setAttribute('disabled', arg);
			document.getElementById('dms-statuspopup-clearmenu').setAttribute('disabled', arg);

			break;

		case 'status' :

			document.getElementById('dms-statusicon').setAttribute('src',
				'chrome://dustmeselectors/content/statusicon_'
					+ (DustMeSelectors_preferences.getPreference('boolean', 'disable') ? 'disabled' : 'default') + '.png');
			document.getElementById('dms-mainmenu-clear').setAttribute('disabled', arg);
			document.getElementById('dms-statuspopup-clear').setAttribute('disabled', arg);

			break;

		case 'find-stop' :

			document.getElementById('dms-statusicon').setAttribute('tooltiptext',
				DustMeSelectors_tools.bundle.getString('menu.' + (arg ? 'find' : 'stop')));
			document.getElementById('dms-mainmenu-stop').setAttribute('disabled', arg);
			document.getElementById('dms-statuspopup-stop').setAttribute('disabled', arg);
			document.getElementById('dms-mainmenu-find').setAttribute('disabled', !arg);
			document.getElementById('dms-statuspopup-find').setAttribute('disabled', !arg);

			break;

		case 'autorun' :

			document.getElementById('dms-mainmenu-autorun').setAttribute('checked', !arg);
			document.getElementById('dms-statuspopup-autorun').setAttribute('checked', !arg);

			break;

		case 'disable' :

			document.getElementById('dms-statusicon').setAttribute('src',
				'chrome://dustmeselectors/content/statusicon_' + (arg ? 'disabled' : 'default') + '.png');
			document.getElementById('dms-statusicon').setAttribute('tooltiptext',
				DustMeSelectors_tools.bundle.getString('menu.' + (arg ? 'disabled' : 'find')));
			document.getElementById('dms-statusicon').setAttribute('style', 'cursor:' + (arg ? 'default' : 'pointer') + ';');
			document.getElementById('dms-mainmenu-find').setAttribute('disabled', arg);
			document.getElementById('dms-statuspopup-find').setAttribute('disabled', arg);
			document.getElementById('dms-mainmenu-automationmenu').setAttribute('disabled', arg);
			document.getElementById('dms-statuspopup-automationmenu').setAttribute('disabled', arg);
			document.getElementById('dms-mainmenu-disable').setAttribute('checked', arg);
			document.getElementById('dms-statuspopup-disable').setAttribute('checked', arg);

			//disable view and clear menus on demand, but on re-enable,
			//enable them only if there's data in the hosts index
			this.setUIState('view-clearmenu', (arg || DustMeSelectors_tools.getHostsIndex().length == 0));

			//hide the status label when disabling the extension
			//re-showing will happen if applicable by the reinitialision method
			if(arg) { this.hideStatusLabel(); }

			break;

		case 'suppress' :

			document.getElementById('dms-mainmenu-disable').setAttribute('disabled', arg);
			document.getElementById('dms-statuspopup-disable').setAttribute('disabled', arg);
			document.getElementById('dms-mainmenu-disable').setAttribute('checked', false);
			document.getElementById('dms-statuspopup-disable').setAttribute('checked', false);
			document.getElementById('dms-mainmenu-preferences').setAttribute('disabled', arg);
			document.getElementById('dms-statuspopup-preferences').setAttribute('disabled', arg);

			break;

		case 'statusoverlay' :

			document.getElementById('dms-statusoverlay').setAttribute('hidden', arg);

			break;
	}
};



//update the status label
DustMeSelectors_browser.updateStatusLabel = function(style, rules, sheets)
{
	//if the extension is disabled do nothing here
	if(DustMeSelectors_preferences.getPreference('boolean', 'disable')) { return; }

	//save a reference to the status label
	var statuslabel = document.getElementById('dms-statuslabel');

	//show the status label
	statuslabel.setAttribute('hidden', 'false');

	//if the style argument is not null, set the attribute
	if(style != null)
	{
		statuslabel.setAttribute('style', style);
	}

	//if the rules and sheets arguments are strictly null,
	//remove their value and tooltip attributes and hide the label
	if(rules === null && sheets === null)
	{
		statuslabel.removeAttribute('value');
		statuslabel.removeAttribute('tooltiptext');
	}

	//otherwise if the values are not both strictly true apply the values
	else if(!(rules === true && sheets === true))
	{
		statuslabel.setAttribute('value', rules + '/' + sheets);
		statuslabel.setAttribute('tooltiptext', DustMeSelectors_tools.bundle
			.getString('view.summary')
			.replace('%1', rules)
			.replace('%P1', (rules == 1 ? '' : DustMeSelectors_tools.bundle.getString('view.plural')))
			.replace('%2', sheets)
			.replace('%P2', (sheets == 1 ? '' : DustMeSelectors_tools.bundle.getString('view.plural')))
			);
	}

	//if the values are boolean true, it means leave the value and tooltip as it is
};


//hide the status label
DustMeSelectors_browser.hideStatusLabel = function()
{
	//get a reference to the status label
	var statuslabel = document.getElementById('dms-statuslabel');

	//remove the value and tooltip attributes
	statuslabel.removeAttribute('value');
	statuslabel.removeAttribute('tooltiptext');

	//hide the label
	statuslabel.setAttribute('hidden', 'true');
};



