﻿

//* Drill Down   {
	//** Drill Down Menu v1.0- (c) Dynamic Drive DHTML code library: http://www.dynamicdrive.com
	//** Script Download/ instructions page: http://www.dynamicdrive.com/dynamicindex1/drilldownmenu.htm
	//Version 1.5: June 21st, 09':
	//**1) Ability to include menu from external file and added to page via Ajax
	//**2) Adds support for arbitrary links to act as "back buttons" for a drill menu. Just give the link a rel="drillback-menuid" attribute
	//**3) Adds drillmenuinstance.back() method, which can be called on demand to go back one level within drill menu
	//**4) Fixes bug with background ULs being visible following foreground UL contents
	//**5) Updated css so browsers with JS disabled will get a scrolling, fixed height UL

	//Version 1.6: August 9th, 09': Adds ability to specify explicit height for main menu, instead of defaulting to top UL's natural height.


function drilldownmenu(setting){
	this.sublevelarrow={src:'right.gif', width:'8px', top:'3px', left:'6px'} //full URL to image used to indicate there's a sub level
	this.breadcrumbarrow='images/right.gif' //full URL to image separating breadcrumb links (if it's defined)
	this.loadingimage='loader.gif' //full URL to 'loading' image, if menu is loaded via Ajax
	this.homecrumbtext='All Topics' //Top level crumb text
	this.titlelength=35 //length of parent LI text to extract from to use as crumb titles
	this.backarrow='leftarrow.gif' //full URL to image added in front of back LI 

	////////// No need to edit beyond here /////////////
	this.menuid=setting.menuid
	this.$menudiv=null
	this.mainul=null
	this.$uls=null
	this.navdivs={}
	this.menuheight=setting.menuheight || 'auto'
	this.selectedul=setting.selectedul || null
	this.speed=setting.speed || 70
	this.persist=setting.persist || {enable: false, overrideselectedurl: false}
	this.$arrowimgs=null
	this.currentul=0
	this.filesetting=setting.filesetting || {url: null, targetElement: null}
	this.zIndexvalue=100
	this.arrowposx=0 //default arrow x position relative to parent LI
	var thisdrill=this
	thisdrill.init(setting)
	$(".drillmenu li a").addClass("ui-corner-all").hover(
		function() { $(this).addClass("ui-state-hover");},
		function() { $(this).removeClass("ui-state-hover");}
		);
}

drilldownmenu.prototype.init = function(setting) {
	var thisdrill = this;
	//relative position main DIV
	var $maindiv = $('#' + setting.menuid).css({ position: 'relative' });
	//$maindiv.find("a").addclass("ui-state-default ui-corner-all");
	var $uls = $maindiv.find('ul')
	//absolutely position ULs
	$uls.css({ position: 'absolute', left: 0, top: 0, visibility: 'hidden' });
	this.$maindiv = $maindiv;
	this.$uls = $uls;
	this.navdivs.$crumb = $('#' + setting.breadcrumbid);
	this.navdivs.$backbuttons = $('a[rel^="drillback-' + setting.menuid + '"]').css({ outline: 'none' })
		.click(function(e) {
			thisdrill.back();
			e.preventDefault();
		})
	this.buildmenu();
	$(window).bind('unload', function() {
		thisdrill.uninit();
	})
	setting = null;
}

drilldownmenu.prototype.buildmenu = function() {
	var thisdrill = this;
	//loop through each UL

	this.$uls.each(function(i) {
		var $thisul = $(this);
		if (i == 0) { //if topmost UL
			$('<li class="backcontroltitle ui-corner-all ui-state-default">Select a Topic:</li>').prependTo($thisul)
				.click(function(e) { e.preventDefault() });
			//set main menu DIV's height to top level UL's height
			thisdrill.$maindiv
				.css({ height: (thisdrill.menuheight == 'auto') ? $thisul.outerHeight() : parseInt(thisdrill.menuheight), overflow: 'hidden' })
				.data('h', parseInt(thisdrill.$maindiv.css('height')));
		}
		else { //if this isn't the topmost UL
			var $parentul = $thisul.parents('ul:eq(0)');
			var $parentli = $thisul.parents('li:eq(0)');
			//add back LI item
			$('<li class="backcontrol ui-corner-all ui-state-default">' +
					$parentli[0].firstChild.innerHTML +'</li>')
				.click(function(e) { thisdrill.back(); e.preventDefault() })
				.prependTo($thisul);
			//remove outline from anchor links
			var $anchorlink = $parentli.children('a:eq(0)').data('control', { order: i })
				.append('<span class="DDMenu-SubMenuIndicator ui-icon ui-icon-triangle-1-e"/>');
			//assign click behavior to anchor link
			$anchorlink.click(function(e) {
				thisdrill.slidemenu(jQuery(this).data('control').order);
				e.preventDefault();
			});
		}
		var ulheight = ($thisul.outerHeight() < thisdrill.$maindiv.data('h')) ? thisdrill.$maindiv.data('h') : 'auto';
		$thisul.css({ visibility: 'visible', width: '100%', height: ulheight, left: (i == 0) ? 'auto' : $parentli.outerWidth(), top: 0 });
		$thisul.data('specs', {
			w: $thisul.outerWidth(),
			h: $thisul.outerHeight(),
			order: i,
			parentorder: (i == 0) ? -1 : $anchorlink.parents('ul:eq(0)').data('specs').order,
			x: (i == 0) ? $thisul.position().left : $parentul.data('specs').x + $parentul.data('specs').w,
			title: (i == 0) ? thisdrill.homecrumbtext : $parentli.find('a:eq(0)').text().substring(0, thisdrill.titlelength)
		});
	}) //end UL loop
	//check if "selectedul" defined, plus if actual element exists
	var selectedulcheck = this.selectedul && document.getElementById(this.selectedul)
	this.$arrowimgs = this.$maindiv.find('img.arrowclass')
	this.arrowposx = parseInt(this.$arrowimgs.eq(0).css('left')) //get default x position of arrow
	if (window.opera)
		this.$uls.eq(0).css({ zIndex: this.zIndexvalue }) //Opera seems to require this in order for top level arrows to show
	if (this.persist.enable && (this.persist.overrideselectedul || !this.persist.overrideselectedul && !selectedulcheck) && drilldownmenu.routines.getCookie(this.menuid)) { //go to last persisted UL?
		var ulorder = parseInt(drilldownmenu.routines.getCookie(this.menuid))
		this.slidemenu(ulorder, true)
	}
	else if (selectedulcheck) { //if "selectedul" setting defined, slide to that UL by default
		var ulorder = $('#' + this.selectedul).data('specs').order;
		this.slidemenu(ulorder, true);
	}
	else {
		this.slidemenu(0, true);
	}
	//assign click behavior to breadcrumb div
	this.navdivs.$crumb.click(function(e) {
		if (e.target.tagName == "A") {
			var order = parseInt(e.target.getAttribute('rel'));
			//check link contains a valid rel attribute int value (is anchor link)
			if (!isNaN(order)) {
				thisdrill.slidemenu(order);
				e.preventDefault();
			}
		}
	})
}

drilldownmenu.prototype.slidemenu=function(order, disableanimate){
	var order=isNaN(order)? 0 : order
	this.$uls.css({display: 'none'})
	var $targetul=this.$uls.eq(order).css({zIndex: this.zIndexvalue++})
	$targetul.parents('ul').andSelf().css({display: 'block'})
	this.currentul=order
	if ($targetul.data('specs').h > this.$maindiv.data('h')){
		this.$maindiv.css({overflowY:'auto'}).scrollTop(0)
		this.$arrowimgs.css('left', this.arrowposx-15) //adjust arrow position if scrollbar is added
	}
	else{
		this.$maindiv.css({overflowY: 'hidden'}).scrollTop(0)
		this.$arrowimgs.css('left', this.arrowposx)
	}
	this.updatenav(order)
	this.$uls.eq(0).animate({left:-$targetul.data('specs').x}, typeof disableanimate!="undefined"? 0 : this.speed)
}

drilldownmenu.prototype.back=function(){
	if (this.currentul>0){
		var order=this.$uls.eq(this.currentul).parents('ul:eq(0)').data('specs').order
		this.slidemenu(order)
	}
}

drilldownmenu.prototype.updatenav = function(endorder) {
	var $currentul = this.$uls.eq(endorder)
	if (this.navdivs.$crumb.length == 1) { //if breadcrumb div defined
		var $crumb = this.navdivs.$crumb.empty()
		if (endorder > 0) { //if this isn't the topmost UL (no point in showing crumbs if it is)
			var crumbhtml = ''
			while ($currentul && $currentul.data('specs').order >= 0) {
				crumbhtml = ' <img src="' + this.breadcrumbarrow + '" /> <a href="#nav" rel="' + $currentul.data('specs').order + '">' + $currentul.data('specs').title + '</a>' + crumbhtml
				$currentul = ($currentul.data('specs').order > 0) ? this.$uls.eq($currentul.data('specs').parentorder) : null
			}
			$crumb.append(crumbhtml).find('img:eq(0)').remove().end().find('a:last').replaceWith(this.$uls.eq(endorder).data('specs').title) //remove link from very last crumb trail
		}
		else {
			$crumb.append(this.homecrumbtext)
		}
	}
	if (this.navdivs.$backbuttons.length > 0) { //if back buttons found
		if (!/Safari/i.test(navigator.userAgent)) //exclude Safari from button state toggling due to bug when the button is an image link
			this.navdivs.$backbuttons.css((endorder > 0) ? { opacity: 1, cursor: 'pointer'} : { opacity: 0.5, cursor: 'default' })
	}
	if (endorder > 0) { $("#TipsMenuBackButton").show(); } else { $("#TipsMenuBackButton").hide(); }
}

drilldownmenu.prototype.uninit = function() {
	if (this.persist.enable)
		drilldownmenu.routines.setCookie(this.menuid, this.currentul);
}

drilldownmenu.routines={

	getCookie:function(Name){ 
		var re=new RegExp(Name+"=[^;]*", "i"); //construct RE to search for target name/value pair
		if (document.cookie.match(re)) //if cookie found
			return document.cookie.match(re)[0].split("=")[1] //return its value
		return null
	},

	setCookie:function(name, value){
		document.cookie = name+"="+value+"; path=/"
	}

}

// }  Drill down close

/* Kwicks for jQuery (version 1.5.1) {
Copyright (c) 2008 Jeremy Martin
http://www.jeremymartin.name/projects.php?project=kwicks
	
Licensed under the MIT license:
http://www.opensource.org/licenses/mit-license.php

Any and all use of this script must be accompanied by this copyright/license notice in its present form.
*/
$.fn.kwicks = function (options) {
	var defaults = {
		isVertical: false,
		sticky: false,
		defaultKwick: 0,
		event: 'mouseover',
		spacing: 0,
		duration: 500
	};
	var o = $.extend(defaults, options);
	var WoH = (o.isVertical ? 'height' : 'width'); // WoH = Width or Height
	var LoT = (o.isVertical ? 'top' : 'left'); // LoT = Left or Top


	return this.each(function () {
		var container = $(this);
		var kwicks = container.children('li');
		var normWoH = kwicks.eq(0).css(WoH).replace(/px/, ''); // normWoH = Normal Width or Height
		if (!o.max) {
			o.max = (normWoH * kwicks.size()) - (o.min * (kwicks.size() - 1));
		} else {
			o.min = ((normWoH * kwicks.size()) - o.max) / (kwicks.size() - 1);
		}
		// set width of container ul
		if (o.isVertical) {
			container.css({
				width: kwicks.eq(0).css('width'),
				height: (normWoH * kwicks.size()) + (o.spacing * (kwicks.size() - 1)) + 'px'
			});
		} else {
			container.css({
				width: (normWoH * kwicks.size()) + (o.spacing * (kwicks.size() - 1)) + 'px',
				height: kwicks.eq(0).css('height')
			});
		}

		// pre calculate left or top values for all kwicks but the first and last
		// i = index of currently hovered kwick, j = index of kwick we're calculating
		var preCalcLoTs = []; // preCalcLoTs = pre-calculated Left or Top's
		for (i = 0; i < kwicks.size(); i++) {
			preCalcLoTs[i] = [];
			// don't need to calculate values for first or last kwick
			for (j = 1; j < kwicks.size() - 1; j++) {
				if (i == j) {
					preCalcLoTs[i][j] = o.isVertical ? j * o.min + (j * o.spacing) : j * o.min + (j * o.spacing);
				} else {
					preCalcLoTs[i][j] = (j <= i ? (j * o.min) : (j - 1) * o.min + o.max) + (j * o.spacing);
				}
			}
		}

		// loop through all kwick elements
		kwicks.each(function (i) {
			var kwick = $(this);
			// set initial width or height and left or top values
			// set first kwick
			if (i === 0) {
				kwick.css(LoT, '0px');
			}
			// set last kwick
			else if (i == kwicks.size() - 1) {
				kwick.css(o.isVertical ? 'bottom' : 'right', '0px');
			}
			// set all other kwicks
			else {
				if (o.sticky) {
					kwick.css(LoT, preCalcLoTs[o.defaultKwick][i]);
				} else {
					kwick.css(LoT, (i * normWoH) + (i * o.spacing));
				}
			}
			// correct size in sticky mode
			if (o.sticky) {
				if (o.defaultKwick == i) {
					kwick.css(WoH, o.max + 'px');
					kwick.addClass('active');
				} else {
					kwick.css(WoH, o.min + 'px');
				}
			}
			kwick.css({
				margin: 0,
				position: 'absolute'
			});

			kwick.bind(o.event, function () {
				// calculate previous width or heights and left or top values
				var prevWoHs = []; // prevWoHs = previous Widths or Heights
				var prevLoTs = []; // prevLoTs = previous Left or Tops
				kwicks.stop().removeClass('active');
				for (j = 0; j < kwicks.size(); j++) {
					prevWoHs[j] = kwicks.eq(j).css(WoH).replace(/px/, '');
					prevLoTs[j] = kwicks.eq(j).css(LoT).replace(/px/, '');
				}
				var aniObj = {};
				aniObj[WoH] = o.max;
				var maxDif = o.max - prevWoHs[i];
				var prevWoHsMaxDifRatio = prevWoHs[i] / maxDif;
				kwick.addClass('active').animate(aniObj, {
					step: function (now) {
						// calculate animation completeness as percentage
						var percentage = maxDif != 0 ? now / maxDif - prevWoHsMaxDifRatio : 1;
						// adjsut other elements based on percentage
						kwicks.each(function (j) {
							if (j != i) {
								kwicks.eq(j).css(WoH, prevWoHs[j] - ((prevWoHs[j] - o.min) * percentage) + 'px');
							}
							if (j > 0 && j < kwicks.size() - 1) { // if not the first or last kwick
								kwicks.eq(j).css(LoT, prevLoTs[j] - ((prevLoTs[j] - preCalcLoTs[i][j]) * percentage) + 'px');
							}
						});
					},
					duration: o.duration,
					easing: o.easing
				});
			});
		});
		if (!o.sticky) {
			container.bind("mouseleave", function () {
				var prevWoHs = [];
				var prevLoTs = [];
				kwicks.removeClass('active').stop();
				for (i = 0; i < kwicks.size(); i++) {
					prevWoHs[i] = kwicks.eq(i).css(WoH).replace(/px/, '');
					prevLoTs[i] = kwicks.eq(i).css(LoT).replace(/px/, '');
				}
				var aniObj = {};
				aniObj[WoH] = normWoH;
				var normDif = normWoH - prevWoHs[0];
				kwicks.eq(0).animate(aniObj, {
					step: function (now) {
						var percentage = normDif != 0 ? (now - prevWoHs[0]) / normDif : 1;
						for (i = 1; i < kwicks.size(); i++) {
							kwicks.eq(i).css(WoH, prevWoHs[i] - ((prevWoHs[i] - normWoH) * percentage) + 'px');
							if (i < kwicks.size() - 1) {
								kwicks.eq(i).css(LoT, prevLoTs[i] - ((prevLoTs[i] - ((i * normWoH) + (i * o.spacing))) * percentage) + 'px');
							}
						}
					},
					duration: o.duration,
					easing: o.easing
				});

				$('.kwicks [hash_ref="' + window.location.hash + '"]').mouseover();

			});
		}
	});
};
//}

// Tabby Plugin
(function($) {
/*
 *	Tabby jQuery plugin version 0.12
 *
 *	Ted Devito - http://teddevito.com/demos/textarea.html
 *
 *	Copyright (c) 2009 Ted Devito
 *	 
 *	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following 
 *	conditions are met:
 *	
 *		1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *		2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer  
 *			in the documentation and/or other materials provided with the distribution.
 *		3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written 
 *			permission. 
 */
 
	// plugin definition

	$.fn.tabby = function(options) {
		//debug(this);
		// build main options before element iteration
		var opts = $.extend({}, $.fn.tabby.defaults, options);
		var pressed = $.fn.tabby.pressed; 
		
		// iterate and reformat each matched element
		return this.each(function() {
			$this = $(this);
			
			// build element specific options
			var options = $.meta ? $.extend({}, opts, $this.data()) : opts;
			
			$this.bind('keydown',function (e) {
				var kc = $.fn.tabby.catch_kc(e);
				if (16 == kc) pressed.shft = true;
				/*
				because both CTRL+TAB and ALT+TAB default to an event (changing tab/window) that 
				will prevent js from capturing the keyup event, we'll set a timer on releasing them.
				*/
				if (17 == kc) {pressed.ctrl = true;	setTimeout("$.fn.tabby.pressed.ctrl = false;",1000);}
				if (18 == kc) {pressed.alt = true; 	setTimeout("$.fn.tabby.pressed.alt = false;",1000);}
					
				if (9 == kc && !pressed.ctrl && !pressed.alt) {
					e.preventDefault; // does not work in O9.63 ??
					pressed.last = kc;	setTimeout("$.fn.tabby.pressed.last = null;",0);
					process_keypress ($(e.target).get(0), pressed.shft, options);
					return false;
				}
				
			}).bind('keyup',function (e) {
				if (16 == $.fn.tabby.catch_kc(e)) pressed.shft = false;
			}).bind('blur',function (e) { // workaround for Opera -- http://www.webdeveloper.com/forum/showthread.php?p=806588
				if (9 == pressed.last) $(e.target).one('focus',function (e) {pressed.last = null;}).get(0).focus();
			});
		
		});
	};
	
	// define and expose any extra methods
	$.fn.tabby.catch_kc = function(e) { return e.keyCode ? e.keyCode : e.charCode ? e.charCode : e.which; };
	$.fn.tabby.pressed = {shft : false, ctrl : false, alt : false, last: null};
	
	// private function for debugging
	function debug($obj) {
		if (window.console && window.console.log)
		window.console.log('textarea count: ' + $obj.size());
	};

	function process_keypress (o,shft,options) {
		var scrollTo = o.scrollTop;
		//var tabString = String.fromCharCode(9);
		
		// gecko; o.setSelectionRange is only available when the text box has focus
		if (o.setSelectionRange) gecko_tab (o, shft, options);
		
		// ie; document.selection is always available
		else if (document.selection) ie_tab (o, shft, options);
		
		o.scrollTop = scrollTo;
	}
	
	// plugin defaults
	$.fn.tabby.defaults = {tabString : String.fromCharCode(9)};
	
	function gecko_tab (o, shft, options) {
		var ss = o.selectionStart;
		var es = o.selectionEnd;	
				
		// when there's no selection and we're just working with the caret, we'll add/remove the tabs at the caret, providing more control
		if(ss == es) {
			// SHIFT+TAB
			if (shft) {
				// check to the left of the caret first
				if ("\t" == o.value.substring(ss-options.tabString.length, ss)) {
					o.value = o.value.substring(0, ss-options.tabString.length) + o.value.substring(ss); // put it back together omitting one character to the left
					o.focus();
					o.setSelectionRange(ss - options.tabString.length, ss - options.tabString.length);
				} 
				// then check to the right of the caret
				else if ("\t" == o.value.substring(ss, ss + options.tabString.length)) {
					o.value = o.value.substring(0, ss) + o.value.substring(ss + options.tabString.length); // put it back together omitting one character to the right
					o.focus();
					o.setSelectionRange(ss,ss);
				}
			}
			// TAB
			else {			
				o.value = o.value.substring(0, ss) + options.tabString + o.value.substring(ss);
				o.focus();
				o.setSelectionRange(ss + options.tabString.length, ss + options.tabString.length);
			}
		} 
		// selections will always add/remove tabs from the start of the line
		else {
			// split the textarea up into lines and figure out which lines are included in the selection
			var lines = o.value.split("\n");
			var indices = new Array();
			var sl = 0; // start of the line
			var el = 0; // end of the line
			var sel = false;
			for (var i in lines) {
				el = sl + lines[i].length;
				indices.push({start: sl, end: el, selected: (sl <= ss && el > ss) || (el >= es && sl < es) || (sl > ss && el < es)});
				sl = el + 1;// for "\n"
			}
			
			// walk through the array of lines (indices) and add tabs where appropriate						
			var modifier = 0;
			for (var i in indices) {
				if (indices[i].selected) {
					var pos = indices[i].start + modifier; // adjust for tabs already inserted/removed
					// SHIFT+TAB
					if (shft && options.tabString == o.value.substring(pos,pos+options.tabString.length)) { // only SHIFT+TAB if there's a tab at the start of the line
						o.value = o.value.substring(0,pos) + o.value.substring(pos + options.tabString.length); // omit the tabstring to the right
						modifier -= options.tabString.length;
					}
					// TAB
					else if (!shft) {
						o.value = o.value.substring(0,pos) + options.tabString + o.value.substring(pos); // insert the tabstring
						modifier += options.tabString.length;
					}
				}
			}
			o.focus();
			var ns = ss + ((modifier > 0) ? options.tabString.length : (modifier < 0) ? -options.tabString.length : 0);
			var ne = es + modifier;
			o.setSelectionRange(ns,ne);
		}
	}
	
	function ie_tab (o, shft, options) {
		var range = document.selection.createRange();
		
		if (o == range.parentElement()) {
			// when there's no selection and we're just working with the caret, we'll add/remove the tabs at the caret, providing more control
			if ('' == range.text) {
				// SHIFT+TAB
				if (shft) {
					var bookmark = range.getBookmark();
					//first try to the left by moving opening up our empty range to the left
					 range.moveStart('character', -options.tabString.length);
					 if (options.tabString == range.text) {
						range.text = '';
					 } else {
						// if that didn't work then reset the range and try opening it to the right
						range.moveToBookmark(bookmark);
						range.moveEnd('character', options.tabString.length);
						if (options.tabString == range.text) 
							range.text = '';
					 }
					 // move the pointer to the start of them empty range and select it
					 range.collapse(true);
					range.select();
				}
				
				else {
					// very simple here. just insert the tab into the range and put the pointer at the end
					range.text = options.tabString; 
					range.collapse(false);
					range.select();
				}
			}
			// selections will always add/remove tabs from the start of the line
			else {
			
				var selection_text = range.text;
				var selection_len = selection_text.length;
				var selection_arr = selection_text.split("\r\n");
				
				var before_range = document.body.createTextRange();
				before_range.moveToElementText(o);
				before_range.setEndPoint("EndToStart", range);
				var before_text = before_range.text;
				var before_arr = before_text.split("\r\n");
				var before_len = before_text.length; // - before_arr.length + 1;
				
				var after_range = document.body.createTextRange();
				after_range.moveToElementText(o);
				after_range.setEndPoint("StartToEnd", range);
				var after_text = after_range.text; // we can accurately calculate distance to the end because we're not worried about MSIE trimming a \r\n
				
				var end_range = document.body.createTextRange();
				end_range.moveToElementText(o);
				end_range.setEndPoint("StartToEnd", before_range);
				var end_text = end_range.text; // we can accurately calculate distance to the end because we're not worried about MSIE trimming a \r\n
								
				var check_html = $(o).html();
				$("#r3").text(before_len + " + " + selection_len + " + " + after_text.length + " = " + check_html.length);				
				if((before_len + end_text.length) < check_html.length) {
					before_arr.push("");
					before_len += 2; // for the \r\n that was trimmed	
					if (shft && options.tabString == selection_arr[0].substring(0,options.tabString.length))
						selection_arr[0] = selection_arr[0].substring(options.tabString.length);
					else if (!shft) selection_arr[0] = options.tabString + selection_arr[0];	
				} else {
					if (shft && options.tabString == before_arr[before_arr.length-1].substring(0,options.tabString.length)) 
						before_arr[before_arr.length-1] = before_arr[before_arr.length-1].substring(options.tabString.length);
					else if (!shft) before_arr[before_arr.length-1] = options.tabString + before_arr[before_arr.length-1];
				}
				
				for (var i = 1; i < selection_arr.length; i++) {
					if (shft && options.tabString == selection_arr[i].substring(0,options.tabString.length))
						selection_arr[i] = selection_arr[i].substring(options.tabString.length);
					else if (!shft) selection_arr[i] = options.tabString + selection_arr[i];
				}
				
				if (1 == before_arr.length && 0 == before_len) {
					if (shft && options.tabString == selection_arr[0].substring(0,options.tabString.length))
						selection_arr[0] = selection_arr[0].substring(options.tabString.length);
					else if (!shft) selection_arr[0] = options.tabString + selection_arr[0];
				}

				if ((before_len + selection_len + after_text.length) < check_html.length) {
					selection_arr.push("");
					selection_len += 2; // for the \r\n that was trimmed
				}
				
				before_range.text = before_arr.join("\r\n");
				range.text = selection_arr.join("\r\n");
				
				var new_range = document.body.createTextRange();
				new_range.moveToElementText(o);
				
				if (0 < before_len)	new_range.setEndPoint("StartToEnd", before_range);
				else new_range.setEndPoint("StartToStart", before_range);
				new_range.setEndPoint("EndToEnd", range);
				
				new_range.select();
				
			} 
		}
	}

// end of closure
})(jQuery);

