if ( !self.SMM ) { self.SMM = new Object(); self.SMM.wcomp = new Object(); }
self.SMM.wcomp.palette = {
 cookiename:	'palette',
 path:		'/css/colours/',
 domain:	'.webcompliant.co.uk',
 maxlists:	5,
 maxpairs:	15,
 expires:	1, // in years
 namelen:	20,
 lthresh_aaa:	7,
 lthresh_aa:	5,
 lthresh_a:	3,
 brt_thresh:	125,
 col_thresh:	500,
 undolimit:	30,
 pcls:		'P',
 fcls:		'F',
 okcls:		'L',
 lname:		{ fg: 'foreground', bg: 'background' },
 defcol:	{ fg: '000', bg: 'fff' },

 curcol:	{ fg: null, bg: null }, // don't change
 curlist:	0,
 lists:		new Array(),
 palette:	{
	colpatt:	null,
	listpatt:	null,
	otherprop:	{ fg: 'bg', bg: 'fg' },
	undolist:	new Array(), //each arr elt is Array( shortcol_fg, shortcol_bg )
	redolist:	new Array(), //each arr elt is Array( shortcol_fg, shortcol_bg )
	e:		new Object(), //relevant DOM Elements
	objs:		new Object(), //[ shortcol ] = obj, [ longcol ] = obj for each col
	pairs:		new Object()
 },

 run: function () {
	var o = this;
	var error = o.do_palette();
	if ( error ) { window.alert( "Sorry! Your browser's JavaScript is not compatible with this Interactive Palette (Error:" + error + ")" ) }
 },

 do_palette: function () {
	var o = this;
	var pa = o.palette;
	var g = self.SMM.wcomp.gen;
	g.noHL = true;
	if ( !g.DOMok || !document.body ) { return 1 }

	if ( o.fail_core() ) { return 2 }
	o.maxpairslen = o.maxpairs.toString().length;
	
	var s = pa.e.stat = document.getElementById( 'status' );
	pa.e.exdiv = document.getElementById( 'examplediv' );
	var tdiv = document.getElementById( 'tables' );
	var tds = tdiv.getElementsByTagName( 'td' );
	if ( !s | !tdiv || !tds || !pa.e.exdiv || !s.nextSibling || !s.childNodes || !s.replaceChild ||
		!s.cloneNode || !s.firstChild || !s.firstChild.data || s.firstChild.data != '-' || !s.insertBefore ) { return 3 }

	var nsc = document.getElementById( 'noscript' );
	if ( !nsc || !nsc.style || !nsc.removeChild ) { return 4 }
	nsc.style.display = 'none'; nsc.parentNode.removeChild( nsc );

	o.createform( document.getElementById( 'formcontainer' ) );
	if ( !pa.e.thisbg || !pa.e.thisbg.form ) { return 5 }

	var eltlist = [ 'lumcr', 'aaa', 'aa', 'bd', 'cd', 'bdiff', 'cdiff', 'saved', 'reset', 'swap', 'savepair', 'savedpairs', 'undo', 'redo' ];
	for ( var i = 0; i < eltlist.length; i++ ) {
		var id = eltlist[i];
		pa.e[id] = document.getElementById( id );
		if ( !pa.e[id] ) { return 6 }
	}
	//var sl = g.cElt( 'select', 'yrlists' );
	var sl = g.cElt( 'select' );
	if ( !sl || !sl.add ) { return 7 }
	var tmpop = g.cElt( 'option' );
	tmpop.text = 'tmp';
	tmpop.value = '-1';
	try		{ sl.add(tmpop, null); }
	catch(e1)	{
		try { sl.add(tmpop); }
		catch (e2) { return 8 }
	}
	tmpop.parentNode.remove( tmpop.index );
	tmpop = null;
	if ( sl.options.length ) { return 9 }
	sl.title = 'Your Colour Pair Lists';

	pa.expl = { aaa: new Object(), aa: new Object(), bd: new Object(), cd: new Object() };
	pa.expl.aaa[ o.pcls ] = 'Pass (>' + o.lthresh_aaa + ':1)';
	pa.expl.aaa[ o.okcls ] = 'Pass for Larger Scale Text Only (>' + o.lthresh_aa + ':1 but <' + o.lthresh_aaa + ':1)';
	pa.expl.aaa[ o.fcls ] = 'Fail (<' + o.lthresh_aa + ':1)';
	pa.expl.aa[ o.pcls ]= 'Pass (>' + o.lthresh_aa + ':1)';
	pa.expl.aa[ o.okcls ] = 'Pass for Larger Scale Text Only (>' + o.lthresh_a + ':1 but <' + o.lthresh_aa + ':1)';
	pa.expl.aa[ o.fcls ] = 'Fail (<' + o.lthresh_a + ':1)';
	pa.expl.bd[ o.pcls ]= 'Pass (>' + o.brt_thresh + ')';
	pa.expl.bd[ o.fcls ] = 'Fail (<' + o.brt_thresh + ')';
	pa.expl.cd[ o.pcls ]= 'Pass (>' + o.col_thresh + ')';
	pa.expl.cd[ o.fcls ] = 'Fail (<' + o.col_thresh + ')';

	var ex_p = g.cElt( 'p' );
	ex_p.appendChild( g.cElt( 'span', '', '', 'Normal, ' ) );
	ex_p.appendChild( g.cElt( 'i', '', '', 'Italic, ' ) );
	var bold = g.cElt( 'b', '', '', 'Bold, ' );
	bold.appendChild( g.cElt( 'i', '', '', 'Bold+Italic' ) );
	ex_p.appendChild( bold );
	pa.e.examplep = ex_p;

	pa.e.savedempty = g.cElt( 'p', '', 'savedempty', 'No saved pairs' );

	for ( var i = 0; i < tds.length; i++ ) {
		var fch = tds[ i ].firstChild;
		var obj = o.get_object( fch.data );
		if ( obj.brightness < 127.5 )	{ obj.b_or_w = 'white' }
		else				{ obj.b_or_w = 'black' }
		obj.fg = g.cElt( 'a', '', '', 'FG' );
		obj.bg = g.cElt( 'a', '', '', 'BG' );
		obj.fg.href = obj.bg.href = '#';

		var div = g.cElt( 'div' );
		div.shortcol = obj.shortcol;
		div.appendChild( obj.fg );
//		g.aTxt( div, ' ' );
		div.appendChild( g.cElt( 'span', '', '', obj.shortcol ) );
//		g.aTxt( div, ' ' );
		div.appendChild( obj.bg );

		tds[ i ].replaceChild( div, fch );
		o.set_up_a( 'fg', obj.fg, obj.shortcol );
		o.set_up_a( 'bg', obj.bg, obj.shortcol );
		div.parentNode.style.backgroundColor = obj.hashcol;

	}

// XXX deftitle and greytitle are expandos
	pa.e.submit.deftitle = 'View colours';
	pa.e.submit.greytitle = 'You are already viewing these colours';

	pa.e.undo.deftitle = 'Undo colour change';
	pa.e.undo.greytitle = "Can't undo - no previous colours - max undos stored: " + o.undolimit;
	pa.e.undo.redolen = 0; // XXX expando

	pa.e.redo.deftitle = 'Redo previously undone colour change';
	pa.e.redo.greytitle = "Can't redo - no more colours";

	pa.e.swap.deftitle = 'Swap ' + o.lname.fg + ' and ' + o.lname.bg + ' colours';
	pa.e.swap.greytitle = 'The ' + o.lname.fg + ' and ' + o.lname.bg + ' colours are identical';

	pa.e.palert = g.cElt( 'div', 'palert', '', 'Dummy' );
	pa.e.pprompt = g.cElt( 'form', 'pprompt' );
	pa.e.pprompt.setAttribute( 'action', '' );
	pa.e.plab = g.cElt( 'label', '', '', 'Name:' );
	pa.e.plab.htmlFor = 'pinp'; pa.e.plab.setAttribute( 'for', 'pinp' );
	pa.e.pprompt.appendChild( pa.e.plab );
	g.aTxt( pa.e.pprompt, "\u00a0" );
	pa.e.pinp = g.cElt( 'input', 'pinp' );
	pa.e.pinp.value = '';
	pa.e.pinp.setAttribute( 'maxlength', o.namelen ); pa.e.pinp.setAttribute( 'size', o.namelen );
	pa.e.pprompt.appendChild( pa.e.pinp );
	pa.e.psub = g.cElt( 'button', '', '', 'Go' );
	g.aTxt( pa.e.pprompt, "\u00a0" );
	pa.e.pprompt.appendChild( pa.e.psub );
	pa.e.pcanc = g.cElt( 'button', '', '', 'Cancel' );
	g.aTxt( pa.e.pprompt, "\u00a0" );
	pa.e.pprompt.appendChild( pa.e.pcanc );
	pa.e.pconfirm = g.cElt( 'div', 'pconfirm' );
	pa.e.pspan = g.cElt( 'span', '', '', 'Dummy' );
	pa.e.pconfirm.appendChild( pa.e.pspan );
	pa.e.yes = g.cElt( 'button', '', '', 'Do It' );
	g.aTxt( pa.e.pconfirm, "\u00a0" );
	pa.e.pconfirm.appendChild( pa.e.yes );
	pa.e.no = g.cElt( 'button', '', '', 'Cancel' );
	g.aTxt( pa.e.pconfirm, "\u00a0" );
	pa.e.pconfirm.appendChild( pa.e.no );
	var p = g.cElt( 'p', '', '', "NOTE - there is no way to undo this action" );
	pa.e.pconfirm.appendChild( p );

	o.close_saved( false );
	pa.e.savedpairs.title = 'Toggle Saved Pairs Display';
	var vtop = pa.e.vtop = g.cElt( 'div', 'vtop' );
	pa.e.saved.appendChild( vtop );
	pa.e.listcont = g.cElt( 'div' );
	pa.e.saved.appendChild( pa.e.listcont );
//	var label = g.cElt( 'label', '', '', "Your Lists:\u00a0" );
//	label.htmlFor = 'yrlists'; label.setAttribute( 'for', 'yrlists' );
//	vtop.appendChild( label );
	pa.e.select = sl;
	vtop.appendChild( sl );
	g.aTxt( vtop, "\u00a0" );
	pa.e.newlist = g.cElt( 'button', '', '', 'New List' );
	vtop.appendChild( pa.e.newlist );
	pa.e.newlist.deftitle = 'Create a New Colour Pair List';
	pa.e.newlist.greytitle = "Sorry but you can't have more than " + o.maxlists + ' lists';
	g.aTxt( vtop, "\u00a0" );
	pa.e.savedclose = g.cElt( 'button', 'close', '', 'X' );
	vtop.appendChild( pa.e.savedclose );
	pa.e.savedclose.title = 'Close the Saved Pairs Display';

	pa.e.thisbg.onfocus = pa.e.thisfg.onfocus = o.onfocus_close_saved;
	pa.e.thisbg.onchange = pa.e.thisfg.onchange = o.onchange_check;
	pa.e.thisbg.form.onsubmit =	o.onsubmit_check;
	pa.e.undo.onclick =		o.onclick_undo;
	pa.e.redo.onclick =		o.onclick_redo;
	pa.e.swap.onclick =		o.onclick_swap_cols;
	pa.e.reset.onclick =		o.onclick_reset_cols;
	pa.e.savepair.onclick =		o.onclick_save_pair;
	pa.e.savedpairs.onclick =	o.onclick_toggle_saved;
	pa.e.savedclose.onclick =	o.onclick_close_saved;
	pa.e.select.onchange =		o.onchange_clist;
	pa.e.newlist.onclick =		o.onclick_nlist;
	document.body.onmousedown =	o.onclick_remove_alert;

	o.curcol.fg = o.defcol.fg = o.get_object( o.defcol.fg ).shortcol;
	o.curcol.bg = o.defcol.bg = o.get_object( o.defcol.bg ).shortcol;
	pa.e.reset.greytitle = 'You are already viewing the default colours';
	pa.e.reset.deftitle = 'Revert to ' + o.lname.fg + ' #' + o.defcol.fg + ' and ' + o.lname.bg + ' #' + o.defcol.bg + ' (retaining any saved pairs)';

	if ( !o.import_from_cookie() ) { o.create_list( o.curlist, 'Default List' ); o.change_list( o.curlist ); o.reset_cols( true ); }
	o.lists[o.curlist][3].defaultSelected = true;
	return 0;
 },

 create_list: function ( lno, name ) {
	var o = this;
	var pa = o.palette;
	if ( lno < 0 || lno >= o.maxlists || o.lists[lno] ) return false;
	var g = self.SMM.wcomp.gen;
	var div = g.cElt( 'div' );
	div.className = 'clist';
	div.style.display = 'none';
	pa.e.listcont.appendChild( div );
	var div2 = g.cElt( 'div', '', 'clisttop' )
	div.appendChild( div2 );
	var h3 = g.cElt( 'h3', '', '', name );
	h3.title = 'Current Colour Pair List';
	div2.appendChild( h3 );
	g.aTxt( div2, ' ' );
	// list-specific buttons
	var bts = g.cElt( 'span', '', 'bts' );
	div2.appendChild( bts );
	var el = g.cElt( 'button', '', '', 'Empty' );
	bts.appendChild( el );
	el.deftitle = "Remove ALL Saved Pairs from this List";
	el.greytitle = "There are no Saved Pairs in this List to remove";
	o.disable_button( el );
	g.aTxt( bts, "\u00a0" );
	var dl = g.cElt( 'button', '', '', 'Del' );
	bts.appendChild( dl );
	dl.deftitle = "Delete this List completely";
	dl.greytitle = "Can't delete the last remaining List";
	g.aTxt( bts, "\u00a0" );
	var rl = g.cElt( 'button', '', '', 'Rename' );
	bts.appendChild( rl );
	rl.title = "Rename this List";
	var prs = g.cElt( 'div', '', '', '0' );
	div2.appendChild( prs );

	bts.lno = div.lno = lno; // XXX expandos
	var p = pa.e.savedempty.cloneNode( true );
	div.appendChild( p );
	var op = g.cElt( 'option' );
	op.text = name;
	op.value = lno;
	try		{ pa.e.select.add(op, null); }
	catch(e1)	{
		try { pa.e.select.add(op); }
		catch (e2) {}
	}
	o.lists[ lno ] = [ div, p, name, op, el, dl, prs ];

	el.onclick = o.onclick_elist;
	dl.onclick = o.onclick_dlist;
	rl.onclick = o.onclick_rlist;
	o.update_listcount(lno);
	o.check_listbutts();
	return true;
 },

 change_list: function ( lno ) {
	var o = this;
	o.remove_dialogues();
	var pa = o.palette;
	if ( !o.lists[lno] ) { return false; }
	var kids = pa.e.listcont.childNodes;
	for ( var i = 0; i < kids.length; i++ ) {
		if ( kids[i].lno != lno ) { kids[i].style.display = 'none'; continue }
		kids[i].style.display = 'block';
		o.curlist = lno;
		o.lists[lno][3].selected = true;
//		if ( lno != o.lists[lno][3].index ) { window.alert( 'lno:' + lno + ' - ind:' + o.lists[lno][3].index ) }
	}
	o.check_saved();
	o.check_viewbuttons();
	o.export_to_cookie( false );
	return true;
 },

 check_listbutts: function () {
	var o = this;
	var pa = o.palette;
	var numlists = pa.e.listcont.childNodes.length;
	if ( numlists < o.maxlists ) { o.enable_button( pa.e.newlist ) }
	else { o.disable_button( pa.e.newlist ) }

	var ops = pa.e.select.options;
	if ( numlists == 1 ) {
		o.disable_button( o.lists[ ops[0].value ][5] );
		return;
	}
	for ( var i = 0; i < ops.length; i++ ) {
		o.enable_button( o.lists[ ops[i].value ][5] );
	}
 },

 import_from_cookie: function () {
	var o = this;
	var pa = o.palette;
	var v = o.get_storedcookie_val();
	if (!v) return false;
	var mainbits = v.split('!*');
	if ( !mainbits || mainbits.length != 2 ) return false;
	var lprs = mainbits[0].split('!');
	var lnames = new Array();
	for ( var i = 0; i < lprs.length; i++ ) {
		var tmp = lprs[i].split('-');
		if ( !tmp || tmp.length != 2 ) continue;
		var lno = tmp[0]-0;
		if ( lno < 0 || lno >= o.maxlists || !tmp[1] ) continue;
		if ( tmp[1].match( pa.listpatt ) ) lnames[ lno ] = tmp[1];
	}
	if ( lnames.length == 0 ) return false;
	var cprs = mainbits[1].split('x');
	var valid = new Array();
	for ( var i = 0; i < cprs.length; i++ ) {
		var fgbg = cprs[i].split('-');
		if ( !fgbg || fgbg.length != 3 || o.is_invalid( fgbg[0] ) || o.is_invalid( fgbg[1] ) || !lnames[ fgbg[2] ] ) continue;
		valid[ valid.length ] = fgbg;
	}
	var lastpos = valid.length - 1;
	if ( valid.length == 0 || !lnames[ valid[lastpos][2] ] ) return false;
	for ( var j = 0; j < lnames.length; j++ )	{ if ( lnames[j] ) o.create_list( j, lnames[j] ) }
	for ( var j = 0; j < lastpos; j++ )		{ o.save_pair( valid[j][2], valid[j][0], valid[j][1], true ) }
	for ( var j = 0; j < lnames.length; j++ )	{ if ( lnames[j] ) o.update_listcount(j) }
	o.change_list( valid[lastpos][2] );
	o.change_both_cols( valid[lastpos][0], valid[lastpos][1], false, false, true );
	return true;
 },

 update_listcount: function ( lno ) {
	var o = this;
	var prs = o.get_listcount( lno );
	prs = o.num_str( prs, o.maxpairslen, ' ' );
	var pairs = prs == 1 ? ' pair ' : ' pairs';
	o.lists[lno][6].firstChild.data = prs + pairs + ' (max ' + o.maxpairs + ' per list)';
 },

 get_listcount: function ( lno ) {
	var o = this;
	var kids = o.lists[lno][0].childNodes;
	if ( kids[1].className == 'savedempty' ) return 0;
	return ( kids.length - 1 );
 },

 export_to_cookie: function ( change_saved ) {
	var o = this;
	var pa = o.palette;
	if ( change_saved || !o.cookie_saved ) {
		var v = '';
		var l = '';
		var ops = pa.e.select.options;
		for ( var i = 0; i < ops.length; i++ ) {
			var lno = ops[i].value;
			if ( !o.lists[lno] ) continue;
			var name = o.lists[lno][2];
			l += ( lno + '-' + name + '!' );
			var kids = o.lists[lno][0].childNodes;
			var ids = new Array();
			for ( var j = 0; j < kids.length; j++ ) {
				if ( kids[j].id ) ids[ ids.length ] = kids[j].id;
			}
			var numids = ids.length;
			if ( numids > o.maxpairs ) numids = o.maxpairs;
			for ( var j = 0; j < numids; j++ ) { v += ids[j] }
		}
		o.cookie_saved = [ l, v ];
	}
	o.set_cookie( o.cookie_saved[0] + '*' + o.cookie_saved[1] + 'x' + o.curcol.fg + '-' + o.curcol.bg + '-' + o.curlist );
 },

 get_storedcookie_val: function () {
	var o = this;
	if ( !document.cookie ) return null;
	var ct = o.cookiename + '=';
	var ac = document.cookie.split(';');
	for ( var i = 0; i < ac.length; i++ ) {
		var c = ac[i];
		c = c.replace( /^\s+/, '' );
		c = c.replace( /\s+$/, '' );
		if ( c.indexOf(ct) == 0 ) return decodeURIComponent( c.substring( ct.length, c.length ) );
	}
	return null;
 },

 set_cookie: function ( v ) {
	var o = this;
	var d = new Date();
	d.setFullYear(d.getFullYear()+o.expires);
	var cookiestr = o.cookiename + '=' + encodeURIComponent( v ) + '; path=' + o.path + '; domain=' + o.domain + '; expires=' + d.toGMTString();
	//window.alert( cookiestr );
	document.cookie = cookiestr;
 },

 fail_core: function () {
	var o = this;
	var pa = o.palette;
	if ( pa.otherprop[ 'fg' ] != 'bg' || !pa.undolist.shift || !pa.undolist.pop ||
		!Math.abs || !Math.floor || !Math.pow || !Math.min || !Math.max ) return true;

	var tnm = 1.2345;
	if ( !tnm.toFixed || tnm.toFixed( 2 ) != '1.23' ||
		!tnm.toString || !tnm.toString( 10 ) || tnm.toString( 10 ) != '1.2345' ||
		parseInt( ' 5:1' ) != 5 || parseInt( '0x' + 'FF' ) != 255 ) return true;

	var tst = 'abcdef';
	if ( !tst.length || tst.length != 6 || !tst.substring || tst.toUpperCase() != 'ABCDEF' ||
		tst.substring( 0, 2 ) != 'ab' || tst.substring( 2, 4 ) != 'cd' || tst.substring( 4 ) != 'ef' ) return true;

	var ss = 'abc';
	pa.colpatt = new RegExp( '^#?([a-f0-9]{3})([a-f0-9]{3})?$' );
	if ( !ss.match || !ss.match( pa.colpatt ) ) return true;
	ss = '#aabbcc';
	if ( !ss.match( pa.colpatt ) ) return true;
	ss = ss.replace( /^#/, '' );
	if ( ss != 'aabbcc' || !ss.charAt ) return true;
	if ( ss.charAt( 0 ) == ss.charAt( 1 ) && ss.charAt( 2 ) == ss.charAt( 3 ) && ss.charAt( 4 ) == ss.charAt( 5 ) ) {
		ss = ss.charAt( 0 ) + ss.charAt( 2 ) + ss.charAt( 4 );
	}
	if ( ss != 'abc' ) return true;
	pa.listpatt = new RegExp( '^[a-zA-Z0-9 .]{1,' + o.namelen + '}$' );

	return false;
 },

 createform: function ( formcont ) {
	var o = this;
	var pa = o.palette;
	var g = self.SMM.wcomp.gen;
	var pbasic = g.cElt( 'p', '', '', 'Before you start: see the ' );
	var abasic = g.cElt( 'a', '', '', 'Basic Colour Pair Analyser' );
	abasic.href = 'basic.html';
	pbasic.appendChild( abasic );
	g.aTxt( pbasic, ' for details about the tests.' );
	formcont.appendChild( pbasic );
	var pacc = g.cElt( 'p', '', 'acc' );
	pacc.appendChild( g.cElt( 'strong', '', '', 'You will need CSS styles enabled in order to make best use of this palette.' ) );
	formcont.appendChild( pacc );
	
	var p1  = g.cElt( 'p', '', '', 'To change ' );
	var abbr1 = g.cElt( 'abbr', '', '', 'FG' );
	abbr1.title = 'foreground colour';
	p1.appendChild( abbr1 );
	g.aTxt( p1, ' or ' );
	var abbr2 = g.cElt( 'abbr', '', '', 'BG' );
	abbr2.title = 'background colour';
	p1.appendChild( abbr2 );
	g.aTxt( p1, ' in the Example div, use either the tables below or this form:' );
	formcont.appendChild( p1 );

	var form = g.cElt( 'form' );
	form.setAttribute( 'action', '' );
	var fd = g.cElt( 'div', 'formdiv' );
	var lab1 = g.cElt( 'label', '', '', "FG:\u00a0" );
	lab1.htmlFor = 'thisfg'; lab1.setAttribute( 'for', 'thisfg' );
	fd.appendChild( lab1 );
	pa.e.thisfg = g.cElt( 'input', 'thisfg' );
	pa.e.thisfg.value = '000';
	pa.e.thisfg.setAttribute( 'maxlength', 7 ); pa.e.thisfg.setAttribute( 'size', 7 );
	fd.appendChild( pa.e.thisfg );
	g.aTxt( fd, ' ' );
	var lab2 = g.cElt( 'label', '', '', "BG:\u00a0" );
	lab2.htmlFor = 'thisbg'; lab2.setAttribute( 'for', 'thisbg' );
	fd.appendChild( lab2 );
	pa.e.thisbg = g.cElt( 'input', 'thisbg' );
	pa.e.thisbg.value = 'fff';
	pa.e.thisbg.setAttribute( 'maxlength', 7 ); pa.e.thisbg.setAttribute( 'size', 7 );
	fd.appendChild( pa.e.thisbg );
	g.aTxt( fd, ' ' );
// submit needs to be input type="submit" and not button since IE's default
// button type is "button" and the attribute is read-only
	pa.e.submit = g.cElt( 'input', 'submit' );
	pa.e.submit.type = 'submit';
	pa.e.submit.value = 'View';
	fd.appendChild( pa.e.submit );
	form.appendChild( fd );
	formcont.appendChild( form );

	var p = g.cElt( 'p', 'cookie' );
	g.aTxt( p, 'Colour pairs are stored in a cookie using JavaScript - no information is ever sent to our servers.' );
	p.appendChild( g.cElt( 'br' ) );
	var s = o.expires > 1 ? 's' : '';
	g.aTxt( p, 'The cookie is set to expire ' + o.expires + ' year' + s + ' after last use. You can save up to ' + o.maxlists + ' lists of up to ' + o.maxpairs + ' pairs each.' );
	formcont.parentNode.insertBefore( p, formcont.nextSibling );

 },

 set_up_a: function ( fg_or_bg, a, shortcol ) {
	var o = this;
	a.deftitle = 'Set ' + o.lname[ fg_or_bg ] + ' to #' + shortcol;
	a.thistitle = '#' + shortcol + ' is the current ' + o.lname[ fg_or_bg ] + ' colour';
	a.title = a.deftitle;
	a.onclick = o.onclick_change_colour;
 },

 get_object: function ( iv ) {
	var o = this;
	var pa = o.palette;
	iv = iv.toLowerCase();
	if ( o.Vc[ iv ] )	iv = o.Vc[ iv ];
	if ( pa.objs[ iv ] )	return pa.objs[ iv ];
//	window.alert( 'new colour: ' + iv );
	var obj = new Object();
	obj.longcol = obj.shortcol = iv;
	if ( iv.length == 3 ) {
		obj.longcol = iv.charAt( 0 ) + iv.charAt( 0 ) + iv.charAt( 1 ) + iv.charAt( 1 ) + iv.charAt( 2 ) + iv.charAt( 2 );
	}
	else if ( iv.charAt( 0 ) == iv.charAt( 1 ) && iv.charAt( 2 ) == iv.charAt( 3 ) && iv.charAt( 4 ) == iv.charAt( 5 ) ) {
		obj.shortcol = iv.charAt( 0 ) + iv.charAt( 2 ) + iv.charAt( 4 );
	}
	var cl = obj.longcol;
	obj.hashcol = '#' + cl;
	obj.rgb = new Object();
	var r = obj.rgb.r = parseInt( '0x' + cl.substring( 0, 2 ) );
	var g = obj.rgb.g = parseInt( '0x' + cl.substring( 2, 4 ) );
	var b = obj.rgb.b = parseInt( '0x' + cl.substring( 4 ) );
// Brightness: ((Red value X 299) + (Green value X 587) + (Blue value X 114)) / 1000
	obj.brightness = ( r * 299 + g * 587 + b * 114 ) / 1000;
	obj.rel_lum = o.find_rel_lum( r, g, b );
	obj.decstr = o.num_str( r, 3, '0' ) + ',' + o.num_str( g, 3, '0' ) + ',' + o.num_str( b, 3, '0' );
	pa.objs[ obj.shortcol ] = obj;
	pa.objs[ obj.longcol ] = obj;
	return obj;
 },
 
 num_str: function ( n, reqdlen, fillchar ) {
	var str = n.toString( 10 );
	while ( str.length < reqdlen ) { str = fillchar + str };
	return str;
 },

 find_rel_lum: function ( red, green, blue ) {
// L = 0.2126 * R + 0.7152 * G + 0.0722 * B where R, G and B are defined as:
//      if RsRGB <= 0.03928 then R = RsRGB/12.92 else R = ((RsRGB+0.055)/1.055) ^ 2.4
//      if GsRGB <= 0.03928 then G = GsRGB/12.92 else G = ((GsRGB+0.055)/1.055) ^ 2.4
//      if BsRGB <= 0.03928 then B = BsRGB/12.92 else B = ((BsRGB+0.055)/1.055) ^ 2.4
// and RsRGB, GsRGB, and BsRGB are defined as:
//      RsRGB = R8bit/255
//      GsRGB = G8bit/255
//      BsRGB = B8bit/255
	var RsRGB = red / 255;
	var GsRGB = green / 255;
	var BsRGB = blue / 255;
	var R, G, B;
	if ( RsRGB <= 0.03928 )	{ R = RsRGB / 12.92 }
	else			{ R = Math.pow( ( RsRGB + 0.055 ) / 1.055, 2.4 ) }
	if ( GsRGB <= 0.03928 )	{ G = GsRGB / 12.92 }
	else			{ G = Math.pow( ( GsRGB + 0.055 ) / 1.055, 2.4 ) }
	if ( BsRGB <= 0.03928 )	{ B = BsRGB / 12.92 }
	else			{ B = Math.pow( ( BsRGB + 0.055 ) / 1.055, 2.4 ) }
	return ( 0.2126 * R ) + ( 0.7152 * G ) + ( 0.0722 * B );
 },

 validate_inputs: function () {
	var o = this;
	var pa = o.palette;
	var error = '';
	if ( o.is_invalid( pa.e.thisfg ) ) { error = 'Invalid ' + o.lname.fg }
	if ( o.is_invalid( pa.e.thisbg ) ) { if ( error ) { error = error + ' and ' }; error = error + 'Invalid ' + o.lname.bg }
	return error;
 },

 is_invalid: function ( inp ) {
	var o = this;
	var pa = o.palette;
	var val = self.SMM.wcomp.gen.norm_ws( inp );
	val = val.toLowerCase();
	val = val.replace( /^#/, '' );
	if ( inp.tagName ) inp.value = val;
	return ( !o.Vc[ val ] && !val.match( pa.colpatt ) );
 },

 change_colour: function ( fg_or_bg, colstr, is_init ) {
	var o = this;
	var pa = o.palette;
	var bg_or_fg = pa.otherprop[ fg_or_bg ];
	var newobj = o.get_object( colstr );
	if ( newobj.shortcol == o.curcol[ fg_or_bg ] && !is_init ) return false;
// If the current (pre-change) fg_or_bg colour is in the colour table, we need to remove the classNames as appropriate 
	var oldobj = o.get_object( o.curcol[ fg_or_bg ] );
	var olda = oldobj[ fg_or_bg ];
	if ( olda ) {
		olda.className = ''; olda.removeAttribute( 'class' );
		olda.title = olda.deftitle;
		// only remove the classname on the div *if* this colour is not currently used for both fg and bg.
		if ( o.curcol[ bg_or_fg ] != o.curcol[ fg_or_bg ] ) {
		    olda.parentNode.className = ''; olda.parentNode.removeAttribute( 'class' );
		}
	}
	var newa = newobj[ fg_or_bg ];
	if ( newa ) {
		newa.className = 'thislink';
		newa.title = newobj[ fg_or_bg ].thistitle;
		newa.parentNode.className = newobj.b_or_w;
	}
	if	( fg_or_bg == 'bg' )	{ pa.e.exdiv.style.backgroundColor = newobj.hashcol }
	else				{ pa.e.exdiv.style.color = newobj.hashcol }
	o.curcol[ fg_or_bg ] = newobj.shortcol;
	return true;
 },

 change_one_col: function ( fg_or_bg, colstr ) {
	var o = this;
	o.close_saved( false );
	var r = o.change_colour( fg_or_bg.toLowerCase(), colstr, false );
	if ( !r ) return false;
	o.update_status( false );
	o.export_to_cookie( false );
	return true;
 },

 change_both_cols: function ( fgstr, bgstr, isundo_or_redo, close, is_init ) {
	var o = this;
	if ( close ) o.close_saved( false );
	var f = o.change_colour( 'fg', fgstr, is_init );
	var b = o.change_colour( 'bg', bgstr, is_init );
	if ( !f && !b ) return false;
	o.update_status( isundo_or_redo );
	o.export_to_cookie( false );
	return true;
 },

 get_pairs_info: function ( fgobj, bgobj ) {
	var o = this;
	var pa = o.palette;
	var id    = 'x' + fgobj.shortcol + '-' + bgobj.shortcol;
	if ( pa.pairs[id] ) return pa.pairs[id];
	var revid = 'x' + bgobj.shortcol + '-' + fgobj.shortcol;
	//window.alert( 'new pairs info for ' + id );
	var pr = new Object();
	pr.stat = 'Now: ' +
		fgobj.hashcol + ' (' + fgobj.decstr + ') on ' +
		bgobj.hashcol + ' (' + bgobj.decstr + ')';
	pr.cr = o.find_contrast_ratio( fgobj.rel_lum, bgobj.rel_lum );
	pr.bdiff = o.find_brightness_diff( fgobj.brightness, bgobj.brightness );
	pr.cdiff = o.find_colour_diff( fgobj.rgb, bgobj.rgb );
	pr.a3 = pr.a2 = pr.b1 = pr.c1 = o.fcls;
	if ( pr.cr >= o.lthresh_aaa )		{ pr.a3 = pr.a2 = o.pcls }
	else if ( pr.cr >= o.lthresh_aa )	{ pr.a3 = o.okcls; pr.a2 = o.pcls }
	else if ( pr.cr >= o.lthresh_a )	{ pr.a2 = o.okcls }
	if ( pr.bdiff >= o.brt_thresh )		{ pr.b1 = o.pcls }
	if ( pr.cdiff >= o.col_thresh )		{ pr.c1 = o.pcls }
	pa.pairs[id] = { id: id, pr: pr };
	pa.pairs[revid] = { id: revid, pr: pr };
	return pa.pairs[id];
 },

 swap_cols:  function ()		{ var o = this; return o.change_both_cols( o.curcol.bg, o.curcol.fg, false, true, false   ); },
 reset_cols: function ( is_init )	{ var o = this; return o.change_both_cols( o.defcol.fg, o.defcol.bg, false, true, is_init ); },

 check_buttons: function ( error ) {
	var o = this;
	o.remove_dialogues();
	var pa = o.palette;
	if ( error ) { pa.e.submit.className = 'greyout'; pa.e.submit.title = 'Submit will fail: ' + error; return }
	if ( error == '' ) { // only true when onchange handler of input is triggered with no error (empty string) - form has not been submitted yet
		var newfgobj = o.get_object( pa.e.thisfg.value );
		var newbgobj = o.get_object( pa.e.thisbg.value );
		if ( newfgobj.shortcol == o.curcol.fg && newbgobj.shortcol == o.curcol.bg )
			{ o.disable_button( pa.e.submit ) } else { o.enable_button( pa.e.submit ) }
	}
	else {
		if ( o.curcol.fg == o.defcol.fg && o.curcol.bg == o.defcol.bg )
			{ o.disable_button( pa.e.reset ) } else { o.enable_button( pa.e.reset ) }
		if ( o.curcol.fg == o.curcol.bg )
			{ o.disable_button( pa.e.swap ) } else { o.enable_button( pa.e.swap ) }
		o.disable_button( pa.e.submit );
		o.check_saved();
		o.check_viewbuttons();
	}
 },

 check_viewbuttons: function () {
	var o = this;
	var pa = o.palette;
	var bs = o.lists[o.curlist][0].getElementsByTagName( 'button' );
	for ( var i = 0; i < bs.length; i++ ) {
		var butt = bs[i];
		if ( butt.fg == null ) continue;
		if ( o.curcol.fg == butt.fg && o.curcol.bg == butt.bg )
			{ o.disable_button( butt ) } else { o.enable_button( butt ) }
	}
 },

 disable_button: function ( b ) { b.title = b.greytitle; b.className = 'greyout'; },
 enable_button:  function ( b ) { b.title = b.deftitle;  b.className = ''; b.removeAttribute( 'class' ); },

 update_status: function ( isundo_or_redo ) {
	var o = this;
	var pa = o.palette;
	pa.e.thisfg.value = o.curcol.fg;
	pa.e.thisbg.value = o.curcol.bg;

	var fgobj = o.get_object( o.curcol.fg );
	var bgobj = o.get_object( o.curcol.bg );
	var pro = o.get_pairs_info( fgobj, bgobj );
	var pr = pro.pr;
	pa.e.stat.firstChild.data = pr.stat;
	pa.e.aaa.parentNode.className = pr.a3;	pa.e.aaa.firstChild.data = pa.expl.aaa[ pr.a3 ];
	pa.e.aa.parentNode.className = pr.a2;	pa.e.aa.firstChild.data = pa.expl.aa[ pr.a2 ];
	pa.e.bd.parentNode.className = pr.b1;	pa.e.bd.firstChild.data = pa.expl.bd[ pr.b1 ];
	pa.e.cd.parentNode.className = pr.c1;	pa.e.cd.firstChild.data = pa.expl.cd[ pr.c1 ];
	pa.e.lumcr.firstChild.data = pr.cr + ':1';
	pa.e.bdiff.firstChild.data = pr.bdiff;
	pa.e.cdiff.firstChild.data = pr.cdiff;

	o.check_buttons();
	if ( isundo_or_redo ) return;

	var colpair = [ o.curcol.fg, o.curcol.bg ];
	if ( pa.undolist.length == o.undolimit ) { pa.undolist.pop() }
	pa.undolist.unshift( colpair );
	if ( pa.undolist.length == 1 ) { o.disable_button( pa.e.undo ) } else { o.enable_button( pa.e.undo ) }
	pa.redolist.length = pa.e.undo.redolen = 0; o.disable_button( pa.e.redo );
 },

 find_contrast_ratio: function ( fglum, bglum ) {
	var o = this;
// The contrast ratio is: (L1 + 0.05) / (L2 + 0.05) where L1 is the relative luminance of the lighter colour
	var L1 = Math.max( fglum, bglum );
	var L2 = Math.min( fglum, bglum );
	var CR = ( L1 + 0.05 ) / ( L2 + 0.05 );
	return CR.toFixed( 2 );
 },

 find_brightness_diff: function ( fgbri, bgbri ) {
	var o = this;
	return Math.floor( Math.abs( fgbri - bgbri ) );
 },

 find_colour_diff: function ( fgrgb, bgrgb ) {
	var o = this;
// (maximum (Red value 1, Red value 2) - minimum (Red value 1, Red value 2)) +
// (maximum (Green value 1, Green value 2) - minimum (Green value 1, Green value 2)) +
// (maximum (Blue value 1, Blue value 2) - minimum (Blue value 1, Blue value 2))
	var maxred	= Math.max( fgrgb.r, bgrgb.r );
	var minred	= Math.min( fgrgb.r, bgrgb.r );
	var maxgreen	= Math.max( fgrgb.g, bgrgb.g );
	var mingreen	= Math.min( fgrgb.g, bgrgb.g );
	var maxblue	= Math.max( fgrgb.b, bgrgb.b );
	var minblue	= Math.min( fgrgb.b, bgrgb.b );
	return Math.floor( Math.abs( maxred + maxgreen + maxblue - minred - mingreen - minblue ) );
 },

 open_saved: function ( f ) {
	var o = this;
	var pa = o.palette;
	pa.e.saved.style.display = 'block';
	pa.e.saved.closed = false; // XXX expando
	if ( f ) pa.e.savedpairs.focus();
 },

 close_saved: function ( f ) {
	var o = this;
	var pa = o.palette;
	o.remove_dialogues();
	pa.e.saved.style.display = 'none';
	pa.e.saved.closed = true; // XXX expando
	if ( f ) pa.e.savedpairs.focus();
 },

 toggle_saved: function ( f ) {
	var o = this;
	var pa = o.palette;
	if ( pa.e.saved.closed ) o.open_saved( f );
	else			 o.close_saved( f );
 },

 check_saved: function () {
	var o = this;
	var pa = o.palette;
	var lname = o.lists[o.curlist][2];
	if ( document.getElementById( 'x' + o.curcol.fg + '-' + o.curcol.bg + '-' + o.curlist ) ) {
		o.disable_button( pa.e.savepair );
		pa.e.savepair.title = "This pair is already saved in List '" + lname + "'";
	}
	else if ( o.get_listcount( o.curlist ) >= o.maxpairs ) {
		o.disable_button( pa.e.savepair );
		pa.e.savepair.title = 'You have Reached the Maximum ' + o.maxpairs + " pairs for List '" + lname + "'";
	}
	else {
		o.enable_button( pa.e.savepair );
		pa.e.savepair.title = "Save this pair to Current List '" + lname + "'";
	}
 },

 save_pair: function ( lno, fgstr, bgstr, is_init ) {
	var o = this;
	if ( o.get_listcount( lno ) >= o.maxpairs ) return false;
	var pa = o.palette;
	var g = self.SMM.wcomp.gen;
	if ( !fgstr ) fgstr = o.curcol.fg;
	if ( !bgstr ) bgstr = o.curcol.bg;
	var fgobj = o.get_object( fgstr );
	var bgobj = o.get_object( bgstr );
	var pro = o.get_pairs_info( fgobj, bgobj );
	var id = pro.id + '-' + lno;
	if ( document.getElementById( id ) ) return false;
	var pr = pro.pr;

	var item = g.cElt( 'div', id, 'saveditem' );
//	item.title = fgobj.hashcol + ' on ' + bgobj.hashcol;
	
	var h = g.cElt( 'h4' );
	h.appendChild( g.cElt( 'code', '', '', fgobj.hashcol ) );
	g.aTxt( h, "\u00a0" + 'on' + "\u00a0" );
	h.appendChild( g.cElt( 'code', '', '', bgobj.hashcol ) );
	item.appendChild( h );
	g.aTxt( item, "\u00a0" );

	var viewbutton = g.cElt( 'button', '', '', 'Go' );
	viewbutton.deftitle = 'Set palette to these values';
	viewbutton.greytitle = pa.e.submit.greytitle;
	viewbutton.title = viewbutton.deftitle;
	viewbutton.fg = fgobj.shortcol; // XXX expando
	viewbutton.bg = bgobj.shortcol; // XXX expando
	item.appendChild( viewbutton );
	g.aTxt( item, "\u00a0" );

	var delbutton = g.cElt( 'button', '', '', 'Del' );
	delbutton.deftitle = 'Delete this pair';
	delbutton.title = delbutton.deftitle;
	item.appendChild( delbutton );
	g.aTxt( item, "\u00a0" );

	var detbutton = g.cElt( 'button', '', '', 'More' );
	item.appendChild( detbutton );

	var s1 = g.cElt( 'span', '', 'sample', 'Samp' );
	s1.style.backgroundColor = bgobj.hashcol;
	s1.style.color = fgobj.hashcol;
	var s3 = g.cElt( 'span', '', pr.a3, pr.a3 );
	var s4 = g.cElt( 'span', '', pr.a2, pr.a2 );
	var s5 = g.cElt( 'span', '', pr.b1, pr.b1 );
	var s6 = g.cElt( 'span', '', pr.c1, pr.c1 );
	s3.title = 'Contrast Ratio=' + pr.cr + ':1 => AAA: ' + pa.expl.aaa[ pr.a3 ];
	s4.title = 'Contrast Ratio=' + pr.cr + ':1 => AA: ' + pa.expl.aa[ pr.a2 ];
	s5.title = 'Brightness Difference=' + pr.bdiff + ' => ' + pa.expl.bd[ pr.b1 ];
	s6.title = 'Colour Difference=' + pr.cdiff + ' => ' + pa.expl.cd[ pr.c1 ];

	var summ = g.cElt( 'span', '', 'summary', ' ' );
	summ.appendChild( s1 ); g.aTxt( summ, "\u00a0" );
	summ.appendChild( s3 ); g.aTxt( summ, "\u00a0" );
	summ.appendChild( s4 ); g.aTxt( summ, "\u00a0" );
	summ.appendChild( s5 ); g.aTxt( summ, "\u00a0" );
	summ.appendChild( s6 );
	item.appendChild( summ );

	var div = g.cElt( 'div', '', 'details' );

	var p = pa.e.examplep.cloneNode( true );
	p.style.color = fgobj.hashcol;
	p.style.backgroundColor = bgobj.hashcol;
	div.appendChild( p );

	g.aTxt( div, 'CR=' + pr.cr + ':1 => ' );
	div.appendChild( g.cElt( 'br' ) ); //XXX why do we have this?

	var ul = g.cElt( 'ul' );
	ul.appendChild( g.cElt( 'li', '', pr.a3, 'AAA: ' + pa.expl.aaa[ pr.a3 ] ) );
	ul.appendChild( g.cElt( 'li', '', pr.a2, ' AA: ' + pa.expl.aaa[ pr.a2 ] ) );
	div.appendChild( ul );

	g.aTxt( div, 'BD=' + pr.bdiff + ' => ' );
	div.appendChild( g.cElt( 'span', '', pr.b1, pa.expl.bd[ pr.b1 ] ) );
	g.aTxt( div, ', CD=' + pr.cdiff + ' => ' );
	div.appendChild( g.cElt( 'span', '', pr.c1, pa.expl.cd[ pr.c1 ] ) );

	item.appendChild( div );

	var listdiv = o.lists[lno][0];
	var elt = listdiv.firstChild.nextSibling;
	if ( elt.className == 'savedempty' ) listdiv.removeChild( elt );
	listdiv.appendChild( item );

	delbutton.onclick = o.onclick_removepr;
	detbutton.onclick = o.onclick_toggle_info;
	viewbutton.onclick = o.onclick_view_item;
	o.toggle_info( detbutton );
	o.enable_button( o.lists[lno][4] );
	if (!is_init) {
 //if we're restoring saved pairs on init, don't open saved div or export to cookie - also don't need to check buttons as we're doing a change_both_cols straight after
		o.open_saved( false );
		o.export_to_cookie( true );
		o.check_saved();
		o.check_viewbuttons();
		o.update_listcount(lno);
	}
	return true;
 },

 confirm_removepr: function ( butt ) {
	var o = this;
	o.remove_dialogues();
	if ( !butt || !butt.parentNode ) return false;
	o.p_confirm( butt.title + '?', null, o.onclick_do_removepr, butt.parentNode );
	return true;
 },

 do_removepr: function( item ) {
	var o = this;
	o.remove_dialogues();
	var div = item.parentNode;
	if ( !div ) return;
	o.removepr( item );
	o.update_listcount( div.lno );
	o.export_to_cookie( true );
 },

 removepr: function ( item ) {
	var o = this;
	item.removeAttribute( 'id' );
	// First remove handlers from item ( all handlers are on children of item )
	var kids = item.childNodes;
	for ( var i = 0; i < kids.length; i++ ) { if ( kids[i].onclick ) { kids[i].onclick = null } }
	var div = item.parentNode;
	div.removeChild( item );
	if ( div.childNodes.length == 1 ) { div.appendChild( o.lists[div.lno][1] ); o.disable_button( o.lists[div.lno][4] ) }
	o.check_saved();
 },

 confirm_elist: function ( butt ) {
	var o = this;
	o.remove_dialogues();
	if ( butt.parentNode.parentNode.nextSibling.className == 'savedempty' ) return false;
	var lno = butt.parentNode.lno;
	o.p_confirm( butt.title + '?', lno, o.onclick_do_elist );
	return true;
 },

 do_elist: function( lno ) { 
	var o = this;
	o.remove_dialogues();
	o.elist( lno );
	o.update_listcount( lno );
	o.export_to_cookie( true );
 },

 elist: function ( lno ) {
	var o = this;
	var items = new Array();
	var kids = o.lists[lno][0].childNodes;
	for ( var i = 0; i < kids.length; i++ ) {
		if ( kids[i].id ) items[ items.length ] = kids[i];
	}
	for ( var j = 0; j < items.length; j++ ) { o.removepr( items[j] ); }
 },

 confirm_dlist: function ( butt ) {
	var o = this;
	o.remove_dialogues();
	var pa = o.palette;
	// don't allow deletion if there's only one list left
	if ( pa.e.listcont.childNodes.length == 1 ) return false;
	var lno = butt.parentNode.lno;
	o.p_confirm( butt.title + '?', lno, o.onclick_do_dlist );
	return true;
 },

 do_dlist: function( lno ) {
	var o = this;
	o.remove_dialogues();
	o.dlist( lno );
	o.export_to_cookie( true );
 },
 
 dlist: function ( lno ) {
	var o = this;
	var arr = o.lists[lno];
	if ( !arr ) return;
	// Remove all pairs from the list
	o.elist( lno );
	var op = arr[3];
	// Change to an appropriate other list
	var ind = op.index;
	var newind = ind - 1;
	if ( ind == 0 ) newind = ind + 1;
	var newop = op.parentNode.options[ newind ];
	o.change_list( newop.value );
	// Remove deleted option from select
	op.parentNode.remove( ind );
	// Remove handlers from specific buttons
	var bts = arr[4].parentNode.childNodes;
	for ( var i = 0; i < bts.length; i++ ) { if ( bts[i].onclick ) { bts[i].onclick = null } }
	// Remove div itself
	arr[0].parentNode.removeChild( arr[0] );
	o.lists[lno] = null;
	o.check_listbutts();
 },

 check_lname: function ( name ) {
	var o = this;
	var pa = o.palette;
	if ( name.match( pa.listpatt ) ) return '';
	var lenwarn = "\nUse only the characters a-z A-Z 0-9 . and space";
	if ( name.length > o.namelen ) lenwarn = "\nAt " + name.length + ' characters, your name is too long - please use a maximum of ' + o.namelen + ' characters';
	return 'Error - Invalid Name.' + lenwarn;
 },

 prompt_nlist: function ( def, err ) {
	var o = this;
	var pa = o.palette;
	var newnumlists = pa.e.listcont.childNodes.length + 1;
	if ( newnumlists > o.maxlists ) { o.remove_dialogues(); return false; }
	o.p_prompt( 'New List:', def, err );
	return true;
 },

 do_nlist: function () {
	var o = this;
	var pa = o.palette;
	if ( !o.nlist() ) o.p_alert( pa.e.newlist.title, pa.e.psub );
 },

 nlist: function () {
	var o = this;
	var pa = o.palette;
	var name =  pa.e.pinp.value;
	name = self.SMM.wcomp.gen.norm_ws( name );
	var err = o.check_lname( name );
	if ( err ) { return o.prompt_nlist( name, err ) }
	o.remove_dialogues();
	return o.new_list( name );
 },

 new_list: function ( name ) {
	var o = this;
	var pa = o.palette;
	var newnumlists = pa.e.listcont.childNodes.length + 1;
	if ( newnumlists > o.maxlists ) return false;
	var ol = o.lists;
	var num = ol.length;
	for ( var i = 0; i < ol.length; i++ ) {
		if ( !ol[i] ) { num = i; break; }
	}
	o.create_list( num, name );
	o.change_list( num );
	o.export_to_cookie( true );
	return true;
 },

 prompt_rlist: function ( def, err, lno ) {
	var o = this;
	if ( lno == null || !o.lists[lno] ) return false;
	if ( !err ) def = o.lists[lno][2];
	o.p_prompt( 'Rename List:', def, err, lno );
	return true;
 },

 do_rlist: function ( lno ) {
	var o = this;
	var pa = o.palette;
	if ( !o.rlist( lno ) ) o.p_alert( 'Error renaming list', pa.e.psub );
 },

 rlist: function ( lno ) {
	var o = this;
	var pa = o.palette;
	var name =  pa.e.pinp.value;
	name = self.SMM.wcomp.gen.norm_ws( name );
	var err = o.check_lname( name );
	if ( err ) { return o.prompt_rlist( name, err, lno ) }
	o.remove_dialogues();
	return o.rename_list( name, lno );
 },

 rename_list: function ( name, lno ) {
	var o = this;
	if ( lno == null || !o.lists[lno] ) return false;
	o.lists[lno][2] = name;
	o.lists[lno][0].firstChild.firstChild.firstChild.data = name; // h3
	o.lists[lno][3].firstChild.data = name; // option
	o.export_to_cookie( true );
	return true;
 },

 toggle_info: function ( butt ) {
	var o = this;
	o.remove_dialogues();
	if ( !butt || !butt.parentNode ) return false;
	var item = butt.parentNode;
	if ( !item.parentNode ) return false;
	var spans = item.getElementsByTagName( 'span' );
	var divs = item.getElementsByTagName( 'div' );
	var summ = spans[0];
	var details = divs[0];
	if ( summ.className != 'summary' || details.className != 'details' ) return false;
	if ( details.style.display == 'none' ) {
		summ.style.display = 'none';
		details.style.display = 'block';
		butt.firstChild.data = 'Less';
		butt.title = 'Return to Summary';
	}
	else {
		summ.style.display = 'inline';
		details.style.display = 'none';
		butt.firstChild.data = 'More';
		butt.title = 'Show Details';
	}
	return true;
 },

 view_item: function ( butt ) {
	var o = this;
	if ( !butt || !butt.parentNode ) return false;
	var retval = o.change_both_cols( butt.fg, butt.bg, false, false, false );
	return retval;
 },

 undo: function () {
	var o = this;
	var pa = o.palette;
	if ( pa.undolist.length == 1 ) return false;
	pa.redolist.unshift( pa.undolist.shift() );
	pa.e.undo.redolen = pa.redolist.length;
	if ( pa.undolist.length == 1 ) { o.disable_button( pa.e.undo ) }
	o.enable_button( pa.e.redo );
	o.change_both_cols( pa.undolist[0][0], pa.undolist[0][1], true, false, false );
	return true;
 },

 redo: function () {
	var o = this;
	var pa = o.palette;
	if ( !pa.redolist.length ) return false;
	pa.undolist.unshift( pa.redolist.shift() );
	pa.e.undo.redolen = pa.redolist.length;
	if ( !pa.redolist.length ) { o.disable_button( pa.e.redo ) }
	o.enable_button( pa.e.undo );
	o.change_both_cols( pa.undolist[0][0], pa.undolist[0][1], true, false, false );
	return true;
 },

 p_prompt: function ( lab, def, err, lno ) {
	var o = this;
	o.remove_dialogues();
	var pa = o.palette;
	var p = pa.e.pprompt;
	var i = pa.e.pinp;
	var l = pa.e.plab;
	i.value = def;
	l.firstChild.data = lab;
	if ( lno != null ) {
	    var d2 = o.lists[lno][0].firstChild;
	    d2.appendChild( p );
	    p.onsubmit = o.onsubmit_sub_rlist;
	    pa.e.psub.onclick = o.onclick_do_rlist;
	}
	else {
	    pa.e.vtop.appendChild( p );
	    p.onsubmit = o.onsubmit_sub_nlist;
	    pa.e.psub.onclick = o.onclick_do_nlist;
	}
	pa.e.pcanc.onclick = o.onclick_remove_dialogues;
	pa.e.psub.form.lno = lno; //XXX expando
	pa.e.pinp.focus();
	pa.e.pinp.select();
	var g = self.SMM.wcomp.gen;
	if ( !err ) return;
	p.appendChild( g.cElt( 'p', '', 'err', err ) );
 },

 p_confirm: function ( txt, lno, action, pr ) {
	var o = this;
	o.remove_dialogues();
	var pa = o.palette;
	var c = pa.e.pconfirm;
	pa.e.pspan.firstChild.data = txt;
	pa.e.yes.lno = lno;
	if ( lno != null ) {
		var d2 = o.lists[lno][0].firstChild;
		d2.appendChild( c );
	}
	else {
		pr.insertBefore( c, pr.lastChild );
	}
	pa.e.yes.onclick = action;
	pa.e.no.onclick = o.onclick_remove_dialogues;
	pa.e.yes.focus();
 },

 remove_dialogues: function () {
	var o = this;
	var pa = o.palette;
	var c = pa.e.pconfirm;
	pa.e.yes.onclick = null;
	pa.e.no.onclick = null;
	if ( c.parentNode ) c.parentNode.removeChild( c );
	var p = pa.e.pprompt;
	p.onsubmit = null;
	pa.e.pcanc.onclick = null;
	pa.e.psub.onclick = null;
	if ( p.lastChild.className && p.lastChild.className == 'err' ) p.removeChild( p.lastChild );
	if ( p.parentNode ) p.parentNode.removeChild( p );
 },

 p_alert: function ( txt, elt ) {
	var o = this;
	o.remove_alert();
	var pa = o.palette;
	var d = pa.e.palert;
	d.firstChild.data = txt;
	var wh = o.win_size();
	wh[0] -= 20; // scrollbar guess
	var xy = o.get_scroll();
	var wid = 0.8*wh[0];
	if ( wid > 600 ) wid = 600;
	var s_left = 0.5*(wh[0] - wid) + xy[0] - 12;
	var s_top = 0.25*wh[1] + xy[1];
	if ( elt ) {
		var lt = o.get_elt_offset(elt);
		var eh = elt.offsetHeight || 20;
		var ew = elt.offsetWidth || 20;
//		alert( 's_left=' + s_left + '  wid=' + wid + '  ew=' + ew + '  lt[0]=' + lt[0] + '  wh[0]=' + wh[0] );
		if ( lt[1] && lt[1] > xy[1] ) s_top = lt[1] + 0.75*eh;
		if ( lt[0] && lt[0] > xy[0] ) { 
			if ( lt[0] < s_left ) s_left = lt[0];
			else if ( s_left < lt[0] + ew - wid - 4 ) {
				s_left = lt[0] + ew - wid - 4;
				if ( s_left + wid + 4 > wh[0] ) s_left = wh[0] - wid - 4;
				if ( s_left < 4 ) s_left = 4;
			}
		}
	}
		
	d.style.width = Math.round(wid) + 'px';
	d.style.top = Math.round(s_top) + 'px';
	d.style.left = Math.round(s_left) + 'px';
	var b = document.body;
	b.appendChild( d );
 },

 win_size: function () {
	var w = 0, h = 0;
	if( typeof( window.innerWidth ) == 'number' ) {
		w = window.innerWidth;
		h = window.innerHeight;
	}
	else if ( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
		w = document.documentElement.clientWidth;
		h = document.documentElement.clientHeight;
	}
	else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
		w = document.body.clientWidth;
		h = document.body.clientHeight;
	}
	return [ w, h ];
 },

 get_scroll: function () {
	var x = 0, y = 0;
	if( typeof( window.pageYOffset ) == 'number' ) {
		y = window.pageYOffset;
		x = window.pageXOffset;
	}
	else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
		y = document.body.scrollTop;
		x = document.body.scrollLeft;
	}
	else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
		y = document.documentElement.scrollTop;
		x = document.documentElement.scrollLeft;
	}
	return [ x, y ];
 },

 get_elt_offset: function ( elt ) {
	var l = 0, t = 0;
	if ( elt.offsetParent != null ) {
		do {
			l += elt.offsetLeft;
			t += elt.offsetTop;
			elt = elt.offsetParent;
		} while ( elt );
	}
	return [ l, t ];
 },

 remove_alert: function () {
	var o = this;
	var pa = o.palette;
	if ( pa.e.palert.parentNode ) { pa.e.palert.parentNode.removeChild( pa.e.palert ); }
 },

