﻿
Array.prototype.find = function ( item, caseSensitive )
{
	if (this.length < 1) return -1;
	if (caseSensitive == true && typeof(item) == "string")
	{
		var itemL = item.toLowerCase();
		for (var k = this.length; k >= 0; k--)
			if (itemL == (new String(this[k])).toLowerCase()) return k;
	}
	else
		for (var k = this.length; k >= 0; k--)
			if (this[k] == item) return k;
	return -1;
};

Array.prototype.has = function ( item, caseSensitive )
{
	if (this.length < 1) return false;
	if (typeof(item) == "string")
	{
		var s = String.bellChar + this.join( String.bellChar ) + String.bellChar;
		return (s.indexOf( String.bellChar + item + String.bellChar, 0, caseSensitive ) > -1);
	}

	for (var k = this.length; k >= 0; k--)
		if (this[k] == item) return true;
	return false;
};

Array.prototype.item = function ( idx )
{
	return this[idx];
};

Array.prototype.coalesce = function( isText )
{
	for (var k = 0; k < this.length; k++)
		if (this[k] != null && typeof( this[k] != "undefined") && (typeof(isText) == "undefined" || this[k] != "")) return this[k];
	return null;
};

Array.prototype.randomItem = function()
{
    var n = 0;
    if (this.length > 1) n = Math.round((this.length - 1) * Math.random());
    return this[n];
}
Array.prototype.append = function( a )
{
   if (!(a instanceof Array)) a = [a];
   for (var k = 0; k < a.length; k++)
      this.push(a[k]);
   return this;
}

String.prototype.contains = function ( search )
{
	if (search instanceof RegExp) 
		return (this.search( search ) > -1);
	else
		return (this.indexOf( search ) > -1);
};

String.prototype.startsWith = function ( fragment )
{
	if (typeof(fragment) != "string") return null;
	return (this.substr(0,fragment.length) == fragment);
};

String.prototype.endsWith = function ( fragment )
{
	if (typeof(fragment) != "string") return null;
	return (this.slice(0 - fragment.length) == fragment);
};

String.prototype.dropTrailing = function ( fragment )
{
	return this.endsWith( fragment ) ? this.slice( 0, 0 - (fragment.length) ) : this;
};

String.prototype.truncate = function ( len )
{
	if (!len || len < 0 || this.length < len) return this;
	return this.substring( 0, len - 1 ) + "…";
};

String.bellChar = String.fromCharCode( 7 );

String.prototype.replaceAll = function( search, replace )
{
	return this.replace( new RegExp( search, "g" ), replace );
};

String.prototype.splitAt = function( at )
{
	if (typeof(at) == "number")
		return [ this.substr( 0, Math.floor(at) ), this.substr( Math.floor(at) ) ];
	var k = this.search( at );
	if (k < 0) return [ this ];
	return [ this.substr( 0, k ), this.substr( k + ((at instanceof RegExp)? 0 : at.length) ) ];
};

String.prototype.escapeHtml = function()
{
	return this.replace( /\&/g, "&amp;" )
		.replace( /\</g, "&lt;" );
};

String.prototype.unescapeHtml = function()
{
	return this.replace(/&amp;/g,"&")
		.replace(/&lt;/g,"<");
};