// File Upload Plugin
jQuery.extend({
	createUploadIframe: function (id, uri) {
		//create frame
		var frameId = 'jUploadFrame' + id;

		if (window.ActiveXObject) {
			var io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
			if (typeof uri == 'boolean') {
				io.src = 'javascript:false';
			}
			else if (typeof uri == 'string') {
				io.src = uri;
			}
		}
		else {
			var io = document.createElement('iframe');
			io.id = frameId;
			io.name = frameId;
		}
		io.style.position = 'absolute';
		io.style.top = '-1000px';
		io.style.left = '-1000px';

		document.body.appendChild(io);

		return io
	},
	createUploadForm: function (id, fileElementId, fileNameElementId) {
		//create form	
		var formId = 'jUploadForm' + id;
		var fileId = 'jUploadFile' + id;
		var form = $('<form  action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>');
		var oldElement = $('#' + fileElementId);
		var newElement = $(oldElement).clone();
		var oldElement2 = $('#' + fileNameElementId);
		var newElement2 = $(oldElement2).clone();

		$(oldElement).attr('id', fileId + "1");
		$(oldElement).before(newElement);
		$(oldElement).appendTo(form);

		$(oldElement2).attr('id', fileId + "2");
		$(oldElement2).before(newElement2);
		$(oldElement2).appendTo(form);
		//set attributes
		$(form).css('position', 'absolute');
		$(form).css('top', '-1200px');
		$(form).css('left', '-1200px');
		$(form).appendTo('body');
		return form;
	},

	ajaxFileUpload: function (s) {
		// TODO introduce global settings, allowing the client to modify them for all requests, not only timeout		
		s = jQuery.extend({}, jQuery.ajaxSettings, s);
		var id = new Date().getTime()
		var form = jQuery.createUploadForm(id, s.fileElementId, s.fileNameElementId);
		var io = jQuery.createUploadIframe(id, s.secureuri);
		var frameId = 'jUploadFrame' + id;
		var formId = 'jUploadForm' + id;
		// Watch for a new set of requests
		if (s.global && !jQuery.active++) {
			jQuery.event.trigger("ajaxStart");
		}
		var requestDone = false;
		// Create the request object
		var xml = {}
		if (s.global)
			jQuery.event.trigger("ajaxSend", [xml, s]);
		// Wait for a response to come back
		var uploadCallback = function (isTimeout) {
			var io = document.getElementById(frameId);
			try {
				if (io.contentWindow) {
					xml.responseText = io.contentWindow.document.body ? io.contentWindow.document.body.innerHTML : null;
					xml.responseXML = io.contentWindow.document.XMLDocument ? io.contentWindow.document.XMLDocument : io.contentWindow.document;

				} else if (io.contentDocument) {
					xml.responseText = io.contentDocument.document.body ? io.contentDocument.document.body.innerHTML : null;
					xml.responseXML = io.contentDocument.document.XMLDocument ? io.contentDocument.document.XMLDocument : io.contentDocument.document;
				}
			} catch (e) {
				jQuery.handleError(s, xml, null, e);
			}
			if (xml || isTimeout == "timeout") {
				requestDone = true;
				var status;
				try {
					status = isTimeout != "timeout" ? "success" : "error";
					// Make sure that the request was successful or notmodified
					if (status != "error") {
						// process the data (runs the xml through httpData regardless of callback)
						var data = jQuery.uploadHttpData(xml, s.dataType);
						// If a local callback was specified, fire it and pass it the data
						if (s.success)
							s.success(data, status);

						// Fire the global callback
						if (s.global)
							jQuery.event.trigger("ajaxSuccess", [xml, s]);
					} else
						jQuery.handleError(s, xml, status);
				} catch (e) {
					status = "error";
					jQuery.handleError(s, xml, status, e);
				}

				// The request was completed
				if (s.global)
					jQuery.event.trigger("ajaxComplete", [xml, s]);

				// Handle the global AJAX counter
				if (s.global && ! --jQuery.active)
					jQuery.event.trigger("ajaxStop");

				// Process result
				if (s.complete)
					s.complete(xml, status);

				jQuery(io).unbind()

				setTimeout(function () {
					try {
						$(io).remove();
						$(form).remove();

					} catch (e) {
						jQuery.handleError(s, xml, null, e);
					}

				}, 100)

				xml = null

			}
		}
		// Timeout checker
		if (s.timeout > 0) {
			setTimeout(function () {
				// Check to see if the request is still happening
				if (!requestDone) uploadCallback("timeout");
			}, s.timeout);
		}
		try {
			// var io = $('#' + frameId);
			var form = $('#' + formId);
			$(form).attr('action', s.url);
			$(form).attr('method', 'POST');
			$(form).attr('target', frameId);
			if (form.encoding) {
				form.encoding = 'multipart/form-data';
			}
			else {
				form.enctype = 'multipart/form-data';
			}
			$(form).submit();

		} catch (e) {
			jQuery.handleError(s, xml, null, e);
		}
		if (window.attachEvent) {
			document.getElementById(frameId).attachEvent('onload', uploadCallback);
		}
		else {
			document.getElementById(frameId).addEventListener('load', uploadCallback, false);
		}
		return { abort: function () { } };

	},

	uploadHttpData: function (r, type) {
		var data = !type;
		data = type == "xml" || data ? r.responseXML : r.responseText;
		// If the type is "script", eval it in global context
		if (type == "script")
			jQuery.globalEval(data);
		// Get the JavaScript object, if JSON is used.
		if (type == "json")
			eval("data = " + data);
		// evaluate scripts within html
		if (type == "html")
			jQuery("<div>").html(data).evalScripts();
		//alert($('param', data).each(function(){alert($(this).attr('value'));}));
		return data;
	}
});



//http://www.daimi.au.dk/~u061768/file-input.html
jQuery.fn.choose = function(f) {
	 $(this).bind('choose', f);
};
jQuery.fn.file = function () {
	return this.each(function () {
		var btn = $(this);
		var pos = btn.offset();

		function update() {
			pos = btn.offset();
			file.css({
				'top': pos.top,
				'left': pos.left,
				'width': btn.width(),
				'height': btn.height()
			});
		}

		btn.mouseover(update);

		var hidden = $('<div></div>').css({ 'display': 'none' }).appendTo('body');

		var file = $('<div><form></form></div>').appendTo('body').css({
			'position': 'absolute',
			'overflow': 'hidden',
			'-moz-opacity': '0',
			'filter': 'alpha(opacity: 0)',
			'opacity': '0',
			'z-index': '10000',
			'cursor': 'pointer'
		});

		var form = file.find('form');
		//var input = form.find('input');

		function reset() {
			var input = $('<input type="file" style="cursor:pointer" multiple>').appendTo(form);
			input.change(function (e) {
				input.unbind();
				input.detach();
				btn.trigger('choose', [input]);
				reset();
			});
		};

		reset();

		function placer(e) {
			form.css('margin-left', e.pageX - pos.left - offset.width);
			form.css('margin-top', e.pageY - pos.top - offset.height + 3);
		}

		function redirect(name) {
			file[name](function (e) {
				btn.trigger(name);
			});
		}

		file.mousemove(placer);
		btn.mousemove(placer);

		redirect('mouseover');
		redirect('mouseout');
		redirect('mousedown');
		redirect('mouseup');

		var offset = {
			width: file.width() - 25,
			height: file.height() / 2
		};

		update();
	});
};





/* Javascript plotting library for jQuery, v. 0.6.
 *
 * Released under the MIT license by IOLA, December 2007.
 *
 */

// first an inline dependency, jquery.colorhelpers.js, we inline it here
// for convenience

/* Plugin for jQuery for working with colors.
 * 
 * Version 1.0.
 * 
 * Inspiration from jQuery color animation plugin by John Resig.
 *
 * Released under the MIT license by Ole Laursen, October 2009.
 *
 * Examples:
 *
 *   $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
 *   var c = $.color.extract($("#mydiv"), 'background-color');
 *   console.log(c.r, c.g, c.b, c.a);
 *   $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
 *
 * Note that .scale() and .add() work in-place instead of returning
 * new objects.
 */ 