// EVENT HANDLERS:
 onsubmit_check: function () {
	var o = self.SMM.wcomp.palette;
	var pa = o.palette;
	var error = o.validate_inputs();
	if ( error )	{ o.p_alert( error, pa.e.submit ); o.check_buttons( error ) }
	else		{ if ( !o.change_both_cols( pa.e.thisfg.value, pa.e.thisbg.value, false, true, false ) ) o.p_alert( pa.e.submit.title, pa.e.submit ); }
	return false;
 },
 onclick_remove_alert:	function () { self.SMM.wcomp.palette.remove_alert() },
 onclick_remove_dialogues: function () { self.SMM.wcomp.palette.remove_dialogues(); return false },
 onchange_check:	function () { var o = self.SMM.wcomp.palette; o.check_buttons( o.validate_inputs() ); },
 onclick_change_colour:	function () { self.SMM.wcomp.palette.change_one_col( this.firstChild.data, this.parentNode.shortcol ); return false; },
 onfocus_close_saved:	function () { self.SMM.wcomp.palette.close_saved( false ); }, 
 onclick_close_saved:	function () { self.SMM.wcomp.palette.close_saved( true );	return false; },
 onclick_toggle_saved:	function () { self.SMM.wcomp.palette.toggle_saved( false );	return false; },
 onclick_toggle_info:	function () { self.SMM.wcomp.palette.toggle_info( this );	return false; },
 onchange_clist:	function () { self.SMM.wcomp.palette.change_list( this.options[ this.selectedIndex ].value ); },
 onclick_view_item:	function () { var o = self.SMM.wcomp.palette; if ( !o.view_item( this ) )	{ o.p_alert( this.title, this ) }; return false; },
 onclick_save_pair:	function () { var o = self.SMM.wcomp.palette; if ( !o.save_pair(o.curlist) )	{ o.p_alert( this.title, this ) }; return false; },
 onclick_undo:		function () { var o = self.SMM.wcomp.palette; if ( !o.undo() )			{ o.p_alert( this.title + ', currently storing: ' + this.redolen, this ) }; return false; },
 onclick_redo:		function () { var o = self.SMM.wcomp.palette; if ( !o.redo() )			{ o.p_alert( this.title, this ) }; return false; },
 onclick_swap_cols:	function () { var o = self.SMM.wcomp.palette; if ( !o.swap_cols() )		{ o.p_alert( this.title, this ) }; return false; },
 onclick_reset_cols:	function () { var o = self.SMM.wcomp.palette; if ( !o.reset_cols( false ) )	{ o.p_alert( this.title, this ) }; return false; },

 onclick_removepr:	function () { self.SMM.wcomp.palette.confirm_removepr( this );	return false; },
 onclick_do_removepr:	function () { self.SMM.wcomp.palette.do_removepr(this.parentNode.parentNode); return false; },
 onclick_elist:		function () { var o = self.SMM.wcomp.palette; if ( !o.confirm_elist(this) ) { o.p_alert( this.title, this ) }; return false; },
 onclick_do_elist:	function () { self.SMM.wcomp.palette.do_elist(this.lno); return false; },
 onclick_dlist:		function () { var o = self.SMM.wcomp.palette; if ( !o.confirm_dlist(this) )	{ o.p_alert( this.title, this ) }; return false; },
 onclick_do_dlist:	function () { self.SMM.wcomp.palette.do_dlist(this.lno); return false; },
 onclick_nlist:		function () { var o = self.SMM.wcomp.palette; if ( !o.prompt_nlist('New List','') ) { o.p_alert( this.title, this ) }; return false; },
 onclick_rlist:		function () { self.SMM.wcomp.palette.prompt_rlist('','',this.parentNode.lno); return false; },
 onclick_do_nlist:	function () { self.SMM.wcomp.palette.do_nlist();return false; },
 onsubmit_sub_nlist:	function () { self.SMM.wcomp.palette.do_nlist();return false; },
 onclick_do_rlist:	function () { self.SMM.wcomp.palette.do_rlist(this.form.lno); return false; },
 onsubmit_sub_rlist:	function () { self.SMM.wcomp.palette.do_rlist(this.lno); return false; },

 Vc: {
	white:	'fff',
	black:	'000',
	red:	'f00',
	lime:	'0f0',
	blue:	'00f',
	yellow:	'ff0',
	aqua:	'0ff',
	fuchsia:'f0f',
	silver:	'c0c0c0',
	gray:	'808080',
	maroon:	'800000',
	green:	'008000',
	navy:	'000080',
	olive:	'808000',
	teal:	'008080',
	purple:	'800080',
	orange:	'ffa500'
 }
};