String.prototype.escapeXml = function()
{
	return this.replace( /\&/g, "&amp;" )
		.replace( /\</g, "&lt;" ).replace( /\>/g, "&gt;" )
		.replace( /"/g, "&quot;" ).replace( /'/g, "&apos;" );
};

String.prototype.unescapeXml = function()
{
	return this.replace(/&amp;/g,"&")
		.replace(/&lt;/g,"<").replace(/&gt;/g,">")
		.replace(/&quot;/g,"\"").replace(/&apos;/g,"'");
};

String.prototype.trim = function( scope )
{
	if (scope === undefined) 
		return this.replace(/(^\s*)|(\s*$)/g, "");
	var s = String(scope).substr( 0, 1 ).toLowerCase();
	if (s == "s" || s == "l" || s == "t")	// 'start', 'left', true
		return this.replace(/^\s*/g, "");
	else
		return this.replace(/\s*$/g, "");
};
String.prototype.quote = function( q )
{
    if (q != '"') q = "'";
    if (q == "'") return q + this.replace( /'/g, "&#96;" ) + q;
    return q + this.replace( /"/g, '&#34;' ) + q;
}
String.prototype.isEmailAddress = function ()
{
	return (this.search( /^([a-zA-Z0-9'_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/ ) == 0);
};

Number.prototype.zeroPad = function ( pad )
{
	var s = this.toString();
	if (s.length < pad)
		s = "00000000000000".substring( 0, pad - s.length ) + s;
	return s;
};
Number.prototype.between = function( min, max )
{
   return (this <= max && this >= min);
};

// all numerics aren't Numbers by default so a static method will be created instead
Number.format = function ( pNum, format )
{
	num = Number(pNum);
	if (isNaN( num )) return "?";
	if (typeof(format) != "string" || format == "decimal") return num.toString( );
	if (format == "integer") return Math.round( num ).toString( );
	format = String( format ).toLowerCase();
	if (format == "integer") format = "9";
	var parens = format.contains( /\(/ );
	if (parens) format = format.replace( /\(|\)/g, "" );
	if (format.contains( /money|currency/ )) format = "$,9.99";
	format = format.trim();
	var comma = format.contains(",");
	if (comma) format = format.replace( /,/g, "" );
	var dollar = format.contains("$");
	if (dollar) format = format.replace( /\$/g, "" );	
	var zero = (format.substr(0,1) == "0");
		
	var w = 0, f = 0;
	var a = format.split(".");
	if (a.length > 2) return "?" + num.toString();
	if (a.length > 1) f = a[1].length;
	w = a[0].length;
	
	var s = Math.abs( num ).toFixed( (f > 20 ? 20 : f) );
	a = s.split( "." );
	if (comma)
	{
		for (var k = a[0].length - 3; k > 0; k -= 3)
		{
			a[0] = a[0].substr( 0, k ) + "," + a[0].substr( k );
		}
		w += Math.floor((w + 1) / 3);
		zero = false;
	}
	if (dollar) 
	{
		a[0] = "$"+ a[0];
		w += 1;
		zero = false;
	}
	if (num < 0)
		if (parens)
		{
			a[0] = "("+ a[0];
			a[a.length - 1] += ")";
			zero = false;
		}
		else
		{
			a[0] = "-"+ a[0];
		}

	w -= a[0].length;
	if (w > 0)
		a[0] = (zero ? "00000000000000000000" : "                  ").substr(0,w) + a[0].toString();
		
	return a.join( "." ).trim();
};

Math.randomNumber = function( num )
{
	var n = parseInt( num, 10 );
	if (isNaN(n)) return null;
	return Math.round( n * Math.random() );
};

Date._months = "January/February/March/April/May/June/July/August/September/October/November/December".split("/");
Date._days = "Sunday/Monday/Tuesday/Wednesday/Thursday/Friday/Saturday".split("/");
Date.ISO_FORMAT = "yyyy-mm-ddThh:mm:ss";
Date.prototype._toString = Date.prototype.toString;
Date.prototype.toString = function ( format )
{
	// year | yyyy | yr | yy : 4/2 digit year
	// mm | m (as date): 2/1 digit month
	// month | mon: full/abbrevated name of month
	// dd | d: 2/1 digit date
	// weekday | wd: full/abbreviated day of week
	// hour | hr | h: 2/1 digit hour
	// min | mm (as time): 2 digit minute
	// sec | ss: 2 digit second
	
	if (typeof(format) != "string") return this._toString();
	
	// normalize the format
	var fmt = format.toLowerCase().replace( /year/g,"yyyy" ).replace( /yr/g,"yy" ).replace( /hour|hr/g,"hh" ).replace( /min/g, "mm" ).replace( /sec/g, "ss" ).replace( /am\/pm|pm/g, "am" );
	fmt = fmt.replace("weekday","week").replace("wd","wx").replace("month","zontx").replace("mon","xon");		// handle conflicts with 'd' and 'm' and 'h'
	if (fmt.search(/iso|xml/) == 0) 
		fmt = Date.ISO_FORMAT;
	else if (fmt == "date") 
		fmt = "mm/dd/yyyy";
	else if (fmt == "time") 
		fmt = "hh:mmam";
	else if (fmt == "datetime") 
		fmt = "mm/dd/yyyy hh:mmam";

	var z = { h: this.getHours(), mm: this.getMinutes().zeroPad(2), ss: this.getSeconds().zeroPad(2),
			yyyy: this.getFullYear(), yy: this.getFullYear().toString().slice(-2), mD: (this.getMonth()+ 1),
			d: this.getDate(), month: Date._months[this.getMonth()],
			weekday: Date._days[this.getDay()] };
	if (fmt.contains(/a\/p|am/)) 
	{
		z.ampm = (z.h >= 12) ? "p" : "a";
		if (z.h == 0) z.h = 12; else if (z.h > 12) z.h -= 12;
	}
	z.hh = z.h.zeroPad(2);
	z.mmD = z.mD.zeroPad(2);
	z.mon = z.month.substr(0,3);
	z.dd = z.d.zeroPad(2);
	z.wd = z.weekday.substr(0,3);
	fmt = fmt.replace( "ss", z.ss ).replace( "hmm", "h"+ z.mm ).replace( ":mm", ":"+ z.mm );
	fmt = fmt.replace( "hh", z.hh ).replace( "h", z.h ).replace( "a/p", z.ampm ).replace( "am", z.ampm+ "%" );
	fmt = fmt.replace( "yyyy", z.yyyy ).replace( "yy", z.yy ).replace( "mm", z.mmD ).replace("m", z.mD );
	fmt = fmt.replace( "dd", z.dd ).replace( "d", z.d ).replace( "zontx",z.month ).replace( "xon",z.mon );
	fmt = fmt.replace( "week", z.weekday ).replace( "wx", z.wd );

	return fmt.replace("%","m");
};
Date.convert24HourTime = function( time )
{
   time = time * 1;
   var h = Math.floor(time / 100), m = time % 100;
   m = ':'+ m.zeroPad(2);
   if (h < 12) m+= 'am'; else m+= 'pm';
   if (h == 0) h = 12;
   if (h > 12) h = h - 12;
   return h + m;
};
Date.prototype.roundToQtrHour = function( )
{
   var n = this.getMinutes();
   n = Math.round( n / 15 ) * 15;
   this.setMinutes( n );
   return this;
};
Date.prototype.elapsedMilliseconds = function( date )
{
   if (!date) date = new Date();
   if (!(date instanceof Date)) return null;
   return date.getTime() - this.getTime();
};
Date.parseDate = function ( text )	// supports dates in yyyy-mm-dd order
{
	var dt = new Date( text );
	if (isNaN(dt))
	{
	   var n = parseInt( text, 10 );
	   if (n > 19710100 && n < 20781232 ) 
	      dt = new Date( Math.floor(n / 10000), (Math.floor(n / 100) % 100) - 1, n % 100 ); 
	}
	if (isNaN( dt ))
	{
		dt = new String( text );
		var time = "";
		var k = dt.search( /[\sT]/ );	// whitespace or T seperates date from time
		if (k > -1) 
		{
			time = dt.substring( k + 1 );
			dt = dt.substring( 0, k );
		}
		if (dt.indexOf(":") > -1 && time == "") 
		{	// input is time only
			time = dt;
			dt = "";
		}
		if (dt == "")
			dt = new Date();
		else
		{
			dt += "----";
			var a = text.split( "-" );
			dt = new Date( parseInt( a[0], 10 ), parseInt( a[1], 10 ) - 1, parseInt( a[2], 10 ) );		}		if (!isNaN( dt ) && time != "")		{			dt = (dt.getMonth()+ 1) + "/"+ dt.getDate() + "/"+ dt.getFullYear() + " "+ time;			dt = new Date( dt );		}	}
	if (dt.getFullYear() < 2000 && text.indexOf(dt.getFullYear()) < 0) dt.setFullYear( dt.getFullYear() + 100 );
	return dt;
};
Date.prototype.addDays = function( days )
{
   this.setDate( this.getDate() + days ); return this;
};
Date.prototype.addHours = function( hours )
{
   this.setHours( this.getHours() + hours ); return this;
};
Date.prototype.addMinutes = function( minutes )
{
   this.setMinutes( this.getMinutes() + minutes ); return this;
};
Date.prototype.addSeconds = function( seconds )
{
   this.setSeconds( this.getSeconds() + seconds ); return this;
};
Date.prototype.addHMS = function( hours, minutes, seconds )
{
   if (typeof(hours) == "number") this.setHours( this.getHours() + hours );
   if (typeof(minutes) == "number") this.setMinutes( this.getMinutes() + minutes );
   if (typeof(seconds) == "number") this.setSeconds( this.getSeconds() + seconds );
   return this;
};
Date.prototype.daysDifference = function( date )
{
   return (this - date) / 86400000;
};
Date.prototype.setHMS = function( hours, minutes, seconds )
{
   this.setHours( (typeof(hours) == "number") ? hours : 0 );
   this.setMinutes( (typeof(minutes) == "number") ? minutes : 0 );
   this.setSeconds( (typeof(seconds) == "number") ? seconds : 0 );
   this.setMilliseconds(0);
   return this;
};
Date.prototype.getEndOfMonth = function()
{
    var d = new Date( this.valueOf() );
    d.setMonth( d.getMonth() + 1 ); d.setDate( 0 );
    return d;
};
Date.prototype.getStartOfWeek = function()
{
    var d = new Date( this.valueOf() );
    d.setDate( d.getDate() - d.getDay() );
    return d;
};
Date.prototype.getEndOfWeek = function()
{
    var d = new Date( this.valueOf() );
    d.setDate( d.getDate() - d.getDay() + 6 );
    return d;
};
Date.parseTime = function ( t, defaultAmHour )
{
 t = t.toLowerCase().replace(/\.|m|\s/g,"").replace("a","am").replace("p","pm");
 if (t.indexOf(":") < 0)
 {
  var k = t.search( /a|p/ );
  if (k < 0) 
   t += ":00";
  else
   t = t.substr( 0, k ) + ":00" + t.substr(k);
 }
 var d = new Date( "1/1/1970 "+ t );
 if (isNaN(d)) return null;
 if (t.indexOf("m") < 0 && d.getHours() < defaultAmHour) d.setHours( d.getHours()+12 );
 if (d.getFullYear() < 1970) return null;
 return d;
};


var d3 = {};
var d3 = { log$: []};
d3.log = function() { return; }
d3.enableDebugLog = function( enable )
{
   if (enable)
   {
      d3.log = function( msg, scope, reset )
      {
         if (reset == "reset")
            d3.log$ = [];
         var d = new Date();
         d = [d.toLocaleTimeString()+"."+ d.getMilliseconds().zeroPad(3)];
         if (scope) d.push(scope);
         d.push(msg);
         d3.log$.push(d.join("|"));
         return msg;
      }
   }
   else
      d3.log = function( msg ) { return msg; }
}

d3.ifNot = function( a, b )
{
   if (typeof(a) == "undefined" || a == null) return b;
   if (typeof(a) == "object") return a;   // handle cases where a can't be compared to a boolean
   if (a == false) return b;
   return a;
};
d3.ifNotNumeric = function( a, b )
{
    if (isNaN(a)) return b; else return a;
};
d3.generateId = function( o )
{
   if (o.getAttribute)
   {
      var id = o.getAttribute("id");
      if (id == null || id == "") o.id = [ o.tagName ? o.tagName : "AUTO", (new Date()).valueOf() ].join("-");
   }
   return o.id;
};
var getHtmlParamValue = function( text, param, defValue )
{
   if (typeof(text) == "undefined" || typeof(param) == "undefined") return defValue;
   text = new String(text);
   param = (new String( param )).toLowerCase();
   var a = text.substr(text.indexOf("?")+1).split("&");
   for (var k = 0; k < a.length; k++)
   {
      var v = a[k].split("=");
      if (v[0].toLowerCase() == param)
      {
         v.shift();
         return v.join("=");
      }
   }
   return defValue;
};

d3.getChildElements = function( o )
{
   var a = [];
   if (!o.hasChildNodes()) return a;
   for (var k = 0; k < o.childNodes.length; k++)
      if (o.childNodes[k].tagName) a.push( o.childNodes[k] );
   return a;
}

d3.getXml = function( url )
{
   d3.getXml.error = "";
   d3.log(url,"get-xml");
   var data = $.ajax({ url: url, async: false});
   if (!data.responseXML.documentElement) d3.getXml.error = "failed: "+ url.split("&")[0]+ "\n"+ String(data.responseText).substr(0,512);
   d3.log(url + (d3.getXml.error ? " failed" : " OK"),"get-xml");
   return data;
};
d3.detachNode = function( o )
{
   if (o) return o.parentNode.removeChild(o);
   return o;
}
d3.XmlDoc = function ( path )
{
    var doc  = new ActiveXObject( "MSXML2.DOMDocument" );
    if (path)
    {
        doc.load( path );
        window.console( "loaded "+ path + "; parseError="+ doc.parseError );
    }
    return doc;
};

d3.env = 
{
   HAS_ACTIVEX : (typeof(ActiveXObject) != "undefined"),
   IS_IE : (typeof(window) != "undefined" && typeof(ActiveXObject) != "undefined"),
   IS_IE7 : ((typeof(window) != "undefined" && typeof(ActiveXObject) != "undefined") && navigator.userAgent.search( /MSIE\s+7/i ) > -1)
};

d3.parseAttribute = function( object, attribute )
{
   var tag = object.outerHTML.split(">");			// get the tag and attributes
   tag = tag[0].split( " "+ attribute+ "=" );	// search for the given attribute
   if (tag.length < 2) return null;
	
   tag = tag[1].split( /[ \/>]/ )[0];					// get the attribute value
   if (tag.search( /^['"].*["']$/ ) == 0) tag = tag.substr( 1, tag.length - 2 );	// remove enclosing quotes

   return tag.unescapeXml();
}

d3.TABLE_TAGS = [ "TABLE", "TH", "TR", "TD", "TBODY", "THEAD", "TFOOT" ];
d3.getAttribute = function( o, a, d )
{
   var r;
   if ( d3.env.IS_IE && o.tagName && d3.TABLE_TAGS.has(o.tagName.toUpperCase()) )
   {
      // IE can't get namespaced attributes for table elements
      // this isn't foolproof but workarounds to bugs usually aren't
      r = d3.parseAttribute( o, a );
      return (r ? r : d);
   }
   if (typeof(o.getAttribute) == "undefined")
   {
      try { r = o[a]; } catch (e) {}
      return (r ? r : d);
   } 
   r = o.getAttribute( a );
   return (r ? r : d);
};

d3.about = function ( alt, tab, recurse )
{
   var s = "";
   var object = (alt ? alt : this);
   var aFunc = new Array(), aProp = new Array;
   if (object._about) aProp.push( object._about );
   if (!recurse) recurse = (object.about && object.about.recurse);
   for (var item in object)
   {
	   if (item.substring(0,1) == "_" || item == "about") continue;	// skip internal use onlys
	   try
	   {
		   switch (typeof( object[item] ))
		   {
			   case "function":
			   {
				   s = item + "( ";
				   for (var i = 0; i < object[item].length; i++)
					   s += ((i == 0) ? "" : ", ") + String.fromCharCode( "a".charCodeAt(0) + i);
				   aFunc.push( s += " )" );
				   break;
			   }
			   case "object":
			   {
				   s = item;
				   if (d3.isNull(object[item]))
					   s += " = null";
				   else if ( recurse  )
					   s += ": "+ callee( object[item], d3.ifNot(tab,"") + item + ".", recurse );
				   else 
					   s += " = [object]";
				   aProp.push( s );
				   break;
			   }
			   default:
			   {
				   aProp.push( item + " = "+ object[item] );
			   }
		   }
	   }
	   catch (e)
	   {
		   aProp.push( item + ": [unknown type]" );
	   }
   }
   s = ((typeof(tab)=="string")? tab : "");
   return s + aProp.concat( aFunc ).sort().join( "; "+ s );
};

d3.NameValue = function( name, value )
{
   this.name = name; this.value = value;
   if (typeof(value) == "undefined" && typeof(name) == "string")
   {
	   var x = name.splitAt("=");
	   if (x.length == 2) 
	   {
		   this.name = x[0];
		   this.value = x[1];
	   }
	   else
	   {
		   this.name = value;
		   this.value = name;
	   }
   }
}

d3.NameValue.prototype.toString = function()
{
   return this.name + "="+ this.value;
}

d3.HashTable = function( )
{
   this.clear();
}

d3.HashTable.prototype.add = function( name, value )
{
   var nv;
   if (name instanceof d3.NameValue)
	   nv = name;
   else
	   nv = new d3.NameValue( name, value );
   var n = this._getIndex( nv.name );
   if (n > -1)
	   this._data[n] = nv;
   else
	   this._data.push( nv );
   this.length = this._data.length;
   return nv;
}

d3.HashTable.prototype.clear = function() 
{
   this._data = [];
   this.length = 0;
}

d3.HashTable.prototype.toArray = function()
{
   var a = [];
   for (var k = 0; k < this._data.length; k++)
   {
	   var nv = this._data[k];
	   a.push( new d3.NameValue( nv.name, nv.value ) );
   }
}

d3.HashTable.prototype.remove = function( name )
{
   var n = this._getIndex( name );
   if (n > -1) 
   {
	   this.length = this._data.length - 1;
	   return this._data( n, 1 );
   }
   return null;
}
d3.HashTable.prototype._getIndex = function( id )
{
   for (var k = 0; k < this._data.length; k++)
	   if (this._data[k].name == id) return k;
   return -1;
}
d3.HashTable.prototype.item = function( id )
{
   if (typeof(id) == "number") return this._data[ parseInt(id) ];
   return this._data[ this._getIndex( id ) ];
}

d3.Enumerator = function( list, autoLoop )
{
   var k = 0;
   if (typeof(list.length) != "number" || !list.item) throw "Enumerator requires a colleciton";
   var _l = list;
   var _len = _l.length;
	
   this.autoLoop = (autoLoop == true);
   this.reset = function () { this.position = k = 0; };
   this.moveFirst = this.reset;
   this.position = k;
   this.length = _len;
   this.moveNext = function () 
   {
	   if (k < _len) k++;
	   if (k == _len && this.autoLoop)
            this.moveFirst();
	   return this.item();
   }
   this.item = function ()
   {
	   var x;
	   if (k < _len) 
	   {
		   x = _l.item(k);
		   this.position = k;
       }
       else
           this.position = -1;
	   this.length = _len;
	   return x;
   }
   this.atEnd = function () { 
       if (k < _len)
       {
           this.position = k;
           return false;
       }
       this.position = -1;
       return true; }
};

if (typeof(jQuery) != "undefined")
{
   jQuery.fn.generateId = function( )
   {
     return this.each( function()
        {
           if (this.getAttribute)
           {
              var id = this.getAttribute("id");
              if (id == null || id == "") this.id = [ this.tagName ? this.tagName : "AUTO", (new Date()).valueOf() ].join("-");
           }
        }
     );
   };

   jQuery.fn.callMethod = function( method )
   {
     if (typeof(method) != "string") return this;
     var j = (method + "(").indexOf( "(" );
     var m = method.substr(0, j);
     if (typeof(this[m]) != "function") return this;
     j++;
     var k = (method + ")").lastIndexOf( ")" ) - 1;
     var p = method.substr( j, (k - j) );
     if (p == "")
         p = [];
     else
     {
         p = p.split(",");
         for (var k = 0; k < p.length; k++) if (!isNaN(p[k])) p[k] = Number(p[k]);
     }
     return this[m]( p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9] ); 
   };

   jQuery.fn.callMethodInAttribute = function( attribute, defaultMethod )
   {
     return this.each( function()
             {
                 if (NewsProcessor.state == 'active')
                     jQuery(this).callMethod( defaultMethod+ "(300)" );
                 else
                     jQuery(this).callMethod( this[attribute] ? this[attribute] : defaultMethod );
             }
         );
   };

   jQuery.fn.balanceVertical = function( filter )
   {
     return this.each( function()
         {
             var x = jQuery(this);
             if ((!filter) || x.is(filter))
             {
                 var h = x.height(); 
                 var css = { marginTop: 0 - Math.round(Number(h)/2) + "px" };
                 var t = x.css("top");
                 if (t == "auto" || t == "" || (!isNaN(t)) || !t) css.top = "50%";
                 x.css( css );
             }
         } );
   };
   
   jQuery.fn.balanceHorizontal = function( filter )
   {
     return this.each( function()
         {   
             var x = jQuery(this);
             if ((!filter) || x.is(filter))
             {
                 var w = x.width(); 
                 var css = { marginLeft: 0 - Math.round(Number(h)/2) + "px" };
                 var l = x.css("left");
                 if (l == "auto" || l == "" || (!isNaN(l)) || !l) css.left = "50%";
                 x.css( css );
             }
         } );
   };
   
   jQuery.fn.switchSrc = function( baseUrl )
   {
     var a = String(document.location.href).split("/");
     a[a.length - 1] = "";
     var oldBaseUrl = a.join("/");
     return this.each( function()
             {
                 if (this.src)
                 {
                     //var a = this.src.split("/");
                     //var s = this.src;
                     var s = this.src;
                     this.src = s.replace( oldBaseUrl, baseUrl ); // baseUrl + a[a.length - 1];
                     window.console( "FIX: "+ s + " -> "+ this.src );
                 }
             }
         );
   };
   
   jQuery._d3cssText = [];
   jQuery.fn.saveCssText = function()
   {
      return this.generateId().each( function()
         {
            jQuery._d3cssText[ this.id ] = this.style.cssText;
         } );
   };
   jQuery.fn.restoreCssText = function()
   {
      return this.each( function()
         {
            var t = jQuery._d3cssText[ this.id ];
            if (t) this.style.cssText = t;
         } );
   };
   jQuery.fn.resetOpacity = function()
   {
      return this.each( function()
         {
            var t = this.style.cssText + ";";
            if (t != ";") 
            {
               this.style.cssText = t.replace( "opacity: 0.9999;","" ).replace( "ZOOM: 1;", "" ).replace( "FILTER: ;", "" );
            }
         } );
   }
   jQuery.fn.fadeInFully = function( speed, callback )
   {
      return this.fadeIn( speed, 
         function() 
         {
            var t = this.style.cssText + ";";
            if (t != ";") 
            {
               this.style.cssText = t.replace( "opacity: 0.9999;","" ).replace( "ZOOM: 1;", "" ).replace( "FILTER: ;", "" );
            }
            if (callback) callback.apply( this );
         } );
   }
   

   if (typeof(HTMLElement) != "undefined" && !HTMLElement.prototype.innerText)
   {
      HTMLElement.prototype.__defineGetter__("innerText", function()
         {
	         return this.innerHTML.replace(/<br(\s)*\\>/gi,"\n").replace(/<[^>]+>/g,"");
         } );
   }
   if (typeof(HTMLElement) != "undefined" && !HTMLElement.prototype.outerHTML)
   {
      HTMLElement.prototype.__defineGetter__("outerHTML", function()
         {
	         return (new XMLSerializer).serializeToString( this ); 
         } );
   }
   if (typeof(Node) != "undefined" && !Node.prototype.xml)
   {  
      Node.prototype.__defineGetter__("xml", function()
         { 
            return (new XMLSerializer).serializeToString( this ); 
         } );
   }
   if (typeof(Node) != "undefined" && !Node.prototype.text)
   {  
      Node.prototype.__defineGetter__("text", function()
         { 
            var a = [];
            if (this.nodeValue) a.push( this.nodeValue );
            for (var k = 0; k < this.childNodes.length; k++)
               a.push( this.childNodes.text );
            return a.join(" ");
         } );
   }    
   
   d3.WriteAutoText = function( objects )
   {
      if (!objects instanceof Array) return;
      for (var k = 0; k < objects.length; k++)
      {
         var o = objects[k];
         var text = o.getAttribute( "d3:text" );
         var value = d3.ifNot(o.getAttribute( "d3:value" ),"");
         value = value.splitAt( ":" );
         switch (value[0].toLowerCase())
         {
            case "year":
               text = (new Date()).getFullYear().toString();   
               break;
            case "date":
               var v = value[1].split("|");
               var n = d3.ifNotNumeric( parseInt(v[1],10), 0 );
               text = (new Date()).addDays( n ).toString( v[0] );    // date/time format follows colon (e.g.: date:mm/dd/yyyy or date:hh:mma or date:mon d|+3)
               break;
            case "random":
               text = Math.randomNumber( "0"+ value[1] ).toString();
               break;
         }
         if (typeof(text) != "string") throw "invalid TextWriter data - "+ ["id:", o.id, "text:", text, "value:", value ].join("; ");
         
         text = text.escapeHtml();
         var attrib = d3.ifNot( o.getAttribute( "d3:attribute" ), "");
         if (attrib != "")
         {
            var s = d3.ifNot( o.getAttribute( attrib ), "" );
            if (s.contains("#v!"))
               s = s.replace("#v!",text);
            else
               s = s + text;
            o.setAttribute( attrib,  s );
         }
         else
         {
            var s = o.innerHTML;
            if (s.contains("#value!"))
               s = s.replace("#value!",text);
            else
               s = s + text;
            o.innerHTML = s;
         }
      }
   };

   d3.QuickLoader = { images: [],
      current: -1,
      loader: null,
      initialize: function( className, loaderId ) {
         if (!loaderId) loaderId = "image-loader";
         if (!className) className = "quickload";
         this.loader = document.getElementById(loaderId);
         $(this.loader).load( function() { d3.QuickLoader.next(); } );
         this.images = $("IMG."+ className).get();
         d3.QuickLoader.next();
      }
   };
   d3.QuickLoader.next = function()
   {
      var img = d3.QuickLoader.images[ d3.QuickLoader.current ];
      if (img) img.src = d3.getAttribute( img, "d3:src" );
      d3.QuickLoader.current++;
      var img = d3.QuickLoader.images[ d3.QuickLoader.current ];
      if (img)
      {
        d3.QuickLoader.loader.src = d3.getAttribute( img, "d3:src" );
      }
   };

   d3.LayerChanger = {
       items: [],
       topId: null,
       topClass: "topmost",
       underClass: "underlying",
       topIndex: 100,
       initialize: function( identifyingClass, topClass, underClass, topIndex )
       {
           if (typeof(identifyingClass) != "string") identifyingClass = "layer";
           if (typeof(topClass) != "string") topClass = this.topClass;
           if (topClass.trim() == "") topClass = this.topClass;
           if (typeof(underClass) != "string") underClass = this.underClass;
           if (underClass.trim() == "") underClass = this.underClass;
           if (typeof(topIndex) != "number") topIndex = this.topIndex;
           d3.LayerChanger.topClass = topClass;
           d3.LayerChanger.topIndex = topIndex;
           var a = $("."+identifyingClass).generateId().get();
           var max = 0;
           for (var k = 0; k < a.length; k++)
           {   // save original z-index of all items and determine topmost
               var idx = this.items[ a[k].id ] = $(a[k]).css("z-index");
               idx = parseInt(idx,10);
               max = Math.max( max, d3.ifNotNumeric( idx, 0 ) );
               if (max == idx) this.topId = a[k].id;
           }

           for (var k = 0; k < a.length; k++)
           {
               if (a[k].id == this.topId)
                   $(a[k]).removeClass( this.underClass ).addClass( this.topClass ); //.click( function() { window.status = this.className+ ": "+ this.id + ","+ this.tagName;  } );
               else
                   $(a[k]).removeClass( this.topClass ).addClass( this.underClass ).one( "click", d3.LayerChanger.elevate ); //.click( function() { window.status = this.className+ ": "+ this.id + ","+ this.tagName;   } )
           }   // set appropriate class and stop clicks from propagating between layers
       }
   };
   
   d3.LayerChanger.elevate = function()
   {
       var LC = d3.LayerChanger;   // shorthand
       var action = d3.getAttribute( this, "d3:onactivate", "" );
       if (action != "") eval(action);
       $(this).css("z-index", LC.topIndex).removeClass( LC.underClass ).addClass( LC.topClass );
       var t = "";
       if (LC.topId)
       {
           t = LC.topId;
           $("#"+ t).css("z-index", LC.items[t]).removeClass( LC.topClass ).addClass( LC.underClass ).one( "click", LC.elevate );
           action = d3.getAttribute( document.getElementById(t), "d3:ondeactivate", "" );
           if (action != "") eval(action);
       }
       LC.topId = this.id;
   };
   
   d3.TabController = 
   {
      data: [],
      add : function( controller )
      {
         var id = controller.id;
         var current = null;
         var tabItems = $("#"+ controller.id +  " .tab-item").generateId().attr("d3:controller", id );
         
         var z = tabItems.filter(".active" ).get(0);
         current = (z ? z.id : "dummy-999").replace(/\-tab$/,""); 
         for (var k = 0; k < tabItems.length; k++)
         {
            var tab = tabItems.get(k).id.replace(/\-tab$/,"");
            if (tab != current) $("#"+ tab).hide(); 
         }
         this.data[ id ] = { controller : controller,
            onchange : d3.getAttribute( controller, "d3:onchange", "" ),
            current : current,
            tabItems : tabItems,
            revealMethod : d3.getAttribute( controller, "d3:reveal-method", "show" ),
            concealMethod : d3.getAttribute( controller, "d3:conceal-method", "hide" ),
            hideAll : function ()
            {
               tabItems.removeClass("active");
               $("#"+ this.current).hide(); 
               this.current = "dum-90";
            },
            show : function( id ) 
            {
               id = id.replace(/\-tab$/,"");
               var x = { oldTab: this.current + "-tab", oldContent: this.current,
                  newTab: id + "-tab", newContent: id }; 
               this.current = x.newContent;
               if (this.onchange != "") eval( this.onchange );
               tabItems.removeClass("active");
               var old = document.getElementById(x.oldContent);
               if (old) 
               {
                  $(old).callMethod(this.concealMethod);
                  var oldShow = d3.getAttribute( old, "d3:onhide", "" );
                  if (oldShow != "") eval( oldShow );
               }
               $("#"+ x.newTab).addClass("active");
               $("#"+ x.newContent + " .sidebar").hide();
               var newbie = document.getElementById(x.newContent);
               if (newbie)
               {
                  $(newbie).callMethod(this.revealMethod);
                  var newShow = d3.getAttribute( newbie, "d3:onshow", "" );
                  if (newShow != "") eval( newShow );
               }
               $("#"+ x.newContent + " .sidebar").fadeIn();
            }
         };
         tabItems.click( this.click ); 
      },
      initialize : function()
      {
            var a = $(".tab-controller").generateId();
            for (var k = 0; k < a.length; k++)
               d3.TabController.add( a.get(k) );
      },
      click: function( )
      {  //alert( this.outerHTML );
         var g = d3.getAttribute(this,"d3:controller");
         if (!g) return;
         var tc = d3.TabController.data[g];
         if (tc) tc.show( this.id );
      }
   };
 
   d3.AjaxLoader = {
      data: [], keys: [],
      load: function( id, parms )
      {
         var info = this.data[id];
         if (!parms) parms = "";
         if (info) 
         { //alert( [id,parms,info.src+parms].join("\n"));
            var url = encodeURI( info.src + parms ); //if (id == 'rank-2') alert(url);
            var o = document.getElementById(info.id);
            if (o) 
            {
               d3.log( "load "+ info.id, "ajax-load" );
               $(o).html("loading...").addClass('busy').load(url, function() { 
                  d3.log( "loaded "+ info.id, "ajax-load" );
                  $(this).removeClass('busy').trigger("readystatechange"); } );
            }
            //$("#"+info.id).html("loading...").load( url, 
            
            /*function( evt ) { if (this.onreadystatechange) $(this).trigger("readystatechange") }  /*function() 
               { $('#'+info.id + ' A').attr("target","_other"); 
                  var x = document.getElementById(info.id); 
                  var f = x["afterload"];
                  alert(f);
                  if (typeof(f) == "function") 
                  {  
                     f( x );
                  }
                  else if (typeof(f) == "string")
                     eval(f);
               } ); */
         }
      },
      add: function( loader )
      {
         var info = { id: null, defer: false, src: null };
         info.id = $(loader).generateId().get(0).id;
         info.defer = d3.getAttribute(loader, "d3:defer") == "true";
         info.src = d3.getAttribute(loader,"d3:src");
         this.data[info.id] = info; this.keys.push(info.id);
         if ((!info.defer) && info.src) { //alert('ajax '+ info.id );
            this.load( info.id ); }
         return info.id;
      },
      initialize: function( objects )
      {
         d3.log("initialize", "ajax-load");
         if (!objects) objects = $(".ajax-loader").get();
         if (!(objects instanceof Array)) objects = [objects];
         d3.log("found "+ objects.length, "ajax-load");
         for (var k = 0; k < objects.length; k++)
            this.add( objects[k] );    
         d3.log("initialized "+ objects.length, "ajax-load");
      }
   }

   d3.AjaxSelect = {
      data: [], keys: [],
   
      initialize: function( objects )
      {
         if (!objects) objects = $("SELECT.ajax-select").get();
         if (!(objects instanceof Array)) objects = [objects];
         
         for (var k = 0; k < objects.length; k++)
            this.add( objects[k] );
      },
      add: function( loader )
      {
         var info = { id: null, defer: false, src: null };
         info.id = $(loader).generateId().get(0).id;
         info.defer = d3.getAttribute(loader, "d3:defer") == "true";
         info.src = d3.getAttribute(loader,"d3:src");
         this.data[info.id] = info; this.keys.push(info.id);
         if ((!info.defer) && info.src) { //alert('ajax '+ info.id );
            this.load( info.id ); }
      },    
      load: function( id, parms )
      {
         var info = this.data[id];
         if (!parms) parms = "";
         if (info) 
         {
            var select = document.getElementById(info.id);
            for (var k = select.childNodes.length; k > 0; k--)
               select.removeChild( select.childNodes[k - 1] ); 
            d3.log("load "+ id, "ajaxselect");
            $.get( info.src + parms,
               function( data ) 
               {
                  var s = data.documentElement.text;
                  var a = s.split(";");
                  var first = null;
                  for (var k = 0; k < a.length; k++)
                  {
                     if (a[k] == "") continue;
                     var s = a[k].split('=');
                     if (s.length < 2) s[1] = s[0];
                     if (!first) first = s[0];
                     select.options[select.options.length] = ( new Option( s[1],s[0] ) );
                  }
                  if (first) select.value = first;
                  $(select).trigger("change");
                  d3.log("loaded "+ id, "ajaxselect");
               }
                );
                        
         }
      }
   }

   

}