(function(){jQuery.color={};jQuery.color.make=function(E,D,B,C){var F={};F.r=E||0;F.g=D||0;F.b=B||0;F.a=C!=null?C:1;F.add=function(I,H){for(var G=0;G<I.length;++G){F[I.charAt(G)]+=H}return F.normalize()};F.scale=function(I,H){for(var G=0;G<I.length;++G){F[I.charAt(G)]*=H}return F.normalize()};F.toString=function(){if(F.a>=1){return"rgb("+[F.r,F.g,F.b].join(",")+")"}else{return"rgba("+[F.r,F.g,F.b,F.a].join(",")+")"}};F.normalize=function(){function G(I,J,H){return J<I?I:(J>H?H:J)}F.r=G(0,parseInt(F.r),255);F.g=G(0,parseInt(F.g),255);F.b=G(0,parseInt(F.b),255);F.a=G(0,F.a,1);return F};F.clone=function(){return jQuery.color.make(F.r,F.b,F.g,F.a)};return F.normalize()};jQuery.color.extract=function(C,B){var D;do{D=C.css(B).toLowerCase();if(D!=""&&D!="transparent"){break}C=C.parent()}while(!jQuery.nodeName(C.get(0),"body"));if(D=="rgba(0, 0, 0, 0)"){D="transparent"}return jQuery.color.parse(D)};jQuery.color.parse=function(E){var D,B=jQuery.color.make;if(D=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(E)){return B(parseInt(D[1],10),parseInt(D[2],10),parseInt(D[3],10))}if(D=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(E)){return B(parseInt(D[1],10),parseInt(D[2],10),parseInt(D[3],10),parseFloat(D[4]))}if(D=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(E)){return B(parseFloat(D[1])*2.55,parseFloat(D[2])*2.55,parseFloat(D[3])*2.55)}if(D=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(E)){return B(parseFloat(D[1])*2.55,parseFloat(D[2])*2.55,parseFloat(D[3])*2.55,parseFloat(D[4]))}if(D=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(E)){return B(parseInt(D[1],16),parseInt(D[2],16),parseInt(D[3],16))}if(D=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(E)){return B(parseInt(D[1]+D[1],16),parseInt(D[2]+D[2],16),parseInt(D[3]+D[3],16))}var C=jQuery.trim(E).toLowerCase();if(C=="transparent"){return B(255,255,255,0)}else{D=A[C];return B(D[0],D[1],D[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})();

// the actual Flot code
(function($) {
	 function Plot(placeholder, data_, options_, plugins) {
		  // data is on the form:
		  //   [ series1, series2 ... ]
		  // where series is either just the data as [ [x1, y1], [x2, y2], ... ]
		  // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... }
		  
		  var series = [],
				options = {
					 // the color theme used for graphs
					 colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
					 legend: {
						  show: true,
						  noColumns: 1, // number of colums in legend table
						  labelFormatter: null, // fn: string -> string
						  labelBoxBorderColor: "#ccc", // border color for the little label boxes
						  container: null, // container (as jQuery object) to put legend in, null means default on top of graph
						  position: "ne", // position of default legend container within plot
						  margin: 5, // distance from grid edge to default legend container within plot
						  backgroundColor: null, // null means auto-detect
						  backgroundOpacity: 0.85 // set to 0 to avoid background
					 },
					 xaxis: {
						  mode: null, // null or "time"
						  transform: null, // null or f: number -> number to transform axis
						  inverseTransform: null, // if transform is set, this should be the inverse function
						  min: null, // min. value to show, null means set automatically
						  max: null, // max. value to show, null means set automatically
						  autoscaleMargin: null, // margin in % to add if auto-setting min/max
						  ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
						  tickFormatter: null, // fn: number -> string
						  labelWidth: null, // size of tick labels in pixels
						  labelHeight: null,
						  
						  // mode specific options
						  tickDecimals: null, // no. of decimals, null means auto
						  tickSize: null, // number or [number, "unit"]
						  minTickSize: null, // number or [number, "unit"]
						  monthNames: null, // list of names of months
						  timeformat: null, // format string to use
						  twelveHourClock: false // 12 or 24 time in time mode
					 },
					 yaxis: {
						  autoscaleMargin: 0.02
					 },
					 x2axis: {
						  autoscaleMargin: null
					 },
					 y2axis: {
						  autoscaleMargin: 0.02
					 },
					 series: {
						  points: {
								show: false,
								radius: 3,
								lineWidth: 2, // in pixels
								fill: true,
								fillColor: "#ffffff"
						  },
						  lines: {
								// we don't put in show: false so we can see
								// whether lines were actively disabled 
								lineWidth: 2, // in pixels
								fill: false,
								fillColor: null,
								steps: false
						  },
						  bars: {
								show: false,
								lineWidth: 2, // in pixels
								barWidth: 1, // in units of the x axis
								fill: true,
								fillColor: null,
								align: "left", // or "center" 
								horizontal: false // when horizontal, left is now top
						  },
						  shadowSize: 3
					 },
					 grid: {
						  show: true,
						  aboveData: false,
						  color: "#545454", // primary color used for outline and labels
						  backgroundColor: null, // null for transparent, else color
						  tickColor: "rgba(0,0,0,0.15)", // color used for the ticks
						  labelMargin: 5, // in pixels
						  borderWidth: 2, // in pixels
						  borderColor: null, // set if different from the grid color
						  markings: null, // array of ranges or fn: axes -> array of ranges
						  markingsColor: "#f4f4f4",
						  markingsLineWidth: 2,
						  // interactive stuff
						  clickable: false,
						  hoverable: false,
						  autoHighlight: true, // highlight in case mouse is near
						  mouseActiveRadius: 10 // how far the mouse can be away to activate an item
					 },
					 hooks: {}
				},
		  canvas = null,      // the canvas for the plot itself
		  overlay = null,     // canvas for interactive stuff on top of plot
		  eventHolder = null, // jQuery object that events should be bound to
		  ctx = null, octx = null,
		  axes = { xaxis: {}, yaxis: {}, x2axis: {}, y2axis: {} },
		  plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
		  canvasWidth = 0, canvasHeight = 0,
		  plotWidth = 0, plotHeight = 0,
		  hooks = {
				processOptions: [],
				processRawData: [],
				processDatapoints: [],
				draw: [],
				bindEvents: [],
				drawOverlay: []
		  },
		  plot = this;

		  // public functions
		  plot.setData = setData;
		  plot.setupGrid = setupGrid;
		  plot.draw = draw;
		  plot.getPlaceholder = function() { return placeholder; };
		  plot.getCanvas = function() { return canvas; };
		  plot.getPlotOffset = function() { return plotOffset; };
		  plot.width = function () { return plotWidth; };
		  plot.height = function () { return plotHeight; };
		  plot.offset = function () {
				var o = eventHolder.offset();
				o.left += plotOffset.left;
				o.top += plotOffset.top;
				return o;
		  };
		  plot.getData = function() { return series; };
		  plot.getAxes = function() { return axes; };
		  plot.getOptions = function() { return options; };
		  plot.highlight = highlight;
		  plot.unhighlight = unhighlight;
		  plot.triggerRedrawOverlay = triggerRedrawOverlay;
		  plot.pointOffset = function(point) {
				return { left: parseInt(axisSpecToRealAxis(point, "xaxis").p2c(+point.x) + plotOffset.left),
							top: parseInt(axisSpecToRealAxis(point, "yaxis").p2c(+point.y) + plotOffset.top) };
		  };
		  

		  // public attributes
		  plot.hooks = hooks;
		  
		  // initialize
		  initPlugins(plot);
		  parseOptions(options_);
		  constructCanvas();
		  setData(data_);
		  setupGrid();
		  draw();
		  bindEvents();


		  function executeHooks(hook, args) {
				args = [plot].concat(args);
				for (var i = 0; i < hook.length; ++i)
					 hook[i].apply(this, args);
		  }

		  function initPlugins() {
				for (var i = 0; i < plugins.length; ++i) {
					 var p = plugins[i];
					 p.init(plot);
					 if (p.options)
						  $.extend(true, options, p.options);
				}
		  }
		  
		  function parseOptions(opts) {
				$.extend(true, options, opts);
				if (options.grid.borderColor == null)
					 options.grid.borderColor = options.grid.color;
				// backwards compatibility, to be removed in future
				if (options.xaxis.noTicks && options.xaxis.ticks == null)
					 options.xaxis.ticks = options.xaxis.noTicks;
				if (options.yaxis.noTicks && options.yaxis.ticks == null)
					 options.yaxis.ticks = options.yaxis.noTicks;
				if (options.grid.coloredAreas)
					 options.grid.markings = options.grid.coloredAreas;
				if (options.grid.coloredAreasColor)
					 options.grid.markingsColor = options.grid.coloredAreasColor;
				if (options.lines)
					 $.extend(true, options.series.lines, options.lines);
				if (options.points)
					 $.extend(true, options.series.points, options.points);
				if (options.bars)
					 $.extend(true, options.series.bars, options.bars);
				if (options.shadowSize)
					 options.series.shadowSize = options.shadowSize;

				for (var n in hooks)
					 if (options.hooks[n] && options.hooks[n].length)
						  hooks[n] = hooks[n].concat(options.hooks[n]);

				executeHooks(hooks.processOptions, [options]);
		  }

		  function setData(d) {
				series = parseData(d);
				fillInSeriesOptions();
				processData();
		  }
		  
		  function parseData(d) {
				var res = [];
				for (var i = 0; i < d.length; ++i) {
					 var s = $.extend(true, {}, options.series);

					 if (d[i].data) {
						  s.data = d[i].data; // move the data instead of deep-copy
						  delete d[i].data;

						  $.extend(true, s, d[i]);

						  d[i].data = s.data;
					 }
					 else
						  s.data = d[i];
					 res.push(s);
				}

				return res;
		  }
		  
		  function axisSpecToRealAxis(obj, attr) {
				var a = obj[attr];
				if (!a || a == 1)
					 return axes[attr];
				if (typeof a == "number")
					 return axes[attr.charAt(0) + a + attr.slice(1)];
				return a; // assume it's OK
		  }
		  
		  function fillInSeriesOptions() {
				var i;
				
				// collect what we already got of colors
				var neededColors = series.length,
					 usedColors = [],
					 assignedColors = [];
				for (i = 0; i < series.length; ++i) {
					 var sc = series[i].color;
					 if (sc != null) {
						  --neededColors;
						  if (typeof sc == "number")
								assignedColors.push(sc);
						  else
								usedColors.push($.color.parse(series[i].color));
					 }
				}
				
				// we might need to generate more colors if higher indices
				// are assigned
				for (i = 0; i < assignedColors.length; ++i) {
					 neededColors = Math.max(neededColors, assignedColors[i] + 1);
				}

				// produce colors as needed
				var colors = [], variation = 0;
				i = 0;
				while (colors.length < neededColors) {
					 var c;
					 if (options.colors.length == i) // check degenerate case
						  c = $.color.make(100, 100, 100);
					 else
						  c = $.color.parse(options.colors[i]);

					 // vary color if needed
					 var sign = variation % 2 == 1 ? -1 : 1;
					 c.scale('rgb', 1 + sign * Math.ceil(variation / 2) * 0.2)

					 // FIXME: if we're getting to close to something else,
					 // we should probably skip this one
					 colors.push(c);
					 
					 ++i;
					 if (i >= options.colors.length) {
						  i = 0;
						  ++variation;
					 }
				}

				// fill in the options
				var colori = 0, s;
				for (i = 0; i < series.length; ++i) {
					 s = series[i];
					 
					 // assign colors
					 if (s.color == null) {
						  s.color = colors[colori].toString();
						  ++colori;
					 }
					 else if (typeof s.color == "number")
						  s.color = colors[s.color].toString();

					 // turn on lines automatically in case nothing is set
					 if (s.lines.show == null) {
						  var v, show = true;
						  for (v in s)
								if (s[v].show) {
									 show = false;
									 break;
								}
						  if (show)
								s.lines.show = true;
					 }

					 // setup axes
					 s.xaxis = axisSpecToRealAxis(s, "xaxis");
					 s.yaxis = axisSpecToRealAxis(s, "yaxis");
				}
		  }
		  
		  function processData() {
				var topSentry = Number.POSITIVE_INFINITY,
					 bottomSentry = Number.NEGATIVE_INFINITY,
					 i, j, k, m, length,
					 s, points, ps, x, y, axis, val, f, p;

				for (axis in axes) {
					 axes[axis].datamin = topSentry;
					 axes[axis].datamax = bottomSentry;
					 axes[axis].used = false;
				}

				function updateAxis(axis, min, max) {
					 if (min < axis.datamin)
						  axis.datamin = min;
					 if (max > axis.datamax)
						  axis.datamax = max;
				}

				for (i = 0; i < series.length; ++i) {
					 s = series[i];
					 s.datapoints = { points: [] };
					 
					 executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]);
				}
				
				// first pass: clean and copy data
				for (i = 0; i < series.length; ++i) {
					 s = series[i];

					 var data = s.data, format = s.datapoints.format;

					 if (!format) {
						  format = [];
						  // find out how to copy
						  format.push({ x: true, number: true, required: true });
						  format.push({ y: true, number: true, required: true });

						  if (s.bars.show)
								format.push({ y: true, number: true, required: false, defaultValue: 0 });
						  
						  s.datapoints.format = format;
					 }

					 if (s.datapoints.pointsize != null)
						  continue; // already filled in

					 if (s.datapoints.pointsize == null)
						  s.datapoints.pointsize = format.length;
					 
					 ps = s.datapoints.pointsize;
					 points = s.datapoints.points;

					 insertSteps = s.lines.show && s.lines.steps;
					 s.xaxis.used = s.yaxis.used = true;
					 
					 for (j = k = 0; j < data.length; ++j, k += ps) {
						  p = data[j];

						  var nullify = p == null;
						  if (!nullify) {
								for (m = 0; m < ps; ++m) {
									 val = p[m];
									 f = format[m];

									 if (f) {
										  if (f.number && val != null) {
												val = +val; // convert to number
												if (isNaN(val))
													 val = null;
										  }

										  if (val == null) {
												if (f.required)
													 nullify = true;
												
												if (f.defaultValue != null)
													 val = f.defaultValue;
										  }
									 }
									 
									 points[k + m] = val;
								}
						  }
						  
						  if (nullify) {
								for (m = 0; m < ps; ++m) {
									 val = points[k + m];
									 if (val != null) {
										  f = format[m];
										  // extract min/max info
										  if (f.x)
												updateAxis(s.xaxis, val, val);
										  if (f.y)
												updateAxis(s.yaxis, val, val);
									 }
									 points[k + m] = null;
								}
						  }
						  else {
								// a little bit of line specific stuff that
								// perhaps shouldn't be here, but lacking
								// better means...
								if (insertSteps && k > 0
									 && points[k - ps] != null
									 && points[k - ps] != points[k]
									 && points[k - ps + 1] != points[k + 1]) {
									 // copy the point to make room for a middle point
									 for (m = 0; m < ps; ++m)
										  points[k + ps + m] = points[k + m];

									 // middle point has same y
									 points[k + 1] = points[k - ps + 1];

									 // we've added a point, better reflect that
									 k += ps;
								}
						  }
					 }
				}

				// give the hooks a chance to run
				for (i = 0; i < series.length; ++i) {
					 s = series[i];
					 
					 executeHooks(hooks.processDatapoints, [ s, s.datapoints]);
				}

				// second pass: find datamax/datamin for auto-scaling
				for (i = 0; i < series.length; ++i) {
					 s = series[i];
					 points = s.datapoints.points,
					 ps = s.datapoints.pointsize;

					 var xmin = topSentry, ymin = topSentry,
						  xmax = bottomSentry, ymax = bottomSentry;
					 
					 for (j = 0; j < points.length; j += ps) {
						  if (points[j] == null)
								continue;

						  for (m = 0; m < ps; ++m) {
								val = points[j + m];
								f = format[m];
								if (!f)
									 continue;
								
								if (f.x) {
									 if (val < xmin)
										  xmin = val;
									 if (val > xmax)
										  xmax = val;
								}
								if (f.y) {
									 if (val < ymin)
										  ymin = val;
									 if (val > ymax)
										  ymax = val;
								}
						  }
					 }
					 
					 if (s.bars.show) {
						  // make sure we got room for the bar on the dancing floor
						  var delta = s.bars.align == "left" ? 0 : -s.bars.barWidth/2;
						  if (s.bars.horizontal) {
								ymin += delta;
								ymax += delta + s.bars.barWidth;
						  }
						  else {
								xmin += delta;
								xmax += delta + s.bars.barWidth;
						  }
					 }
					 
					 updateAxis(s.xaxis, xmin, xmax);
					 updateAxis(s.yaxis, ymin, ymax);
				}

				for (axis in axes) {
					 if (axes[axis].datamin == topSentry)
						  axes[axis].datamin = null;
					 if (axes[axis].datamax == bottomSentry)
						  axes[axis].datamax = null;
				}
		  }

		  function constructCanvas() {
				function makeCanvas(width, height) {
					 var c = document.createElement('canvas');
					 c.width = width;
					 c.height = height;
					 if ($.browser.msie) // excanvas hack
						  c = window.G_vmlCanvasManager.initElement(c);
					 return c;
				}
				
				canvasWidth = placeholder.width();
				canvasHeight = placeholder.height();
				placeholder.html(""); // clear placeholder
				if (placeholder.css("position") == 'static')
					 placeholder.css("position", "relative"); // for positioning labels and overlay

				if (canvasWidth <= 0 || canvasHeight <= 0)
					 throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight;

				if ($.browser.msie) // excanvas hack
					 window.G_vmlCanvasManager.init_(document); // make sure everything is setup
				
				// the canvas
				canvas = $(makeCanvas(canvasWidth, canvasHeight)).appendTo(placeholder).get(0);
				ctx = canvas.getContext("2d");

				// overlay canvas for interactive features
				overlay = $(makeCanvas(canvasWidth, canvasHeight)).css({ position: 'absolute', left: 0, top: 0 }).appendTo(placeholder).get(0);
				octx = overlay.getContext("2d");
				octx.stroke();
		  }

		  function bindEvents() {
				// we include the canvas in the event holder too, because IE 7
				// sometimes has trouble with the stacking order
				eventHolder = $([overlay, canvas]);

				// bind events
				if (options.grid.hoverable)
					 eventHolder.mousemove(onMouseMove);

				if (options.grid.clickable)
					 eventHolder.click(onClick);

				executeHooks(hooks.bindEvents, [eventHolder]);
		  }

		  function setupGrid() {
				function setTransformationHelpers(axis, o) {
					 function identity(x) { return x; }
					 
					 var s, m, t = o.transform || identity,
						  it = o.inverseTransform;
						  
					 // add transformation helpers
					 if (axis == axes.xaxis || axis == axes.x2axis) {
						  // precompute how much the axis is scaling a point
						  // in canvas space
						  s = axis.scale = plotWidth / (t(axis.max) - t(axis.min));
						  m = t(axis.min);

						  // data point to canvas coordinate
						  if (t == identity) // slight optimization
								axis.p2c = function (p) { return (p - m) * s; };
						  else
								axis.p2c = function (p) { return (t(p) - m) * s; };
						  // canvas coordinate to data point
						  if (!it)
								axis.c2p = function (c) { return m + c / s; };
						  else
								axis.c2p = function (c) { return it(m + c / s); };
					 }
					 else {
						  s = axis.scale = plotHeight / (t(axis.max) - t(axis.min));
						  m = t(axis.max);
						  
						  if (t == identity)
								axis.p2c = function (p) { return (m - p) * s; };
						  else
								axis.p2c = function (p) { return (m - t(p)) * s; };
						  if (!it)
								axis.c2p = function (c) { return m - c / s; };
						  else
								axis.c2p = function (c) { return it(m - c / s); };
					 }
				}

				function measureLabels(axis, axisOptions) {
					 var i, labels = [], l;
					 
					 axis.labelWidth = axisOptions.labelWidth;
					 axis.labelHeight = axisOptions.labelHeight;

					 if (axis == axes.xaxis || axis == axes.x2axis) {
						  // to avoid measuring the widths of the labels, we
						  // construct fixed-size boxes and put the labels inside
						  // them, we don't need the exact figures and the
						  // fixed-size box content is easy to center
						  if (axis.labelWidth == null)
								axis.labelWidth = canvasWidth / (axis.ticks.length > 0 ? axis.ticks.length : 1);

						  // measure x label heights
						  if (axis.labelHeight == null) {
								labels = [];
								for (i = 0; i < axis.ticks.length; ++i) {
									 l = axis.ticks[i].label;
									 if (l)
										  labels.push('<div class="tickLabel" style="float:left;width:' + axis.labelWidth + 'px">' + l + '</div>');
								}
								
								if (labels.length > 0) {
									 var dummyDiv = $('<div style="position:absolute;top:-10000px;width:10000px;font-size:smaller">'
															+ labels.join("") + '<div style="clear:left"></div></div>').appendTo(placeholder);
									 axis.labelHeight = dummyDiv.height();
									 dummyDiv.remove();
								}
						  }
					 }
					 else if (axis.labelWidth == null || axis.labelHeight == null) {
						  // calculate y label dimensions
						  for (i = 0; i < axis.ticks.length; ++i) {
								l = axis.ticks[i].label;
								if (l)
									 labels.push('<div class="tickLabel">' + l + '</div>');
						  }
						  
						  if (labels.length > 0) {
								var dummyDiv = $('<div style="position:absolute;top:-10000px;font-size:smaller">'
													  + labels.join("") + '</div>').appendTo(placeholder);
								if (axis.labelWidth == null)
									 axis.labelWidth = dummyDiv.width();
								if (axis.labelHeight == null)
									 axis.labelHeight = dummyDiv.find("div").height();
								dummyDiv.remove();
						  }
						  
					 }

					 if (axis.labelWidth == null)
						  axis.labelWidth = 0;
					 if (axis.labelHeight == null)
						  axis.labelHeight = 0;
				}
				
				function setGridSpacing() {
					 // get the most space needed around the grid for things
					 // that may stick out
					 var maxOutset = options.grid.borderWidth;
					 for (i = 0; i < series.length; ++i)
						  maxOutset = Math.max(maxOutset, 2 * (series[i].points.radius + series[i].points.lineWidth/2));
					 
					 plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = maxOutset;
					 
					 var margin = options.grid.labelMargin + options.grid.borderWidth;
					 
					 if (axes.xaxis.labelHeight > 0)
						  plotOffset.bottom = Math.max(maxOutset, axes.xaxis.labelHeight + margin);
					 if (axes.yaxis.labelWidth > 0)
						  plotOffset.left = Math.max(maxOutset, axes.yaxis.labelWidth + margin);
					 if (axes.x2axis.labelHeight > 0)
						  plotOffset.top = Math.max(maxOutset, axes.x2axis.labelHeight + margin);
					 if (axes.y2axis.labelWidth > 0)
						  plotOffset.right = Math.max(maxOutset, axes.y2axis.labelWidth + margin);
				
					 plotWidth = canvasWidth - plotOffset.left - plotOffset.right;
					 plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;
				}
				
				var axis;
				for (axis in axes)
					 setRange(axes[axis], options[axis]);
				
				if (options.grid.show) {
					 for (axis in axes) {
						  prepareTickGeneration(axes[axis], options[axis]);
						  setTicks(axes[axis], options[axis]);
						  measureLabels(axes[axis], options[axis]);
					 }

					 setGridSpacing();
				}
				else {
					 plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = 0;
					 plotWidth = canvasWidth;
					 plotHeight = canvasHeight;
				}
				
				for (axis in axes)
					 setTransformationHelpers(axes[axis], options[axis]);

				if (options.grid.show)
					 insertLabels();
				
				insertLegend();
		  }
		  
		  function setRange(axis, axisOptions) {
				var min = +(axisOptions.min != null ? axisOptions.min : axis.datamin),
					 max = +(axisOptions.max != null ? axisOptions.max : axis.datamax),
					 delta = max - min;

				if (delta == 0.0) {
					 // degenerate case
					 var widen = max == 0 ? 1 : 0.01;

					 if (axisOptions.min == null)
						  min -= widen;
					 // alway widen max if we couldn't widen min to ensure we
					 // don't fall into min == max which doesn't work
					 if (axisOptions.max == null || axisOptions.min != null)
						  max += widen;
				}
				else {
					 // consider autoscaling
					 var margin = axisOptions.autoscaleMargin;
					 if (margin != null) {
						  if (axisOptions.min == null) {
								min -= delta * margin;
								// make sure we don't go below zero if all values
								// are positive
								if (min < 0 && axis.datamin != null && axis.datamin >= 0)
									 min = 0;
						  }
						  if (axisOptions.max == null) {
								max += delta * margin;
								if (max > 0 && axis.datamax != null && axis.datamax <= 0)
									 max = 0;
						  }
					 }
				}
				axis.min = min;
				axis.max = max;
		  }

		  function prepareTickGeneration(axis, axisOptions) {
				// estimate number of ticks
				var noTicks;
				if (typeof axisOptions.ticks == "number" && axisOptions.ticks > 0)
					 noTicks = axisOptions.ticks;
				else if (axis == axes.xaxis || axis == axes.x2axis)
					  // heuristic based on the model a*sqrt(x) fitted to
					  // some reasonable data points
					 noTicks = 0.3 * Math.sqrt(canvasWidth);
				else
					 noTicks = 0.3 * Math.sqrt(canvasHeight);
				
				var delta = (axis.max - axis.min) / noTicks,
					 size, generator, unit, formatter, i, magn, norm;

				if (axisOptions.mode == "time") {
					 // pretty handling of time
					 
					 // map of app. size of time units in milliseconds
					 var timeUnitSize = {
						  "second": 1000,
						  "minute": 60 * 1000,
						  "hour": 60 * 60 * 1000,
						  "day": 24 * 60 * 60 * 1000,
						  "month": 30 * 24 * 60 * 60 * 1000,
						  "year": 365.2425 * 24 * 60 * 60 * 1000
					 };


					 // the allowed tick sizes, after 1 year we use
					 // an integer algorithm
					 var spec = [
						  [1, "second"], [2, "second"], [5, "second"], [10, "second"],
						  [30, "second"], 
						  [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
						  [30, "minute"], 
						  [1, "hour"], [2, "hour"], [4, "hour"],
						  [8, "hour"], [12, "hour"],
						  [1, "day"], [2, "day"], [3, "day"],
						  [0.25, "month"], [0.5, "month"], [1, "month"],
						  [2, "month"], [3, "month"], [6, "month"],
						  [1, "year"]
					 ];

					 var minSize = 0;
					 if (axisOptions.minTickSize != null) {
						  if (typeof axisOptions.tickSize == "number")
								minSize = axisOptions.tickSize;
						  else
								minSize = axisOptions.minTickSize[0] * timeUnitSize[axisOptions.minTickSize[1]];
					 }

					 for (i = 0; i < spec.length - 1; ++i)
						  if (delta < (spec[i][0] * timeUnitSize[spec[i][1]]
											+ spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
							  && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize)
								break;
					 size = spec[i][0];
					 unit = spec[i][1];
					 
					 // special-case the possibility of several years
					 if (unit == "year") {
						  magn = Math.pow(10, Math.floor(Math.log(delta / timeUnitSize.year) / Math.LN10));
						  norm = (delta / timeUnitSize.year) / magn;
						  if (norm < 1.5)
								size = 1;
						  else if (norm < 3)
								size = 2;
						  else if (norm < 7.5)
								size = 5;
						  else
								size = 10;

						  size *= magn;
					 }

					 if (axisOptions.tickSize) {
						  size = axisOptions.tickSize[0];
						  unit = axisOptions.tickSize[1];
					 }
					 
					 generator = function(axis) {
						  var ticks = [],
								tickSize = axis.tickSize[0], unit = axis.tickSize[1],
								d = new Date(axis.min);
						  
						  var step = tickSize * timeUnitSize[unit];

						  if (unit == "second")
								d.setUTCSeconds(floorInBase(d.getUTCSeconds(), tickSize));
						  if (unit == "minute")
								d.setUTCMinutes(floorInBase(d.getUTCMinutes(), tickSize));
						  if (unit == "hour")
								d.setUTCHours(floorInBase(d.getUTCHours(), tickSize));
						  if (unit == "month")
								d.setUTCMonth(floorInBase(d.getUTCMonth(), tickSize));
						  if (unit == "year")
								d.setUTCFullYear(floorInBase(d.getUTCFullYear(), tickSize));
						  
						  // reset smaller components
						  d.setUTCMilliseconds(0);
						  if (step >= timeUnitSize.minute)
								d.setUTCSeconds(0);
						  if (step >= timeUnitSize.hour)
								d.setUTCMinutes(0);
						  if (step >= timeUnitSize.day)
								d.setUTCHours(0);
						  if (step >= timeUnitSize.day * 4)
								d.setUTCDate(1);
						  if (step >= timeUnitSize.year)
								d.setUTCMonth(0);


						  var carry = 0, v = Number.NaN, prev;
						  do {
								prev = v;
								v = d.getTime();
								ticks.push({ v: v, label: axis.tickFormatter(v, axis) });
								if (unit == "month") {
									 if (tickSize < 1) {
										  // a bit complicated - we'll divide the month
										  // up but we need to take care of fractions
										  // so we don't end up in the middle of a day
										  d.setUTCDate(1);
										  var start = d.getTime();
										  d.setUTCMonth(d.getUTCMonth() + 1);
										  var end = d.getTime();
										  d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
										  carry = d.getUTCHours();
										  d.setUTCHours(0);
									 }
									 else
										  d.setUTCMonth(d.getUTCMonth() + tickSize);
								}
								else if (unit == "year") {
									 d.setUTCFullYear(d.getUTCFullYear() + tickSize);
								}
								else
									 d.setTime(v + step);
						  } while (v < axis.max && v != prev);

						  return ticks;
					 };

					 formatter = function (v, axis) {
						  var d = new Date(v);

						  // first check global format
						  if (axisOptions.timeformat != null)
								return $.plot.formatDate(d, axisOptions.timeformat, axisOptions.monthNames);
						  
						  var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
						  var span = axis.max - axis.min;
						  var suffix = (axisOptions.twelveHourClock) ? " %p" : "";
						  
						  if (t < timeUnitSize.minute)
								fmt = "%h:%M:%S" + suffix;
						  else if (t < timeUnitSize.day) {
								if (span < 2 * timeUnitSize.day)
									 fmt = "%h:%M" + suffix;
								else
									 fmt = "%b %d %h:%M" + suffix;
						  }
						  else if (t < timeUnitSize.month)
								fmt = "%b %d";
						  else if (t < timeUnitSize.year) {
								if (span < timeUnitSize.year)
									 fmt = "%b";
								else
									 fmt = "%b %y";
						  }
						  else
								fmt = "%y";
						  
						  return $.plot.formatDate(d, fmt, axisOptions.monthNames);
					 };
				}
				else {
					 // pretty rounding of base-10 numbers
					 var maxDec = axisOptions.tickDecimals;
					 var dec = -Math.floor(Math.log(delta) / Math.LN10);
					 if (maxDec != null && dec > maxDec)
						  dec = maxDec;

					 magn = Math.pow(10, -dec);
					 norm = delta / magn; // norm is between 1.0 and 10.0
					 
					 if (norm < 1.5)
						  size = 1;
					 else if (norm < 3) {
						  size = 2;
						  // special case for 2.5, requires an extra decimal
						  if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
								size = 2.5;
								++dec;
						  }
					 }
					 else if (norm < 7.5)
						  size = 5;
					 else
						  size = 10;

					 size *= magn;
					 
					 if (axisOptions.minTickSize != null && size < axisOptions.minTickSize)
						  size = axisOptions.minTickSize;

					 if (axisOptions.tickSize != null)
						  size = axisOptions.tickSize;

					 axis.tickDecimals = Math.max(0, (maxDec != null) ? maxDec : dec);

					 generator = function (axis) {
						  var ticks = [];

						  // spew out all possible ticks
						  var start = floorInBase(axis.min, axis.tickSize),
								i = 0, v = Number.NaN, prev;
						  do {
								prev = v;
								v = start + i * axis.tickSize;
								ticks.push({ v: v, label: axis.tickFormatter(v, axis) });
								++i;
						  } while (v < axis.max && v != prev);
						  return ticks;
					 };

					 formatter = function (v, axis) {
						  return v.toFixed(axis.tickDecimals);
					 };
				}

				axis.tickSize = unit ? [size, unit] : size;
				axis.tickGenerator = generator;
				if ($.isFunction(axisOptions.tickFormatter))
					 axis.tickFormatter = function (v, axis) { return "" + axisOptions.tickFormatter(v, axis); };
				else
					 axis.tickFormatter = formatter;
		  }
		  
		  function setTicks(axis, axisOptions) {
				axis.ticks = [];

				if (!axis.used)
					 return;
				
				if (axisOptions.ticks == null)
					 axis.ticks = axis.tickGenerator(axis);
				else if (typeof axisOptions.ticks == "number") {
					 if (axisOptions.ticks > 0)
						  axis.ticks = axis.tickGenerator(axis);
				}
				else if (axisOptions.ticks) {
					 var ticks = axisOptions.ticks;

					 if ($.isFunction(ticks))
						  // generate the ticks
						  ticks = ticks({ min: axis.min, max: axis.max });
					 
					 // clean up the user-supplied ticks, copy them over
					 var i, v;
					 for (i = 0; i < ticks.length; ++i) {
						  var label = null;
						  var t = ticks[i];
						  if (typeof t == "object") {
								v = t[0];
								if (t.length > 1)
									 label = t[1];
						  }
						  else
								v = t;
						  if (label == null)
								label = axis.tickFormatter(v, axis);
						  axis.ticks[i] = { v: v, label: label };
					 }
				}

				if (axisOptions.autoscaleMargin != null && axis.ticks.length > 0) {
					 // snap to ticks
					 if (axisOptions.min == null)
						  axis.min = Math.min(axis.min, axis.ticks[0].v);
					 if (axisOptions.max == null && axis.ticks.length > 1)
						  axis.max = Math.max(axis.max, axis.ticks[axis.ticks.length - 1].v);
				}
		  }
		
		  function draw() {
				ctx.clearRect(0, 0, canvasWidth, canvasHeight);

				var grid = options.grid;
				
				if (grid.show && !grid.aboveData)
					 drawGrid();

				for (var i = 0; i < series.length; ++i)
					 drawSeries(series[i]);

				executeHooks(hooks.draw, [ctx]);
				
				if (grid.show && grid.aboveData)
					 drawGrid();
		  }

		  function extractRange(ranges, coord) {
				var firstAxis = coord + "axis",
					 secondaryAxis = coord + "2axis",
					 axis, from, to, reverse;

				if (ranges[firstAxis]) {
					 axis = axes[firstAxis];
					 from = ranges[firstAxis].from;
					 to = ranges[firstAxis].to;
				}
				else if (ranges[secondaryAxis]) {
					 axis = axes[secondaryAxis];
					 from = ranges[secondaryAxis].from;
					 to = ranges[secondaryAxis].to;
				}
				else {
					 // backwards-compat stuff - to be removed in future
					 axis = axes[firstAxis];
					 from = ranges[coord + "1"];
					 to = ranges[coord + "2"];
				}

				// auto-reverse as an added bonus
				if (from != null && to != null && from > to)
					 return { from: to, to: from, axis: axis };
				
				return { from: from, to: to, axis: axis };
		  }
		  
		  function drawGrid() {
				var i;
				
				ctx.save();
				ctx.translate(plotOffset.left, plotOffset.top);

				// draw background, if any
				if (options.grid.backgroundColor) {
					 ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)");
					 ctx.fillRect(0, 0, plotWidth, plotHeight);
				}

				// draw markings
				var markings = options.grid.markings;
				if (markings) {
					 if ($.isFunction(markings))
						  // xmin etc. are backwards-compatible, to be removed in future
						  markings = markings({ xmin: axes.xaxis.min, xmax: axes.xaxis.max, ymin: axes.yaxis.min, ymax: axes.yaxis.max, xaxis: axes.xaxis, yaxis: axes.yaxis, x2axis: axes.x2axis, y2axis: axes.y2axis });

					 for (i = 0; i < markings.length; ++i) {
						  var m = markings[i],
								xrange = extractRange(m, "x"),
								yrange = extractRange(m, "y");

						  // fill in missing
						  if (xrange.from == null)
								xrange.from = xrange.axis.min;
						  if (xrange.to == null)
								xrange.to = xrange.axis.max;
						  if (yrange.from == null)
								yrange.from = yrange.axis.min;
						  if (yrange.to == null)
								yrange.to = yrange.axis.max;

						  // clip
						  if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
								yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)
								continue;

						  xrange.from = Math.max(xrange.from, xrange.axis.min);
						  xrange.to = Math.min(xrange.to, xrange.axis.max);
						  yrange.from = Math.max(yrange.from, yrange.axis.min);
						  yrange.to = Math.min(yrange.to, yrange.axis.max);

						  if (xrange.from == xrange.to && yrange.from == yrange.to)
								continue;

						  // then draw
						  xrange.from = xrange.axis.p2c(xrange.from);
						  xrange.to = xrange.axis.p2c(xrange.to);
						  yrange.from = yrange.axis.p2c(yrange.from);
						  yrange.to = yrange.axis.p2c(yrange.to);
						  
						  if (xrange.from == xrange.to || yrange.from == yrange.to) {
								// draw line
								ctx.beginPath();
								ctx.strokeStyle = m.color || options.grid.markingsColor;
								ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
								//ctx.moveTo(Math.floor(xrange.from), yrange.from);
								//ctx.lineTo(Math.floor(xrange.to), yrange.to);
								ctx.moveTo(xrange.from, yrange.from);
								ctx.lineTo(xrange.to, yrange.to);
								ctx.stroke();
						  }
						  else {
								// fill area
								ctx.fillStyle = m.color || options.grid.markingsColor;
								ctx.fillRect(xrange.from, yrange.to,
												 xrange.to - xrange.from,
												 yrange.from - yrange.to);
						  }
					 }
				}
				
				// draw the inner grid
				ctx.lineWidth = 1;
				ctx.strokeStyle = options.grid.tickColor;
				ctx.beginPath();
				var v, axis = axes.xaxis;
				for (i = 0; i < axis.ticks.length; ++i) {
					 v = axis.ticks[i].v;
					 if (v <= axis.min || v >= axes.xaxis.max)
						  continue;   // skip those lying on the axes

					 ctx.moveTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, 0);
					 ctx.lineTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, plotHeight);
				}

				axis = axes.yaxis;
				for (i = 0; i < axis.ticks.length; ++i) {
					 v = axis.ticks[i].v;
					 if (v <= axis.min || v >= axis.max)
						  continue;

					 ctx.moveTo(0, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
					 ctx.lineTo(plotWidth, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
				}

				axis = axes.x2axis;
				for (i = 0; i < axis.ticks.length; ++i) {
					 v = axis.ticks[i].v;
					 if (v <= axis.min || v >= axis.max)
						  continue;
	 
					 ctx.moveTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, -5);
					 ctx.lineTo(Math.floor(axis.p2c(v)) + ctx.lineWidth/2, 5);
				}

				axis = axes.y2axis;
				for (i = 0; i < axis.ticks.length; ++i) {
					 v = axis.ticks[i].v;
					 if (v <= axis.min || v >= axis.max)
						  continue;

					 ctx.moveTo(plotWidth-5, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
					 ctx.lineTo(plotWidth+5, Math.floor(axis.p2c(v)) + ctx.lineWidth/2);
				}
				
				ctx.stroke();
				
				if (options.grid.borderWidth) {
					 // draw border
					 var bw = options.grid.borderWidth;
					 ctx.lineWidth = bw;
					 ctx.strokeStyle = options.grid.borderColor;
					 ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw);
				}

				ctx.restore();
		  }

		  function insertLabels() {
				placeholder.find(".tickLabels").remove();
				
				var html = ['<div class="tickLabels" style="font-size:smaller;color:' + options.grid.color + '">'];

				function addLabels(axis, labelGenerator) {
					 for (var i = 0; i < axis.ticks.length; ++i) {
						  var tick = axis.ticks[i];
						  if (!tick.label || tick.v < axis.min || tick.v > axis.max)
								continue;
						  html.push(labelGenerator(tick, axis));
					 }
				}

				var margin = options.grid.labelMargin + options.grid.borderWidth;
				
				addLabels(axes.xaxis, function (tick, axis) {
					 return '<div style="position:absolute;top:' + (plotOffset.top + plotHeight + margin) + 'px;left:' + Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2) + 'px;width:' + axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
				});
				
				
				addLabels(axes.yaxis, function (tick, axis) {
					 return '<div style="position:absolute;top:' + Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2) + 'px;right:' + (plotOffset.right + plotWidth + margin) + 'px;width:' + axis.labelWidth + 'px;text-align:right" class="tickLabel">' + tick.label + "</div>";
				});
				
				addLabels(axes.x2axis, function (tick, axis) {
					 return '<div style="position:absolute;bottom:' + (plotOffset.bottom + plotHeight + margin) + 'px;left:' + Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2) + 'px;width:' + axis.labelWidth + 'px;text-align:center" class="tickLabel">' + tick.label + "</div>";
				});
				
				addLabels(axes.y2axis, function (tick, axis) {
					 return '<div style="position:absolute;top:' + Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2) + 'px;left:' + (plotOffset.left + plotWidth + margin) +'px;width:' + axis.labelWidth + 'px;text-align:left" class="tickLabel">' + tick.label + "</div>";
				});

				html.push('</div>');
				
				placeholder.append(html.join(""));
		  }

		  function drawSeries(series) {
				if (series.lines.show)
					 drawSeriesLines(series);
				if (series.bars.show)
					 drawSeriesBars(series);
				if (series.points.show)
					 drawSeriesPoints(series);
		  }
		  
		  function drawSeriesLines(series) {
				function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {
					 var points = datapoints.points,
						  ps = datapoints.pointsize,
						  prevx = null, prevy = null;
					 
					 ctx.beginPath();
					 for (var i = ps; i < points.length; i += ps) {
						  var x1 = points[i - ps], y1 = points[i - ps + 1],
								x2 = points[i], y2 = points[i + 1];
						  
						  if (x1 == null || x2 == null)
								continue;

						  // clip with ymin
						  if (y1 <= y2 && y1 < axisy.min) {
								if (y2 < axisy.min)
									 continue;   // line segment is outside
								// compute new intersection point
								x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
								y1 = axisy.min;
						  }
						  else if (y2 <= y1 && y2 < axisy.min) {
								if (y1 < axisy.min)
									 continue;
								x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
								y2 = axisy.min;
						  }

						  // clip with ymax
						  if (y1 >= y2 && y1 > axisy.max) {
								if (y2 > axisy.max)
									 continue;
								x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
								y1 = axisy.max;
						  }
						  else if (y2 >= y1 && y2 > axisy.max) {
								if (y1 > axisy.max)
									 continue;
								x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
								y2 = axisy.max;
						  }

						  // clip with xmin
						  if (x1 <= x2 && x1 < axisx.min) {
								if (x2 < axisx.min)
									 continue;
								y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
								x1 = axisx.min;
						  }
						  else if (x2 <= x1 && x2 < axisx.min) {
								if (x1 < axisx.min)
									 continue;
								y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
								x2 = axisx.min;
						  }

						  // clip with xmax
						  if (x1 >= x2 && x1 > axisx.max) {
								if (x2 > axisx.max)
									 continue;
								y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
								x1 = axisx.max;
						  }
						  else if (x2 >= x1 && x2 > axisx.max) {
								if (x1 > axisx.max)
									 continue;
								y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
								x2 = axisx.max;
						  }

						  if (x1 != prevx || y1 != prevy)
								ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);
						  
						  prevx = x2;
						  prevy = y2;
						  ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);
					 }
					 ctx.stroke();
				}

				function plotLineArea(datapoints, axisx, axisy) {
					 var points = datapoints.points,
						  ps = datapoints.pointsize,
						  bottom = Math.min(Math.max(0, axisy.min), axisy.max),
						  top, lastX = 0, areaOpen = false;
					 
					 for (var i = ps; i < points.length; i += ps) {
						  var x1 = points[i - ps], y1 = points[i - ps + 1],
								x2 = points[i], y2 = points[i + 1];
						  
						  if (areaOpen && x1 != null && x2 == null) {
								// close area
								ctx.lineTo(axisx.p2c(lastX), axisy.p2c(bottom));
								ctx.fill();
								areaOpen = false;
								continue;
						  }

						  if (x1 == null || x2 == null)
								continue;

						  // clip x values
						  
						  // clip with xmin
						  if (x1 <= x2 && x1 < axisx.min) {
								if (x2 < axisx.min)
									 continue;
								y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
								x1 = axisx.min;
						  }
						  else if (x2 <= x1 && x2 < axisx.min) {
								if (x1 < axisx.min)
									 continue;
								y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
								x2 = axisx.min;
						  }

						  // clip with xmax
						  if (x1 >= x2 && x1 > axisx.max) {
								if (x2 > axisx.max)
									 continue;
								y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
								x1 = axisx.max;
						  }
						  else if (x2 >= x1 && x2 > axisx.max) {
								if (x1 > axisx.max)
									 continue;
								y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
								x2 = axisx.max;
						  }

						  if (!areaOpen) {
								// open area
								ctx.beginPath();
								ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
								areaOpen = true;
						  }
						  
						  // now first check the case where both is outside
						  if (y1 >= axisy.max && y2 >= axisy.max) {
								ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
								ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));
								lastX = x2;
								continue;
						  }
						  else if (y1 <= axisy.min && y2 <= axisy.min) {
								ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));
								ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
								lastX = x2;
								continue;
						  }
						  
						  // else it's a bit more complicated, there might
						  // be two rectangles and two triangles we need to fill
						  // in; to find these keep track of the current x values
						  var x1old = x1, x2old = x2;

						  // and clip the y values, without shortcutting
						  
						  // clip with ymin
						  if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {
								x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
								y1 = axisy.min;
						  }
						  else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) {
								x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
								y2 = axisy.min;
						  }

						  // clip with ymax
						  if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) {
								x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
								y1 = axisy.max;
						  }
						  else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) {
								x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
								y2 = axisy.max;
						  }


						  // if the x value was changed we got a rectangle
						  // to fill
						  if (x1 != x1old) {
								if (y1 <= axisy.min)
									 top = axisy.min;
								else
									 top = axisy.max;
								
								ctx.lineTo(axisx.p2c(x1old), axisy.p2c(top));
								ctx.lineTo(axisx.p2c(x1), axisy.p2c(top));
						  }
						  
						  // fill the triangles
						  ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
						  ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));

						  // fill the other rectangle if it's there
						  if (x2 != x2old) {
								if (y2 <= axisy.min)
									 top = axisy.min;
								else
									 top = axisy.max;
								
								ctx.lineTo(axisx.p2c(x2), axisy.p2c(top));
								ctx.lineTo(axisx.p2c(x2old), axisy.p2c(top));
						  }

						  lastX = Math.max(x2, x2old);
					 }

					 if (areaOpen) {
						  ctx.lineTo(axisx.p2c(lastX), axisy.p2c(bottom));
						  ctx.fill();
					 }
				}
				
				ctx.save();
				ctx.translate(plotOffset.left, plotOffset.top);
				ctx.lineJoin = "round";

				var lw = series.lines.lineWidth,
					 sw = series.shadowSize;
				// FIXME: consider another form of shadow when filling is turned on
				if (lw > 0 && sw > 0) {
					 // draw shadow as a thick and thin line with transparency
					 ctx.lineWidth = sw;
					 ctx.strokeStyle = "rgba(0,0,0,0.1)";
					 // position shadow at angle from the mid of line
					 var angle = Math.PI/18;
					 plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis);
					 ctx.lineWidth = sw/2;
					 plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis);
				}

				ctx.lineWidth = lw;
				ctx.strokeStyle = series.color;
				var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight);
				if (fillStyle) {
					 ctx.fillStyle = fillStyle;
					 plotLineArea(series.datapoints, series.xaxis, series.yaxis);
				}

				if (lw > 0)
					 plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis);
				ctx.restore();
		  }

		  function drawSeriesPoints(series) {
				function plotPoints(datapoints, radius, fillStyle, offset, circumference, axisx, axisy) {
					 var points = datapoints.points, ps = datapoints.pointsize;
					 
					 for (var i = 0; i < points.length; i += ps) {
						  var x = points[i], y = points[i + 1];
						  if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
								continue;
						  
						  ctx.beginPath();
						  ctx.arc(axisx.p2c(x), axisy.p2c(y) + offset, radius, 0, circumference, false);
						  if (fillStyle) {
								ctx.fillStyle = fillStyle;
								ctx.fill();
						  }
						  ctx.stroke();
					 }
				}
				
				ctx.save();
				ctx.translate(plotOffset.left, plotOffset.top);

				var lw = series.lines.lineWidth,
					 sw = series.shadowSize,
					 radius = series.points.radius;
				if (lw > 0 && sw > 0) {
					 // draw shadow in two steps
					 var w = sw / 2;
					 ctx.lineWidth = w;
					 ctx.strokeStyle = "rgba(0,0,0,0.1)";
					 plotPoints(series.datapoints, radius, null, w + w/2, Math.PI,
									series.xaxis, series.yaxis);

					 ctx.strokeStyle = "rgba(0,0,0,0.2)";
					 plotPoints(series.datapoints, radius, null, w/2, Math.PI,
									series.xaxis, series.yaxis);
				}

				ctx.lineWidth = lw;
				ctx.strokeStyle = series.color;
				plotPoints(series.datapoints, radius,
							  getFillStyle(series.points, series.color), 0, 2 * Math.PI,
							  series.xaxis, series.yaxis);
				ctx.restore();
		  }

		  function drawBar(x, y, b, barLeft, barRight, offset, fillStyleCallback, axisx, axisy, c, horizontal) {
				var left, right, bottom, top,
					 drawLeft, drawRight, drawTop, drawBottom,
					 tmp;

				if (horizontal) {
					 drawBottom = drawRight = drawTop = true;
					 drawLeft = false;
					 left = b;
					 right = x;
					 top = y + barLeft;
					 bottom = y + barRight;

					 // account for negative bars
					 if (right < left) {
						  tmp = right;
						  right = left;
						  left = tmp;
						  drawLeft = true;
						  drawRight = false;
					 }
				}
				else {
					 drawLeft = drawRight = drawTop = true;
					 drawBottom = false;
					 left = x + barLeft;
					 right = x + barRight;
					 bottom = b;
					 top = y;

					 // account for negative bars
					 if (top < bottom) {
						  tmp = top;
						  top = bottom;
						  bottom = tmp;
						  drawBottom = true;
						  drawTop = false;
					 }
				}
			  
				// clip
				if (right < axisx.min || left > axisx.max ||
					 top < axisy.min || bottom > axisy.max)
					 return;
				
				if (left < axisx.min) {
					 left = axisx.min;
					 drawLeft = false;
				}

				if (right > axisx.max) {
					 right = axisx.max;
					 drawRight = false;
				}

				if (bottom < axisy.min) {
					 bottom = axisy.min;
					 drawBottom = false;
				}
				
				if (top > axisy.max) {
					 top = axisy.max;
					 drawTop = false;
				}

				left = axisx.p2c(left);
				bottom = axisy.p2c(bottom);
				right = axisx.p2c(right);
				top = axisy.p2c(top);
				
				// fill the bar
				if (fillStyleCallback) {
					 c.beginPath();
					 c.moveTo(left, bottom);
					 c.lineTo(left, top);
					 c.lineTo(right, top);
					 c.lineTo(right, bottom);
					 c.fillStyle = fillStyleCallback(bottom, top);
					 c.fill();
				}

				// draw outline
				if (drawLeft || drawRight || drawTop || drawBottom) {
					 c.beginPath();

					 // FIXME: inline moveTo is buggy with excanvas
					 c.moveTo(left, bottom + offset);
					 if (drawLeft)
						  c.lineTo(left, top + offset);
					 else
						  c.moveTo(left, top + offset);
					 if (drawTop)
						  c.lineTo(right, top + offset);
					 else
						  c.moveTo(right, top + offset);
					 if (drawRight)
						  c.lineTo(right, bottom + offset);
					 else
						  c.moveTo(right, bottom + offset);
					 if (drawBottom)
						  c.lineTo(left, bottom + offset);
					 else
						  c.moveTo(left, bottom + offset);
					 c.stroke();
				}
		  }
		  
		  function drawSeriesBars(series) {
				function plotBars(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) {
					 var points = datapoints.points, ps = datapoints.pointsize;
					 
					 for (var i = 0; i < points.length; i += ps) {
						  if (points[i] == null)
								continue;
						  drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, offset, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal);
					 }
				}

				ctx.save();
				ctx.translate(plotOffset.left, plotOffset.top);

				// FIXME: figure out a way to add shadows (for instance along the right edge)
				ctx.lineWidth = series.bars.lineWidth;
				ctx.strokeStyle = series.color;
				var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
				var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null;
				plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, fillStyleCallback, series.xaxis, series.yaxis);
				ctx.restore();
		  }

		  function getFillStyle(filloptions, seriesColor, bottom, top) {
				var fill = filloptions.fill;
				if (!fill)
					 return null;

				if (filloptions.fillColor)
					 return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);
				
				var c = $.color.parse(seriesColor);
				c.a = typeof fill == "number" ? fill : 0.4;
				c.normalize();
				return c.toString();
		  }
		  
		  function insertLegend() {
				placeholder.find(".legend").remove();

				if (!options.legend.show)
					 return;
				
				var fragments = [], rowStarted = false,
					 lf = options.legend.labelFormatter, s, label;
				for (i = 0; i < series.length; ++i) {
					 s = series[i];
					 label = s.label;
					 if (!label)
						  continue;
					 
					 if (i % options.legend.noColumns == 0) {
						  if (rowStarted)
								fragments.push('</tr>');
						  fragments.push('<tr>');
						  rowStarted = true;
					 }

					 if (lf)
						  label = lf(label, s);
					 
					 fragments.push(
						  '<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + s.color + ';overflow:hidden"></div></div></td>' +
						  '<td class="legendLabel">' + label + '</td>');
				}
				if (rowStarted)
					 fragments.push('</tr>');
				
				if (fragments.length == 0)
					 return;

				var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + '</table>';
				if (options.legend.container != null)
					 $(options.legend.container).html(table);
				else {
					 var pos = "",
						  p = options.legend.position,
						  m = options.legend.margin;
					 if (m[0] == null)
						  m = [m, m];
					 if (p.charAt(0) == "n")
						  pos += 'top:' + (m[1] + plotOffset.top) + 'px;';
					 else if (p.charAt(0) == "s")
						  pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;';
					 if (p.charAt(1) == "e")
						  pos += 'right:' + (m[0] + plotOffset.right) + 'px;';
					 else if (p.charAt(1) == "w")
						  pos += 'left:' + (m[0] + plotOffset.left) + 'px;';
					 var legend = $('<div class="legend">' + table.replace('style="', 'style="position:absolute;' + pos +';') + '</div>').appendTo(placeholder);
					 if (options.legend.backgroundOpacity != 0.0) {
						  // put in the transparent background
						  // separately to avoid blended labels and
						  // label boxes
						  var c = options.legend.backgroundColor;
						  if (c == null) {
								c = options.grid.backgroundColor;
								if (c && typeof c == "string")
									 c = $.color.parse(c);
								else
									 c = $.color.extract(legend, 'background-color');
								c.a = 1;
								c = c.toString();
						  }
						  var div = legend.children();
						  $('<div style="position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity);
					 }
				}
		  }


		  // interactive features
		  
		  var highlights = [],
				redrawTimeout = null;
		  
		  // returns the data item the mouse is over, or null if none is found
		  function findNearbyItem(mouseX, mouseY, seriesFilter) {
				var maxDistance = options.grid.mouseActiveRadius,
					 smallestDistance = maxDistance * maxDistance + 1,
					 item = null, foundPoint = false, i, j;

				for (i = 0; i < series.length; ++i) {
					 if (!seriesFilter(series[i]))
						  continue;
					 
					 var s = series[i],
						  axisx = s.xaxis,
						  axisy = s.yaxis,
						  points = s.datapoints.points,
						  ps = s.datapoints.pointsize,
						  mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster
						  my = axisy.c2p(mouseY),
						  maxx = maxDistance / axisx.scale,
						  maxy = maxDistance / axisy.scale;

					 if (s.lines.show || s.points.show) {
						  for (j = 0; j < points.length; j += ps) {
								var x = points[j], y = points[j + 1];
								if (x == null)
									 continue;
								
								// For points and lines, the cursor must be within a
								// certain distance to the data point
								if (x - mx > maxx || x - mx < -maxx ||
									 y - my > maxy || y - my < -maxy)
									 continue;

								// We have to calculate distances in pixels, not in
								// data units, because the scales of the axes may be different
								var dx = Math.abs(axisx.p2c(x) - mouseX),
									 dy = Math.abs(axisy.p2c(y) - mouseY),
									 dist = dx * dx + dy * dy; // we save the sqrt

								// use <= to ensure last point takes precedence
								// (last generally means on top of)
								if (dist <= smallestDistance) {
									 smallestDistance = dist;
									 item = [i, j / ps];
								}
						  }
					 }
						  
					 if (s.bars.show && !item) { // no other point can be nearby
						  var barLeft = s.bars.align == "left" ? 0 : -s.bars.barWidth/2,
								barRight = barLeft + s.bars.barWidth;
						  
						  for (j = 0; j < points.length; j += ps) {
								var x = points[j], y = points[j + 1], b = points[j + 2];
								if (x == null)
									 continue;
  
								// for a bar graph, the cursor must be inside the bar
								if (series[i].bars.horizontal ? 
									 (mx <= Math.max(b, x) && mx >= Math.min(b, x) && 
									  my >= y + barLeft && my <= y + barRight) :
									 (mx >= x + barLeft && mx <= x + barRight &&
									  my >= Math.min(b, y) && my <= Math.max(b, y)))
										  item = [i, j / ps];
						  }
					 }
				}

				if (item) {
					 i = item[0];
					 j = item[1];
					 ps = series[i].datapoints.pointsize;
					 
					 return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps),
								 dataIndex: j,
								 series: series[i],
								 seriesIndex: i };
				}
				
				return null;
		  }

		  function onMouseMove(e) {
				if (options.grid.hoverable)
					 triggerClickHoverEvent("plothover", e,
													function (s) { return s["hoverable"] != false; });
		  }
		  
		  function onClick(e) {
				triggerClickHoverEvent("plotclick", e,
											  function (s) { return s["clickable"] != false; });
		  }

		  // trigger click or hover event (they send the same parameters
		  // so we share their code)
		  function triggerClickHoverEvent(eventname, event, seriesFilter) {
				var offset = eventHolder.offset(),
					 pos = { pageX: event.pageX, pageY: event.pageY },
					 canvasX = event.pageX - offset.left - plotOffset.left,
					 canvasY = event.pageY - offset.top - plotOffset.top;

				if (axes.xaxis.used)
					 pos.x = axes.xaxis.c2p(canvasX);
				if (axes.yaxis.used)
					 pos.y = axes.yaxis.c2p(canvasY);
				if (axes.x2axis.used)
					 pos.x2 = axes.x2axis.c2p(canvasX);
				if (axes.y2axis.used)
					 pos.y2 = axes.y2axis.c2p(canvasY);

				var item = findNearbyItem(canvasX, canvasY, seriesFilter);

				if (item) {
					 // fill in mouse pos for any listeners out there
					 item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left);
					 item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top);
				}

				if (options.grid.autoHighlight) {
					 // clear auto-highlights
					 for (var i = 0; i < highlights.length; ++i) {
						  var h = highlights[i];
						  if (h.auto == eventname &&
								!(item && h.series == item.series && h.point == item.datapoint))
								unhighlight(h.series, h.point);
					 }
					 
					 if (item)
						  highlight(item.series, item.datapoint, eventname);
				}
				
				placeholder.trigger(eventname, [ pos, item ]);
		  }

		  function triggerRedrawOverlay() {
				if (!redrawTimeout)
					 redrawTimeout = setTimeout(drawOverlay, 30);
		  }

		  function drawOverlay() {
				redrawTimeout = null;

				// draw highlights
				octx.save();
				octx.clearRect(0, 0, canvasWidth, canvasHeight);
				octx.translate(plotOffset.left, plotOffset.top);
				
				var i, hi;
				for (i = 0; i < highlights.length; ++i) {
					 hi = highlights[i];

					 if (hi.series.bars.show)
						  drawBarHighlight(hi.series, hi.point);
					 else
						  drawPointHighlight(hi.series, hi.point);
				}
				octx.restore();
				
				executeHooks(hooks.drawOverlay, [octx]);
		  }
		  
		  function highlight(s, point, auto) {
				if (typeof s == "number")
					 s = series[s];

				if (typeof point == "number")
					 point = s.data[point];

				var i = indexOfHighlight(s, point);
				if (i == -1) {
					 highlights.push({ series: s, point: point, auto: auto });

					 triggerRedrawOverlay();
				}
				else if (!auto)
					 highlights[i].auto = false;
		  }
				
		  function unhighlight(s, point) {
				if (s == null && point == null) {
					 highlights = [];
					 triggerRedrawOverlay();
				}
				
				if (typeof s == "number")
					 s = series[s];

				if (typeof point == "number")
					 point = s.data[point];

				var i = indexOfHighlight(s, point);
				if (i != -1) {
					 highlights.splice(i, 1);

					 triggerRedrawOverlay();
				}
		  }
		  
		  function indexOfHighlight(s, p) {
				for (var i = 0; i < highlights.length; ++i) {
					 var h = highlights[i];
					 if (h.series == s && h.point[0] == p[0]
						  && h.point[1] == p[1])
						  return i;
				}
				return -1;
		  }
		  
		  function drawPointHighlight(series, point) {
				var x = point[0], y = point[1],
					 axisx = series.xaxis, axisy = series.yaxis;
				
				if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
					 return;
				
				var pointRadius = series.points.radius + series.points.lineWidth / 2;
				octx.lineWidth = pointRadius;
				octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();
				var radius = 1.5 * pointRadius;
				octx.beginPath();
				octx.arc(axisx.p2c(x), axisy.p2c(y), radius, 0, 2 * Math.PI, false);
				octx.stroke();
		  }

		  function drawBarHighlight(series, point) {
				octx.lineWidth = series.bars.lineWidth;
				octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();
				var fillStyle = $.color.parse(series.color).scale('a', 0.5).toString();
				var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
				drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth,
						  0, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal);
		  }

		  function getColorOrGradient(spec, bottom, top, defaultColor) {
				if (typeof spec == "string")
					 return spec;
				else {
					 // assume this is a gradient spec; IE currently only
					 // supports a simple vertical gradient properly, so that's
					 // what we support too
					 var gradient = ctx.createLinearGradient(0, top, 0, bottom);
					 
					 for (var i = 0, l = spec.colors.length; i < l; ++i) {
						  var c = spec.colors[i];
						  if (typeof c != "string") {
								c = $.color.parse(defaultColor).scale('rgb', c.brightness);
								c.a *= c.opacity;
								c = c.toString();
						  }
						  gradient.addColorStop(i / (l - 1), c);
					 }
					 
					 return gradient;
				}
		  }
	 }

	 $.plot = function(placeholder, data, options) {
		  var plot = new Plot($(placeholder), data, options, $.plot.plugins);
		  /*var t0 = new Date();
		  var t1 = new Date();
		  var tstr = "time used (msecs): " + (t1.getTime() - t0.getTime())
		  if (window.console)
				console.log(tstr);
		  else
				alert(tstr);*/
		  return plot;
	 };

	 $.plot.plugins = [];

	 // returns a string with the date d formatted according to fmt
	 $.plot.formatDate = function(d, fmt, monthNames) {
		  var leftPad = function(n) {
				n = "" + n;
				return n.length == 1 ? "0" + n : n;
		  };
		  
		  var r = [];
		  var escape = false;
		  var hours = d.getUTCHours();
		  var isAM = hours < 12;
		  if (monthNames == null)
				monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

		  if (fmt.search(/%p|%P/) != -1) {
				if (hours > 12) {
					 hours = hours - 12;
				} else if (hours == 0) {
					 hours = 12;
				}
		  }
		  for (var i = 0; i < fmt.length; ++i) {
				var c = fmt.charAt(i);
				
				if (escape) {
					 switch (c) {
					 case 'h': c = "" + hours; break;
					 case 'H': c = leftPad(hours); break;
					 case 'M': c = leftPad(d.getUTCMinutes()); break;
					 case 'S': c = leftPad(d.getUTCSeconds()); break;
					 case 'd': c = "" + d.getUTCDate(); break;
					 case 'm': c = "" + (d.getUTCMonth() + 1); break;
					 case 'y': c = "" + d.getUTCFullYear(); break;
					 case 'b': c = "" + monthNames[d.getUTCMonth()]; break;
					 case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
					 case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
					 }
					 r.push(c);
					 escape = false;
				}
				else {
					 if (c == "%")
						  escape = true;
					 else
						  r.push(c);
				}
		  }
		  return r.join("");
	 };
	 
	 // round to nearby lower multiple of base
	 function floorInBase(n, base) {
		  return base * Math.floor(n / base);
	 }
	 
})(jQuery);





