/*
 * Build No: 15102 
 */
/*
 * jQuery Cycle Plugin (with Transition Definitions)
 * Examples and documentation at: http://jquery.malsup.com/cycle/
 * Copyright (c) 2007-2010 M. Alsup
 * Version: 2.86 (05-APR-2010)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 * Requires: jQuery v1.2.6 or later
 */
(function($){var ver="2.86";if($.support==undefined){$.support={opacity:!($.browser.msie)};}function debug(s){if($.fn.cycle.debug){log(s);}}function log(){if(window.console&&window.console.log){window.console.log("[cycle] "+Array.prototype.join.call(arguments," "));}}$.fn.cycle=function(options,arg2){var o={s:this.selector,c:this.context};if(this.length===0&&options!="stop"){if(!$.isReady&&o.s){log("DOM not ready, queuing slideshow");$(function(){$(o.s,o.c).cycle(options,arg2);});return this;}log("terminating; zero elements found by selector"+($.isReady?"":" (DOM not ready)"));return this;}return this.each(function(){var opts=handleArguments(this,options,arg2);if(opts===false){return;}opts.updateActivePagerLink=opts.updateActivePagerLink||$.fn.cycle.updateActivePagerLink;if(this.cycleTimeout){clearTimeout(this.cycleTimeout);}this.cycleTimeout=this.cyclePause=0;var $cont=$(this);var $slides=opts.slideExpr?$(opts.slideExpr,this):$cont.children();var els=$slides.get();if(els.length<2){log("terminating; too few slides: "+els.length);return;}var opts2=buildOptions($cont,$slides,els,opts,o);if(opts2===false){return;}var startTime=opts2.continuous?10:getTimeout(opts2.currSlide,opts2.nextSlide,opts2,!opts2.rev);if(startTime){startTime+=(opts2.delay||0);if(startTime<10){startTime=10;}debug("first timeout: "+startTime);this.cycleTimeout=setTimeout(function(){go(els,opts2,0,!opts2.rev);},startTime);}});};function handleArguments(cont,options,arg2){if(cont.cycleStop==undefined){cont.cycleStop=0;}if(options===undefined||options===null){options={};}if(options.constructor==String){switch(options){case"destroy":case"stop":var opts=$(cont).data("cycle.opts");if(!opts){return false;}cont.cycleStop++;if(cont.cycleTimeout){clearTimeout(cont.cycleTimeout);}cont.cycleTimeout=0;$(cont).removeData("cycle.opts");if(options=="destroy"){destroy(opts);}return false;case"toggle":cont.cyclePause=(cont.cyclePause===1)?0:1;checkInstantResume(cont.cyclePause,arg2,cont);return false;case"pause":cont.cyclePause=1;return false;case"resume":cont.cyclePause=0;checkInstantResume(false,arg2,cont);return false;case"prev":case"next":var opts=$(cont).data("cycle.opts");if(!opts){log('options not found, "prev/next" ignored');return false;}$.fn.cycle[options](opts);return false;default:options={fx:options};}return options;}else{if(options.constructor==Number){var num=options;options=$(cont).data("cycle.opts");if(!options){log("options not found, can not advance slide");return false;}if(num<0||num>=options.elements.length){log("invalid slide index: "+num);return false;}options.nextSlide=num;if(cont.cycleTimeout){clearTimeout(cont.cycleTimeout);cont.cycleTimeout=0;}if(typeof arg2=="string"){options.oneTimeFx=arg2;}go(options.elements,options,1,num>=options.currSlide);return false;}}return options;function checkInstantResume(isPaused,arg2,cont){if(!isPaused&&arg2===true){var options=$(cont).data("cycle.opts");if(!options){log("options not found, can not resume");return false;}if(cont.cycleTimeout){clearTimeout(cont.cycleTimeout);cont.cycleTimeout=0;}go(options.elements,options,1,1);}}}function removeFilter(el,opts){if(!$.support.opacity&&opts.cleartype&&el.style.filter){try{el.style.removeAttribute("filter");}catch(smother){}}}function destroy(opts){if(opts.next){$(opts.next).unbind(opts.prevNextEvent);}if(opts.prev){$(opts.prev).unbind(opts.prevNextEvent);}if(opts.pager||opts.pagerAnchorBuilder){$.each(opts.pagerAnchors||[],function(){this.unbind().remove();});}opts.pagerAnchors=null;if(opts.destroy){opts.destroy(opts);}}function buildOptions($cont,$slides,els,options,o){var opts=$.extend({},$.fn.cycle.defaults,options||{},$.metadata?$cont.metadata():$.meta?$cont.data():{});if(opts.autostop){opts.countdown=opts.autostopCount||els.length;}var cont=$cont[0];$cont.data("cycle.opts",opts);opts.$cont=$cont;opts.stopCount=cont.cycleStop;opts.elements=els;opts.before=opts.before?[opts.before]:[];opts.after=opts.after?[opts.after]:[];opts.after.unshift(function(){opts.busy=0;});if(!$.support.opacity&&opts.cleartype){opts.after.push(function(){removeFilter(this,opts);});}if(opts.continuous){opts.after.push(function(){go(els,opts,0,!opts.rev);});}saveOriginalOpts(opts);if(!$.support.opacity&&opts.cleartype&&!opts.cleartypeNoBg){clearTypeFix($slides);}if($cont.css("position")=="static"){$cont.css("position","relative");}if(opts.width){$cont.width(opts.width);}if(opts.height&&opts.height!="auto"){$cont.height(opts.height);}if(opts.startingSlide){opts.startingSlide=parseInt(opts.startingSlide);}if(opts.random){opts.randomMap=[];for(var i=0;i<els.length;i++){opts.randomMap.push(i);}opts.randomMap.sort(function(a,b){return Math.random()-0.5;});opts.randomIndex=1;opts.startingSlide=opts.randomMap[1];}else{if(opts.startingSlide>=els.length){opts.startingSlide=0;}}opts.currSlide=opts.startingSlide||0;var first=opts.startingSlide;$slides.css({position:"absolute",top:0,left:0}).hide().each(function(i){var z=first?i>=first?els.length-(i-first):first-i:els.length-i;$(this).css("z-index",z);});$(els[first]).css("opacity",1).show();removeFilter(els[first],opts);if(opts.fit&&opts.width){$slides.width(opts.width);}if(opts.fit&&opts.height&&opts.height!="auto"){$slides.height(opts.height);}var reshape=opts.containerResize&&!$cont.innerHeight();if(reshape){var maxw=0,maxh=0;for(var j=0;j<els.length;j++){var $e=$(els[j]),e=$e[0],w=$e.outerWidth(),h=$e.outerHeight();if(!w){w=e.offsetWidth||e.width||$e.attr("width");}if(!h){h=e.offsetHeight||e.height||$e.attr("height");}maxw=w>maxw?w:maxw;maxh=h>maxh?h:maxh;}if(maxw>0&&maxh>0){$cont.css({width:maxw+"px",height:maxh+"px"});}}if(opts.pause){$cont.hover(function(){this.cyclePause++;},function(){this.cyclePause--;});}if(supportMultiTransitions(opts)===false){return false;}var requeue=false;options.requeueAttempts=options.requeueAttempts||0;$slides.each(function(){var $el=$(this);this.cycleH=(opts.fit&&opts.height)?opts.height:($el.height()||this.offsetHeight||this.height||$el.attr("height")||0);this.cycleW=(opts.fit&&opts.width)?opts.width:($el.width()||this.offsetWidth||this.width||$el.attr("width")||0);if($el.is("img")){var loadingIE=($.browser.msie&&this.cycleW==28&&this.cycleH==30&&!this.complete);var loadingFF=($.browser.mozilla&&this.cycleW==34&&this.cycleH==19&&!this.complete);var loadingOp=($.browser.opera&&((this.cycleW==42&&this.cycleH==19)||(this.cycleW==37&&this.cycleH==17))&&!this.complete);var loadingOther=(this.cycleH==0&&this.cycleW==0&&!this.complete);if(loadingIE||loadingFF||loadingOp||loadingOther){if(o.s&&opts.requeueOnImageNotLoaded&&++options.requeueAttempts<100){log(options.requeueAttempts," - img slide not loaded, requeuing slideshow: ",this.src,this.cycleW,this.cycleH);setTimeout(function(){$(o.s,o.c).cycle(options);},opts.requeueTimeout);requeue=true;return false;}else{log("could not determine size of image: "+this.src,this.cycleW,this.cycleH);}}}return true;});if(requeue){return false;}opts.cssBefore=opts.cssBefore||{};opts.animIn=opts.animIn||{};opts.animOut=opts.animOut||{};$slides.not(":eq("+first+")").css(opts.cssBefore);if(opts.cssFirst){$($slides[first]).css(opts.cssFirst);}if(opts.timeout){opts.timeout=parseInt(opts.timeout);if(opts.speed.constructor==String){opts.speed=$.fx.speeds[opts.speed]||parseInt(opts.speed);}if(!opts.sync){opts.speed=opts.speed/2;}var buffer=opts.fx=="shuffle"?500:250;while((opts.timeout-opts.speed)<buffer){opts.timeout+=opts.speed;}}if(opts.easing){opts.easeIn=opts.easeOut=opts.easing;}if(!opts.speedIn){opts.speedIn=opts.speed;}if(!opts.speedOut){opts.speedOut=opts.speed;}opts.slideCount=els.length;opts.currSlide=opts.lastSlide=first;if(opts.random){if(++opts.randomIndex==els.length){opts.randomIndex=0;}opts.nextSlide=opts.randomMap[opts.randomIndex];}else{opts.nextSlide=opts.startingSlide>=(els.length-1)?0:opts.startingSlide+1;}if(!opts.multiFx){var init=$.fn.cycle.transitions[opts.fx];if($.isFunction(init)){init($cont,$slides,opts);}else{if(opts.fx!="custom"&&!opts.multiFx){log("unknown transition: "+opts.fx,"; slideshow terminating");return false;}}}var e0=$slides[first];if(opts.before.length){opts.before[0].apply(e0,[e0,e0,opts,true]);}if(opts.after.length>1){opts.after[1].apply(e0,[e0,e0,opts,true]);}if(opts.next){$(opts.next).bind(opts.prevNextEvent,function(){return advance(opts,opts.rev?-1:1);});}if(opts.prev){$(opts.prev).bind(opts.prevNextEvent,function(){return advance(opts,opts.rev?1:-1);});}if(opts.pager||opts.pagerAnchorBuilder){buildPager(els,opts);}exposeAddSlide(opts,els);return opts;}function saveOriginalOpts(opts){opts.original={before:[],after:[]};opts.original.cssBefore=$.extend({},opts.cssBefore);opts.original.cssAfter=$.extend({},opts.cssAfter);opts.original.animIn=$.extend({},opts.animIn);opts.original.animOut=$.extend({},opts.animOut);$.each(opts.before,function(){opts.original.before.push(this);});$.each(opts.after,function(){opts.original.after.push(this);});}function supportMultiTransitions(opts){var i,tx,txs=$.fn.cycle.transitions;if(opts.fx.indexOf(",")>0){opts.multiFx=true;opts.fxs=opts.fx.replace(/\s*/g,"").split(",");for(i=0;i<opts.fxs.length;i++){var fx=opts.fxs[i];tx=txs[fx];if(!tx||!txs.hasOwnProperty(fx)||!$.isFunction(tx)){log("discarding unknown transition: ",fx);opts.fxs.splice(i,1);i--;}}if(!opts.fxs.length){log("No valid transitions named; slideshow terminating.");return false;}}else{if(opts.fx=="all"){opts.multiFx=true;opts.fxs=[];for(p in txs){tx=txs[p];if(txs.hasOwnProperty(p)&&$.isFunction(tx)){opts.fxs.push(p);}}}}if(opts.multiFx&&opts.randomizeEffects){var r1=Math.floor(Math.random()*20)+30;for(i=0;i<r1;i++){var r2=Math.floor(Math.random()*opts.fxs.length);opts.fxs.push(opts.fxs.splice(r2,1)[0]);}debug("randomized fx sequence: ",opts.fxs);}return true;}function exposeAddSlide(opts,els){opts.addSlide=function(newSlide,prepend){var $s=$(newSlide),s=$s[0];if(!opts.autostopCount){opts.countdown++;}els[prepend?"unshift":"push"](s);if(opts.els){opts.els[prepend?"unshift":"push"](s);}opts.slideCount=els.length;$s.css("position","absolute");$s[prepend?"prependTo":"appendTo"](opts.$cont);if(prepend){opts.currSlide++;opts.nextSlide++;}if(!$.support.opacity&&opts.cleartype&&!opts.cleartypeNoBg){clearTypeFix($s);}if(opts.fit&&opts.width){$s.width(opts.width);}if(opts.fit&&opts.height&&opts.height!="auto"){$slides.height(opts.height);}s.cycleH=(opts.fit&&opts.height)?opts.height:$s.height();s.cycleW=(opts.fit&&opts.width)?opts.width:$s.width();$s.css(opts.cssBefore);if(opts.pager||opts.pagerAnchorBuilder){$.fn.cycle.createPagerAnchor(els.length-1,s,$(opts.pager),els,opts);}if($.isFunction(opts.onAddSlide)){opts.onAddSlide($s);}else{$s.hide();}};}$.fn.cycle.resetState=function(opts,fx){fx=fx||opts.fx;opts.before=[];opts.after=[];opts.cssBefore=$.extend({},opts.original.cssBefore);opts.cssAfter=$.extend({},opts.original.cssAfter);opts.animIn=$.extend({},opts.original.animIn);opts.animOut=$.extend({},opts.original.animOut);opts.fxFn=null;$.each(opts.original.before,function(){opts.before.push(this);});$.each(opts.original.after,function(){opts.after.push(this);});var init=$.fn.cycle.transitions[fx];if($.isFunction(init)){init(opts.$cont,$(opts.elements),opts);}};function go(els,opts,manual,fwd){if(manual&&opts.busy&&opts.manualTrump){debug("manualTrump in go(), stopping active transition");$(els).stop(true,true);opts.busy=false;}if(opts.busy){debug("transition active, ignoring new tx request");return;}var p=opts.$cont[0],curr=els[opts.currSlide],next=els[opts.nextSlide];if(p.cycleStop!=opts.stopCount||p.cycleTimeout===0&&!manual){return;}if(!manual&&!p.cyclePause&&((opts.autostop&&(--opts.countdown<=0))||(opts.nowrap&&!opts.random&&opts.nextSlide<opts.currSlide))){if(opts.end){opts.end(opts);}return;}var changed=false;if((manual||!p.cyclePause)&&(opts.nextSlide!=opts.currSlide)){changed=true;var fx=opts.fx;curr.cycleH=curr.cycleH||$(curr).height();curr.cycleW=curr.cycleW||$(curr).width();next.cycleH=next.cycleH||$(next).height();next.cycleW=next.cycleW||$(next).width();if(opts.multiFx){if(opts.lastFx==undefined||++opts.lastFx>=opts.fxs.length){opts.lastFx=0;}fx=opts.fxs[opts.lastFx];opts.currFx=fx;}if(opts.oneTimeFx){fx=opts.oneTimeFx;opts.oneTimeFx=null;}$.fn.cycle.resetState(opts,fx);if(opts.before.length){$.each(opts.before,function(i,o){if(p.cycleStop!=opts.stopCount){return;}o.apply(next,[curr,next,opts,fwd]);});}var after=function(){$.each(opts.after,function(i,o){if(p.cycleStop!=opts.stopCount){return;}o.apply(next,[curr,next,opts,fwd]);});};debug("tx firing; currSlide: "+opts.currSlide+"; nextSlide: "+opts.nextSlide);opts.busy=1;if(opts.fxFn){opts.fxFn(curr,next,opts,after,fwd,manual&&opts.fastOnEvent);}else{if($.isFunction($.fn.cycle[opts.fx])){$.fn.cycle[opts.fx](curr,next,opts,after,fwd,manual&&opts.fastOnEvent);}else{$.fn.cycle.custom(curr,next,opts,after,fwd,manual&&opts.fastOnEvent);}}}if(changed||opts.nextSlide==opts.currSlide){opts.lastSlide=opts.currSlide;if(opts.random){opts.currSlide=opts.nextSlide;if(++opts.randomIndex==els.length){opts.randomIndex=0;}opts.nextSlide=opts.randomMap[opts.randomIndex];if(opts.nextSlide==opts.currSlide){opts.nextSlide=(opts.currSlide==opts.slideCount-1)?0:opts.currSlide+1;}}else{var roll=(opts.nextSlide+1)==els.length;opts.nextSlide=roll?0:opts.nextSlide+1;opts.currSlide=roll?els.length-1:opts.nextSlide-1;}}if(changed&&opts.pager){opts.updateActivePagerLink(opts.pager,opts.currSlide,opts.activePagerClass);}var ms=0;if(opts.timeout&&!opts.continuous){ms=getTimeout(curr,next,opts,fwd);}else{if(opts.continuous&&p.cyclePause){ms=10;}}if(ms>0){p.cycleTimeout=setTimeout(function(){go(els,opts,0,!opts.rev);},ms);}}$.fn.cycle.updateActivePagerLink=function(pager,currSlide,clsName){$(pager).each(function(){$(this).children().removeClass(clsName).eq(currSlide).addClass(clsName);});};function getTimeout(curr,next,opts,fwd){if(opts.timeoutFn){var t=opts.timeoutFn(curr,next,opts,fwd);while((t-opts.speed)<250){t+=opts.speed;}debug("calculated timeout: "+t+"; speed: "+opts.speed);if(t!==false){return t;}}return opts.timeout;}$.fn.cycle.next=function(opts){advance(opts,opts.rev?-1:1);};$.fn.cycle.prev=function(opts){advance(opts,opts.rev?1:-1);};function advance(opts,val){var els=opts.elements;var p=opts.$cont[0],timeout=p.cycleTimeout;if(timeout){clearTimeout(timeout);p.cycleTimeout=0;}if(opts.random&&val<0){opts.randomIndex--;if(--opts.randomIndex==-2){opts.randomIndex=els.length-2;}else{if(opts.randomIndex==-1){opts.randomIndex=els.length-1;}}opts.nextSlide=opts.randomMap[opts.randomIndex];}else{if(opts.random){opts.nextSlide=opts.randomMap[opts.randomIndex];}else{opts.nextSlide=opts.currSlide+val;if(opts.nextSlide<0){if(opts.nowrap){return false;}opts.nextSlide=els.length-1;}else{if(opts.nextSlide>=els.length){if(opts.nowrap){return false;}opts.nextSlide=0;}}}}var cb=opts.onPrevNextEvent||opts.prevNextClick;if($.isFunction(cb)){cb(val>0,opts.nextSlide,els[opts.nextSlide]);}go(els,opts,1,val>=0);return false;}function buildPager(els,opts){var $p=$(opts.pager);$.each(els,function(i,o){$.fn.cycle.createPagerAnchor(i,o,$p,els,opts);});opts.updateActivePagerLink(opts.pager,opts.startingSlide,opts.activePagerClass);}$.fn.cycle.createPagerAnchor=function(i,el,$p,els,opts){var a;if($.isFunction(opts.pagerAnchorBuilder)){a=opts.pagerAnchorBuilder(i,el);debug("pagerAnchorBuilder("+i+", el) returned: "+a);}else{a='<a href="#">'+(i+1)+"</a>";}if(!a){return;}var $a=$(a);if($a.parents("body").length===0){var arr=[];if($p.length>1){$p.each(function(){var $clone=$a.clone(true);$(this).append($clone);arr.push($clone[0]);});$a=$(arr);}else{$a.appendTo($p);}}opts.pagerAnchors=opts.pagerAnchors||[];opts.pagerAnchors.push($a);$a.bind(opts.pagerEvent,function(e){e.preventDefault();opts.nextSlide=i;var p=opts.$cont[0],timeout=p.cycleTimeout;if(timeout){clearTimeout(timeout);p.cycleTimeout=0;}var cb=opts.onPagerEvent||opts.pagerClick;if($.isFunction(cb)){cb(opts.nextSlide,els[opts.nextSlide]);}go(els,opts,1,opts.currSlide<i);});if(!/^click/.test(opts.pagerEvent)&&!opts.allowPagerClickBubble){$a.bind("click.cycle",function(){return false;});}if(opts.pauseOnPagerHover){$a.hover(function(){opts.$cont[0].cyclePause++;},function(){opts.$cont[0].cyclePause--;});}};$.fn.cycle.hopsFromLast=function(opts,fwd){var hops,l=opts.lastSlide,c=opts.currSlide;if(fwd){hops=c>l?c-l:opts.slideCount-l;}else{hops=c<l?l-c:l+opts.slideCount-c;}return hops;};function clearTypeFix($slides){debug("applying clearType background-color hack");function hex(s){s=parseInt(s).toString(16);return s.length<2?"0"+s:s;}function getBg(e){for(;e&&e.nodeName.toLowerCase()!="html";e=e.parentNode){var v=$.css(e,"background-color");if(v.indexOf("rgb")>=0){var rgb=v.match(/\d+/g);return"#"+hex(rgb[0])+hex(rgb[1])+hex(rgb[2]);}if(v&&v!="transparent"){return v;}}return"#ffffff";}$slides.each(function(){$(this).css("background-color",getBg(this));});}$.fn.cycle.commonReset=function(curr,next,opts,w,h,rev){$(opts.elements).not(curr).hide();opts.cssBefore.opacity=1;opts.cssBefore.display="block";if(w!==false&&next.cycleW>0){opts.cssBefore.width=next.cycleW;}if(h!==false&&next.cycleH>0){opts.cssBefore.height=next.cycleH;}opts.cssAfter=opts.cssAfter||{};opts.cssAfter.display="none";$(curr).css("zIndex",opts.slideCount+(rev===true?1:0));$(next).css("zIndex",opts.slideCount+(rev===true?0:1));};$.fn.cycle.custom=function(curr,next,opts,cb,fwd,speedOverride){var $l=$(curr),$n=$(next);var speedIn=opts.speedIn,speedOut=opts.speedOut,easeIn=opts.easeIn,easeOut=opts.easeOut;$n.css(opts.cssBefore);if(speedOverride){if(typeof speedOverride=="number"){speedIn=speedOut=speedOverride;}else{speedIn=speedOut=1;}easeIn=easeOut=null;}var fn=function(){$n.animate(opts.animIn,speedIn,easeIn,cb);};$l.animate(opts.animOut,speedOut,easeOut,function(){if(opts.cssAfter){$l.css(opts.cssAfter);}if(!opts.sync){fn();}});if(opts.sync){fn();}};$.fn.cycle.transitions={fade:function($cont,$slides,opts){$slides.not(":eq("+opts.currSlide+")").css("opacity",0);opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts);opts.cssBefore.opacity=0;});opts.animIn={opacity:1};opts.animOut={opacity:0};opts.cssBefore={top:0,left:0};}};$.fn.cycle.ver=function(){return ver;};$.fn.cycle.defaults={fx:"fade",timeout:4000,timeoutFn:null,continuous:0,speed:1000,speedIn:null,speedOut:null,next:null,prev:null,onPrevNextEvent:null,prevNextEvent:"click.cycle",pager:null,onPagerEvent:null,pagerEvent:"click.cycle",allowPagerClickBubble:false,pagerAnchorBuilder:null,before:null,after:null,end:null,easing:null,easeIn:null,easeOut:null,shuffle:null,animIn:null,animOut:null,cssBefore:null,cssAfter:null,fxFn:null,height:"auto",startingSlide:0,sync:1,random:0,fit:0,containerResize:1,pause:0,pauseOnPagerHover:0,autostop:0,autostopCount:0,delay:0,slideExpr:null,cleartype:!$.support.opacity,cleartypeNoBg:false,nowrap:0,fastOnEvent:0,randomizeEffects:1,rev:0,manualTrump:true,requeueOnImageNotLoaded:true,requeueTimeout:250,activePagerClass:"activeSlide",updateActivePagerLink:null};})(jQuery);
/*
 * jQuery Cycle Plugin Transition Definitions
 * This script is a plugin for the jQuery Cycle Plugin
 * Examples and documentation at: http://malsup.com/jquery/cycle/
 * Copyright (c) 2007-2008 M. Alsup
 * Version:	 2.72
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 */
(function($){$.fn.cycle.transitions.none=function($cont,$slides,opts){opts.fxFn=function(curr,next,opts,after){$(next).show();$(curr).hide();after();};};$.fn.cycle.transitions.scrollUp=function($cont,$slides,opts){$cont.css("overflow","hidden");opts.before.push($.fn.cycle.commonReset);var h=$cont.height();opts.cssBefore={top:h,left:0};opts.cssFirst={top:0};opts.animIn={top:0};opts.animOut={top:-h};};$.fn.cycle.transitions.scrollDown=function($cont,$slides,opts){$cont.css("overflow","hidden");opts.before.push($.fn.cycle.commonReset);var h=$cont.height();opts.cssFirst={top:0};opts.cssBefore={top:-h,left:0};opts.animIn={top:0};opts.animOut={top:h};};$.fn.cycle.transitions.scrollLeft=function($cont,$slides,opts){$cont.css("overflow","hidden");opts.before.push($.fn.cycle.commonReset);var w=$cont.width();opts.cssFirst={left:0};opts.cssBefore={left:w,top:0};opts.animIn={left:0};opts.animOut={left:0-w};};$.fn.cycle.transitions.scrollRight=function($cont,$slides,opts){$cont.css("overflow","hidden");opts.before.push($.fn.cycle.commonReset);var w=$cont.width();opts.cssFirst={left:0};opts.cssBefore={left:-w,top:0};opts.animIn={left:0};opts.animOut={left:w};};$.fn.cycle.transitions.scrollHorz=function($cont,$slides,opts){$cont.css("overflow","hidden").width();opts.before.push(function(curr,next,opts,fwd){$.fn.cycle.commonReset(curr,next,opts);opts.cssBefore.left=fwd?(next.cycleW-1):(1-next.cycleW);opts.animOut.left=fwd?-curr.cycleW:curr.cycleW;});opts.cssFirst={left:0};opts.cssBefore={top:0};opts.animIn={left:0};opts.animOut={top:0};};$.fn.cycle.transitions.scrollVert=function($cont,$slides,opts){$cont.css("overflow","hidden");opts.before.push(function(curr,next,opts,fwd){$.fn.cycle.commonReset(curr,next,opts);opts.cssBefore.top=fwd?(1-next.cycleH):(next.cycleH-1);opts.animOut.top=fwd?curr.cycleH:-curr.cycleH;});opts.cssFirst={top:0};opts.cssBefore={left:0};opts.animIn={top:0};opts.animOut={left:0};};$.fn.cycle.transitions.slideX=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$(opts.elements).not(curr).hide();$.fn.cycle.commonReset(curr,next,opts,false,true);opts.animIn.width=next.cycleW;});opts.cssBefore={left:0,top:0,width:0};opts.animIn={width:"show"};opts.animOut={width:0};};$.fn.cycle.transitions.slideY=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$(opts.elements).not(curr).hide();$.fn.cycle.commonReset(curr,next,opts,true,false);opts.animIn.height=next.cycleH;});opts.cssBefore={left:0,top:0,height:0};opts.animIn={height:"show"};opts.animOut={height:0};};$.fn.cycle.transitions.shuffle=function($cont,$slides,opts){var i,w=$cont.css("overflow","visible").width();$slides.css({left:0,top:0});opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,true,true,true);});if(!opts.speedAdjusted){opts.speed=opts.speed/2;opts.speedAdjusted=true;}opts.random=0;opts.shuffle=opts.shuffle||{left:-w,top:15};opts.els=[];for(i=0;i<$slides.length;i++){opts.els.push($slides[i]);}for(i=0;i<opts.currSlide;i++){opts.els.push(opts.els.shift());}opts.fxFn=function(curr,next,opts,cb,fwd){var $el=fwd?$(curr):$(next);$(next).css(opts.cssBefore);var count=opts.slideCount;$el.animate(opts.shuffle,opts.speedIn,opts.easeIn,function(){var hops=$.fn.cycle.hopsFromLast(opts,fwd);for(var k=0;k<hops;k++){fwd?opts.els.push(opts.els.shift()):opts.els.unshift(opts.els.pop());}if(fwd){for(var i=0,len=opts.els.length;i<len;i++){$(opts.els[i]).css("z-index",len-i+count);}}else{var z=$(curr).css("z-index");$el.css("z-index",parseInt(z)+1+count);}$el.animate({left:0,top:0},opts.speedOut,opts.easeOut,function(){$(fwd?this:curr).hide();if(cb){cb();}});});};opts.cssBefore={display:"block",opacity:1,top:0,left:0};};$.fn.cycle.transitions.turnUp=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,true,false);opts.cssBefore.top=next.cycleH;opts.animIn.height=next.cycleH;});opts.cssFirst={top:0};opts.cssBefore={left:0,height:0};opts.animIn={top:0};opts.animOut={height:0};};$.fn.cycle.transitions.turnDown=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,true,false);opts.animIn.height=next.cycleH;opts.animOut.top=curr.cycleH;});opts.cssFirst={top:0};opts.cssBefore={left:0,top:0,height:0};opts.animOut={height:0};};$.fn.cycle.transitions.turnLeft=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,false,true);opts.cssBefore.left=next.cycleW;opts.animIn.width=next.cycleW;});opts.cssBefore={top:0,width:0};opts.animIn={left:0};opts.animOut={width:0};};$.fn.cycle.transitions.turnRight=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,false,true);opts.animIn.width=next.cycleW;opts.animOut.left=curr.cycleW;});opts.cssBefore={top:0,left:0,width:0};opts.animIn={left:0};opts.animOut={width:0};};$.fn.cycle.transitions.zoom=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,false,false,true);opts.cssBefore.top=next.cycleH/2;opts.cssBefore.left=next.cycleW/2;opts.animIn={top:0,left:0,width:next.cycleW,height:next.cycleH};opts.animOut={width:0,height:0,top:curr.cycleH/2,left:curr.cycleW/2};});opts.cssFirst={top:0,left:0};opts.cssBefore={width:0,height:0};};$.fn.cycle.transitions.fadeZoom=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,false,false);opts.cssBefore.left=next.cycleW/2;opts.cssBefore.top=next.cycleH/2;opts.animIn={top:0,left:0,width:next.cycleW,height:next.cycleH};});opts.cssBefore={width:0,height:0};opts.animOut={opacity:0};};$.fn.cycle.transitions.blindX=function($cont,$slides,opts){var w=$cont.css("overflow","hidden").width();opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts);opts.animIn.width=next.cycleW;opts.animOut.left=curr.cycleW;});opts.cssBefore={left:w,top:0};opts.animIn={left:0};opts.animOut={left:w};};$.fn.cycle.transitions.blindY=function($cont,$slides,opts){var h=$cont.css("overflow","hidden").height();opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts);opts.animIn.height=next.cycleH;opts.animOut.top=curr.cycleH;});opts.cssBefore={top:h,left:0};opts.animIn={top:0};opts.animOut={top:h};};$.fn.cycle.transitions.blindZ=function($cont,$slides,opts){var h=$cont.css("overflow","hidden").height();var w=$cont.width();opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts);opts.animIn.height=next.cycleH;opts.animOut.top=curr.cycleH;});opts.cssBefore={top:h,left:w};opts.animIn={top:0,left:0};opts.animOut={top:h,left:w};};$.fn.cycle.transitions.growX=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,false,true);opts.cssBefore.left=this.cycleW/2;opts.animIn={left:0,width:this.cycleW};opts.animOut={left:0};});opts.cssBefore={width:0,top:0};};$.fn.cycle.transitions.growY=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,true,false);opts.cssBefore.top=this.cycleH/2;opts.animIn={top:0,height:this.cycleH};opts.animOut={top:0};});opts.cssBefore={height:0,left:0};};$.fn.cycle.transitions.curtainX=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,false,true,true);opts.cssBefore.left=next.cycleW/2;opts.animIn={left:0,width:this.cycleW};opts.animOut={left:curr.cycleW/2,width:0};});opts.cssBefore={top:0,width:0};};$.fn.cycle.transitions.curtainY=function($cont,$slides,opts){opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,true,false,true);opts.cssBefore.top=next.cycleH/2;opts.animIn={top:0,height:next.cycleH};opts.animOut={top:curr.cycleH/2,height:0};});opts.cssBefore={left:0,height:0};};$.fn.cycle.transitions.cover=function($cont,$slides,opts){var d=opts.direction||"left";var w=$cont.css("overflow","hidden").width();var h=$cont.height();opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts);if(d=="right"){opts.cssBefore.left=-w;}else{if(d=="up"){opts.cssBefore.top=h;}else{if(d=="down"){opts.cssBefore.top=-h;}else{opts.cssBefore.left=w;}}}});opts.animIn={left:0,top:0};opts.animOut={opacity:1};opts.cssBefore={top:0,left:0};};$.fn.cycle.transitions.uncover=function($cont,$slides,opts){var d=opts.direction||"left";var w=$cont.css("overflow","hidden").width();var h=$cont.height();opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,true,true,true);if(d=="right"){opts.animOut.left=w;}else{if(d=="up"){opts.animOut.top=-h;}else{if(d=="down"){opts.animOut.top=h;}else{opts.animOut.left=-w;}}}});opts.animIn={left:0,top:0};opts.animOut={opacity:1};opts.cssBefore={top:0,left:0};};$.fn.cycle.transitions.toss=function($cont,$slides,opts){var w=$cont.css("overflow","visible").width();var h=$cont.height();opts.before.push(function(curr,next,opts){$.fn.cycle.commonReset(curr,next,opts,true,true,true);if(!opts.animOut.left&&!opts.animOut.top){opts.animOut={left:w*2,top:-h/2,opacity:0};}else{opts.animOut.opacity=0;}});opts.cssBefore={left:0,top:0};opts.animIn={left:0};};$.fn.cycle.transitions.wipe=function($cont,$slides,opts){var w=$cont.css("overflow","hidden").width();var h=$cont.height();opts.cssBefore=opts.cssBefore||{};var clip;if(opts.clip){if(/l2r/.test(opts.clip)){clip="rect(0px 0px "+h+"px 0px)";}else{if(/r2l/.test(opts.clip)){clip="rect(0px "+w+"px "+h+"px "+w+"px)";}else{if(/t2b/.test(opts.clip)){clip="rect(0px "+w+"px 0px 0px)";}else{if(/b2t/.test(opts.clip)){clip="rect("+h+"px "+w+"px "+h+"px 0px)";}else{if(/zoom/.test(opts.clip)){var top=parseInt(h/2);var left=parseInt(w/2);clip="rect("+top+"px "+left+"px "+top+"px "+left+"px)";}}}}}}opts.cssBefore.clip=opts.cssBefore.clip||clip||"rect(0px 0px 0px 0px)";var d=opts.cssBefore.clip.match(/(\d+)/g);var t=parseInt(d[0]),r=parseInt(d[1]),b=parseInt(d[2]),l=parseInt(d[3]);opts.before.push(function(curr,next,opts){if(curr==next){return;}var $curr=$(curr),$next=$(next);$.fn.cycle.commonReset(curr,next,opts,true,true,false);opts.cssAfter.display="block";var step=1,count=parseInt((opts.speedIn/13))-1;(function f(){var tt=t?t-parseInt(step*(t/count)):0;var ll=l?l-parseInt(step*(l/count)):0;var bb=b<h?b+parseInt(step*((h-b)/count||1)):h;var rr=r<w?r+parseInt(step*((w-r)/count||1)):w;$next.css({clip:"rect("+tt+"px "+rr+"px "+bb+"px "+ll+"px)"});(step++<=count)?setTimeout(f,13):$curr.css("display","none");})();});opts.cssBefore={display:"block",opacity:1,top:0,left:0};opts.animIn={left:0};opts.animOut={left:0};};})(jQuery);