/*
 * File:        jquery.dataTables.min.js
 * Version:     1.8.1
 * Author:      Allan Jardine (www.sprymedia.co.uk)
 * Info:        www.datatables.net
 * 
 * Copyright 2008-2011 Allan Jardine, all rights reserved.
 *
 * This source file is free software, under either the GPL v2 license or a
 * BSD style license, as supplied with this software.
 * 
 * This source file is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
 */
(function(i,wa,p){i.fn.dataTableSettings=[];var D=i.fn.dataTableSettings;i.fn.dataTableExt={};var o=i.fn.dataTableExt;o.sVersion="1.8.1";o.sErrMode="alert";o.iApiIndex=0;o.oApi={};o.afnFiltering=[];o.aoFeatures=[];o.ofnSearch={};o.afnSortData=[];o.oStdClasses={sPagePrevEnabled:"paginate_enabled_previous",sPagePrevDisabled:"paginate_disabled_previous",sPageNextEnabled:"paginate_enabled_next",sPageNextDisabled:"paginate_disabled_next",sPageJUINext:"",sPageJUIPrev:"",sPageButton:"paginate_button",sPageButtonActive:"paginate_active",
sPageButtonStaticDisabled:"paginate_button paginate_button_disabled",sPageFirst:"first",sPagePrevious:"previous",sPageNext:"next",sPageLast:"last",sStripOdd:"odd",sStripEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",
sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sFooterTH:""};o.oJUIClasses={sPagePrevEnabled:"fg-button ui-button ui-state-default ui-corner-left",
sPagePrevDisabled:"fg-button ui-button ui-state-default ui-corner-left ui-state-disabled",sPageNextEnabled:"fg-button ui-button ui-state-default ui-corner-right",sPageNextDisabled:"fg-button ui-button ui-state-default ui-corner-right ui-state-disabled",sPageJUINext:"ui-icon ui-icon-circle-arrow-e",sPageJUIPrev:"ui-icon ui-icon-circle-arrow-w",sPageButton:"fg-button ui-button ui-state-default",sPageButtonActive:"fg-button ui-button ui-state-default ui-state-disabled",sPageButtonStaticDisabled:"fg-button ui-button ui-state-default ui-state-disabled",
sPageFirst:"first ui-corner-tl ui-corner-bl",sPagePrevious:"previous",sPageNext:"next",sPageLast:"last ui-corner-tr ui-corner-br",sStripOdd:"odd",sStripEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"ui-state-default",sSortDesc:"ui-state-default",sSortable:"ui-state-default",
sSortableAsc:"ui-state-default",sSortableDesc:"ui-state-default",sSortableNone:"ui-state-default",sSortColumn:"sorting_",sSortJUIAsc:"css_right ui-icon ui-icon-triangle-1-n",sSortJUIDesc:"css_right ui-icon ui-icon-triangle-1-s",sSortJUI:"css_right ui-icon ui-icon-carat-2-n-s",sSortJUIAscAllowed:"css_right ui-icon ui-icon-carat-1-n",sSortJUIDescAllowed:"css_right ui-icon ui-icon-carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollWrapper:"dataTables_scroll",
sScrollHead:"dataTables_scrollHead ui-state-default",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot ui-state-default",sScrollFootInner:"dataTables_scrollFootInner",sFooterTH:"ui-state-default"};o.oPagination={two_button:{fnInit:function(g,l,r){var s,w,y;if(g.bJUI){s=p.createElement("a");w=p.createElement("a");y=p.createElement("span");y.className=g.oClasses.sPageJUINext;w.appendChild(y);y=p.createElement("span");y.className=g.oClasses.sPageJUIPrev;
s.appendChild(y)}else{s=p.createElement("div");w=p.createElement("div")}s.className=g.oClasses.sPagePrevDisabled;w.className=g.oClasses.sPageNextDisabled;s.title=g.oLanguage.oPaginate.sPrevious;w.title=g.oLanguage.oPaginate.sNext;l.appendChild(s);l.appendChild(w);i(s).bind("click.DT",function(){g.oApi._fnPageChange(g,"previous")&&r(g)});i(w).bind("click.DT",function(){g.oApi._fnPageChange(g,"next")&&r(g)});i(s).bind("selectstart.DT",function(){return false});i(w).bind("selectstart.DT",function(){return false});
if(g.sTableId!==""&&typeof g.aanFeatures.p=="undefined"){l.setAttribute("id",g.sTableId+"_paginate");s.setAttribute("id",g.sTableId+"_previous");w.setAttribute("id",g.sTableId+"_next")}},fnUpdate:function(g){if(g.aanFeatures.p)for(var l=g.aanFeatures.p,r=0,s=l.length;r<s;r++)if(l[r].childNodes.length!==0){l[r].childNodes[0].className=g._iDisplayStart===0?g.oClasses.sPagePrevDisabled:g.oClasses.sPagePrevEnabled;l[r].childNodes[1].className=g.fnDisplayEnd()==g.fnRecordsDisplay()?g.oClasses.sPageNextDisabled:
g.oClasses.sPageNextEnabled}}},iFullNumbersShowPages:5,full_numbers:{fnInit:function(g,l,r){var s=p.createElement("span"),w=p.createElement("span"),y=p.createElement("span"),G=p.createElement("span"),x=p.createElement("span");s.innerHTML=g.oLanguage.oPaginate.sFirst;w.innerHTML=g.oLanguage.oPaginate.sPrevious;G.innerHTML=g.oLanguage.oPaginate.sNext;x.innerHTML=g.oLanguage.oPaginate.sLast;var v=g.oClasses;s.className=v.sPageButton+" "+v.sPageFirst;w.className=v.sPageButton+" "+v.sPagePrevious;G.className=
v.sPageButton+" "+v.sPageNext;x.className=v.sPageButton+" "+v.sPageLast;l.appendChild(s);l.appendChild(w);l.appendChild(y);l.appendChild(G);l.appendChild(x);i(s).bind("click.DT",function(){g.oApi._fnPageChange(g,"first")&&r(g)});i(w).bind("click.DT",function(){g.oApi._fnPageChange(g,"previous")&&r(g)});i(G).bind("click.DT",function(){g.oApi._fnPageChange(g,"next")&&r(g)});i(x).bind("click.DT",function(){g.oApi._fnPageChange(g,"last")&&r(g)});i("span",l).bind("mousedown.DT",function(){return false}).bind("selectstart.DT",
function(){return false});if(g.sTableId!==""&&typeof g.aanFeatures.p=="undefined"){l.setAttribute("id",g.sTableId+"_paginate");s.setAttribute("id",g.sTableId+"_first");w.setAttribute("id",g.sTableId+"_previous");G.setAttribute("id",g.sTableId+"_next");x.setAttribute("id",g.sTableId+"_last")}},fnUpdate:function(g,l){if(g.aanFeatures.p){var r=o.oPagination.iFullNumbersShowPages,s=Math.floor(r/2),w=Math.ceil(g.fnRecordsDisplay()/g._iDisplayLength),y=Math.ceil(g._iDisplayStart/g._iDisplayLength)+1,G=
"",x,v=g.oClasses;if(w<r){s=1;x=w}else if(y<=s){s=1;x=r}else if(y>=w-s){s=w-r+1;x=w}else{s=y-Math.ceil(r/2)+1;x=s+r-1}for(r=s;r<=x;r++)G+=y!=r?'<span class="'+v.sPageButton+'">'+r+"</span>":'<span class="'+v.sPageButtonActive+'">'+r+"</span>";x=g.aanFeatures.p;var z,Y=function(L){g._iDisplayStart=(this.innerHTML*1-1)*g._iDisplayLength;l(g);L.preventDefault()},V=function(){return false};r=0;for(s=x.length;r<s;r++)if(x[r].childNodes.length!==0){z=i("span:eq(2)",x[r]);z.html(G);i("span",z).bind("click.DT",
Y).bind("mousedown.DT",V).bind("selectstart.DT",V);z=x[r].getElementsByTagName("span");z=[z[0],z[1],z[z.length-2],z[z.length-1]];i(z).removeClass(v.sPageButton+" "+v.sPageButtonActive+" "+v.sPageButtonStaticDisabled);if(y==1){z[0].className+=" "+v.sPageButtonStaticDisabled;z[1].className+=" "+v.sPageButtonStaticDisabled}else{z[0].className+=" "+v.sPageButton;z[1].className+=" "+v.sPageButton}if(w===0||y==w||g._iDisplayLength==-1){z[2].className+=" "+v.sPageButtonStaticDisabled;z[3].className+=" "+
v.sPageButtonStaticDisabled}else{z[2].className+=" "+v.sPageButton;z[3].className+=" "+v.sPageButton}}}}}};o.oSort={"string-asc":function(g,l){if(typeof g!="string")g="";if(typeof l!="string")l="";g=g.toLowerCase();l=l.toLowerCase();return g<l?-1:g>l?1:0},"string-desc":function(g,l){if(typeof g!="string")g="";if(typeof l!="string")l="";g=g.toLowerCase();l=l.toLowerCase();return g<l?1:g>l?-1:0},"html-asc":function(g,l){g=g.replace(/<.*?>/g,"").toLowerCase();l=l.replace(/<.*?>/g,"").toLowerCase();return g<
l?-1:g>l?1:0},"html-desc":function(g,l){g=g.replace(/<.*?>/g,"").toLowerCase();l=l.replace(/<.*?>/g,"").toLowerCase();return g<l?1:g>l?-1:0},"date-asc":function(g,l){g=Date.parse(g);l=Date.parse(l);if(isNaN(g)||g==="")g=Date.parse("01/01/1970 00:00:00");if(isNaN(l)||l==="")l=Date.parse("01/01/1970 00:00:00");return g-l},"date-desc":function(g,l){g=Date.parse(g);l=Date.parse(l);if(isNaN(g)||g==="")g=Date.parse("01/01/1970 00:00:00");if(isNaN(l)||l==="")l=Date.parse("01/01/1970 00:00:00");return l-
g},"numeric-asc":function(g,l){return(g=="-"||g===""?0:g*1)-(l=="-"||l===""?0:l*1)},"numeric-desc":function(g,l){return(l=="-"||l===""?0:l*1)-(g=="-"||g===""?0:g*1)}};o.aTypes=[function(g){if(typeof g=="number")return"numeric";else if(typeof g!="string")return null;var l,r=false;l=g.charAt(0);if("0123456789-".indexOf(l)==-1)return null;for(var s=1;s<g.length;s++){l=g.charAt(s);if("0123456789.".indexOf(l)==-1)return null;if(l=="."){if(r)return null;r=true}}return"numeric"},function(g){var l=Date.parse(g);
if(l!==null&&!isNaN(l)||typeof g=="string"&&g.length===0)return"date";return null},function(g){if(typeof g=="string"&&g.indexOf("<")!=-1&&g.indexOf(">")!=-1)return"html";return null}];o.fnVersionCheck=function(g){var l=function(x,v){for(;x.length<v;)x+="0";return x},r=o.sVersion.split(".");g=g.split(".");for(var s="",w="",y=0,G=g.length;y<G;y++){s+=l(r[y],3);w+=l(g[y],3)}return parseInt(s,10)>=parseInt(w,10)};o._oExternConfig={iNextUnique:0};i.fn.dataTable=function(g){function l(){this.fnRecordsTotal=
function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsTotal,10):this.aiDisplayMaster.length};this.fnRecordsDisplay=function(){return this.oFeatures.bServerSide?parseInt(this._iRecordsDisplay,10):this.aiDisplay.length};this.fnDisplayEnd=function(){return this.oFeatures.bServerSide?this.oFeatures.bPaginate===false||this._iDisplayLength==-1?this._iDisplayStart+this.aiDisplay.length:Math.min(this._iDisplayStart+this._iDisplayLength,this._iRecordsDisplay):this._iDisplayEnd};this.sInstance=
this.oInstance=null;this.oFeatures={bPaginate:true,bLengthChange:true,bFilter:true,bSort:true,bInfo:true,bAutoWidth:true,bProcessing:false,bSortClasses:true,bStateSave:false,bServerSide:false,bDeferRender:false};this.oScroll={sX:"",sXInner:"",sY:"",bCollapse:false,bInfinite:false,iLoadGap:100,iBarWidth:0,bAutoCss:true};this.aanFeatures=[];this.oLanguage={sProcessing:"Processing...",sLengthMenu:"Show _MENU_ entries",sZeroRecords:"No matching records found",sEmptyTable:"No data available in table",
sLoadingRecords:"Loading...",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sSearch:"Search:",sUrl:"",oPaginate:{sFirst:"First",sPrevious:"Previous",sNext:"Next",sLast:"Last"},fnInfoCallback:null};this.aoData=[];this.aiDisplay=[];this.aiDisplayMaster=[];this.aoColumns=[];this.aoHeader=[];this.aoFooter=[];this.iNextId=0;this.asDataSearch=[];this.oPreviousSearch={sSearch:"",bRegex:false,
bSmart:true};this.aoPreSearchCols=[];this.aaSorting=[[0,"asc",0]];this.aaSortingFixed=null;this.asStripClasses=[];this.asDestoryStrips=[];this.sDestroyWidth=0;this.fnFooterCallback=this.fnHeaderCallback=this.fnRowCallback=null;this.aoDrawCallback=[];this.fnInitComplete=this.fnPreDrawCallback=null;this.sTableId="";this.nTableWrapper=this.nTBody=this.nTFoot=this.nTHead=this.nTable=null;this.bInitialised=this.bDeferLoading=false;this.aoOpenRows=[];this.sDom="lfrtip";this.sPaginationType="two_button";
this.iCookieDuration=7200;this.sCookiePrefix="SpryMedia_DataTables_";this.fnCookieCallback=null;this.aoStateSave=[];this.aoStateLoad=[];this.sAjaxSource=this.oLoadedState=null;this.sAjaxDataProp="aaData";this.bAjaxDataGet=true;this.jqXHR=null;this.fnServerData=function(a,b,c,d){d.jqXHR=i.ajax({url:a,data:b,success:c,dataType:"json",cache:false,error:function(f,e){e=="parsererror"&&alert("DataTables warning: JSON data from server could not be parsed. This is caused by a JSON formatting error.")}})};
this.fnFormatNumber=function(a){if(a<1E3)return a;else{var b=a+"";a=b.split("");var c="";b=b.length;for(var d=0;d<b;d++){if(d%3===0&&d!==0)c=","+c;c=a[b-d-1]+c}}return c};this.aLengthMenu=[10,25,50,100];this.bDrawing=this.iDraw=0;this.iDrawError=-1;this._iDisplayLength=10;this._iDisplayStart=0;this._iDisplayEnd=10;this._iRecordsDisplay=this._iRecordsTotal=0;this.bJUI=false;this.oClasses=o.oStdClasses;this.bSortCellsTop=this.bSorted=this.bFiltered=false;this.oInit=null}function r(a){return function(){var b=
[A(this[o.iApiIndex])].concat(Array.prototype.slice.call(arguments));return o.oApi[a].apply(this,b)}}function s(a){var b,c,d=a.iInitDisplayStart;if(a.bInitialised===false)setTimeout(function(){s(a)},200);else{xa(a);V(a);L(a,a.aoHeader);a.nTFoot&&L(a,a.aoFooter);K(a,true);a.oFeatures.bAutoWidth&&ea(a);b=0;for(c=a.aoColumns.length;b<c;b++)if(a.aoColumns[b].sWidth!==null)a.aoColumns[b].nTh.style.width=u(a.aoColumns[b].sWidth);if(a.oFeatures.bSort)R(a);else if(a.oFeatures.bFilter)M(a,a.oPreviousSearch);
else{a.aiDisplay=a.aiDisplayMaster.slice();E(a);C(a)}if(a.sAjaxSource!==null&&!a.oFeatures.bServerSide)a.fnServerData.call(a.oInstance,a.sAjaxSource,[],function(f){var e=f;if(a.sAjaxDataProp!=="")e=Z(a.sAjaxDataProp)(f);for(b=0;b<e.length;b++)v(a,e[b]);a.iInitDisplayStart=d;if(a.oFeatures.bSort)R(a);else{a.aiDisplay=a.aiDisplayMaster.slice();E(a);C(a)}K(a,false);w(a,f)},a);else if(!a.oFeatures.bServerSide){K(a,false);w(a)}}}function w(a,b){a._bInitComplete=true;if(typeof a.fnInitComplete=="function")typeof b!=
"undefined"?a.fnInitComplete.call(a.oInstance,a,b):a.fnInitComplete.call(a.oInstance,a)}function y(a,b,c){n(a.oLanguage,b,"sProcessing");n(a.oLanguage,b,"sLengthMenu");n(a.oLanguage,b,"sEmptyTable");n(a.oLanguage,b,"sLoadingRecords");n(a.oLanguage,b,"sZeroRecords");n(a.oLanguage,b,"sInfo");n(a.oLanguage,b,"sInfoEmpty");n(a.oLanguage,b,"sInfoFiltered");n(a.oLanguage,b,"sInfoPostFix");n(a.oLanguage,b,"sSearch");if(typeof b.oPaginate!="undefined"){n(a.oLanguage.oPaginate,b.oPaginate,"sFirst");n(a.oLanguage.oPaginate,
b.oPaginate,"sPrevious");n(a.oLanguage.oPaginate,b.oPaginate,"sNext");n(a.oLanguage.oPaginate,b.oPaginate,"sLast")}typeof b.sEmptyTable=="undefined"&&typeof b.sZeroRecords!="undefined"&&n(a.oLanguage,b,"sZeroRecords","sEmptyTable");typeof b.sLoadingRecords=="undefined"&&typeof b.sZeroRecords!="undefined"&&n(a.oLanguage,b,"sZeroRecords","sLoadingRecords");c&&s(a)}function G(a,b){var c=a.aoColumns.length;b={sType:null,_bAutoType:true,bVisible:true,bSearchable:true,bSortable:true,asSorting:["asc","desc"],
sSortingClass:a.oClasses.sSortable,sSortingClassJUI:a.oClasses.sSortJUI,sTitle:b?b.innerHTML:"",sName:"",sWidth:null,sWidthOrig:null,sClass:null,fnRender:null,bUseRendered:true,iDataSort:c,mDataProp:c,fnGetData:null,fnSetData:null,sSortDataType:"std",sDefaultContent:null,sContentPadding:"",nTh:b?b:p.createElement("th"),nTf:null};a.aoColumns.push(b);if(typeof a.aoPreSearchCols[c]=="undefined"||a.aoPreSearchCols[c]===null)a.aoPreSearchCols[c]={sSearch:"",bRegex:false,bSmart:true};else{if(typeof a.aoPreSearchCols[c].bRegex==
"undefined")a.aoPreSearchCols[c].bRegex=true;if(typeof a.aoPreSearchCols[c].bSmart=="undefined")a.aoPreSearchCols[c].bSmart=true}x(a,c,null)}function x(a,b,c){b=a.aoColumns[b];if(typeof c!="undefined"&&c!==null){if(typeof c.sType!="undefined"){b.sType=c.sType;b._bAutoType=false}n(b,c,"bVisible");n(b,c,"bSearchable");n(b,c,"bSortable");n(b,c,"sTitle");n(b,c,"sName");n(b,c,"sWidth");n(b,c,"sWidth","sWidthOrig");n(b,c,"sClass");n(b,c,"fnRender");n(b,c,"bUseRendered");n(b,c,"iDataSort");n(b,c,"mDataProp");
n(b,c,"asSorting");n(b,c,"sSortDataType");n(b,c,"sDefaultContent");n(b,c,"sContentPadding")}b.fnGetData=Z(b.mDataProp);b.fnSetData=ya(b.mDataProp);if(!a.oFeatures.bSort)b.bSortable=false;if(!b.bSortable||i.inArray("asc",b.asSorting)==-1&&i.inArray("desc",b.asSorting)==-1){b.sSortingClass=a.oClasses.sSortableNone;b.sSortingClassJUI=""}else if(b.bSortable||i.inArray("asc",b.asSorting)==-1&&i.inArray("desc",b.asSorting)==-1){b.sSortingClass=a.oClasses.sSortable;b.sSortingClassJUI=a.oClasses.sSortJUI}else if(i.inArray("asc",
b.asSorting)!=-1&&i.inArray("desc",b.asSorting)==-1){b.sSortingClass=a.oClasses.sSortableAsc;b.sSortingClassJUI=a.oClasses.sSortJUIAscAllowed}else if(i.inArray("asc",b.asSorting)==-1&&i.inArray("desc",b.asSorting)!=-1){b.sSortingClass=a.oClasses.sSortableDesc;b.sSortingClassJUI=a.oClasses.sSortJUIDescAllowed}}function v(a,b){var c;c=typeof b.length=="number"?b.slice():i.extend(true,{},b);b=a.aoData.length;var d={nTr:null,_iId:a.iNextId++,_aData:c,_anHidden:[],_sRowStripe:""};a.aoData.push(d);for(var f,
e=0,h=a.aoColumns.length;e<h;e++){c=a.aoColumns[e];typeof c.fnRender=="function"&&c.bUseRendered&&c.mDataProp!==null&&N(a,b,e,c.fnRender({iDataRow:b,iDataColumn:e,aData:d._aData,oSettings:a}));if(c._bAutoType&&c.sType!="string"){f=H(a,b,e,"type");if(f!==null&&f!==""){f=fa(f);if(c.sType===null)c.sType=f;else if(c.sType!=f)c.sType="string"}}}a.aiDisplayMaster.push(b);a.oFeatures.bDeferRender||z(a,b);return b}function z(a,b){var c=a.aoData[b],d;if(c.nTr===null){c.nTr=p.createElement("tr");typeof c._aData.DT_RowId!=
"undefined"&&c.nTr.setAttribute("id",c._aData.DT_RowId);typeof c._aData.DT_RowClass!="undefined"&&i(c.nTr).addClass(c._aData.DT_RowClass);for(var f=0,e=a.aoColumns.length;f<e;f++){var h=a.aoColumns[f];d=p.createElement("td");d.innerHTML=typeof h.fnRender=="function"&&(!h.bUseRendered||h.mDataProp===null)?h.fnRender({iDataRow:b,iDataColumn:f,aData:c._aData,oSettings:a}):H(a,b,f,"display");if(h.sClass!==null)d.className=h.sClass;if(h.bVisible){c.nTr.appendChild(d);c._anHidden[f]=null}else c._anHidden[f]=
d}}}function Y(a){var b,c,d,f,e,h,j,k,m;if(a.bDeferLoading||a.sAjaxSource===null){j=a.nTBody.childNodes;b=0;for(c=j.length;b<c;b++)if(j[b].nodeName.toUpperCase()=="TR"){k=a.aoData.length;a.aoData.push({nTr:j[b],_iId:a.iNextId++,_aData:[],_anHidden:[],_sRowStripe:""});a.aiDisplayMaster.push(k);h=j[b].childNodes;d=e=0;for(f=h.length;d<f;d++){m=h[d].nodeName.toUpperCase();if(m=="TD"||m=="TH"){N(a,k,e,i.trim(h[d].innerHTML));e++}}}}j=$(a);h=[];b=0;for(c=j.length;b<c;b++){d=0;for(f=j[b].childNodes.length;d<
f;d++){e=j[b].childNodes[d];m=e.nodeName.toUpperCase();if(m=="TD"||m=="TH")h.push(e)}}h.length!=j.length*a.aoColumns.length&&J(a,1,"Unexpected number of TD elements. Expected "+j.length*a.aoColumns.length+" and got "+h.length+". DataTables does not support rowspan / colspan in the table body, and there must be one cell for each row/column combination.");d=0;for(f=a.aoColumns.length;d<f;d++){if(a.aoColumns[d].sTitle===null)a.aoColumns[d].sTitle=a.aoColumns[d].nTh.innerHTML;j=a.aoColumns[d]._bAutoType;
m=typeof a.aoColumns[d].fnRender=="function";e=a.aoColumns[d].sClass!==null;k=a.aoColumns[d].bVisible;var t,q;if(j||m||e||!k){b=0;for(c=a.aoData.length;b<c;b++){t=h[b*f+d];if(j&&a.aoColumns[d].sType!="string"){q=H(a,b,d,"type");if(q!==""){q=fa(q);if(a.aoColumns[d].sType===null)a.aoColumns[d].sType=q;else if(a.aoColumns[d].sType!=q)a.aoColumns[d].sType="string"}}if(m){q=a.aoColumns[d].fnRender({iDataRow:b,iDataColumn:d,aData:a.aoData[b]._aData,oSettings:a});t.innerHTML=q;a.aoColumns[d].bUseRendered&&
N(a,b,d,q)}if(e)t.className+=" "+a.aoColumns[d].sClass;if(k)a.aoData[b]._anHidden[d]=null;else{a.aoData[b]._anHidden[d]=t;t.parentNode.removeChild(t)}}}}}function V(a){var b,c,d;a.nTHead.getElementsByTagName("tr");if(a.nTHead.getElementsByTagName("th").length!==0){b=0;for(d=a.aoColumns.length;b<d;b++){c=a.aoColumns[b].nTh;a.aoColumns[b].sClass!==null&&i(c).addClass(a.aoColumns[b].sClass);if(a.aoColumns[b].sTitle!=c.innerHTML)c.innerHTML=a.aoColumns[b].sTitle}}else{var f=p.createElement("tr");b=0;
for(d=a.aoColumns.length;b<d;b++){c=a.aoColumns[b].nTh;c.innerHTML=a.aoColumns[b].sTitle;a.aoColumns[b].sClass!==null&&i(c).addClass(a.aoColumns[b].sClass);f.appendChild(c)}i(a.nTHead).html("")[0].appendChild(f);W(a.aoHeader,a.nTHead)}if(a.bJUI){b=0;for(d=a.aoColumns.length;b<d;b++){c=a.aoColumns[b].nTh;f=p.createElement("div");f.className=a.oClasses.sSortJUIWrapper;i(c).contents().appendTo(f);var e=p.createElement("span");e.className=a.oClasses.sSortIcon;f.appendChild(e);c.appendChild(f)}}d=function(){this.onselectstart=
function(){return false};return false};if(a.oFeatures.bSort)for(b=0;b<a.aoColumns.length;b++)if(a.aoColumns[b].bSortable!==false){ga(a,a.aoColumns[b].nTh,b);i(a.aoColumns[b].nTh).bind("mousedown.DT",d)}else i(a.aoColumns[b].nTh).addClass(a.oClasses.sSortableNone);a.oClasses.sFooterTH!==""&&i(">tr>th",a.nTFoot).addClass(a.oClasses.sFooterTH);if(a.nTFoot!==null){c=S(a,null,a.aoFooter);b=0;for(d=a.aoColumns.length;b<d;b++)if(typeof c[b]!="undefined")a.aoColumns[b].nTf=c[b]}}function L(a,b,c){var d,f,
e,h=[],j=[],k=a.aoColumns.length;if(typeof c=="undefined")c=false;d=0;for(f=b.length;d<f;d++){h[d]=b[d].slice();h[d].nTr=b[d].nTr;for(e=k-1;e>=0;e--)!a.aoColumns[e].bVisible&&!c&&h[d].splice(e,1);j.push([])}d=0;for(f=h.length;d<f;d++){if(h[d].nTr){a=0;for(e=h[d].nTr.childNodes.length;a<e;a++)h[d].nTr.removeChild(h[d].nTr.childNodes[0])}e=0;for(b=h[d].length;e<b;e++){k=c=1;if(typeof j[d][e]=="undefined"){h[d].nTr.appendChild(h[d][e].cell);for(j[d][e]=1;typeof h[d+c]!="undefined"&&h[d][e].cell==h[d+
c][e].cell;){j[d+c][e]=1;c++}for(;typeof h[d][e+k]!="undefined"&&h[d][e].cell==h[d][e+k].cell;){for(a=0;a<c;a++)j[d+a][e+k]=1;k++}h[d][e].cell.setAttribute("rowspan",c);h[d][e].cell.setAttribute("colspan",k)}}}}function C(a){var b,c,d=[],f=0,e=false;b=a.asStripClasses.length;c=a.aoOpenRows.length;if(!(a.fnPreDrawCallback!==null&&a.fnPreDrawCallback.call(a.oInstance,a)===false)){a.bDrawing=true;if(typeof a.iInitDisplayStart!="undefined"&&a.iInitDisplayStart!=-1){a._iDisplayStart=a.oFeatures.bServerSide?
a.iInitDisplayStart:a.iInitDisplayStart>=a.fnRecordsDisplay()?0:a.iInitDisplayStart;a.iInitDisplayStart=-1;E(a)}if(a.bDeferLoading){a.bDeferLoading=false;a.iDraw++}else if(a.oFeatures.bServerSide){if(!a.bDestroying&&!za(a))return}else a.iDraw++;if(a.aiDisplay.length!==0){var h=a._iDisplayStart,j=a._iDisplayEnd;if(a.oFeatures.bServerSide){h=0;j=a.aoData.length}for(h=h;h<j;h++){var k=a.aoData[a.aiDisplay[h]];k.nTr===null&&z(a,a.aiDisplay[h]);var m=k.nTr;if(b!==0){var t=a.asStripClasses[f%b];if(k._sRowStripe!=
t){i(m).removeClass(k._sRowStripe).addClass(t);k._sRowStripe=t}}if(typeof a.fnRowCallback=="function"){m=a.fnRowCallback.call(a.oInstance,m,a.aoData[a.aiDisplay[h]]._aData,f,h);if(!m&&!e){J(a,0,"A node was not returned by fnRowCallback");e=true}}d.push(m);f++;if(c!==0)for(k=0;k<c;k++)m==a.aoOpenRows[k].nParent&&d.push(a.aoOpenRows[k].nTr)}}else{d[0]=p.createElement("tr");if(typeof a.asStripClasses[0]!="undefined")d[0].className=a.asStripClasses[0];e=a.oLanguage.sZeroRecords.replace("_MAX_",a.fnFormatNumber(a.fnRecordsTotal()));
if(a.iDraw==1&&a.sAjaxSource!==null&&!a.oFeatures.bServerSide)e=a.oLanguage.sLoadingRecords;else if(typeof a.oLanguage.sEmptyTable!="undefined"&&a.fnRecordsTotal()===0)e=a.oLanguage.sEmptyTable;b=p.createElement("td");b.setAttribute("valign","top");b.colSpan=X(a);b.className=a.oClasses.sRowEmpty;b.innerHTML=e;d[f].appendChild(b)}typeof a.fnHeaderCallback=="function"&&a.fnHeaderCallback.call(a.oInstance,i(">tr",a.nTHead)[0],aa(a),a._iDisplayStart,a.fnDisplayEnd(),a.aiDisplay);typeof a.fnFooterCallback==
"function"&&a.fnFooterCallback.call(a.oInstance,i(">tr",a.nTFoot)[0],aa(a),a._iDisplayStart,a.fnDisplayEnd(),a.aiDisplay);f=p.createDocumentFragment();b=p.createDocumentFragment();if(a.nTBody){e=a.nTBody.parentNode;b.appendChild(a.nTBody);if(!a.oScroll.bInfinite||!a._bInitComplete||a.bSorted||a.bFiltered){c=a.nTBody.childNodes;for(b=c.length-1;b>=0;b--)c[b].parentNode.removeChild(c[b])}b=0;for(c=d.length;b<c;b++)f.appendChild(d[b]);a.nTBody.appendChild(f);e!==null&&e.appendChild(a.nTBody)}for(b=a.aoDrawCallback.length-
1;b>=0;b--)a.aoDrawCallback[b].fn.call(a.oInstance,a);a.bSorted=false;a.bFiltered=false;a.bDrawing=false;if(a.oFeatures.bServerSide){K(a,false);typeof a._bInitComplete=="undefined"&&w(a)}}}function ba(a){if(a.oFeatures.bSort)R(a,a.oPreviousSearch);else if(a.oFeatures.bFilter)M(a,a.oPreviousSearch);else{E(a);C(a)}}function za(a){if(a.bAjaxDataGet){K(a,true);var b=a.aoColumns.length,c=[],d,f;a.iDraw++;c.push({name:"sEcho",value:a.iDraw});c.push({name:"iColumns",value:b});c.push({name:"sColumns",value:ha(a)});
c.push({name:"iDisplayStart",value:a._iDisplayStart});c.push({name:"iDisplayLength",value:a.oFeatures.bPaginate!==false?a._iDisplayLength:-1});for(f=0;f<b;f++){d=a.aoColumns[f].mDataProp;c.push({name:"mDataProp_"+f,value:typeof d=="function"?"function":d})}if(a.oFeatures.bFilter!==false){c.push({name:"sSearch",value:a.oPreviousSearch.sSearch});c.push({name:"bRegex",value:a.oPreviousSearch.bRegex});for(f=0;f<b;f++){c.push({name:"sSearch_"+f,value:a.aoPreSearchCols[f].sSearch});c.push({name:"bRegex_"+
f,value:a.aoPreSearchCols[f].bRegex});c.push({name:"bSearchable_"+f,value:a.aoColumns[f].bSearchable})}}if(a.oFeatures.bSort!==false){d=a.aaSortingFixed!==null?a.aaSortingFixed.length:0;var e=a.aaSorting.length;c.push({name:"iSortingCols",value:d+e});for(f=0;f<d;f++){c.push({name:"iSortCol_"+f,value:a.aaSortingFixed[f][0]});c.push({name:"sSortDir_"+f,value:a.aaSortingFixed[f][1]})}for(f=0;f<e;f++){c.push({name:"iSortCol_"+(f+d),value:a.aaSorting[f][0]});c.push({name:"sSortDir_"+(f+d),value:a.aaSorting[f][1]})}for(f=
0;f<b;f++)c.push({name:"bSortable_"+f,value:a.aoColumns[f].bSortable})}a.fnServerData.call(a.oInstance,a.sAjaxSource,c,function(h){Aa(a,h)},a);return false}else return true}function Aa(a,b){if(typeof b.sEcho!="undefined")if(b.sEcho*1<a.iDraw)return;else a.iDraw=b.sEcho*1;if(!a.oScroll.bInfinite||a.oScroll.bInfinite&&(a.bSorted||a.bFiltered))ia(a);a._iRecordsTotal=b.iTotalRecords;a._iRecordsDisplay=b.iTotalDisplayRecords;var c=ha(a);if(c=typeof b.sColumns!="undefined"&&c!==""&&b.sColumns!=c)var d=
Ba(a,b.sColumns);b=Z(a.sAjaxDataProp)(b);for(var f=0,e=b.length;f<e;f++)if(c){for(var h=[],j=0,k=a.aoColumns.length;j<k;j++)h.push(b[f][d[j]]);v(a,h)}else v(a,b[f]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=false;C(a);a.bAjaxDataGet=true;K(a,false)}function xa(a){var b=p.createElement("div");a.nTable.parentNode.insertBefore(b,a.nTable);a.nTableWrapper=p.createElement("div");a.nTableWrapper.className=a.oClasses.sWrapper;a.sTableId!==""&&a.nTableWrapper.setAttribute("id",a.sTableId+"_wrapper");
a.nTableReinsertBefore=a.nTable.nextSibling;for(var c=a.nTableWrapper,d=a.sDom.split(""),f,e,h,j,k,m,t,q=0;q<d.length;q++){e=0;h=d[q];if(h=="<"){j=p.createElement("div");k=d[q+1];if(k=="'"||k=='"'){m="";for(t=2;d[q+t]!=k;){m+=d[q+t];t++}if(m=="H")m="fg-toolbar ui-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix";else if(m=="F")m="fg-toolbar ui-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix";if(m.indexOf(".")!=-1){k=m.split(".");j.setAttribute("id",k[0].substr(1,
k[0].length-1));j.className=k[1]}else if(m.charAt(0)=="#")j.setAttribute("id",m.substr(1,m.length-1));else j.className=m;q+=t}c.appendChild(j);c=j}else if(h==">")c=c.parentNode;else if(h=="l"&&a.oFeatures.bPaginate&&a.oFeatures.bLengthChange){f=Ca(a);e=1}else if(h=="f"&&a.oFeatures.bFilter){f=Da(a);e=1}else if(h=="r"&&a.oFeatures.bProcessing){f=Ea(a);e=1}else if(h=="t"){f=Fa(a);e=1}else if(h=="i"&&a.oFeatures.bInfo){f=Ga(a);e=1}else if(h=="p"&&a.oFeatures.bPaginate){f=Ha(a);e=1}else if(o.aoFeatures.length!==
0){j=o.aoFeatures;t=0;for(k=j.length;t<k;t++)if(h==j[t].cFeature){if(f=j[t].fnInit(a))e=1;break}}if(e==1&&f!==null){if(typeof a.aanFeatures[h]!="object")a.aanFeatures[h]=[];a.aanFeatures[h].push(f);c.appendChild(f)}}b.parentNode.replaceChild(a.nTableWrapper,b)}function Fa(a){if(a.oScroll.sX===""&&a.oScroll.sY==="")return a.nTable;var b=p.createElement("div"),c=p.createElement("div"),d=p.createElement("div"),f=p.createElement("div"),e=p.createElement("div"),h=p.createElement("div"),j=a.nTable.cloneNode(false),
k=a.nTable.cloneNode(false),m=a.nTable.getElementsByTagName("thead")[0],t=a.nTable.getElementsByTagName("tfoot").length===0?null:a.nTable.getElementsByTagName("tfoot")[0],q=typeof g.bJQueryUI!="undefined"&&g.bJQueryUI?o.oJUIClasses:o.oStdClasses;c.appendChild(d);e.appendChild(h);f.appendChild(a.nTable);b.appendChild(c);b.appendChild(f);d.appendChild(j);j.appendChild(m);if(t!==null){b.appendChild(e);h.appendChild(k);k.appendChild(t)}b.className=q.sScrollWrapper;c.className=q.sScrollHead;d.className=
q.sScrollHeadInner;f.className=q.sScrollBody;e.className=q.sScrollFoot;h.className=q.sScrollFootInner;if(a.oScroll.bAutoCss){c.style.overflow="hidden";c.style.position="relative";e.style.overflow="hidden";f.style.overflow="auto"}c.style.border="0";c.style.width="100%";e.style.border="0";d.style.width="150%";j.removeAttribute("id");j.style.marginLeft="0";a.nTable.style.marginLeft="0";if(t!==null){k.removeAttribute("id");k.style.marginLeft="0"}d=i(">caption",a.nTable);h=0;for(k=d.length;h<k;h++)j.appendChild(d[h]);
if(a.oScroll.sX!==""){c.style.width=u(a.oScroll.sX);f.style.width=u(a.oScroll.sX);if(t!==null)e.style.width=u(a.oScroll.sX);i(f).scroll(function(){c.scrollLeft=this.scrollLeft;if(t!==null)e.scrollLeft=this.scrollLeft})}if(a.oScroll.sY!=="")f.style.height=u(a.oScroll.sY);a.aoDrawCallback.push({fn:Ia,sName:"scrolling"});a.oScroll.bInfinite&&i(f).scroll(function(){if(!a.bDrawing)if(i(this).scrollTop()+i(this).height()>i(a.nTable).height()-a.oScroll.iLoadGap)if(a.fnDisplayEnd()<a.fnRecordsDisplay()){ja(a,
"next");E(a);C(a)}});a.nScrollHead=c;a.nScrollFoot=e;return b}function Ia(a){var b=a.nScrollHead.getElementsByTagName("div")[0],c=b.getElementsByTagName("table")[0],d=a.nTable.parentNode,f,e,h,j,k,m,t,q,I=[];h=a.nTable.getElementsByTagName("thead");h.length>0&&a.nTable.removeChild(h[0]);if(a.nTFoot!==null){k=a.nTable.getElementsByTagName("tfoot");k.length>0&&a.nTable.removeChild(k[0])}h=a.nTHead.cloneNode(true);a.nTable.insertBefore(h,a.nTable.childNodes[0]);if(a.nTFoot!==null){k=a.nTFoot.cloneNode(true);
a.nTable.insertBefore(k,a.nTable.childNodes[1])}if(a.oScroll.sX===""){d.style.width="100%";b.parentNode.style.width="100%"}var O=S(a,h);f=0;for(e=O.length;f<e;f++){t=Ja(a,f);O[f].style.width=a.aoColumns[t].sWidth}a.nTFoot!==null&&P(function(B){B.style.width=""},k.getElementsByTagName("tr"));f=i(a.nTable).outerWidth();if(a.oScroll.sX===""){a.nTable.style.width="100%";if(i.browser.msie&&i.browser.version<=7)a.nTable.style.width=u(i(a.nTable).outerWidth()-a.oScroll.iBarWidth)}else if(a.oScroll.sXInner!==
"")a.nTable.style.width=u(a.oScroll.sXInner);else if(f==i(d).width()&&i(d).height()<i(a.nTable).height()){a.nTable.style.width=u(f-a.oScroll.iBarWidth);if(i(a.nTable).outerWidth()>f-a.oScroll.iBarWidth)a.nTable.style.width=u(f)}else a.nTable.style.width=u(f);f=i(a.nTable).outerWidth();if(a.oScroll.sX===""){d.style.width=u(f+a.oScroll.iBarWidth);b.parentNode.style.width=u(f+a.oScroll.iBarWidth)}e=a.nTHead.getElementsByTagName("tr");h=h.getElementsByTagName("tr");P(function(B,F){m=B.style;m.paddingTop=
"0";m.paddingBottom="0";m.borderTopWidth="0";m.borderBottomWidth="0";m.height=0;q=i(B).width();F.style.width=u(q);I.push(q)},h,e);i(h).height(0);if(a.nTFoot!==null){j=k.getElementsByTagName("tr");k=a.nTFoot.getElementsByTagName("tr");P(function(B,F){m=B.style;m.paddingTop="0";m.paddingBottom="0";m.borderTopWidth="0";m.borderBottomWidth="0";m.height=0;q=i(B).width();F.style.width=u(q);I.push(q)},j,k);i(j).height(0)}P(function(B){B.innerHTML="";B.style.width=u(I.shift())},h);a.nTFoot!==null&&P(function(B){B.innerHTML=
"";B.style.width=u(I.shift())},j);if(i(a.nTable).outerWidth()<f)if(a.oScroll.sX==="")J(a,1,"The table cannot fit into the current element which will cause column misalignment. It is suggested that you enable x-scrolling or increase the width the table has in which to be drawn");else a.oScroll.sXInner!==""&&J(a,1,"The table cannot fit into the current element which will cause column misalignment. It is suggested that you increase the sScrollXInner property to allow it to draw in a larger area, or simply remove that parameter to allow automatic calculation");
if(a.oScroll.sY==="")if(i.browser.msie&&i.browser.version<=7)d.style.height=u(a.nTable.offsetHeight+a.oScroll.iBarWidth);if(a.oScroll.sY!==""&&a.oScroll.bCollapse){d.style.height=u(a.oScroll.sY);j=a.oScroll.sX!==""&&a.nTable.offsetWidth>d.offsetWidth?a.oScroll.iBarWidth:0;if(a.nTable.offsetHeight<d.offsetHeight)d.style.height=u(i(a.nTable).height()+j)}j=i(a.nTable).outerWidth();c.style.width=u(j);b.style.width=u(j+a.oScroll.iBarWidth);if(a.nTFoot!==null){b=a.nScrollFoot.getElementsByTagName("div")[0];
c=b.getElementsByTagName("table")[0];b.style.width=u(a.nTable.offsetWidth+a.oScroll.iBarWidth);c.style.width=u(a.nTable.offsetWidth)}if(a.bSorted||a.bFiltered)d.scrollTop=0}function ca(a){if(a.oFeatures.bAutoWidth===false)return false;ea(a);for(var b=0,c=a.aoColumns.length;b<c;b++)a.aoColumns[b].nTh.style.width=a.aoColumns[b].sWidth}function Da(a){var b=a.oLanguage.sSearch;b=b.indexOf("_INPUT_")!==-1?b.replace("_INPUT_",'<input type="text" />'):b===""?'<input type="text" />':b+' <input type="text" />';
var c=p.createElement("div");c.className=a.oClasses.sFilter;c.innerHTML="<label>"+b+"</label>";a.sTableId!==""&&typeof a.aanFeatures.f=="undefined"&&c.setAttribute("id",a.sTableId+"_filter");b=i("input",c);b.val(a.oPreviousSearch.sSearch.replace('"',"&quot;"));b.bind("keyup.DT",function(){for(var d=a.aanFeatures.f,f=0,e=d.length;f<e;f++)d[f]!=this.parentNode&&i("input",d[f]).val(this.value);this.value!=a.oPreviousSearch.sSearch&&M(a,{sSearch:this.value,bRegex:a.oPreviousSearch.bRegex,bSmart:a.oPreviousSearch.bSmart})});
b.bind("keypress.DT",function(d){if(d.keyCode==13)return false});return c}function M(a,b,c){Ka(a,b.sSearch,c,b.bRegex,b.bSmart);for(b=0;b<a.aoPreSearchCols.length;b++)La(a,a.aoPreSearchCols[b].sSearch,b,a.aoPreSearchCols[b].bRegex,a.aoPreSearchCols[b].bSmart);o.afnFiltering.length!==0&&Ma(a);a.bFiltered=true;a._iDisplayStart=0;E(a);C(a);ka(a,0)}function Ma(a){for(var b=o.afnFiltering,c=0,d=b.length;c<d;c++)for(var f=0,e=0,h=a.aiDisplay.length;e<h;e++){var j=a.aiDisplay[e-f];if(!b[c](a,da(a,j,"filter"),
j)){a.aiDisplay.splice(e-f,1);f++}}}function La(a,b,c,d,f){if(b!==""){var e=0;b=la(b,d,f);for(d=a.aiDisplay.length-1;d>=0;d--){f=ma(H(a,a.aiDisplay[d],c,"filter"),a.aoColumns[c].sType);if(!b.test(f)){a.aiDisplay.splice(d,1);e++}}}}function Ka(a,b,c,d,f){var e=la(b,d,f);if(typeof c=="undefined"||c===null)c=0;if(o.afnFiltering.length!==0)c=1;if(b.length<=0){a.aiDisplay.splice(0,a.aiDisplay.length);a.aiDisplay=a.aiDisplayMaster.slice()}else if(a.aiDisplay.length==a.aiDisplayMaster.length||a.oPreviousSearch.sSearch.length>
b.length||c==1||b.indexOf(a.oPreviousSearch.sSearch)!==0){a.aiDisplay.splice(0,a.aiDisplay.length);ka(a,1);for(c=0;c<a.aiDisplayMaster.length;c++)e.test(a.asDataSearch[c])&&a.aiDisplay.push(a.aiDisplayMaster[c])}else{var h=0;for(c=0;c<a.asDataSearch.length;c++)if(!e.test(a.asDataSearch[c])){a.aiDisplay.splice(c-h,1);h++}}a.oPreviousSearch.sSearch=b;a.oPreviousSearch.bRegex=d;a.oPreviousSearch.bSmart=f}function ka(a,b){a.asDataSearch.splice(0,a.asDataSearch.length);b=typeof b!="undefined"&&b==1?a.aiDisplayMaster:
a.aiDisplay;for(var c=0,d=b.length;c<d;c++)a.asDataSearch[c]=na(a,da(a,b[c],"filter"))}function na(a,b){var c="";if(typeof a.__nTmpFilter=="undefined")a.__nTmpFilter=p.createElement("div");for(var d=a.__nTmpFilter,f=0,e=a.aoColumns.length;f<e;f++)if(a.aoColumns[f].bSearchable)c+=ma(b[f],a.aoColumns[f].sType)+"  ";if(c.indexOf("&")!==-1){d.innerHTML=c;c=d.textContent?d.textContent:d.innerText;c=c.replace(/\n/g," ").replace(/\r/g,"")}return c}function la(a,b,c){if(c){a=b?a.split(" "):oa(a).split(" ");
a="^(?=.*?"+a.join(")(?=.*?")+").*$";return new RegExp(a,"i")}else{a=b?a:oa(a);return new RegExp(a,"i")}}function ma(a,b){if(typeof o.ofnSearch[b]=="function")return o.ofnSearch[b](a);else if(b=="html")return a.replace(/\n/g," ").replace(/<.*?>/g,"");else if(typeof a=="string")return a.replace(/\n/g," ");else if(a===null)return"";return a}function R(a,b){var c,d,f,e,h=[],j=[],k=o.oSort;d=a.aoData;var m=a.aoColumns;if(!a.oFeatures.bServerSide&&(a.aaSorting.length!==0||a.aaSortingFixed!==null)){h=a.aaSortingFixed!==
null?a.aaSortingFixed.concat(a.aaSorting):a.aaSorting.slice();for(c=0;c<h.length;c++){var t=h[c][0];f=pa(a,t);e=a.aoColumns[t].sSortDataType;if(typeof o.afnSortData[e]!="undefined"){var q=o.afnSortData[e](a,t,f);f=0;for(e=d.length;f<e;f++)N(a,f,t,q[f])}}c=0;for(d=a.aiDisplayMaster.length;c<d;c++)j[a.aiDisplayMaster[c]]=c;var I=h.length;a.aiDisplayMaster.sort(function(O,B){var F,qa;for(c=0;c<I;c++){F=m[h[c][0]].iDataSort;qa=m[F].sType;F=k[(qa?qa:"string")+"-"+h[c][1]](H(a,O,F,"sort"),H(a,B,F,"sort"));
if(F!==0)return F}return k["numeric-asc"](j[O],j[B])})}if((typeof b=="undefined"||b)&&!a.oFeatures.bDeferRender)T(a);a.bSorted=true;if(a.oFeatures.bFilter)M(a,a.oPreviousSearch,1);else{a.aiDisplay=a.aiDisplayMaster.slice();a._iDisplayStart=0;E(a);C(a)}}function ga(a,b,c,d){i(b).bind("click.DT",function(f){if(a.aoColumns[c].bSortable!==false){var e=function(){var h,j;if(f.shiftKey){for(var k=false,m=0;m<a.aaSorting.length;m++)if(a.aaSorting[m][0]==c){k=true;h=a.aaSorting[m][0];j=a.aaSorting[m][2]+
1;if(typeof a.aoColumns[h].asSorting[j]=="undefined")a.aaSorting.splice(m,1);else{a.aaSorting[m][1]=a.aoColumns[h].asSorting[j];a.aaSorting[m][2]=j}break}k===false&&a.aaSorting.push([c,a.aoColumns[c].asSorting[0],0])}else if(a.aaSorting.length==1&&a.aaSorting[0][0]==c){h=a.aaSorting[0][0];j=a.aaSorting[0][2]+1;if(typeof a.aoColumns[h].asSorting[j]=="undefined")j=0;a.aaSorting[0][1]=a.aoColumns[h].asSorting[j];a.aaSorting[0][2]=j}else{a.aaSorting.splice(0,a.aaSorting.length);a.aaSorting.push([c,a.aoColumns[c].asSorting[0],
0])}R(a)};if(a.oFeatures.bProcessing){K(a,true);setTimeout(function(){e();a.oFeatures.bServerSide||K(a,false)},0)}else e();typeof d=="function"&&d(a)}})}function T(a){var b,c,d,f,e,h=a.aoColumns.length,j=a.oClasses;for(b=0;b<h;b++)a.aoColumns[b].bSortable&&i(a.aoColumns[b].nTh).removeClass(j.sSortAsc+" "+j.sSortDesc+" "+a.aoColumns[b].sSortingClass);f=a.aaSortingFixed!==null?a.aaSortingFixed.concat(a.aaSorting):a.aaSorting.slice();for(b=0;b<a.aoColumns.length;b++)if(a.aoColumns[b].bSortable){e=a.aoColumns[b].sSortingClass;
d=-1;for(c=0;c<f.length;c++)if(f[c][0]==b){e=f[c][1]=="asc"?j.sSortAsc:j.sSortDesc;d=c;break}i(a.aoColumns[b].nTh).addClass(e);if(a.bJUI){c=i("span",a.aoColumns[b].nTh);c.removeClass(j.sSortJUIAsc+" "+j.sSortJUIDesc+" "+j.sSortJUI+" "+j.sSortJUIAscAllowed+" "+j.sSortJUIDescAllowed);c.addClass(d==-1?a.aoColumns[b].sSortingClassJUI:f[d][1]=="asc"?j.sSortJUIAsc:j.sSortJUIDesc)}}else i(a.aoColumns[b].nTh).addClass(a.aoColumns[b].sSortingClass);e=j.sSortColumn;if(a.oFeatures.bSort&&a.oFeatures.bSortClasses){d=
Q(a);if(a.oFeatures.bDeferRender)i(d).removeClass(e+"1 "+e+"2 "+e+"3");else if(d.length>=h)for(b=0;b<h;b++)if(d[b].className.indexOf(e+"1")!=-1){c=0;for(a=d.length/h;c<a;c++)d[h*c+b].className=i.trim(d[h*c+b].className.replace(e+"1",""))}else if(d[b].className.indexOf(e+"2")!=-1){c=0;for(a=d.length/h;c<a;c++)d[h*c+b].className=i.trim(d[h*c+b].className.replace(e+"2",""))}else if(d[b].className.indexOf(e+"3")!=-1){c=0;for(a=d.length/h;c<a;c++)d[h*c+b].className=i.trim(d[h*c+b].className.replace(" "+
e+"3",""))}j=1;var k;for(b=0;b<f.length;b++){k=parseInt(f[b][0],10);c=0;for(a=d.length/h;c<a;c++)d[h*c+k].className+=" "+e+j;j<3&&j++}}}function Ha(a){if(a.oScroll.bInfinite)return null;var b=p.createElement("div");b.className=a.oClasses.sPaging+a.sPaginationType;o.oPagination[a.sPaginationType].fnInit(a,b,function(c){E(c);C(c)});typeof a.aanFeatures.p=="undefined"&&a.aoDrawCallback.push({fn:function(c){o.oPagination[c.sPaginationType].fnUpdate(c,function(d){E(d);C(d)})},sName:"pagination"});return b}
function ja(a,b){var c=a._iDisplayStart;if(b=="first")a._iDisplayStart=0;else if(b=="previous"){a._iDisplayStart=a._iDisplayLength>=0?a._iDisplayStart-a._iDisplayLength:0;if(a._iDisplayStart<0)a._iDisplayStart=0}else if(b=="next")if(a._iDisplayLength>=0){if(a._iDisplayStart+a._iDisplayLength<a.fnRecordsDisplay())a._iDisplayStart+=a._iDisplayLength}else a._iDisplayStart=0;else if(b=="last")if(a._iDisplayLength>=0){b=parseInt((a.fnRecordsDisplay()-1)/a._iDisplayLength,10)+1;a._iDisplayStart=(b-1)*a._iDisplayLength}else a._iDisplayStart=
0;else J(a,0,"Unknown paging action: "+b);return c!=a._iDisplayStart}function Ga(a){var b=p.createElement("div");b.className=a.oClasses.sInfo;if(typeof a.aanFeatures.i=="undefined"){a.aoDrawCallback.push({fn:Na,sName:"information"});a.sTableId!==""&&b.setAttribute("id",a.sTableId+"_info")}return b}function Na(a){if(!(!a.oFeatures.bInfo||a.aanFeatures.i.length===0)){var b=a._iDisplayStart+1,c=a.fnDisplayEnd(),d=a.fnRecordsTotal(),f=a.fnRecordsDisplay(),e=a.fnFormatNumber(b),h=a.fnFormatNumber(c),j=
a.fnFormatNumber(d),k=a.fnFormatNumber(f);if(a.oScroll.bInfinite)e=a.fnFormatNumber(1);e=a.fnRecordsDisplay()===0&&a.fnRecordsDisplay()==a.fnRecordsTotal()?a.oLanguage.sInfoEmpty+a.oLanguage.sInfoPostFix:a.fnRecordsDisplay()===0?a.oLanguage.sInfoEmpty+" "+a.oLanguage.sInfoFiltered.replace("_MAX_",j)+a.oLanguage.sInfoPostFix:a.fnRecordsDisplay()==a.fnRecordsTotal()?a.oLanguage.sInfo.replace("_START_",e).replace("_END_",h).replace("_TOTAL_",k)+a.oLanguage.sInfoPostFix:a.oLanguage.sInfo.replace("_START_",
e).replace("_END_",h).replace("_TOTAL_",k)+" "+a.oLanguage.sInfoFiltered.replace("_MAX_",a.fnFormatNumber(a.fnRecordsTotal()))+a.oLanguage.sInfoPostFix;if(a.oLanguage.fnInfoCallback!==null)e=a.oLanguage.fnInfoCallback(a,b,c,d,f,e);a=a.aanFeatures.i;b=0;for(c=a.length;b<c;b++)i(a[b]).html(e)}}function Ca(a){if(a.oScroll.bInfinite)return null;var b='<select size="1" '+(a.sTableId===""?"":'name="'+a.sTableId+'_length"')+">",c,d;if(a.aLengthMenu.length==2&&typeof a.aLengthMenu[0]=="object"&&typeof a.aLengthMenu[1]==
"object"){c=0;for(d=a.aLengthMenu[0].length;c<d;c++)b+='<option value="'+a.aLengthMenu[0][c]+'">'+a.aLengthMenu[1][c]+"</option>"}else{c=0;for(d=a.aLengthMenu.length;c<d;c++)b+='<option value="'+a.aLengthMenu[c]+'">'+a.aLengthMenu[c]+"</option>"}b+="</select>";var f=p.createElement("div");a.sTableId!==""&&typeof a.aanFeatures.l=="undefined"&&f.setAttribute("id",a.sTableId+"_length");f.className=a.oClasses.sLength;f.innerHTML="<label>"+a.oLanguage.sLengthMenu.replace("_MENU_",b)+"</label>";i('select option[value="'+
a._iDisplayLength+'"]',f).attr("selected",true);i("select",f).bind("change.DT",function(){var e=i(this).val(),h=a.aanFeatures.l;c=0;for(d=h.length;c<d;c++)h[c]!=this.parentNode&&i("select",h[c]).val(e);a._iDisplayLength=parseInt(e,10);E(a);if(a.fnDisplayEnd()==a.fnRecordsDisplay()){a._iDisplayStart=a.fnDisplayEnd()-a._iDisplayLength;if(a._iDisplayStart<0)a._iDisplayStart=0}if(a._iDisplayLength==-1)a._iDisplayStart=0;C(a)});return f}function Ea(a){var b=p.createElement("div");a.sTableId!==""&&typeof a.aanFeatures.r==
"undefined"&&b.setAttribute("id",a.sTableId+"_processing");b.innerHTML=a.oLanguage.sProcessing;b.className=a.oClasses.sProcessing;a.nTable.parentNode.insertBefore(b,a.nTable);return b}function K(a,b){if(a.oFeatures.bProcessing){a=a.aanFeatures.r;for(var c=0,d=a.length;c<d;c++)a[c].style.visibility=b?"visible":"hidden"}}function Ja(a,b){for(var c=-1,d=0;d<a.aoColumns.length;d++){a.aoColumns[d].bVisible===true&&c++;if(c==b)return d}return null}function pa(a,b){for(var c=-1,d=0;d<a.aoColumns.length;d++){a.aoColumns[d].bVisible===
true&&c++;if(d==b)return a.aoColumns[d].bVisible===true?c:null}return null}function U(a,b){var c,d;c=a._iDisplayStart;for(d=a._iDisplayEnd;c<d;c++)if(a.aoData[a.aiDisplay[c]].nTr==b)return a.aiDisplay[c];c=0;for(d=a.aoData.length;c<d;c++)if(a.aoData[c].nTr==b)return c;return null}function X(a){for(var b=0,c=0;c<a.aoColumns.length;c++)a.aoColumns[c].bVisible===true&&b++;return b}function E(a){a._iDisplayEnd=a.oFeatures.bPaginate===false?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength>a.aiDisplay.length||
a._iDisplayLength==-1?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength}function Oa(a,b){if(!a||a===null||a==="")return 0;if(typeof b=="undefined")b=p.getElementsByTagName("body")[0];var c=p.createElement("div");c.style.width=u(a);b.appendChild(c);a=c.offsetWidth;b.removeChild(c);return a}function ea(a){var b=0,c,d=0,f=a.aoColumns.length,e,h=i("th",a.nTHead);for(e=0;e<f;e++)if(a.aoColumns[e].bVisible){d++;if(a.aoColumns[e].sWidth!==null){c=Oa(a.aoColumns[e].sWidthOrig,a.nTable.parentNode);if(c!==
null)a.aoColumns[e].sWidth=u(c);b++}}if(f==h.length&&b===0&&d==f&&a.oScroll.sX===""&&a.oScroll.sY==="")for(e=0;e<a.aoColumns.length;e++){c=i(h[e]).width();if(c!==null)a.aoColumns[e].sWidth=u(c)}else{b=a.nTable.cloneNode(false);e=a.nTHead.cloneNode(true);d=p.createElement("tbody");c=p.createElement("tr");b.removeAttribute("id");b.appendChild(e);if(a.nTFoot!==null){b.appendChild(a.nTFoot.cloneNode(true));P(function(k){k.style.width=""},b.getElementsByTagName("tr"))}b.appendChild(d);d.appendChild(c);
d=i("thead th",b);if(d.length===0)d=i("tbody tr:eq(0)>td",b);h=S(a,e);for(e=d=0;e<f;e++){var j=a.aoColumns[e];if(j.bVisible&&j.sWidthOrig!==null&&j.sWidthOrig!=="")h[e-d].style.width=u(j.sWidthOrig);else if(j.bVisible)h[e-d].style.width="";else d++}for(e=0;e<f;e++)if(a.aoColumns[e].bVisible){d=Pa(a,e);if(d!==null){d=d.cloneNode(true);if(a.aoColumns[e].sContentPadding!=="")d.innerHTML+=a.aoColumns[e].sContentPadding;c.appendChild(d)}}f=a.nTable.parentNode;f.appendChild(b);if(a.oScroll.sX!==""&&a.oScroll.sXInner!==
"")b.style.width=u(a.oScroll.sXInner);else if(a.oScroll.sX!==""){b.style.width="";if(i(b).width()<f.offsetWidth)b.style.width=u(f.offsetWidth)}else if(a.oScroll.sY!=="")b.style.width=u(f.offsetWidth);b.style.visibility="hidden";Qa(a,b);f=i("tbody tr:eq(0)",b).children();if(f.length===0)f=S(a,i("thead",b)[0]);if(a.oScroll.sX!==""){for(e=d=c=0;e<a.aoColumns.length;e++)if(a.aoColumns[e].bVisible){c+=a.aoColumns[e].sWidthOrig===null?i(f[d]).outerWidth():parseInt(a.aoColumns[e].sWidth.replace("px",""),
10)+(i(f[d]).outerWidth()-i(f[d]).width());d++}b.style.width=u(c);a.nTable.style.width=u(c)}for(e=d=0;e<a.aoColumns.length;e++)if(a.aoColumns[e].bVisible){c=i(f[d]).width();if(c!==null&&c>0)a.aoColumns[e].sWidth=u(c);d++}a.nTable.style.width=u(i(b).outerWidth());b.parentNode.removeChild(b)}}function Qa(a,b){if(a.oScroll.sX===""&&a.oScroll.sY!==""){i(b).width();b.style.width=u(i(b).outerWidth()-a.oScroll.iBarWidth)}else if(a.oScroll.sX!=="")b.style.width=u(i(b).outerWidth())}function Pa(a,b){var c=
Ra(a,b);if(c<0)return null;if(a.aoData[c].nTr===null){var d=p.createElement("td");d.innerHTML=H(a,c,b,"");return d}return Q(a,c)[b]}function Ra(a,b){for(var c=-1,d=-1,f=0;f<a.aoData.length;f++){var e=H(a,f,b,"display")+"";e=e.replace(/<.*?>/g,"");if(e.length>c){c=e.length;d=f}}return d}function u(a){if(a===null)return"0px";if(typeof a=="number"){if(a<0)return"0px";return a+"px"}var b=a.charCodeAt(a.length-1);if(b<48||b>57)return a;return a+"px"}function Va(a,b){if(a.length!=b.length)return 1;for(var c=
0;c<a.length;c++)if(a[c]!=b[c])return 2;return 0}function fa(a){for(var b=o.aTypes,c=b.length,d=0;d<c;d++){var f=b[d](a);if(f!==null)return f}return"string"}function A(a){for(var b=0;b<D.length;b++)if(D[b].nTable==a)return D[b];return null}function aa(a){for(var b=[],c=a.aoData.length,d=0;d<c;d++)b.push(a.aoData[d]._aData);return b}function $(a){for(var b=[],c=0,d=a.aoData.length;c<d;c++)a.aoData[c].nTr!==null&&b.push(a.aoData[c].nTr);return b}function Q(a,b){var c=[],d,f,e,h,j;f=0;var k=a.aoData.length;
if(typeof b!="undefined"){f=b;k=b+1}for(f=f;f<k;f++){j=a.aoData[f];if(j.nTr!==null){b=[];e=0;for(h=j.nTr.childNodes.length;e<h;e++){d=j.nTr.childNodes[e].nodeName.toLowerCase();if(d=="td"||d=="th")b.push(j.nTr.childNodes[e])}e=d=0;for(h=a.aoColumns.length;e<h;e++)if(a.aoColumns[e].bVisible)c.push(b[e-d]);else{c.push(j._anHidden[e]);d++}}}return c}function oa(a){return a.replace(new RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^)","g"),"\\$1")}function ra(a,b){for(var c=-1,d=
0,f=a.length;d<f;d++)if(a[d]==b)c=d;else a[d]>b&&a[d]--;c!=-1&&a.splice(c,1)}function Ba(a,b){b=b.split(",");for(var c=[],d=0,f=a.aoColumns.length;d<f;d++)for(var e=0;e<f;e++)if(a.aoColumns[d].sName==b[e]){c.push(e);break}return c}function ha(a){for(var b="",c=0,d=a.aoColumns.length;c<d;c++)b+=a.aoColumns[c].sName+",";if(b.length==d)return"";return b.slice(0,-1)}function J(a,b,c){a=a.sTableId===""?"DataTables warning: "+c:"DataTables warning (table id = '"+a.sTableId+"'): "+c;if(b===0)if(o.sErrMode==
"alert")alert(a);else throw a;else typeof console!="undefined"&&typeof console.log!="undefined"&&console.log(a)}function ia(a){a.aoData.splice(0,a.aoData.length);a.aiDisplayMaster.splice(0,a.aiDisplayMaster.length);a.aiDisplay.splice(0,a.aiDisplay.length);E(a)}function sa(a){if(!(!a.oFeatures.bStateSave||typeof a.bDestroying!="undefined")){var b,c,d,f="{";f+='"iCreate":'+(new Date).getTime()+",";f+='"iStart":'+(a.oScroll.bInfinite?0:a._iDisplayStart)+",";f+='"iEnd":'+(a.oScroll.bInfinite?a._iDisplayLength:
a._iDisplayEnd)+",";f+='"iLength":'+a._iDisplayLength+",";f+='"sFilter":"'+encodeURIComponent(a.oPreviousSearch.sSearch)+'",';f+='"sFilterEsc":'+!a.oPreviousSearch.bRegex+",";f+='"aaSorting":[ ';for(b=0;b<a.aaSorting.length;b++)f+="["+a.aaSorting[b][0]+',"'+a.aaSorting[b][1]+'"],';f=f.substring(0,f.length-1);f+="],";f+='"aaSearchCols":[ ';for(b=0;b<a.aoPreSearchCols.length;b++)f+='["'+encodeURIComponent(a.aoPreSearchCols[b].sSearch)+'",'+!a.aoPreSearchCols[b].bRegex+"],";f=f.substring(0,f.length-
1);f+="],";f+='"abVisCols":[ ';for(b=0;b<a.aoColumns.length;b++)f+=a.aoColumns[b].bVisible+",";f=f.substring(0,f.length-1);f+="]";b=0;for(c=a.aoStateSave.length;b<c;b++){d=a.aoStateSave[b].fn(a,f);if(d!=="")f=d}f+="}";Sa(a.sCookiePrefix+a.sInstance,f,a.iCookieDuration,a.sCookiePrefix,a.fnCookieCallback)}}function Ta(a,b){if(a.oFeatures.bStateSave){var c,d,f;d=ta(a.sCookiePrefix+a.sInstance);if(d!==null&&d!==""){try{c=typeof i.parseJSON=="function"?i.parseJSON(d.replace(/'/g,'"')):eval("("+d+")")}catch(e){return}d=
0;for(f=a.aoStateLoad.length;d<f;d++)if(!a.aoStateLoad[d].fn(a,c))return;a.oLoadedState=i.extend(true,{},c);a._iDisplayStart=c.iStart;a.iInitDisplayStart=c.iStart;a._iDisplayEnd=c.iEnd;a._iDisplayLength=c.iLength;a.oPreviousSearch.sSearch=decodeURIComponent(c.sFilter);a.aaSorting=c.aaSorting.slice();a.saved_aaSorting=c.aaSorting.slice();if(typeof c.sFilterEsc!="undefined")a.oPreviousSearch.bRegex=!c.sFilterEsc;if(typeof c.aaSearchCols!="undefined")for(d=0;d<c.aaSearchCols.length;d++)a.aoPreSearchCols[d]=
{sSearch:decodeURIComponent(c.aaSearchCols[d][0]),bRegex:!c.aaSearchCols[d][1]};if(typeof c.abVisCols!="undefined"){b.saved_aoColumns=[];for(d=0;d<c.abVisCols.length;d++){b.saved_aoColumns[d]={};b.saved_aoColumns[d].bVisible=c.abVisCols[d]}}}}}function Sa(a,b,c,d,f){var e=new Date;e.setTime(e.getTime()+c*1E3);c=wa.location.pathname.split("/");a=a+"_"+c.pop().replace(/[\/:]/g,"").toLowerCase();var h;if(f!==null){h=typeof i.parseJSON=="function"?i.parseJSON(b):eval("("+b+")");b=f(a,h,e.toGMTString(),
c.join("/")+"/")}else b=a+"="+encodeURIComponent(b)+"; expires="+e.toGMTString()+"; path="+c.join("/")+"/";f="";e=9999999999999;if((ta(a)!==null?p.cookie.length:b.length+p.cookie.length)+10>4096){a=p.cookie.split(";");for(var j=0,k=a.length;j<k;j++)if(a[j].indexOf(d)!=-1){var m=a[j].split("=");try{h=eval("("+decodeURIComponent(m[1])+")")}catch(t){continue}if(typeof h.iCreate!="undefined"&&h.iCreate<e){f=m[0];e=h.iCreate}}if(f!=="")p.cookie=f+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+c.join("/")+
"/"}p.cookie=b}function ta(a){var b=wa.location.pathname.split("/");a=a+"_"+b[b.length-1].replace(/[\/:]/g,"").toLowerCase()+"=";b=p.cookie.split(";");for(var c=0;c<b.length;c++){for(var d=b[c];d.charAt(0)==" ";)d=d.substring(1,d.length);if(d.indexOf(a)===0)return decodeURIComponent(d.substring(a.length,d.length))}return null}function W(a,b){b=b.getElementsByTagName("tr");var c,d,f,e,h,j,k,m,t=function(O,B,F){for(;typeof O[B][F]!="undefined";)F++;return F};a.splice(0,a.length);d=0;for(j=b.length;d<
j;d++)a.push([]);d=0;for(j=b.length;d<j;d++){f=0;for(k=b[d].childNodes.length;f<k;f++){c=b[d].childNodes[f];if(c.nodeName.toUpperCase()=="TD"||c.nodeName.toUpperCase()=="TH"){var q=c.getAttribute("colspan")*1,I=c.getAttribute("rowspan")*1;q=!q||q===0||q===1?1:q;I=!I||I===0||I===1?1:I;m=t(a,d,0);for(h=0;h<q;h++)for(e=0;e<I;e++){a[d+e][m+h]={cell:c,unique:q==1?true:false};a[d+e].nTr=b[d]}}}}}function S(a,b,c){var d=[];if(typeof c=="undefined"){c=a.aoHeader;if(typeof b!="undefined"){c=[];W(c,b)}}b=0;
for(var f=c.length;b<f;b++)for(var e=0,h=c[b].length;e<h;e++)if(c[b][e].unique&&(typeof d[e]=="undefined"||!a.bSortCellsTop))d[e]=c[b][e].cell;return d}function Ua(){var a=p.createElement("p"),b=a.style;b.width="100%";b.height="200px";var c=p.createElement("div");b=c.style;b.position="absolute";b.top="0px";b.left="0px";b.visibility="hidden";b.width="200px";b.height="150px";b.overflow="hidden";c.appendChild(a);p.body.appendChild(c);b=a.offsetWidth;c.style.overflow="scroll";a=a.offsetWidth;if(b==a)a=
c.clientWidth;p.body.removeChild(c);return b-a}function P(a,b,c){for(var d=0,f=b.length;d<f;d++)for(var e=0,h=b[d].childNodes.length;e<h;e++)if(b[d].childNodes[e].nodeType==1)typeof c!="undefined"?a(b[d].childNodes[e],c[d].childNodes[e]):a(b[d].childNodes[e])}function n(a,b,c,d){if(typeof d=="undefined")d=c;if(typeof b[c]!="undefined")a[d]=b[c]}function da(a,b,c){for(var d=[],f=0,e=a.aoColumns.length;f<e;f++)d.push(H(a,b,f,c));return d}function H(a,b,c,d){var f=a.aoColumns[c];if((c=f.fnGetData(a.aoData[b]._aData))===
undefined){if(a.iDrawError!=a.iDraw&&f.sDefaultContent===null){J(a,0,"Requested unknown parameter '"+f.mDataProp+"' from the data source for row "+b);a.iDrawError=a.iDraw}return f.sDefaultContent}if(c===null&&f.sDefaultContent!==null)c=f.sDefaultContent;if(d=="display"&&c===null)return"";return c}function N(a,b,c,d){a.aoColumns[c].fnSetData(a.aoData[b]._aData,d)}function Z(a){if(a===null)return function(){return null};else if(typeof a=="function")return function(c){return a(c)};else if(typeof a==
"string"&&a.indexOf(".")!=-1){var b=a.split(".");return b.length==2?function(c){return c[b[0]][b[1]]}:b.length==3?function(c){return c[b[0]][b[1]][b[2]]}:function(c){for(var d=0,f=b.length;d<f;d++)c=c[b[d]];return c}}else return function(c){return c[a]}}function ya(a){if(a===null)return function(){};else if(typeof a=="function")return function(c,d){return a(c,d)};else if(typeof a=="string"&&a.indexOf(".")!=-1){var b=a.split(".");return b.length==2?function(c,d){c[b[0]][b[1]]=d}:b.length==3?function(c,
d){c[b[0]][b[1]][b[2]]=d}:function(c,d){for(var f=0,e=b.length-1;f<e;f++)c=c[b[f]];c[b[b.length-1]]=d}}else return function(c,d){c[a]=d}}this.oApi={};this.fnDraw=function(a){var b=A(this[o.iApiIndex]);if(typeof a!="undefined"&&a===false){E(b);C(b)}else ba(b)};this.fnFilter=function(a,b,c,d,f){var e=A(this[o.iApiIndex]);if(e.oFeatures.bFilter){if(typeof c=="undefined")c=false;if(typeof d=="undefined")d=true;if(typeof f=="undefined")f=true;if(typeof b=="undefined"||b===null){M(e,{sSearch:a,bRegex:c,
bSmart:d},1);if(f&&typeof e.aanFeatures.f!="undefined"){b=e.aanFeatures.f;c=0;for(d=b.length;c<d;c++)i("input",b[c]).val(a)}}else{e.aoPreSearchCols[b].sSearch=a;e.aoPreSearchCols[b].bRegex=c;e.aoPreSearchCols[b].bSmart=d;M(e,e.oPreviousSearch,1)}}};this.fnSettings=function(){return A(this[o.iApiIndex])};this.fnVersionCheck=o.fnVersionCheck;this.fnSort=function(a){var b=A(this[o.iApiIndex]);b.aaSorting=a;R(b)};this.fnSortListener=function(a,b,c){ga(A(this[o.iApiIndex]),a,b,c)};this.fnAddData=function(a,
b){if(a.length===0)return[];var c=[],d,f=A(this[o.iApiIndex]);if(typeof a[0]=="object")for(var e=0;e<a.length;e++){d=v(f,a[e]);if(d==-1)return c;c.push(d)}else{d=v(f,a);if(d==-1)return c;c.push(d)}f.aiDisplay=f.aiDisplayMaster.slice();if(typeof b=="undefined"||b)ba(f);return c};this.fnDeleteRow=function(a,b,c){var d=A(this[o.iApiIndex]);a=typeof a=="object"?U(d,a):a;var f=d.aoData.splice(a,1),e=i.inArray(a,d.aiDisplay);d.asDataSearch.splice(e,1);ra(d.aiDisplayMaster,a);ra(d.aiDisplay,a);typeof b==
"function"&&b.call(this,d,f);if(d._iDisplayStart>=d.aiDisplay.length){d._iDisplayStart-=d._iDisplayLength;if(d._iDisplayStart<0)d._iDisplayStart=0}if(typeof c=="undefined"||c){E(d);C(d)}return f};this.fnClearTable=function(a){var b=A(this[o.iApiIndex]);ia(b);if(typeof a=="undefined"||a)C(b)};this.fnOpen=function(a,b,c){var d=A(this[o.iApiIndex]);this.fnClose(a);var f=p.createElement("tr"),e=p.createElement("td");f.appendChild(e);e.className=c;e.colSpan=X(d);if(typeof b.jquery!="undefined"||typeof b==
"object")e.appendChild(b);else e.innerHTML=b;b=i("tr",d.nTBody);i.inArray(a,b)!=-1&&i(f).insertAfter(a);d.aoOpenRows.push({nTr:f,nParent:a});return f};this.fnClose=function(a){for(var b=A(this[o.iApiIndex]),c=0;c<b.aoOpenRows.length;c++)if(b.aoOpenRows[c].nParent==a){(a=b.aoOpenRows[c].nTr.parentNode)&&a.removeChild(b.aoOpenRows[c].nTr);b.aoOpenRows.splice(c,1);return 0}return 1};this.fnGetData=function(a,b){var c=A(this[o.iApiIndex]);if(typeof a!="undefined"){a=typeof a=="object"?U(c,a):a;if(typeof b!=
"undefined")return H(c,a,b,"");return typeof c.aoData[a]!="undefined"?c.aoData[a]._aData:null}return aa(c)};this.fnGetNodes=function(a){var b=A(this[o.iApiIndex]);if(typeof a!="undefined")return typeof b.aoData[a]!="undefined"?b.aoData[a].nTr:null;return $(b)};this.fnGetPosition=function(a){var b=A(this[o.iApiIndex]),c=a.nodeName.toUpperCase();if(c=="TR")return U(b,a);else if(c=="TD"||c=="TH"){c=U(b,a.parentNode);for(var d=Q(b,c),f=0;f<b.aoColumns.length;f++)if(d[f]==a)return[c,pa(b,f),f]}return null};
this.fnUpdate=function(a,b,c,d,f){var e=A(this[o.iApiIndex]);b=typeof b=="object"?U(e,b):b;if(i.isArray(a)&&typeof a=="object"){e.aoData[b]._aData=a.slice();for(c=0;c<e.aoColumns.length;c++)this.fnUpdate(H(e,b,c),b,c,false,false)}else if(typeof a=="object"){e.aoData[b]._aData=i.extend(true,{},a);for(c=0;c<e.aoColumns.length;c++)this.fnUpdate(H(e,b,c),b,c,false,false)}else{a=a;N(e,b,c,a);if(e.aoColumns[c].fnRender!==null){a=e.aoColumns[c].fnRender({iDataRow:b,iDataColumn:c,aData:e.aoData[b]._aData,
oSettings:e});e.aoColumns[c].bUseRendered&&N(e,b,c,a)}if(e.aoData[b].nTr!==null)Q(e,b)[c].innerHTML=a}c=i.inArray(b,e.aiDisplay);e.asDataSearch[c]=na(e,da(e,b,"filter"));if(typeof f=="undefined"||f)ca(e);if(typeof d=="undefined"||d)ba(e);return 0};this.fnSetColumnVis=function(a,b,c){var d=A(this[o.iApiIndex]),f,e;e=d.aoColumns.length;var h,j;if(d.aoColumns[a].bVisible!=b){if(b){for(f=j=0;f<a;f++)d.aoColumns[f].bVisible&&j++;j=j>=X(d);if(!j)for(f=a;f<e;f++)if(d.aoColumns[f].bVisible){h=f;break}f=0;
for(e=d.aoData.length;f<e;f++)if(d.aoData[f].nTr!==null)j?d.aoData[f].nTr.appendChild(d.aoData[f]._anHidden[a]):d.aoData[f].nTr.insertBefore(d.aoData[f]._anHidden[a],Q(d,f)[h])}else{f=0;for(e=d.aoData.length;f<e;f++)if(d.aoData[f].nTr!==null){h=Q(d,f)[a];d.aoData[f]._anHidden[a]=h;h.parentNode.removeChild(h)}}d.aoColumns[a].bVisible=b;L(d,d.aoHeader);d.nTFoot&&L(d,d.aoFooter);f=0;for(e=d.aoOpenRows.length;f<e;f++)d.aoOpenRows[f].nTr.colSpan=X(d);if(typeof c=="undefined"||c){ca(d);C(d)}sa(d)}};this.fnPageChange=
function(a,b){var c=A(this[o.iApiIndex]);ja(c,a);E(c);if(typeof b=="undefined"||b)C(c)};this.fnDestroy=function(){var a=A(this[o.iApiIndex]),b=a.nTableWrapper.parentNode,c=a.nTBody,d,f;a.bDestroying=true;d=0;for(f=a.aoColumns.length;d<f;d++)a.aoColumns[d].bVisible===false&&this.fnSetColumnVis(d,true);i(a.nTableWrapper).find("*").andSelf().unbind(".DT");i("tbody>tr>td."+a.oClasses.sRowEmpty,a.nTable).parent().remove();if(a.nTable!=a.nTHead.parentNode){i(">thead",a.nTable).remove();a.nTable.appendChild(a.nTHead)}if(a.nTFoot&&
a.nTable!=a.nTFoot.parentNode){i(">tfoot",a.nTable).remove();a.nTable.appendChild(a.nTFoot)}a.nTable.parentNode.removeChild(a.nTable);i(a.nTableWrapper).remove();a.aaSorting=[];a.aaSortingFixed=[];T(a);i($(a)).removeClass(a.asStripClasses.join(" "));if(a.bJUI){i("th",a.nTHead).removeClass([o.oStdClasses.sSortable,o.oJUIClasses.sSortableAsc,o.oJUIClasses.sSortableDesc,o.oJUIClasses.sSortableNone].join(" "));i("th span."+o.oJUIClasses.sSortIcon,a.nTHead).remove();i("th",a.nTHead).each(function(){var e=
i("div."+o.oJUIClasses.sSortJUIWrapper,this),h=e.contents();i(this).append(h);e.remove()})}else i("th",a.nTHead).removeClass([o.oStdClasses.sSortable,o.oStdClasses.sSortableAsc,o.oStdClasses.sSortableDesc,o.oStdClasses.sSortableNone].join(" "));a.nTableReinsertBefore?b.insertBefore(a.nTable,a.nTableReinsertBefore):b.appendChild(a.nTable);d=0;for(f=a.aoData.length;d<f;d++)a.aoData[d].nTr!==null&&c.appendChild(a.aoData[d].nTr);if(a.oFeatures.bAutoWidth===true)a.nTable.style.width=u(a.sDestroyWidth);
i(">tr:even",c).addClass(a.asDestoryStrips[0]);i(">tr:odd",c).addClass(a.asDestoryStrips[1]);d=0;for(f=D.length;d<f;d++)D[d]==a&&D.splice(d,1);a=null};this.fnAdjustColumnSizing=function(a){var b=A(this[o.iApiIndex]);ca(b);if(typeof a=="undefined"||a)this.fnDraw(false);else if(b.oScroll.sX!==""||b.oScroll.sY!=="")this.oApi._fnScrollDraw(b)};for(var ua in o.oApi)if(ua)this[ua]=r(ua);this.oApi._fnExternApiFunc=r;this.oApi._fnInitalise=s;this.oApi._fnInitComplete=w;this.oApi._fnLanguageProcess=y;this.oApi._fnAddColumn=
G;this.oApi._fnColumnOptions=x;this.oApi._fnAddData=v;this.oApi._fnCreateTr=z;this.oApi._fnGatherData=Y;this.oApi._fnBuildHead=V;this.oApi._fnDrawHead=L;this.oApi._fnDraw=C;this.oApi._fnReDraw=ba;this.oApi._fnAjaxUpdate=za;this.oApi._fnAjaxUpdateDraw=Aa;this.oApi._fnAddOptionsHtml=xa;this.oApi._fnFeatureHtmlTable=Fa;this.oApi._fnScrollDraw=Ia;this.oApi._fnAjustColumnSizing=ca;this.oApi._fnFeatureHtmlFilter=Da;this.oApi._fnFilterComplete=M;this.oApi._fnFilterCustom=Ma;this.oApi._fnFilterColumn=La;
this.oApi._fnFilter=Ka;this.oApi._fnBuildSearchArray=ka;this.oApi._fnBuildSearchRow=na;this.oApi._fnFilterCreateSearch=la;this.oApi._fnDataToSearch=ma;this.oApi._fnSort=R;this.oApi._fnSortAttachListener=ga;this.oApi._fnSortingClasses=T;this.oApi._fnFeatureHtmlPaginate=Ha;this.oApi._fnPageChange=ja;this.oApi._fnFeatureHtmlInfo=Ga;this.oApi._fnUpdateInfo=Na;this.oApi._fnFeatureHtmlLength=Ca;this.oApi._fnFeatureHtmlProcessing=Ea;this.oApi._fnProcessingDisplay=K;this.oApi._fnVisibleToColumnIndex=Ja;this.oApi._fnColumnIndexToVisible=
pa;this.oApi._fnNodeToDataIndex=U;this.oApi._fnVisbleColumns=X;this.oApi._fnCalculateEnd=E;this.oApi._fnConvertToWidth=Oa;this.oApi._fnCalculateColumnWidths=ea;this.oApi._fnScrollingWidthAdjust=Qa;this.oApi._fnGetWidestNode=Pa;this.oApi._fnGetMaxLenString=Ra;this.oApi._fnStringToCss=u;this.oApi._fnArrayCmp=Va;this.oApi._fnDetectType=fa;this.oApi._fnSettingsFromNode=A;this.oApi._fnGetDataMaster=aa;this.oApi._fnGetTrNodes=$;this.oApi._fnGetTdNodes=Q;this.oApi._fnEscapeRegex=oa;this.oApi._fnDeleteIndex=
ra;this.oApi._fnReOrderIndex=Ba;this.oApi._fnColumnOrdering=ha;this.oApi._fnLog=J;this.oApi._fnClearTable=ia;this.oApi._fnSaveState=sa;this.oApi._fnLoadState=Ta;this.oApi._fnCreateCookie=Sa;this.oApi._fnReadCookie=ta;this.oApi._fnDetectHeader=W;this.oApi._fnGetUniqueThs=S;this.oApi._fnScrollBarWidth=Ua;this.oApi._fnApplyToChildren=P;this.oApi._fnMap=n;this.oApi._fnGetRowData=da;this.oApi._fnGetCellData=H;this.oApi._fnSetCellData=N;this.oApi._fnGetObjectDataFn=Z;this.oApi._fnSetObjectDataFn=ya;var va=
this;return this.each(function(){var a=0,b,c,d,f;a=0;for(b=D.length;a<b;a++){if(D[a].nTable==this)if(typeof g=="undefined"||typeof g.bRetrieve!="undefined"&&g.bRetrieve===true)return D[a].oInstance;else if(typeof g.bDestroy!="undefined"&&g.bDestroy===true){D[a].oInstance.fnDestroy();break}else{J(D[a],0,"Cannot reinitialise DataTable.\n\nTo retrieve the DataTables object for this table, please pass either no arguments to the dataTable() function, or set bRetrieve to true. Alternatively, to destory the old table and create a new one, set bDestroy to true (note that a lot of changes to the configuration can be made through the API which is usually much faster).");
return}if(D[a].sTableId!==""&&D[a].sTableId==this.getAttribute("id")){D.splice(a,1);break}}var e=new l;D.push(e);var h=false,j=false;a=this.getAttribute("id");if(a!==null){e.sTableId=a;e.sInstance=a}else e.sInstance=o._oExternConfig.iNextUnique++;if(this.nodeName.toLowerCase()!="table")J(e,0,"Attempted to initialise DataTables on a node which is not a table: "+this.nodeName);else{e.nTable=this;e.oInstance=va.length==1?va:i(this).dataTable();e.oApi=va.oApi;e.sDestroyWidth=i(this).width();if(typeof g!=
"undefined"&&g!==null){e.oInit=g;n(e.oFeatures,g,"bPaginate");n(e.oFeatures,g,"bLengthChange");n(e.oFeatures,g,"bFilter");n(e.oFeatures,g,"bSort");n(e.oFeatures,g,"bInfo");n(e.oFeatures,g,"bProcessing");n(e.oFeatures,g,"bAutoWidth");n(e.oFeatures,g,"bSortClasses");n(e.oFeatures,g,"bServerSide");n(e.oFeatures,g,"bDeferRender");n(e.oScroll,g,"sScrollX","sX");n(e.oScroll,g,"sScrollXInner","sXInner");n(e.oScroll,g,"sScrollY","sY");n(e.oScroll,g,"bScrollCollapse","bCollapse");n(e.oScroll,g,"bScrollInfinite",
"bInfinite");n(e.oScroll,g,"iScrollLoadGap","iLoadGap");n(e.oScroll,g,"bScrollAutoCss","bAutoCss");n(e,g,"asStripClasses");n(e,g,"fnPreDrawCallback");n(e,g,"fnRowCallback");n(e,g,"fnHeaderCallback");n(e,g,"fnFooterCallback");n(e,g,"fnCookieCallback");n(e,g,"fnInitComplete");n(e,g,"fnServerData");n(e,g,"fnFormatNumber");n(e,g,"aaSorting");n(e,g,"aaSortingFixed");n(e,g,"aLengthMenu");n(e,g,"sPaginationType");n(e,g,"sAjaxSource");n(e,g,"sAjaxDataProp");n(e,g,"iCookieDuration");n(e,g,"sCookiePrefix");
n(e,g,"sDom");n(e,g,"bSortCellsTop");n(e,g,"oSearch","oPreviousSearch");n(e,g,"aoSearchCols","aoPreSearchCols");n(e,g,"iDisplayLength","_iDisplayLength");n(e,g,"bJQueryUI","bJUI");n(e.oLanguage,g,"fnInfoCallback");typeof g.fnDrawCallback=="function"&&e.aoDrawCallback.push({fn:g.fnDrawCallback,sName:"user"});typeof g.fnStateSaveCallback=="function"&&e.aoStateSave.push({fn:g.fnStateSaveCallback,sName:"user"});typeof g.fnStateLoadCallback=="function"&&e.aoStateLoad.push({fn:g.fnStateLoadCallback,sName:"user"});
if(e.oFeatures.bServerSide&&e.oFeatures.bSort&&e.oFeatures.bSortClasses)e.aoDrawCallback.push({fn:T,sName:"server_side_sort_classes"});else e.oFeatures.bDeferRender&&e.aoDrawCallback.push({fn:T,sName:"defer_sort_classes"});if(typeof g.bJQueryUI!="undefined"&&g.bJQueryUI){e.oClasses=o.oJUIClasses;if(typeof g.sDom=="undefined")e.sDom='<"H"lfr>t<"F"ip>'}if(e.oScroll.sX!==""||e.oScroll.sY!=="")e.oScroll.iBarWidth=Ua();if(typeof g.iDisplayStart!="undefined"&&typeof e.iInitDisplayStart=="undefined"){e.iInitDisplayStart=
g.iDisplayStart;e._iDisplayStart=g.iDisplayStart}if(typeof g.bStateSave!="undefined"){e.oFeatures.bStateSave=g.bStateSave;Ta(e,g);e.aoDrawCallback.push({fn:sa,sName:"state_save"})}if(typeof g.iDeferLoading!="undefined"){e.bDeferLoading=true;e._iRecordsTotal=g.iDeferLoading;e._iRecordsDisplay=g.iDeferLoading}if(typeof g.aaData!="undefined")j=true;if(typeof g!="undefined"&&typeof g.aoData!="undefined")g.aoColumns=g.aoData;if(typeof g.oLanguage!="undefined")if(typeof g.oLanguage.sUrl!="undefined"&&g.oLanguage.sUrl!==
""){e.oLanguage.sUrl=g.oLanguage.sUrl;i.getJSON(e.oLanguage.sUrl,null,function(t){y(e,t,true)});h=true}else y(e,g.oLanguage,false)}else g={};if(typeof g.asStripClasses=="undefined"){e.asStripClasses.push(e.oClasses.sStripOdd);e.asStripClasses.push(e.oClasses.sStripEven)}c=false;d=i(">tbody>tr",this);a=0;for(b=e.asStripClasses.length;a<b;a++)if(d.filter(":lt(2)").hasClass(e.asStripClasses[a])){c=true;break}if(c){e.asDestoryStrips=["",""];if(i(d[0]).hasClass(e.oClasses.sStripOdd))e.asDestoryStrips[0]+=
e.oClasses.sStripOdd+" ";if(i(d[0]).hasClass(e.oClasses.sStripEven))e.asDestoryStrips[0]+=e.oClasses.sStripEven;if(i(d[1]).hasClass(e.oClasses.sStripOdd))e.asDestoryStrips[1]+=e.oClasses.sStripOdd+" ";if(i(d[1]).hasClass(e.oClasses.sStripEven))e.asDestoryStrips[1]+=e.oClasses.sStripEven;d.removeClass(e.asStripClasses.join(" "))}c=[];var k;a=this.getElementsByTagName("thead");if(a.length!==0){W(e.aoHeader,a[0]);c=S(e)}if(typeof g.aoColumns=="undefined"){k=[];a=0;for(b=c.length;a<b;a++)k.push(null)}else k=
g.aoColumns;a=0;for(b=k.length;a<b;a++){if(typeof g.saved_aoColumns!="undefined"&&g.saved_aoColumns.length==b){if(k[a]===null)k[a]={};k[a].bVisible=g.saved_aoColumns[a].bVisible}G(e,c?c[a]:null)}if(typeof g.aoColumnDefs!="undefined")for(a=g.aoColumnDefs.length-1;a>=0;a--){var m=g.aoColumnDefs[a].aTargets;i.isArray(m)||J(e,1,"aTargets must be an array of targets, not a "+typeof m);c=0;for(d=m.length;c<d;c++)if(typeof m[c]=="number"&&m[c]>=0){for(;e.aoColumns.length<=m[c];)G(e);x(e,m[c],g.aoColumnDefs[a])}else if(typeof m[c]==
"number"&&m[c]<0)x(e,e.aoColumns.length+m[c],g.aoColumnDefs[a]);else if(typeof m[c]=="string"){b=0;for(f=e.aoColumns.length;b<f;b++)if(m[c]=="_all"||i(e.aoColumns[b].nTh).hasClass(m[c]))x(e,b,g.aoColumnDefs[a])}}if(typeof k!="undefined"){a=0;for(b=k.length;a<b;a++)x(e,a,k[a])}a=0;for(b=e.aaSorting.length;a<b;a++){if(e.aaSorting[a][0]>=e.aoColumns.length)e.aaSorting[a][0]=0;k=e.aoColumns[e.aaSorting[a][0]];if(typeof e.aaSorting[a][2]=="undefined")e.aaSorting[a][2]=0;if(typeof g.aaSorting=="undefined"&&
typeof e.saved_aaSorting=="undefined")e.aaSorting[a][1]=k.asSorting[0];c=0;for(d=k.asSorting.length;c<d;c++)if(e.aaSorting[a][1]==k.asSorting[c]){e.aaSorting[a][2]=c;break}}T(e);a=i(">thead",this);if(a.length===0){a=[p.createElement("thead")];this.appendChild(a[0])}e.nTHead=a[0];a=i(">tbody",this);if(a.length===0){a=[p.createElement("tbody")];this.appendChild(a[0])}e.nTBody=a[0];a=i(">tfoot",this);if(a.length>0){e.nTFoot=a[0];W(e.aoFooter,e.nTFoot)}if(j)for(a=0;a<g.aaData.length;a++)v(e,g.aaData[a]);
else Y(e);e.aiDisplay=e.aiDisplayMaster.slice();e.bInitialised=true;h===false&&s(e)}})}})(jQuery,window,document);


/* File:        ColVis.js
 * 
 * Version:     1.0.4
 * CVS:         $Id$
 * Description: Controls for column visiblity in DataTables
 * Author:      Allan Jardine (www.sprymedia.co.uk)
 * Created:     Wed Sep 15 18:23:29 BST 2010
 * Modified:    $Date$ by $Author$
 * Language:    Javascript
 * License:     LGPL
 * Project:     Just a little bit of fun :-)
 * Contact:     www.sprymedia.co.uk/contact
 * 
 * Copyright 2010 Allan Jardine, all rights reserved.
 *
 */
 (function ($) {

	/** 
	* ColVis provides column visiblity control for DataTables
	* @class ColVis
	* @constructor
	* @param {object} DataTables settings object
	*/
	ColVis = function (oDTSettings, oInit) {
		/* Santiy check that we are a new instance */
		if (!this.CLASS || this.CLASS != "ColVis") {
			alert("Warning: ColVis must be initialised with the keyword 'new'");
		}

		if (typeof oInit == 'undefined') {
			oInit = {};
		}


		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
		* Public class variables
		* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

		/**
		* @namespace Settings object which contains customisable information for ColVis instance
		*/
		this.s = {
			/**
			* DataTables settings object
			*  @property dt
			*  @type     Object
			*  @default  null
			*/
			"dt": null,

			/**
			* Customisation object
			*  @property oInit
			*  @type     Object
			*  @default  passed in
			*/
			"oInit": oInit,

			/**
			* Callback function to tell the user when the state has changed
			*  @property fnStateChange
			*  @type     function
			*  @default  null
			*/
			"fnStateChange": null,

			/**
			* Mode of activation. Can be 'click' or 'mouseover'
			*  @property activate
			*  @type     String
			*  @default  click
			*/
			"activate": "click",

			/**
			* Position of the collection menu when shown - align "left" or "right"
			*  @property sAlign
			*  @type     String
			*  @default  right
			*/
			"sAlign": "left",

			/**
			* Text used for the button
			*  @property buttonText
			*  @type     String
			*  @default  Show / hide columns
			*/
			"buttonText": "Show / hide columns",

			/**
			* Flag to say if the collection is hidden
			*  @property hidden
			*  @type     boolean
			*  @default  true
			*/
			"hidden": true,

			/**
			* List of columns (integers) which should be excluded from the list
			*  @property aiExclude
			*  @type     Array
			*  @default  []
			*/
			"aiExclude": [],

			/**
			* Store the original viisbility settings so they could be restored
			*  @property abOriginal
			*  @type     Array
			*  @default  []
			*/
			"abOriginal": [],

			/**
			* Show restore button
			*  @property bRestore
			*  @type     Array
			*  @default  []
			*/
			"bRestore": false,

			/**
			* Restore button text
			*  @property sRestore
			*  @type     String
			*  @default  Restore original
			*/
			"sRestore": "Restore original"
		};


		/**
		* @namespace Common and useful DOM elements for the class instance
		*/
		this.dom = {
			/**
			* Wrapper for the button - given back to DataTables as the node to insert
			*  @property wrapper
			*  @type     Node
			*  @default  null
			*/
			"wrapper": null,

			/**
			* Activation button
			*  @property button
			*  @type     Node
			*  @default  null
			*/
			"button": null,

			/**
			* Collection list node
			*  @property collection
			*  @type     Node
			*  @default  null
			*/
			"collection": null,

			/**
			* Background node used for shading the display and event capturing
			*  @property background
			*  @type     Node
			*  @default  null
			*/
			"background": null,

			/**
			* Element to position over the activation button to catch mouse events when using mouseover
			*  @property catcher
			*  @type     Node
			*  @default  null
			*/
			"catcher": null,

			/**
			* List of button elements
			*  @property buttons
			*  @type     Array
			*  @default  []
			*/
			"buttons": [],

			/**
			* Restore button
			*  @property restore
			*  @type     Node
			*  @default  null
			*/
			"restore": null
		};

		/* Store global reference */
		ColVis.aInstances.push(this);

		/* Constructor logic */
		this.s.dt = oDTSettings;
		this._fnConstruct();
		return this;
	};



	ColVis.prototype = {
		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
		* Public methods
		* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

		/**
		* Rebuild the list of buttons for this instance (i.e. if there is a column header update)
		*  @method  fnRebuild
		*  @returns void
		*/
		"fnRebuild": function () {
			/* Remove the old buttons */
			for (var i = this.dom.buttons.length - 1; i >= 0; i--) {
				if (this.dom.buttons[i] !== null) {
					this.dom.collection.removeChild(this.dom.buttons[i]);
				}
			}
			this.dom.buttons.splice(0, this.dom.buttons.length);

			if (this.dom.restore) {
				this.dom.restore.parentNode(this.dom.restore);
			}

			/* Re-add them (this is not the optimal way of doing this, it is fast and effective) */
			this._fnAddButtons();

			/* Update the checkboxes */
			this._fnDrawCallback();
		},



		/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
		* Private methods (they are of course public in JS, but recommended as private)
		* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

		/**
		* Constructor logic
		*  @method  _fnConstruct
		*  @returns void
		*  @private 
		*/
		"_fnConstruct": function () {
			this._fnApplyCustomisation();

			var that = this;
			this.dom.wrapper = document.createElement('div');
			this.dom.wrapper.className = "ColVis TableTools";

			this.dom.button = this._fnDomBaseButton(this.s.buttonText);
			this.dom.button.className += " ColVis_MasterButton";
			this.dom.wrapper.appendChild(this.dom.button);

			this.dom.catcher = this._fnDomCatcher();
			this.dom.collection = this._fnDomCollection();
			this.dom.background = this._fnDomBackground();

			this._fnAddButtons();

			/* Store the original visbility information */
			for (var i = 0, iLen = this.s.dt.aoColumns.length; i < iLen; i++) {
				this.s.abOriginal.push(this.s.dt.aoColumns[i].bVisible);
			}

			/* Update on each draw */
			this.s.dt.aoDrawCallback.push({
				"fn": function () {
					that._fnDrawCallback.call(that);
				},
				"sName": "ColVis"
			});
		},


		/**
		* Apply any customisation to the settings from the DataTables initialisation
		*  @method  _fnApplyCustomisation
		*  @returns void
		*  @private 
		*/
		"_fnApplyCustomisation": function () {
			var oConfig = this.s.oInit;

			if (typeof oConfig.activate != 'undefined') {
				this.s.activate = oConfig.activate;
			}

			if (typeof oConfig.buttonText != 'undefined') {
				this.s.buttonText = oConfig.buttonText;
			}

			if (typeof oConfig.aiExclude != 'undefined') {
				this.s.aiExclude = oConfig.aiExclude;
			}

			if (typeof oConfig.bRestore != 'undefined') {
				this.s.bRestore = oConfig.bRestore;
			}

			if (typeof oConfig.sRestore != 'undefined') {
				this.s.sRestore = oConfig.sRestore;
			}

			if (typeof oConfig.sAlign != 'undefined') {
				this.s.sAlign = oConfig.sAlign;
			}

			if (typeof oConfig.fnStateChange != 'undefined') {
				this.s.fnStateChange = oConfig.fnStateChange;
			}
		},


		/**
		* On each table draw, check the visiblity checkboxes as needed. This allows any process to
		* update the table's column visiblity and ColVis will still be accurate.
		*  @method  _fnDrawCallback
		*  @returns void
		*  @private 
		*/
		"_fnDrawCallback": function () {
			var aoColumns = this.s.dt.aoColumns;

			for (var i = 0, iLen = aoColumns.length; i < iLen; i++) {
				if (this.dom.buttons[i] !== null) {
					if (aoColumns[i].bVisible) {
						$('input', this.dom.buttons[i]).attr('checked', 'checked');
					}
					else {
						$('input', this.dom.buttons[i]).removeAttr('checked');
					}
				}
			}
		},


		/**
		* Loop through the columns in the table and as a new button for each one.
		*  @method  _fnAddButtons
		*  @returns void
		*  @private 
		*/
		"_fnAddButtons": function () {
			var 
			nButton,
			sExclude = "," + this.s.aiExclude.join(',') + ",";

			for (var i = 0, iLen = this.s.dt.aoColumns.length; i < iLen; i++) {
				if (sExclude.indexOf("," + i + ",") == -1) {
					nButton = this._fnDomColumnButton(i);
					this.dom.buttons.push(nButton);
					this.dom.collection.appendChild(nButton);
				}
				else {
					this.dom.buttons.push(null);
				}
			}

			if (this.s.bRestore) {
				nButton = this._fnDomRestoreButton();
				nButton.className += " ColVis_Restore";
				this.dom.buttons.push(nButton);
				this.dom.collection.appendChild(nButton);
			}
		},


		/**
		* Create a button which allows a "restore" action
		*  @method  _fnDomRestoreButton
		*  @returns {Node} Created button
		*  @private 
		*/
		"_fnDomRestoreButton": function () {
			var 
			that = this,
		  nButton = document.createElement('button'),
		  nSpan = document.createElement('span');

			nButton.className = !this.s.dt.bJUI ? "ColVis_Button TableTools_Button" :
			"ColVis_Button TableTools_Button ui-button ui-state-default";
			nButton.appendChild(nSpan);
			$(nSpan).html('<span class="ColVis_title">' + this.s.sRestore + '</span>');

			$(nButton).click(function (e) {
				for (var i = 0, iLen = that.s.abOriginal.length; i < iLen; i++) {
					that.s.dt.oInstance.fnSetColumnVis(i, that.s.abOriginal[i], false);
				}
				that.s.dt.oInstance.fnDraw(false);
			});

			return nButton;
		},


		/**
		* Create the DOM for a show / hide button
		*  @method  _fnDomColumnButton
		*  @param {int} i Column in question
		*  @returns {Node} Created button
		*  @private 
		*/
		"_fnDomColumnButton": function (i) {
			var 
			that = this,
			oColumn = this.s.dt.aoColumns[i],
		  nButton = document.createElement('button'),
		  nSpan = document.createElement('span');

			nButton.className = !this.s.dt.bJUI ? "ColVis_Button TableTools_Button" :
			"ColVis_Button TableTools_Button ui-button ui-state-default";
			nButton.appendChild(nSpan);
			$(nSpan).html(
			'<span class="ColVis_radio"><input type="checkbox"></span>' +
			'<span class="ColVis_title">' + oColumn.sTitle + '</span>');

			$(nButton).click(function (e) {
//				var showHide = $('input', this).attr('checked') === true ? false : true;
//				if (e.target.nodeName.toLowerCase() == "input") {
//					showHide = $('input', this).attr('checked');
//				}

				var showHide = !$('input', this).is(":checked");
				if (e.target.nodeName.toLowerCase() == "input") {
					showHide = $('input', this).is(":checked");
				}

				/* Need to consider the case where the initialiser created more than one table - change the
				* API index that DataTables is using
				*/
				var oldIndex = $.fn.dataTableExt.iApiIndex;
				$.fn.dataTableExt.iApiIndex = that._fnDataTablesApiIndex.call(that);
				that.s.dt.oInstance.fnSetColumnVis(i, showHide);
				$.fn.dataTableExt.iApiIndex = oldIndex; /* Restore */

				if (that.s.fnStateChange !== null) {
					that.s.fnStateChange.call(that, i, showHide);
				}
			});

			return nButton;
		},


		/**
		* Get the position in the DataTables instance array of the table for this instance of ColVis
		*  @method  _fnDataTablesApiIndex
		*  @returns {int} Index
		*  @private 
		*/
		"_fnDataTablesApiIndex": function () {
			for (var i = 0, iLen = this.s.dt.oInstance.length; i < iLen; i++) {
				if (this.s.dt.oInstance[i] == this.s.dt.nTable) {
					return i;
				}
			}
			return 0;
		},


		/**
		* Create the DOM needed for the button and apply some base properties. All buttons start here
		*  @method  _fnDomBaseButton
		*  @param   {String} text Button text
		*  @returns {Node} DIV element for the button
		*  @private 
		*/
		"_fnDomBaseButton": function (text) {
			var 
			that = this,
		  nButton = document.createElement('button'),
		  nSpan = document.createElement('span'),
			sEvent = this.s.activate == "mouseover" ? "mouseover" : "click";

			nButton.className = !this.s.dt.bJUI ? "ColVis_Button TableTools_Button" :
			"ColVis_Button TableTools_Button ui-button ui-state-default";
			nButton.appendChild(nSpan);
			nSpan.innerHTML = text;

			$(nButton).bind(sEvent, function (e) {
				that._fnCollectionShow();
				e.preventDefault();
			});

			return nButton;
		},


		/**
		* Create the element used to contain list the columns (it is shown and hidden as needed)
		*  @method  _fnDomCollection
		*  @returns {Node} div container for the collection
		*  @private 
		*/
		"_fnDomCollection": function () {
			var that = this;
			var nHidden = document.createElement('div');
			nHidden.style.display = "none";
			nHidden.className = !this.s.dt.bJUI ? "ColVis_collection TableTools_collection" :
			"ColVis_collection TableTools_collection ui-buttonset ui-buttonset-multi";
			nHidden.style.position = "absolute";
			$(nHidden).css('opacity', 0);

			return nHidden;
		},


		/**
		* An element to be placed on top of the activate button to catch events
		*  @method  _fnDomCatcher
		*  @returns {Node} div container for the collection
		*  @private 
		*/
		"_fnDomCatcher": function () {
			var 
			that = this,
			nCatcher = document.createElement('div');
			nCatcher.className = "ColVis_catcher TableTools_catcher";

			$(nCatcher).click(function () {
				that._fnCollectionHide.call(that, null, null);
			});

			return nCatcher;
		},


		/**
		* Create the element used to shade the background, and capture hide events (it is shown and 
		* hidden as needed)
		*  @method  _fnDomBackground
		*  @returns {Node} div container for the background
		*  @private 
		*/
		"_fnDomBackground": function () {
			var that = this;

			var nBackground = document.createElement('div');
			nBackground.style.position = "absolute";
			nBackground.style.left = "0px";
			nBackground.style.top = "0px";
			nBackground.className = "ColVis_collectionBackground TableTools_collectionBackground";
			$(nBackground).css('opacity', 0);

			$(nBackground).click(function () {
				that._fnCollectionHide.call(that, null, null);
			});

			/* When considering a mouse over action for the activation, we also consider a mouse out
			* which is the same as a mouse over the background - without all the messing around of
			* bubbling events. Use the catcher element to avoid messing around with bubbling
			*/
			if (this.s.activate == "mouseover") {
				$(nBackground).mouseover(function () {
					that.s.overcollection = false;
					that._fnCollectionHide.call(that, null, null);
				});
			}

			return nBackground;
		},


		/**
		* Show the show / hide list and the background
		*  @method  _fnCollectionShow
		*  @returns void
		*  @private 
		*/
		"_fnCollectionShow": function () {
			var that = this;
			var oPos = $(this.dom.button).offset();
			var nHidden = this.dom.collection;
			var nBackground = this.dom.background;
			var iDivX = parseInt(oPos.left, 10);
			var iDivY = parseInt(oPos.top + $(this.dom.button).outerHeight(), 10);

			nHidden.style.top = iDivY + "px";
			nHidden.style.left = iDivX + "px";
			nHidden.style.display = "block";
			$(nHidden).css('opacity', 0);

			var iWinHeight = $(window).height(), iDocHeight = $(document).height(),
			iWinWidth = $(window).width(), iDocWidth = $(document).width();

			nBackground.style.height = ((iWinHeight > iDocHeight) ? iWinHeight : iDocHeight) + "px";
			nBackground.style.width = ((iWinWidth < iDocWidth) ? iWinWidth : iDocWidth) + "px";

			var oStyle = this.dom.catcher.style;
			oStyle.height = $(this.dom.button).outerHeight() + "px";
			oStyle.width = $(this.dom.button).outerWidth() + "px";
			oStyle.top = oPos.top + "px";
			oStyle.left = iDivX + "px";

			document.body.appendChild(nBackground);
			document.body.appendChild(nHidden);
			document.body.appendChild(this.dom.catcher);

			/* Visual corrections to try and keep the collection visible */
			nHidden.style.left = this.s.sAlign == "left" ?
			iDivX + "px" : (iDivX - $(nHidden).outerWidth() + $(this.dom.button).outerWidth()) + "px";

			var iDivWidth = $(nHidden).outerWidth();
			var iDivHeight = $(nHidden).outerHeight();

			if (iDivX + iDivWidth > iDocWidth) {
				nHidden.style.left = (iDocWidth - iDivWidth) + "px";
			}

			if (iDivY + iDivHeight > iDocHeight) {
				nHidden.style.top = (iDivY - iDivHeight - $(this.dom.button).outerHeight()) + "px";
			}


			/* This results in a very small delay for the end user but it allows the animation to be
			* much smoother. If you don't want the animation, then the setTimeout can be removed
			*/
			setTimeout(function () {
				$(nHidden).animate({ "opacity": 1 }, 500);
				$(nBackground).animate({ "opacity": 0.1 }, 500, 'linear', function () {
					/* In IE6 if you set the checked attribute of a hidden checkbox, then this is not visually
					* reflected. As such, we need to do it here, once it is visible. Unbelievable.
					*/
					if (jQuery.browser.msie && jQuery.browser.version == "6.0") {
						that._fnDrawCallback();
					}
				});
			}, 10);

			this.s.hidden = false;
		},


		/**
		* Hide the show / hide list and the background
		*  @method  _fnCollectionHide
		*  @returns void
		*  @private 
		*/
		"_fnCollectionHide": function () {
			var that = this;

			if (!this.s.hidden && this.dom.collection !== null) {
				this.s.hidden = true;

				$(this.dom.collection).animate({ "opacity": 0 }, 500, function (e) {
					this.style.display = "none";
				});

				$(this.dom.background).animate({ "opacity": 0 }, 500, function (e) {
					document.body.removeChild(that.dom.background);
					document.body.removeChild(that.dom.catcher);
				});
			}
		}
	};





	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	* Static object methods
	* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

	/**
	* Rebuild the collection for a given table, or all tables if no parameter given
	*  @method  ColVis.fnRebuild
	*  @static
	*  @param   object oTable DataTable instance to consider - optional
	*  @returns void
	*/
	ColVis.fnRebuild = function (oTable) {
		var nTable = null;
		if (typeof oTable != 'undefined') {
			nTable = oTable.fnSettings().nTable;
		}

		for (var i = 0, iLen = ColVis.aInstances.length; i < iLen; i++) {
			if (typeof oTable == 'undefined' || nTable == ColVis.aInstances[i].s.dt.nTable) {
				ColVis.aInstances[i].fnRebuild();
			}
		}
	};





	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	* Static object propterties
	* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

	/**
	* Collection of all ColVis instances
	*  @property ColVis.aInstances
	*  @static
	*  @type     Array
	*  @default  []
	*/
	ColVis.aInstances = [];





	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	* Constants
	* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

	/**
	* Name of this class
	*  @constant CLASS
	*  @type     String
	*  @default  ColVis
	*/
	ColVis.prototype.CLASS = "ColVis";


	/**
	* ColVis version
	*  @constant  VERSION
	*  @type      String
	*  @default   1.0.4.dev
	*/
	ColVis.VERSION = "1.0.4";
	ColVis.prototype.VERSION = ColVis.VERSION;





	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
	* Initialisation
	* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

	/*
	* Register a new feature with DataTables
	*/
	if (typeof $.fn.dataTable == "function" &&
	  typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
	  $.fn.dataTableExt.fnVersionCheck('1.7.0')) {
		$.fn.dataTableExt.aoFeatures.push({
			"fnInit": function (oDTSettings) {
				var init = (typeof oDTSettings.oInit.oColVis == 'undefined') ?
				{} : oDTSettings.oInit.oColVis;
				var oColvis = new ColVis(oDTSettings, init);
				return oColvis.dom.wrapper;
			},
			"cFeature": "C",
			"sFeature": "ColVis"
		});
	}
	else {
		alert("Warning: ColVis requires DataTables 1.7 or greater - www.datatables.net/download");
	}

})(jQuery);