/**
 * Observant Visualisation Javascript API
 *
 * @author Robert Tooker
 * @author Prashant Raju
 */

/*
 * obsviz is the 'namespace' for the Observant Visualisation Javascript API
 */
var obsviz = (function() {
    /**
     * BASE_URL
     * The base URL for JSONP calls
     */
    var BASE_URL = 'https://see.observant.net.au/ringo';

    /**
     * EXPORT_URL
     * The URL to post data against
     */
    var EXPORT_URL = 'https://see.observant.net.au/jsapi/exporter';

    /**
     * DEBUG
     * Outputs debugging information to console if running firebug
     */
    var DEBUG = 'true';

    /**
     * DEBUG_LEVEL
     * The debugging level (1-5) where 1 = ALL LOGGING
     *                                 2 = FINE GRAINED
     *                                 3 = DEFAULT
     *                                 4 = ERROR INFORMATION ONLY
     *                                 5 = FATAL ERRORS ONLY
     */
    var DEBUG_LEVEL = '3';

    /**
     * SYSPLANS
     * "Associative Array" of system plan objects, where the index is the plan ID
     */
    var SYSPLANS = {};

    /**
     * SYSINSTANCES
     * "Associative Array" of system instance objects, where the index is the plan ID
     */
    var SYSINSTANCES = {};

    /**
     * SYSDATA
     * "Associative Array" of system data, where the index is the data URL
     */
    var SYSDATA = {};

    /**
     * GALLERIES
     * "Associative Array" of galleries, where the index is the container div
     */
    var GALLERIES = {};
    
    /**
     * THEME
     *  - Base : always use 
     *  - Light
     *  - Dark
     */
    var THEME = {
    	base: {
    		chartOptions: {
    			chart: {
    				plotBackgroundColor: null,
					plotShadow: false,
					plotBorderWidth: 0,
					margin: [80, 30, 60, 70]
    			},
				colors: ["#ff5110", "#7798BF", "#55BF3B", "#DF5353", "#aaeeee", "#ff0066", "#eeaaee", 
		"#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
				title: {
					style: { 
						font: '16px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif',
						margin: '10px 0 0 15px',
						textAlign: 'left'
					}
				},
				subtitle: {
					style: { 
						font: '12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif',
						margin: '5px 0 0 15px',
						textAlign: 'left'
					}
				},
				xAxis: {
					lineColor: '#777',
					tickColor: '#777',
					labels: {
						style: {
							color: '#777'
						}
					},
					title: {
						style: {
							font: 'bold 12px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
						}				
					}
				},
				yAxis: {
					alternateGridColor: null,
					minorTickInterval: null,
					lineWidth: 1,
					lineColor: '#777',
					tickWidth: 1,
					tickColor: '#777',
					labels: {
						style: {
							color: '#777'
						}
					},
					title: {
						style: {
							font: 'normal 11px Lucida Grande, Lucida Sans Unicode, Verdana, Arial, Helvetica, sans-serif'
						}
					}
				},
				legend: {
					enabled: false,
					itemStyle: {
						color: '#CCC'
					},
					itemHoverStyle: {
						color: '#FFF'
					},
					itemHiddenStyle: {
						color: '#333'
					}
				},
				credits: {
					style: {
						right: '10px',
						bottom: '10px'
					}
				},
				labels: {
					style: {
						color: '#CCC'
					}
				},
				tooltip: {
					backgroundColor: {
						linearGradient: [0, 0, 0, 50],
						stops: [
							[0, 'rgba(96, 96, 96, .8)'],
							[1, 'rgba(16, 16, 16, .8)']
						]
					},
					borderWidth: 0,
					style: {
						color: '#FFF'
					}
				},
				plotOptions: {
					line: {
						lineWidth: 1,
						marker: {
							lineWidth: 1,
							lineColor: '#ff5110',
							radius: 1,
							states: {
				               hover: {
				                  enabled: true,
				                  radius: 4
				               }
				            }
						}
					},
					column: {
						lineWidth: 4,
						borderWidth: 0
					},
					area: {
			         lineWidth: 2,
			         marker: {
			            enabled: false,
			            states: {
			               hover: {
			                  enabled: true,
			                  radius: 4
			               }
			            }
			         },
			         shadow: false,
			         states: {
			            hover: {
			               lineWidth: 1                  
			            }
			         }
			      }
				}
    		}
    	},
    	light: {
    		chartOptions: {
	    		chart: {
					backgroundColor: {
						linearGradient: [0, 0, 0, 100],
						stops: [
							[0, 'rgb(255, 255, 255)'],
							[1, 'rgb(238, 238, 238)']
						]
					},
					borderWidth: 1,
					borderColor: '#999',
					borderRadius: 5
				},
				title: {
					style: { 
						color: '#000'
					}
				},
				subtitle: {
					style: { 
						color: '#666'
					}
				},
				xAxis: {
					title: {
						style: {
							color: '#000'
						}				
					}
				},
				yAxis: {
					gridLineColor: 'rgba(0, 0, 0, .1)',
					title: {
						style: {
							color: '#000'
						}
					}
				},
				plotOptions: {
					area: {
			         fillColor: {
			            linearGradient: [0, 0, 0, 200],
			            stops: [
			               [0, 'rgba(255,81,16,1)'],
			               [1, 'rgba(255,255,255,0)']
			            ]
			         },
			         lineColor: '#ff5110'
			      }
				}
	        }
    	},
    	dark: {
    		chartOptions: {
    			chart: {
	                backgroundColor: {
						linearGradient: [0, 0, 0, 100],
						stops: [
							[0, 'rgb(46, 51, 56)'],
							[1, 'rgb(18, 21, 24)']
						]
					},
					borderWidth: 0,
					borderRadius: 5
	            },
	            title: {
					style: { 
						color: '#FFF'					
					}
				},
				subtitle: {
					style: { 
						color: '#999'
					}
				},
				xAxis: {
					title: {
						style: {
							color: '#fff'
						}				
					}
				},
				yAxis: {
					gridLineColor: 'rgba(255, 255, 255, .1)',
					title: {
						style: {
							color: '#fff'
						}				
					}
				},
				plotOptions: {
					area: {
			         fillColor: {
			            linearGradient: [0, 0, 0, 300],
			            stops: [
			               [0, 'rgba(255,81,16,1)'],
			               [1, 'rgba(0,0,0,0)']
			            ]
			         },
			         lineColor: '#fff'
			      }
				}
			}
    	}
    };
    
    /**
     * Creates the photo after the system plan has been obtained
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     * @param system the observant system
     */
    function photoCallBack(container, options, system) {
        // set default options and merge in user options
        var baseOptions = {
            defaultSize: false
        };
        var empty = {};
        var mergedOptions = {};
        if (options.photoOptions !== undefined) {
            mergedOptions = jQuery.extend(true, empty, baseOptions, options.photoOptions);
        } else {
            mergedOptions = jQuery.extend(true, empty, baseOptions);
        }

        // if a visualisation has been defined then search for it
        var activeVizReference = null;
        if(options.vizId){
            jQuery.each(system.sysPlan.vizContainer.vizReferences, function(i,vizReference){
                // check if the vizId has been set perform normal loop
                if(options.vizId == vizReference.vizId){
                    activeVizReference = vizReference;
                }
            });
        } else {
            // not defined, just get the first camera viz
            jQuery.each(system.sysPlan.vizContainer.vizReferences, function(i,vizReference){
                if(vizReference.name == "Camera" && !activeVizReference){
                    activeVizReference = vizReference;
                }
            });
        }
        
        if (!activeVizReference) {
            if (options.vizId) {
                error('Could not find camera visualisation with visualisation ID <em>' + options.vizId + '</em>.', container);
            } else {
                error('Could not find camera visualisation for that system plan.', container);
            }
        } else {
            // get the first field
            var fieldReference = activeVizReference.fieldReferences[0];

            // get the information specific to the vizId from the sysplan
            // loop through json for current data
            $.each(system.sysInstance.siteVizs, function(i, siteVizs){
                // find which matches the sysplan vizid
                if(siteVizs.vizId == activeVizReference.vizId) {
                    // find which matches the sysplan fieldid
                    $.each(siteVizs.vizFields, function(i, vizField){
                        if(vizField.fieldId == fieldReference.fieldId) {
                            // add in the photo if exists
                            if (!vizField.currentValue) {
                                error('No current photo found for <em>' + activeVizReference.vizId + '</em>.', container);
                            } else {
                                var src = BASE_URL + vizField.currentValue.currentValue + '?_overlay=true&media=jpeg';
                                if (options.resAuthKey) {
                                	src = src + '&resAuthKey='+options.resAuthKey;
                                }
                                // If the defaultSize is TRUE
                                if(mergedOptions.defaultSize) {
                                    $('#'+container).html('<img src="'+src+'" alt="'+ system.sysPlan.planName +' '+ activeVizReference.site +' ' + fieldReference.name + '" class="obsviz-photo" />');
                                } else {
                                    var imageWidth = $('#' + container).width() > 0 ? $('#' + container).width() : 480;
                                    var imageHeight = $('#' + container).height() > 0 ? $('#' + container).height() : Math.min(360, imageWidth*0.75);
                                    $('#'+container).html('<img src="'+src+'" alt="'+ system.sysPlan.planName +' '+ activeVizReference.site +' ' + fieldReference.name + '" width="' + imageWidth + '" height="' + imageHeight + '" class="obsviz-photo" />');
                                }
                            }
                       }
                    });
                }
            });
        }
    }

    /**
     * Creates the photo gallery after the system plan has been obtained
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     * @param sysInstance the observant system instance
     */
    function photoGalleryCallBack(container, options, sysInstance) {
        // photoGallery options
        var baseOptions = {
            fx: 'fade'
            , timeout: 4000
            , continuous: 0
            , speed: 1000
            , speedIn: null
            , speedOut: null
        };

        var empty = {};
        var mergedOptions = {};
        if (options.photoGalleryOptions !== undefined) {
            mergedOptions = jQuery.extend(true, empty, baseOptions, options.photoGalleryOptions);
        } else {
            mergedOptions = jQuery.extend(true, empty, baseOptions);
        }

        // control panel options
        var baseControlPanelOptions = {
            enabled: true
        };

        var mergedControlPanelOptions = {};
        if (options.controlPanelOptions !== undefined) {
            mergedControlPanelOptions = jQuery.extend(true, empty, baseControlPanelOptions, options.controlPanelOptions);
        } else {
            mergedControlPanelOptions = jQuery.extend(true, empty, baseControlPanelOptions);
        }

        // get the image information
        var imageProperties = getImageProperties(sysInstance, options.vizIds, options.resAuthKey);

        // clear container and add the slideshow class
        $('#'+container).html('');
        $('#'+container).addClass('obsviz-gallery-container');

        // add image container and slideshow class
        $('#'+container).append('<div class="obsviz-image-container slideshow"></div>');

        var imageContainer = $('#' + container + ' .obsviz-image-container');

        // set width and height
        var imageWidth = $('#' + container).width() > 0 ? $('#' + container).width() : 480;
        var imageHeight = $('#' + container).height() > 0 ? $('#' + container).height() : Math.min(360, imageWidth*0.75);
        
        // add the images (hidden)
        $.each(imageProperties, function(i, imageProperty) {
            var displayType = '';
            if (i == 1) {
                displayType = 'block';
            } else {
                displayType = 'none';
            }
            $(imageContainer).append('<img src="'+imageProperty.url+'" alt="'+imageProperty.vizId+'" width="'+imageWidth+'" height="'+imageHeight+'" + style="display: '+displayType+';"></img>');
        });

        // add controls if required
        if (mergedControlPanelOptions.enabled) {
            // add control container and hover event
            $('#'+container).append('<div class="obsviz-control-container"></div>');
            var controlContainer = '#'+container+' .obsviz-control-container';
            $('#'+container).hover(
                function() { $(controlContainer).fadeIn(); },
                function() { $(controlContainer).fadeOut(); }
            );

            // add controls
            $(controlContainer).append('<span><a href="" class="obsviz-prev">Prev</a></span>');
            $(controlContainer).append('<span><a href="" class="obsviz-next">Next</a></span>');
            $(controlContainer).append('<span><a href="" class="obsviz-pause">Pause</a></span>');
            $(controlContainer).append('<span><a href="" class="obsviz-play">Play</a></span>');

            // add control events
            $('#'+container + ' .obsviz-pause').click(function() { 
                $(imageContainer).cycle('pause'); return false;
            });
            $('#'+container + ' .obsviz-play').click(function() { 
                $(imageContainer).cycle('resume'); return false;
            });
            $('#'+container + ' .obsviz-next').click(function() {
                $(imageContainer).cycle('next'); return false;
            });
            $('#'+container + ' .obsviz-prev').click(function() {
                $(imageContainer).cycle('prev'); return false;
            });
        }


        // cycle images
        $(imageContainer).cycle(mergedOptions);

    }

    /**
     * Returns the image properties
     * @param sysInstance the observant system instance
     * @param vizIds optional list of viz IDs
     * @param resAuthKey optional authorisation key
     * @return mixed the image data
     */
    function getImageProperties(sysInstance, vizIds, resAuthKey) {
        var imageProperties = [];
        var imagePropertyIndex = 0;

        // run through viz IDs
        $.each(vizIds, function(i, vizId){
            // run through system instance to match the viz ID
            $.each(sysInstance.siteVizs, function(j, siteViz) {
                // match to vizId
                if (vizId == siteViz.vizId) {
                    // run through fields
                    $.each(siteViz.vizFields, function(k, vizField) {
                        // make sure have photo type
                        if(vizField.dataType == 'photo') {
                            // add to image property array
                            imageProperties[imagePropertyIndex] = {};
                            imageProperties[imagePropertyIndex].vizId = vizId;
                            imageProperties[imagePropertyIndex].dataType = vizField.dataType;
                            imageProperties[imagePropertyIndex].urlFragment = vizField.currentValue.currentValue;
                            imageProperties[imagePropertyIndex].url = BASE_URL + vizField.currentValue.currentValue + '?_overlay=true&media=jpeg';
                            if(resAuthKey) {
                            	imageProperties[imagePropertyIndex].url = imageProperties[imagePropertyIndex].url + '&resAuthKey=' + resAuthKey;
                            }
                            imageProperties[imagePropertyIndex].lastCommunicationTime = vizField.currentValue.lastCommunicationTime;
                            imageProperties[imagePropertyIndex].lastUpdateTime = vizField.currentValue.lastUpdateTime;
                            imagePropertyIndex++;
                        }
                    });
                }
            });
        });
        return imageProperties;
    }

    /**
     * Returns the name of the viz based on the vizId
     * @param vizId the viz ID
     * @param sysPlan the observant system plan
     * @return string the name of the viz
     */
    function findNameForViz(vizId, sysPlan) {
    	var name;
        $.each(sysPlan.vizContainer.vizReferences, function(i, vizRef){
        	if(vizRef.vizId == vizId) {
        		name = vizRef.name;
        	}
        });
    	return name;
    }
    /**
     * Returns the field reference that has a history spec for the nominated viz
     * @param viz the viz
     * @return the designated history field
     */
    function findHistoryFieldForViz(viz) {
    	var fld;
    	$.each(viz.fieldReferences, function(i,fldReference){
            if (fldReference.history !== undefined) {
            	fld = fldReference;
            }
        });
    	return fld;
    }

    /**
     * Creates the map after the system plan has been obtained
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     * @param sysInstance the observant system 
     */
    function mapCallBack(container, options, system) {
        // loop through system instance to create markers and map bounds
        var mapPoints = [];
        var mapBounds = new google.maps.LatLngBounds();
        var pointCounter = 0;

        $.each(system.sites, function(i, site){
            if (site.latitude && site.longitude) {
                mapPoints[pointCounter] = {};
                mapPoints[pointCounter].latitude = site.latitude;
                mapPoints[pointCounter].longitude = site.longitude;
                mapPoints[pointCounter].latlng = new google.maps.LatLng(site.latitude, site.longitude);
                mapPoints[pointCounter].icon = "http://maps.google.com/mapfiles/marker" + String.fromCharCode(pointCounter + 65) + ".png";
                mapPoints[pointCounter].name = site.name;
                mapBounds.extend(mapPoints[pointCounter].latlng);
                pointCounter++;
            }
        });

        // create the map using the marker boundaries to define zoom and centre
        var mapOptions = {
          center: mapBounds.getCenter(),
          mapTypeId: google.maps.MapTypeId.HYBRID
        };
        var map = new google.maps.Map(document.getElementById(container), mapOptions);
        if (mapPoints.length > 1) {
            map.fitBounds(mapBounds);
        } else {
            map.setZoom(10);
        }

        // add markers to map
        $.each(mapPoints, function(i, mapPoint) {
            var marker = new google.maps.Marker({
               position: mapPoint.latlng
               , map: map
               , title: mapPoint.name
               , icon: mapPoints[i].icon
            });
            if (options.mapOptions && (eval("typeof " + options.mapOptions.markerClickFn + " == 'function'")) ) {
                google.maps.event.addListener(
                    marker
                    , 'click'
                    , function() {
                        options.mapOptions.markerClickFn(mapPoint.latitude, mapPoint.longitude);
                    });
            }
        });
    }

    /**
     * Calls a call back funtion after the system plan and instance have been obtained
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     * @param sysInstance the observant system
     */
    function systemCallBack(container, options, system) {
        debug('systemCallBack', 1);
        options.callBackFn(system);
    }
    
    /**
     * Creates the chart, table or CSV after the system plan has been obtained
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     * @param sysPlan the observant system plan
     * @param renderCallBackFn the call back function to render the visualisation with the signature fn(container, plotData, options)
     */
    function dataVizCallBack(container, options, sysPlan, renderCallBackFn) {
        var undefinedVizIdError = new Error ("No visualisation specified in options.");
        try {
            var activeViz = {};
            var activeField = {};
            var dataUrl = '';

            // vizId required for charts and tables
            if (options.vizId === undefined) {
                throw undefinedVizIdError;
            }

            var notFoundVizIdError = new Error ("Could not find visualisation for vizId <em>" + options.vizId + "</em>.");

            // cycle through sysPlan to find the viz reference
            jQuery.each(sysPlan.vizContainer.vizReferences, function(i,vizReference){
            	// compare vizid first, if found, set ActiveViz
            	if(vizReference.vizId == options.vizId) {
                    jQuery.each(vizReference.fieldReferences, function(j,fldReference){
                        if(options.fieldId == fldReference.fieldId){
                            activeViz = vizReference;
                        	activeField=fldReference;
                        }
                    });
                }
            });

            // check if viz returned
            if (isEmpty(activeViz)) {
                throw notFoundVizIdError;
            }


            // create the url
            dataUrl = BASE_URL + '/system/' + options.orgId + '/' + options.planId + '/history/' + options.history + '/' + activeViz.vizId + '/' + activeField.fieldId + '?media=json';

            if (options.resAuthKey) {
                dataUrl = dataUrl + '&resAuthKey=' + options.resAuthKey;
            }


            // check if we already have this data
            if (SYSDATA[dataUrl] !== undefined) {
                var sysInstance = SYSDATA[dataUrl];
                var plotData = getPlotData(sysPlan, options, activeViz, activeField, sysInstance);
                renderCallBackFn(container, plotData, options);
            } else {
                // go get it
                debug(dataUrl, 3);

                // create the chart or table or download
                jQuery.ajax({
                    type: 'GET',
                    url : dataUrl,
                    success : function(sysInstance) {
                        debug(sysInstance, 3);
                        SYSDATA[dataUrl] = sysInstance;

                        var plotData = getPlotData(sysPlan, options, activeViz, activeField, sysInstance);
                        renderCallBackFn(container, plotData, options);
                    },
                    error: function(XMLHttpRequest, textStatus, errorThrown) {
                        error(errorThrown, container);
                        debug(XMLHttpRequest);
                        debug(textStatus);
                        debug(errorThrown);
                    }
                });

            }
            

        }
        catch(e) {
            error(e.message, container);
        }
    }

    /**
     * Creates the chart gallery after the system plan has been obtained
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     * @param sysPlan the observant system plan
     */
    function chartGalleryCallBack(container, options, sysPlan) {
        // clear the container
        $('#'+container).html('');
        GALLERIES[container].activeVizReferences = [];

        // get the visualisations allowed to use
        if(options.vizId){
            jQuery.each(sysPlan.vizContainer.vizReferences, function(i,vizReference){
                // check if the vizId has been set perform normal loop
                if(options.vizId == vizReference.vizId){
                	GALLERIES[container].activeVizReference = vizReference;
                    jQuery.each(vizReference.fieldReferences, function(j,fldReference){
                        if(options.fieldId == fldReference.fieldId){
                        	GALLERIES[container].activeFieldReference=fldReference;
                        }
                    });
                }
            });
        }
        
        jQuery.each(sysPlan.vizContainer.vizReferences, function(i,vizReference){
            jQuery.each(vizReference.fieldReferences, function(j,fldReference){
                if (fldReference.history !== undefined) {
                    GALLERIES[container].activeVizReferences.push(vizReference);
                    if(GALLERIES[container].activeVizReference == undefined) {
                    	GALLERIES[container].activeVizReference=vizReference;
                    	GALLERIES[container].activeFieldReference=fldReference;
                    }
            	}
            });
        });

        // choose the active viz and the history specification
        GALLERIES[container].activeHistorySpecification = null;                                  // set this later, can be different for each viz

        // these are the IDs for containers that need to be referenced later
        GALLERIES[container].chartContainer = container + 'obsviz-cg-body';
        GALLERIES[container].vizSelectContainer = container + 'obsviz-cg-select';
        GALLERIES[container].historyContainer = container + 'obsviz-cg-history';

        var headerHeight = 58;
        var chartContainerHeight = $('#' + container).height() - 20; // 20 for padding and borders
        
        // Append the wrapper
        $('#'+container).append('<div class="obsviz-cg-wrap"></div>');
        // Append the header to the wrapper
        $('#'+container+' .obsviz-cg-wrap').append('<div class="obsviz-cg-header"></div>');
        // Append the header left wrapper to the header
        $('#'+container+' .obsviz-cg-wrap .obsviz-cg-header').append('<div class="obsviz-cg-header-left"></div>');
        // Append the header title to the header left wrapper
        $('#'+container+' .obsviz-cg-wrap .obsviz-cg-header .obsviz-cg-header-left').append('<div class="obsviz-cg-header-title"><h3>'+GALLERIES[container].activeVizReference.site+':</h3><h4>'+sysPlan.planName+'</h4></div>');
/*         $('#'+container+' .obsviz-cg-wrap .obsviz-cg-header .obsviz-cg-header-left').append('<div class="obsviz-cg-header-title">'+sysPlan.planName+' : <em>'+GALLERIES[container].activeVizReference.site+'</em></div>'); */
        // Append the viz id select box to the header left wrappers
        $('#'+container+' .obsviz-cg-wrap .obsviz-cg-header .obsviz-cg-header-left').append('<div id="' + GALLERIES[container].vizSelectContainer + '" class="obsviz-cg-select"></div>');
        // Append the header right to the header
        $('#'+container+' .obsviz-cg-wrap .obsviz-cg-header').append('<div id="' + GALLERIES[container].historyContainer + '" class="obsviz-cg-history"></div>');
        // Append the clear div for floats
        $('#'+container+' .obsviz-cg-header').append('<div class="obsviz-cg-clear"></div>');
        // Append the body to the wrapper
        $('#'+container+' .obsviz-cg-wrap').append('<div id="' + GALLERIES[container].chartContainer + '" class="obsviz-cg-body" style="height:' + chartContainerHeight + 'px;"></div>');


        // add select list with change event to refresh chart and history links
        // deep copy the options for the chart
        var chartOptions = {};
        var chartLinkOptions = {};
        jQuery.extend(true, chartOptions, options);
        jQuery.extend(true, chartLinkOptions, options);
        if(!options.vizId){
            // create the select box
            var vizIdSelectHtml = '<select class="obsviz-cg-select-viz">';
            jQuery.each(GALLERIES[container].activeVizReferences, function(i,vizReference){
                jQuery.each(vizReference.fieldReferences, function(j,fldReference){
                    if (fldReference.history !== undefined) {
                		vizIdSelectHtml += '<option value="'+vizReference.name+':'+fldReference.fieldId+'">'+vizReference.name+' ('+fldReference.name+')</option>';
                	}
                });
            });
            vizIdSelectHtml += '</select>';
	        $(vizIdSelectHtml).appendTo('#' + GALLERIES[container].vizSelectContainer).change(function(e) {
	        	// get header for this chart
	        	var titleH3 = $(this).parent().parent().find(".obsviz-cg-header-title h3");
	            // set the activeViz values
	            jQuery.each(GALLERIES[container].activeVizReferences, function(i,vizReference){
	            	var ids = e.currentTarget.value.split(':');
	                if(vizReference.name == ids[0]) {
	                	titleH3.text(vizReference.site);
	                    GALLERIES[container].activeVizReference = vizReference;
	                    jQuery.each(vizReference.fieldReferences, function(j,fldReference){
	                        if(fldReference.fieldId == ids[1]) {
	                            GALLERIES[container].activeFieldReference = fldReference;
	                        }
	                    });
	                }
	            });
	
	            // reset history specification
	            GALLERIES[container].activeHistorySpecification = null;
	            // render the links
	            renderChartGalleryHistoryLinks(container, chartLinkOptions, sysPlan);
	            // render the chart
	            renderChartGalleryChart(container, chartOptions);
	            return false;
	        });
        }

        // render the chart and history links for first run
        // deep copy the options for each
        var chartGalleryLinkOptions = {};
        var chartGalleryChartOptions = {};
        jQuery.extend(true, chartGalleryLinkOptions, options);
        jQuery.extend(true, chartGalleryChartOptions, options);
        renderChartGalleryHistoryLinks(container, chartGalleryLinkOptions, sysPlan);
        renderChartGalleryChart(container, chartGalleryChartOptions);
    }

    /**
     * renderChartGalleryHistoryLinks renders the history links for the chart gallery
     * @param container the container div
     * @param options an object representing the options
     * @param sysPlan the system plan
     */
    function renderChartGalleryHistoryLinks(container, options, sysPlan) {
        // set history specifications
        GALLERIES[container].historySpecifications = GALLERIES[container].activeFieldReference.history.split(',');

        // set default
        if (!GALLERIES[container].activeHistorySpecification) {
            GALLERIES[container].activeHistorySpecification = GALLERIES[container].historySpecifications[0];
        }

        $('#' + GALLERIES[container].historyContainer).html('');

        // for the history specifications need click event
        // deep copy the options for the chart
        var chartOptions = {};
        jQuery.extend(true, chartOptions, options);
        jQuery.each(GALLERIES[container].historySpecifications, function(i, historySpecification) {
        	/* get first two chars of the history specs */
        	var historyText = historySpecification.substring(0,2);
        	
			/* if (i > 0) {
                $('#' + GALLERIES[container].historyContainer).append(' - ');
            } */

            if (historySpecification == GALLERIES[container].activeHistorySpecification) {
                $('#' + GALLERIES[container].historyContainer).append('<span class="obsviz-cg-history-text">' + historyText + '</span>');
            } else {
                // need to change this as click event is on the container
                $('<a href="#" class="obsviz-cg-history-text" title="' + historySpecification + '">' + historyText + '</a>').appendTo('#' + GALLERIES[container].historyContainer).click(function() {
                    // redo the links and the chart
                    GALLERIES[container].activeHistorySpecification = historySpecification;
                    renderChartGalleryHistoryLinks(container, chartOptions, sysPlan);
                    renderChartGalleryChart(container, chartOptions);
                    return false;
                });
            }
        });
        
        // add the download link if required
        var showLink = true;
        if (options.showLink !== undefined) {
            showLink = options.showLink;
        }
        
        if (showLink) {
            var spanId = container + '_download_link';
            // add export text so doesn't flash while loading the data
            
            //$('#' + GALLERIES[container].historyContainer).append('<span id="'+ spanId +'" class="obsviz-csv-export"><a>CSV Exportlalala &rarr;</a></span>');
             $('#' + GALLERIES[container].historyContainer).append('<div class="utils_menu"><a class="utils_link" title="Click to view more options"><span>menu</span></a><ul class="utils_ul hide"><li id="'+ spanId +'" class="obsviz-csv-export"></li></ul></div>');
			
			$("a.utils_link").toggle(
				function () {
			        $(this).parent().find("ul.utils_ul").fadeIn();
			        return false;
			    },
			    function () {
			        $(this).parent().find("ul.utils_ul").fadeOut();
			        return false;
			    }
			);
			
            var chartOptions = {};
            jQuery.extend(true, chartOptions, options);
            chartOptions.name = GALLERIES[container].activeVizReference.name;
            chartOptions.vizId = GALLERIES[container].activeVizReference.vizId;
            chartOptions.fieldId = GALLERIES[container].activeFieldReference.fieldId;
            chartOptions.history = GALLERIES[container].activeHistorySpecification;
            obsviz.DownloadLink(spanId, chartOptions);
        }
    }

    /**
     * renderChartGalleryChart renders the chart for the chart gallery using the Chart object
     * @param container the container div for the gallery
     * @param options an object representing the options, will be passed to the callback function
     * @return void
     */
    function renderChartGalleryChart(container, options) {
        // deep copy the options for the chart gallery chart
        var chartOptions = {};
        jQuery.extend(true, chartOptions, options);
        chartOptions.name = GALLERIES[container].activeVizReference.name;
        chartOptions.vizId = GALLERIES[container].activeVizReference.vizId;
        chartOptions.fieldId = GALLERIES[container].activeFieldReference.fieldId;
        chartOptions.history = GALLERIES[container].activeHistorySpecification;
        chartOptions.showLink = false;
        var chart = new obsviz.Chart(GALLERIES[container].chartContainer, chartOptions);
    }

    /**
     * getSystem get the system plan ANd system instance from observant and runs a callback function
     * @param container the container div, will be passed to the callback function
     * @param options an object representing the options, will be passed to the callback function
     * @param callbackFn the callback funtion which accepts the container, options, and system
     * @returns void
     */
    function getSystem(container, options, callbackFn) {
        debug('getSystem', 1);
        // system gets both plan and instance by chaining the asynchronous json requests
        getSystemPlan(container, options, getSystemCallBack, callbackFn);
    }

    /**
     * getSystemCallBack call back function for getSystem
     * @param container the container div, will be passed to the callback function
     * @param options an object representing the options, will be passed to the callback function
     * @param sysPlan the system plan
     * @param callbackFn the callback funtion which accepts the container, options, system instance and system plan
     * @returns void
     */
    function getSystemCallBack(container, options, sysPlan, callbackFn) {
        debug('getSystemCallBack', 1);
        getSystemInstance(container, options, callbackFn, sysPlan);
    }

    /**
     * getSystemPlan get the system plan from observant and runs a callback function
     * @param container the container div, will be passed to the callback function
     * @param options an object representing the options, will be passed to the callback function
     * @param callbackFn the callback funtion which accepts the container and options parameters, plus the system plan object
     * @param renderCallBackFn the call back function to render the visualisation with the signature fn(container, plotData, options)
     * @returns void
     */
    function getSystemPlan(container, options, callbackFn, renderCallBackFn) {
        debug('getSystemPlan', 1);
        undefinedOrgIdError = new Error ("No organisation ID specified in options.");
        undefinedPlanIdError = new Error ("No system plan ID specified in options.");
        
        try {
            // make sure we have the required fields to get a system plan
            if (options.orgId === undefined) {
                throw undefinedOrgIdError;
            }

            if (options.planId === undefined) {
                throw undefinedPlanIdError;
            }

            // check if the system plan has already been retrieved and use object if so
            if (SYSPLANS[options.planId] !== undefined) {
                callbackFn(container, options, SYSPLANS[options.planId], renderCallBackFn);
            } else {
                // url for sysplan
                //var observantSysPlanUrl = BASE_URL + '/orgs/' + options.orgId + '/sysplans/' + options.planId + '?media=json';
                var observantSysPlanUrl = BASE_URL + '/sysplans/' + options.orgId + '/' + options.planId + '?media=json';
                if (options.resAuthKey) {
                    observantSysPlanUrl = observantSysPlanUrl + '&resAuthKey=' + options.resAuthKey;
                }

                debug ('getSystemPlan.observantSysPlanUrl: ' + observantSysPlanUrl, 1);
                jQuery.ajax({
                    type: 'GET',
                    url : observantSysPlanUrl,
                    success : function(sysPlan) {
                        debug(sysPlan, 3);
                        SYSPLANS[options.planId] = sysPlan;
                        callbackFn(container, options, sysPlan, renderCallBackFn);
                    },
                    error: function(XMLHttpRequest, textStatus, errorThrown) {
                        error(errorThrown, container);
                        debug(XMLHttpRequest, 4);
                        debug(textStatus, 4);
                        debug(errorThrown, 4);
                    }
                });
            }

        }
        catch(e) {
            error(e.message, container);
        }
    }

    /**
     * getSystemInstance get the system instance from observant and runs a callback function
     * @param container the container div, will be passed to the callback function
     * @param options an object representing the options, will be passed to the callback function
     * @param callbackFn the callback funtion which accepts the container and options parameters, plus the system plan object
     * @param sysPlan the system plan if this is chained together with getSystemPlan
     * @returns void
     */
    function getSystemInstance(container, options, callbackFn, sysPlan) {
        undefinedOrgIdError = new Error ("No organisation ID specified in options.");
        undefinedPlanIdError = new Error ("No system plan ID specified in options.");

        try {
            // make sure we have the required fields to get a system plan
            if (options.orgId === undefined) {
                throw undefinedOrgIdError;
            }

            if (options.planId === undefined) {
                throw undefinedPlanIdError;
            }

            // check if the system plan has already been retrieved and use object if so
            if (SYSINSTANCES[options.planId] !== undefined) {
                if (sysPlan) {
                    var system = {};
                    system.sysPlan = sysPlan;
                    system.sysInstance = SYSINSTANCES[options.planId];
                    system.sites = getSites(sysPlan, SYSINSTANCES[options.planId]);
                    callbackFn(container, options, system);
                } else {
                    callbackFn(container, options, SYSINSTANCES[options.planId]);
                }
            } else {
                // url for sysplan
                var observantSysInstanceUrl = BASE_URL + '/system/' + options.orgId + '/' + options.planId + '/current?media=json';
                if (options.resAuthKey) {
                    observantSysInstanceUrl = observantSysInstanceUrl + '&resAuthKey=' + options.resAuthKey;
                }

                debug ('getSystemInstance.observantSysInstanceUrl: ' + observantSysInstanceUrl, 1);
                jQuery.ajax({
                    type: 'GET',
                    url : observantSysInstanceUrl,
                    success : function(sysInstance) {
                        debug(sysInstance, 3);
                        SYSINSTANCES[options.planId] = sysInstance;
                        if (sysPlan) {
                            var system = {};
                            system.sysPlan = sysPlan;
                            system.sysInstance = sysInstance;
                            system.sites = getSites(sysPlan, sysInstance);
                            callbackFn(container, options, system);
                        } else {
                            callbackFn(container, options, sysInstance);
                        }
                    },
                    error: function(XMLHttpRequest, textStatus, errorThrown) {
                        error(errorThrown, container);
                        debug(XMLHttpRequest, 4);
                        debug(textStatus, 4);
                        debug(errorThrown, 4);
                    }
                });
            }

        }
        catch(e) {
            error(e.message, container);
        }
    }

    /**
     * getSites gets a sites object based on the system plan and system instance
     * @param sysPlan the system plan
     * @param sysInstance the system instance
     * @returns sites the sites for the system
     */
    function getSites(sysPlan, sysInstance) {
        var sites = [];

        $.each(sysPlan.vizContainer.vizReferences, function(i, vizReference) {
            // see if site already exists
            var siteExists = false;
            $.each(sites, function(j, site) {
                if (site.name == vizReference.site) {
                    siteExists = true;
                }
            });
            if (!siteExists) {
                // create new site
                var site = {};
                site.name = vizReference.site;
                // get long and lat

                $.each(sysInstance.siteVizs, function(j, siteViz) {
                    if (vizReference.vizId==siteViz.vizId) {
                        if(siteViz.geoLocation){
                        	if(siteViz.geoLocation.latitude&&siteViz.geoLocation.longitude) {
                        		site.latitude = siteViz.geoLocation.latitude;
                        		site.longitude = siteViz.geoLocation.longitude;
                        	}
                        }
                    }
                });

                site.vizs = [];
                site.vizs.push(vizReference.vizId);
                sites.push(site);
            } else {
                // add this viz to the existing site
                $.each(sites, function(j, site) {
                    if (site.name == vizReference.site) {
                        sites[j].vizs.push(vizReference.vizId);
                    }
                });
            }
        });

        return sites;

    }

    /**
     * Viz is the abstract base class for observant visualisations
     * @constructor
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     */
    function Viz(container, options) {
        this.container = container;
        this.options = options;
        if (container) {
            jQuery.ajaxSetup({
                dataType : 'jsonp'
                , cache: true
            });

            /*
            try {
                if (options.chartOptions.theme) {
                    $('#'+container).addClass(options.chartOptions.theme);
                }
	     }
            catch(e) {
            	error(e.message, container);
            }
            */

            // loading indicator
            if (options.vizType != 'downloadLink' && options.vizType != 'system' && options.vizType != 'map') {
                jQuery('#' + container).addClass('obsviz-main-container');
            }
            if (options.vizType != 'downloadLink' && options.vizType != 'system') {
                jQuery('#' + container).html('<p class="obsviz-loading">Fetching '+options.vizType+'...</p>');
            }
        }
    }

    /**
     * Photo is a subclass of Viz
     * @constructor
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     */
    function Photo(container, options) {
        // deep copy options
        var dcOptions = jQuery.extend({}, options);
        dcOptions.vizType = 'photo';

        //call parent construtor
        Viz.call(this, container, dcOptions);
        // get the system plan and run photoCallBack
        getSystem(container, dcOptions, photoCallBack);
    }

    // Photo is a subclass of Viz
    Photo.prototype = new Viz();

    /**
     * PhotoGallery is a subclass of Viz
     * @constructor
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     */
    function PhotoGallery(container, options) {
        // deep copy options
        var dcOptions = jQuery.extend({}, options);
        dcOptions.vizType = 'photo gallery';

        //call parent construtor
        Viz.call(this, container, dcOptions);
        // get the system instance and run photoGalleryCallBack
        getSystemInstance(container, dcOptions, photoGalleryCallBack);
    }

    // Photo is a subclass of Viz
    PhotoGallery.prototype = new Viz();


    /**
     * Map is a subclass of Viz
     * @constructor
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     */
    function Map(container, options) {
        // deep copy options
        var dcOptions = jQuery.extend({}, options);
        dcOptions.vizType = 'map';
        
        //call parent construtor
        Viz.call(this, container, dcOptions);

        // get the system (plan and instance) and run mapCallBack
        getSystem(container, dcOptions, mapCallBack);
    }

    // Map is a subclass of Viz
    Map.prototype = new Viz();

    /**
     * System is a subclass of Viz
     * @constructor
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     */
    function System(options) {
        debug('System Constructor', 1);

        // deep copy options
        var dcOptions = jQuery.extend({}, options);
        dcOptions.vizType = 'system';

        //call parent construtor
        Viz.call(this, '#', dcOptions);

        // get the system (plan and instance) and run mapCallBack
        getSystem('#', dcOptions, systemCallBack);
    }

    System.prototype = new Viz();

    /**
     * Table is a subclass of Viz
     * @constructor
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     */
    function Table(container, options) {
        // deep copy options
        var dcOptions = jQuery.extend({}, options);
        dcOptions.vizType = 'table';

        // call parent constructor
        Viz.call(this, container, dcOptions);

        // get the system plan and run dataVizCallBack
        getSystemPlan(container, dcOptions, dataVizCallBack, renderTableViz);
    }


    // Table is a subclass of Viz
    Table.prototype = new Viz();

    /**
     * DownloadLink is a subclass of Viz
     * @constructor
     * @param container the container element (could be p, span, div etc.)
     * @param options an object literal representing the options of the observant API
     */
    function DownloadLink(container, options) {
        // deep copy options
        var dcOptions = jQuery.extend({}, options);
        dcOptions.vizType = 'downloadLink';
        
        // call parent constructor
        Viz.call(this, container, dcOptions);

        // get the system plan and run dataVizCallBack
        getSystemPlan(container, dcOptions, dataVizCallBack, renderDownloadLink);
    }

    // DownloadLink is a subclass of Viz
    DownloadLink.prototype = new Viz();

    /**
     * Chart is a subclass of Viz
     * @constructor
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     */
    function Chart(container, options) {
        // deep copy options
        var dcOptions = jQuery.extend({}, options);
        dcOptions.vizType = 'chart';

        // call parent constructor
        Viz.call(this, container, dcOptions);

        // get the system plan and run dataVizCallBack
        getSystemPlan(container, dcOptions, dataVizCallBack, renderChartViz);
    }

    // Chart is a subclass of Viz
    Chart.prototype = new Viz();

    /**
     * chartGallery is a subclass of Viz
     * @constructor
     * @param container the container div
     * @param options an object literal representing the options of the observant API
     */
    function ChartGallery(container, options) {       
        // no titles for charts gallery, titles created separately 
        var chartGalleryOptions = {
        	chartOptions: {
				title: {
					text: ''
				},
				subtitle: {
					text: ''
				}
			}
		}
		
		// deep copy options
        var dcOptions = jQuery.extend({}, options);
        dcOptions = jQuery.extend(true, {}, dcOptions, chartGalleryOptions);
        dcOptions.vizType = 'chart gallery';

        // call parent constructor
        Viz.call(this, container, dcOptions);

        // add an entry to global gallers, will need to maintain state of gallery
        if (GALLERIES[container] === undefined) {
            GALLERIES[container] = {};
        }

        // get the system plan and run chartGalleryCallBack
        dcOptions.vizType = 'chart';
        getSystemPlan(container, dcOptions, chartGalleryCallBack);
    }

    // chartGallery is a subclass of Vizs
    ChartGallery.prototype = new Viz();

    /**
     * getPlotData returns data in a format for charting and tabling
     * @param    sysPlan the system plan from observant
     * @param    options the user options
     * @param    activeViz the viz reference json
     * @param    activeField the field reference json
     * @param    sysInstance the system instance from observant
     * @return   mixed a highcharts object
     */
    function getPlotData(sysPlan, options, activeViz, activeField, sysInstance) {
        var plotData = {};


        // first decide what the output type will be
        if (options.vizType == 'table' || options.vizType == 'downloadLink') {
            plotData.chart = {
                defaultSeriesType : 'table'
            };
        } else {
            if (sysInstance.dataType == 'continuous') {
                plotData.chart = {
                    defaultSeriesType : 'line'
                };
            } else if (sysInstance.dataType == 'discrete') {
                plotData.chart = {
                    defaultSeriesType : 'column'
                };
            }
        }
		
		// reformat ydata
        var ydata = formatYData(sysInstance.history.ydata);

        // ytitle depends on unit specification
        if (sysInstance.history.units) {
            plotData.yAxis = { 
                title: {
                    text: activeViz.name + ' (' + activeField.name + ')' + ' [' + sysInstance.history.units + ']'
                }
            };
        } else {
            plotData.yAxis = { 
                title: {
                	text: activeViz.name + ' (' + activeField.name + ')'
                }
            };
        }

        // variables for plotting for all chart types
        plotData.title = {
            text: activeViz.site + ': ' + activeViz.name
        };
        plotData.subtitle = {
            text: sysPlan.planName
        };
        plotData.xAxis = {
            dateTimeLabelFormats: {
                second: '%H:%M:%S',
                minute: '%H:%M',
                hour: '%H:%M',
                day: '%d %b',
                week: '%d %b',
                month: '%b %y',
                year: '%Y'
            }
        };

        // tooltip function
        var xDataFormat;
        switch (options.history.substring(options.history.length - 1)) {
            case 'h':
                xDataFormat = '%d %b %H:%M';
                break;
            default:
                xDataFormat = plotData.xAxis.dateTimeLabelFormats.day;
                break;
        }

        
        plotData.tooltip = {
            formatter: function() {
                var toolTipText = '<strong>'+ activeViz.name +'</strong><br/>'+ Highcharts.dateFormat(xDataFormat, this.x) + '<br/>' + this.y;
                if (sysInstance.history.units) {
                    toolTipText = toolTipText + sysInstance.history.units;
                }
                return toolTipText;
            }
        };


        // series type specific options
        if (plotData.chart.defaultSeriesType == 'line' || plotData.chart.defaultSeriesType == 'table' || plotData.chart.defaultSeriesType == 'area') {
            plotData.xAxis.type = 'datetime';

            plotData.legend = {
                layout: 'vertical',
                style: {
                    left: 'auto',
                    bottom: 'auto',
                    right: '30px',
                    top: '10px'
                }
            };

            plotData.series = new Array();
            plotData.series[0] = {};
            if (sysInstance.history.units) {
                plotData.series[0].name = activeViz.name + ' (' + sysInstance.history.units + ')';
            } else {
                plotData.series[0].name = activeViz.name;
            }
            plotData.series[0].data = timeSeries(sysInstance.history.startTime, sysInstance.history.timeInterval, ydata, true);

        } else if (plotData.chart.defaultSeriesType == 'column' || plotData.chart.defaultSeriesType == 'bar') {
            plotData.series = new Array();
            plotData.series[0] = {};
            if (sysInstance.history.units) {
                plotData.series[0].name = activeViz.name + ' (' + sysInstance.history.units + ')';
            } else {
                plotData.series[0].name = activeViz.name;
            }
            
            // with categories
/*
            plotData.series[0].data = ydata;
            plotData.xAxis.categories = timeSeries(sysInstance.history.startTime, sysInstance.history.timeInterval, ydata, false);
            plotData.xAxis.labels = {
                enabled: false
            };
*/
            
            // make column same as line
            plotData.xAxis.type = 'datetime';
            plotData.series[0].data = timeSeries(sysInstance.history.startTime, sysInstance.history.timeInterval, ydata, true);
                    
            
        }

        return plotData;
    }

    /**
    * timeSeries returns an array of time points, with y data if required
    * @param    startTime the UNIX timestamp to commence the datapoints
    * @param    timeInterval the interval in milliseconds to seperate the datapoints
    * @param    ydata the ydata to add to the series
    * @param    embedYData whether to return the y data in the series
    * @return   array the series for plotting with HighCharts or displaying in a table
    */
    function timeSeries(startTime, timeInterval, ydata, embedYData) {
        var series = [];
        var d = new Date();
        var localOffset = d.getTimezoneOffset() * 60000;
        
        for(var i = 0; i<ydata.length; i++) {
            if (embedYData) {
                series[i] = [startTime - localOffset + (i*timeInterval), ydata[i]];
            } else {
                series[i] = [startTime - localOffset + (i*timeInterval)];
            }
        }
        return series;
    }

    /**
    * formatYData returns numeric y data array from mixed data with a fixed precision (currently hardcoded)
    * @param    ydata an array of data points of mixed format
    * @return   an array of data points, with null if input was NaN (which highcharts can handle)
    */
    function formatYData(ydata) {
        var yDataOut = [];
        for(var i = 0; i<ydata.length; i++) {
            // if not a number then return null
            if (isNaN(ydata[i])) {
                yDataOut[i] = null;
            } else {
                yDataOut[i] = parseFloat(ydata[i].toFixed(3));
            }
        }
        return yDataOut;
    }

    /**
     * renderChartViz renders the highcharts chart object
     * @param    container the ID of the container div
     * @param    plotData the highcharts data to plot
     * @param    userOptions the user defined options for the chart
     * @return   void
     */
    function renderChartViz(container, plotData, userOptions) {
        var chartContainer = container + '_chart';
        jQuery('#' + container).html('<div id="' + chartContainer + '"></div>');

        var chartHeight = $('#' + container).height();
        var chartWidth = $('#' + container).width();

        // base level options for all chart types
        var baseOptions = {
            showLink: false,
            chartOptions: {
                chart: {
                	renderTo: chartContainer
                	, height: chartHeight
                	, width: chartWidth
                },
                credits: {
                    enabled:true
                    , href:"http://observant.com.au/"
                    , text: "<span>Observant</span>"
                    , target: "_blank"
                },
                theme: 'light'
            }
        };

        // add user defined options to default options
        var plotDataOptions = {};
        plotDataOptions.chartOptions = plotData;

        var empty = {};
        var mergedOptions = jQuery.extend(true, empty, plotDataOptions, baseOptions);

        // add base theme
        mergedOptions = jQuery.extend(true, empty, mergedOptions, THEME.base);

        // add user theme
        if (userOptions.chartOptions && userOptions.chartOptions.theme) {
            mergedOptions = jQuery.extend(true, empty, mergedOptions, THEME[userOptions.chartOptions.theme]);
        }  else {
            mergedOptions = jQuery.extend(true, empty, mergedOptions, THEME[baseOptions.chartOptions.theme]);
        }

        // add user options
        mergedOptions = jQuery.extend(true, empty, mergedOptions, userOptions);

        //add the chart download link
        if (mergedOptions.showLink == true) {
            var spanId = container + '_download_link';
            jQuery('#' + container).append('<div class="utils_menu"><a class="utils_link"><span>menu</span></a><ul><li id="'+ spanId +'"></li></ul></div>');
            obsviz.DownloadLink(spanId, userOptions);
        }

        // create the chart
        var chart = new Highcharts.Chart(mergedOptions.chartOptions);
    }

    /**
    * renderTableViz renders the table visualisation
    * @param    container the ID of the container div
    * @param    plotData the data in highcharts format
    * @param    userOptions the options
    * @return   void
    */
    function renderTableViz(container, plotData, userOptions) {
        var baseOptions = {
            sort: 'asc',
            showLink: true
        };
        var empty = {};
        var mergedOptions = jQuery.extend(true, empty, baseOptions, userOptions.tableOptions);

        var tableHtml = '<table>';
        tableHtml = tableHtml + '<caption>'+ plotData.title.text + ' (' + plotData.subtitle.text + ')</caption>';
        tableHtml = tableHtml + '<tr><th>Date</th><th>'+plotData.yAxis.title.text+'</th></tr>';

        var dataPointCount = plotData.series[0].data.length;

        for (var i in plotData.series[0].data) {
            if (mergedOptions.sort == 'desc') {
                tableHtml = tableHtml + '<tr><td>' + Highcharts.dateFormat(plotData.xAxis.dateTimeLabelFormats.day, plotData.series[0].data[dataPointCount -1 - i][0])  + '</td><td>' + plotData.series[0].data[dataPointCount -1 - i][1] + '</td></tr>';
            } else {
                tableHtml = tableHtml + '<tr><td>' + Highcharts.dateFormat(plotData.xAxis.dateTimeLabelFormats.day, plotData.series[0].data[i][0])  + '</td><td>' + plotData.series[0].data[i][1] + '</td></tr>';
            }
        }
        tableHtml = tableHtml + '</table>';

        //add the table
        jQuery('#' + container).html(tableHtml);
        
        //add the table download link
        if (mergedOptions.showLink == true) {
            var spanId = container + '_download_link';
            jQuery('#' + container + ' caption').append('<span id="'+ spanId +'"></span>');
            obsviz.DownloadLink(spanId, userOptions);
        }
    }

    /**
    * renderDownloadLink renders the download link
    * @param    container the ID of the container element
    * @param    plotData the data in highcharts format
    * @param    options the download options
    * @return   void
    */
    function renderDownloadLink(container, plotData, options) {
        // set default options and merge in user options
        var baseOptions = {
            delimiter: ','
            , lineFeed: '\n'
            , stringsEnclosedBy: '"'
            , fileName: 'download.csv'
            , sort: 'asc'
            , linkText : 'Export as CSV...'
            , linkTitle : 'Export as CSV'
        };
        var empty = {};
        var mergedOptions = {};
        if (options.downloadOptions !== undefined) {
            mergedOptions = jQuery.extend(true, empty, baseOptions, options.downloadOptions);
        } else {
            mergedOptions = jQuery.extend(true, empty, baseOptions);
        }

        // create header row
        var payLoad = 'Date' + mergedOptions.delimiter + plotData.yAxis.title.text + mergedOptions.lineFeed;
        
        // create data (ascending or descending)
        var dataPointCount = plotData.series[0].data.length;
        for (var i in plotData.series[0].data) {
            if (mergedOptions.sort == 'desc') {
                payLoad = payLoad + Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', plotData.series[0].data[dataPointCount -1 - i][0])  + mergedOptions.delimiter + plotData.series[0].data[dataPointCount -1 - i][1] + mergedOptions.lineFeed;
            } else {
                payLoad = payLoad + Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', plotData.series[0].data[i][0])  + mergedOptions.delimiter + plotData.series[0].data[i][1] + mergedOptions.lineFeed;
            }
        }

        // create form with the necessary details
        var formId = container + '_form';
        var dataId = container + '_data';
        var fileNameId = container + '_filename';
        
        // post to generate csv, can't use jQuery.post here
        // because need the response to be handled
        // by the browser, not by the javascript
        $("body").append('<form id="'+formId+'" action="' + EXPORT_URL + '" method="post" target="_blank"><input type="hidden" id="' + dataId + '" name="exportdata" /><input type="hidden" id="'+fileNameId+'" name="exportfilename" /></form>');

        $("#"+dataId).val(payLoad);
        $("#"+fileNameId).val(mergedOptions.fileName);

        // now add a link to submit the form
        // check for link text and title
        // create the link
        
        $('#' + container).html('<a href="#" title="' + mergedOptions.linkTitle + '">' + mergedOptions.linkText + '</a>');

        // add click event
        $('#' + container + ' a').bind('click', function() {
                $("#" + formId).submit();
                return false;
            }
        );

    }


    /**
    * debug writes debug messages if in debug mode AND using firebug
    * @param   debug_message the message to display in the console
    * @param   debug_level the level to debug to (see DEBUG_LEVEL)
    */
    function debug(debug_message, debug_level) {
        // 3 is default level
        if (!debug_level) {
            debug_level = 3;
        }

        if (DEBUG && typeof(console) == 'object' && DEBUG_LEVEL <= debug_level) {
            console.log(debug_message);
        }
    }

    /**
     * unrecoverable errors
     * @param   error_message the message to display
     * @param   container the container div to display the message
     */
    function error(error_message, container) {
        if (typeof(console) == 'object') {
            console.log(error_message);
        }
        if (isUndefined(error_message)) {
        	error_message = "There seems to be a problem fetching the widget. Please try again later."
        }
        if (container && container != '#') {
            jQuery('#' + container).html('<div class="obsviz-error"><p class="obsviz-error-text">A small problem has sprung up, but we\'ll get it fixed. <br />Please try again a bit later.</p><p class="obsviz-tech-error"><strong>Error :</strong> ' + error_message + '</p></div>');
            
/*         <img src="https://see.observant.net.au/jsapi/images/fail-roo.jpg" title="Fail Roo" /> */
        }
    }

    /**
     * checks if a **primitive** object is empty
     * @param   obj the object to check
     */
    function isEmpty(obj){
        for(var i in obj) {
            return false;
        }
        return true;
    }
    
    /* 
     * checks if something is undefined
     */
    function isUndefined(x) {var u; return x === u;}


    return {
        Viz : Viz,
        Table : Table,
        Chart : Chart,
        Photo : Photo,
        PhotoGallery : PhotoGallery,
        Map : Map,
        ChartGallery : ChartGallery,
        DownloadLink : DownloadLink,
        System : System
    };
})();