jQuery.fn.dataTableExt.oApi.fnSetFilteringDelay = function ( oSettings, iDelay ) {
	/*
	 * Inputs:      object:oSettings - dataTables settings object - automatically given
	 *              integer:iDelay - delay in milliseconds
	 * Usage:       $('#example').dataTable().fnSetFilteringDelay(250);
	 * Author:      Zygimantas Berziunas (www.zygimantas.com) and Allan Jardine
	 * License:     GPL v2 or BSD 3 point style
	 * Contact:     zygimantas.berziunas /AT\ hotmail.com
	 */
	var
		_that = this,
		iDelay = (typeof iDelay == 'undefined') ? 250 : iDelay;
	
	this.each( function ( i ) {
		$.fn.dataTableExt.iApiIndex = i;
		var
			$this = this, 
			oTimerId = null, 
			sPreviousSearch = null,
			anControl = $( 'input', _that.fnSettings().aanFeatures.f );
		
			anControl.unbind( 'keyup' ).bind( 'keyup', function() {
			var $$this = $this;

			if (sPreviousSearch === null || sPreviousSearch != anControl.val()) {
				window.clearTimeout(oTimerId);
				sPreviousSearch = anControl.val();	
				oTimerId = window.setTimeout(function() {
					$.fn.dataTableExt.iApiIndex = i;
					_that.fnFilter( anControl.val() );
				}, iDelay);
			}
		});
		
		return this;
	} );
	return this;
}

/* Example call */
$(document).ready(function() {
	$('.dataTable').dataTable().fnSetFilteringDelay();
} );
