a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e){return{}}g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent){for(n in {submit:1,change:1,focusin:1}){m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o}}j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i){f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}}var u;if(!f.support.appendChecked){if(l[0]&&typeof(u=l.length)=="number"){for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get" in k&&(g=k.get(a,!1,e))!==b){return g}return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d)){return}h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set" in k)||(d=k.set(a,d))!==b){try{j[c]=d}catch(l){}}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get" in g&&(e=g.get(a,!0,d))!==b){return e}if(by){return by(a,c)}},swap:function(a,b,c){var d={},e,f;for(f in b){d[f]=a.style[f],a.style[f]=b[f]}e=c.call(a);for(f in b){a.style[f]=d[f]}return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c){return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})}},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter){return}}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return !f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++){f[a+bx[d]+b]=e[d]||e[d-2]||e[0]}return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/
+ *
+ *
+ * To host the Timeline files on your own server:
+ * 1) Install the Timeline and Simile-Ajax files onto your webserver using
+ * timeline_libraries.zip or timeline_source.zip
+ *
+ * 2) Set global js variables used to send parameters to this script:
+ * Timeline_ajax_url -- url for simile-ajax-api.js
+ * Timeline_urlPrefix -- url for the *directory* that contains timeline-api.js
+ * Include trailing slash
+ * Timeline_parameters='bundle=true'; // you must set bundle to true if you are using
+ * // timeline_libraries.zip since only the
+ * // bundled libraries are included
+ *
+ * eg your html page would include
+ *
+ *
+ *
+ *
+ * SCRIPT PARAMETERS
+ * This script auto-magically figures out locale and has defaults for other parameters
+ * To set parameters explicity, set js global variable Timeline_parameters or include as
+ * parameters on the url using GET style. Eg the two next lines pass the same parameters:
+ * Timeline_parameters='bundle=true'; // pass parameter via js variable
+ * ");
+ } catch (e) {
+ createScriptElement();
+ }
+ } else {
+ createScriptElement();
+ }
+ } else {
+ loadMe();
+ }
+
+})();
+/*=================================================
+ *
+ * Coding standards:
+ *
+ * We aim towards Douglas Crockford's Javascript conventions.
+ * See: http://javascript.crockford.com/code.html
+ * See also: http://www.crockford.com/javascript/javascript.html
+ *
+ * That said, this JS code was written before some recent JS
+ * support libraries became widely used or available.
+ * In particular, the _ character is used to indicate a class function or
+ * variable that should be considered private to the class.
+ *
+ * The code mostly uses accessor methods for getting/setting the private
+ * class variables.
+ *
+ * Over time, we'd like to formalize the convention by using support libraries
+ * which enforce privacy in objects.
+ *
+ * We also want to use jslint: http://www.jslint.com/
+ *
+ *
+ *==================================================
+ */
+
+
+
+/*==================================================
+ * Timeline VERSION
+ *==================================================
+ */
+// Note: version is also stored in the build.xml file
+Timeline.version = '2.3.1'; // use format 'pre 1.2.3' for trunk versions
+Timeline.ajax_lib_version = SimileAjax.version; // Waiting for version string method from Ajax library
+Timeline.display_version = Timeline.version + ' (with Ajax lib ' + Timeline.ajax_lib_version + ')';
+ // cf method Timeline.writeVersion
+
+/*==================================================
+ * Timeline
+ *==================================================
+ */
+Timeline.strings = {}; // localization string tables
+Timeline.HORIZONTAL = 0;
+Timeline.VERTICAL = 1;
+Timeline._defaultTheme = null;
+
+Timeline.getDefaultLocale = function() {
+ return Timeline.clientLocale;
+};
+
+Timeline.create = function(elmt, bandInfos, orientation, unit) {
+ if (Timeline.timelines == null) {
+ Timeline.timelines = [];
+ // Timeline.timelines array can have null members--Timelines that
+ // once existed on the page, but were later disposed of.
+ }
+
+ var timelineID = Timeline.timelines.length;
+ Timeline.timelines[timelineID] = null; // placeholder until we have the object
+ var new_tl = new Timeline._Impl(elmt, bandInfos, orientation, unit,
+ timelineID);
+ Timeline.timelines[timelineID] = new_tl;
+ return new_tl;
+};
+
+Timeline.createBandInfo = function(params) {
+ var theme = ("theme" in params) ? params.theme : Timeline.getDefaultTheme();
+
+ var eventSource = ("eventSource" in params) ? params.eventSource : null;
+
+ var ether = new Timeline.LinearEther({
+ centersOn: ("date" in params) ? params.date : new Date(),
+ interval: SimileAjax.DateTime.gregorianUnitLengths[params.intervalUnit],
+ pixelsPerInterval: params.intervalPixels,
+ theme: theme
+ });
+
+ var etherPainter = new Timeline.GregorianEtherPainter({
+ unit: params.intervalUnit,
+ multiple: ("multiple" in params) ? params.multiple : 1,
+ theme: theme,
+ align: ("align" in params) ? params.align : undefined
+ });
+
+ var eventPainterParams = {
+ showText: ("showEventText" in params) ? params.showEventText : true,
+ theme: theme
+ };
+ // pass in custom parameters for the event painter
+ if ("eventPainterParams" in params) {
+ for (var prop in params.eventPainterParams) {
+ eventPainterParams[prop] = params.eventPainterParams[prop];
+ }
+ }
+
+ if ("trackHeight" in params) {
+ eventPainterParams.trackHeight = params.trackHeight;
+ }
+ if ("trackGap" in params) {
+ eventPainterParams.trackGap = params.trackGap;
+ }
+
+ var layout = ("overview" in params && params.overview) ? "overview" : ("layout" in params ? params.layout : "original");
+ var eventPainter;
+ if ("eventPainter" in params) {
+ eventPainter = new params.eventPainter(eventPainterParams);
+ } else {
+ switch (layout) {
+ case "overview" :
+ eventPainter = new Timeline.OverviewEventPainter(eventPainterParams);
+ break;
+ case "detailed" :
+ eventPainter = new Timeline.DetailedEventPainter(eventPainterParams);
+ break;
+ default:
+ eventPainter = new Timeline.OriginalEventPainter(eventPainterParams);
+ }
+ }
+
+ return {
+ width: params.width,
+ eventSource: eventSource,
+ timeZone: ("timeZone" in params) ? params.timeZone : 0,
+ ether: ether,
+ etherPainter: etherPainter,
+ eventPainter: eventPainter,
+ theme: theme,
+ zoomIndex: ("zoomIndex" in params) ? params.zoomIndex : 0,
+ zoomSteps: ("zoomSteps" in params) ? params.zoomSteps : null
+ };
+};
+
+Timeline.createHotZoneBandInfo = function(params) {
+ var theme = ("theme" in params) ? params.theme : Timeline.getDefaultTheme();
+
+ var eventSource = ("eventSource" in params) ? params.eventSource : null;
+
+ var ether = new Timeline.HotZoneEther({
+ centersOn: ("date" in params) ? params.date : new Date(),
+ interval: SimileAjax.DateTime.gregorianUnitLengths[params.intervalUnit],
+ pixelsPerInterval: params.intervalPixels,
+ zones: params.zones,
+ theme: theme
+ });
+
+ var etherPainter = new Timeline.HotZoneGregorianEtherPainter({
+ unit: params.intervalUnit,
+ zones: params.zones,
+ theme: theme,
+ align: ("align" in params) ? params.align : undefined
+ });
+
+ var eventPainterParams = {
+ showText: ("showEventText" in params) ? params.showEventText : true,
+ theme: theme
+ };
+ // pass in custom parameters for the event painter
+ if ("eventPainterParams" in params) {
+ for (var prop in params.eventPainterParams) {
+ eventPainterParams[prop] = params.eventPainterParams[prop];
+ }
+ }
+ if ("trackHeight" in params) {
+ eventPainterParams.trackHeight = params.trackHeight;
+ }
+ if ("trackGap" in params) {
+ eventPainterParams.trackGap = params.trackGap;
+ }
+
+ var layout = ("overview" in params && params.overview) ? "overview" : ("layout" in params ? params.layout : "original");
+ var eventPainter;
+ if ("eventPainter" in params) {
+ eventPainter = new params.eventPainter(eventPainterParams);
+ } else {
+ switch (layout) {
+ case "overview" :
+ eventPainter = new Timeline.OverviewEventPainter(eventPainterParams);
+ break;
+ case "detailed" :
+ eventPainter = new Timeline.DetailedEventPainter(eventPainterParams);
+ break;
+ default:
+ eventPainter = new Timeline.OriginalEventPainter(eventPainterParams);
+ }
+ }
+ return {
+ width: params.width,
+ eventSource: eventSource,
+ timeZone: ("timeZone" in params) ? params.timeZone : 0,
+ ether: ether,
+ etherPainter: etherPainter,
+ eventPainter: eventPainter,
+ theme: theme,
+ zoomIndex: ("zoomIndex" in params) ? params.zoomIndex : 0,
+ zoomSteps: ("zoomSteps" in params) ? params.zoomSteps : null
+ };
+};
+
+Timeline.getDefaultTheme = function() {
+ if (Timeline._defaultTheme == null) {
+ Timeline._defaultTheme = Timeline.ClassicTheme.create(Timeline.getDefaultLocale());
+ }
+ return Timeline._defaultTheme;
+};
+
+Timeline.setDefaultTheme = function(theme) {
+ Timeline._defaultTheme = theme;
+};
+
+Timeline.loadXML = function(url, f) {
+ var fError = function(statusText, status, xmlhttp) {
+ alert("Failed to load data xml from " + url + "\n" + statusText);
+ };
+ var fDone = function(xmlhttp) {
+ var xml = xmlhttp.responseXML;
+ if (!xml.documentElement && xmlhttp.responseStream) {
+ xml.load(xmlhttp.responseStream);
+ }
+ f(xml, url);
+ };
+ SimileAjax.XmlHttp.get(url, fError, fDone);
+};
+
+
+Timeline.loadJSON = function(url, f) {
+ var fError = function(statusText, status, xmlhttp) {
+ alert("Failed to load json data from " + url + "\n" + statusText);
+ };
+ var fDone = function(xmlhttp) {
+ f(eval('(' + xmlhttp.responseText + ')'), url);
+ };
+ SimileAjax.XmlHttp.get(url, fError, fDone);
+};
+
+Timeline.getTimelineFromID = function(timelineID) {
+ return Timeline.timelines[timelineID];
+};
+
+// Write the current Timeline version as the contents of element with id el_id
+Timeline.writeVersion = function(el_id) {
+ document.getElementById(el_id).innerHTML = this.display_version;
+};
+
+
+
+/*==================================================
+ * Timeline Implementation object
+ *==================================================
+ */
+Timeline._Impl = function(elmt, bandInfos, orientation, unit, timelineID) {
+ SimileAjax.WindowManager.initialize();
+
+ this._containerDiv = elmt;
+
+ this._bandInfos = bandInfos;
+ this._orientation = orientation == null ? Timeline.HORIZONTAL : orientation;
+ this._unit = (unit != null) ? unit : SimileAjax.NativeDateUnit;
+ this._starting = true; // is the Timeline being created? Used by autoWidth
+ // functions
+ this._autoResizing = false;
+
+ // autoWidth is a "public" property of the Timeline object
+ this.autoWidth = bandInfos && bandInfos[0] && bandInfos[0].theme &&
+ bandInfos[0].theme.autoWidth;
+ this.autoWidthAnimationTime = bandInfos && bandInfos[0] && bandInfos[0].theme &&
+ bandInfos[0].theme.autoWidthAnimationTime;
+ this.timelineID = timelineID; // also public attribute
+ this.timeline_start = bandInfos && bandInfos[0] && bandInfos[0].theme &&
+ bandInfos[0].theme.timeline_start;
+ this.timeline_stop = bandInfos && bandInfos[0] && bandInfos[0].theme &&
+ bandInfos[0].theme.timeline_stop;
+ this.timeline_at_start = false; // already at start or stop? Then won't
+ this.timeline_at_stop = false; // try to move further in the wrong direction
+
+ this._initialize();
+};
+
+//
+// Public functions used by client sw
+//
+Timeline._Impl.prototype.dispose = function() {
+ for (var i = 0; i < this._bands.length; i++) {
+ this._bands[i].dispose();
+ }
+ this._bands = null;
+ this._bandInfos = null;
+ this._containerDiv.innerHTML = "";
+ // remove from array of Timelines
+ Timeline.timelines[this.timelineID] = null;
+};
+
+Timeline._Impl.prototype.getBandCount = function() {
+ return this._bands.length;
+};
+
+Timeline._Impl.prototype.getBand = function(index) {
+ return this._bands[index];
+};
+
+Timeline._Impl.prototype.finishedEventLoading = function() {
+ // Called by client after events have been loaded into Timeline
+ // Only used if the client has set autoWidth
+ // Sets width to Timeline's requested amount and will shrink down the div if
+ // need be.
+ this._autoWidthCheck(true);
+ this._starting = false;
+};
+
+Timeline._Impl.prototype.layout = function() {
+ // called by client when browser is resized
+ this._autoWidthCheck(true);
+ this._distributeWidths();
+};
+
+Timeline._Impl.prototype.paint = function() {
+ for (var i = 0; i < this._bands.length; i++) {
+ this._bands[i].paint();
+ }
+};
+
+Timeline._Impl.prototype.getDocument = function() {
+ return this._containerDiv.ownerDocument;
+};
+
+Timeline._Impl.prototype.addDiv = function(div) {
+ this._containerDiv.appendChild(div);
+};
+
+Timeline._Impl.prototype.removeDiv = function(div) {
+ this._containerDiv.removeChild(div);
+};
+
+Timeline._Impl.prototype.isHorizontal = function() {
+ return this._orientation == Timeline.HORIZONTAL;
+};
+
+Timeline._Impl.prototype.isVertical = function() {
+ return this._orientation == Timeline.VERTICAL;
+};
+
+Timeline._Impl.prototype.getPixelLength = function() {
+ return this._orientation == Timeline.HORIZONTAL ?
+ this._containerDiv.offsetWidth : this._containerDiv.offsetHeight;
+};
+
+Timeline._Impl.prototype.getPixelWidth = function() {
+ return this._orientation == Timeline.VERTICAL ?
+ this._containerDiv.offsetWidth : this._containerDiv.offsetHeight;
+};
+
+Timeline._Impl.prototype.getUnit = function() {
+ return this._unit;
+};
+
+Timeline._Impl.prototype.getWidthStyle = function() {
+ // which element.style attribute should be changed to affect Timeline's "width"
+ return this._orientation == Timeline.HORIZONTAL ? 'height' : 'width';
+};
+
+Timeline._Impl.prototype.loadXML = function(url, f) {
+ var tl = this;
+
+
+ var fError = function(statusText, status, xmlhttp) {
+ alert("Failed to load data xml from " + url + "\n" + statusText);
+ tl.hideLoadingMessage();
+ };
+ var fDone = function(xmlhttp) {
+ try {
+ var xml = xmlhttp.responseXML;
+ if (!xml.documentElement && xmlhttp.responseStream) {
+ xml.load(xmlhttp.responseStream);
+ }
+ f(xml, url);
+ } finally {
+ tl.hideLoadingMessage();
+ }
+ };
+
+ this.showLoadingMessage();
+ window.setTimeout(function() { SimileAjax.XmlHttp.get(url, fError, fDone); }, 0);
+};
+
+Timeline._Impl.prototype.loadJSON = function(url, f) {
+ var tl = this;
+
+ var fError = function(statusText, status, xmlhttp) {
+ alert("Failed to load json data from " + url + "\n" + statusText);
+ tl.hideLoadingMessage();
+ };
+ var fDone = function(xmlhttp) {
+ try {
+ f(eval('(' + xmlhttp.responseText + ')'), url);
+ } finally {
+ tl.hideLoadingMessage();
+ }
+ };
+
+ this.showLoadingMessage();
+ window.setTimeout(function() { SimileAjax.XmlHttp.get(url, fError, fDone); }, 0);
+};
+
+
+//
+// Private functions used by Timeline object functions
+//
+
+Timeline._Impl.prototype._autoWidthScrollListener = function(band) {
+ band.getTimeline()._autoWidthCheck(false);
+};
+
+// called to re-calculate auto width and adjust the overall Timeline div if needed
+Timeline._Impl.prototype._autoWidthCheck = function(okToShrink) {
+ var timeline = this; // this Timeline
+ var immediateChange = timeline._starting;
+ var newWidth = 0;
+
+ function changeTimelineWidth() {
+ var widthStyle = timeline.getWidthStyle();
+ if (immediateChange) {
+ timeline._containerDiv.style[widthStyle] = newWidth + 'px';
+ } else {
+ // animate change
+ timeline._autoResizing = true;
+ var animateParam ={};
+ animateParam[widthStyle] = newWidth + 'px';
+
+ SimileAjax.jQuery(timeline._containerDiv).animate(
+ animateParam, timeline.autoWidthAnimationTime,
+ 'linear', function(){timeline._autoResizing = false;});
+ }
+ }
+
+ function checkTimelineWidth() {
+ var targetWidth = 0; // the new desired width
+ var currentWidth = timeline.getPixelWidth();
+
+ if (timeline._autoResizing) {
+ return; // early return
+ }
+
+ // compute targetWidth
+ for (var i = 0; i < timeline._bands.length; i++) {
+ timeline._bands[i].checkAutoWidth();
+ targetWidth += timeline._bandInfos[i].width;
+ }
+
+ if (targetWidth > currentWidth || okToShrink) {
+ // yes, let's change the size
+ newWidth = targetWidth;
+ changeTimelineWidth();
+ timeline._distributeWidths();
+ }
+ }
+
+ // function's mainline
+ if (!timeline.autoWidth) {
+ return; // early return
+ }
+
+ checkTimelineWidth();
+};
+
+Timeline._Impl.prototype._initialize = function() {
+ var containerDiv = this._containerDiv;
+ var doc = containerDiv.ownerDocument;
+
+ containerDiv.className =
+ containerDiv.className.split(" ").concat("timeline-container").join(" ");
+
+ /*
+ * Set css-class on container div that will define orientation
+ */
+ var orientation = (this.isHorizontal()) ? 'horizontal' : 'vertical'
+ containerDiv.className +=' timeline-'+orientation;
+
+
+ while (containerDiv.firstChild) {
+ containerDiv.removeChild(containerDiv.firstChild);
+ }
+
+ /*
+ * inserting copyright and link to simile
+ */
+ var elmtCopyright = SimileAjax.Graphics.createTranslucentImage(Timeline.urlPrefix + (this.isHorizontal() ? "images/copyright-vertical.png" : "images/copyright.png"));
+ elmtCopyright.className = "timeline-copyright";
+ elmtCopyright.title = "Timeline copyright SIMILE - www.code.google.com/p/simile-widgets/";
+ SimileAjax.DOM.registerEvent(elmtCopyright, "click", function() { window.location = "http://code.google.com/p/simile-widgets/"; });
+ containerDiv.appendChild(elmtCopyright);
+
+ /*
+ * creating bands
+ */
+ this._bands = [];
+ for (var i = 0; i < this._bandInfos.length; i++) {
+ var band = new Timeline._Band(this, this._bandInfos[i], i);
+ this._bands.push(band);
+ }
+ this._distributeWidths();
+
+ /*
+ * sync'ing bands
+ */
+ for (var i = 0; i < this._bandInfos.length; i++) {
+ var bandInfo = this._bandInfos[i];
+ if ("syncWith" in bandInfo) {
+ this._bands[i].setSyncWithBand(
+ this._bands[bandInfo.syncWith],
+ ("highlight" in bandInfo) ? bandInfo.highlight : false
+ );
+ }
+ }
+
+
+ if (this.autoWidth) {
+ for (var i = 0; i < this._bands.length; i++) {
+ this._bands[i].addOnScrollListener(this._autoWidthScrollListener);
+ }
+ }
+
+
+ /*
+ * creating loading UI
+ */
+ var message = SimileAjax.Graphics.createMessageBubble(doc);
+ message.containerDiv.className = "timeline-message-container";
+ containerDiv.appendChild(message.containerDiv);
+
+ message.contentDiv.className = "timeline-message";
+ message.contentDiv.innerHTML = " Loading...";
+
+ this.showLoadingMessage = function() { message.containerDiv.style.display = "block"; };
+ this.hideLoadingMessage = function() { message.containerDiv.style.display = "none"; };
+};
+
+Timeline._Impl.prototype._distributeWidths = function() {
+ var length = this.getPixelLength();
+ var width = this.getPixelWidth();
+ var cumulativeWidth = 0;
+
+ for (var i = 0; i < this._bands.length; i++) {
+ var band = this._bands[i];
+ var bandInfos = this._bandInfos[i];
+ var widthString = bandInfos.width;
+ var bandWidth;
+
+ if (typeof widthString == 'string') {
+ var x = widthString.indexOf("%");
+ if (x > 0) {
+ var percent = parseInt(widthString.substr(0, x));
+ bandWidth = Math.round(percent * width / 100);
+ } else {
+ bandWidth = parseInt(widthString);
+ }
+ } else {
+ // was given an integer
+ bandWidth = widthString;
+ }
+
+ band.setBandShiftAndWidth(cumulativeWidth, bandWidth);
+ band.setViewLength(length);
+
+ cumulativeWidth += bandWidth;
+ }
+};
+
+Timeline._Impl.prototype.shiftOK = function(index, shift) {
+ // Returns true if the proposed shift is ok
+ //
+ // Positive shift means going back in time
+ var going_back = shift > 0,
+ going_forward = shift < 0;
+
+ // Is there an edge?
+ if ((going_back && this.timeline_start == null) ||
+ (going_forward && this.timeline_stop == null) ||
+ (shift == 0)) {
+ return (true); // early return
+ }
+
+ // If any of the bands has noted that it is changing the others,
+ // then this shift is a secondary shift in reaction to the real shift,
+ // which already happened. In such cases, ignore it. (The issue is
+ // that a positive original shift can cause a negative secondary shift,
+ // as the bands adjust.)
+ var secondary_shift = false;
+ for (var i = 0; i < this._bands.length && !secondary_shift; i++) {
+ secondary_shift = this._bands[i].busy();
+ }
+ if (secondary_shift) {
+ return(true); // early return
+ }
+
+ // If we are already at an edge, then don't even think about going any further
+ if ((going_back && this.timeline_at_start) ||
+ (going_forward && this.timeline_at_stop)) {
+ return (false); // early return
+ }
+
+ // Need to check all the bands
+ var ok = false; // return value
+ // If any of the bands will be or are showing an ok date, then let the shift proceed.
+ for (var i = 0; i < this._bands.length && !ok; i++) {
+ var band = this._bands[i];
+ if (going_back) {
+ ok = (i == index ? band.getMinVisibleDateAfterDelta(shift) : band.getMinVisibleDate())
+ >= this.timeline_start;
+ } else {
+ ok = (i == index ? band.getMaxVisibleDateAfterDelta(shift) : band.getMaxVisibleDate())
+ <= this.timeline_stop;
+ }
+ }
+
+ // process results
+ if (going_back) {
+ this.timeline_at_start = !ok;
+ this.timeline_at_stop = false;
+ } else {
+ this.timeline_at_stop = !ok;
+ this.timeline_at_start = false;
+ }
+ // This is where you could have an effect once per hitting an
+ // edge of the Timeline. Eg jitter the Timeline
+ //if (!ok) {
+ //alert(going_back ? "At beginning" : "At end");
+ //}
+ return (ok);
+};
+
+Timeline._Impl.prototype.zoom = function (zoomIn, x, y, target) {
+ var matcher = new RegExp("^timeline-band-([0-9]+)$");
+ var bandIndex = null;
+
+ var result = matcher.exec(target.id);
+ if (result) {
+ bandIndex = parseInt(result[1]);
+ }
+
+ if (bandIndex != null) {
+ this._bands[bandIndex].zoom(zoomIn, x, y, target);
+ }
+
+ this.paint();
+};
+
+/*=================================================
+ *
+ * Coding standards:
+ *
+ * We aim towards Douglas Crockford's Javascript conventions.
+ * See: http://javascript.crockford.com/code.html
+ * See also: http://www.crockford.com/javascript/javascript.html
+ *
+ * That said, this JS code was written before some recent JS
+ * support libraries became widely used or available.
+ * In particular, the _ character is used to indicate a class function or
+ * variable that should be considered private to the class.
+ *
+ * The code mostly uses accessor methods for getting/setting the private
+ * class variables.
+ *
+ * Over time, we'd like to formalize the convention by using support libraries
+ * which enforce privacy in objects.
+ *
+ * We also want to use jslint: http://www.jslint.com/
+ *
+ *
+ *==================================================
+ */
+
+
+
+/*==================================================
+ * Band
+ *==================================================
+ */
+Timeline._Band = function(timeline, bandInfo, index) {
+ // Set up the band's object
+
+ // Munge params: If autoWidth is on for the Timeline, then ensure that
+ // bandInfo.width is an integer
+ if (timeline.autoWidth && typeof bandInfo.width == 'string') {
+ bandInfo.width = bandInfo.width.indexOf("%") > -1 ? 0 : parseInt(bandInfo.width);
+ }
+
+ this._timeline = timeline;
+ this._bandInfo = bandInfo;
+
+ this._index = index;
+
+ this._locale = ("locale" in bandInfo) ? bandInfo.locale : Timeline.getDefaultLocale();
+ this._timeZone = ("timeZone" in bandInfo) ? bandInfo.timeZone : 0;
+ this._labeller = ("labeller" in bandInfo) ? bandInfo.labeller :
+ (("createLabeller" in timeline.getUnit()) ?
+ timeline.getUnit().createLabeller(this._locale, this._timeZone) :
+ new Timeline.GregorianDateLabeller(this._locale, this._timeZone));
+ this._theme = bandInfo.theme;
+ this._zoomIndex = ("zoomIndex" in bandInfo) ? bandInfo.zoomIndex : 0;
+ this._zoomSteps = ("zoomSteps" in bandInfo) ? bandInfo.zoomSteps : null;
+
+ this._dragging = false;
+ this._changing = false;
+ this._originalScrollSpeed = 5; // pixels
+ this._scrollSpeed = this._originalScrollSpeed;
+ this._onScrollListeners = [];
+
+ var b = this;
+ this._syncWithBand = null;
+ this._syncWithBandHandler = function(band) {
+ b._onHighlightBandScroll();
+ };
+ this._selectorListener = function(band) {
+ b._onHighlightBandScroll();
+ };
+
+ /*
+ * Install a textbox to capture keyboard events
+ */
+ var inputDiv = this._timeline.getDocument().createElement("div");
+ inputDiv.className = "timeline-band-input";
+ this._timeline.addDiv(inputDiv);
+
+ this._keyboardInput = document.createElement("input");
+ this._keyboardInput.type = "text";
+ inputDiv.appendChild(this._keyboardInput);
+ SimileAjax.DOM.registerEventWithObject(this._keyboardInput, "keydown", this, "_onKeyDown");
+ SimileAjax.DOM.registerEventWithObject(this._keyboardInput, "keyup", this, "_onKeyUp");
+
+ /*
+ * The band's outer most div that slides with respect to the timeline's div
+ */
+ this._div = this._timeline.getDocument().createElement("div");
+ this._div.id = "timeline-band-" + index;
+ this._div.className = "timeline-band timeline-band-" + index;
+ this._timeline.addDiv(this._div);
+
+ SimileAjax.DOM.registerEventWithObject(this._div, "mousedown", this, "_onMouseDown");
+ SimileAjax.DOM.registerEventWithObject(this._div, "mousemove", this, "_onMouseMove");
+ SimileAjax.DOM.registerEventWithObject(this._div, "mouseup", this, "_onMouseUp");
+ SimileAjax.DOM.registerEventWithObject(this._div, "mouseout", this, "_onMouseOut");
+ SimileAjax.DOM.registerEventWithObject(this._div, "dblclick", this, "_onDblClick");
+
+ var mouseWheel = this._theme!= null ? this._theme.mouseWheel : 'scroll'; // theme is not always defined
+ if (mouseWheel === 'zoom' || mouseWheel === 'scroll' || this._zoomSteps) {
+ // capture mouse scroll
+ if (SimileAjax.Platform.browser.isFirefox) {
+ SimileAjax.DOM.registerEventWithObject(this._div, "DOMMouseScroll", this, "_onMouseScroll");
+ } else {
+ SimileAjax.DOM.registerEventWithObject(this._div, "mousewheel", this, "_onMouseScroll");
+ }
+ }
+
+ /*
+ * The inner div that contains layers
+ */
+ this._innerDiv = this._timeline.getDocument().createElement("div");
+ this._innerDiv.className = "timeline-band-inner";
+ this._div.appendChild(this._innerDiv);
+
+ /*
+ * Initialize parts of the band
+ */
+ this._ether = bandInfo.ether;
+ bandInfo.ether.initialize(this, timeline);
+
+ this._etherPainter = bandInfo.etherPainter;
+ bandInfo.etherPainter.initialize(this, timeline);
+
+ this._eventSource = bandInfo.eventSource;
+ if (this._eventSource) {
+ this._eventListener = {
+ onAddMany: function() { b._onAddMany(); },
+ onClear: function() { b._onClear(); }
+ }
+ this._eventSource.addListener(this._eventListener);
+ }
+
+ this._eventPainter = bandInfo.eventPainter;
+ this._eventTracksNeeded = 0; // set by painter via updateEventTrackInfo
+ this._eventTrackIncrement = 0;
+ bandInfo.eventPainter.initialize(this, timeline);
+
+ this._decorators = ("decorators" in bandInfo) ? bandInfo.decorators : [];
+ for (var i = 0; i < this._decorators.length; i++) {
+ this._decorators[i].initialize(this, timeline);
+ }
+};
+
+Timeline._Band.SCROLL_MULTIPLES = 5;
+
+Timeline._Band.prototype.dispose = function() {
+ this.closeBubble();
+
+ if (this._eventSource) {
+ this._eventSource.removeListener(this._eventListener);
+ this._eventListener = null;
+ this._eventSource = null;
+ }
+
+ this._timeline = null;
+ this._bandInfo = null;
+
+ this._labeller = null;
+ this._ether = null;
+ this._etherPainter = null;
+ this._eventPainter = null;
+ this._decorators = null;
+
+ this._onScrollListeners = null;
+ this._syncWithBandHandler = null;
+ this._selectorListener = null;
+
+ this._div = null;
+ this._innerDiv = null;
+ this._keyboardInput = null;
+};
+
+Timeline._Band.prototype.addOnScrollListener = function(listener) {
+ this._onScrollListeners.push(listener);
+};
+
+Timeline._Band.prototype.removeOnScrollListener = function(listener) {
+ for (var i = 0; i < this._onScrollListeners.length; i++) {
+ if (this._onScrollListeners[i] == listener) {
+ this._onScrollListeners.splice(i, 1);
+ break;
+ }
+ }
+};
+
+Timeline._Band.prototype.setSyncWithBand = function(band, highlight) {
+ if (this._syncWithBand) {
+ this._syncWithBand.removeOnScrollListener(this._syncWithBandHandler);
+ }
+
+ this._syncWithBand = band;
+ this._syncWithBand.addOnScrollListener(this._syncWithBandHandler);
+ this._highlight = highlight;
+ this._positionHighlight();
+};
+
+Timeline._Band.prototype.getLocale = function() {
+ return this._locale;
+};
+
+Timeline._Band.prototype.getTimeZone = function() {
+ return this._timeZone;
+};
+
+Timeline._Band.prototype.getLabeller = function() {
+ return this._labeller;
+};
+
+Timeline._Band.prototype.getIndex = function() {
+ return this._index;
+};
+
+Timeline._Band.prototype.getEther = function() {
+ return this._ether;
+};
+
+Timeline._Band.prototype.getEtherPainter = function() {
+ return this._etherPainter;
+};
+
+Timeline._Band.prototype.getEventSource = function() {
+ return this._eventSource;
+};
+
+Timeline._Band.prototype.getEventPainter = function() {
+ return this._eventPainter;
+};
+
+Timeline._Band.prototype.getTimeline = function() {
+ return this._timeline;
+};
+
+// Autowidth support
+Timeline._Band.prototype.updateEventTrackInfo = function(tracks, increment) {
+ this._eventTrackIncrement = increment; // doesn't vary for a specific band
+
+ if (tracks > this._eventTracksNeeded) {
+ this._eventTracksNeeded = tracks;
+ }
+};
+
+// Autowidth support
+Timeline._Band.prototype.checkAutoWidth = function() {
+ // if a new (larger) width is needed by the band
+ // then: a) updates the band's bandInfo.width
+ //
+ // desiredWidth for the band is
+ // (number of tracks + margin) * track increment
+ if (! this._timeline.autoWidth) {
+ return; // early return
+ }
+
+ var overviewBand = this._eventPainter.getType() == 'overview';
+ var margin = overviewBand ?
+ this._theme.event.overviewTrack.autoWidthMargin :
+ this._theme.event.track.autoWidthMargin;
+ var desiredWidth = Math.ceil((this._eventTracksNeeded + margin) *
+ this._eventTrackIncrement);
+ // add offset amount (additional margin)
+ desiredWidth += overviewBand ? this._theme.event.overviewTrack.offset :
+ this._theme.event.track.offset;
+ var bandInfo = this._bandInfo;
+
+ if (desiredWidth != bandInfo.width) {
+ bandInfo.width = desiredWidth;
+ }
+};
+
+Timeline._Band.prototype.layout = function() {
+ this.paint();
+};
+
+Timeline._Band.prototype.paint = function() {
+ this._etherPainter.paint();
+ this._paintDecorators();
+ this._paintEvents();
+};
+
+Timeline._Band.prototype.softLayout = function() {
+ this.softPaint();
+};
+
+Timeline._Band.prototype.softPaint = function() {
+ this._etherPainter.softPaint();
+ this._softPaintDecorators();
+ this._softPaintEvents();
+};
+
+Timeline._Band.prototype.setBandShiftAndWidth = function(shift, width) {
+ var inputDiv = this._keyboardInput.parentNode;
+ var middle = shift + Math.floor(width / 2);
+ if (this._timeline.isHorizontal()) {
+ this._div.style.top = shift + "px";
+ this._div.style.height = width + "px";
+
+ inputDiv.style.top = middle + "px";
+ inputDiv.style.left = "-1em";
+ } else {
+ this._div.style.left = shift + "px";
+ this._div.style.width = width + "px";
+
+ inputDiv.style.left = middle + "px";
+ inputDiv.style.top = "-1em";
+ }
+};
+
+Timeline._Band.prototype.getViewWidth = function() {
+ if (this._timeline.isHorizontal()) {
+ return this._div.offsetHeight;
+ } else {
+ return this._div.offsetWidth;
+ }
+};
+
+Timeline._Band.prototype.setViewLength = function(length) {
+ this._viewLength = length;
+ this._recenterDiv();
+ this._onChanging();
+};
+
+Timeline._Band.prototype.getViewLength = function() {
+ return this._viewLength;
+};
+
+Timeline._Band.prototype.getTotalViewLength = function() {
+ return Timeline._Band.SCROLL_MULTIPLES * this._viewLength;
+};
+
+Timeline._Band.prototype.getViewOffset = function() {
+ return this._viewOffset;
+};
+
+Timeline._Band.prototype.getMinDate = function() {
+ return this._ether.pixelOffsetToDate(this._viewOffset);
+};
+
+Timeline._Band.prototype.getMaxDate = function() {
+ return this._ether.pixelOffsetToDate(this._viewOffset + Timeline._Band.SCROLL_MULTIPLES * this._viewLength);
+};
+
+Timeline._Band.prototype.getMinVisibleDate = function() {
+ return this._ether.pixelOffsetToDate(0);
+};
+
+Timeline._Band.prototype.getMinVisibleDateAfterDelta = function(delta) {
+ return this._ether.pixelOffsetToDate(delta);
+};
+
+Timeline._Band.prototype.getMaxVisibleDate = function() {
+ // Max date currently visible on band
+ return this._ether.pixelOffsetToDate(this._viewLength);
+};
+
+Timeline._Band.prototype.getMaxVisibleDateAfterDelta = function(delta) {
+ // Max date visible on band after delta px view change is applied
+ return this._ether.pixelOffsetToDate(this._viewLength + delta);
+};
+
+Timeline._Band.prototype.getCenterVisibleDate = function() {
+ return this._ether.pixelOffsetToDate(this._viewLength / 2);
+};
+
+Timeline._Band.prototype.setMinVisibleDate = function(date) {
+ if (!this._changing) {
+ this._moveEther(Math.round(-this._ether.dateToPixelOffset(date)));
+ }
+};
+
+Timeline._Band.prototype.setMaxVisibleDate = function(date) {
+ if (!this._changing) {
+ this._moveEther(Math.round(this._viewLength - this._ether.dateToPixelOffset(date)));
+ }
+};
+
+Timeline._Band.prototype.setCenterVisibleDate = function(date) {
+ if (!this._changing) {
+ this._moveEther(Math.round(this._viewLength / 2 - this._ether.dateToPixelOffset(date)));
+ }
+};
+
+Timeline._Band.prototype.dateToPixelOffset = function(date) {
+ return this._ether.dateToPixelOffset(date) - this._viewOffset;
+};
+
+Timeline._Band.prototype.pixelOffsetToDate = function(pixels) {
+ return this._ether.pixelOffsetToDate(pixels + this._viewOffset);
+};
+
+Timeline._Band.prototype.createLayerDiv = function(zIndex, className) {
+ var div = this._timeline.getDocument().createElement("div");
+ div.className = "timeline-band-layer" + (typeof className == "string" ? (" " + className) : "");
+ div.style.zIndex = zIndex;
+ this._innerDiv.appendChild(div);
+
+ var innerDiv = this._timeline.getDocument().createElement("div");
+ innerDiv.className = "timeline-band-layer-inner";
+ if (SimileAjax.Platform.browser.isIE) {
+ innerDiv.style.cursor = "move";
+ } else {
+ innerDiv.style.cursor = "-moz-grab";
+ }
+ div.appendChild(innerDiv);
+
+ return innerDiv;
+};
+
+Timeline._Band.prototype.removeLayerDiv = function(div) {
+ this._innerDiv.removeChild(div.parentNode);
+};
+
+Timeline._Band.prototype.scrollToCenter = function(date, f) {
+ var pixelOffset = this._ether.dateToPixelOffset(date);
+ if (pixelOffset < -this._viewLength / 2) {
+ this.setCenterVisibleDate(this.pixelOffsetToDate(pixelOffset + this._viewLength));
+ } else if (pixelOffset > 3 * this._viewLength / 2) {
+ this.setCenterVisibleDate(this.pixelOffsetToDate(pixelOffset - this._viewLength));
+ }
+ this._autoScroll(Math.round(this._viewLength / 2 - this._ether.dateToPixelOffset(date)), f);
+};
+
+Timeline._Band.prototype.showBubbleForEvent = function(eventID) {
+ var evt = this.getEventSource().getEvent(eventID);
+ if (evt) {
+ var self = this;
+ this.scrollToCenter(evt.getStart(), function() {
+ self._eventPainter.showBubble(evt);
+ });
+ }
+};
+
+Timeline._Band.prototype.zoom = function(zoomIn, x, y, target) {
+ if (!this._zoomSteps) {
+ // zoom disabled
+ return;
+ }
+
+ // shift the x value by our offset
+ x += this._viewOffset;
+
+ var zoomDate = this._ether.pixelOffsetToDate(x);
+ var netIntervalChange = this._ether.zoom(zoomIn);
+ this._etherPainter.zoom(netIntervalChange);
+
+ // shift our zoom date to the far left
+ this._moveEther(Math.round(-this._ether.dateToPixelOffset(zoomDate)));
+ // then shift it back to where the mouse was
+ this._moveEther(x);
+};
+
+Timeline._Band.prototype._onMouseDown = function(innerFrame, evt, target) {
+ this.closeBubble();
+
+ this._dragging = true;
+ this._dragX = evt.clientX;
+ this._dragY = evt.clientY;
+};
+
+Timeline._Band.prototype._onMouseMove = function(innerFrame, evt, target) {
+ if (this._dragging) {
+ var diffX = evt.clientX - this._dragX;
+ var diffY = evt.clientY - this._dragY;
+
+ this._dragX = evt.clientX;
+ this._dragY = evt.clientY;
+
+ this._moveEther(this._timeline.isHorizontal() ? diffX : diffY);
+ this._positionHighlight();
+ }
+};
+
+Timeline._Band.prototype._onMouseUp = function(innerFrame, evt, target) {
+ this._dragging = false;
+ this._keyboardInput.focus();
+};
+
+Timeline._Band.prototype._onMouseOut = function(innerFrame, evt, target) {
+ var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
+ coords.x += this._viewOffset;
+ if (coords.x < 0 || coords.x > innerFrame.offsetWidth ||
+ coords.y < 0 || coords.y > innerFrame.offsetHeight) {
+ this._dragging = false;
+ }
+};
+
+Timeline._Band.prototype._onMouseScroll = function(innerFrame, evt, target) {
+ var now = new Date();
+ now = now.getTime();
+
+ if (!this._lastScrollTime || ((now - this._lastScrollTime) > 50)) {
+ // limit 1 scroll per 200ms due to FF3 sending multiple events back to back
+ this._lastScrollTime = now;
+
+ var delta = 0;
+ if (evt.wheelDelta) {
+ delta = evt.wheelDelta/120;
+ } else if (evt.detail) {
+ delta = -evt.detail/3;
+ }
+
+ // either scroll or zoom
+ var mouseWheel = this._theme.mouseWheel;
+
+ if (this._zoomSteps || mouseWheel === 'zoom') {
+ var loc = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
+ if (delta != 0) {
+ var zoomIn;
+ if (delta > 0)
+ zoomIn = true;
+ if (delta < 0)
+ zoomIn = false;
+ // call zoom on the timeline so we could zoom multiple bands if desired
+ this._timeline.zoom(zoomIn, loc.x, loc.y, innerFrame);
+ }
+ }
+ else if (mouseWheel === 'scroll') {
+ var move_amt = 50 * (delta < 0 ? -1 : 1);
+ this._moveEther(move_amt);
+ }
+ }
+
+ // prevent bubble
+ if (evt.stopPropagation) {
+ evt.stopPropagation();
+ }
+ evt.cancelBubble = true;
+
+ // prevent the default action
+ if (evt.preventDefault) {
+ evt.preventDefault();
+ }
+ evt.returnValue = false;
+};
+
+Timeline._Band.prototype._onDblClick = function(innerFrame, evt, target) {
+ var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt, innerFrame);
+ var distance = coords.x - (this._viewLength / 2 - this._viewOffset);
+
+ this._autoScroll(-distance);
+};
+
+Timeline._Band.prototype._onKeyDown = function(keyboardInput, evt, target) {
+ if (!this._dragging) {
+ switch (evt.keyCode) {
+ case 27: // ESC
+ break;
+ case 37: // left arrow
+ case 38: // up arrow
+ this._scrollSpeed = Math.min(50, Math.abs(this._scrollSpeed * 1.05));
+ this._moveEther(this._scrollSpeed);
+ break;
+ case 39: // right arrow
+ case 40: // down arrow
+ this._scrollSpeed = -Math.min(50, Math.abs(this._scrollSpeed * 1.05));
+ this._moveEther(this._scrollSpeed);
+ break;
+ default:
+ return true;
+ }
+ this.closeBubble();
+
+ SimileAjax.DOM.cancelEvent(evt);
+ return false;
+ }
+ return true;
+};
+
+Timeline._Band.prototype._onKeyUp = function(keyboardInput, evt, target) {
+ if (!this._dragging) {
+ this._scrollSpeed = this._originalScrollSpeed;
+
+ switch (evt.keyCode) {
+ case 35: // end
+ this.setCenterVisibleDate(this._eventSource.getLatestDate());
+ break;
+ case 36: // home
+ this.setCenterVisibleDate(this._eventSource.getEarliestDate());
+ break;
+ case 33: // page up
+ this._autoScroll(this._timeline.getPixelLength());
+ break;
+ case 34: // page down
+ this._autoScroll(-this._timeline.getPixelLength());
+ break;
+ default:
+ return true;
+ }
+
+ this.closeBubble();
+
+ SimileAjax.DOM.cancelEvent(evt);
+ return false;
+ }
+ return true;
+};
+
+Timeline._Band.prototype._autoScroll = function(distance, f) {
+ var b = this;
+ var a = SimileAjax.Graphics.createAnimation(
+ function(abs, diff) {
+ b._moveEther(diff);
+ },
+ 0,
+ distance,
+ 1000,
+ f
+ );
+ a.run();
+};
+
+Timeline._Band.prototype._moveEther = function(shift) {
+ this.closeBubble();
+
+ // A positive shift means back in time
+ // Check that we're not moving beyond Timeline's limits
+ if (!this._timeline.shiftOK(this._index, shift)) {
+ return; // early return
+ }
+
+ this._viewOffset += shift;
+ this._ether.shiftPixels(-shift);
+ if (this._timeline.isHorizontal()) {
+ this._div.style.left = this._viewOffset + "px";
+ } else {
+ this._div.style.top = this._viewOffset + "px";
+ }
+
+ if (this._viewOffset > -this._viewLength * 0.5 ||
+ this._viewOffset < -this._viewLength * (Timeline._Band.SCROLL_MULTIPLES - 1.5)) {
+
+ this._recenterDiv();
+ } else {
+ this.softLayout();
+ }
+
+ this._onChanging();
+}
+
+Timeline._Band.prototype._onChanging = function() {
+ this._changing = true;
+
+ this._fireOnScroll();
+ this._setSyncWithBandDate();
+
+ this._changing = false;
+};
+
+Timeline._Band.prototype.busy = function() {
+ // Is this band busy changing other bands?
+ return(this._changing);
+};
+
+Timeline._Band.prototype._fireOnScroll = function() {
+ for (var i = 0; i < this._onScrollListeners.length; i++) {
+ this._onScrollListeners[i](this);
+ }
+};
+
+Timeline._Band.prototype._setSyncWithBandDate = function() {
+ if (this._syncWithBand) {
+ var centerDate = this._ether.pixelOffsetToDate(this.getViewLength() / 2);
+ this._syncWithBand.setCenterVisibleDate(centerDate);
+ }
+};
+
+Timeline._Band.prototype._onHighlightBandScroll = function() {
+ if (this._syncWithBand) {
+ var centerDate = this._syncWithBand.getCenterVisibleDate();
+ var centerPixelOffset = this._ether.dateToPixelOffset(centerDate);
+
+ this._moveEther(Math.round(this._viewLength / 2 - centerPixelOffset));
+
+ if (this._highlight) {
+ this._etherPainter.setHighlight(
+ this._syncWithBand.getMinVisibleDate(),
+ this._syncWithBand.getMaxVisibleDate());
+ }
+ }
+};
+
+Timeline._Band.prototype._onAddMany = function() {
+ this._paintEvents();
+};
+
+Timeline._Band.prototype._onClear = function() {
+ this._paintEvents();
+};
+
+Timeline._Band.prototype._positionHighlight = function() {
+ if (this._syncWithBand) {
+ var startDate = this._syncWithBand.getMinVisibleDate();
+ var endDate = this._syncWithBand.getMaxVisibleDate();
+
+ if (this._highlight) {
+ this._etherPainter.setHighlight(startDate, endDate);
+ }
+ }
+};
+
+Timeline._Band.prototype._recenterDiv = function() {
+ this._viewOffset = -this._viewLength * (Timeline._Band.SCROLL_MULTIPLES - 1) / 2;
+ if (this._timeline.isHorizontal()) {
+ this._div.style.left = this._viewOffset + "px";
+ this._div.style.width = (Timeline._Band.SCROLL_MULTIPLES * this._viewLength) + "px";
+ } else {
+ this._div.style.top = this._viewOffset + "px";
+ this._div.style.height = (Timeline._Band.SCROLL_MULTIPLES * this._viewLength) + "px";
+ }
+ this.layout();
+};
+
+Timeline._Band.prototype._paintEvents = function() {
+ this._eventPainter.paint();
+};
+
+Timeline._Band.prototype._softPaintEvents = function() {
+ this._eventPainter.softPaint();
+};
+
+Timeline._Band.prototype._paintDecorators = function() {
+ for (var i = 0; i < this._decorators.length; i++) {
+ this._decorators[i].paint();
+ }
+};
+
+Timeline._Band.prototype._softPaintDecorators = function() {
+ for (var i = 0; i < this._decorators.length; i++) {
+ this._decorators[i].softPaint();
+ }
+};
+
+Timeline._Band.prototype.closeBubble = function() {
+ SimileAjax.WindowManager.cancelPopups();
+};
+/*==================================================
+ * Classic Theme
+ *==================================================
+ */
+
+
+
+Timeline.ClassicTheme = new Object();
+
+Timeline.ClassicTheme.implementations = [];
+
+Timeline.ClassicTheme.create = function(locale) {
+ if (locale == null) {
+ locale = Timeline.getDefaultLocale();
+ }
+
+ var f = Timeline.ClassicTheme.implementations[locale];
+ if (f == null) {
+ f = Timeline.ClassicTheme._Impl;
+ }
+ return new f();
+};
+
+Timeline.ClassicTheme._Impl = function() {
+ this.firstDayOfWeek = 0; // Sunday
+
+ // Note: Many styles previously set here are now set using CSS
+ // The comments indicate settings controlled by CSS, not
+ // lines to be un-commented.
+ //
+ //
+ // Attributes autoWidth, autoWidthAnimationTime, timeline_start
+ // and timeline_stop must be set on the first band's theme.
+ // The other attributes can be set differently for each
+ // band by using different themes for the bands.
+ this.autoWidth = false; // Should the Timeline automatically grow itself, as
+ // needed when too many events for the available width
+ // are painted on the visible part of the Timeline?
+ this.autoWidthAnimationTime = 500; // mSec
+ this.timeline_start = null; // Setting a date, eg new Date(Date.UTC(2008,0,17,20,00,00,0)) will prevent the
+ // Timeline from being moved to anytime before the date.
+ this.timeline_stop = null; // Use for setting a maximum date. The Timeline will not be able
+ // to be moved to anytime after this date.
+ this.ether = {
+ backgroundColors: [
+ // "#EEE",
+ // "#DDD",
+ // "#CCC",
+ // "#AAA"
+ ],
+ // highlightColor: "white",
+ highlightOpacity: 50,
+ interval: {
+ line: {
+ show: true,
+ opacity: 25
+ // color: "#aaa",
+ },
+ weekend: {
+ opacity: 30
+ // color: "#FFFFE0",
+ },
+ marker: {
+ hAlign: "Bottom",
+ vAlign: "Right"
+ /*
+ hBottomStyler: function(elmt) {
+ elmt.className = "timeline-ether-marker-bottom";
+ },
+ hBottomEmphasizedStyler: function(elmt) {
+ elmt.className = "timeline-ether-marker-bottom-emphasized";
+ },
+ hTopStyler: function(elmt) {
+ elmt.className = "timeline-ether-marker-top";
+ },
+ hTopEmphasizedStyler: function(elmt) {
+ elmt.className = "timeline-ether-marker-top-emphasized";
+ },
+ */
+
+
+ /*
+ vRightStyler: function(elmt) {
+ elmt.className = "timeline-ether-marker-right";
+ },
+ vRightEmphasizedStyler: function(elmt) {
+ elmt.className = "timeline-ether-marker-right-emphasized";
+ },
+ vLeftStyler: function(elmt) {
+ elmt.className = "timeline-ether-marker-left";
+ },
+ vLeftEmphasizedStyler:function(elmt) {
+ elmt.className = "timeline-ether-marker-left-emphasized";
+ }
+ */
+ }
+ }
+ };
+
+ this.event = {
+ track: {
+ height: 10, // px. You will need to change the track
+ // height if you change the tape height.
+ gap: 2, // px. Gap between tracks
+ offset: 2, // px. top margin above tapes
+ autoWidthMargin: 1.5
+ /* autoWidthMargin is only used if autoWidth (see above) is true.
+ The autoWidthMargin setting is used to set how close the bottom of the
+ lowest track is to the edge of the band's div. The units are total track
+ width (tape + label + gap). A min of 0.5 is suggested. Use this setting to
+ move the bottom track's tapes above the axis markers, if needed for your
+ Timeline.
+ */
+ },
+ overviewTrack: {
+ offset: 20, // px -- top margin above tapes
+ tickHeight: 6, // px
+ height: 2, // px
+ gap: 1, // px
+ autoWidthMargin: 5 // This attribute is only used if autoWidth (see above) is true.
+ },
+ tape: {
+ height: 4 // px. For thicker tapes, remember to change track height too.
+ },
+ instant: {
+ icon: Timeline.urlPrefix + "images/dull-blue-circle.png",
+ // default icon. Icon can also be specified per event
+ iconWidth: 10,
+ iconHeight: 10,
+ impreciseOpacity: 20, // opacity of the tape when durationEvent is false
+ impreciseIconMargin: 3 // A tape and an icon are painted for imprecise instant
+ // events. This attribute is the margin between the
+ // bottom of the tape and the top of the icon in that
+ // case.
+ // color: "#58A0DC",
+ // impreciseColor: "#58A0DC",
+ },
+ duration: {
+ impreciseOpacity: 20 // tape opacity for imprecise part of duration events
+ // color: "#58A0DC",
+ // impreciseColor: "#58A0DC",
+ },
+ label: {
+ backgroundOpacity: 50,// only used in detailed painter
+ offsetFromLine: 3 // px left margin amount from icon's right edge
+ // backgroundColor: "white",
+ // lineColor: "#58A0DC",
+ },
+ highlightColors: [ // Use with getEventPainter().setHighlightMatcher
+ // See webapp/examples/examples.js
+ "#FFFF00",
+ "#FFC000",
+ "#FF0000",
+ "#0000FF"
+ ],
+ highlightLabelBackground: false, // When highlighting an event, also change the event's label background?
+ bubble: {
+ width: 250, // px
+ maxHeight: 0, // px Maximum height of bubbles. 0 means no max height.
+ // scrollbar will be added for taller bubbles
+ titleStyler: function(elmt) {
+ elmt.className = "timeline-event-bubble-title";
+ },
+ bodyStyler: function(elmt) {
+ elmt.className = "timeline-event-bubble-body";
+ },
+ imageStyler: function(elmt) {
+ elmt.className = "timeline-event-bubble-image";
+ },
+ wikiStyler: function(elmt) {
+ elmt.className = "timeline-event-bubble-wiki";
+ },
+ timeStyler: function(elmt) {
+ elmt.className = "timeline-event-bubble-time";
+ }
+ }
+ };
+
+ this.mouseWheel = 'scroll'; // 'default', 'zoom', 'scroll'
+};
+/*==================================================
+ * An "ether" is a object that maps date/time to pixel coordinates.
+ *==================================================
+ */
+
+/*==================================================
+ * Linear Ether
+ *==================================================
+ */
+
+Timeline.LinearEther = function(params) {
+ this._params = params;
+ this._interval = params.interval;
+ this._pixelsPerInterval = params.pixelsPerInterval;
+};
+
+Timeline.LinearEther.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+ this._unit = timeline.getUnit();
+
+ if ("startsOn" in this._params) {
+ this._start = this._unit.parseFromObject(this._params.startsOn);
+ } else if ("endsOn" in this._params) {
+ this._start = this._unit.parseFromObject(this._params.endsOn);
+ this.shiftPixels(-this._timeline.getPixelLength());
+ } else if ("centersOn" in this._params) {
+ this._start = this._unit.parseFromObject(this._params.centersOn);
+ this.shiftPixels(-this._timeline.getPixelLength() / 2);
+ } else {
+ this._start = this._unit.makeDefaultValue();
+ this.shiftPixels(-this._timeline.getPixelLength() / 2);
+ }
+};
+
+Timeline.LinearEther.prototype.setDate = function(date) {
+ this._start = this._unit.cloneValue(date);
+};
+
+Timeline.LinearEther.prototype.shiftPixels = function(pixels) {
+ var numeric = this._interval * pixels / this._pixelsPerInterval;
+ this._start = this._unit.change(this._start, numeric);
+};
+
+Timeline.LinearEther.prototype.dateToPixelOffset = function(date) {
+ var numeric = this._unit.compare(date, this._start);
+ return this._pixelsPerInterval * numeric / this._interval;
+};
+
+Timeline.LinearEther.prototype.pixelOffsetToDate = function(pixels) {
+ var numeric = pixels * this._interval / this._pixelsPerInterval;
+ return this._unit.change(this._start, numeric);
+};
+
+Timeline.LinearEther.prototype.zoom = function(zoomIn) {
+ var netIntervalChange = 0;
+ var currentZoomIndex = this._band._zoomIndex;
+ var newZoomIndex = currentZoomIndex;
+
+ if (zoomIn && (currentZoomIndex > 0)) {
+ newZoomIndex = currentZoomIndex - 1;
+ }
+
+ if (!zoomIn && (currentZoomIndex < (this._band._zoomSteps.length - 1))) {
+ newZoomIndex = currentZoomIndex + 1;
+ }
+
+ this._band._zoomIndex = newZoomIndex;
+ this._interval =
+ SimileAjax.DateTime.gregorianUnitLengths[this._band._zoomSteps[newZoomIndex].unit];
+ this._pixelsPerInterval = this._band._zoomSteps[newZoomIndex].pixelsPerInterval;
+ netIntervalChange = this._band._zoomSteps[newZoomIndex].unit -
+ this._band._zoomSteps[currentZoomIndex].unit;
+
+ return netIntervalChange;
+};
+
+
+/*==================================================
+ * Hot Zone Ether
+ *==================================================
+ */
+
+Timeline.HotZoneEther = function(params) {
+ this._params = params;
+ this._interval = params.interval;
+ this._pixelsPerInterval = params.pixelsPerInterval;
+ this._theme = params.theme;
+};
+
+Timeline.HotZoneEther.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+ this._unit = timeline.getUnit();
+
+ this._zones = [{
+ startTime: Number.NEGATIVE_INFINITY,
+ endTime: Number.POSITIVE_INFINITY,
+ magnify: 1
+ }];
+ var params = this._params;
+ for (var i = 0; i < params.zones.length; i++) {
+ var zone = params.zones[i];
+ var zoneStart = this._unit.parseFromObject(zone.start);
+ var zoneEnd = this._unit.parseFromObject(zone.end);
+
+ for (var j = 0; j < this._zones.length && this._unit.compare(zoneEnd, zoneStart) > 0; j++) {
+ var zone2 = this._zones[j];
+
+ if (this._unit.compare(zoneStart, zone2.endTime) < 0) {
+ if (this._unit.compare(zoneStart, zone2.startTime) > 0) {
+ this._zones.splice(j, 0, {
+ startTime: zone2.startTime,
+ endTime: zoneStart,
+ magnify: zone2.magnify
+ });
+ j++;
+
+ zone2.startTime = zoneStart;
+ }
+
+ if (this._unit.compare(zoneEnd, zone2.endTime) < 0) {
+ this._zones.splice(j, 0, {
+ startTime: zoneStart,
+ endTime: zoneEnd,
+ magnify: zone.magnify * zone2.magnify
+ });
+ j++;
+
+ zone2.startTime = zoneEnd;
+ zoneStart = zoneEnd;
+ } else {
+ zone2.magnify *= zone.magnify;
+ zoneStart = zone2.endTime;
+ }
+ } // else, try the next existing zone
+ }
+ }
+
+ if ("startsOn" in this._params) {
+ this._start = this._unit.parseFromObject(this._params.startsOn);
+ } else if ("endsOn" in this._params) {
+ this._start = this._unit.parseFromObject(this._params.endsOn);
+ this.shiftPixels(-this._timeline.getPixelLength());
+ } else if ("centersOn" in this._params) {
+ this._start = this._unit.parseFromObject(this._params.centersOn);
+ this.shiftPixels(-this._timeline.getPixelLength() / 2);
+ } else {
+ this._start = this._unit.makeDefaultValue();
+ this.shiftPixels(-this._timeline.getPixelLength() / 2);
+ }
+};
+
+Timeline.HotZoneEther.prototype.setDate = function(date) {
+ this._start = this._unit.cloneValue(date);
+};
+
+Timeline.HotZoneEther.prototype.shiftPixels = function(pixels) {
+ this._start = this.pixelOffsetToDate(pixels);
+};
+
+Timeline.HotZoneEther.prototype.dateToPixelOffset = function(date) {
+ return this._dateDiffToPixelOffset(this._start, date);
+};
+
+Timeline.HotZoneEther.prototype.pixelOffsetToDate = function(pixels) {
+ return this._pixelOffsetToDate(pixels, this._start);
+};
+
+Timeline.HotZoneEther.prototype.zoom = function(zoomIn) {
+ var netIntervalChange = 0;
+ var currentZoomIndex = this._band._zoomIndex;
+ var newZoomIndex = currentZoomIndex;
+
+ if (zoomIn && (currentZoomIndex > 0)) {
+ newZoomIndex = currentZoomIndex - 1;
+ }
+
+ if (!zoomIn && (currentZoomIndex < (this._band._zoomSteps.length - 1))) {
+ newZoomIndex = currentZoomIndex + 1;
+ }
+
+ this._band._zoomIndex = newZoomIndex;
+ this._interval =
+ SimileAjax.DateTime.gregorianUnitLengths[this._band._zoomSteps[newZoomIndex].unit];
+ this._pixelsPerInterval = this._band._zoomSteps[newZoomIndex].pixelsPerInterval;
+ netIntervalChange = this._band._zoomSteps[newZoomIndex].unit -
+ this._band._zoomSteps[currentZoomIndex].unit;
+
+ return netIntervalChange;
+};
+
+Timeline.HotZoneEther.prototype._dateDiffToPixelOffset = function(fromDate, toDate) {
+ var scale = this._getScale();
+ var fromTime = fromDate;
+ var toTime = toDate;
+
+ var pixels = 0;
+ if (this._unit.compare(fromTime, toTime) < 0) {
+ var z = 0;
+ while (z < this._zones.length) {
+ if (this._unit.compare(fromTime, this._zones[z].endTime) < 0) {
+ break;
+ }
+ z++;
+ }
+
+ while (this._unit.compare(fromTime, toTime) < 0) {
+ var zone = this._zones[z];
+ var toTime2 = this._unit.earlier(toTime, zone.endTime);
+
+ pixels += (this._unit.compare(toTime2, fromTime) / (scale / zone.magnify));
+
+ fromTime = toTime2;
+ z++;
+ }
+ } else {
+ var z = this._zones.length - 1;
+ while (z >= 0) {
+ if (this._unit.compare(fromTime, this._zones[z].startTime) > 0) {
+ break;
+ }
+ z--;
+ }
+
+ while (this._unit.compare(fromTime, toTime) > 0) {
+ var zone = this._zones[z];
+ var toTime2 = this._unit.later(toTime, zone.startTime);
+
+ pixels += (this._unit.compare(toTime2, fromTime) / (scale / zone.magnify));
+
+ fromTime = toTime2;
+ z--;
+ }
+ }
+ return pixels;
+};
+
+Timeline.HotZoneEther.prototype._pixelOffsetToDate = function(pixels, fromDate) {
+ var scale = this._getScale();
+ var time = fromDate;
+ if (pixels > 0) {
+ var z = 0;
+ while (z < this._zones.length) {
+ if (this._unit.compare(time, this._zones[z].endTime) < 0) {
+ break;
+ }
+ z++;
+ }
+
+ while (pixels > 0) {
+ var zone = this._zones[z];
+ var scale2 = scale / zone.magnify;
+
+ if (zone.endTime == Number.POSITIVE_INFINITY) {
+ time = this._unit.change(time, pixels * scale2);
+ pixels = 0;
+ } else {
+ var pixels2 = this._unit.compare(zone.endTime, time) / scale2;
+ if (pixels2 > pixels) {
+ time = this._unit.change(time, pixels * scale2);
+ pixels = 0;
+ } else {
+ time = zone.endTime;
+ pixels -= pixels2;
+ }
+ }
+ z++;
+ }
+ } else {
+ var z = this._zones.length - 1;
+ while (z >= 0) {
+ if (this._unit.compare(time, this._zones[z].startTime) > 0) {
+ break;
+ }
+ z--;
+ }
+
+ pixels = -pixels;
+ while (pixels > 0) {
+ var zone = this._zones[z];
+ var scale2 = scale / zone.magnify;
+
+ if (zone.startTime == Number.NEGATIVE_INFINITY) {
+ time = this._unit.change(time, -pixels * scale2);
+ pixels = 0;
+ } else {
+ var pixels2 = this._unit.compare(time, zone.startTime) / scale2;
+ if (pixels2 > pixels) {
+ time = this._unit.change(time, -pixels * scale2);
+ pixels = 0;
+ } else {
+ time = zone.startTime;
+ pixels -= pixels2;
+ }
+ }
+ z--;
+ }
+ }
+ return time;
+};
+
+Timeline.HotZoneEther.prototype._getScale = function() {
+ return this._interval / this._pixelsPerInterval;
+};
+/*==================================================
+ * Gregorian Ether Painter
+ *==================================================
+ */
+
+Timeline.GregorianEtherPainter = function(params) {
+ this._params = params;
+ this._theme = params.theme;
+ this._unit = params.unit;
+ this._multiple = ("multiple" in params) ? params.multiple : 1;
+};
+
+Timeline.GregorianEtherPainter.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+
+ this._backgroundLayer = band.createLayerDiv(0);
+ this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
+ this._backgroundLayer.className = 'timeline-ether-bg';
+ // this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
+
+
+ this._markerLayer = null;
+ this._lineLayer = null;
+
+ var align = ("align" in this._params && this._params.align != undefined) ? this._params.align :
+ this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
+ var showLine = ("showLine" in this._params) ? this._params.showLine :
+ this._theme.ether.interval.line.show;
+
+ this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
+ this._timeline, this._band, this._theme, align, showLine);
+
+ this._highlight = new Timeline.EtherHighlight(
+ this._timeline, this._band, this._theme, this._backgroundLayer);
+}
+
+Timeline.GregorianEtherPainter.prototype.setHighlight = function(startDate, endDate) {
+ this._highlight.position(startDate, endDate);
+}
+
+Timeline.GregorianEtherPainter.prototype.paint = function() {
+ if (this._markerLayer) {
+ this._band.removeLayerDiv(this._markerLayer);
+ }
+ this._markerLayer = this._band.createLayerDiv(100);
+ this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
+ this._markerLayer.style.display = "none";
+
+ if (this._lineLayer) {
+ this._band.removeLayerDiv(this._lineLayer);
+ }
+ this._lineLayer = this._band.createLayerDiv(1);
+ this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
+ this._lineLayer.style.display = "none";
+
+ var minDate = this._band.getMinDate();
+ var maxDate = this._band.getMaxDate();
+
+ var timeZone = this._band.getTimeZone();
+ var labeller = this._band.getLabeller();
+
+ SimileAjax.DateTime.roundDownToInterval(minDate, this._unit, timeZone, this._multiple, this._theme.firstDayOfWeek);
+
+ var p = this;
+ var incrementDate = function(date) {
+ for (var i = 0; i < p._multiple; i++) {
+ SimileAjax.DateTime.incrementByInterval(date, p._unit);
+ }
+ };
+
+ while (minDate.getTime() < maxDate.getTime()) {
+ this._intervalMarkerLayout.createIntervalMarker(
+ minDate, labeller, this._unit, this._markerLayer, this._lineLayer);
+
+ incrementDate(minDate);
+ }
+ this._markerLayer.style.display = "block";
+ this._lineLayer.style.display = "block";
+};
+
+Timeline.GregorianEtherPainter.prototype.softPaint = function() {
+};
+
+Timeline.GregorianEtherPainter.prototype.zoom = function(netIntervalChange) {
+ if (netIntervalChange != 0) {
+ this._unit += netIntervalChange;
+ }
+};
+
+
+/*==================================================
+ * Hot Zone Gregorian Ether Painter
+ *==================================================
+ */
+
+Timeline.HotZoneGregorianEtherPainter = function(params) {
+ this._params = params;
+ this._theme = params.theme;
+
+ this._zones = [{
+ startTime: Number.NEGATIVE_INFINITY,
+ endTime: Number.POSITIVE_INFINITY,
+ unit: params.unit,
+ multiple: 1
+ }];
+ for (var i = 0; i < params.zones.length; i++) {
+ var zone = params.zones[i];
+ var zoneStart = SimileAjax.DateTime.parseGregorianDateTime(zone.start).getTime();
+ var zoneEnd = SimileAjax.DateTime.parseGregorianDateTime(zone.end).getTime();
+
+ for (var j = 0; j < this._zones.length && zoneEnd > zoneStart; j++) {
+ var zone2 = this._zones[j];
+
+ if (zoneStart < zone2.endTime) {
+ if (zoneStart > zone2.startTime) {
+ this._zones.splice(j, 0, {
+ startTime: zone2.startTime,
+ endTime: zoneStart,
+ unit: zone2.unit,
+ multiple: zone2.multiple
+ });
+ j++;
+
+ zone2.startTime = zoneStart;
+ }
+
+ if (zoneEnd < zone2.endTime) {
+ this._zones.splice(j, 0, {
+ startTime: zoneStart,
+ endTime: zoneEnd,
+ unit: zone.unit,
+ multiple: (zone.multiple) ? zone.multiple : 1
+ });
+ j++;
+
+ zone2.startTime = zoneEnd;
+ zoneStart = zoneEnd;
+ } else {
+ zone2.multiple = zone.multiple;
+ zone2.unit = zone.unit;
+ zoneStart = zone2.endTime;
+ }
+ } // else, try the next existing zone
+ }
+ }
+};
+
+Timeline.HotZoneGregorianEtherPainter.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+
+ this._backgroundLayer = band.createLayerDiv(0);
+ this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
+ this._backgroundLayer.className ='timeline-ether-bg';
+ //this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
+
+ this._markerLayer = null;
+ this._lineLayer = null;
+
+ var align = ("align" in this._params && this._params.align != undefined) ? this._params.align :
+ this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
+ var showLine = ("showLine" in this._params) ? this._params.showLine :
+ this._theme.ether.interval.line.show;
+
+ this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
+ this._timeline, this._band, this._theme, align, showLine);
+
+ this._highlight = new Timeline.EtherHighlight(
+ this._timeline, this._band, this._theme, this._backgroundLayer);
+}
+
+Timeline.HotZoneGregorianEtherPainter.prototype.setHighlight = function(startDate, endDate) {
+ this._highlight.position(startDate, endDate);
+}
+
+Timeline.HotZoneGregorianEtherPainter.prototype.paint = function() {
+ if (this._markerLayer) {
+ this._band.removeLayerDiv(this._markerLayer);
+ }
+ this._markerLayer = this._band.createLayerDiv(100);
+ this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
+ this._markerLayer.style.display = "none";
+
+ if (this._lineLayer) {
+ this._band.removeLayerDiv(this._lineLayer);
+ }
+ this._lineLayer = this._band.createLayerDiv(1);
+ this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
+ this._lineLayer.style.display = "none";
+
+ var minDate = this._band.getMinDate();
+ var maxDate = this._band.getMaxDate();
+
+ var timeZone = this._band.getTimeZone();
+ var labeller = this._band.getLabeller();
+
+ var p = this;
+ var incrementDate = function(date, zone) {
+ for (var i = 0; i < zone.multiple; i++) {
+ SimileAjax.DateTime.incrementByInterval(date, zone.unit);
+ }
+ };
+
+ var zStart = 0;
+ while (zStart < this._zones.length) {
+ if (minDate.getTime() < this._zones[zStart].endTime) {
+ break;
+ }
+ zStart++;
+ }
+ var zEnd = this._zones.length - 1;
+ while (zEnd >= 0) {
+ if (maxDate.getTime() > this._zones[zEnd].startTime) {
+ break;
+ }
+ zEnd--;
+ }
+
+ for (var z = zStart; z <= zEnd; z++) {
+ var zone = this._zones[z];
+
+ var minDate2 = new Date(Math.max(minDate.getTime(), zone.startTime));
+ var maxDate2 = new Date(Math.min(maxDate.getTime(), zone.endTime));
+
+ SimileAjax.DateTime.roundDownToInterval(minDate2, zone.unit, timeZone, zone.multiple, this._theme.firstDayOfWeek);
+ SimileAjax.DateTime.roundUpToInterval(maxDate2, zone.unit, timeZone, zone.multiple, this._theme.firstDayOfWeek);
+
+ while (minDate2.getTime() < maxDate2.getTime()) {
+ this._intervalMarkerLayout.createIntervalMarker(
+ minDate2, labeller, zone.unit, this._markerLayer, this._lineLayer);
+
+ incrementDate(minDate2, zone);
+ }
+ }
+ this._markerLayer.style.display = "block";
+ this._lineLayer.style.display = "block";
+};
+
+Timeline.HotZoneGregorianEtherPainter.prototype.softPaint = function() {
+};
+
+Timeline.HotZoneGregorianEtherPainter.prototype.zoom = function(netIntervalChange) {
+ if (netIntervalChange != 0) {
+ for (var i = 0; i < this._zones.length; ++i) {
+ if (this._zones[i]) {
+ this._zones[i].unit += netIntervalChange;
+ }
+ }
+ }
+};
+
+/*==================================================
+ * Year Count Ether Painter
+ *==================================================
+ */
+
+Timeline.YearCountEtherPainter = function(params) {
+ this._params = params;
+ this._theme = params.theme;
+ this._startDate = SimileAjax.DateTime.parseGregorianDateTime(params.startDate);
+ this._multiple = ("multiple" in params) ? params.multiple : 1;
+};
+
+Timeline.YearCountEtherPainter.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+
+ this._backgroundLayer = band.createLayerDiv(0);
+ this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
+ this._backgroundLayer.className = 'timeline-ether-bg';
+ // this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
+
+ this._markerLayer = null;
+ this._lineLayer = null;
+
+ var align = ("align" in this._params) ? this._params.align :
+ this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
+ var showLine = ("showLine" in this._params) ? this._params.showLine :
+ this._theme.ether.interval.line.show;
+
+ this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
+ this._timeline, this._band, this._theme, align, showLine);
+
+ this._highlight = new Timeline.EtherHighlight(
+ this._timeline, this._band, this._theme, this._backgroundLayer);
+};
+
+Timeline.YearCountEtherPainter.prototype.setHighlight = function(startDate, endDate) {
+ this._highlight.position(startDate, endDate);
+};
+
+Timeline.YearCountEtherPainter.prototype.paint = function() {
+ if (this._markerLayer) {
+ this._band.removeLayerDiv(this._markerLayer);
+ }
+ this._markerLayer = this._band.createLayerDiv(100);
+ this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
+ this._markerLayer.style.display = "none";
+
+ if (this._lineLayer) {
+ this._band.removeLayerDiv(this._lineLayer);
+ }
+ this._lineLayer = this._band.createLayerDiv(1);
+ this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
+ this._lineLayer.style.display = "none";
+
+ var minDate = new Date(this._startDate.getTime());
+ var maxDate = this._band.getMaxDate();
+ var yearDiff = this._band.getMinDate().getUTCFullYear() - this._startDate.getUTCFullYear();
+ minDate.setUTCFullYear(this._band.getMinDate().getUTCFullYear() - yearDiff % this._multiple);
+
+ var p = this;
+ var incrementDate = function(date) {
+ for (var i = 0; i < p._multiple; i++) {
+ SimileAjax.DateTime.incrementByInterval(date, SimileAjax.DateTime.YEAR);
+ }
+ };
+ var labeller = {
+ labelInterval: function(date, intervalUnit) {
+ var diff = date.getUTCFullYear() - p._startDate.getUTCFullYear();
+ return {
+ text: diff,
+ emphasized: diff == 0
+ };
+ }
+ };
+
+ while (minDate.getTime() < maxDate.getTime()) {
+ this._intervalMarkerLayout.createIntervalMarker(
+ minDate, labeller, SimileAjax.DateTime.YEAR, this._markerLayer, this._lineLayer);
+
+ incrementDate(minDate);
+ }
+ this._markerLayer.style.display = "block";
+ this._lineLayer.style.display = "block";
+};
+
+Timeline.YearCountEtherPainter.prototype.softPaint = function() {
+};
+
+/*==================================================
+ * Quarterly Ether Painter
+ *==================================================
+ */
+
+Timeline.QuarterlyEtherPainter = function(params) {
+ this._params = params;
+ this._theme = params.theme;
+ this._startDate = SimileAjax.DateTime.parseGregorianDateTime(params.startDate);
+};
+
+Timeline.QuarterlyEtherPainter.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+
+ this._backgroundLayer = band.createLayerDiv(0);
+ this._backgroundLayer.setAttribute("name", "ether-background"); // for debugging
+ this._backgroundLayer.className = 'timeline-ether-bg';
+ // this._backgroundLayer.style.background = this._theme.ether.backgroundColors[band.getIndex()];
+
+ this._markerLayer = null;
+ this._lineLayer = null;
+
+ var align = ("align" in this._params) ? this._params.align :
+ this._theme.ether.interval.marker[timeline.isHorizontal() ? "hAlign" : "vAlign"];
+ var showLine = ("showLine" in this._params) ? this._params.showLine :
+ this._theme.ether.interval.line.show;
+
+ this._intervalMarkerLayout = new Timeline.EtherIntervalMarkerLayout(
+ this._timeline, this._band, this._theme, align, showLine);
+
+ this._highlight = new Timeline.EtherHighlight(
+ this._timeline, this._band, this._theme, this._backgroundLayer);
+};
+
+Timeline.QuarterlyEtherPainter.prototype.setHighlight = function(startDate, endDate) {
+ this._highlight.position(startDate, endDate);
+};
+
+Timeline.QuarterlyEtherPainter.prototype.paint = function() {
+ if (this._markerLayer) {
+ this._band.removeLayerDiv(this._markerLayer);
+ }
+ this._markerLayer = this._band.createLayerDiv(100);
+ this._markerLayer.setAttribute("name", "ether-markers"); // for debugging
+ this._markerLayer.style.display = "none";
+
+ if (this._lineLayer) {
+ this._band.removeLayerDiv(this._lineLayer);
+ }
+ this._lineLayer = this._band.createLayerDiv(1);
+ this._lineLayer.setAttribute("name", "ether-lines"); // for debugging
+ this._lineLayer.style.display = "none";
+
+ var minDate = new Date(0);
+ var maxDate = this._band.getMaxDate();
+
+ minDate.setUTCFullYear(Math.max(this._startDate.getUTCFullYear(), this._band.getMinDate().getUTCFullYear()));
+ minDate.setUTCMonth(this._startDate.getUTCMonth());
+
+ var p = this;
+ var incrementDate = function(date) {
+ date.setUTCMonth(date.getUTCMonth() + 3);
+ };
+ var labeller = {
+ labelInterval: function(date, intervalUnit) {
+ var quarters = (4 + (date.getUTCMonth() - p._startDate.getUTCMonth()) / 3) % 4;
+ if (quarters != 0) {
+ return { text: "Q" + (quarters + 1), emphasized: false };
+ } else {
+ return { text: "Y" + (date.getUTCFullYear() - p._startDate.getUTCFullYear() + 1), emphasized: true };
+ }
+ }
+ };
+
+ while (minDate.getTime() < maxDate.getTime()) {
+ this._intervalMarkerLayout.createIntervalMarker(
+ minDate, labeller, SimileAjax.DateTime.YEAR, this._markerLayer, this._lineLayer);
+
+ incrementDate(minDate);
+ }
+ this._markerLayer.style.display = "block";
+ this._lineLayer.style.display = "block";
+};
+
+Timeline.QuarterlyEtherPainter.prototype.softPaint = function() {
+};
+
+/*==================================================
+ * Ether Interval Marker Layout
+ *==================================================
+ */
+
+Timeline.EtherIntervalMarkerLayout = function(timeline, band, theme, align, showLine) {
+ var horizontal = timeline.isHorizontal();
+ if (horizontal) {
+ if (align == "Top") {
+ this.positionDiv = function(div, offset) {
+ div.style.left = offset + "px";
+ div.style.top = "0px";
+ };
+ } else {
+ this.positionDiv = function(div, offset) {
+ div.style.left = offset + "px";
+ div.style.bottom = "0px";
+ };
+ }
+ } else {
+ if (align == "Left") {
+ this.positionDiv = function(div, offset) {
+ div.style.top = offset + "px";
+ div.style.left = "0px";
+ };
+ } else {
+ this.positionDiv = function(div, offset) {
+ div.style.top = offset + "px";
+ div.style.right = "0px";
+ };
+ }
+ }
+
+ var markerTheme = theme.ether.interval.marker;
+ var lineTheme = theme.ether.interval.line;
+ var weekendTheme = theme.ether.interval.weekend;
+
+ var stylePrefix = (horizontal ? "h" : "v") + align;
+ var labelStyler = markerTheme[stylePrefix + "Styler"];
+ var emphasizedLabelStyler = markerTheme[stylePrefix + "EmphasizedStyler"];
+ var day = SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.DAY];
+
+ this.createIntervalMarker = function(date, labeller, unit, markerDiv, lineDiv) {
+ var offset = Math.round(band.dateToPixelOffset(date));
+
+ if (showLine && unit != SimileAjax.DateTime.WEEK) {
+ var divLine = timeline.getDocument().createElement("div");
+ divLine.className = "timeline-ether-lines";
+
+ if (lineTheme.opacity < 100) {
+ SimileAjax.Graphics.setOpacity(divLine, lineTheme.opacity);
+ }
+
+ if (horizontal) {
+ //divLine.className += " timeline-ether-lines-vertical";
+ divLine.style.left = offset + "px";
+ } else {
+ //divLine.className += " timeline-ether-lines-horizontal";
+ divLine.style.top = offset + "px";
+ }
+ lineDiv.appendChild(divLine);
+ }
+ if (unit == SimileAjax.DateTime.WEEK) {
+ var firstDayOfWeek = theme.firstDayOfWeek;
+
+ var saturday = new Date(date.getTime() + (6 - firstDayOfWeek - 7) * day);
+ var monday = new Date(saturday.getTime() + 2 * day);
+
+ var saturdayPixel = Math.round(band.dateToPixelOffset(saturday));
+ var mondayPixel = Math.round(band.dateToPixelOffset(monday));
+ var length = Math.max(1, mondayPixel - saturdayPixel);
+
+ var divWeekend = timeline.getDocument().createElement("div");
+ divWeekend.className = 'timeline-ether-weekends'
+
+ if (weekendTheme.opacity < 100) {
+ SimileAjax.Graphics.setOpacity(divWeekend, weekendTheme.opacity);
+ }
+
+ if (horizontal) {
+ divWeekend.style.left = saturdayPixel + "px";
+ divWeekend.style.width = length + "px";
+ } else {
+ divWeekend.style.top = saturdayPixel + "px";
+ divWeekend.style.height = length + "px";
+ }
+ lineDiv.appendChild(divWeekend);
+ }
+
+ var label = labeller.labelInterval(date, unit);
+
+ var div = timeline.getDocument().createElement("div");
+ div.innerHTML = label.text;
+
+
+
+ div.className = 'timeline-date-label'
+ if(label.emphasized) div.className += ' timeline-date-label-em'
+
+ this.positionDiv(div, offset);
+ markerDiv.appendChild(div);
+
+ return div;
+ };
+};
+
+/*==================================================
+ * Ether Highlight Layout
+ *==================================================
+ */
+
+Timeline.EtherHighlight = function(timeline, band, theme, backgroundLayer) {
+ var horizontal = timeline.isHorizontal();
+
+ this._highlightDiv = null;
+ this._createHighlightDiv = function() {
+ if (this._highlightDiv == null) {
+ this._highlightDiv = timeline.getDocument().createElement("div");
+ this._highlightDiv.setAttribute("name", "ether-highlight"); // for debugging
+ this._highlightDiv.className = 'timeline-ether-highlight'
+
+ var opacity = theme.ether.highlightOpacity;
+ if (opacity < 100) {
+ SimileAjax.Graphics.setOpacity(this._highlightDiv, opacity);
+ }
+
+ backgroundLayer.appendChild(this._highlightDiv);
+ }
+ }
+
+ this.position = function(startDate, endDate) {
+ this._createHighlightDiv();
+
+ var startPixel = Math.round(band.dateToPixelOffset(startDate));
+ var endPixel = Math.round(band.dateToPixelOffset(endDate));
+ var length = Math.max(endPixel - startPixel, 3);
+ if (horizontal) {
+ this._highlightDiv.style.left = startPixel + "px";
+ this._highlightDiv.style.width = length + "px";
+ this._highlightDiv.style.height = (band.getViewWidth() - 4) + "px";
+ } else {
+ this._highlightDiv.style.top = startPixel + "px";
+ this._highlightDiv.style.height = length + "px";
+ this._highlightDiv.style.width = (band.getViewWidth() - 4) + "px";
+ }
+ }
+};
+/*==================================================
+ * Event Utils
+ *==================================================
+ */
+Timeline.EventUtils = {};
+
+Timeline.EventUtils.getNewEventID = function() {
+ // global across page
+ if (this._lastEventID == null) {
+ this._lastEventID = 0;
+ }
+
+ this._lastEventID += 1;
+ return "e" + this._lastEventID;
+};
+
+Timeline.EventUtils.decodeEventElID = function(elementID) {
+ /*==================================================
+ *
+ * Use this function to decode an event element's id on a band (label div,
+ * tape div or icon img).
+ *
+ * Returns {band: , evt: }
+ *
+ * To enable a single event listener to monitor everything
+ * on a Timeline, a set format is used for the id's of the
+ * elements on the Timeline--
+ *
+ * element id format for labels, icons, tapes:
+ * labels: label-tl---
+ * icons: icon-tl---
+ * tapes: tape1-tl---
+ * tape2-tl---
+ * // some events have more than one tape
+ * highlight: highlight1-tl---
+ * highlight2-tl---
+ * // some events have more than one highlight div (future)
+ * Note: use split('-') to get array of the format's parts
+ *
+ * You can then retrieve the timeline object and event object
+ * by using Timeline.getTimeline, Timeline.getBand, or
+ * Timeline.getEvent and passing in the element's id
+ *
+ *==================================================
+ */
+
+ var parts = elementID.split('-');
+ if (parts[1] != 'tl') {
+ alert("Internal Timeline problem 101, please consult support");
+ return {band: null, evt: null}; // early return
+ }
+
+ var timeline = Timeline.getTimelineFromID(parts[2]);
+ var band = timeline.getBand(parts[3]);
+ var evt = band.getEventSource.getEvent(parts[4]);
+
+ return {band: band, evt: evt};
+};
+
+Timeline.EventUtils.encodeEventElID = function(timeline, band, elType, evt) {
+ // elType should be one of {label | icon | tapeN | highlightN}
+ return elType + "-tl-" + timeline.timelineID +
+ "-" + band.getIndex() + "-" + evt.getID();
+};
+/*==================================================
+ * Gregorian Date Labeller
+ *==================================================
+ */
+
+Timeline.GregorianDateLabeller = function(locale, timeZone) {
+ this._locale = locale;
+ this._timeZone = timeZone;
+};
+
+Timeline.GregorianDateLabeller.monthNames = [];
+Timeline.GregorianDateLabeller.dayNames = [];
+Timeline.GregorianDateLabeller.labelIntervalFunctions = [];
+
+Timeline.GregorianDateLabeller.getMonthName = function(month, locale) {
+ return Timeline.GregorianDateLabeller.monthNames[locale][month];
+};
+
+Timeline.GregorianDateLabeller.prototype.labelInterval = function(date, intervalUnit) {
+ var f = Timeline.GregorianDateLabeller.labelIntervalFunctions[this._locale];
+ if (f == null) {
+ f = Timeline.GregorianDateLabeller.prototype.defaultLabelInterval;
+ }
+ return f.call(this, date, intervalUnit);
+};
+
+Timeline.GregorianDateLabeller.prototype.labelPrecise = function(date) {
+ return SimileAjax.DateTime.removeTimeZoneOffset(
+ date,
+ this._timeZone //+ (new Date().getTimezoneOffset() / 60)
+ ).toUTCString();
+};
+
+Timeline.GregorianDateLabeller.prototype.defaultLabelInterval = function(date, intervalUnit) {
+ var text;
+ var emphasized = false;
+
+ date = SimileAjax.DateTime.removeTimeZoneOffset(date, this._timeZone);
+
+ switch(intervalUnit) {
+ case SimileAjax.DateTime.MILLISECOND:
+ text = date.getUTCMilliseconds();
+ break;
+ case SimileAjax.DateTime.SECOND:
+ text = date.getUTCSeconds();
+ break;
+ case SimileAjax.DateTime.MINUTE:
+ var m = date.getUTCMinutes();
+ if (m == 0) {
+ text = date.getUTCHours() + ":00";
+ emphasized = true;
+ } else {
+ text = m;
+ }
+ break;
+ case SimileAjax.DateTime.HOUR:
+ text = date.getUTCHours() + "hr";
+ break;
+ case SimileAjax.DateTime.DAY:
+ text = Timeline.GregorianDateLabeller.getMonthName(date.getUTCMonth(), this._locale) + " " + date.getUTCDate();
+ break;
+ case SimileAjax.DateTime.WEEK:
+ text = Timeline.GregorianDateLabeller.getMonthName(date.getUTCMonth(), this._locale) + " " + date.getUTCDate();
+ break;
+ case SimileAjax.DateTime.MONTH:
+ var m = date.getUTCMonth();
+ if (m != 0) {
+ text = Timeline.GregorianDateLabeller.getMonthName(m, this._locale);
+ break;
+ } // else, fall through
+ case SimileAjax.DateTime.YEAR:
+ case SimileAjax.DateTime.DECADE:
+ case SimileAjax.DateTime.CENTURY:
+ case SimileAjax.DateTime.MILLENNIUM:
+ var y = date.getUTCFullYear();
+ if (y > 0) {
+ text = date.getUTCFullYear();
+ } else {
+ text = (1 - y) + "BC";
+ }
+ emphasized =
+ (intervalUnit == SimileAjax.DateTime.MONTH) ||
+ (intervalUnit == SimileAjax.DateTime.DECADE && y % 100 == 0) ||
+ (intervalUnit == SimileAjax.DateTime.CENTURY && y % 1000 == 0);
+ break;
+ default:
+ text = date.toUTCString();
+ }
+ return { text: text, emphasized: emphasized };
+}
+
+/*==================================================
+ * Default Event Source
+ *==================================================
+ */
+
+
+Timeline.DefaultEventSource = function(eventIndex) {
+ this._events = (eventIndex instanceof Object) ? eventIndex : new SimileAjax.EventIndex();
+ this._listeners = [];
+};
+
+Timeline.DefaultEventSource.prototype.addListener = function(listener) {
+ this._listeners.push(listener);
+};
+
+Timeline.DefaultEventSource.prototype.removeListener = function(listener) {
+ for (var i = 0; i < this._listeners.length; i++) {
+ if (this._listeners[i] == listener) {
+ this._listeners.splice(i, 1);
+ break;
+ }
+ }
+};
+
+Timeline.DefaultEventSource.prototype.loadXML = function(xml, url) {
+ var base = this._getBaseURL(url);
+
+ var wikiURL = xml.documentElement.getAttribute("wiki-url");
+ var wikiSection = xml.documentElement.getAttribute("wiki-section");
+
+ var dateTimeFormat = xml.documentElement.getAttribute("date-time-format");
+ var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
+
+ var node = xml.documentElement.firstChild;
+ var added = false;
+ while (node != null) {
+ if (node.nodeType == 1) {
+ var description = "";
+ if (node.firstChild != null && node.firstChild.nodeType == 3) {
+ description = node.firstChild.nodeValue;
+ }
+ // instant event: default is true. Or use values from isDuration or durationEvent
+ var instant = (node.getAttribute("isDuration") === null &&
+ node.getAttribute("durationEvent") === null) ||
+ node.getAttribute("isDuration") == "false" ||
+ node.getAttribute("durationEvent") == "false";
+
+ var evt = new Timeline.DefaultEventSource.Event( {
+ id: node.getAttribute("id"),
+ start: parseDateTimeFunction(node.getAttribute("start")),
+ end: parseDateTimeFunction(node.getAttribute("end")),
+ latestStart: parseDateTimeFunction(node.getAttribute("latestStart")),
+ earliestEnd: parseDateTimeFunction(node.getAttribute("earliestEnd")),
+ instant: instant,
+ text: node.getAttribute("title"),
+ description: description,
+ image: this._resolveRelativeURL(node.getAttribute("image"), base),
+ link: this._resolveRelativeURL(node.getAttribute("link") , base),
+ icon: this._resolveRelativeURL(node.getAttribute("icon") , base),
+ color: node.getAttribute("color"),
+ textColor: node.getAttribute("textColor"),
+ hoverText: node.getAttribute("hoverText"),
+ classname: node.getAttribute("classname"),
+ tapeImage: node.getAttribute("tapeImage"),
+ tapeRepeat: node.getAttribute("tapeRepeat"),
+ caption: node.getAttribute("caption"),
+ eventID: node.getAttribute("eventID"),
+ trackNum: node.getAttribute("trackNum")
+ });
+
+ evt._node = node;
+ evt.getProperty = function(name) {
+ return this._node.getAttribute(name);
+ };
+ evt.setWikiInfo(wikiURL, wikiSection);
+
+ this._events.add(evt);
+
+ added = true;
+ }
+ node = node.nextSibling;
+ }
+
+ if (added) {
+ this._fire("onAddMany", []);
+ }
+};
+
+
+Timeline.DefaultEventSource.prototype.loadJSON = function(data, url) {
+ var base = this._getBaseURL(url);
+ var added = false;
+ if (data && data.events){
+ var wikiURL = ("wikiURL" in data) ? data.wikiURL : null;
+ var wikiSection = ("wikiSection" in data) ? data.wikiSection : null;
+
+ var dateTimeFormat = ("dateTimeFormat" in data) ? data.dateTimeFormat : null;
+ var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
+
+ for (var i=0; i < data.events.length; i++){
+ var event = data.events[i];
+ // Fixing issue 33:
+ // instant event: default (for JSON only) is false. Or use values from isDuration or durationEvent
+ // isDuration was negated (see issue 33, so keep that interpretation
+ var instant = event.isDuration || (event.durationEvent != null && !event.durationEvent);
+
+ var evt = new Timeline.DefaultEventSource.Event({
+ id: ("id" in event) ? event.id : undefined,
+ start: parseDateTimeFunction(event.start),
+ end: parseDateTimeFunction(event.end),
+ latestStart: parseDateTimeFunction(event.latestStart),
+ earliestEnd: parseDateTimeFunction(event.earliestEnd),
+ instant: instant,
+ text: event.title,
+ description: event.description,
+ image: this._resolveRelativeURL(event.image, base),
+ link: this._resolveRelativeURL(event.link , base),
+ icon: this._resolveRelativeURL(event.icon , base),
+ color: event.color,
+ textColor: event.textColor,
+ hoverText: event.hoverText,
+ classname: event.classname,
+ tapeImage: event.tapeImage,
+ tapeRepeat: event.tapeRepeat,
+ caption: event.caption,
+ eventID: event.eventID,
+ trackNum: event.trackNum
+ });
+ evt._obj = event;
+ evt.getProperty = function(name) {
+ return this._obj[name];
+ };
+ evt.setWikiInfo(wikiURL, wikiSection);
+
+ this._events.add(evt);
+ added = true;
+ }
+ }
+
+ if (added) {
+ this._fire("onAddMany", []);
+ }
+};
+
+/*
+ * Contributed by Morten Frederiksen, http://www.wasab.dk/morten/
+ */
+Timeline.DefaultEventSource.prototype.loadSPARQL = function(xml, url) {
+ var base = this._getBaseURL(url);
+
+ var dateTimeFormat = 'iso8601';
+ var parseDateTimeFunction = this._events.getUnit().getParser(dateTimeFormat);
+
+ if (xml == null) {
+ return;
+ }
+
+ /*
+ * Find tag
+ */
+ var node = xml.documentElement.firstChild;
+ while (node != null && (node.nodeType != 1 || node.nodeName != 'results')) {
+ node = node.nextSibling;
+ }
+
+ var wikiURL = null;
+ var wikiSection = null;
+ if (node != null) {
+ wikiURL = node.getAttribute("wiki-url");
+ wikiSection = node.getAttribute("wiki-section");
+
+ node = node.firstChild;
+ }
+
+ var added = false;
+ while (node != null) {
+ if (node.nodeType == 1) {
+ var bindings = { };
+ var binding = node.firstChild;
+ while (binding != null) {
+ if (binding.nodeType == 1 &&
+ binding.firstChild != null &&
+ binding.firstChild.nodeType == 1 &&
+ binding.firstChild.firstChild != null &&
+ binding.firstChild.firstChild.nodeType == 3) {
+ bindings[binding.getAttribute('name')] = binding.firstChild.firstChild.nodeValue;
+ }
+ binding = binding.nextSibling;
+ }
+
+ if (bindings["start"] == null && bindings["date"] != null) {
+ bindings["start"] = bindings["date"];
+ }
+
+ // instant event: default is true. Or use values from isDuration or durationEvent
+ var instant = (bindings["isDuration"] === null &&
+ bindings["durationEvent"] === null) ||
+ bindings["isDuration"] == "false" ||
+ bindings["durationEvent"] == "false";
+
+ var evt = new Timeline.DefaultEventSource.Event({
+ id: bindings["id"],
+ start: parseDateTimeFunction(bindings["start"]),
+ end: parseDateTimeFunction(bindings["end"]),
+ latestStart: parseDateTimeFunction(bindings["latestStart"]),
+ earliestEnd: parseDateTimeFunction(bindings["earliestEnd"]),
+ instant: instant, // instant
+ text: bindings["title"], // text
+ description: bindings["description"],
+ image: this._resolveRelativeURL(bindings["image"], base),
+ link: this._resolveRelativeURL(bindings["link"] , base),
+ icon: this._resolveRelativeURL(bindings["icon"] , base),
+ color: bindings["color"],
+ textColor: bindings["textColor"],
+ hoverText: bindings["hoverText"],
+ caption: bindings["caption"],
+ classname: bindings["classname"],
+ tapeImage: bindings["tapeImage"],
+ tapeRepeat: bindings["tapeRepeat"],
+ eventID: bindings["eventID"],
+ trackNum: bindings["trackNum"]
+ });
+ evt._bindings = bindings;
+ evt.getProperty = function(name) {
+ return this._bindings[name];
+ };
+ evt.setWikiInfo(wikiURL, wikiSection);
+
+ this._events.add(evt);
+ added = true;
+ }
+ node = node.nextSibling;
+ }
+
+ if (added) {
+ this._fire("onAddMany", []);
+ }
+};
+
+Timeline.DefaultEventSource.prototype.add = function(evt) {
+ this._events.add(evt);
+ this._fire("onAddOne", [evt]);
+};
+
+Timeline.DefaultEventSource.prototype.addMany = function(events) {
+ for (var i = 0; i < events.length; i++) {
+ this._events.add(events[i]);
+ }
+ this._fire("onAddMany", []);
+};
+
+Timeline.DefaultEventSource.prototype.clear = function() {
+ this._events.removeAll();
+ this._fire("onClear", []);
+};
+
+Timeline.DefaultEventSource.prototype.getEvent = function(id) {
+ return this._events.getEvent(id);
+};
+
+Timeline.DefaultEventSource.prototype.getEventIterator = function(startDate, endDate) {
+ return this._events.getIterator(startDate, endDate);
+};
+
+Timeline.DefaultEventSource.prototype.getEventReverseIterator = function(startDate, endDate) {
+ return this._events.getReverseIterator(startDate, endDate);
+};
+
+Timeline.DefaultEventSource.prototype.getAllEventIterator = function() {
+ return this._events.getAllIterator();
+};
+
+Timeline.DefaultEventSource.prototype.getCount = function() {
+ return this._events.getCount();
+};
+
+Timeline.DefaultEventSource.prototype.getEarliestDate = function() {
+ return this._events.getEarliestDate();
+};
+
+Timeline.DefaultEventSource.prototype.getLatestDate = function() {
+ return this._events.getLatestDate();
+};
+
+Timeline.DefaultEventSource.prototype._fire = function(handlerName, args) {
+ for (var i = 0; i < this._listeners.length; i++) {
+ var listener = this._listeners[i];
+ if (handlerName in listener) {
+ try {
+ listener[handlerName].apply(listener, args);
+ } catch (e) {
+ SimileAjax.Debug.exception(e);
+ }
+ }
+ }
+};
+
+Timeline.DefaultEventSource.prototype._getBaseURL = function(url) {
+ if (url.indexOf("://") < 0) {
+ var url2 = this._getBaseURL(document.location.href);
+ if (url.substr(0,1) == "/") {
+ url = url2.substr(0, url2.indexOf("/", url2.indexOf("://") + 3)) + url;
+ } else {
+ url = url2 + url;
+ }
+ }
+
+ var i = url.lastIndexOf("/");
+ if (i < 0) {
+ return "";
+ } else {
+ return url.substr(0, i+1);
+ }
+};
+
+Timeline.DefaultEventSource.prototype._resolveRelativeURL = function(url, base) {
+ if (url == null || url == "") {
+ return url;
+ } else if (url.indexOf("://") > 0) {
+ return url;
+ } else if (url.substr(0,1) == "/") {
+ return base.substr(0, base.indexOf("/", base.indexOf("://") + 3)) + url;
+ } else {
+ return base + url;
+ }
+};
+
+
+Timeline.DefaultEventSource.Event = function(args) {
+ //
+ // Attention developers!
+ // If you add a new event attribute, please be sure to add it to
+ // all three load functions: loadXML, loadSPARCL, loadJSON.
+ // Thanks!
+ //
+ // args is a hash/object. It supports the following keys. Most are optional
+ // id -- an internal id. Really shouldn't be used by events.
+ // Timeline library clients should use eventID
+ // eventID -- For use by library client when writing custom painters or
+ // custom fillInfoBubble
+ // start
+ // end
+ // latestStart
+ // earliestEnd
+ // instant -- boolean. Controls precise/non-precise logic & duration/instant issues
+ // text -- event source attribute 'title' -- used as the label on Timelines and in bubbles.
+ // description -- used in bubbles
+ // image -- used in bubbles
+ // link -- used in bubbles
+ // icon -- on the Timeline
+ // color -- Timeline label and tape color
+ // textColor -- Timeline label color, overrides color attribute
+ // hoverText -- deprecated, here for backwards compatibility.
+ // Superceeded by caption
+ // caption -- tooltip-like caption on the Timeline. Uses HTML title attribute
+ // classname -- used to set classname in Timeline. Enables better CSS selector rules
+ // tapeImage -- background image of the duration event's tape div on the Timeline
+ // tapeRepeat -- repeat attribute for tapeImage. {repeat | repeat-x | repeat-y }
+
+ function cleanArg(arg) {
+ // clean up an arg
+ return (args[arg] != null && args[arg] != "") ? args[arg] : null;
+ }
+
+ var id = args.id ? args.id.trim() : "";
+ this._id = id.length > 0 ? id : Timeline.EventUtils.getNewEventID();
+
+ this._instant = args.instant || (args.end == null);
+
+ this._start = args.start;
+ this._end = (args.end != null) ? args.end : args.start;
+
+ this._latestStart = (args.latestStart != null) ?
+ args.latestStart : (args.instant ? this._end : this._start);
+ this._earliestEnd = (args.earliestEnd != null) ? args.earliestEnd : this._end;
+
+ // check sanity of dates since incorrect dates will later cause calculation errors
+ // when painting
+ var err=[];
+ if (this._start > this._latestStart) {
+ this._latestStart = this._start;
+ err.push("start is > latestStart");}
+ if (this._start > this._earliestEnd) {
+ this._earliestEnd = this._latestStart;
+ err.push("start is > earliestEnd");}
+ if (this._start > this._end) {
+ this._end = this._earliestEnd;
+ err.push("start is > end");}
+ if (this._latestStart > this._earliestEnd) {
+ this._earliestEnd = this._latestStart;
+ err.push("latestStart is > earliestEnd");}
+ if (this._latestStart > this._end) {
+ this._end = this._earliestEnd;
+ err.push("latestStart is > end");}
+ if (this._earliestEnd > this._end) {
+ this._end = this._earliestEnd;
+ err.push("earliestEnd is > end");}
+
+ this._eventID = cleanArg('eventID');
+ this._text = (args.text != null) ? SimileAjax.HTML.deEntify(args.text) : ""; // Change blank titles to ""
+ if (err.length > 0) {
+ this._text += " PROBLEM: " + err.join(", ");
+ }
+
+ this._description = SimileAjax.HTML.deEntify(args.description);
+ this._image = cleanArg('image');
+ this._link = cleanArg('link');
+ this._title = cleanArg('hoverText');
+ this._title = cleanArg('caption');
+
+ this._icon = cleanArg('icon');
+ this._color = cleanArg('color');
+ this._textColor = cleanArg('textColor');
+ this._classname = cleanArg('classname');
+ this._tapeImage = cleanArg('tapeImage');
+ this._tapeRepeat = cleanArg('tapeRepeat');
+ this._trackNum = cleanArg('trackNum');
+ if (this._trackNum != null) {
+ this._trackNum = parseInt(this._trackNum);
+ }
+
+ this._wikiURL = null;
+ this._wikiSection = null;
+};
+
+Timeline.DefaultEventSource.Event.prototype = {
+ getID: function() { return this._id; },
+
+ isInstant: function() { return this._instant; },
+ isImprecise: function() { return this._start != this._latestStart || this._end != this._earliestEnd; },
+
+ getStart: function() { return this._start; },
+ getEnd: function() { return this._end; },
+ getLatestStart: function() { return this._latestStart; },
+ getEarliestEnd: function() { return this._earliestEnd; },
+
+ getEventID: function() { return this._eventID; },
+ getText: function() { return this._text; }, // title
+ getDescription: function() { return this._description; },
+ getImage: function() { return this._image; },
+ getLink: function() { return this._link; },
+
+ getIcon: function() { return this._icon; },
+ getColor: function() { return this._color; },
+ getTextColor: function() { return this._textColor; },
+ getClassName: function() { return this._classname; },
+ getTapeImage: function() { return this._tapeImage; },
+ getTapeRepeat: function() { return this._tapeRepeat; },
+ getTrackNum: function() { return this._trackNum; },
+
+ getProperty: function(name) { return null; },
+
+ getWikiURL: function() { return this._wikiURL; },
+ getWikiSection: function() { return this._wikiSection; },
+ setWikiInfo: function(wikiURL, wikiSection) {
+ this._wikiURL = wikiURL;
+ this._wikiSection = wikiSection;
+ },
+
+ fillDescription: function(elmt) {
+ elmt.innerHTML = this._description;
+ },
+ fillWikiInfo: function(elmt) {
+ // Many bubbles will not support a wiki link.
+ //
+ // Strategy: assume no wiki link. If we do have
+ // enough parameters for one, then create it.
+ elmt.style.display = "none"; // default
+
+ if (this._wikiURL == null || this._wikiSection == null) {
+ return; // EARLY RETURN
+ }
+
+ // create the wikiID from the property or from the event text (the title)
+ var wikiID = this.getProperty("wikiID");
+ if (wikiID == null || wikiID.length == 0) {
+ wikiID = this.getText(); // use the title as the backup wiki id
+ }
+
+ if (wikiID == null || wikiID.length == 0) {
+ return; // No wikiID. Thus EARLY RETURN
+ }
+
+ // ready to go...
+ elmt.style.display = "inline";
+ wikiID = wikiID.replace(/\s/g, "_");
+ var url = this._wikiURL + this._wikiSection.replace(/\s/g, "_") + "/" + wikiID;
+ var a = document.createElement("a");
+ a.href = url;
+ a.target = "new";
+ a.innerHTML = Timeline.strings[Timeline.clientLocale].wikiLinkLabel;
+
+ elmt.appendChild(document.createTextNode("["));
+ elmt.appendChild(a);
+ elmt.appendChild(document.createTextNode("]"));
+ },
+
+ fillTime: function(elmt, labeller) {
+ if (this._instant) {
+ if (this.isImprecise()) {
+ elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
+ elmt.appendChild(elmt.ownerDocument.createElement("br"));
+ elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));
+ } else {
+ elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
+ }
+ } else {
+ if (this.isImprecise()) {
+ elmt.appendChild(elmt.ownerDocument.createTextNode(
+ labeller.labelPrecise(this._start) + " ~ " + labeller.labelPrecise(this._latestStart)));
+ elmt.appendChild(elmt.ownerDocument.createElement("br"));
+ elmt.appendChild(elmt.ownerDocument.createTextNode(
+ labeller.labelPrecise(this._earliestEnd) + " ~ " + labeller.labelPrecise(this._end)));
+ } else {
+ elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._start)));
+ elmt.appendChild(elmt.ownerDocument.createElement("br"));
+ elmt.appendChild(elmt.ownerDocument.createTextNode(labeller.labelPrecise(this._end)));
+ }
+ }
+ },
+
+ fillInfoBubble: function(elmt, theme, labeller) {
+ var doc = elmt.ownerDocument;
+
+ var title = this.getText();
+ var link = this.getLink();
+ var image = this.getImage();
+
+ if (image != null) {
+ var img = doc.createElement("img");
+ img.src = image;
+
+ theme.event.bubble.imageStyler(img);
+ elmt.appendChild(img);
+ }
+
+ var divTitle = doc.createElement("div");
+ var textTitle = doc.createTextNode(title);
+ if (link != null) {
+ var a = doc.createElement("a");
+ a.href = link;
+ a.appendChild(textTitle);
+ divTitle.appendChild(a);
+ } else {
+ divTitle.appendChild(textTitle);
+ }
+ theme.event.bubble.titleStyler(divTitle);
+ elmt.appendChild(divTitle);
+
+ var divBody = doc.createElement("div");
+ this.fillDescription(divBody);
+ theme.event.bubble.bodyStyler(divBody);
+ elmt.appendChild(divBody);
+
+ var divTime = doc.createElement("div");
+ this.fillTime(divTime, labeller);
+ theme.event.bubble.timeStyler(divTime);
+ elmt.appendChild(divTime);
+
+ var divWiki = doc.createElement("div");
+ this.fillWikiInfo(divWiki);
+ theme.event.bubble.wikiStyler(divWiki);
+ elmt.appendChild(divWiki);
+ }
+};
+
+
+/*==================================================
+ * Original Event Painter
+ *==================================================
+ */
+
+/*==================================================
+ *
+ * To enable a single event listener to monitor everything
+ * on a Timeline, we need a way to map from an event's icon,
+ * label or tape element to the associated timeline, band and
+ * specific event.
+ *
+ * Thus a set format is used for the id's of the
+ * events' elements on the Timeline--
+ *
+ * element id format for labels, icons, tapes:
+ * labels: label-tl---
+ * icons: icon-tl---
+ * tapes: tape1-tl---
+ * tape2-tl---
+ * // some events have more than one tape
+ * highlight: highlight1-tl---
+ * highlight2-tl---
+ * // some events have more than one highlight div (future)
+ * You can then retrieve the band/timeline objects and event object
+ * by using Timeline.EventUtils.decodeEventElID
+ *
+ *==================================================
+ */
+
+/*
+ * eventPaintListener functions receive calls about painting.
+ * function(band, op, evt, els)
+ * context: 'this' will be an OriginalEventPainter object.
+ * It has properties and methods for obtaining
+ * the relevant band, timeline, etc
+ * band = the band being painted
+ * op = 'paintStarting' // the painter is about to remove
+ * all previously painted events, if any. It will
+ * then start painting all of the visible events that
+ * pass the filter.
+ * evt = null, els = null
+ * op = 'paintEnded' // the painter has finished painting
+ * all of the visible events that passed the filter
+ * evt = null, els = null
+ * op = 'paintedEvent' // the painter just finished painting an event
+ * evt = event just painted
+ * els = array of painted elements' divs. Depending on the event,
+ * the array could be just a tape or icon (if no label).
+ * Or could include label, multiple tape divs (imprecise event),
+ * highlight divs. The array is not ordered. The meaning of
+ * each el is available by decoding the el's id
+ * Note that there may be no paintedEvent calls if no events were visible
+ * or passed the filter.
+ */
+
+Timeline.OriginalEventPainter = function(params) {
+ this._params = params;
+ this._onSelectListeners = [];
+ this._eventPaintListeners = [];
+
+ this._filterMatcher = null;
+ this._highlightMatcher = null;
+ this._frc = null;
+
+ this._eventIdToElmt = {};
+};
+
+Timeline.OriginalEventPainter.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+
+ this._backLayer = null;
+ this._eventLayer = null;
+ this._lineLayer = null;
+ this._highlightLayer = null;
+
+ this._eventIdToElmt = null;
+};
+
+Timeline.OriginalEventPainter.prototype.getType = function() {
+ return 'original';
+};
+
+Timeline.OriginalEventPainter.prototype.addOnSelectListener = function(listener) {
+ this._onSelectListeners.push(listener);
+};
+
+Timeline.OriginalEventPainter.prototype.removeOnSelectListener = function(listener) {
+ for (var i = 0; i < this._onSelectListeners.length; i++) {
+ if (this._onSelectListeners[i] == listener) {
+ this._onSelectListeners.splice(i, 1);
+ break;
+ }
+ }
+};
+
+Timeline.OriginalEventPainter.prototype.addEventPaintListener = function(listener) {
+ this._eventPaintListeners.push(listener);
+};
+
+Timeline.OriginalEventPainter.prototype.removeEventPaintListener = function(listener) {
+ for (var i = 0; i < this._eventPaintListeners.length; i++) {
+ if (this._eventPaintListeners[i] == listener) {
+ this._eventPaintListeners.splice(i, 1);
+ break;
+ }
+ }
+};
+
+Timeline.OriginalEventPainter.prototype.getFilterMatcher = function() {
+ return this._filterMatcher;
+};
+
+Timeline.OriginalEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
+ this._filterMatcher = filterMatcher;
+};
+
+Timeline.OriginalEventPainter.prototype.getHighlightMatcher = function() {
+ return this._highlightMatcher;
+};
+
+Timeline.OriginalEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
+ this._highlightMatcher = highlightMatcher;
+};
+
+Timeline.OriginalEventPainter.prototype.paint = function() {
+ // Paints the events for a given section of the band--what is
+ // visible on screen and some extra.
+ var eventSource = this._band.getEventSource();
+ if (eventSource == null) {
+ return;
+ }
+
+ this._eventIdToElmt = {};
+ this._fireEventPaintListeners('paintStarting', null, null);
+ this._prepareForPainting();
+
+ var eventTheme = this._params.theme.event;
+ var trackHeight = Math.max(eventTheme.track.height, eventTheme.tape.height +
+ this._frc.getLineHeight());
+ var metrics = {
+ trackOffset: eventTheme.track.offset,
+ trackHeight: trackHeight,
+ trackGap: eventTheme.track.gap,
+ trackIncrement: trackHeight + eventTheme.track.gap,
+ icon: eventTheme.instant.icon,
+ iconWidth: eventTheme.instant.iconWidth,
+ iconHeight: eventTheme.instant.iconHeight,
+ labelWidth: eventTheme.label.width,
+ maxLabelChar: eventTheme.label.maxLabelChar,
+ impreciseIconMargin: eventTheme.instant.impreciseIconMargin
+ }
+
+ var minDate = this._band.getMinDate();
+ var maxDate = this._band.getMaxDate();
+
+ var filterMatcher = (this._filterMatcher != null) ?
+ this._filterMatcher :
+ function(evt) { return true; };
+ var highlightMatcher = (this._highlightMatcher != null) ?
+ this._highlightMatcher :
+ function(evt) { return -1; };
+
+ var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
+ while (iterator.hasNext()) {
+ var evt = iterator.next();
+ if (filterMatcher(evt)) {
+ this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
+ }
+ }
+
+ this._highlightLayer.style.display = "block";
+ this._lineLayer.style.display = "block";
+ this._eventLayer.style.display = "block";
+ // update the band object for max number of tracks in this section of the ether
+ this._band.updateEventTrackInfo(this._tracks.length, metrics.trackIncrement);
+ this._fireEventPaintListeners('paintEnded', null, null);
+};
+
+Timeline.OriginalEventPainter.prototype.softPaint = function() {
+};
+
+Timeline.OriginalEventPainter.prototype._prepareForPainting = function() {
+ // Remove everything previously painted: highlight, line and event layers.
+ // Prepare blank layers for painting.
+ var band = this._band;
+
+ if (this._backLayer == null) {
+ this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
+ this._backLayer.style.visibility = "hidden";
+
+ var eventLabelPrototype = document.createElement("span");
+ eventLabelPrototype.className = "timeline-event-label";
+ this._backLayer.appendChild(eventLabelPrototype);
+ this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
+ }
+ this._frc.update();
+ this._tracks = [];
+
+ if (this._highlightLayer != null) {
+ band.removeLayerDiv(this._highlightLayer);
+ }
+ this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
+ this._highlightLayer.style.display = "none";
+
+ if (this._lineLayer != null) {
+ band.removeLayerDiv(this._lineLayer);
+ }
+ this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
+ this._lineLayer.style.display = "none";
+
+ if (this._eventLayer != null) {
+ band.removeLayerDiv(this._eventLayer);
+ }
+ this._eventLayer = band.createLayerDiv(115, "timeline-band-events");
+ this._eventLayer.style.display = "none";
+};
+
+Timeline.OriginalEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
+ if (evt.isInstant()) {
+ this.paintInstantEvent(evt, metrics, theme, highlightIndex);
+ } else {
+ this.paintDurationEvent(evt, metrics, theme, highlightIndex);
+ }
+};
+
+Timeline.OriginalEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
+ if (evt.isImprecise()) {
+ this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
+ } else {
+ this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
+ }
+}
+
+Timeline.OriginalEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
+ if (evt.isImprecise()) {
+ this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
+ } else {
+ this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
+ }
+}
+
+Timeline.OriginalEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+ var doc = this._timeline.getDocument();
+ var text = evt.getText();
+
+ var startDate = evt.getStart();
+ var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+ var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
+ var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
+
+ var labelDivClassName = this._getLabelDivClassName(evt);
+ var labelSize = this._frc.computeSize(text, labelDivClassName);
+ var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
+ var labelRight = labelLeft + labelSize.width;
+
+ var rightEdge = labelRight;
+ var track = this._findFreeTrack(evt, rightEdge);
+
+ var labelTop = Math.round(
+ metrics.trackOffset + track * metrics.trackIncrement +
+ metrics.trackHeight / 2 - labelSize.height / 2);
+
+ var iconElmtData = this._paintEventIcon(evt, track, iconLeftEdge, metrics, theme, 0);
+ var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
+ labelSize.height, theme, labelDivClassName, highlightIndex);
+ var els = [iconElmtData.elmt, labelElmtData.elmt];
+
+ var self = this;
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
+ };
+ SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+ var hDiv = this._createHighlightDiv(highlightIndex, iconElmtData, theme, evt);
+ if (hDiv != null) {els.push(hDiv);}
+ this._fireEventPaintListeners('paintedEvent', evt, els);
+
+
+ this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
+ this._tracks[track] = iconLeftEdge;
+};
+
+Timeline.OriginalEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+ var doc = this._timeline.getDocument();
+ var text = evt.getText();
+
+ var startDate = evt.getStart();
+ var endDate = evt.getEnd();
+ var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+ var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+
+ var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
+ var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
+
+ var labelDivClassName = this._getLabelDivClassName(evt);
+ var labelSize = this._frc.computeSize(text, labelDivClassName);
+ var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
+ var labelRight = labelLeft + labelSize.width;
+
+ var rightEdge = Math.max(labelRight, endPixel);
+ var track = this._findFreeTrack(evt, rightEdge);
+ var tapeHeight = theme.event.tape.height;
+ var labelTop = Math.round(
+ metrics.trackOffset + track * metrics.trackIncrement + tapeHeight);
+
+ var iconElmtData = this._paintEventIcon(evt, track, iconLeftEdge, metrics, theme, tapeHeight);
+ var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
+ labelSize.height, theme, labelDivClassName, highlightIndex);
+
+ var color = evt.getColor();
+ color = color != null ? color : theme.event.instant.impreciseColor;
+
+ var tapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel,
+ color, theme.event.instant.impreciseOpacity, metrics, theme, 0);
+ var els = [iconElmtData.elmt, labelElmtData.elmt, tapeElmtData.elmt];
+
+ var self = this;
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
+ };
+ SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+ var hDiv = this._createHighlightDiv(highlightIndex, iconElmtData, theme, evt);
+ if (hDiv != null) {els.push(hDiv);}
+ this._fireEventPaintListeners('paintedEvent', evt, els);
+
+ this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
+ this._tracks[track] = iconLeftEdge;
+};
+
+Timeline.OriginalEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+ var doc = this._timeline.getDocument();
+ var text = evt.getText();
+
+ var startDate = evt.getStart();
+ var endDate = evt.getEnd();
+ var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+ var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+
+ var labelDivClassName = this._getLabelDivClassName(evt);
+ var labelSize = this._frc.computeSize(text, labelDivClassName);
+ var labelLeft = startPixel;
+ var labelRight = labelLeft + labelSize.width;
+
+ var rightEdge = Math.max(labelRight, endPixel);
+ var track = this._findFreeTrack(evt, rightEdge);
+ var labelTop = Math.round(
+ metrics.trackOffset + track * metrics.trackIncrement + theme.event.tape.height);
+
+ var color = evt.getColor();
+ color = color != null ? color : theme.event.duration.color;
+
+ var tapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel, color, 100, metrics, theme, 0);
+ var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width,
+ labelSize.height, theme, labelDivClassName, highlightIndex);
+ var els = [tapeElmtData.elmt, labelElmtData.elmt];
+
+ var self = this;
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
+ };
+ SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+ var hDiv = this._createHighlightDiv(highlightIndex, tapeElmtData, theme, evt);
+ if (hDiv != null) {els.push(hDiv);}
+ this._fireEventPaintListeners('paintedEvent', evt, els);
+
+ this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
+ this._tracks[track] = startPixel;
+};
+
+Timeline.OriginalEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+ var doc = this._timeline.getDocument();
+ var text = evt.getText();
+
+ var startDate = evt.getStart();
+ var latestStartDate = evt.getLatestStart();
+ var endDate = evt.getEnd();
+ var earliestEndDate = evt.getEarliestEnd();
+
+ var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+ var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
+ var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+ var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
+
+ var labelDivClassName = this._getLabelDivClassName(evt);
+ var labelSize = this._frc.computeSize(text, labelDivClassName);
+ var labelLeft = latestStartPixel;
+ var labelRight = labelLeft + labelSize.width;
+
+ var rightEdge = Math.max(labelRight, endPixel);
+ var track = this._findFreeTrack(evt, rightEdge);
+ var labelTop = Math.round(
+ metrics.trackOffset + track * metrics.trackIncrement + theme.event.tape.height);
+
+ var color = evt.getColor();
+ color = color != null ? color : theme.event.duration.color;
+
+ // Imprecise events can have two event tapes
+ // The imprecise dates tape, uses opacity to be dimmer than precise dates
+ var impreciseTapeElmtData = this._paintEventTape(evt, track, startPixel, endPixel,
+ theme.event.duration.impreciseColor,
+ theme.event.duration.impreciseOpacity, metrics, theme, 0);
+ // The precise dates tape, regular (100%) opacity
+ var tapeElmtData = this._paintEventTape(evt, track, latestStartPixel,
+ earliestEndPixel, color, 100, metrics, theme, 1);
+
+ var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop,
+ labelSize.width, labelSize.height, theme, labelDivClassName, highlightIndex);
+ var els = [impreciseTapeElmtData.elmt, tapeElmtData.elmt, labelElmtData.elmt];
+
+ var self = this;
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
+ };
+ SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+ var hDiv = this._createHighlightDiv(highlightIndex, tapeElmtData, theme, evt);
+ if (hDiv != null) {els.push(hDiv);}
+ this._fireEventPaintListeners('paintedEvent', evt, els);
+
+ this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
+ this._tracks[track] = startPixel;
+};
+
+Timeline.OriginalEventPainter.prototype._encodeEventElID = function(elType, evt) {
+ return Timeline.EventUtils.encodeEventElID(this._timeline, this._band, elType, evt);
+};
+
+Timeline.OriginalEventPainter.prototype._findFreeTrack = function(event, rightEdge) {
+ var trackAttribute = event.getTrackNum();
+ if (trackAttribute != null) {
+ return trackAttribute; // early return since event includes track number
+ }
+
+ // normal case: find an open track
+ for (var i = 0; i < this._tracks.length; i++) {
+ var t = this._tracks[i];
+ if (t > rightEdge) {
+ break;
+ }
+ }
+ return i;
+};
+
+Timeline.OriginalEventPainter.prototype._paintEventIcon = function(evt, iconTrack, left, metrics, theme, tapeHeight) {
+ // If no tape, then paint the icon in the middle of the track.
+ // If there is a tape, paint the icon below the tape + impreciseIconMargin
+ var icon = evt.getIcon();
+ icon = icon != null ? icon : metrics.icon;
+
+ var top; // top of the icon
+ if (tapeHeight > 0) {
+ top = metrics.trackOffset + iconTrack * metrics.trackIncrement +
+ tapeHeight + metrics.impreciseIconMargin;
+ } else {
+ var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement +
+ metrics.trackHeight / 2;
+ top = Math.round(middle - metrics.iconHeight / 2);
+ }
+ var img = SimileAjax.Graphics.createTranslucentImage(icon);
+ var iconDiv = this._timeline.getDocument().createElement("div");
+ iconDiv.className = this._getElClassName('timeline-event-icon', evt, 'icon');
+ iconDiv.id = this._encodeEventElID('icon', evt);
+ iconDiv.style.left = left + "px";
+ iconDiv.style.top = top + "px";
+ iconDiv.appendChild(img);
+
+ if(evt._title != null)
+ iconDiv.title = evt._title;
+
+ this._eventLayer.appendChild(iconDiv);
+
+ return {
+ left: left,
+ top: top,
+ width: metrics.iconWidth,
+ height: metrics.iconHeight,
+ elmt: iconDiv
+ };
+};
+
+Timeline.OriginalEventPainter.prototype._paintEventLabel = function(evt, text, left, top, width,
+ height, theme, labelDivClassName, highlightIndex) {
+ var doc = this._timeline.getDocument();
+
+ var labelDiv = doc.createElement("div");
+ labelDiv.className = labelDivClassName;
+ labelDiv.id = this._encodeEventElID('label', evt);
+ labelDiv.style.left = left + "px";
+ labelDiv.style.width = width + "px";
+ labelDiv.style.top = top + "px";
+ labelDiv.innerHTML = text;
+
+ if(evt._title != null)
+ labelDiv.title = evt._title;
+
+ var color = evt.getTextColor();
+ if (color == null) {
+ color = evt.getColor();
+ }
+ if (color != null) {
+ labelDiv.style.color = color;
+ }
+ if (theme.event.highlightLabelBackground && highlightIndex >= 0) {
+ labelDiv.style.background = this._getHighlightColor(highlightIndex, theme);
+ }
+
+ this._eventLayer.appendChild(labelDiv);
+
+ return {
+ left: left,
+ top: top,
+ width: width,
+ height: height,
+ elmt: labelDiv
+ };
+};
+
+Timeline.OriginalEventPainter.prototype._paintEventTape = function(
+ evt, iconTrack, startPixel, endPixel, color, opacity, metrics, theme, tape_index) {
+
+ var tapeWidth = endPixel - startPixel;
+ var tapeHeight = theme.event.tape.height;
+ var top = metrics.trackOffset + iconTrack * metrics.trackIncrement;
+
+ var tapeDiv = this._timeline.getDocument().createElement("div");
+ tapeDiv.className = this._getElClassName('timeline-event-tape', evt, 'tape');
+ tapeDiv.id = this._encodeEventElID('tape' + tape_index, evt);
+ tapeDiv.style.left = startPixel + "px";
+ tapeDiv.style.width = tapeWidth + "px";
+ tapeDiv.style.height = tapeHeight + "px";
+ tapeDiv.style.top = top + "px";
+
+ if(evt._title != null)
+ tapeDiv.title = evt._title;
+
+ if(color != null) {
+ tapeDiv.style.backgroundColor = color;
+ }
+
+ var backgroundImage = evt.getTapeImage();
+ var backgroundRepeat = evt.getTapeRepeat();
+ backgroundRepeat = backgroundRepeat != null ? backgroundRepeat : 'repeat';
+ if(backgroundImage != null) {
+ tapeDiv.style.backgroundImage = "url(" + backgroundImage + ")";
+ tapeDiv.style.backgroundRepeat = backgroundRepeat;
+ }
+
+ SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
+
+ this._eventLayer.appendChild(tapeDiv);
+
+ return {
+ left: startPixel,
+ top: top,
+ width: tapeWidth,
+ height: tapeHeight,
+ elmt: tapeDiv
+ };
+}
+
+Timeline.OriginalEventPainter.prototype._getLabelDivClassName = function(evt) {
+ return this._getElClassName('timeline-event-label', evt, 'label');
+};
+
+Timeline.OriginalEventPainter.prototype._getElClassName = function(elClassName, evt, prefix) {
+ // Prefix and '_' is added to the event's classname. Set to null for no prefix
+ var evt_classname = evt.getClassName(),
+ pieces = [];
+
+ if (evt_classname) {
+ if (prefix) {pieces.push(prefix + '-' + evt_classname + ' ');}
+ pieces.push(evt_classname + ' ');
+ }
+ pieces.push(elClassName);
+ return(pieces.join(''));
+};
+
+Timeline.OriginalEventPainter.prototype._getHighlightColor = function(highlightIndex, theme) {
+ var highlightColors = theme.event.highlightColors;
+ return highlightColors[Math.min(highlightIndex, highlightColors.length - 1)];
+};
+
+Timeline.OriginalEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme, evt) {
+ var div = null;
+ if (highlightIndex >= 0) {
+ var doc = this._timeline.getDocument();
+ var color = this._getHighlightColor(highlightIndex, theme);
+
+ div = doc.createElement("div");
+ div.className = this._getElClassName('timeline-event-highlight', evt, 'highlight');
+ div.id = this._encodeEventElID('highlight0', evt); // in future will have other
+ // highlight divs for tapes + icons
+ div.style.position = "absolute";
+ div.style.overflow = "hidden";
+ div.style.left = (dimensions.left - 2) + "px";
+ div.style.width = (dimensions.width + 4) + "px";
+ div.style.top = (dimensions.top - 2) + "px";
+ div.style.height = (dimensions.height + 4) + "px";
+ div.style.background = color;
+
+ this._highlightLayer.appendChild(div);
+ }
+ return div;
+};
+
+Timeline.OriginalEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
+ var c = SimileAjax.DOM.getPageCoordinates(icon);
+ this._showBubble(
+ c.left + Math.ceil(icon.offsetWidth / 2),
+ c.top + Math.ceil(icon.offsetHeight / 2),
+ evt
+ );
+ this._fireOnSelect(evt.getID());
+
+ domEvt.cancelBubble = true;
+ SimileAjax.DOM.cancelEvent(domEvt);
+ return false;
+};
+
+Timeline.OriginalEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
+ if ("pageX" in domEvt) {
+ var x = domEvt.pageX;
+ var y = domEvt.pageY;
+ } else {
+ var c = SimileAjax.DOM.getPageCoordinates(target);
+ var x = domEvt.offsetX + c.left;
+ var y = domEvt.offsetY + c.top;
+ }
+ this._showBubble(x, y, evt);
+ this._fireOnSelect(evt.getID());
+
+ domEvt.cancelBubble = true;
+ SimileAjax.DOM.cancelEvent(domEvt);
+ return false;
+};
+
+Timeline.OriginalEventPainter.prototype.showBubble = function(evt) {
+ var elmt = this._eventIdToElmt[evt.getID()];
+ if (elmt) {
+ var c = SimileAjax.DOM.getPageCoordinates(elmt);
+ this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, evt);
+ }
+};
+
+Timeline.OriginalEventPainter.prototype._showBubble = function(x, y, evt) {
+ var div = document.createElement("div");
+ var themeBubble = this._params.theme.event.bubble;
+ evt.fillInfoBubble(div, this._params.theme, this._band.getLabeller());
+
+ SimileAjax.WindowManager.cancelPopups();
+ SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y,
+ themeBubble.width, null, themeBubble.maxHeight);
+};
+
+Timeline.OriginalEventPainter.prototype._fireOnSelect = function(eventID) {
+ for (var i = 0; i < this._onSelectListeners.length; i++) {
+ this._onSelectListeners[i](eventID);
+ }
+};
+
+Timeline.OriginalEventPainter.prototype._fireEventPaintListeners = function(op, evt, els) {
+ for (var i = 0; i < this._eventPaintListeners.length; i++) {
+ this._eventPaintListeners[i](this._band, op, evt, els);
+ }
+};
+/*==================================================
+ * Detailed Event Painter
+ *==================================================
+ */
+
+// Note: a number of features from original-painter
+// are not yet implemented in detailed painter.
+// Eg classname, id attributes for icons, labels, tapes
+
+Timeline.DetailedEventPainter = function(params) {
+ this._params = params;
+ this._onSelectListeners = [];
+
+ this._filterMatcher = null;
+ this._highlightMatcher = null;
+ this._frc = null;
+
+ this._eventIdToElmt = {};
+};
+
+Timeline.DetailedEventPainter.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+
+ this._backLayer = null;
+ this._eventLayer = null;
+ this._lineLayer = null;
+ this._highlightLayer = null;
+
+ this._eventIdToElmt = null;
+};
+
+Timeline.DetailedEventPainter.prototype.getType = function() {
+ return 'detailed';
+};
+
+Timeline.DetailedEventPainter.prototype.addOnSelectListener = function(listener) {
+ this._onSelectListeners.push(listener);
+};
+
+Timeline.DetailedEventPainter.prototype.removeOnSelectListener = function(listener) {
+ for (var i = 0; i < this._onSelectListeners.length; i++) {
+ if (this._onSelectListeners[i] == listener) {
+ this._onSelectListeners.splice(i, 1);
+ break;
+ }
+ }
+};
+
+Timeline.DetailedEventPainter.prototype.getFilterMatcher = function() {
+ return this._filterMatcher;
+};
+
+Timeline.DetailedEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
+ this._filterMatcher = filterMatcher;
+};
+
+Timeline.DetailedEventPainter.prototype.getHighlightMatcher = function() {
+ return this._highlightMatcher;
+};
+
+Timeline.DetailedEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
+ this._highlightMatcher = highlightMatcher;
+};
+
+Timeline.DetailedEventPainter.prototype.paint = function() {
+ var eventSource = this._band.getEventSource();
+ if (eventSource == null) {
+ return;
+ }
+
+ this._eventIdToElmt = {};
+ this._prepareForPainting();
+
+ var eventTheme = this._params.theme.event;
+ var trackHeight = Math.max(eventTheme.track.height, this._frc.getLineHeight());
+ var metrics = {
+ trackOffset: Math.round(this._band.getViewWidth() / 2 - trackHeight / 2),
+ trackHeight: trackHeight,
+ trackGap: eventTheme.track.gap,
+ trackIncrement: trackHeight + eventTheme.track.gap,
+ icon: eventTheme.instant.icon,
+ iconWidth: eventTheme.instant.iconWidth,
+ iconHeight: eventTheme.instant.iconHeight,
+ labelWidth: eventTheme.label.width
+ }
+
+ var minDate = this._band.getMinDate();
+ var maxDate = this._band.getMaxDate();
+
+ var filterMatcher = (this._filterMatcher != null) ?
+ this._filterMatcher :
+ function(evt) { return true; };
+ var highlightMatcher = (this._highlightMatcher != null) ?
+ this._highlightMatcher :
+ function(evt) { return -1; };
+
+ var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
+ while (iterator.hasNext()) {
+ var evt = iterator.next();
+ if (filterMatcher(evt)) {
+ this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
+ }
+ }
+
+ this._highlightLayer.style.display = "block";
+ this._lineLayer.style.display = "block";
+ this._eventLayer.style.display = "block";
+ // update the band object for max number of tracks in this section of the ether
+ this._band.updateEventTrackInfo(this._lowerTracks.length + this._upperTracks.length,
+ metrics.trackIncrement);
+};
+
+Timeline.DetailedEventPainter.prototype.softPaint = function() {
+};
+
+Timeline.DetailedEventPainter.prototype._prepareForPainting = function() {
+ var band = this._band;
+
+ if (this._backLayer == null) {
+ this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
+ this._backLayer.style.visibility = "hidden";
+
+ var eventLabelPrototype = document.createElement("span");
+ eventLabelPrototype.className = "timeline-event-label";
+ this._backLayer.appendChild(eventLabelPrototype);
+ this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
+ }
+ this._frc.update();
+ this._lowerTracks = [];
+ this._upperTracks = [];
+
+ if (this._highlightLayer != null) {
+ band.removeLayerDiv(this._highlightLayer);
+ }
+ this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
+ this._highlightLayer.style.display = "none";
+
+ if (this._lineLayer != null) {
+ band.removeLayerDiv(this._lineLayer);
+ }
+ this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
+ this._lineLayer.style.display = "none";
+
+ if (this._eventLayer != null) {
+ band.removeLayerDiv(this._eventLayer);
+ }
+ this._eventLayer = band.createLayerDiv(110, "timeline-band-events");
+ this._eventLayer.style.display = "none";
+};
+
+Timeline.DetailedEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
+ if (evt.isInstant()) {
+ this.paintInstantEvent(evt, metrics, theme, highlightIndex);
+ } else {
+ this.paintDurationEvent(evt, metrics, theme, highlightIndex);
+ }
+};
+
+Timeline.DetailedEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
+ if (evt.isImprecise()) {
+ this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
+ } else {
+ this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
+ }
+}
+
+Timeline.DetailedEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
+ if (evt.isImprecise()) {
+ this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
+ } else {
+ this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
+ }
+}
+
+Timeline.DetailedEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+ var doc = this._timeline.getDocument();
+ var text = evt.getText();
+
+ var startDate = evt.getStart();
+ var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+ var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
+ var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
+
+ var labelSize = this._frc.computeSize(text);
+ var iconTrack = this._findFreeTrackForSolid(iconRightEdge, startPixel);
+ var iconElmtData = this._paintEventIcon(evt, iconTrack, iconLeftEdge, metrics, theme);
+
+ var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
+ var labelTrack = iconTrack;
+
+ var iconTrackData = this._getTrackData(iconTrack);
+ if (Math.min(iconTrackData.solid, iconTrackData.text) >= labelLeft + labelSize.width) { // label on the same track, to the right of icon
+ iconTrackData.solid = iconLeftEdge;
+ iconTrackData.text = labelLeft;
+ } else { // label on a different track, below icon
+ iconTrackData.solid = iconLeftEdge;
+
+ labelLeft = startPixel + theme.event.label.offsetFromLine;
+ labelTrack = this._findFreeTrackForText(iconTrack, labelLeft + labelSize.width, function(t) { t.line = startPixel - 2; });
+ this._getTrackData(labelTrack).text = iconLeftEdge;
+
+ this._paintEventLine(evt, startPixel, iconTrack, labelTrack, metrics, theme);
+ }
+
+ var labelTop = Math.round(
+ metrics.trackOffset + labelTrack * metrics.trackIncrement +
+ metrics.trackHeight / 2 - labelSize.height / 2);
+
+ var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
+
+ var self = this;
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
+ };
+ SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+ this._createHighlightDiv(highlightIndex, iconElmtData, theme);
+
+ this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
+};
+
+Timeline.DetailedEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+ var doc = this._timeline.getDocument();
+ var text = evt.getText();
+
+ var startDate = evt.getStart();
+ var endDate = evt.getEnd();
+ var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+ var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+
+ var iconRightEdge = Math.round(startPixel + metrics.iconWidth / 2);
+ var iconLeftEdge = Math.round(startPixel - metrics.iconWidth / 2);
+
+ var labelSize = this._frc.computeSize(text);
+ var iconTrack = this._findFreeTrackForSolid(endPixel, startPixel);
+
+ var tapeElmtData = this._paintEventTape(evt, iconTrack, startPixel, endPixel,
+ theme.event.instant.impreciseColor, theme.event.instant.impreciseOpacity, metrics, theme);
+ var iconElmtData = this._paintEventIcon(evt, iconTrack, iconLeftEdge, metrics, theme);
+
+ var iconTrackData = this._getTrackData(iconTrack);
+ iconTrackData.solid = iconLeftEdge;
+
+ var labelLeft = iconRightEdge + theme.event.label.offsetFromLine;
+ var labelRight = labelLeft + labelSize.width;
+ var labelTrack;
+ if (labelRight < endPixel) {
+ labelTrack = iconTrack;
+ } else {
+ labelLeft = startPixel + theme.event.label.offsetFromLine;
+ labelRight = labelLeft + labelSize.width;
+
+ labelTrack = this._findFreeTrackForText(iconTrack, labelRight, function(t) { t.line = startPixel - 2; });
+ this._getTrackData(labelTrack).text = iconLeftEdge;
+
+ this._paintEventLine(evt, startPixel, iconTrack, labelTrack, metrics, theme);
+ }
+ var labelTop = Math.round(
+ metrics.trackOffset + labelTrack * metrics.trackIncrement +
+ metrics.trackHeight / 2 - labelSize.height / 2);
+
+ var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
+
+ var self = this;
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(iconElmtData.elmt, domEvt, evt);
+ };
+ SimileAjax.DOM.registerEvent(iconElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+ this._createHighlightDiv(highlightIndex, iconElmtData, theme);
+
+ this._eventIdToElmt[evt.getID()] = iconElmtData.elmt;
+};
+
+Timeline.DetailedEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+ var doc = this._timeline.getDocument();
+ var text = evt.getText();
+
+ var startDate = evt.getStart();
+ var endDate = evt.getEnd();
+ var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+ var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+
+ var labelSize = this._frc.computeSize(text);
+ var tapeTrack = this._findFreeTrackForSolid(endPixel);
+ var color = evt.getColor();
+ color = color != null ? color : theme.event.duration.color;
+
+ var tapeElmtData = this._paintEventTape(evt, tapeTrack, startPixel, endPixel, color, 100, metrics, theme);
+
+ var tapeTrackData = this._getTrackData(tapeTrack);
+ tapeTrackData.solid = startPixel;
+
+ var labelLeft = startPixel + theme.event.label.offsetFromLine;
+ var labelTrack = this._findFreeTrackForText(tapeTrack, labelLeft + labelSize.width, function(t) { t.line = startPixel - 2; });
+ this._getTrackData(labelTrack).text = startPixel - 2;
+
+ this._paintEventLine(evt, startPixel, tapeTrack, labelTrack, metrics, theme);
+
+ var labelTop = Math.round(
+ metrics.trackOffset + labelTrack * metrics.trackIncrement +
+ metrics.trackHeight / 2 - labelSize.height / 2);
+
+ var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
+
+ var self = this;
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
+ };
+ SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+ this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
+
+ this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
+};
+
+Timeline.DetailedEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+ var doc = this._timeline.getDocument();
+ var text = evt.getText();
+
+ var startDate = evt.getStart();
+ var latestStartDate = evt.getLatestStart();
+ var endDate = evt.getEnd();
+ var earliestEndDate = evt.getEarliestEnd();
+
+ var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+ var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
+ var endPixel = Math.round(this._band.dateToPixelOffset(endDate));
+ var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
+
+ var labelSize = this._frc.computeSize(text);
+ var tapeTrack = this._findFreeTrackForSolid(endPixel);
+ var color = evt.getColor();
+ color = color != null ? color : theme.event.duration.color;
+
+ var impreciseTapeElmtData = this._paintEventTape(evt, tapeTrack, startPixel, endPixel,
+ theme.event.duration.impreciseColor, theme.event.duration.impreciseOpacity, metrics, theme);
+ var tapeElmtData = this._paintEventTape(evt, tapeTrack, latestStartPixel, earliestEndPixel, color, 100, metrics, theme);
+
+ var tapeTrackData = this._getTrackData(tapeTrack);
+ tapeTrackData.solid = startPixel;
+
+ var labelLeft = latestStartPixel + theme.event.label.offsetFromLine;
+ var labelTrack = this._findFreeTrackForText(tapeTrack, labelLeft + labelSize.width, function(t) { t.line = latestStartPixel - 2; });
+ this._getTrackData(labelTrack).text = latestStartPixel - 2;
+
+ this._paintEventLine(evt, latestStartPixel, tapeTrack, labelTrack, metrics, theme);
+
+ var labelTop = Math.round(
+ metrics.trackOffset + labelTrack * metrics.trackIncrement +
+ metrics.trackHeight / 2 - labelSize.height / 2);
+
+ var labelElmtData = this._paintEventLabel(evt, text, labelLeft, labelTop, labelSize.width, labelSize.height, theme);
+
+ var self = this;
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickDurationEvent(tapeElmtData.elmt, domEvt, evt);
+ };
+ SimileAjax.DOM.registerEvent(tapeElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+ this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
+
+ this._eventIdToElmt[evt.getID()] = tapeElmtData.elmt;
+};
+
+Timeline.DetailedEventPainter.prototype._findFreeTrackForSolid = function(solidEdge, softEdge) {
+ for (var i = 0; true; i++) {
+ if (i < this._lowerTracks.length) {
+ var t = this._lowerTracks[i];
+ if (Math.min(t.solid, t.text) > solidEdge && (!(softEdge) || t.line > softEdge)) {
+ return i;
+ }
+ } else {
+ this._lowerTracks.push({
+ solid: Number.POSITIVE_INFINITY,
+ text: Number.POSITIVE_INFINITY,
+ line: Number.POSITIVE_INFINITY
+ });
+
+ return i;
+ }
+
+ if (i < this._upperTracks.length) {
+ var t = this._upperTracks[i];
+ if (Math.min(t.solid, t.text) > solidEdge && (!(softEdge) || t.line > softEdge)) {
+ return -1 - i;
+ }
+ } else {
+ this._upperTracks.push({
+ solid: Number.POSITIVE_INFINITY,
+ text: Number.POSITIVE_INFINITY,
+ line: Number.POSITIVE_INFINITY
+ });
+
+ return -1 - i;
+ }
+ }
+};
+
+Timeline.DetailedEventPainter.prototype._findFreeTrackForText = function(fromTrack, edge, occupiedTrackVisitor) {
+ var extendUp;
+ var index;
+ var firstIndex;
+ var result;
+
+ if (fromTrack < 0) {
+ extendUp = true;
+ firstIndex = -fromTrack;
+
+ index = this._findFreeUpperTrackForText(firstIndex, edge);
+ result = -1 - index;
+ } else if (fromTrack > 0) {
+ extendUp = false;
+ firstIndex = fromTrack + 1;
+
+ index = this._findFreeLowerTrackForText(firstIndex, edge);
+ result = index;
+ } else {
+ var upIndex = this._findFreeUpperTrackForText(0, edge);
+ var downIndex = this._findFreeLowerTrackForText(1, edge);
+
+ if (downIndex - 1 <= upIndex) {
+ extendUp = false;
+ firstIndex = 1;
+ index = downIndex;
+ result = index;
+ } else {
+ extendUp = true;
+ firstIndex = 0;
+ index = upIndex;
+ result = -1 - index;
+ }
+ }
+
+ if (extendUp) {
+ if (index == this._upperTracks.length) {
+ this._upperTracks.push({
+ solid: Number.POSITIVE_INFINITY,
+ text: Number.POSITIVE_INFINITY,
+ line: Number.POSITIVE_INFINITY
+ });
+ }
+ for (var i = firstIndex; i < index; i++) {
+ occupiedTrackVisitor(this._upperTracks[i]);
+ }
+ } else {
+ if (index == this._lowerTracks.length) {
+ this._lowerTracks.push({
+ solid: Number.POSITIVE_INFINITY,
+ text: Number.POSITIVE_INFINITY,
+ line: Number.POSITIVE_INFINITY
+ });
+ }
+ for (var i = firstIndex; i < index; i++) {
+ occupiedTrackVisitor(this._lowerTracks[i]);
+ }
+ }
+ return result;
+};
+
+Timeline.DetailedEventPainter.prototype._findFreeLowerTrackForText = function(index, edge) {
+ for (; index < this._lowerTracks.length; index++) {
+ var t = this._lowerTracks[index];
+ if (Math.min(t.solid, t.text) >= edge) {
+ break;
+ }
+ }
+ return index;
+};
+
+Timeline.DetailedEventPainter.prototype._findFreeUpperTrackForText = function(index, edge) {
+ for (; index < this._upperTracks.length; index++) {
+ var t = this._upperTracks[index];
+ if (Math.min(t.solid, t.text) >= edge) {
+ break;
+ }
+ }
+ return index;
+};
+
+Timeline.DetailedEventPainter.prototype._getTrackData = function(index) {
+ return (index < 0) ? this._upperTracks[-index - 1] : this._lowerTracks[index];
+};
+
+Timeline.DetailedEventPainter.prototype._paintEventLine = function(evt, left, startTrack, endTrack, metrics, theme) {
+ var top = Math.round(metrics.trackOffset + startTrack * metrics.trackIncrement + metrics.trackHeight / 2);
+ var height = Math.round(Math.abs(endTrack - startTrack) * metrics.trackIncrement);
+
+ var lineStyle = "1px solid " + theme.event.label.lineColor;
+ var lineDiv = this._timeline.getDocument().createElement("div");
+ lineDiv.style.position = "absolute";
+ lineDiv.style.left = left + "px";
+ lineDiv.style.width = theme.event.label.offsetFromLine + "px";
+ lineDiv.style.height = height + "px";
+ if (startTrack > endTrack) {
+ lineDiv.style.top = (top - height) + "px";
+ lineDiv.style.borderTop = lineStyle;
+ } else {
+ lineDiv.style.top = top + "px";
+ lineDiv.style.borderBottom = lineStyle;
+ }
+ lineDiv.style.borderLeft = lineStyle;
+ this._lineLayer.appendChild(lineDiv);
+};
+
+Timeline.DetailedEventPainter.prototype._paintEventIcon = function(evt, iconTrack, left, metrics, theme) {
+ var icon = evt.getIcon();
+ icon = icon != null ? icon : metrics.icon;
+
+ var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement + metrics.trackHeight / 2;
+ var top = Math.round(middle - metrics.iconHeight / 2);
+
+ var img = SimileAjax.Graphics.createTranslucentImage(icon);
+ var iconDiv = this._timeline.getDocument().createElement("div");
+ iconDiv.style.position = "absolute";
+ iconDiv.style.left = left + "px";
+ iconDiv.style.top = top + "px";
+ iconDiv.appendChild(img);
+ iconDiv.style.cursor = "pointer";
+
+ if(evt._title != null)
+ iconDiv.title = evt._title
+
+ this._eventLayer.appendChild(iconDiv);
+
+ return {
+ left: left,
+ top: top,
+ width: metrics.iconWidth,
+ height: metrics.iconHeight,
+ elmt: iconDiv
+ };
+};
+
+Timeline.DetailedEventPainter.prototype._paintEventLabel = function(evt, text, left, top, width, height, theme) {
+ var doc = this._timeline.getDocument();
+
+ var labelBackgroundDiv = doc.createElement("div");
+ labelBackgroundDiv.style.position = "absolute";
+ labelBackgroundDiv.style.left = left + "px";
+ labelBackgroundDiv.style.width = width + "px";
+ labelBackgroundDiv.style.top = top + "px";
+ labelBackgroundDiv.style.height = height + "px";
+ labelBackgroundDiv.style.backgroundColor = theme.event.label.backgroundColor;
+ SimileAjax.Graphics.setOpacity(labelBackgroundDiv, theme.event.label.backgroundOpacity);
+ this._eventLayer.appendChild(labelBackgroundDiv);
+
+ var labelDiv = doc.createElement("div");
+ labelDiv.style.position = "absolute";
+ labelDiv.style.left = left + "px";
+ labelDiv.style.width = width + "px";
+ labelDiv.style.top = top + "px";
+ labelDiv.innerHTML = text;
+ labelDiv.style.cursor = "pointer";
+
+ if(evt._title != null)
+ labelDiv.title = evt._title;
+
+ var color = evt.getTextColor();
+ if (color == null) {
+ color = evt.getColor();
+ }
+ if (color != null) {
+ labelDiv.style.color = color;
+ }
+
+ this._eventLayer.appendChild(labelDiv);
+
+ return {
+ left: left,
+ top: top,
+ width: width,
+ height: height,
+ elmt: labelDiv
+ };
+};
+
+Timeline.DetailedEventPainter.prototype._paintEventTape = function(
+ evt, iconTrack, startPixel, endPixel, color, opacity, metrics, theme) {
+
+ var tapeWidth = endPixel - startPixel;
+ var tapeHeight = theme.event.tape.height;
+ var middle = metrics.trackOffset + iconTrack * metrics.trackIncrement + metrics.trackHeight / 2;
+ var top = Math.round(middle - tapeHeight / 2);
+
+ var tapeDiv = this._timeline.getDocument().createElement("div");
+ tapeDiv.style.position = "absolute";
+ tapeDiv.style.left = startPixel + "px";
+ tapeDiv.style.width = tapeWidth + "px";
+ tapeDiv.style.top = top + "px";
+ tapeDiv.style.height = tapeHeight + "px";
+ tapeDiv.style.backgroundColor = color;
+ tapeDiv.style.overflow = "hidden";
+ tapeDiv.style.cursor = "pointer";
+
+ if(evt._title != null)
+ tapeDiv.title = evt._title;
+
+ SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
+
+ this._eventLayer.appendChild(tapeDiv);
+
+ return {
+ left: startPixel,
+ top: top,
+ width: tapeWidth,
+ height: tapeHeight,
+ elmt: tapeDiv
+ };
+}
+
+Timeline.DetailedEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
+ if (highlightIndex >= 0) {
+ var doc = this._timeline.getDocument();
+ var eventTheme = theme.event;
+
+ var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
+
+ var div = doc.createElement("div");
+ div.style.position = "absolute";
+ div.style.overflow = "hidden";
+ div.style.left = (dimensions.left - 2) + "px";
+ div.style.width = (dimensions.width + 4) + "px";
+ div.style.top = (dimensions.top - 2) + "px";
+ div.style.height = (dimensions.height + 4) + "px";
+ div.style.background = color;
+
+ this._highlightLayer.appendChild(div);
+ }
+};
+
+Timeline.DetailedEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
+ var c = SimileAjax.DOM.getPageCoordinates(icon);
+ this._showBubble(
+ c.left + Math.ceil(icon.offsetWidth / 2),
+ c.top + Math.ceil(icon.offsetHeight / 2),
+ evt
+ );
+ this._fireOnSelect(evt.getID());
+
+ domEvt.cancelBubble = true;
+ SimileAjax.DOM.cancelEvent(domEvt);
+ return false;
+};
+
+Timeline.DetailedEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
+ if ("pageX" in domEvt) {
+ var x = domEvt.pageX;
+ var y = domEvt.pageY;
+ } else {
+ var c = SimileAjax.DOM.getPageCoordinates(target);
+ var x = domEvt.offsetX + c.left;
+ var y = domEvt.offsetY + c.top;
+ }
+ this._showBubble(x, y, evt);
+ this._fireOnSelect(evt.getID());
+
+ domEvt.cancelBubble = true;
+ SimileAjax.DOM.cancelEvent(domEvt);
+ return false;
+};
+
+Timeline.DetailedEventPainter.prototype.showBubble = function(evt) {
+ var elmt = this._eventIdToElmt[evt.getID()];
+ if (elmt) {
+ var c = SimileAjax.DOM.getPageCoordinates(elmt);
+ this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, evt);
+ }
+};
+
+Timeline.DetailedEventPainter.prototype._showBubble = function(x, y, evt) {
+ var div = document.createElement("div");
+ var themeBubble = this._params.theme.event.bubble;
+ evt.fillInfoBubble(div, this._params.theme, this._band.getLabeller());
+
+ SimileAjax.WindowManager.cancelPopups();
+ SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y,
+ themeBubble.width, null, themeBubble.maxHeight);
+};
+
+Timeline.DetailedEventPainter.prototype._fireOnSelect = function(eventID) {
+ for (var i = 0; i < this._onSelectListeners.length; i++) {
+ this._onSelectListeners[i](eventID);
+ }
+};
+/*==================================================
+ * Overview Event Painter
+ *==================================================
+ */
+
+Timeline.OverviewEventPainter = function(params) {
+ this._params = params;
+ this._onSelectListeners = [];
+
+ this._filterMatcher = null;
+ this._highlightMatcher = null;
+};
+
+Timeline.OverviewEventPainter.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+
+ this._eventLayer = null;
+ this._highlightLayer = null;
+};
+
+Timeline.OverviewEventPainter.prototype.getType = function() {
+ return 'overview';
+};
+
+Timeline.OverviewEventPainter.prototype.addOnSelectListener = function(listener) {
+ this._onSelectListeners.push(listener);
+};
+
+Timeline.OverviewEventPainter.prototype.removeOnSelectListener = function(listener) {
+ for (var i = 0; i < this._onSelectListeners.length; i++) {
+ if (this._onSelectListeners[i] == listener) {
+ this._onSelectListeners.splice(i, 1);
+ break;
+ }
+ }
+};
+
+Timeline.OverviewEventPainter.prototype.getFilterMatcher = function() {
+ return this._filterMatcher;
+};
+
+Timeline.OverviewEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
+ this._filterMatcher = filterMatcher;
+};
+
+Timeline.OverviewEventPainter.prototype.getHighlightMatcher = function() {
+ return this._highlightMatcher;
+};
+
+Timeline.OverviewEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
+ this._highlightMatcher = highlightMatcher;
+};
+
+Timeline.OverviewEventPainter.prototype.paint = function() {
+ var eventSource = this._band.getEventSource();
+ if (eventSource == null) {
+ return;
+ }
+
+ this._prepareForPainting();
+
+ var eventTheme = this._params.theme.event;
+ var metrics = {
+ trackOffset: eventTheme.overviewTrack.offset,
+ trackHeight: eventTheme.overviewTrack.height,
+ trackGap: eventTheme.overviewTrack.gap,
+ trackIncrement: eventTheme.overviewTrack.height + eventTheme.overviewTrack.gap
+ }
+
+ var minDate = this._band.getMinDate();
+ var maxDate = this._band.getMaxDate();
+
+ var filterMatcher = (this._filterMatcher != null) ?
+ this._filterMatcher :
+ function(evt) { return true; };
+ var highlightMatcher = (this._highlightMatcher != null) ?
+ this._highlightMatcher :
+ function(evt) { return -1; };
+
+ var iterator = eventSource.getEventReverseIterator(minDate, maxDate);
+ while (iterator.hasNext()) {
+ var evt = iterator.next();
+ if (filterMatcher(evt)) {
+ this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
+ }
+ }
+
+ this._highlightLayer.style.display = "block";
+ this._eventLayer.style.display = "block";
+ // update the band object for max number of tracks in this section of the ether
+ this._band.updateEventTrackInfo(this._tracks.length, metrics.trackIncrement);
+};
+
+Timeline.OverviewEventPainter.prototype.softPaint = function() {
+};
+
+Timeline.OverviewEventPainter.prototype._prepareForPainting = function() {
+ var band = this._band;
+
+ this._tracks = [];
+
+ if (this._highlightLayer != null) {
+ band.removeLayerDiv(this._highlightLayer);
+ }
+ this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
+ this._highlightLayer.style.display = "none";
+
+ if (this._eventLayer != null) {
+ band.removeLayerDiv(this._eventLayer);
+ }
+ this._eventLayer = band.createLayerDiv(110, "timeline-band-events");
+ this._eventLayer.style.display = "none";
+};
+
+Timeline.OverviewEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
+ if (evt.isInstant()) {
+ this.paintInstantEvent(evt, metrics, theme, highlightIndex);
+ } else {
+ this.paintDurationEvent(evt, metrics, theme, highlightIndex);
+ }
+};
+
+Timeline.OverviewEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
+ var startDate = evt.getStart();
+ var startPixel = Math.round(this._band.dateToPixelOffset(startDate));
+
+ var color = evt.getColor(),
+ klassName = evt.getClassName();
+ if (klassName) {
+ color = null;
+ } else {
+ color = color != null ? color : theme.event.duration.color;
+ }
+
+ var tickElmtData = this._paintEventTick(evt, startPixel, color, 100, metrics, theme);
+
+ this._createHighlightDiv(highlightIndex, tickElmtData, theme);
+};
+
+Timeline.OverviewEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
+ var latestStartDate = evt.getLatestStart();
+ var earliestEndDate = evt.getEarliestEnd();
+
+ var latestStartPixel = Math.round(this._band.dateToPixelOffset(latestStartDate));
+ var earliestEndPixel = Math.round(this._band.dateToPixelOffset(earliestEndDate));
+
+ var tapeTrack = 0;
+ for (; tapeTrack < this._tracks.length; tapeTrack++) {
+ if (earliestEndPixel < this._tracks[tapeTrack]) {
+ break;
+ }
+ }
+ this._tracks[tapeTrack] = earliestEndPixel;
+
+ var color = evt.getColor(),
+ klassName = evt.getClassName();
+ if (klassName) {
+ color = null;
+ } else {
+ color = color != null ? color : theme.event.duration.color;
+ }
+
+ var tapeElmtData = this._paintEventTape(evt, tapeTrack, latestStartPixel, earliestEndPixel,
+ color, 100, metrics, theme, klassName);
+
+ this._createHighlightDiv(highlightIndex, tapeElmtData, theme);
+};
+
+Timeline.OverviewEventPainter.prototype._paintEventTape = function(
+ evt, track, left, right, color, opacity, metrics, theme, klassName) {
+
+ var top = metrics.trackOffset + track * metrics.trackIncrement;
+ var width = right - left;
+ var height = metrics.trackHeight;
+
+ var tapeDiv = this._timeline.getDocument().createElement("div");
+ tapeDiv.className = 'timeline-small-event-tape'
+ if (klassName) {tapeDiv.className += ' small-' + klassName;}
+ tapeDiv.style.left = left + "px";
+ tapeDiv.style.width = width + "px";
+ tapeDiv.style.top = top + "px";
+ tapeDiv.style.height = height + "px";
+
+ if (color) {
+ tapeDiv.style.backgroundColor = color; // set color here if defined by event. Else use css
+ }
+ // tapeDiv.style.overflow = "hidden"; // now set in css
+ // tapeDiv.style.position = "absolute";
+ if(opacity<100) SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
+
+ this._eventLayer.appendChild(tapeDiv);
+
+ return {
+ left: left,
+ top: top,
+ width: width,
+ height: height,
+ elmt: tapeDiv
+ };
+}
+
+Timeline.OverviewEventPainter.prototype._paintEventTick = function(
+ evt, left, color, opacity, metrics, theme) {
+
+ var height = theme.event.overviewTrack.tickHeight;
+ var top = metrics.trackOffset - height;
+ var width = 1;
+
+ var tickDiv = this._timeline.getDocument().createElement("div");
+ tickDiv.className = 'timeline-small-event-icon'
+ tickDiv.style.left = left + "px";
+ tickDiv.style.top = top + "px";
+ // tickDiv.style.width = width + "px";
+ // tickDiv.style.position = "absolute";
+ // tickDiv.style.height = height + "px";
+ // tickDiv.style.backgroundColor = color;
+ // tickDiv.style.overflow = "hidden";
+
+ var klassName = evt.getClassName()
+ if (klassName) {tickDiv.className +=' small-' + klassName};
+
+ if(opacity<100) {SimileAjax.Graphics.setOpacity(tickDiv, opacity)};
+
+ this._eventLayer.appendChild(tickDiv);
+
+ return {
+ left: left,
+ top: top,
+ width: width,
+ height: height,
+ elmt: tickDiv
+ };
+}
+
+Timeline.OverviewEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
+ if (highlightIndex >= 0) {
+ var doc = this._timeline.getDocument();
+ var eventTheme = theme.event;
+
+ var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
+
+ var div = doc.createElement("div");
+ div.style.position = "absolute";
+ div.style.overflow = "hidden";
+ div.style.left = (dimensions.left - 1) + "px";
+ div.style.width = (dimensions.width + 2) + "px";
+ div.style.top = (dimensions.top - 1) + "px";
+ div.style.height = (dimensions.height + 2) + "px";
+ div.style.background = color;
+
+ this._highlightLayer.appendChild(div);
+ }
+};
+
+Timeline.OverviewEventPainter.prototype.showBubble = function(evt) {
+ // not implemented
+};
+/*==================================================
+ * Original Event Painter
+ *==================================================
+ */
+
+Timeline.CompactEventPainter = function(params) {
+ this._params = params;
+ this._onSelectListeners = [];
+
+ this._filterMatcher = null;
+ this._highlightMatcher = null;
+ this._frc = null;
+
+ this._eventIdToElmt = {};
+};
+
+Timeline.CompactEventPainter.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+
+ this._backLayer = null;
+ this._eventLayer = null;
+ this._lineLayer = null;
+ this._highlightLayer = null;
+
+ this._eventIdToElmt = null;
+};
+
+Timeline.CompactEventPainter.prototype.addOnSelectListener = function(listener) {
+ this._onSelectListeners.push(listener);
+};
+
+Timeline.CompactEventPainter.prototype.removeOnSelectListener = function(listener) {
+ for (var i = 0; i < this._onSelectListeners.length; i++) {
+ if (this._onSelectListeners[i] == listener) {
+ this._onSelectListeners.splice(i, 1);
+ break;
+ }
+ }
+};
+
+Timeline.CompactEventPainter.prototype.getFilterMatcher = function() {
+ return this._filterMatcher;
+};
+
+Timeline.CompactEventPainter.prototype.setFilterMatcher = function(filterMatcher) {
+ this._filterMatcher = filterMatcher;
+};
+
+Timeline.CompactEventPainter.prototype.getHighlightMatcher = function() {
+ return this._highlightMatcher;
+};
+
+Timeline.CompactEventPainter.prototype.setHighlightMatcher = function(highlightMatcher) {
+ this._highlightMatcher = highlightMatcher;
+};
+
+Timeline.CompactEventPainter.prototype.paint = function() {
+ var eventSource = this._band.getEventSource();
+ if (eventSource == null) {
+ return;
+ }
+
+ this._eventIdToElmt = {};
+ this._prepareForPainting();
+
+ var theme = this._params.theme;
+ var eventTheme = theme.event;
+
+ var metrics = {
+ trackOffset: "trackOffset" in this._params ? this._params.trackOffset : 10,
+ trackHeight: "trackHeight" in this._params ? this._params.trackHeight : 10,
+
+ tapeHeight: theme.event.tape.height,
+ tapeBottomMargin: "tapeBottomMargin" in this._params ? this._params.tapeBottomMargin : 2,
+
+ labelBottomMargin: "labelBottomMargin" in this._params ? this._params.labelBottomMargin : 5,
+ labelRightMargin: "labelRightMargin" in this._params ? this._params.labelRightMargin : 5,
+
+ defaultIcon: eventTheme.instant.icon,
+ defaultIconWidth: eventTheme.instant.iconWidth,
+ defaultIconHeight: eventTheme.instant.iconHeight,
+
+ customIconWidth: "iconWidth" in this._params ? this._params.iconWidth : eventTheme.instant.iconWidth,
+ customIconHeight: "iconHeight" in this._params ? this._params.iconHeight : eventTheme.instant.iconHeight,
+
+ iconLabelGap: "iconLabelGap" in this._params ? this._params.iconLabelGap : 2,
+ iconBottomMargin: "iconBottomMargin" in this._params ? this._params.iconBottomMargin : 2
+ };
+ if ("compositeIcon" in this._params) {
+ metrics.compositeIcon = this._params.compositeIcon;
+ metrics.compositeIconWidth = this._params.compositeIconWidth || metrics.customIconWidth;
+ metrics.compositeIconHeight = this._params.compositeIconHeight || metrics.customIconHeight;
+ } else {
+ metrics.compositeIcon = metrics.defaultIcon;
+ metrics.compositeIconWidth = metrics.defaultIconWidth;
+ metrics.compositeIconHeight = metrics.defaultIconHeight;
+ }
+ metrics.defaultStackIcon = "icon" in this._params.stackConcurrentPreciseInstantEvents ?
+ this._params.stackConcurrentPreciseInstantEvents.icon : metrics.defaultIcon;
+ metrics.defaultStackIconWidth = "iconWidth" in this._params.stackConcurrentPreciseInstantEvents ?
+ this._params.stackConcurrentPreciseInstantEvents.iconWidth : metrics.defaultIconWidth;
+ metrics.defaultStackIconHeight = "iconHeight" in this._params.stackConcurrentPreciseInstantEvents ?
+ this._params.stackConcurrentPreciseInstantEvents.iconHeight : metrics.defaultIconHeight;
+
+ var minDate = this._band.getMinDate();
+ var maxDate = this._band.getMaxDate();
+
+ var filterMatcher = (this._filterMatcher != null) ?
+ this._filterMatcher :
+ function(evt) { return true; };
+
+ var highlightMatcher = (this._highlightMatcher != null) ?
+ this._highlightMatcher :
+ function(evt) { return -1; };
+
+ var iterator = eventSource.getEventIterator(minDate, maxDate);
+
+ var stackConcurrentPreciseInstantEvents = "stackConcurrentPreciseInstantEvents" in this._params && typeof this._params.stackConcurrentPreciseInstantEvents == "object";
+ var collapseConcurrentPreciseInstantEvents = "collapseConcurrentPreciseInstantEvents" in this._params && this._params.collapseConcurrentPreciseInstantEvents;
+ if (collapseConcurrentPreciseInstantEvents || stackConcurrentPreciseInstantEvents) {
+ var bufferedEvents = [];
+ var previousInstantEvent = null;
+
+ while (iterator.hasNext()) {
+ var evt = iterator.next();
+ if (filterMatcher(evt)) {
+ if (!evt.isInstant() || evt.isImprecise()) {
+ this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
+ } else if (previousInstantEvent != null &&
+ previousInstantEvent.getStart().getTime() == evt.getStart().getTime()) {
+ bufferedEvents[bufferedEvents.length - 1].push(evt);
+ } else {
+ bufferedEvents.push([ evt ]);
+ previousInstantEvent = evt;
+ }
+ }
+ }
+
+ for (var i = 0; i < bufferedEvents.length; i++) {
+ var compositeEvents = bufferedEvents[i];
+ if (compositeEvents.length == 1) {
+ this.paintEvent(compositeEvents[0], metrics, this._params.theme, highlightMatcher(evt));
+ } else {
+ var match = -1;
+ for (var j = 0; match < 0 && j < compositeEvents.length; j++) {
+ match = highlightMatcher(compositeEvents[j]);
+ }
+
+ if (stackConcurrentPreciseInstantEvents) {
+ this.paintStackedPreciseInstantEvents(compositeEvents, metrics, this._params.theme, match);
+ } else {
+ this.paintCompositePreciseInstantEvents(compositeEvents, metrics, this._params.theme, match);
+ }
+ }
+ }
+ } else {
+ while (iterator.hasNext()) {
+ var evt = iterator.next();
+ if (filterMatcher(evt)) {
+ this.paintEvent(evt, metrics, this._params.theme, highlightMatcher(evt));
+ }
+ }
+ }
+
+ this._highlightLayer.style.display = "block";
+ this._lineLayer.style.display = "block";
+ this._eventLayer.style.display = "block";
+};
+
+Timeline.CompactEventPainter.prototype.softPaint = function() {
+};
+
+Timeline.CompactEventPainter.prototype._prepareForPainting = function() {
+ var band = this._band;
+
+ if (this._backLayer == null) {
+ this._backLayer = this._band.createLayerDiv(0, "timeline-band-events");
+ this._backLayer.style.visibility = "hidden";
+
+ var eventLabelPrototype = document.createElement("span");
+ eventLabelPrototype.className = "timeline-event-label";
+ this._backLayer.appendChild(eventLabelPrototype);
+ this._frc = SimileAjax.Graphics.getFontRenderingContext(eventLabelPrototype);
+ }
+ this._frc.update();
+ this._tracks = [];
+
+ if (this._highlightLayer != null) {
+ band.removeLayerDiv(this._highlightLayer);
+ }
+ this._highlightLayer = band.createLayerDiv(105, "timeline-band-highlights");
+ this._highlightLayer.style.display = "none";
+
+ if (this._lineLayer != null) {
+ band.removeLayerDiv(this._lineLayer);
+ }
+ this._lineLayer = band.createLayerDiv(110, "timeline-band-lines");
+ this._lineLayer.style.display = "none";
+
+ if (this._eventLayer != null) {
+ band.removeLayerDiv(this._eventLayer);
+ }
+ this._eventLayer = band.createLayerDiv(115, "timeline-band-events");
+ this._eventLayer.style.display = "none";
+};
+
+Timeline.CompactEventPainter.prototype.paintEvent = function(evt, metrics, theme, highlightIndex) {
+ if (evt.isInstant()) {
+ this.paintInstantEvent(evt, metrics, theme, highlightIndex);
+ } else {
+ this.paintDurationEvent(evt, metrics, theme, highlightIndex);
+ }
+};
+
+Timeline.CompactEventPainter.prototype.paintInstantEvent = function(evt, metrics, theme, highlightIndex) {
+ if (evt.isImprecise()) {
+ this.paintImpreciseInstantEvent(evt, metrics, theme, highlightIndex);
+ } else {
+ this.paintPreciseInstantEvent(evt, metrics, theme, highlightIndex);
+ }
+}
+
+Timeline.CompactEventPainter.prototype.paintDurationEvent = function(evt, metrics, theme, highlightIndex) {
+ if (evt.isImprecise()) {
+ this.paintImpreciseDurationEvent(evt, metrics, theme, highlightIndex);
+ } else {
+ this.paintPreciseDurationEvent(evt, metrics, theme, highlightIndex);
+ }
+}
+
+Timeline.CompactEventPainter.prototype.paintPreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+ var commonData = {
+ tooltip: evt.getProperty("tooltip") || evt.getText()
+ };
+
+ var iconData = {
+ url: evt.getIcon()
+ };
+ if (iconData.url == null) {
+ iconData.url = metrics.defaultIcon;
+ iconData.width = metrics.defaultIconWidth;
+ iconData.height = metrics.defaultIconHeight;
+ iconData.className = "timeline-event-icon-default";
+ } else {
+ iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
+ iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
+ }
+
+ var labelData = {
+ text: evt.getText(),
+ color: evt.getTextColor() || evt.getColor(),
+ className: evt.getClassName()
+ };
+
+ var result = this.paintTapeIconLabel(
+ evt.getStart(),
+ commonData,
+ null, // no tape data
+ iconData,
+ labelData,
+ metrics,
+ theme,
+ highlightIndex
+ );
+
+ var self = this;
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
+ };
+ SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
+
+ this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
+};
+
+Timeline.CompactEventPainter.prototype.paintCompositePreciseInstantEvents = function(events, metrics, theme, highlightIndex) {
+ var evt = events[0];
+
+ var tooltips = [];
+ for (var i = 0; i < events.length; i++) {
+ tooltips.push(events[i].getProperty("tooltip") || events[i].getText());
+ }
+ var commonData = {
+ tooltip: tooltips.join("; ")
+ };
+
+ var iconData = {
+ url: metrics.compositeIcon,
+ width: metrics.compositeIconWidth,
+ height: metrics.compositeIconHeight,
+ className: "timeline-event-icon-composite"
+ };
+
+ var labelData = {
+ text: String.substitute(this._params.compositeEventLabelTemplate, [ events.length ])
+ };
+
+ var result = this.paintTapeIconLabel(
+ evt.getStart(),
+ commonData,
+ null, // no tape data
+ iconData,
+ labelData,
+ metrics,
+ theme,
+ highlightIndex
+ );
+
+ var self = this;
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickMultiplePreciseInstantEvent(result.iconElmtData.elmt, domEvt, events);
+ };
+
+ SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
+
+ for (var i = 0; i < events.length; i++) {
+ this._eventIdToElmt[events[i].getID()] = result.iconElmtData.elmt;
+ }
+};
+
+Timeline.CompactEventPainter.prototype.paintStackedPreciseInstantEvents = function(events, metrics, theme, highlightIndex) {
+ var limit = "limit" in this._params.stackConcurrentPreciseInstantEvents ?
+ this._params.stackConcurrentPreciseInstantEvents.limit : 10;
+ var moreMessageTemplate = "moreMessageTemplate" in this._params.stackConcurrentPreciseInstantEvents ?
+ this._params.stackConcurrentPreciseInstantEvents.moreMessageTemplate : "%0 More Events";
+ var showMoreMessage = limit <= events.length - 2; // We want at least 2 more events above the limit.
+ // Otherwise we'd need the singular case of "1 More Event"
+
+ var band = this._band;
+ var getPixelOffset = function(date) {
+ return Math.round(band.dateToPixelOffset(date));
+ };
+ var getIconData = function(evt) {
+ var iconData = {
+ url: evt.getIcon()
+ };
+ if (iconData.url == null) {
+ iconData.url = metrics.defaultStackIcon;
+ iconData.width = metrics.defaultStackIconWidth;
+ iconData.height = metrics.defaultStackIconHeight;
+ iconData.className = "timeline-event-icon-stack timeline-event-icon-default";
+ } else {
+ iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
+ iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
+ iconData.className = "timeline-event-icon-stack";
+ }
+ return iconData;
+ };
+
+ var firstIconData = getIconData(events[0]);
+ var horizontalIncrement = 5;
+ var leftIconEdge = 0;
+ var totalLabelWidth = 0;
+ var totalLabelHeight = 0;
+ var totalIconHeight = 0;
+
+ var records = [];
+ for (var i = 0; i < events.length && (!showMoreMessage || i < limit); i++) {
+ var evt = events[i];
+ var text = evt.getText();
+ var iconData = getIconData(evt);
+ var labelSize = this._frc.computeSize(text);
+ var record = {
+ text: text,
+ iconData: iconData,
+ labelSize: labelSize,
+ iconLeft: firstIconData.width + i * horizontalIncrement - iconData.width
+ };
+ record.labelLeft = firstIconData.width + i * horizontalIncrement + metrics.iconLabelGap;
+ record.top = totalLabelHeight;
+ records.push(record);
+
+ leftIconEdge = Math.min(leftIconEdge, record.iconLeft);
+ totalLabelHeight += labelSize.height;
+ totalLabelWidth = Math.max(totalLabelWidth, record.labelLeft + labelSize.width);
+ totalIconHeight = Math.max(totalIconHeight, record.top + iconData.height);
+ }
+ if (showMoreMessage) {
+ var moreMessage = String.substitute(moreMessageTemplate, [ events.length - limit ]);
+
+ var moreMessageLabelSize = this._frc.computeSize(moreMessage);
+ var moreMessageLabelLeft = firstIconData.width + (limit - 1) * horizontalIncrement + metrics.iconLabelGap;
+ var moreMessageLabelTop = totalLabelHeight;
+
+ totalLabelHeight += moreMessageLabelSize.height;
+ totalLabelWidth = Math.max(totalLabelWidth, moreMessageLabelLeft + moreMessageLabelSize.width);
+ }
+ totalLabelWidth += metrics.labelRightMargin;
+ totalLabelHeight += metrics.labelBottomMargin;
+ totalIconHeight += metrics.iconBottomMargin;
+
+ var anchorPixel = getPixelOffset(events[0].getStart());
+ var newTracks = [];
+
+ var trackCount = Math.ceil(Math.max(totalIconHeight, totalLabelHeight) / metrics.trackHeight);
+ var rightIconEdge = firstIconData.width + (events.length - 1) * horizontalIncrement;
+ for (var i = 0; i < trackCount; i++) {
+ newTracks.push({ start: leftIconEdge, end: rightIconEdge });
+ }
+ var labelTrackCount = Math.ceil(totalLabelHeight / metrics.trackHeight);
+ for (var i = 0; i < labelTrackCount; i++) {
+ var track = newTracks[i];
+ track.end = Math.max(track.end, totalLabelWidth);
+ }
+
+ var firstTrack = this._fitTracks(anchorPixel, newTracks);
+ var verticalPixelOffset = firstTrack * metrics.trackHeight + metrics.trackOffset;
+
+ var iconStackDiv = this._timeline.getDocument().createElement("div");
+ iconStackDiv.className = 'timeline-event-icon-stack';
+ iconStackDiv.style.position = "absolute";
+ iconStackDiv.style.overflow = "visible";
+ iconStackDiv.style.left = anchorPixel + "px";
+ iconStackDiv.style.top = verticalPixelOffset + "px";
+ iconStackDiv.style.width = rightIconEdge + "px";
+ iconStackDiv.style.height = totalIconHeight + "px";
+ iconStackDiv.innerHTML = "";
+ this._eventLayer.appendChild(iconStackDiv);
+
+ var self = this;
+ var onMouseOver = function(domEvt) {
+ try {
+ var n = parseInt(this.getAttribute("index"));
+ var childNodes = iconStackDiv.firstChild.childNodes;
+ for (var i = 0; i < childNodes.length; i++) {
+ var child = childNodes[i];
+ if (i == n) {
+ child.style.zIndex = childNodes.length;
+ } else {
+ child.style.zIndex = childNodes.length - i;
+ }
+ }
+ } catch (e) {
+ }
+ };
+ var paintEvent = function(index) {
+ var record = records[index];
+ var evt = events[index];
+ var tooltip = evt.getProperty("tooltip") || evt.getText();
+
+ var labelElmtData = self._paintEventLabel(
+ { tooltip: tooltip },
+ { text: record.text },
+ anchorPixel + record.labelLeft,
+ verticalPixelOffset + record.top,
+ record.labelSize.width,
+ record.labelSize.height,
+ theme
+ );
+ labelElmtData.elmt.setAttribute("index", index);
+ labelElmtData.elmt.onmouseover = onMouseOver;
+
+ var img = SimileAjax.Graphics.createTranslucentImage(record.iconData.url);
+ var iconDiv = self._timeline.getDocument().createElement("div");
+ iconDiv.className = 'timeline-event-icon' + ("className" in record.iconData ? (" " + record.iconData.className) : "");
+ iconDiv.style.left = record.iconLeft + "px";
+ iconDiv.style.top = record.top + "px";
+ iconDiv.style.zIndex = (records.length - index);
+ iconDiv.appendChild(img);
+ iconDiv.setAttribute("index", index);
+ iconDiv.onmouseover = onMouseOver;
+
+ iconStackDiv.firstChild.appendChild(iconDiv);
+
+ var clickHandler = function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(labelElmtData.elmt, domEvt, evt);
+ };
+
+ SimileAjax.DOM.registerEvent(iconDiv, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(labelElmtData.elmt, "mousedown", clickHandler);
+
+ self._eventIdToElmt[evt.getID()] = iconDiv;
+ };
+ for (var i = 0; i < records.length; i++) {
+ paintEvent(i);
+ }
+
+ if (showMoreMessage) {
+ var moreEvents = events.slice(limit);
+ var moreMessageLabelElmtData = this._paintEventLabel(
+ { tooltip: moreMessage },
+ { text: moreMessage },
+ anchorPixel + moreMessageLabelLeft,
+ verticalPixelOffset + moreMessageLabelTop,
+ moreMessageLabelSize.width,
+ moreMessageLabelSize.height,
+ theme
+ );
+
+ var moreMessageClickHandler = function(elmt, domEvt, target) {
+ return self._onClickMultiplePreciseInstantEvent(moreMessageLabelElmtData.elmt, domEvt, moreEvents);
+ };
+ SimileAjax.DOM.registerEvent(moreMessageLabelElmtData.elmt, "mousedown", moreMessageClickHandler);
+
+ for (var i = 0; i < moreEvents.length; i++) {
+ this._eventIdToElmt[moreEvents[i].getID()] = moreMessageLabelElmtData.elmt;
+ }
+ }
+ //this._createHighlightDiv(highlightIndex, iconElmtData, theme);
+};
+
+Timeline.CompactEventPainter.prototype.paintImpreciseInstantEvent = function(evt, metrics, theme, highlightIndex) {
+ var commonData = {
+ tooltip: evt.getProperty("tooltip") || evt.getText()
+ };
+
+ var tapeData = {
+ start: evt.getStart(),
+ end: evt.getEnd(),
+ latestStart: evt.getLatestStart(),
+ earliestEnd: evt.getEarliestEnd(),
+ isInstant: true
+ };
+
+ var iconData = {
+ url: evt.getIcon()
+ };
+ if (iconData.url == null) {
+ iconData = null;
+ } else {
+ iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
+ iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
+ }
+
+ var labelData = {
+ text: evt.getText(),
+ color: evt.getTextColor() || evt.getColor(),
+ className: evt.getClassName()
+ };
+
+ var result = this.paintTapeIconLabel(
+ evt.getStart(),
+ commonData,
+ tapeData, // no tape data
+ iconData,
+ labelData,
+ metrics,
+ theme,
+ highlightIndex
+ );
+
+ var self = this;
+ var clickHandler = iconData != null ?
+ function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
+ } :
+ function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
+ };
+
+ SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(result.impreciseTapeElmtData.elmt, "mousedown", clickHandler);
+
+ if (iconData != null) {
+ SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
+ this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
+ } else {
+ this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
+ }
+};
+
+Timeline.CompactEventPainter.prototype.paintPreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+ var commonData = {
+ tooltip: evt.getProperty("tooltip") || evt.getText()
+ };
+
+ var tapeData = {
+ start: evt.getStart(),
+ end: evt.getEnd(),
+ isInstant: false
+ };
+
+ var iconData = {
+ url: evt.getIcon()
+ };
+ if (iconData.url == null) {
+ iconData = null;
+ } else {
+ iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
+ iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
+ }
+
+ var labelData = {
+ text: evt.getText(),
+ color: evt.getTextColor() || evt.getColor(),
+ className: evt.getClassName()
+ };
+
+ var result = this.paintTapeIconLabel(
+ evt.getLatestStart(),
+ commonData,
+ tapeData, // no tape data
+ iconData,
+ labelData,
+ metrics,
+ theme,
+ highlightIndex
+ );
+
+ var self = this;
+ var clickHandler = iconData != null ?
+ function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
+ } :
+ function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
+ };
+
+ SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(result.tapeElmtData.elmt, "mousedown", clickHandler);
+
+ if (iconData != null) {
+ SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
+ this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
+ } else {
+ this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
+ }
+};
+
+Timeline.CompactEventPainter.prototype.paintImpreciseDurationEvent = function(evt, metrics, theme, highlightIndex) {
+ var commonData = {
+ tooltip: evt.getProperty("tooltip") || evt.getText()
+ };
+
+ var tapeData = {
+ start: evt.getStart(),
+ end: evt.getEnd(),
+ latestStart: evt.getLatestStart(),
+ earliestEnd: evt.getEarliestEnd(),
+ isInstant: false
+ };
+
+ var iconData = {
+ url: evt.getIcon()
+ };
+ if (iconData.url == null) {
+ iconData = null;
+ } else {
+ iconData.width = evt.getProperty("iconWidth") || metrics.customIconWidth;
+ iconData.height = evt.getProperty("iconHeight") || metrics.customIconHeight;
+ }
+
+ var labelData = {
+ text: evt.getText(),
+ color: evt.getTextColor() || evt.getColor(),
+ className: evt.getClassName()
+ };
+
+ var result = this.paintTapeIconLabel(
+ evt.getLatestStart(),
+ commonData,
+ tapeData, // no tape data
+ iconData,
+ labelData,
+ metrics,
+ theme,
+ highlightIndex
+ );
+
+ var self = this;
+ var clickHandler = iconData != null ?
+ function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(result.iconElmtData.elmt, domEvt, evt);
+ } :
+ function(elmt, domEvt, target) {
+ return self._onClickInstantEvent(result.labelElmtData.elmt, domEvt, evt);
+ };
+
+ SimileAjax.DOM.registerEvent(result.labelElmtData.elmt, "mousedown", clickHandler);
+ SimileAjax.DOM.registerEvent(result.tapeElmtData.elmt, "mousedown", clickHandler);
+
+ if (iconData != null) {
+ SimileAjax.DOM.registerEvent(result.iconElmtData.elmt, "mousedown", clickHandler);
+ this._eventIdToElmt[evt.getID()] = result.iconElmtData.elmt;
+ } else {
+ this._eventIdToElmt[evt.getID()] = result.labelElmtData.elmt;
+ }
+};
+
+Timeline.CompactEventPainter.prototype.paintTapeIconLabel = function(
+ anchorDate,
+ commonData,
+ tapeData,
+ iconData,
+ labelData,
+ metrics,
+ theme,
+ highlightIndex
+) {
+ var band = this._band;
+ var getPixelOffset = function(date) {
+ return Math.round(band.dateToPixelOffset(date));
+ };
+
+ var anchorPixel = getPixelOffset(anchorDate);
+ var newTracks = [];
+
+ var tapeHeightOccupied = 0; // how many pixels (vertically) the tape occupies, including bottom margin
+ var tapeTrackCount = 0; // how many tracks the tape takes up, usually just 1
+ var tapeLastTrackExtraSpace = 0; // on the last track that the tape occupies, how many pixels are left (for icon and label to occupy as well)
+ if (tapeData != null) {
+ tapeHeightOccupied = metrics.tapeHeight + metrics.tapeBottomMargin;
+ tapeTrackCount = Math.ceil(metrics.tapeHeight / metrics.trackHeight);
+
+ var tapeEndPixelOffset = getPixelOffset(tapeData.end) - anchorPixel;
+ var tapeStartPixelOffset = getPixelOffset(tapeData.start) - anchorPixel;
+
+ for (var t = 0; t < tapeTrackCount; t++) {
+ newTracks.push({ start: tapeStartPixelOffset, end: tapeEndPixelOffset });
+ }
+
+ tapeLastTrackExtraSpace = metrics.trackHeight - (tapeHeightOccupied % metrics.tapeHeight);
+ }
+
+ var iconStartPixelOffset = 0; // where the icon starts compared to the anchor pixel;
+ // this can be negative if the icon is center-aligned around the anchor
+ var iconHorizontalSpaceOccupied = 0; // how many pixels the icon take up from the anchor pixel,
+ // including the gap between the icon and the label
+ if (iconData != null) {
+ if ("iconAlign" in iconData && iconData.iconAlign == "center") {
+ iconStartPixelOffset = -Math.floor(iconData.width / 2);
+ }
+ iconHorizontalSpaceOccupied = iconStartPixelOffset + iconData.width + metrics.iconLabelGap;
+
+ if (tapeTrackCount > 0) {
+ newTracks[tapeTrackCount - 1].end = Math.max(newTracks[tapeTrackCount - 1].end, iconHorizontalSpaceOccupied);
+ }
+
+ var iconHeight = iconData.height + metrics.iconBottomMargin + tapeLastTrackExtraSpace;
+ while (iconHeight > 0) {
+ newTracks.push({ start: iconStartPixelOffset, end: iconHorizontalSpaceOccupied });
+ iconHeight -= metrics.trackHeight;
+ }
+ }
+
+ var text = labelData.text;
+ var labelSize = this._frc.computeSize(text);
+ var labelHeight = labelSize.height + metrics.labelBottomMargin + tapeLastTrackExtraSpace;
+ var labelEndPixelOffset = iconHorizontalSpaceOccupied + labelSize.width + metrics.labelRightMargin;
+ if (tapeTrackCount > 0) {
+ newTracks[tapeTrackCount - 1].end = Math.max(newTracks[tapeTrackCount - 1].end, labelEndPixelOffset);
+ }
+ for (var i = 0; labelHeight > 0; i++) {
+ if (tapeTrackCount + i < newTracks.length) {
+ var track = newTracks[tapeTrackCount + i];
+ track.end = labelEndPixelOffset;
+ } else {
+ newTracks.push({ start: 0, end: labelEndPixelOffset });
+ }
+ labelHeight -= metrics.trackHeight;
+ }
+
+ /*
+ * Try to fit the new track on top of the existing tracks, then
+ * render the various elements.
+ */
+ var firstTrack = this._fitTracks(anchorPixel, newTracks);
+ var verticalPixelOffset = firstTrack * metrics.trackHeight + metrics.trackOffset;
+ var result = {};
+
+ result.labelElmtData = this._paintEventLabel(
+ commonData,
+ labelData,
+ anchorPixel + iconHorizontalSpaceOccupied,
+ verticalPixelOffset + tapeHeightOccupied,
+ labelSize.width,
+ labelSize.height,
+ theme
+ );
+
+ if (tapeData != null) {
+ if ("latestStart" in tapeData || "earliestEnd" in tapeData) {
+ result.impreciseTapeElmtData = this._paintEventTape(
+ commonData,
+ tapeData,
+ metrics.tapeHeight,
+ verticalPixelOffset,
+ getPixelOffset(tapeData.start),
+ getPixelOffset(tapeData.end),
+ theme.event.duration.impreciseColor,
+ theme.event.duration.impreciseOpacity,
+ metrics,
+ theme
+ );
+ }
+ if (!tapeData.isInstant && "start" in tapeData && "end" in tapeData) {
+ result.tapeElmtData = this._paintEventTape(
+ commonData,
+ tapeData,
+ metrics.tapeHeight,
+ verticalPixelOffset,
+ anchorPixel,
+ getPixelOffset("earliestEnd" in tapeData ? tapeData.earliestEnd : tapeData.end),
+ tapeData.color,
+ 100,
+ metrics,
+ theme
+ );
+ }
+ }
+
+ if (iconData != null) {
+ result.iconElmtData = this._paintEventIcon(
+ commonData,
+ iconData,
+ verticalPixelOffset + tapeHeightOccupied,
+ anchorPixel + iconStartPixelOffset,
+ metrics,
+ theme
+ );
+ }
+ //this._createHighlightDiv(highlightIndex, iconElmtData, theme);
+
+ return result;
+};
+
+Timeline.CompactEventPainter.prototype._fitTracks = function(anchorPixel, newTracks) {
+ var firstTrack;
+ for (firstTrack = 0; firstTrack < this._tracks.length; firstTrack++) {
+ var fit = true;
+ for (var j = 0; j < newTracks.length && (firstTrack + j) < this._tracks.length; j++) {
+ var existingTrack = this._tracks[firstTrack + j];
+ var newTrack = newTracks[j];
+ if (anchorPixel + newTrack.start < existingTrack) {
+ fit = false;
+ break;
+ }
+ }
+
+ if (fit) {
+ break;
+ }
+ }
+ for (var i = 0; i < newTracks.length; i++) {
+ this._tracks[firstTrack + i] = anchorPixel + newTracks[i].end;
+ }
+
+ return firstTrack;
+};
+
+
+Timeline.CompactEventPainter.prototype._paintEventIcon = function(commonData, iconData, top, left, metrics, theme) {
+ var img = SimileAjax.Graphics.createTranslucentImage(iconData.url);
+ var iconDiv = this._timeline.getDocument().createElement("div");
+ iconDiv.className = 'timeline-event-icon' + ("className" in iconData ? (" " + iconData.className) : "");
+ iconDiv.style.left = left + "px";
+ iconDiv.style.top = top + "px";
+ iconDiv.appendChild(img);
+
+ if ("tooltip" in commonData && typeof commonData.tooltip == "string") {
+ iconDiv.title = commonData.tooltip;
+ }
+
+ this._eventLayer.appendChild(iconDiv);
+
+ return {
+ left: left,
+ top: top,
+ width: metrics.iconWidth,
+ height: metrics.iconHeight,
+ elmt: iconDiv
+ };
+};
+
+Timeline.CompactEventPainter.prototype._paintEventLabel = function(commonData, labelData, left, top, width, height, theme) {
+ var doc = this._timeline.getDocument();
+
+ var labelDiv = doc.createElement("div");
+ labelDiv.className = 'timeline-event-label';
+
+ labelDiv.style.left = left + "px";
+ labelDiv.style.width = (width + 1) + "px";
+ labelDiv.style.top = top + "px";
+ labelDiv.innerHTML = labelData.text;
+
+ if ("tooltip" in commonData && typeof commonData.tooltip == "string") {
+ labelDiv.title = commonData.tooltip;
+ }
+ if ("color" in labelData && typeof labelData.color == "string") {
+ labelDiv.style.color = labelData.color;
+ }
+ if ("className" in labelData && typeof labelData.className == "string") {
+ labelDiv.className += ' ' + labelData.className;
+ }
+
+ this._eventLayer.appendChild(labelDiv);
+
+ return {
+ left: left,
+ top: top,
+ width: width,
+ height: height,
+ elmt: labelDiv
+ };
+};
+
+Timeline.CompactEventPainter.prototype._paintEventTape = function(
+ commonData, tapeData, height, top, startPixel, endPixel, color, opacity, metrics, theme) {
+
+ var width = endPixel - startPixel;
+
+ var tapeDiv = this._timeline.getDocument().createElement("div");
+ tapeDiv.className = "timeline-event-tape"
+
+ tapeDiv.style.left = startPixel + "px";
+ tapeDiv.style.top = top + "px";
+ tapeDiv.style.width = width + "px";
+ tapeDiv.style.height = height + "px";
+
+ if ("tooltip" in commonData && typeof commonData.tooltip == "string") {
+ tapeDiv.title = commonData.tooltip;
+ }
+ if (color != null && typeof tapeData.color == "string") {
+ tapeDiv.style.backgroundColor = color;
+ }
+
+ if ("backgroundImage" in tapeData && typeof tapeData.backgroundImage == "string") {
+ tapeDiv.style.backgroundImage = "url(" + backgroundImage + ")";
+ tapeDiv.style.backgroundRepeat =
+ ("backgroundRepeat" in tapeData && typeof tapeData.backgroundRepeat == "string")
+ ? tapeData.backgroundRepeat : 'repeat';
+ }
+
+ SimileAjax.Graphics.setOpacity(tapeDiv, opacity);
+
+ if ("className" in tapeData && typeof tapeData.className == "string") {
+ tapeDiv.className += ' ' + tapeData.className;
+ }
+
+ this._eventLayer.appendChild(tapeDiv);
+
+ return {
+ left: startPixel,
+ top: top,
+ width: width,
+ height: height,
+ elmt: tapeDiv
+ };
+}
+
+Timeline.CompactEventPainter.prototype._createHighlightDiv = function(highlightIndex, dimensions, theme) {
+ if (highlightIndex >= 0) {
+ var doc = this._timeline.getDocument();
+ var eventTheme = theme.event;
+
+ var color = eventTheme.highlightColors[Math.min(highlightIndex, eventTheme.highlightColors.length - 1)];
+
+ var div = doc.createElement("div");
+ div.style.position = "absolute";
+ div.style.overflow = "hidden";
+ div.style.left = (dimensions.left - 2) + "px";
+ div.style.width = (dimensions.width + 4) + "px";
+ div.style.top = (dimensions.top - 2) + "px";
+ div.style.height = (dimensions.height + 4) + "px";
+// div.style.background = color;
+
+ this._highlightLayer.appendChild(div);
+ }
+};
+
+Timeline.CompactEventPainter.prototype._onClickMultiplePreciseInstantEvent = function(icon, domEvt, events) {
+ var c = SimileAjax.DOM.getPageCoordinates(icon);
+ this._showBubble(
+ c.left + Math.ceil(icon.offsetWidth / 2),
+ c.top + Math.ceil(icon.offsetHeight / 2),
+ events
+ );
+
+ var ids = [];
+ for (var i = 0; i < events.length; i++) {
+ ids.push(events[i].getID());
+ }
+ this._fireOnSelect(ids);
+
+ domEvt.cancelBubble = true;
+ SimileAjax.DOM.cancelEvent(domEvt);
+
+ return false;
+};
+
+Timeline.CompactEventPainter.prototype._onClickInstantEvent = function(icon, domEvt, evt) {
+ var c = SimileAjax.DOM.getPageCoordinates(icon);
+ this._showBubble(
+ c.left + Math.ceil(icon.offsetWidth / 2),
+ c.top + Math.ceil(icon.offsetHeight / 2),
+ [evt]
+ );
+ this._fireOnSelect([evt.getID()]);
+
+ domEvt.cancelBubble = true;
+ SimileAjax.DOM.cancelEvent(domEvt);
+ return false;
+};
+
+Timeline.CompactEventPainter.prototype._onClickDurationEvent = function(target, domEvt, evt) {
+ if ("pageX" in domEvt) {
+ var x = domEvt.pageX;
+ var y = domEvt.pageY;
+ } else {
+ var c = SimileAjax.DOM.getPageCoordinates(target);
+ var x = domEvt.offsetX + c.left;
+ var y = domEvt.offsetY + c.top;
+ }
+ this._showBubble(x, y, [evt]);
+ this._fireOnSelect([evt.getID()]);
+
+ domEvt.cancelBubble = true;
+ SimileAjax.DOM.cancelEvent(domEvt);
+ return false;
+};
+
+Timeline.CompactEventPainter.prototype.showBubble = function(evt) {
+ var elmt = this._eventIdToElmt[evt.getID()];
+ if (elmt) {
+ var c = SimileAjax.DOM.getPageCoordinates(elmt);
+ this._showBubble(c.left + elmt.offsetWidth / 2, c.top + elmt.offsetHeight / 2, [evt]);
+ }
+};
+
+Timeline.CompactEventPainter.prototype._showBubble = function(x, y, evts) {
+ var div = document.createElement("div");
+
+ evts = ("fillInfoBubble" in evts) ? [evts] : evts;
+ for (var i = 0; i < evts.length; i++) {
+ var div2 = document.createElement("div");
+ div.appendChild(div2);
+
+ evts[i].fillInfoBubble(div2, this._params.theme, this._band.getLabeller());
+ }
+
+ SimileAjax.WindowManager.cancelPopups();
+ SimileAjax.Graphics.createBubbleForContentAndPoint(div, x, y, this._params.theme.event.bubble.width);
+};
+
+Timeline.CompactEventPainter.prototype._fireOnSelect = function(eventIDs) {
+ for (var i = 0; i < this._onSelectListeners.length; i++) {
+ this._onSelectListeners[i](eventIDs);
+ }
+};
+/*==================================================
+ * Span Highlight Decorator
+ *==================================================
+ */
+
+Timeline.SpanHighlightDecorator = function(params) {
+ // When evaluating params, test against null. Not "p in params". Testing against
+ // null enables caller to explicitly request the default. Testing against "in" means
+ // that the param has to be ommitted to get the default.
+ this._unit = params.unit != null ? params.unit : SimileAjax.NativeDateUnit;
+ this._startDate = (typeof params.startDate == "string") ?
+ this._unit.parseFromObject(params.startDate) : params.startDate;
+ this._endDate = (typeof params.endDate == "string") ?
+ this._unit.parseFromObject(params.endDate) : params.endDate;
+ this._startLabel = params.startLabel != null ? params.startLabel : ""; // not null!
+ this._endLabel = params.endLabel != null ? params.endLabel : ""; // not null!
+ this._color = params.color;
+ this._cssClass = params.cssClass != null ? params.cssClass : null;
+ this._opacity = params.opacity != null ? params.opacity : 100;
+ // Default z is 10, behind everything but background grid.
+ // If inFront, then place just behind events, in front of everything else
+ this._zIndex = (params.inFront != null && params.inFront) ? 113 : 10;
+};
+
+Timeline.SpanHighlightDecorator.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+
+ this._layerDiv = null;
+};
+
+Timeline.SpanHighlightDecorator.prototype.paint = function() {
+ if (this._layerDiv != null) {
+ this._band.removeLayerDiv(this._layerDiv);
+ }
+ this._layerDiv = this._band.createLayerDiv(this._zIndex);
+ this._layerDiv.setAttribute("name", "span-highlight-decorator"); // for debugging
+ this._layerDiv.style.display = "none";
+
+ var minDate = this._band.getMinDate();
+ var maxDate = this._band.getMaxDate();
+
+ if (this._unit.compare(this._startDate, maxDate) < 0 &&
+ this._unit.compare(this._endDate, minDate) > 0) {
+
+ minDate = this._unit.later(minDate, this._startDate);
+ maxDate = this._unit.earlier(maxDate, this._endDate);
+
+ var minPixel = this._band.dateToPixelOffset(minDate);
+ var maxPixel = this._band.dateToPixelOffset(maxDate);
+
+ var doc = this._timeline.getDocument();
+
+ var createTable = function() {
+ var table = doc.createElement("table");
+ table.insertRow(0).insertCell(0);
+ return table;
+ };
+
+ var div = doc.createElement("div");
+ div.className='timeline-highlight-decorator'
+ if(this._cssClass) {
+ div.className += ' ' + this._cssClass;
+ }
+ if(this._color != null) {
+ div.style.backgroundColor = this._color;
+ }
+ if (this._opacity < 100) {
+ SimileAjax.Graphics.setOpacity(div, this._opacity);
+ }
+ this._layerDiv.appendChild(div);
+
+ var tableStartLabel = createTable();
+ tableStartLabel.className = 'timeline-highlight-label timeline-highlight-label-start'
+ var tdStart = tableStartLabel.rows[0].cells[0]
+ tdStart.innerHTML = this._startLabel;
+ if (this._cssClass) {
+ tdStart.className = 'label_' + this._cssClass;
+ }
+ this._layerDiv.appendChild(tableStartLabel);
+
+ var tableEndLabel = createTable();
+ tableEndLabel.className = 'timeline-highlight-label timeline-highlight-label-end'
+ var tdEnd = tableEndLabel.rows[0].cells[0]
+ tdEnd.innerHTML = this._endLabel;
+ if (this._cssClass) {
+ tdEnd.className = 'label_' + this._cssClass;
+ }
+ this._layerDiv.appendChild(tableEndLabel);
+
+ if (this._timeline.isHorizontal()){
+ div.style.left = minPixel + "px";
+ div.style.width = (maxPixel - minPixel) + "px";
+
+ tableStartLabel.style.right = (this._band.getTotalViewLength() - minPixel) + "px";
+ tableStartLabel.style.width = (this._startLabel.length) + "em";
+
+ tableEndLabel.style.left = maxPixel + "px";
+ tableEndLabel.style.width = (this._endLabel.length) + "em";
+
+ } else {
+ div.style.top = minPixel + "px";
+ div.style.height = (maxPixel - minPixel) + "px";
+
+ tableStartLabel.style.bottom = minPixel + "px";
+ tableStartLabel.style.height = "1.5px";
+
+ tableEndLabel.style.top = maxPixel + "px";
+ tableEndLabel.style.height = "1.5px";
+ }
+ }
+ this._layerDiv.style.display = "block";
+};
+
+Timeline.SpanHighlightDecorator.prototype.softPaint = function() {
+};
+
+/*==================================================
+ * Point Highlight Decorator
+ *==================================================
+ */
+
+Timeline.PointHighlightDecorator = function(params) {
+ this._unit = params.unit != null ? params.unit : SimileAjax.NativeDateUnit;
+ this._date = (typeof params.date == "string") ?
+ this._unit.parseFromObject(params.date) : params.date;
+ this._width = params.width != null ? params.width : 10;
+ // Since the width is used to calculate placements (see minPixel, below), we
+ // specify width here, not in css.
+ this._color = params.color;
+ this._cssClass = params.cssClass != null ? params.cssClass : '';
+ this._opacity = params.opacity != null ? params.opacity : 100;
+};
+
+Timeline.PointHighlightDecorator.prototype.initialize = function(band, timeline) {
+ this._band = band;
+ this._timeline = timeline;
+ this._layerDiv = null;
+};
+
+Timeline.PointHighlightDecorator.prototype.paint = function() {
+ if (this._layerDiv != null) {
+ this._band.removeLayerDiv(this._layerDiv);
+ }
+ this._layerDiv = this._band.createLayerDiv(10);
+ this._layerDiv.setAttribute("name", "span-highlight-decorator"); // for debugging
+ this._layerDiv.style.display = "none";
+
+ var minDate = this._band.getMinDate();
+ var maxDate = this._band.getMaxDate();
+
+ if (this._unit.compare(this._date, maxDate) < 0 &&
+ this._unit.compare(this._date, minDate) > 0) {
+
+ var pixel = this._band.dateToPixelOffset(this._date);
+ var minPixel = pixel - Math.round(this._width / 2);
+
+ var doc = this._timeline.getDocument();
+
+ var div = doc.createElement("div");
+ div.className='timeline-highlight-point-decorator';
+ div.className += ' ' + this._cssClass;
+
+ if(this._color != null) {
+ div.style.backgroundColor = this._color;
+ }
+ if (this._opacity < 100) {
+ SimileAjax.Graphics.setOpacity(div, this._opacity);
+ }
+ this._layerDiv.appendChild(div);
+
+ if (this._timeline.isHorizontal()) {
+ div.style.left = minPixel + "px";
+ div.style.width = this._width;
+ } else {
+ div.style.top = minPixel + "px";
+ div.style.height = this._width;
+ }
+ }
+ this._layerDiv.style.display = "block";
+};
+
+Timeline.PointHighlightDecorator.prototype.softPaint = function() {
+};
+/*==================================================
+ * Default Unit
+ *==================================================
+ */
+
+Timeline.NativeDateUnit = new Object();
+
+Timeline.NativeDateUnit.createLabeller = function(locale, timeZone) {
+ return new Timeline.GregorianDateLabeller(locale, timeZone);
+};
+
+Timeline.NativeDateUnit.makeDefaultValue = function() {
+ return new Date();
+};
+
+Timeline.NativeDateUnit.cloneValue = function(v) {
+ return new Date(v.getTime());
+};
+
+Timeline.NativeDateUnit.getParser = function(format) {
+ if (typeof format == "string") {
+ format = format.toLowerCase();
+ }
+ return (format == "iso8601" || format == "iso 8601") ?
+ Timeline.DateTime.parseIso8601DateTime :
+ Timeline.DateTime.parseGregorianDateTime;
+};
+
+Timeline.NativeDateUnit.parseFromObject = function(o) {
+ return Timeline.DateTime.parseGregorianDateTime(o);
+};
+
+Timeline.NativeDateUnit.toNumber = function(v) {
+ return v.getTime();
+};
+
+Timeline.NativeDateUnit.fromNumber = function(n) {
+ return new Date(n);
+};
+
+Timeline.NativeDateUnit.compare = function(v1, v2) {
+ var n1, n2;
+ if (typeof v1 == "object") {
+ n1 = v1.getTime();
+ } else {
+ n1 = Number(v1);
+ }
+ if (typeof v2 == "object") {
+ n2 = v2.getTime();
+ } else {
+ n2 = Number(v2);
+ }
+
+ return n1 - n2;
+};
+
+Timeline.NativeDateUnit.earlier = function(v1, v2) {
+ return Timeline.NativeDateUnit.compare(v1, v2) < 0 ? v1 : v2;
+};
+
+Timeline.NativeDateUnit.later = function(v1, v2) {
+ return Timeline.NativeDateUnit.compare(v1, v2) > 0 ? v1 : v2;
+};
+
+Timeline.NativeDateUnit.change = function(v, n) {
+ return new Date(v.getTime() + n);
+};
+
+/*==================================================
+ * Common localization strings
+ *==================================================
+ */
+
+Timeline.strings["en"] = {
+ wikiLinkLabel: "Discuss"
+};
+
+/*==================================================
+ * Localization of labellers.js
+ *==================================================
+ */
+
+Timeline.GregorianDateLabeller.monthNames["en"] = [
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+];
+
+Timeline.GregorianDateLabeller.dayNames["en"] = [
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
+];
+/*
+* timeplot-ajax-basic.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301 USA
+*/
+
+/**
+ * basic code which is included in front of timeplot code for the minified version
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-25
+ * @version date: 2012-07-25
+ */
+
+if ( typeof window.Timeplot == "undefined") {
+ window.Timeplot = {
+ params : {
+ bundle : true,
+ autoCreate : true
+ },
+ namespace : "http://simile.mit.edu/2007/06/timeplot#",
+ importers : {}
+ };
+ Timeplot.urlPrefix = GeoTemCoMinifier_urlPrefix + 'lib/simile/timeplot/';
+}
+
+/*==================================================
+ * Simile Timeplot API
+ *
+ * Include Timeplot in your HTML file as follows:
+ *
+ *
+ *==================================================*/
+
+(function() {
+
+ var local = false;
+
+ // obtain local mode from the document URL
+ if (document.location.search.length > 0) {
+ var params = document.location.search.substr(1).split("&");
+ for (var i = 0; i < params.length; i++) {
+ if (params[i] == "local") {
+ local = true;
+ }
+ }
+ }
+
+ // obtain local mode from the script URL params attribute
+ if (!local) {
+ var heads = document.documentElement.getElementsByTagName("head");
+ for (var h = 0; h < heads.length; h++) {
+ var node = heads[h].firstChild;
+ while (node != null) {
+ if (node.nodeType == 1 && node.tagName.toLowerCase() == "script") {
+ var url = node.src;
+ if (url.indexOf("timeplot-api") >= 0) {
+ local = (url.indexOf("local") >= 0);
+ }
+ }
+ node = node.nextSibling;
+ }
+ }
+ }
+
+ // Load Timeplot if it's not already loaded (after SimileAjax and Timeline)
+ var loadTimeplot = function() {
+
+ if (typeof window.Timeplot != "undefined") {
+ return;
+ }
+
+ window.Timeplot = {
+ loaded: false,
+ params: { bundle: true, autoCreate: true },
+ namespace: "http://simile.mit.edu/2007/06/timeplot#",
+ importers: {}
+ };
+
+ var javascriptFiles = [
+ "timeplot.js",
+ "plot.js",
+ "sources.js",
+ "geometry.js",
+ "color.js",
+ "math.js",
+ "processor.js"
+ ];
+ var cssFiles = [
+ "timeplot.css"
+ ];
+
+ var locales = [ "en" ];
+
+ var defaultClientLocales = ("language" in navigator ? navigator.language : navigator.browserLanguage).split(";");
+ for (var l = 0; l < defaultClientLocales.length; l++) {
+ var locale = defaultClientLocales[l];
+ if (locale != "en") {
+ var segments = locale.split("-");
+ if (segments.length > 1 && segments[0] != "en") {
+ locales.push(segments[0]);
+ }
+ locales.push(locale);
+ }
+ }
+
+ var paramTypes = { bundle:Boolean, js:Array, css:Array, autoCreate:Boolean };
+ if (typeof Timeplot_urlPrefix == "string") {
+ Timeplot.urlPrefix = Timeplot_urlPrefix;
+ if ("Timeplot_parameters" in window) {
+ SimileAjax.parseURLParameters(Timeplot_parameters, Timeplot.params, paramTypes);
+ }
+ } else {
+ var url = SimileAjax.findScript(document, "/timeplot-api.js");
+ if (url == null) {
+ Timeplot.error = new Error("Failed to derive URL prefix for Simile Timeplot API code files");
+ return;
+ }
+ Timeplot.urlPrefix = url.substr(0, url.indexOf("timeplot-api.js"));
+
+ SimileAjax.parseURLParameters(url, Timeplot.params, paramTypes);
+ }
+
+ if (Timeplot.params.locale) { // ISO-639 language codes,
+ // optional ISO-3166 country codes (2 characters)
+ if (Timeplot.params.locale != "en") {
+ var segments = Timeplot.params.locale.split("-");
+ if (segments.length > 1 && segments[0] != "en") {
+ locales.push(segments[0]);
+ }
+ locales.push(Timeplot.params.locale);
+ }
+ }
+
+ var timeplotURLPrefix = Timeplot.urlPrefix;
+
+ if (local && !("console" in window)) {
+ var firebug = [ timeplotURLPrefix + "lib/firebug/firebug.js" ];
+ SimileAjax.includeJavascriptFiles(document, "", firebug);
+ }
+
+ var canvas = document.createElement("canvas");
+
+ if (!canvas.getContext) {
+ var excanvas = [ timeplotURLPrefix + "lib/excanvas.js" ];
+ SimileAjax.includeJavascriptFiles(document, "", excanvas);
+ }
+
+ var scriptURLs = Timeplot.params.js || [];
+ var cssURLs = Timeplot.params.css || [];
+
+ // Core scripts and styles
+ if (Timeplot.params.bundle && !local) {
+ scriptURLs.push(timeplotURLPrefix + "timeplot-bundle.js");
+ cssURLs.push(timeplotURLPrefix + "timeplot-bundle.css");
+ } else {
+ SimileAjax.prefixURLs(scriptURLs, timeplotURLPrefix + "scripts/", javascriptFiles);
+ SimileAjax.prefixURLs(cssURLs, timeplotURLPrefix + "styles/", cssFiles);
+ }
+
+ // Localization
+ //for (var i = 0; i < locales.length; i++) {
+ // scriptURLs.push(Timeplot.urlPrefix + "locales/" + locales[i] + "/locale.js");
+ //};
+
+ window.SimileAjax_onLoad = function() {
+ if (local && window.console.open) window.console.open();
+ if (Timeplot.params.callback) {
+ eval(Timeplot.params.callback + "()");
+ }
+ }
+
+ SimileAjax.includeJavascriptFiles(document, "", scriptURLs);
+ SimileAjax.includeCssFiles(document, "", cssURLs);
+ Timeplot.loaded = true;
+ };
+
+ // Load Timeline if it's not already loaded (after SimileAjax and before Timeplot)
+ var loadTimeline = function() {
+ if (typeof Timeline != "undefined") {
+ loadTimeplot();
+ } else {
+ var timelineURL = "http://api.simile-widgets.org/timeline/2.3.1/timeline-api.js?bundle=true";
+ window.SimileAjax_onLoad = loadTimeplot;
+ SimileAjax.includeJavascriptFile(document, timelineURL);
+ }
+ };
+
+ // Load SimileAjax if it's not already loaded
+ if (typeof SimileAjax == "undefined") {
+ window.SimileAjax_onLoad = loadTimeline;
+
+ var url = "http://api.simile-widgets.org/ajax/2.2.1/simile-ajax-api.js?bundle=true";
+
+ var createScriptElement = function() {
+ var script = document.createElement("script");
+ script.type = "text/javascript";
+ script.language = "JavaScript";
+ script.src = url;
+ document.getElementsByTagName("head")[0].appendChild(script);
+ }
+
+ if (document.body == null) {
+ try {
+ document.write("");
+ } catch (e) {
+ createScriptElement();
+ }
+ } else {
+ createScriptElement();
+ }
+ } else {
+ loadTimeline();
+ }
+})();
+/**
+ * Timeplot
+ *
+ * @fileOverview Timeplot
+ * @name Timeplot
+ */
+
+Timeline.Debug = SimileAjax.Debug; // timeline uses it's own debug system which is not as advanced
+var log = SimileAjax.Debug.log; // shorter name is easier to use
+
+/*
+ * This function is used to implement a raw but effective OOP-like inheritance
+ * in various Timeplot classes.
+ */
+Object.extend = function(destination, source) {
+ for (var property in source) {
+ destination[property] = source[property];
+ }
+ return destination;
+}
+
+// ---------------------------------------------
+
+/**
+ * Create a timeplot attached to the given element and using the configuration from the given array of PlotInfos
+ */
+Timeplot.create = function(elmt, plotInfos) {
+ return new Timeplot._Impl(elmt, plotInfos);
+};
+
+/**
+ * Create a PlotInfo configuration from the given map of params
+ */
+Timeplot.createPlotInfo = function(params) {
+ return {
+ id: ("id" in params) ? params.id : "p" + Math.round(Math.random() * 1000000),
+ dataSource: ("dataSource" in params) ? params.dataSource : null,
+ eventSource: ("eventSource" in params) ? params.eventSource : null,
+ timeGeometry: ("timeGeometry" in params) ? params.timeGeometry : new Timeplot.DefaultTimeGeometry(),
+ valueGeometry: ("valueGeometry" in params) ? params.valueGeometry : new Timeplot.DefaultValueGeometry(),
+ timeZone: ("timeZone" in params) ? params.timeZone : 0,
+ fillColor: ("fillColor" in params) ? ((params.fillColor == "string") ? new Timeplot.Color(params.fillColor) : params.fillColor) : null,
+ fillGradient: ("fillGradient" in params) ? params.fillGradient : true,
+ fillFrom: ("fillFrom" in params) ? params.fillFrom : Number.NEGATIVE_INFINITY,
+ lineColor: ("lineColor" in params) ? ((params.lineColor == "string") ? new Timeplot.Color(params.lineColor) : params.lineColor) : new Timeplot.Color("#606060"),
+ lineWidth: ("lineWidth" in params) ? params.lineWidth : 1.0,
+ dotRadius: ("dotRadius" in params) ? params.dotRadius : 2.0,
+ dotColor: ("dotColor" in params) ? params.dotColor : null,
+ eventLineWidth: ("eventLineWidth" in params) ? params.eventLineWidth : 1.0,
+ showValues: ("showValues" in params) ? params.showValues : false,
+ roundValues: ("roundValues" in params) ? params.roundValues : true,
+ valuesOpacity: ("valuesOpacity" in params) ? params.valuesOpacity : 75,
+ bubbleWidth: ("bubbleWidth" in params) ? params.bubbleWidth : 300,
+ bubbleHeight: ("bubbleHeight" in params) ? params.bubbleHeight : 200
+ };
+};
+
+// -------------------------------------------------------
+
+/**
+ * This is the implementation of the Timeplot object.
+ *
+ * @constructor
+ */
+Timeplot._Impl = function(elmt, plotInfos) {
+ this._id = "t" + Math.round(Math.random() * 1000000);
+ this._containerDiv = elmt;
+ this._plotInfos = plotInfos;
+ this._painters = {
+ background: [],
+ foreground: []
+ };
+ this._painter = null;
+ this._active = false;
+ this._upright = false;
+ this._initialize();
+};
+
+Timeplot._Impl.prototype = {
+
+ dispose: function() {
+ for (var i = 0; i < this._plots.length; i++) {
+ this._plots[i].dispose();
+ }
+ this._plots = null;
+ this._plotsInfos = null;
+ this._containerDiv.innerHTML = "";
+ },
+
+ /**
+ * Returns the main container div this timeplot is operating on.
+ */
+ getElement: function() {
+ return this._containerDiv;
+ },
+
+ /**
+ * Returns document this timeplot belongs to.
+ */
+ getDocument: function() {
+ return this._containerDiv.ownerDocument;
+ },
+
+ /**
+ * Append the given element to the timeplot DOM
+ */
+ add: function(div) {
+ this._containerDiv.appendChild(div);
+ },
+
+ /**
+ * Remove the given element to the timeplot DOM
+ */
+ remove: function(div) {
+ this._containerDiv.removeChild(div);
+ },
+
+ /**
+ * Add a painter to the timeplot
+ */
+ addPainter: function(layerName, painter) {
+ var layer = this._painters[layerName];
+ if (layer) {
+ for (var i = 0; i < layer.length; i++) {
+ if (layer[i].context._id == painter.context._id) {
+ return;
+ }
+ }
+ layer.push(painter);
+ }
+ },
+
+ /**
+ * Remove a painter from the timeplot
+ */
+ removePainter: function(layerName, painter) {
+ var layer = this._painters[layerName];
+ if (layer) {
+ for (var i = 0; i < layer.length; i++) {
+ if (layer[i].context._id == painter.context._id) {
+ layer.splice(i, 1);
+ break;
+ }
+ }
+ }
+ },
+
+ /**
+ * Get the width in pixels of the area occupied by the entire timeplot in the page
+ */
+ getWidth: function() {
+ return this._containerDiv.clientWidth;
+ },
+
+ /**
+ * Get the height in pixels of the area occupied by the entire timeplot in the page
+ */
+ getHeight: function() {
+ return this._containerDiv.clientHeight;
+ },
+
+ /**
+ * Get the drawing canvas associated with this timeplot
+ */
+ getCanvas: function() {
+ return this._canvas;
+ },
+
+ /**
+ *
Load the data from the given url into the given eventSource, using
+ * the given separator to parse the columns and preprocess it before parsing
+ * thru the optional filter function. The filter is useful for when
+ * the data is row-oriented but the format is not compatible with the
+ * one that Timeplot expects.
+ *
+ *
Here is an example of a filter that changes dates in the form 'yyyy/mm/dd'
+ * in the required 'yyyy-mm-dd' format:
+ *
var dataFilter = function(data) {
+ * for (var i = 0; i < data.length; i++) {
+ * var row = data[i];
+ * row[0] = row[0].replace(/\//g,"-");
+ * }
+ * return data;
+ * };
+ */
+ loadText: function(url, separator, eventSource, filter, format) {
+ if (this._active) {
+ var tp = this;
+
+ var fError = function(statusText, status, xmlhttp) {
+ alert("Failed to load data xml from " + url + "\n" + statusText);
+ tp.hideLoadingMessage();
+ };
+
+ var fDone = function(xmlhttp) {
+ try {
+ eventSource.loadText(xmlhttp.responseText, separator, url, filter, format);
+ } catch (e) {
+ SimileAjax.Debug.exception(e);
+ } finally {
+ tp.hideLoadingMessage();
+ }
+ };
+
+ this.showLoadingMessage();
+ window.setTimeout(function() { SimileAjax.XmlHttp.get(url, fError, fDone); }, 0);
+ }
+ },
+
+ /**
+ * Load event data from the given url into the given eventSource, using
+ * the Timeline XML event format.
+ */
+ loadXML: function(url, eventSource) {
+ if (this._active) {
+ var tl = this;
+
+ var fError = function(statusText, status, xmlhttp) {
+ alert("Failed to load data xml from " + url + "\n" + statusText);
+ tl.hideLoadingMessage();
+ };
+
+ var fDone = function(xmlhttp) {
+ try {
+ var xml = xmlhttp.responseXML;
+ if (!xml.documentElement && xmlhttp.responseStream) {
+ xml.load(xmlhttp.responseStream);
+ }
+ eventSource.loadXML(xml, url);
+ } finally {
+ tl.hideLoadingMessage();
+ }
+ };
+
+ this.showLoadingMessage();
+ window.setTimeout(function() { SimileAjax.XmlHttp.get(url, fError, fDone); }, 0);
+ }
+ },
+
+ /**
+ * Overlay a 'div' element filled with the given text and styles to this timeplot
+ * This is used to implement labels since canvas does not support drawing text.
+ */
+ putText: function(id, text, clazz, styles) {
+ var div = this.putDiv(id, "timeplot-div " + clazz, styles);
+ div.innerHTML = text;
+ return div;
+ },
+
+ /**
+ * Overlay a 'div' element, with the given class and the given styles to this timeplot.
+ * This is used for labels and horizontal and vertical grids.
+ */
+ putDiv: function(id, clazz, styles) {
+ var tid = this._id + "-" + id;
+ var div = document.getElementById(tid);
+ if (!div) {
+ var container = this._containerDiv.firstChild; // get the divs container
+ div = document.createElement("div");
+ div.setAttribute("id",tid);
+ container.appendChild(div);
+ }
+ div.setAttribute("class","timeplot-div " + clazz);
+ div.setAttribute("className","timeplot-div " + clazz);
+ this.placeDiv(div,styles);
+ return div;
+ },
+
+ /**
+ * Associate the given map of styles to the given element.
+ * In case such styles indicate position (left,right,top,bottom) correct them
+ * with the padding information so that they align to the 'internal' area
+ * of the timeplot.
+ */
+ placeDiv: function(div, styles) {
+ if (styles) {
+ for (style in styles) {
+ if (style == "left") {
+ styles[style] += this._paddingX;
+ styles[style] += "px";
+ } else if (style == "right") {
+ styles[style] += this._paddingX;
+ styles[style] += "px";
+ } else if (style == "top") {
+ styles[style] += this._paddingY;
+ styles[style] += "px";
+ } else if (style == "bottom") {
+ styles[style] += this._paddingY;
+ styles[style] += "px";
+ } else if (style == "width") {
+ if (styles[style] < 0) styles[style] = 0;
+ styles[style] += "px";
+ } else if (style == "height") {
+ if (styles[style] < 0) styles[style] = 0;
+ styles[style] += "px";
+ }
+ div.style[style] = styles[style];
+ }
+ }
+ },
+
+ /**
+ * return a {x,y} map with the location of the given element relative to the 'internal' area of the timeplot
+ * (that is, without the container padding)
+ */
+ locate: function(div) {
+ return {
+ x: div.offsetLeft - this._paddingX,
+ y: div.offsetTop - this._paddingY
+ }
+ },
+
+ /**
+ * Forces timeplot to re-evaluate the various value and time geometries
+ * associated with its plot layers and repaint accordingly. This should
+ * be invoked after the data in any of the data sources has been
+ * modified.
+ */
+ update: function() {
+ if (this._active) {
+ for (var i = 0; i < this._plots.length; i++) {
+ var plot = this._plots[i];
+ var dataSource = plot.getDataSource();
+ if (dataSource) {
+ var range = dataSource.getRange();
+ if (range) {
+ plot._valueGeometry.setRange(range);
+ plot._timeGeometry.setRange(range);
+ }
+ }
+ plot.hideValues();
+ }
+ this.paint();
+ }
+ },
+
+ /**
+ * Forces timeplot to re-evaluate its own geometry, clear itself and paint.
+ * This should be used instead of paint() when you're not sure if the
+ * geometry of the page has changed or not.
+ */
+ repaint: function() {
+ if (this._active) {
+ this._prepareCanvas();
+ for (var i = 0; i < this._plots.length; i++) {
+ var plot = this._plots[i];
+ if (plot._timeGeometry) plot._timeGeometry.reset();
+ if (plot._valueGeometry) plot._valueGeometry.reset();
+ }
+ this.paint();
+ }
+ },
+
+ /**
+ * Calls all the painters that were registered to this timeplot and makes them
+ * paint the timeplot. This should be used only when you're sure that the geometry
+ * of the page hasn't changed.
+ * NOTE: painting is performed by a different thread and it's safe to call this
+ * function in bursts (as in mousemove or during window resizing
+ */
+ paint: function() {
+ if (this._active && this._painter == null) {
+ var timeplot = this;
+ this._painter = window.setTimeout(function() {
+ timeplot._clearCanvas();
+
+ var run = function(action,context) {
+ try {
+ if (context.setTimeplot) context.setTimeplot(timeplot);
+ action.apply(context,[]);
+ } catch (e) {
+ SimileAjax.Debug.exception(e);
+ }
+ }
+
+ var background = timeplot._painters.background;
+ for (var i = 0; i < background.length; i++) {
+ run(background[i].action, background[i].context);
+ }
+ var foreground = timeplot._painters.foreground;
+ for (var i = 0; i < foreground.length; i++) {
+ run(foreground[i].action, foreground[i].context);
+ }
+
+ timeplot._painter = null;
+ }, 20);
+ }
+ },
+
+ _clearCanvas: function() {
+ var canvas = this.getCanvas();
+ var ctx = canvas.getContext('2d');
+ ctx.clearRect(0,0,canvas.width,canvas.height);
+ },
+
+ _clearLabels: function() {
+ var labels = this._containerDiv.firstChild;
+ if (labels) this._containerDiv.removeChild(labels);
+ labels = document.createElement("div");
+ this._containerDiv.appendChild(labels);
+ },
+
+ _prepareCanvas: function() {
+ var canvas = this.getCanvas();
+
+ // using jQuery. note we calculate the average padding; if your
+ // padding settings are not symmetrical, the labels will be off
+ // since they expect to be centered on the canvas.
+ var con = SimileAjax.jQuery(this._containerDiv);
+ this._paddingX = (parseInt(con.css('paddingLeft')) +
+ parseInt(con.css('paddingRight'))) / 2;
+ this._paddingY = (parseInt(con.css('paddingTop')) +
+ parseInt(con.css('paddingBottom'))) / 2;
+
+ canvas.width = this.getWidth() - (this._paddingX * 2);
+ canvas.height = this.getHeight() - (this._paddingY * 2);
+
+ var ctx = canvas.getContext('2d');
+ this._setUpright(ctx, canvas);
+ ctx.globalCompositeOperation = 'source-over';
+ },
+
+ _setUpright: function(ctx, canvas) {
+ // excanvas+IE requires this to be done only once, ever; actual canvas
+ // implementations reset and require this for each call to re-layout
+ if (!SimileAjax.Platform.browser.isIE) this._upright = false;
+ if (!this._upright) {
+ this._upright = true;
+ ctx.translate(0, canvas.height);
+ ctx.scale(1,-1);
+ }
+ },
+
+ _isBrowserSupported: function(canvas) {
+ var browser = SimileAjax.Platform.browser;
+ if ((canvas.getContext && window.getComputedStyle) ||
+ (browser.isIE && browser.majorVersion >= 6)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ _initialize: function() {
+
+ // initialize the window manager (used to handle the popups)
+ // NOTE: this is a singleton and it's safe to call multiple times
+ SimileAjax.WindowManager.initialize();
+
+ var containerDiv = this._containerDiv;
+ var doc = containerDiv.ownerDocument;
+
+ // make sure the timeplot div has the right class
+ containerDiv.className = "timeplot-container " + containerDiv.className;
+
+ // clean it up if it contains some content
+ while (containerDiv.firstChild) {
+ containerDiv.removeChild(containerDiv.firstChild);
+ }
+
+ var canvas = doc.createElement("canvas");
+
+ if (this._isBrowserSupported(canvas)) {
+ this._clearLabels();
+
+ this._canvas = canvas;
+ canvas.className = "timeplot-canvas";
+ containerDiv.appendChild(canvas);
+ if(!canvas.getContext && G_vmlCanvasManager) {
+ canvas = G_vmlCanvasManager.initElement(this._canvas);
+ this._canvas = canvas;
+ }
+ this._prepareCanvas();
+
+ // inserting copyright and link to simile
+ var elmtCopyright = SimileAjax.Graphics.createTranslucentImage(Timeplot.urlPrefix + "images/copyright.png");
+ elmtCopyright.className = "timeplot-copyright";
+ elmtCopyright.title = "Timeplot (c) SIMILE - http://simile.mit.edu/timeplot/";
+ SimileAjax.DOM.registerEvent(elmtCopyright, "click", function() { window.location = "http://simile.mit.edu/timeplot/"; });
+ containerDiv.appendChild(elmtCopyright);
+
+ var timeplot = this;
+ var painter = {
+ onAddMany: function() { timeplot.update(); },
+ onClear: function() { timeplot.update(); }
+ }
+
+ // creating painters
+ this._plots = [];
+ if (this._plotInfos) {
+ for (var i = 0; i < this._plotInfos.length; i++) {
+ var plot = new Timeplot.Plot(this, this._plotInfos[i]);
+ var dataSource = plot.getDataSource();
+ if (dataSource) {
+ dataSource.addListener(painter);
+ }
+ this.addPainter("background", {
+ context: plot.getTimeGeometry(),
+ action: plot.getTimeGeometry().paint
+ });
+ this.addPainter("background", {
+ context: plot.getValueGeometry(),
+ action: plot.getValueGeometry().paint
+ });
+ this.addPainter("foreground", {
+ context: plot,
+ action: plot.paint
+ });
+ this._plots.push(plot);
+ plot.initialize();
+ }
+ }
+
+ // creating loading UI
+ var message = SimileAjax.Graphics.createMessageBubble(doc);
+ message.containerDiv.className = "timeplot-message-container";
+ containerDiv.appendChild(message.containerDiv);
+
+ message.contentDiv.className = "timeplot-message";
+ message.contentDiv.innerHTML = " Loading...";
+
+ this.showLoadingMessage = function() { message.containerDiv.style.display = "block"; };
+ this.hideLoadingMessage = function() { message.containerDiv.style.display = "none"; };
+
+ this._active = true;
+
+ } else {
+
+ this._message = SimileAjax.Graphics.createMessageBubble(doc);
+ this._message.containerDiv.className = "timeplot-message-container";
+ this._message.containerDiv.style.top = "15%";
+ this._message.containerDiv.style.left = "20%";
+ this._message.containerDiv.style.right = "20%";
+ this._message.containerDiv.style.minWidth = "20em";
+ this._message.contentDiv.className = "timeplot-message";
+ this._message.contentDiv.innerHTML = "We're terribly sorry, but your browser is not currently supported by Timeplot.
We are working on supporting it in the near future but, for now, see the list of currently supported browsers.";
+ this._message.containerDiv.style.display = "block";
+
+ containerDiv.appendChild(this._message.containerDiv);
+
+ }
+ }
+};
+/**
+ * Plot Layer
+ *
+ * @fileOverview Plot Layer
+ * @name Plot
+ */
+
+/**
+ * A plot layer is the main building block for timeplots and it's the object
+ * that is responsible for painting the plot itself. Each plot needs to have
+ * a time geometry, either a DataSource (for time series
+ * plots) or an EventSource (for event plots) and a value geometry in case
+ * of time series plots. Such parameters are passed along
+ * in the 'plotInfo' map.
+ *
+ * @constructor
+ */
+Timeplot.Plot = function(timeplot, plotInfo) {
+ this._timeplot = timeplot;
+ this._canvas = timeplot.getCanvas();
+ this._plotInfo = plotInfo;
+ this._id = plotInfo.id;
+ this._timeGeometry = plotInfo.timeGeometry;
+ this._valueGeometry = plotInfo.valueGeometry;
+ this._theme = new Timeline.getDefaultTheme();
+ this._dataSource = plotInfo.dataSource;
+ this._eventSource = plotInfo.eventSource;
+ this._bubble = null;
+};
+
+Timeplot.Plot.prototype = {
+
+ /**
+ * Initialize the plot layer
+ */
+ initialize: function() {
+ if (this._dataSource && this._dataSource.getValue) {
+ this._timeFlag = this._timeplot.putDiv("timeflag","timeplot-timeflag");
+ this._valueFlag = this._timeplot.putDiv(this._id + "valueflag","timeplot-valueflag");
+ this._valueFlagLineLeft = this._timeplot.putDiv(this._id + "valueflagLineLeft","timeplot-valueflag-line");
+ this._valueFlagLineRight = this._timeplot.putDiv(this._id + "valueflagLineRight","timeplot-valueflag-line");
+ if (!this._valueFlagLineLeft.firstChild) {
+ this._valueFlagLineLeft.appendChild(SimileAjax.Graphics.createTranslucentImage(Timeplot.urlPrefix + "images/line_left.png"));
+ this._valueFlagLineRight.appendChild(SimileAjax.Graphics.createTranslucentImage(Timeplot.urlPrefix + "images/line_right.png"));
+ }
+ this._valueFlagPole = this._timeplot.putDiv(this._id + "valuepole","timeplot-valueflag-pole");
+
+ var opacity = this._plotInfo.valuesOpacity;
+
+ SimileAjax.Graphics.setOpacity(this._timeFlag, opacity);
+ SimileAjax.Graphics.setOpacity(this._valueFlag, opacity);
+ SimileAjax.Graphics.setOpacity(this._valueFlagLineLeft, opacity);
+ SimileAjax.Graphics.setOpacity(this._valueFlagLineRight, opacity);
+ SimileAjax.Graphics.setOpacity(this._valueFlagPole, opacity);
+
+ var plot = this;
+
+ var mouseOverHandler = function(elmt, evt, target) {
+ if (plot._plotInfo.showValues) {
+ plot._valueFlag.style.display = "block";
+ mouseMoveHandler(elmt, evt, target);
+ }
+ }
+
+ var day = 24 * 60 * 60 * 1000;
+ var month = 30 * day;
+
+ var mouseMoveHandler = function(elmt, evt, target) {
+ if (typeof SimileAjax != "undefined" && plot._plotInfo.showValues) {
+ var c = plot._canvas;
+ var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt,plot._canvas).x);
+ if (x > c.width) x = c.width;
+ if (isNaN(x) || x < 0) x = 0;
+ var t = plot._timeGeometry.fromScreen(x);
+ if (t == 0) { // something is wrong
+ plot._valueFlag.style.display = "none";
+ return;
+ }
+
+ var validTime = plot._dataSource.getClosestValidTime(t);
+ x = plot._timeGeometry.toScreen(validTime);
+ var v = plot._dataSource.getValue(validTime);
+ if (plot._plotInfo.roundValues) v = Math.round(v);
+ plot._valueFlag.innerHTML = new String(v);
+ var d = new Date(validTime);
+ var p = plot._timeGeometry.getPeriod();
+ if (p < day) {
+ plot._timeFlag.innerHTML = d.toLocaleTimeString();
+ } else if (p > month) {
+ plot._timeFlag.innerHTML = d.toLocaleDateString();
+ } else {
+ plot._timeFlag.innerHTML = d.toLocaleString();
+ }
+
+ var tw = plot._timeFlag.clientWidth;
+ var th = plot._timeFlag.clientHeight;
+ var tdw = Math.round(tw / 2);
+ var vw = plot._valueFlag.clientWidth;
+ var vh = plot._valueFlag.clientHeight;
+ var y = plot._valueGeometry.toScreen(v);
+
+ if (x + tdw > c.width) {
+ var tx = c.width - tdw;
+ } else if (x - tdw < 0) {
+ var tx = tdw;
+ } else {
+ var tx = x;
+ }
+
+ if (plot._timeGeometry._timeValuePosition == "top") {
+ plot._timeplot.placeDiv(plot._valueFlagPole, {
+ left: x,
+ top: th - 5,
+ height: c.height - y - th + 6,
+ display: "block"
+ });
+ plot._timeplot.placeDiv(plot._timeFlag,{
+ left: tx - tdw,
+ top: -6,
+ display: "block"
+ });
+ } else {
+ plot._timeplot.placeDiv(plot._valueFlagPole, {
+ left: x,
+ bottom: th - 5,
+ height: y - th + 6,
+ display: "block"
+ });
+ plot._timeplot.placeDiv(plot._timeFlag,{
+ left: tx - tdw,
+ bottom: -6,
+ display: "block"
+ });
+ }
+
+ if (x + vw + 14 > c.width && y + vh + 4 > c.height) {
+ plot._valueFlagLineLeft.style.display = "none";
+ plot._timeplot.placeDiv(plot._valueFlagLineRight,{
+ left: x - 14,
+ bottom: y - 14,
+ display: "block"
+ });
+ plot._timeplot.placeDiv(plot._valueFlag,{
+ left: x - vw - 13,
+ bottom: y - vh - 13,
+ display: "block"
+ });
+ } else if (x + vw + 14 > c.width && y + vh + 4 < c.height) {
+ plot._valueFlagLineRight.style.display = "none";
+ plot._timeplot.placeDiv(plot._valueFlagLineLeft,{
+ left: x - 14,
+ bottom: y,
+ display: "block"
+ });
+ plot._timeplot.placeDiv(plot._valueFlag,{
+ left: x - vw - 13,
+ bottom: y + 13,
+ display: "block"
+ });
+ } else if (x + vw + 14 < c.width && y + vh + 4 > c.height) {
+ plot._valueFlagLineRight.style.display = "none";
+ plot._timeplot.placeDiv(plot._valueFlagLineLeft,{
+ left: x,
+ bottom: y - 13,
+ display: "block"
+ });
+ plot._timeplot.placeDiv(plot._valueFlag,{
+ left: x + 13,
+ bottom: y - 13,
+ display: "block"
+ });
+ } else {
+ plot._valueFlagLineLeft.style.display = "none";
+ plot._timeplot.placeDiv(plot._valueFlagLineRight,{
+ left: x,
+ bottom: y,
+ display: "block"
+ });
+ plot._timeplot.placeDiv(plot._valueFlag,{
+ left: x + 13,
+ bottom: y + 13,
+ display: "block"
+ });
+ }
+ }
+ }
+
+ var timeplotElement = this._timeplot.getElement();
+ SimileAjax.DOM.registerEvent(timeplotElement, "mouseover", mouseOverHandler);
+ SimileAjax.DOM.registerEvent(timeplotElement, "mousemove", mouseMoveHandler);
+ }
+ },
+
+ /**
+ * Dispose the plot layer and all the data sources and listeners associated to it
+ */
+ dispose: function() {
+ if (this._dataSource) {
+ this._dataSource.removeListener(this._paintingListener);
+ this._paintingListener = null;
+ this._dataSource.dispose();
+ this._dataSource = null;
+ }
+ },
+
+ /**
+ * Hide the values
+ */
+ hideValues: function() {
+ if (this._valueFlag) this._valueFlag.style.display = "none";
+ if (this._timeFlag) this._timeFlag.style.display = "none";
+ if (this._valueFlagLineLeft) this._valueFlagLineLeft.style.display = "none";
+ if (this._valueFlagLineRight) this._valueFlagLineRight.style.display = "none";
+ if (this._valueFlagPole) this._valueFlagPole.style.display = "none";
+ },
+
+ /**
+ * Return the data source of this plot layer (it could be either a DataSource or an EventSource)
+ */
+ getDataSource: function() {
+ return (this._dataSource) ? this._dataSource : this._eventSource;
+ },
+
+ /**
+ * Return the time geometry associated with this plot layer
+ */
+ getTimeGeometry: function() {
+ return this._timeGeometry;
+ },
+
+ /**
+ * Return the value geometry associated with this plot layer
+ */
+ getValueGeometry: function() {
+ return this._valueGeometry;
+ },
+
+ /**
+ * Paint this plot layer
+ */
+ paint: function() {
+ var ctx = this._canvas.getContext('2d');
+
+ ctx.lineWidth = this._plotInfo.lineWidth;
+ ctx.lineJoin = 'miter';
+
+ if (this._dataSource) {
+ if (this._plotInfo.fillColor) {
+ if (this._plotInfo.fillGradient) {
+ var gradient = ctx.createLinearGradient(0,this._canvas.height,0,0);
+ gradient.addColorStop(0,this._plotInfo.fillColor.toString());
+ gradient.addColorStop(0.5,this._plotInfo.fillColor.toString());
+ gradient.addColorStop(1, 'rgba(255,255,255,0)');
+
+ ctx.fillStyle = gradient;
+ } else {
+ ctx.fillStyle = this._plotInfo.fillColor.toString();
+ }
+
+ ctx.beginPath();
+ ctx.moveTo(0,0);
+ this._plot(function(x,y) {
+ ctx.lineTo(x,y);
+ });
+ if (this._plotInfo.fillFrom == Number.NEGATIVE_INFINITY) {
+ ctx.lineTo(this._canvas.width, 0);
+ } else if (this._plotInfo.fillFrom == Number.POSITIVE_INFINITY) {
+ ctx.lineTo(this._canvas.width, this._canvas.height);
+ ctx.lineTo(0, this._canvas.height);
+ } else {
+ ctx.lineTo(this._canvas.width, this._valueGeometry.toScreen(this._plotInfo.fillFrom));
+ ctx.lineTo(0, this._valueGeometry.toScreen(this._plotInfo.fillFrom));
+ }
+ ctx.fill();
+ }
+
+ if (this._plotInfo.lineColor) {
+ ctx.strokeStyle = this._plotInfo.lineColor.toString();
+ ctx.beginPath();
+ var first = true;
+ this._plot(function(x,y) {
+ if (first) {
+ first = false;
+ ctx.moveTo(x,y);
+ }
+ ctx.lineTo(x,y);
+ });
+ ctx.stroke();
+ }
+
+ if (this._plotInfo.dotColor) {
+ ctx.fillStyle = this._plotInfo.dotColor.toString();
+ var r = this._plotInfo.dotRadius;
+ this._plot(function(x,y) {
+ ctx.beginPath();
+ ctx.arc(x,y,r,0,2*Math.PI,true);
+ ctx.fill();
+ });
+ }
+ }
+
+ if (this._eventSource) {
+ var gradient = ctx.createLinearGradient(0,0,0,this._canvas.height);
+ gradient.addColorStop(1, 'rgba(255,255,255,0)');
+
+ ctx.strokeStyle = gradient;
+ ctx.fillStyle = gradient;
+ ctx.lineWidth = this._plotInfo.eventLineWidth;
+ ctx.lineJoin = 'miter';
+
+ var i = this._eventSource.getAllEventIterator();
+ while (i.hasNext()) {
+ var event = i.next();
+ var color = event.getColor();
+ color = (color) ? new Timeplot.Color(color) : this._plotInfo.lineColor;
+ var eventStart = event.getStart().getTime();
+ var eventEnd = event.getEnd().getTime();
+ if (eventStart == eventEnd) {
+ var c = color.toString();
+ gradient.addColorStop(0, c);
+ var start = this._timeGeometry.toScreen(eventStart);
+ start = Math.floor(start) + 0.5; // center it between two pixels (makes the rendering nicer)
+ var end = start;
+ ctx.beginPath();
+ ctx.moveTo(start,0);
+ ctx.lineTo(start,this._canvas.height);
+ ctx.stroke();
+ var x = start - 4;
+ var w = 7;
+ } else {
+ var c = color.toString(0.5);
+ gradient.addColorStop(0, c);
+ var start = this._timeGeometry.toScreen(eventStart);
+ start = Math.floor(start) + 0.5; // center it between two pixels (makes the rendering nicer)
+ var end = this._timeGeometry.toScreen(eventEnd);
+ end = Math.floor(end) + 0.5; // center it between two pixels (makes the rendering nicer)
+ ctx.fillRect(start,0,end - start, this._canvas.height);
+ var x = start;
+ var w = end - start - 1;
+ }
+
+ var div = this._timeplot.putDiv(event.getID(),"timeplot-event-box",{
+ left: Math.round(x),
+ width: Math.round(w),
+ top: 0,
+ height: this._canvas.height - 1
+ });
+
+ var plot = this;
+ var clickHandler = function(event) {
+ return function(elmt, evt, target) {
+ var doc = plot._timeplot.getDocument();
+ plot._closeBubble();
+ var coords = SimileAjax.DOM.getEventPageCoordinates(evt);
+ var elmtCoords = SimileAjax.DOM.getPageCoordinates(elmt);
+ plot._bubble = SimileAjax.Graphics.createBubbleForPoint(coords.x, elmtCoords.top + plot._canvas.height, plot._plotInfo.bubbleWidth, plot._plotInfo.bubbleHeight, "bottom");
+ event.fillInfoBubble(plot._bubble.content, plot._theme, plot._timeGeometry.getLabeler());
+ }
+ };
+ var mouseOverHandler = function(elmt, evt, target) {
+ elmt.oldClass = elmt.className;
+ elmt.className = elmt.className + " timeplot-event-box-highlight";
+ };
+ var mouseOutHandler = function(elmt, evt, target) {
+ elmt.className = elmt.oldClass;
+ elmt.oldClass = null;
+ }
+
+ if (!div.instrumented) {
+ SimileAjax.DOM.registerEvent(div, "click" , clickHandler(event));
+ SimileAjax.DOM.registerEvent(div, "mouseover", mouseOverHandler);
+ SimileAjax.DOM.registerEvent(div, "mouseout" , mouseOutHandler);
+ div.instrumented = true;
+ }
+ }
+ }
+ },
+
+ _plot: function(f) {
+ var data = this._dataSource.getData();
+ if (data) {
+ var times = data.times;
+ var values = data.values;
+ var T = times.length;
+ for (var t = 0; t < T; t++) {
+ var x = this._timeGeometry.toScreen(times[t]);
+ var y = this._valueGeometry.toScreen(values[t]);
+ f(x, y);
+ }
+ }
+ },
+
+ _closeBubble: function() {
+ if (this._bubble != null) {
+ this._bubble.close();
+ this._bubble = null;
+ }
+ }
+
+}
+/**
+ * Sources
+ *
+ * @fileOverview Sources
+ * @name Sources
+ */
+
+/**
+ * Timeplot.DefaultEventSource is an extension of Timeline.DefaultEventSource
+ * and therefore reuses the exact same event loading subsystem that
+ * Timeline uses.
+ *
+ * @constructor
+ */
+Timeplot.DefaultEventSource = function(eventIndex) {
+ Timeline.DefaultEventSource.apply(this, arguments);
+};
+
+Object.extend(Timeplot.DefaultEventSource.prototype, Timeline.DefaultEventSource.prototype);
+
+/**
+ * Function used by Timeplot to load time series data from a text file.
+ */
+Timeplot.DefaultEventSource.prototype.loadText = function(text, separator, url, filter, format) {
+
+ if (text == null) {
+ return;
+ }
+
+ this._events.maxValues = new Array();
+ var base = this._getBaseURL(url);
+
+ if (!format) format = 'iso8601';
+ var parseDateTimeFunction = this._events.getUnit().getParser(format);
+
+ var data = this._parseText(text, separator);
+
+ var added = false;
+
+ if (filter) {
+ data = filter(data);
+ }
+
+ if (data) {
+ for (var i = 0; i < data.length; i++){
+ var row = data[i];
+ if (row.length > 1) {
+ var dateStr = SimileAjax.jQuery.trim(row[0]);
+ var date = parseDateTimeFunction(dateStr);
+ if (date) {
+ var evt = new Timeplot.DefaultEventSource.NumericEvent(date,row.slice(1));
+ this._events.add(evt);
+ added = true;
+ }
+ }
+ }
+ }
+
+ if (added) {
+ this._fire("onAddMany", []);
+ }
+}
+
+/*
+ * Parse the data file.
+ *
+ * Adapted from http://www.kawa.net/works/js/jkl/js/jkl-parsexml.js by Yusuke Kawasaki
+ */
+Timeplot.DefaultEventSource.prototype._parseText = function (text, separator) {
+ text = text.replace( /\r\n?/g, "\n" ); // normalize newlines
+ var pos = 0;
+ var len = text.length;
+ var table = [];
+ while (pos < len) {
+ var line = [];
+ if (text.charAt(pos) != '#') { // if it's not a comment, process
+ while (pos < len) {
+ if (text.charAt(pos) == '"') { // "..." quoted column
+ var nextquote = text.indexOf('"', pos+1 );
+ while (nextquote -1) {
+ if (text.charAt(nextquote+1) != '"') {
+ break; // end of column
+ }
+ nextquote = text.indexOf('"', nextquote + 2);
+ }
+ if ( nextquote < 0 ) {
+ // unclosed quote
+ } else if (text.charAt(nextquote + 1) == separator) { // end of column
+ var quoted = text.substr(pos + 1, nextquote-pos - 1);
+ quoted = quoted.replace(/""/g,'"');
+ line[line.length] = quoted;
+ pos = nextquote + 2;
+ continue;
+ } else if (text.charAt(nextquote + 1) == "\n" || // end of line
+ len == nextquote + 1 ) { // end of file
+ var quoted = text.substr(pos + 1, nextquote-pos - 1);
+ quoted = quoted.replace(/""/g,'"');
+ line[line.length] = quoted;
+ pos = nextquote + 2;
+ break;
+ } else {
+ // invalid column
+ }
+ }
+ var nextseparator = text.indexOf(separator, pos);
+ var nextnline = text.indexOf("\n", pos);
+ if (nextnline < 0) nextnline = len;
+ if (nextseparator > -1 && nextseparator < nextnline) {
+ line[line.length] = text.substr(pos, nextseparator-pos);
+ pos = nextseparator + 1;
+ } else { // end of line
+ line[line.length] = text.substr(pos, nextnline-pos);
+ pos = nextnline + 1;
+ break;
+ }
+ }
+ } else { // if it's a comment, ignore
+ var nextnline = text.indexOf("\n", pos);
+ pos = (nextnline > -1) ? nextnline + 1 : cur;
+ }
+ if (line.length > 0) {
+ table[table.length] = line; // push line
+ }
+ }
+ if (table.length < 0) return; // null data
+ return table;
+}
+
+/**
+ * Return the range of the loaded data
+ */
+Timeplot.DefaultEventSource.prototype.getRange = function() {
+ var earliestDate = this.getEarliestDate();
+ var latestDate = this.getLatestDate();
+ return {
+ earliestDate: (earliestDate) ? earliestDate : null,
+ latestDate: (latestDate) ? latestDate : null,
+ min: 0,
+ max: 0
+ };
+}
+
+// -----------------------------------------------------------------------
+
+/**
+ * A NumericEvent is an Event that also contains an array of values,
+ * one for each columns in the loaded data file.
+ *
+ * @constructor
+ */
+Timeplot.DefaultEventSource.NumericEvent = function(time, values) {
+ this._id = "e" + Math.round(Math.random() * 1000000);
+ this._time = time;
+ this._values = values;
+};
+
+Timeplot.DefaultEventSource.NumericEvent.prototype = {
+ getID: function() { return this._id; },
+ getTime: function() { return this._time; },
+ getValues: function() { return this._values; },
+
+ // these are required by the EventSource
+ getStart: function() { return this._time; },
+ getEnd: function() { return this._time; }
+};
+
+// -----------------------------------------------------------------------
+
+/**
+ * A DataSource represent an abstract class that represents a monodimensional time series.
+ *
+ * @constructor
+ */
+Timeplot.DataSource = function(eventSource) {
+ this._eventSource = eventSource;
+ var source = this;
+ this._processingListener = {
+ onAddMany: function() { source._process(); },
+ onClear: function() { source._clear(); }
+ }
+ this.addListener(this._processingListener);
+ this._listeners = [];
+ this._data = null;
+ this._range = null;
+};
+
+Timeplot.DataSource.prototype = {
+
+ _clear: function() {
+ this._data = null;
+ this._range = null;
+ },
+
+ _process: function() {
+ this._data = {
+ times: new Array(),
+ values: new Array()
+ };
+ this._range = {
+ earliestDate: null,
+ latestDate: null,
+ min: 0,
+ max: 0
+ };
+ },
+
+ /**
+ * Return the range of this data source
+ */
+ getRange: function() {
+ return this._range;
+ },
+
+ /**
+ * Return the actual data that this data source represents.
+ * NOTE: _data = { times: [], values: [] }
+ */
+ getData: function() {
+ return this._data;
+ },
+
+ /**
+ * Return the value associated with the given time in this time series
+ */
+ getValue: function(t) {
+ if (this._data) {
+ for (var i = 0; i < this._data.times.length; i++) {
+ var l = this._data.times[i];
+ if (l >= t) {
+ return this._data.values[i];
+ }
+ }
+ }
+ return 0;
+ },
+
+ /**
+ * Return the time of the data point closest to the given time.
+ */
+ getClosestValidTime: function(t) {
+ if (this._data) {
+ for (var i = 0; i < this._data.times.length; i++) {
+ var currentTime = this._data.times[i];
+ if (currentTime >= t) {
+ if (i <= 0) {
+ return currentTime;
+ } else {
+ var lastTime = this._data.times[i - 1];
+ // t must be between currentTime and lastTime.
+ // Find the closest one.
+ if (t - lastTime < currentTime - t) {
+ return lastTime;
+ } else {
+ return currentTime;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+ },
+
+ /**
+ * Add a listener to the underlying event source
+ */
+ addListener: function(listener) {
+ this._eventSource.addListener(listener);
+ },
+
+ /**
+ * Remove a listener from the underlying event source
+ */
+ removeListener: function(listener) {
+ this._eventSource.removeListener(listener);
+ },
+
+ /**
+ * Replace a listener from the underlying event source
+ */
+ replaceListener: function(oldListener, newListener) {
+ this.removeListener(oldListener);
+ this.addListener(newListener);
+ }
+
+}
+
+// -----------------------------------------------------------------------
+
+/**
+ * Implementation of a DataSource that extracts the time series out of a
+ * single column from the events
+ *
+ * @constructor
+ */
+Timeplot.ColumnSource = function(eventSource, column) {
+ Timeplot.DataSource.apply(this, arguments);
+ this._column = column - 1;
+};
+
+Object.extend(Timeplot.ColumnSource.prototype,Timeplot.DataSource.prototype);
+
+Timeplot.ColumnSource.prototype.dispose = function() {
+ this.removeListener(this._processingListener);
+ this._clear();
+}
+
+Timeplot.ColumnSource.prototype._process = function() {
+ var count = this._eventSource.getCount();
+ var times = new Array(count);
+ var values = new Array(count);
+ var min = Number.MAX_VALUE;
+ var max = Number.MIN_VALUE;
+ var i = 0;
+
+ var iterator = this._eventSource.getAllEventIterator();
+ while (iterator.hasNext()) {
+ var event = iterator.next();
+ var time = event.getTime();
+ times[i] = time;
+ var value = this._getValue(event);
+ if (!isNaN(value)) {
+ if (value < min) {
+ min = value;
+ }
+ if (value > max) {
+ max = value;
+ }
+ values[i] = value;
+ }
+ i++;
+ }
+
+ this._data = {
+ times: times,
+ values: values
+ };
+
+ if (max == Number.MIN_VALUE) max = 1;
+
+ this._range = {
+ earliestDate: this._eventSource.getEarliestDate(),
+ latestDate: this._eventSource.getLatestDate(),
+ min: min,
+ max: max
+ };
+}
+
+Timeplot.ColumnSource.prototype._getValue = function(event) {
+ return parseFloat(event.getValues()[this._column]);
+}
+
+// ---------------------------------------------------------------
+
+/**
+ * Data Source that generates the time series out of the difference
+ * between the first and the second column
+ *
+ * @constructor
+ */
+Timeplot.ColumnDiffSource = function(eventSource, column1, column2) {
+ Timeplot.ColumnSource.apply(this, arguments);
+ this._column2 = column2 - 1;
+};
+
+Object.extend(Timeplot.ColumnDiffSource.prototype,Timeplot.ColumnSource.prototype);
+
+Timeplot.ColumnDiffSource.prototype._getValue = function(event) {
+ var a = parseFloat(event.getValues()[this._column]);
+ var b = parseFloat(event.getValues()[this._column2]);
+ return a - b;
+}
+/**
+ * Geometries
+ *
+ * @fileOverview Geometries
+ * @name Geometries
+ */
+
+/**
+ * This is the constructor for the default value geometry.
+ * A value geometry is what regulates mapping of the plot values to the screen y coordinate.
+ * If two plots share the same value geometry, they will be drawn using the same scale.
+ * If "min" and "max" parameters are not set, the geometry will stretch itself automatically
+ * so that the entire plot will be drawn without overflowing. The stretching happens also
+ * when a geometry is shared between multiple plots, the one with the biggest range will
+ * win over the others.
+ *
+ * @constructor
+ */
+Timeplot.DefaultValueGeometry = function(params) {
+ if (!params) params = {};
+ this._id = ("id" in params) ? params.id : "g" + Math.round(Math.random() * 1000000);
+ this._axisColor = ("axisColor" in params) ? ((typeof params.axisColor == "string") ? new Timeplot.Color(params.axisColor) : params.axisColor) : new Timeplot.Color("#606060"),
+ this._gridColor = ("gridColor" in params) ? ((typeof params.gridColor == "string") ? new Timeplot.Color(params.gridColor) : params.gridColor) : null,
+ this._gridLineWidth = ("gridLineWidth" in params) ? params.gridLineWidth : 0.5;
+ this._axisLabelsPlacement = ("axisLabelsPlacement" in params) ? params.axisLabelsPlacement : "right";
+ this._gridSpacing = ("gridSpacing" in params) ? params.gridStep : 50;
+ this._gridType = ("gridType" in params) ? params.gridType : "short";
+ this._gridShortSize = ("gridShortSize" in params) ? params.gridShortSize : 10;
+ this._minValue = ("min" in params) ? params.min : null;
+ this._maxValue = ("max" in params) ? params.max : null;
+ this._linMap = {
+ direct: function(v) {
+ return v;
+ },
+ inverse: function(y) {
+ return y;
+ }
+ }
+ this._map = this._linMap;
+ this._labels = [];
+ this._grid = [];
+}
+
+Timeplot.DefaultValueGeometry.prototype = {
+
+ /**
+ * Since geometries can be reused across timeplots, we need to call this function
+ * before we can paint using this geometry.
+ */
+ setTimeplot: function(timeplot) {
+ this._timeplot = timeplot;
+ this._canvas = timeplot.getCanvas();
+ this.reset();
+ },
+
+ /**
+ * Called by all the plot layers this geometry is associated with
+ * to update the value range. Unless min/max values are specified
+ * in the parameters, the biggest value range will be used.
+ */
+ setRange: function(range) {
+ if ((this._minValue == null) || ((this._minValue != null) && (range.min < this._minValue))) {
+ this._minValue = range.min;
+ }
+ if ((this._maxValue == null) || ((this._maxValue != null) && (range.max * 1.05 > this._maxValue))) {
+ this._maxValue = range.max * 1.05; // get a little more head room to avoid hitting the ceiling
+ }
+
+ this._updateMappedValues();
+
+ if (!(this._minValue == 0 && this._maxValue == 0)) {
+ this._grid = this._calculateGrid();
+ }
+ },
+
+ /**
+ * Called after changing ranges or canvas size to reset the grid values
+ */
+ reset: function() {
+ this._clearLabels();
+ this._updateMappedValues();
+ this._grid = this._calculateGrid();
+ },
+
+ /**
+ * Map the given value to a y screen coordinate.
+ */
+ toScreen: function(value) {
+ if (this._canvas && this._maxValue) {
+ var v = value - this._minValue;
+ return this._canvas.height * (this._map.direct(v)) / this._mappedRange;
+ } else {
+ return -50;
+ }
+ },
+
+ /**
+ * Map the given y screen coordinate to a value
+ */
+ fromScreen: function(y) {
+ if (this._canvas) {
+ return this._map.inverse(this._mappedRange * y / this._canvas.height) + this._minValue;
+ } else {
+ return 0;
+ }
+ },
+
+ /**
+ * Each geometry is also a painter and paints the value grid and grid labels.
+ */
+ paint: function() {
+ if (this._timeplot) {
+ var ctx = this._canvas.getContext('2d');
+
+ ctx.lineJoin = 'miter';
+
+ // paint grid
+ if (this._gridColor) {
+ var gridGradient = ctx.createLinearGradient(0,0,0,this._canvas.height);
+ gridGradient.addColorStop(0, this._gridColor.toHexString());
+ gridGradient.addColorStop(0.3, this._gridColor.toHexString());
+ gridGradient.addColorStop(1, "rgba(255,255,255,0.5)");
+
+ ctx.lineWidth = this._gridLineWidth;
+ ctx.strokeStyle = gridGradient;
+
+ for (var i = 0; i < this._grid.length; i++) {
+ var tick = this._grid[i];
+ var y = Math.floor(tick.y) + 0.5;
+ if (typeof tick.label != "undefined") {
+ if (this._axisLabelsPlacement == "left") {
+ var div = this._timeplot.putText(this._id + "-" + i, tick.label,"timeplot-grid-label",{
+ left: 4,
+ bottom: y + 2,
+ color: this._gridColor.toHexString(),
+ visibility: "hidden"
+ });
+ this._labels.push(div);
+ } else if (this._axisLabelsPlacement == "right") {
+ var div = this._timeplot.putText(this._id + "-" + i, tick.label, "timeplot-grid-label",{
+ right: 4,
+ bottom: y + 2,
+ color: this._gridColor.toHexString(),
+ visibility: "hidden"
+ });
+ this._labels.push(div);
+ }
+ if (y + div.clientHeight < this._canvas.height + 10) {
+ div.style.visibility = "visible"; // avoid the labels that would overflow
+ }
+ }
+
+ // draw grid
+ ctx.beginPath();
+ if (this._gridType == "long" || tick.label == 0) {
+ ctx.moveTo(0, y);
+ ctx.lineTo(this._canvas.width, y);
+ } else if (this._gridType == "short") {
+ if (this._axisLabelsPlacement == "left") {
+ ctx.moveTo(0, y);
+ ctx.lineTo(this._gridShortSize, y);
+ } else if (this._axisLabelsPlacement == "right") {
+ ctx.moveTo(this._canvas.width, y);
+ ctx.lineTo(this._canvas.width - this._gridShortSize, y);
+ }
+ }
+ ctx.stroke();
+ }
+ }
+
+ // paint axis
+ var axisGradient = ctx.createLinearGradient(0,0,0,this._canvas.height);
+ axisGradient.addColorStop(0, this._axisColor.toString());
+ axisGradient.addColorStop(0.5, this._axisColor.toString());
+ axisGradient.addColorStop(1, "rgba(255,255,255,0.5)");
+
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = axisGradient;
+
+ // left axis
+ ctx.beginPath();
+ ctx.moveTo(0,this._canvas.height);
+ ctx.lineTo(0,0);
+ ctx.stroke();
+
+ // right axis
+ ctx.beginPath();
+ ctx.moveTo(this._canvas.width,0);
+ ctx.lineTo(this._canvas.width,this._canvas.height);
+ ctx.stroke();
+ }
+ },
+
+ /**
+ * Removes all the labels that were added by this geometry
+ */
+ _clearLabels: function() {
+ for (var i = 0; i < this._labels.length; i++) {
+ var l = this._labels[i];
+ var parent = l.parentNode;
+ if (parent) parent.removeChild(l);
+ }
+ },
+
+ /*
+ * This function calculates the grid spacing that it will be used
+ * by this geometry to draw the grid in order to reduce clutter.
+ */
+ _calculateGrid: function() {
+ var grid = [];
+
+ if (!this._canvas || this._valueRange == 0) return grid;
+
+ var power = 0;
+ if (this._valueRange > 1) {
+ while (Math.pow(10,power) < this._valueRange) {
+ power++;
+ }
+ power--;
+ } else {
+ while (Math.pow(10,power) > this._valueRange) {
+ power--;
+ }
+ }
+
+ var unit = Math.pow(10,power);
+ var inc = unit;
+ while (true) {
+ var dy = this.toScreen(this._minValue + inc);
+
+ while (dy < this._gridSpacing) {
+ inc += unit;
+ dy = this.toScreen(this._minValue + inc);
+ }
+
+ if (dy > 2 * this._gridSpacing) { // grids are too spaced out
+ unit /= 10;
+ inc = unit;
+ } else {
+ break;
+ }
+ }
+
+ var v = 0;
+ var y = this.toScreen(v);
+ if (this._minValue >= 0) {
+ while (y < this._canvas.height) {
+ if (y > 0) {
+ grid.push({ y: y, label: v });
+ }
+ v += inc;
+ y = this.toScreen(v);
+ }
+ } else if (this._maxValue <= 0) {
+ while (y > 0) {
+ if (y < this._canvas.height) {
+ grid.push({ y: y, label: v });
+ }
+ v -= inc;
+ y = this.toScreen(v);
+ }
+ } else {
+ while (y < this._canvas.height) {
+ if (y > 0) {
+ grid.push({ y: y, label: v });
+ }
+ v += inc;
+ y = this.toScreen(v);
+ }
+ v = -inc;
+ y = this.toScreen(v);
+ while (y > 0) {
+ if (y < this._canvas.height) {
+ grid.push({ y: y, label: v });
+ }
+ v -= inc;
+ y = this.toScreen(v);
+ }
+ }
+
+ return grid;
+ },
+
+ /*
+ * Update the values that are used by the paint function so that
+ * we don't have to calculate them at every repaint.
+ */
+ _updateMappedValues: function() {
+ this._valueRange = Math.abs(this._maxValue - this._minValue);
+ this._mappedRange = this._map.direct(this._valueRange);
+ }
+
+}
+
+// --------------------------------------------------
+
+/**
+ * This is the constructor for a Logarithmic value geometry, which
+ * is useful when plots have values in different magnitudes but
+ * exhibit similar trends and such trends want to be shown on the same
+ * plot (here a cartesian geometry would make the small magnitudes
+ * disappear).
+ *
+ * NOTE: this class extends Timeplot.DefaultValueGeometry and inherits
+ * all of the methods of that class. So refer to that class.
+ *
+ * @constructor
+ */
+Timeplot.LogarithmicValueGeometry = function(params) {
+ Timeplot.DefaultValueGeometry.apply(this, arguments);
+ this._logMap = {
+ direct: function(v) {
+ return Math.log(v + 1) / Math.log(10);
+ },
+ inverse: function(y) {
+ return Math.exp(Math.log(10) * y) - 1;
+ }
+ }
+ this._mode = "log";
+ this._map = this._logMap;
+ this._calculateGrid = this._logarithmicCalculateGrid;
+};
+
+Timeplot.LogarithmicValueGeometry.prototype._linearCalculateGrid = Timeplot.DefaultValueGeometry.prototype._calculateGrid;
+
+Object.extend(Timeplot.LogarithmicValueGeometry.prototype,Timeplot.DefaultValueGeometry.prototype);
+
+/*
+ * This function calculates the grid spacing that it will be used
+ * by this geometry to draw the grid in order to reduce clutter.
+ */
+Timeplot.LogarithmicValueGeometry.prototype._logarithmicCalculateGrid = function() {
+ var grid = [];
+
+ if (!this._canvas || this._valueRange == 0) return grid;
+
+ var v = 1;
+ var y = this.toScreen(v);
+ while (y < this._canvas.height || isNaN(y)) {
+ if (y > 0) {
+ grid.push({ y: y, label: v });
+ }
+ v *= 10;
+ y = this.toScreen(v);
+ }
+
+ return grid;
+};
+
+/**
+ * Turn the logarithmic scaling off.
+ */
+Timeplot.LogarithmicValueGeometry.prototype.actLinear = function() {
+ this._mode = "lin";
+ this._map = this._linMap;
+ this._calculateGrid = this._linearCalculateGrid;
+ this.reset();
+}
+
+/**
+ * Turn the logarithmic scaling on.
+ */
+Timeplot.LogarithmicValueGeometry.prototype.actLogarithmic = function() {
+ this._mode = "log";
+ this._map = this._logMap;
+ this._calculateGrid = this._logarithmicCalculateGrid;
+ this.reset();
+}
+
+/**
+ * Toggle logarithmic scaling seeting it to on if off and viceversa.
+ */
+Timeplot.LogarithmicValueGeometry.prototype.toggle = function() {
+ if (this._mode == "log") {
+ this.actLinear();
+ } else {
+ this.actLogarithmic();
+ }
+}
+
+// -----------------------------------------------------
+
+/**
+ * This is the constructor for the default time geometry.
+ *
+ * @constructor
+ */
+Timeplot.DefaultTimeGeometry = function(params) {
+ if (!params) params = {};
+ this._id = ("id" in params) ? params.id : "g" + Math.round(Math.random() * 1000000);
+ this._locale = ("locale" in params) ? params.locale : "en";
+ this._timeZone = ("timeZone" in params) ? params.timeZone : SimileAjax.DateTime.getTimezone();
+ this._labeler = ("labeller" in params) ? params.labeller : null;
+ this._axisColor = ("axisColor" in params) ? ((params.axisColor == "string") ? new Timeplot.Color(params.axisColor) : params.axisColor) : new Timeplot.Color("#606060"),
+ this._gridColor = ("gridColor" in params) ? ((params.gridColor == "string") ? new Timeplot.Color(params.gridColor) : params.gridColor) : null,
+ this._gridLineWidth = ("gridLineWidth" in params) ? params.gridLineWidth : 0.5;
+ this._axisLabelsPlacement = ("axisLabelsPlacement" in params) ? params.axisLabelsPlacement : "bottom";
+ this._gridStep = ("gridStep" in params) ? params.gridStep : 100;
+ this._gridStepRange = ("gridStepRange" in params) ? params.gridStepRange : 20;
+ this._min = ("min" in params) ? params.min : null;
+ this._max = ("max" in params) ? params.max : null;
+ this._timeValuePosition =("timeValuePosition" in params) ? params.timeValuePosition : "bottom";
+ this._unit = ("unit" in params) ? params.unit : Timeline.NativeDateUnit;
+ this._linMap = {
+ direct: function(t) {
+ return t;
+ },
+ inverse: function(x) {
+ return x;
+ }
+ }
+ this._map = this._linMap;
+ if (!this._labeler)
+ this._labeler = this._unit.createLabeller(this._locale, this._timeZone);
+ var dateParser = this._unit.getParser("iso8601");
+ if (this._min && !this._min.getTime) {
+ this._min = dateParser(this._min);
+ }
+ if (this._max && !this._max.getTime) {
+ this._max = dateParser(this._max);
+ }
+ this._labels = [];
+ this._grid = [];
+}
+
+Timeplot.DefaultTimeGeometry.prototype = {
+
+ /**
+ * Since geometries can be reused across timeplots, we need to call this function
+ * before we can paint using this geometry.
+ */
+ setTimeplot: function(timeplot) {
+ this._timeplot = timeplot;
+ this._canvas = timeplot.getCanvas();
+ this.reset();
+ },
+
+ /**
+ * Called by all the plot layers this geometry is associated with
+ * to update the time range. Unless min/max values are specified
+ * in the parameters, the biggest range will be used.
+ */
+ setRange: function(range) {
+ if (this._min) {
+ this._earliestDate = this._min;
+ } else if (range.earliestDate && ((this._earliestDate == null) || ((this._earliestDate != null) && (range.earliestDate.getTime() < this._earliestDate.getTime())))) {
+ this._earliestDate = range.earliestDate;
+ }
+
+ if (this._max) {
+ this._latestDate = this._max;
+ } else if (range.latestDate && ((this._latestDate == null) || ((this._latestDate != null) && (range.latestDate.getTime() > this._latestDate.getTime())))) {
+ this._latestDate = range.latestDate;
+ }
+
+ if (!this._earliestDate && !this._latestDate) {
+ this._grid = [];
+ } else {
+ this.reset();
+ }
+ },
+
+ /**
+ * Called after changing ranges or canvas size to reset the grid values
+ */
+ reset: function() {
+ this._updateMappedValues();
+ if (this._canvas) this._grid = this._calculateGrid();
+ },
+
+ /**
+ * Map the given date to a x screen coordinate.
+ */
+ toScreen: function(time) {
+ if (this._canvas && this._latestDate) {
+ var t = time - this._earliestDate.getTime();
+ var fraction = (this._mappedPeriod > 0) ? this._map.direct(t) / this._mappedPeriod : 0;
+ return this._canvas.width * fraction;
+ } else {
+ return -50;
+ }
+ },
+
+ /**
+ * Map the given x screen coordinate to a date.
+ */
+ fromScreen: function(x) {
+ if (this._canvas) {
+ return this._map.inverse(this._mappedPeriod * x / this._canvas.width) + this._earliestDate.getTime();
+ } else {
+ return 0;
+ }
+ },
+
+ /**
+ * Get a period (in milliseconds) this time geometry spans.
+ */
+ getPeriod: function() {
+ return this._period;
+ },
+
+ /**
+ * Return the labeler that has been associated with this time geometry
+ */
+ getLabeler: function() {
+ return this._labeler;
+ },
+
+ /**
+ * Return the time unit associated with this time geometry
+ */
+ getUnit: function() {
+ return this._unit;
+ },
+
+ /**
+ * Each geometry is also a painter and paints the value grid and grid labels.
+ */
+ paint: function() {
+ if (this._canvas) {
+ var unit = this._unit;
+ var ctx = this._canvas.getContext('2d');
+
+ var gradient = ctx.createLinearGradient(0,0,0,this._canvas.height);
+
+ ctx.strokeStyle = gradient;
+ ctx.lineWidth = this._gridLineWidth;
+ ctx.lineJoin = 'miter';
+
+ // paint grid
+ if (this._gridColor) {
+ gradient.addColorStop(0, this._gridColor.toString());
+ gradient.addColorStop(1, "rgba(255,255,255,0.9)");
+
+ for (var i = 0; i < this._grid.length; i++) {
+ var tick = this._grid[i];
+ var x = Math.floor(tick.x) + 0.5;
+ if (this._axisLabelsPlacement == "top") {
+ var div = this._timeplot.putText(this._id + "-" + i, tick.label,"timeplot-grid-label",{
+ left: x + 4,
+ top: 2,
+ visibility: "hidden"
+ });
+ this._labels.push(div);
+ } else if (this._axisLabelsPlacement == "bottom") {
+ var div = this._timeplot.putText(this._id + "-" + i, tick.label, "timeplot-grid-label",{
+ left: x + 4,
+ bottom: 2,
+ visibility: "hidden"
+ });
+ this._labels.push(div);
+ }
+ if (x + div.clientWidth < this._canvas.width + 10) {
+ div.style.visibility = "visible"; // avoid the labels that would overflow
+ }
+
+ // draw separator
+ ctx.beginPath();
+ ctx.moveTo(x,0);
+ ctx.lineTo(x,this._canvas.height);
+ ctx.stroke();
+ }
+ }
+
+ // paint axis
+ gradient.addColorStop(0, this._axisColor.toString());
+ gradient.addColorStop(1, "rgba(255,255,255,0.5)");
+
+ ctx.lineWidth = 1;
+ gradient.addColorStop(0, this._axisColor.toString());
+
+ ctx.beginPath();
+ ctx.moveTo(0,0);
+ ctx.lineTo(this._canvas.width,0);
+ ctx.stroke();
+ }
+ },
+
+ /*
+ * This function calculates the grid spacing that it will be used
+ * by this geometry to draw the grid in order to reduce clutter.
+ */
+ _calculateGrid: function() {
+ var grid = [];
+
+ var time = SimileAjax.DateTime;
+ var u = this._unit;
+ var p = this._period;
+
+ if (p == 0) return grid;
+
+ // find the time units nearest to the time period
+ if (p > time.gregorianUnitLengths[time.MILLENNIUM]) {
+ unit = time.MILLENNIUM;
+ } else {
+ for (var unit = time.MILLENNIUM; unit > 0; unit--) {
+ if (time.gregorianUnitLengths[unit-1] <= p && p < time.gregorianUnitLengths[unit]) {
+ unit--;
+ break;
+ }
+ }
+ }
+
+ var t = u.cloneValue(this._earliestDate);
+
+ do {
+ time.roundDownToInterval(t, unit, this._timeZone, 1, 0);
+ var x = this.toScreen(u.toNumber(t));
+ switch (unit) {
+ case time.SECOND:
+ var l = t.toLocaleTimeString();
+ break;
+ case time.MINUTE:
+ var m = t.getMinutes();
+ var l = t.getHours() + ":" + ((m < 10) ? "0" : "") + m;
+ break;
+ case time.HOUR:
+ var l = t.getHours() + ":00";
+ break;
+ case time.DAY:
+ case time.WEEK:
+ case time.MONTH:
+ var l = t.toLocaleDateString();
+ break;
+ case time.YEAR:
+ case time.DECADE:
+ case time.CENTURY:
+ case time.MILLENNIUM:
+ var l = t.getUTCFullYear();
+ break;
+ }
+ if (x > 0) {
+ grid.push({ x: x, label: l });
+ }
+ time.incrementByInterval(t, unit, this._timeZone);
+ } while (t.getTime() < this._latestDate.getTime());
+
+ return grid;
+ },
+
+ /*
+ * Clear labels generated by this time geometry.
+ */
+ _clearLabels: function() {
+ for (var i = 0; i < this._labels.length; i++) {
+ var l = this._labels[i];
+ var parent = l.parentNode;
+ if (parent) parent.removeChild(l);
+ }
+ },
+
+ /*
+ * Update the values that are used by the paint function so that
+ * we don't have to calculate them at every repaint.
+ */
+ _updateMappedValues: function() {
+ if (this._latestDate && this._earliestDate) {
+ this._period = this._latestDate.getTime() - this._earliestDate.getTime();
+ this._mappedPeriod = this._map.direct(this._period);
+ } else {
+ this._period = 0;
+ this._mappedPeriod = 0;
+ }
+ }
+
+}
+
+// --------------------------------------------------------------
+
+/**
+ * This is the constructor for the magnifying time geometry.
+ * Users can interact with this geometry and 'magnify' certain areas of the
+ * plot to see the plot enlarged and resolve details that would otherwise
+ * get lost or cluttered with a linear time geometry.
+ *
+ * @constructor
+ */
+Timeplot.MagnifyingTimeGeometry = function(params) {
+ Timeplot.DefaultTimeGeometry.apply(this, arguments);
+
+ var g = this;
+ this._MagnifyingMap = {
+ direct: function(t) {
+ if (t < g._leftTimeMargin) {
+ var x = t * g._leftRate;
+ } else if ( g._leftTimeMargin < t && t < g._rightTimeMargin ) {
+ var x = t * g._expandedRate + g._expandedTimeTranslation;
+ } else {
+ var x = t * g._rightRate + g._rightTimeTranslation;
+ }
+ return x;
+ },
+ inverse: function(x) {
+ if (x < g._leftScreenMargin) {
+ var t = x / g._leftRate;
+ } else if ( g._leftScreenMargin < x && x < g._rightScreenMargin ) {
+ var t = x / g._expandedRate + g._expandedScreenTranslation;
+ } else {
+ var t = x / g._rightRate + g._rightScreenTranslation;
+ }
+ return t;
+ }
+ }
+
+ this._mode = "lin";
+ this._map = this._linMap;
+};
+
+Object.extend(Timeplot.MagnifyingTimeGeometry.prototype,Timeplot.DefaultTimeGeometry.prototype);
+
+/**
+ * Initialize this geometry associating it with the given timeplot and
+ * register the geometry event handlers to the timeplot so that it can
+ * interact with the user.
+ */
+Timeplot.MagnifyingTimeGeometry.prototype.initialize = function(timeplot) {
+ Timeplot.DefaultTimeGeometry.prototype.initialize.apply(this, arguments);
+
+ if (!this._lens) {
+ this._lens = this._timeplot.putDiv("lens","timeplot-lens");
+ }
+
+ var period = 1000 * 60 * 60 * 24 * 30; // a month in the magnifying lens
+
+ var geometry = this;
+
+ var magnifyWith = function(lens) {
+ var aperture = lens.clientWidth;
+ var loc = geometry._timeplot.locate(lens);
+ geometry.setMagnifyingParams(loc.x + aperture / 2, aperture, period);
+ geometry.actMagnifying();
+ geometry._timeplot.paint();
+ }
+
+ var canvasMouseDown = function(elmt, evt, target) {
+ geometry._canvas.startCoords = SimileAjax.DOM.getEventRelativeCoordinates(evt,elmt);
+ geometry._canvas.pressed = true;
+ }
+
+ var canvasMouseUp = function(elmt, evt, target) {
+ geometry._canvas.pressed = false;
+ var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt,elmt);
+ if (Timeplot.Math.isClose(coords,geometry._canvas.startCoords,5)) {
+ geometry._lens.style.display = "none";
+ geometry.actLinear();
+ geometry._timeplot.paint();
+ } else {
+ geometry._lens.style.cursor = "move";
+ magnifyWith(geometry._lens);
+ }
+ }
+
+ var canvasMouseMove = function(elmt, evt, target) {
+ if (geometry._canvas.pressed) {
+ var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt,elmt);
+ if (coords.x < 0) coords.x = 0;
+ if (coords.x > geometry._canvas.width) coords.x = geometry._canvas.width;
+ geometry._timeplot.placeDiv(geometry._lens, {
+ left: geometry._canvas.startCoords.x,
+ width: coords.x - geometry._canvas.startCoords.x,
+ bottom: 0,
+ height: geometry._canvas.height,
+ display: "block"
+ });
+ }
+ }
+
+ var lensMouseDown = function(elmt, evt, target) {
+ geometry._lens.startCoords = SimileAjax.DOM.getEventRelativeCoordinates(evt,elmt);;
+ geometry._lens.pressed = true;
+ }
+
+ var lensMouseUp = function(elmt, evt, target) {
+ geometry._lens.pressed = false;
+ }
+
+ var lensMouseMove = function(elmt, evt, target) {
+ if (geometry._lens.pressed) {
+ var coords = SimileAjax.DOM.getEventRelativeCoordinates(evt,elmt);
+ var lens = geometry._lens;
+ var left = lens.offsetLeft + coords.x - lens.startCoords.x;
+ if (left < geometry._timeplot._paddingX) left = geometry._timeplot._paddingX;
+ if (left + lens.clientWidth > geometry._canvas.width - geometry._timeplot._paddingX) left = geometry._canvas.width - lens.clientWidth + geometry._timeplot._paddingX;
+ lens.style.left = left;
+ magnifyWith(lens);
+ }
+ }
+
+ if (!this._canvas.instrumented) {
+ SimileAjax.DOM.registerEvent(this._canvas, "mousedown", canvasMouseDown);
+ SimileAjax.DOM.registerEvent(this._canvas, "mousemove", canvasMouseMove);
+ SimileAjax.DOM.registerEvent(this._canvas, "mouseup" , canvasMouseUp);
+ SimileAjax.DOM.registerEvent(this._canvas, "mouseup" , lensMouseUp);
+ this._canvas.instrumented = true;
+ }
+
+ if (!this._lens.instrumented) {
+ SimileAjax.DOM.registerEvent(this._lens, "mousedown", lensMouseDown);
+ SimileAjax.DOM.registerEvent(this._lens, "mousemove", lensMouseMove);
+ SimileAjax.DOM.registerEvent(this._lens, "mouseup" , lensMouseUp);
+ SimileAjax.DOM.registerEvent(this._lens, "mouseup" , canvasMouseUp);
+ this._lens.instrumented = true;
+ }
+}
+
+/**
+ * Set the Magnifying parameters. c is the location in pixels where the Magnifying
+ * center should be located in the timeplot, a is the aperture in pixel of
+ * the Magnifying and b is the time period in milliseconds that the Magnifying
+ * should span.
+ */
+Timeplot.MagnifyingTimeGeometry.prototype.setMagnifyingParams = function(c,a,b) {
+ a = a / 2;
+ b = b / 2;
+
+ var w = this._canvas.width;
+ var d = this._period;
+
+ if (c < 0) c = 0;
+ if (c > w) c = w;
+
+ if (c - a < 0) a = c;
+ if (c + a > w) a = w - c;
+
+ var ct = this.fromScreen(c) - this._earliestDate.getTime();
+ if (ct - b < 0) b = ct;
+ if (ct + b > d) b = d - ct;
+
+ this._centerX = c;
+ this._centerTime = ct;
+ this._aperture = a;
+ this._aperturePeriod = b;
+
+ this._leftScreenMargin = this._centerX - this._aperture;
+ this._rightScreenMargin = this._centerX + this._aperture;
+ this._leftTimeMargin = this._centerTime - this._aperturePeriod;
+ this._rightTimeMargin = this._centerTime + this._aperturePeriod;
+
+ this._leftRate = (c - a) / (ct - b);
+ this._expandedRate = a / b;
+ this._rightRate = (w - c - a) / (d - ct - b);
+
+ this._expandedTimeTranslation = this._centerX - this._centerTime * this._expandedRate;
+ this._expandedScreenTranslation = this._centerTime - this._centerX / this._expandedRate;
+ this._rightTimeTranslation = (c + a) - (ct + b) * this._rightRate;
+ this._rightScreenTranslation = (ct + b) - (c + a) / this._rightRate;
+
+ this._updateMappedValues();
+}
+
+/*
+ * Turn magnification off.
+ */
+Timeplot.MagnifyingTimeGeometry.prototype.actLinear = function() {
+ this._mode = "lin";
+ this._map = this._linMap;
+ this.reset();
+}
+
+/*
+ * Turn magnification on.
+ */
+Timeplot.MagnifyingTimeGeometry.prototype.actMagnifying = function() {
+ this._mode = "Magnifying";
+ this._map = this._MagnifyingMap;
+ this.reset();
+}
+
+/*
+ * Toggle magnification.
+ */
+Timeplot.MagnifyingTimeGeometry.prototype.toggle = function() {
+ if (this._mode == "Magnifying") {
+ this.actLinear();
+ } else {
+ this.actMagnifying();
+ }
+}
+
+/**
+ * Color
+ *
+ * @fileOverview Color
+ * @name Color
+ */
+
+/*
+ * Inspired by Plotr
+ * Copyright 2007 (c) Bas Wenneker
+ * For use under the BSD license.
+ */
+
+/**
+ * Create a Color object that can be used to manipulate colors programmatically.
+ */
+Timeplot.Color = function(color) {
+ this._fromHex(color);
+};
+
+Timeplot.Color.prototype = {
+
+ /**
+ * Sets the RGB values of this coor
+ *
+ * @param {Number} r,g,b Red green and blue values (between 0 and 255)
+ */
+ set: function (r,g,b,a) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = (a) ? a : 1.0;
+ return this.check();
+ },
+
+ /**
+ * Set the color transparency
+ *
+ * @param {float} a Transparency value, between 0.0 (fully transparent) and 1.0 (fully opaque).
+ */
+ transparency: function(a) {
+ this.a = a;
+ return this.check();
+ },
+
+ /**
+ * Lightens the color.
+ *
+ * @param {integer} level Level to lighten the color with.
+ */
+ lighten: function(level) {
+ var color = new Timeplot.Color();
+ return color.set(
+ this.r += parseInt(level, 10),
+ this.g += parseInt(level, 10),
+ this.b += parseInt(level, 10)
+ );
+ },
+
+ /**
+ * Darkens the color.
+ *
+ * @param {integer} level Level to darken the color with.
+ */
+ darken: function(level){
+ var color = new Timeplot.Color();
+ return color.set(
+ this.r -= parseInt(level, 10),
+ this.g -= parseInt(level, 10),
+ this.b -= parseInt(level, 10)
+ );
+ },
+
+ /**
+ * Checks and validates if the hex values r, g and b are
+ * between 0 and 255.
+ */
+ check: function() {
+ if (this.r > 255) {
+ this.r = 255;
+ } else if (this.r < 0){
+ this.r = 0;
+ }
+ if (this.g > 255) {
+ this.g = 255;
+ } else if (this.g < 0) {
+ this.g = 0;
+ }
+ if (this.b > 255){
+ this.b = 255;
+ } else if (this.b < 0){
+ this.b = 0;
+ }
+ if (this.a > 1.0){
+ this.a = 1.0;
+ } else if (this.a < 0.0){
+ this.a = 0.0;
+ }
+ return this;
+ },
+
+ /**
+ * Returns a string representation of this color.
+ *
+ * @param {float} alpha (optional) Transparency value, between 0.0 (fully transparent) and 1.0 (fully opaque).
+ */
+ toString: function(alpha) {
+ var a = (alpha) ? alpha : ((this.a) ? this.a : 1.0);
+ return 'rgba(' + this.r + ',' + this.g + ',' + this.b + ',' + a + ')';
+ },
+
+ /**
+ * Returns the hexadecimal representation of this color (without the alpha channel as hex colors don't support it)
+ */
+ toHexString: function() {
+ return "#" + this._toHex(this.r) + this._toHex(this.g) + this._toHex(this.b);
+ },
+
+ /*
+ * Parses and stores the hex values of the input color string.
+ *
+ * @param {String} color Hex or rgb() css string.
+ */
+ _fromHex: function(color) {
+ if(/^#?([\da-f]{3}|[\da-f]{6})$/i.test(color)){
+ color = color.replace(/^#/, '').replace(/^([\da-f])([\da-f])([\da-f])$/i, "$1$1$2$2$3$3");
+ this.r = parseInt(color.substr(0,2), 16);
+ this.g = parseInt(color.substr(2,2), 16);
+ this.b = parseInt(color.substr(4,2), 16);
+ } else if(/^rgb *\( *\d{0,3} *, *\d{0,3} *, *\d{0,3} *\)$/i.test(color)){
+ color = color.match(/^rgb *\( *(\d{0,3}) *, *(\d{0,3}) *, *(\d{0,3}) *\)$/i);
+ this.r = parseInt(color[1], 10);
+ this.g = parseInt(color[2], 10);
+ this.b = parseInt(color[3], 10);
+ }
+ this.a = 1.0;
+ return this.check();
+ },
+
+ /*
+ * Returns an hexadecimal representation of a 8 bit integer
+ */
+ _toHex: function(dec) {
+ var hex = "0123456789ABCDEF"
+ if (dec < 0) return "00";
+ if (dec > 255) return "FF";
+ var i = Math.floor(dec / 16);
+ var j = dec % 16;
+ return hex.charAt(i) + hex.charAt(j);
+ }
+
+};
+/**
+ * Math Utility functions
+ *
+ * @fileOverview Math Utility functions
+ * @name Math
+ */
+
+Timeplot.Math = {
+
+ /**
+ * Evaluates the range (min and max values) of the given array
+ */
+ range: function(f) {
+ var F = f.length;
+ var min = Number.MAX_VALUE;
+ var max = Number.MIN_VALUE;
+
+ for (var t = 0; t < F; t++) {
+ var value = f[t];
+ if (value < min) {
+ min = value;
+ }
+ if (value > max) {
+ max = value;
+ }
+ }
+
+ return {
+ min: min,
+ max: max
+ }
+ },
+
+ /**
+ * Evaluates the windows average of a given array based on the
+ * given window size
+ */
+ movingAverage: function(f, size) {
+ var F = f.length;
+ var g = new Array(F);
+ for (var n = 0; n < F; n++) {
+ var value = 0;
+ for (var m = n - size; m < n + size; m++) {
+ if (m < 0) {
+ var v = f[0];
+ } else if (m >= F) {
+ var v = g[n-1];
+ } else {
+ var v = f[m];
+ }
+ value += v;
+ }
+ g[n] = value / (2 * size);
+ }
+ return g;
+ },
+
+ /**
+ * Returns an array with the integral of the given array
+ */
+ integral: function(f) {
+ var F = f.length;
+
+ var g = new Array(F);
+ var sum = 0;
+
+ for (var t = 0; t < F; t++) {
+ sum += f[t];
+ g[t] = sum;
+ }
+
+ return g;
+ },
+
+ /**
+ * Normalizes an array so that its complete integral is 1.
+ * This is useful to obtain arrays that preserve the overall
+ * integral of a convolution.
+ */
+ normalize: function(f) {
+ var F = f.length;
+ var sum = 0.0;
+
+ for (var t = 0; t < F; t++) {
+ sum += f[t];
+ }
+
+ for (var t = 0; t < F; t++) {
+ f[t] /= sum;
+ }
+
+ return f;
+ },
+
+ /**
+ * Calculates the convolution between two arrays
+ */
+ convolution: function(f,g) {
+ var F = f.length;
+ var G = g.length;
+
+ var c = new Array(F);
+
+ for (var m = 0; m < F; m++) {
+ var r = 0;
+ var end = (m + G < F) ? m + G : F;
+ for (var n = m; n < end; n++) {
+ var a = f[n - G];
+ var b = g[n - m];
+ r += a * b;
+ }
+ c[m] = r;
+ }
+
+ return c;
+ },
+
+ // ------ Array generators -------------------------------------------------
+ // Functions that generate arrays based on mathematical functions
+ // Normally these are used to produce operators by convolving them with the input array
+ // The returned arrays have the property of having
+
+ /**
+ * Generate the heavyside step function of given size
+ */
+ heavyside: function(size) {
+ var f = new Array(size);
+ var value = 1 / size;
+ for (var t = 0; t < size; t++) {
+ f[t] = value;
+ }
+ return f;
+ },
+
+ /**
+ * Generate the gaussian function so that at the given 'size' it has value 'threshold'
+ * and make sure its integral is one.
+ */
+ gaussian: function(size, threshold) {
+ with (Math) {
+ var radius = size / 2;
+ var variance = radius * radius / log(threshold);
+ var g = new Array(size);
+ for (var t = 0; t < size; t++) {
+ var l = t - radius;
+ g[t] = exp(-variance * l * l);
+ }
+ }
+
+ return this.normalize(g);
+ },
+
+ // ---- Utility Methods --------------------------------------------------
+
+ /**
+ * Return x with n significant figures
+ */
+ round: function(x,n) {
+ with (Math) {
+ if (abs(x) > 1) {
+ var l = floor(log(x)/log(10));
+ var d = round(exp((l-n+1)*log(10)));
+ var y = round(round(x / d) * d);
+ return y;
+ } else {
+ log("FIXME(SM): still to implement for 0 < abs(x) < 1");
+ return x;
+ }
+ }
+ },
+
+ /**
+ * Return the hyperbolic tangent of x
+ */
+ tanh: function(x) {
+ if (x > 5) {
+ return 1;
+ } else if (x < 5) {
+ return -1;
+ } else {
+ var expx2 = Math.exp(2 * x);
+ return (expx2 - 1) / (expx2 + 1);
+ }
+ },
+
+ /**
+ * Returns true if |a.x - b.x| < value && | a.y - b.y | < value
+ */
+ isClose: function(a,b,value) {
+ return (a && b && Math.abs(a.x - b.x) < value && Math.abs(a.y - b.y) < value);
+ }
+
+}
+/**
+ * Processing Data Source
+ *
+ * @fileOverview Processing Data Source and Operators
+ * @name Processor
+ */
+
+/* -----------------------------------------------------------------------------
+ * Operators
+ *
+ * These are functions that can be used directly as Timeplot.Processor operators
+ * ----------------------------------------------------------------------------- */
+
+Timeplot.Operator = {
+
+ /**
+ * This is the operator used when you want to draw the cumulative sum
+ * of a time series and not, for example, their daily values.
+ */
+ sum: function(data, params) {
+ return Timeplot.Math.integral(data.values);
+ },
+
+ /**
+ * This is the operator that is used to 'smooth' a given time series
+ * by taking the average value of a moving window centered around
+ * each value. The size of the moving window is influenced by the 'size'
+ * parameters in the params map.
+ */
+ average: function(data, params) {
+ var size = ("size" in params) ? params.size : 30;
+ var result = Timeplot.Math.movingAverage(data.values, size);
+ return result;
+ }
+}
+
+/*==================================================
+ * Processing Data Source
+ *==================================================*/
+
+/**
+ * A Processor is a special DataSource that can apply an Operator
+ * to the DataSource values and thus return a different one.
+ *
+ * @constructor
+ */
+Timeplot.Processor = function(dataSource, operator, params) {
+ this._dataSource = dataSource;
+ this._operator = operator;
+ this._params = params;
+
+ this._data = {
+ times: new Array(),
+ values: new Array()
+ };
+
+ this._range = {
+ earliestDate: null,
+ latestDate: null,
+ min: 0,
+ max: 0
+ };
+
+ var processor = this;
+ this._processingListener = {
+ onAddMany: function() { processor._process(); },
+ onClear: function() { processor._clear(); }
+ }
+ this.addListener(this._processingListener);
+};
+
+Timeplot.Processor.prototype = {
+
+ _clear: function() {
+ this.removeListener(this._processingListener);
+ this._dataSource._clear();
+ },
+
+ _process: function() {
+ // this method requires the dataSource._process() method to be
+ // called first as to setup the data and range used below
+ // this should be guaranteed by the order of the listener registration
+
+ var data = this._dataSource.getData();
+ var range = this._dataSource.getRange();
+
+ var newValues = this._operator(data, this._params);
+ var newValueRange = Timeplot.Math.range(newValues);
+
+ this._data = {
+ times: data.times,
+ values: newValues
+ };
+
+ this._range = {
+ earliestDate: range.earliestDate,
+ latestDate: range.latestDate,
+ min: newValueRange.min,
+ max: newValueRange.max
+ };
+ },
+
+ getRange: function() {
+ return this._range;
+ },
+
+ getData: function() {
+ return this._data;
+ },
+
+ getValue: Timeplot.DataSource.prototype.getValue,
+
+ getClosestValidTime: Timeplot.DataSource.prototype.getClosestValidTime,
+
+ addListener: function(listener) {
+ this._dataSource.addListener(listener);
+ },
+
+ removeListener: function(listener) {
+ this._dataSource.removeListener(listener);
+ }
+}
+/*----------------------------------------------------------------------------\
+| Range Class |
+|-----------------------------------------------------------------------------|
+| Created by Erik Arvidsson |
+| (http://webfx.eae.net/contact.html#erik) |
+| For WebFX (http://webfx.eae.net/) |
+|-----------------------------------------------------------------------------|
+| Used to model the data used when working with sliders, scrollbars and |
+| progress bars. Based on the ideas of the javax.swing.BoundedRangeModel |
+| interface defined by Sun for Java; http://java.sun.com/products/jfc/ |
+| swingdoc-api-1.0.3/com/sun/java/swing/BoundedRangeModel.html |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2002, 2005, 2006 Erik Arvidsson |
+|-----------------------------------------------------------------------------|
+| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
+| use this file except in compliance with the License. You may obtain a copy |
+| of the License at http://www.apache.org/licenses/LICENSE-2.0 |
+| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
+| Unless required by applicable law or agreed to in writing, software |
+| distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
+| WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
+| License for the specific language governing permissions and limitations |
+| under the License. |
+|-----------------------------------------------------------------------------|
+| 2002-10-14 | Original version released |
+| 2005-10-27 | Use Math.round instead of Math.floor |
+| 2006-05-28 | Changed license to Apache Software License 2.0. |
+|-----------------------------------------------------------------------------|
+| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
+\----------------------------------------------------------------------------*/
+
+
+function Range() {
+ this._value = 0;
+ this._minimum = 0;
+ this._maximum = 100;
+ this._extent = 0;
+
+ this._isChanging = false;
+}
+
+Range.prototype.setValue = function (value) {
+ value = Math.round(parseFloat(value));
+ if (isNaN(value)) return;
+ if (this._value != value) {
+ if (value + this._extent > this._maximum)
+ this._value = this._maximum - this._extent;
+ else if (value < this._minimum)
+ this._value = this._minimum;
+ else
+ this._value = value;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getValue = function () {
+ return this._value;
+};
+
+Range.prototype.setExtent = function (extent) {
+ if (this._extent != extent) {
+ if (extent < 0)
+ this._extent = 0;
+ else if (this._value + extent > this._maximum)
+ this._extent = this._maximum - this._value;
+ else
+ this._extent = extent;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getExtent = function () {
+ return this._extent;
+};
+
+Range.prototype.setMinimum = function (minimum) {
+ if (this._minimum != minimum) {
+ var oldIsChanging = this._isChanging;
+ this._isChanging = true;
+
+ this._minimum = minimum;
+
+ if (minimum > this._value)
+ this.setValue(minimum);
+ if (minimum > this._maximum) {
+ this._extent = 0;
+ this.setMaximum(minimum);
+ this.setValue(minimum)
+ }
+ if (minimum + this._extent > this._maximum)
+ this._extent = this._maximum - this._minimum;
+
+ this._isChanging = oldIsChanging;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getMinimum = function () {
+ return this._minimum;
+};
+
+Range.prototype.setMaximum = function (maximum) {
+ if (this._maximum != maximum) {
+ var oldIsChanging = this._isChanging;
+ this._isChanging = true;
+
+ this._maximum = maximum;
+
+ if (maximum < this._value)
+ this.setValue(maximum - this._extent);
+ if (maximum < this._minimum) {
+ this._extent = 0;
+ this.setMinimum(maximum);
+ this.setValue(this._maximum);
+ }
+ if (maximum < this._minimum + this._extent)
+ this._extent = this._maximum - this._minimum;
+ if (maximum < this._value + this._extent)
+ this._extent = this._maximum - this._value;
+
+ this._isChanging = oldIsChanging;
+ if (!this._isChanging && typeof this.onchange == "function")
+ this.onchange();
+ }
+};
+
+Range.prototype.getMaximum = function () {
+ return this._maximum;
+};
+/*----------------------------------------------------------------------------\
+| Slider 1.02 |
+|-----------------------------------------------------------------------------|
+| Created by Erik Arvidsson |
+| (http://webfx.eae.net/contact.html#erik) |
+| For WebFX (http://webfx.eae.net/) |
+|-----------------------------------------------------------------------------|
+| A slider control that degrades to an input control for non supported |
+| browsers. |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2002, 2003, 2006 Erik Arvidsson |
+|-----------------------------------------------------------------------------|
+| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
+| use this file except in compliance with the License. You may obtain a copy |
+| of the License at http://www.apache.org/licenses/LICENSE-2.0 |
+| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
+| Unless required by applicable law or agreed to in writing, software |
+| distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
+| WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
+| License for the specific language governing permissions and limitations |
+| under the License. |
+|-----------------------------------------------------------------------------|
+| Dependencies: timer.js - an OO abstraction of timers |
+| range.js - provides the data model for the slider |
+| winclassic.css or any other css file describing the look |
+|-----------------------------------------------------------------------------|
+| 2002-10-14 | Original version released |
+| 2003-03-27 | Added a test in the constructor for missing oElement arg |
+| 2003-11-27 | Only use mousewheel when focused |
+| 2006-05-28 | Changed license to Apache Software License 2.0. |
+|-----------------------------------------------------------------------------|
+| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
+\----------------------------------------------------------------------------*/
+
+Slider.isSupported = typeof document.createElement != "undefined" &&
+ typeof document.documentElement != "undefined" &&
+ typeof document.documentElement.offsetWidth == "number";
+
+
+function Slider(oElement, oInput, sOrientation) {
+ if (!oElement) return;
+ this._orientation = sOrientation || "horizontal";
+ this._range = new Range();
+ this._range.setExtent(0);
+ this._blockIncrement = 10;
+ this._unitIncrement = 1;
+ this._timer = new Timer(100);
+
+
+ if (Slider.isSupported && oElement) {
+
+ this.document = oElement.ownerDocument || oElement.document;
+
+ this.element = oElement;
+ this.element.slider = this;
+ this.element.unselectable = "on";
+
+ // add class name tag to class name
+ this.element.className = this._orientation + " " + this.classNameTag + " " + this.element.className;
+
+ // create line
+ this.line = this.document.createElement("DIV");
+ this.line.className = "line";
+ this.line.unselectable = "on";
+ this.line.appendChild(this.document.createElement("DIV"));
+ this.element.appendChild(this.line);
+
+ // create handle
+ this.handle = this.document.createElement("DIV");
+ this.handle.className = "handle";
+ this.handle.unselectable = "on";
+ this.handle.appendChild(this.document.createElement("DIV"));
+ this.handle.firstChild.appendChild(
+ this.document.createTextNode(String.fromCharCode(160)));
+ this.element.appendChild(this.handle);
+ }
+
+ this.input = oInput;
+
+ // events
+ var oThis = this;
+ this._range.onchange = function () {
+ oThis.recalculate();
+ if (typeof oThis.onchange == "function")
+ oThis.onchange();
+ };
+
+ if (Slider.isSupported && oElement) {
+ this.element.onfocus = Slider.eventHandlers.onfocus;
+ this.element.onblur = Slider.eventHandlers.onblur;
+ this.element.onmousedown = Slider.eventHandlers.onmousedown;
+ this.element.onmouseover = Slider.eventHandlers.onmouseover;
+ this.element.onmouseout = Slider.eventHandlers.onmouseout;
+ this.element.onkeydown = Slider.eventHandlers.onkeydown;
+ this.element.onkeypress = Slider.eventHandlers.onkeypress;
+ this.element.onmousewheel = Slider.eventHandlers.onmousewheel;
+ this.handle.onselectstart =
+ this.element.onselectstart = function () { return false; };
+
+ this._timer.ontimer = function () {
+ oThis.ontimer();
+ };
+
+ // extra recalculate for ie
+ window.setTimeout(function() {
+ oThis.recalculate();
+ }, 1);
+ }
+ else {
+ this.input.onchange = function (e) {
+ oThis.setValue(oThis.input.value);
+ };
+ }
+}
+
+Slider.eventHandlers = {
+
+ // helpers to make events a bit easier
+ getEvent: function (e, el) {
+ if (!e) {
+ if (el)
+ e = el.document.parentWindow.event;
+ else
+ e = window.event;
+ }
+ if (!e.srcElement) {
+ var el = e.target;
+ while (el != null && el.nodeType != 1)
+ el = el.parentNode;
+ e.srcElement = el;
+ }
+ if (typeof e.offsetX == "undefined") {
+ e.offsetX = e.layerX;
+ e.offsetY = e.layerY;
+ }
+
+ return e;
+ },
+
+ getDocument: function (e) {
+ if (e.target)
+ return e.target.ownerDocument;
+ return e.srcElement.document;
+ },
+
+ getSlider: function (e) {
+ var el = e.target || e.srcElement;
+ while (el != null && el.slider == null) {
+ el = el.parentNode;
+ }
+ if (el)
+ return el.slider;
+ return null;
+ },
+
+ getLine: function (e) {
+ var el = e.target || e.srcElement;
+ while (el != null && el.className != "line") {
+ el = el.parentNode;
+ }
+ return el;
+ },
+
+ getHandle: function (e) {
+ var el = e.target || e.srcElement;
+ var re = /handle/;
+ while (el != null && !re.test(el.className)) {
+ el = el.parentNode;
+ }
+ return el;
+ },
+ // end helpers
+
+ onfocus: function (e) {
+ var s = this.slider;
+ s._focused = true;
+ s.handle.className = "handle hover";
+ },
+
+ onblur: function (e) {
+ var s = this.slider
+ s._focused = false;
+ s.handle.className = "handle";
+ },
+
+ onmouseover: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (e.srcElement == s.handle)
+ s.handle.className = "handle hover";
+ },
+
+ onmouseout: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (e.srcElement == s.handle && !s._focused)
+ s.handle.className = "handle";
+ },
+
+ onmousedown: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (s.element.focus)
+ s.element.focus();
+
+ Slider._currentInstance = s;
+ var doc = s.document;
+
+ if (doc.addEventListener) {
+ doc.addEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
+ doc.addEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
+ }
+ else if (doc.attachEvent) {
+ doc.attachEvent("onmousemove", Slider.eventHandlers.onmousemove);
+ doc.attachEvent("onmouseup", Slider.eventHandlers.onmouseup);
+ doc.attachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
+ s.element.setCapture();
+ }
+
+ if (Slider.eventHandlers.getHandle(e)) { // start drag
+ Slider._sliderDragData = {
+ screenX: e.screenX,
+ screenY: e.screenY,
+ dx: e.screenX - s.handle.offsetLeft,
+ dy: e.screenY - s.handle.offsetTop,
+ startValue: s.getValue(),
+ slider: s
+ };
+ }
+ else {
+ return;
+ var lineEl = Slider.eventHandlers.getLine(e);
+ s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
+ s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
+ s._increasing = null;
+ s.ontimer();
+ }
+ },
+
+ onmousemove: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+
+ if (Slider._sliderDragData) { // drag
+ var s = Slider._sliderDragData.slider;
+
+ var boundSize = s.getMaximum() - s.getMinimum();
+ var size, pos, reset;
+
+ if (s._orientation == "horizontal") {
+ size = s.element.offsetWidth - s.handle.offsetWidth;
+ pos = e.screenX - Slider._sliderDragData.dx;
+ reset = Math.abs(e.screenY - Slider._sliderDragData.screenY) > 100;
+ }
+ else {
+ size = s.element.offsetHeight - s.handle.offsetHeight;
+ pos = s.element.offsetHeight - s.handle.offsetHeight -
+ (e.screenY - Slider._sliderDragData.dy);
+ reset = Math.abs(e.screenX - Slider._sliderDragData.screenX) > 100;
+ }
+ s.setValue(reset ? Slider._sliderDragData.startValue :
+ s.getMinimum() + boundSize * pos / size);
+ return false;
+ }
+ else {
+ return;
+ var s = Slider._currentInstance;
+ if (s != null) {
+ var lineEl = Slider.eventHandlers.getLine(e);
+ s._mouseX = e.offsetX + (lineEl ? s.line.offsetLeft : 0);
+ s._mouseY = e.offsetY + (lineEl ? s.line.offsetTop : 0);
+ }
+ }
+
+ },
+
+ onmouseup: function (e) {
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = Slider._currentInstance;
+ var doc = s.document;
+ if (doc.removeEventListener) {
+ doc.removeEventListener("mousemove", Slider.eventHandlers.onmousemove, true);
+ doc.removeEventListener("mouseup", Slider.eventHandlers.onmouseup, true);
+ }
+ else if (doc.detachEvent) {
+ doc.detachEvent("onmousemove", Slider.eventHandlers.onmousemove);
+ doc.detachEvent("onmouseup", Slider.eventHandlers.onmouseup);
+ doc.detachEvent("onlosecapture", Slider.eventHandlers.onmouseup);
+ s.element.releaseCapture();
+ }
+
+ if (Slider._sliderDragData) { // end drag
+ Slider._sliderDragData = null;
+ }
+ else {
+ return;
+ s._timer.stop();
+ s._increasing = null;
+ }
+ Slider._currentInstance = null;
+ },
+
+ onkeydown: function (e) {
+ return;
+ e = Slider.eventHandlers.getEvent(e, this);
+ //var s = Slider.eventHandlers.getSlider(e);
+ var s = this.slider;
+ var kc = e.keyCode;
+ switch (kc) {
+ case 33: // page up
+ s.setValue(s.getValue() + s.getBlockIncrement());
+ break;
+ case 34: // page down
+ s.setValue(s.getValue() - s.getBlockIncrement());
+ break;
+ case 35: // end
+ s.setValue(s.getOrientation() == "horizontal" ?
+ s.getMaximum() :
+ s.getMinimum());
+ break;
+ case 36: // home
+ s.setValue(s.getOrientation() == "horizontal" ?
+ s.getMinimum() :
+ s.getMaximum());
+ break;
+ case 38: // up
+ case 39: // right
+ s.setValue(s.getValue() + s.getUnitIncrement());
+ break;
+
+ case 37: // left
+ case 40: // down
+ s.setValue(s.getValue() - s.getUnitIncrement());
+ break;
+ }
+
+ if (kc >= 33 && kc <= 40) {
+ return false;
+ }
+ },
+
+ onkeypress: function (e) {
+ return;
+ e = Slider.eventHandlers.getEvent(e, this);
+ var kc = e.keyCode;
+ if (kc >= 33 && kc <= 40) {
+ return false;
+ }
+ },
+
+ onmousewheel: function (e) {
+ return;
+ e = Slider.eventHandlers.getEvent(e, this);
+ var s = this.slider;
+ if (s._focused) {
+ s.setValue(s.getValue() + e.wheelDelta / 120 * s.getUnitIncrement());
+ // windows inverts this on horizontal sliders. That does not
+ // make sense to me
+ return false;
+ }
+ }
+};
+
+
+
+Slider.prototype.classNameTag = "dynamic-slider-control",
+
+Slider.prototype.setValue = function (v) {
+ this._range.setValue(v);
+ this.input.value = this.getValue();
+};
+
+Slider.prototype.getValue = function () {
+ return this._range.getValue();
+};
+
+Slider.prototype.setMinimum = function (v) {
+ this._range.setMinimum(v);
+ this.input.value = this.getValue();
+};
+
+Slider.prototype.getMinimum = function () {
+ return this._range.getMinimum();
+};
+
+Slider.prototype.setMaximum = function (v) {
+ this._range.setMaximum(v);
+ this.input.value = this.getValue();
+};
+
+Slider.prototype.getMaximum = function () {
+ return this._range.getMaximum();
+};
+
+Slider.prototype.setUnitIncrement = function (v) {
+ this._unitIncrement = v;
+};
+
+Slider.prototype.getUnitIncrement = function () {
+ return this._unitIncrement;
+};
+
+Slider.prototype.setBlockIncrement = function (v) {
+ this._blockIncrement = v;
+};
+
+Slider.prototype.getBlockIncrement = function () {
+ return this._blockIncrement;
+};
+
+Slider.prototype.getOrientation = function () {
+ return this._orientation;
+};
+
+Slider.prototype.setOrientation = function (sOrientation) {
+ if (sOrientation != this._orientation) {
+ if (Slider.isSupported && this.element) {
+ // add class name tag to class name
+ this.element.className = this.element.className.replace(this._orientation,
+ sOrientation);
+ }
+ this._orientation = sOrientation;
+ this.recalculate();
+
+ }
+};
+
+Slider.prototype.recalculate = function() {
+ if (!Slider.isSupported || !this.element) return;
+
+ var w = this.element.offsetWidth;
+ var h = this.element.offsetHeight;
+ var hw = this.handle.offsetWidth;
+ var hh = this.handle.offsetHeight;
+ var lw = this.line.offsetWidth;
+ var lh = this.line.offsetHeight;
+
+ // this assumes a border-box layout
+
+ if (this._orientation == "horizontal") {
+ this.handle.style.left = (w - hw) * (this.getValue() - this.getMinimum()) /
+ (this.getMaximum() - this.getMinimum()) + "px";
+ this.handle.style.top = (h - hh) / 2 + "px";
+
+ this.line.style.top = (h - lh) / 2 + "px";
+ this.line.style.left = hw / 2 + "px";
+ //this.line.style.right = hw / 2 + "px";
+ this.line.style.width = Math.max(0, w - hw - 2)+ "px";
+ this.line.firstChild.style.width = Math.max(0, w - hw - 4)+ "px";
+ }
+ else {
+ this.handle.style.left = (w - hw) / 2 + "px";
+ this.handle.style.top = h - hh - (h - hh) * (this.getValue() - this.getMinimum()) /
+ (this.getMaximum() - this.getMinimum()) + "px";
+
+ this.line.style.left = (w - lw) / 2 + "px";
+ this.line.style.top = hh / 2 + "px";
+ this.line.style.height = Math.max(0, h - hh - 2) + "px"; //hard coded border width
+ //this.line.style.bottom = hh / 2 + "px";
+ this.line.firstChild.style.height = Math.max(0, h - hh - 4) + "px"; //hard coded border width
+ }
+};
+
+Slider.prototype.ontimer = function () {
+ var hw = this.handle.offsetWidth;
+ var hh = this.handle.offsetHeight;
+ var hl = this.handle.offsetLeft;
+ var ht = this.handle.offsetTop;
+
+ if (this._orientation == "horizontal") {
+ if (this._mouseX > hl + hw &&
+ (this._increasing == null || this._increasing)) {
+ this.setValue(this.getValue() + this.getBlockIncrement());
+ this._increasing = true;
+ }
+ else if (this._mouseX < hl &&
+ (this._increasing == null || !this._increasing)) {
+ this.setValue(this.getValue() - this.getBlockIncrement());
+ this._increasing = false;
+ }
+ }
+ else {
+ if (this._mouseY > ht + hh &&
+ (this._increasing == null || !this._increasing)) {
+ this.setValue(this.getValue() - this.getBlockIncrement());
+ this._increasing = false;
+ }
+ else if (this._mouseY < ht &&
+ (this._increasing == null || this._increasing)) {
+ this.setValue(this.getValue() + this.getBlockIncrement());
+ this._increasing = true;
+ }
+ }
+
+ this._timer.start();
+};
+/*----------------------------------------------------------------------------\
+| Timer Class |
+|-----------------------------------------------------------------------------|
+| Created by Erik Arvidsson |
+| (http://webfx.eae.net/contact.html#erik) |
+| For WebFX (http://webfx.eae.net/) |
+|-----------------------------------------------------------------------------|
+| Object Oriented Encapsulation of setTimeout fires ontimer when the timer |
+| is triggered. Does not work in IE 5.00 |
+|-----------------------------------------------------------------------------|
+| Copyright (c) 2002, 2006 Erik Arvidsson |
+|-----------------------------------------------------------------------------|
+| Licensed under the Apache License, Version 2.0 (the "License"); you may not |
+| use this file except in compliance with the License. You may obtain a copy |
+| of the License at http://www.apache.org/licenses/LICENSE-2.0 |
+| - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
+| Unless required by applicable law or agreed to in writing, software |
+| distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
+| WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
+| License for the specific language governing permissions and limitations |
+| under the License. |
+|-----------------------------------------------------------------------------|
+| 2002-10-14 | Original version released |
+| 2006-05-28 | Changed license to Apache Software License 2.0. |
+|-----------------------------------------------------------------------------|
+| Created 2002-10-14 | All changes are in the log above. | Updated 2006-05-28 |
+\----------------------------------------------------------------------------*/
+
+function Timer(nPauseTime) {
+ this._pauseTime = typeof nPauseTime == "undefined" ? 1000 : nPauseTime;
+ this._timer = null;
+ this._isStarted = false;
+}
+
+Timer.prototype.start = function () {
+ if (this.isStarted())
+ this.stop();
+ var oThis = this;
+ this._timer = window.setTimeout(function () {
+ if (typeof oThis.ontimer == "function")
+ oThis.ontimer();
+ }, this._pauseTime);
+ this._isStarted = false;
+};
+
+Timer.prototype.stop = function () {
+ if (this._timer != null)
+ window.clearTimeout(this._timer);
+ this._isStarted = false;
+};
+
+Timer.prototype.isStarted = function () {
+ return this._isStarted;
+};
+
+Timer.prototype.getPauseTime = function () {
+ return this._pauseTime;
+};
+
+Timer.prototype.setPauseTime = function (nPauseTime) {
+ this._pauseTime = nPauseTime;
+};
+/*
+* SimileTimeplotModify.js
+*
+* Copyright (c) 2012, Stefan Jänicke. All rights reserved.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+* MA 02110-1301 USA
+*/
+
+/**
+ * Modified (overwritten) Simile Timeplot Functions
+ * @author Stefan Jänicke (stjaenicke@informatik.uni-leipzig.de)
+ * @release 1.0
+ * @release date: 2012-07-25
+ * @version date: 2012-07-25
+ */
+SimileAjax.DateTime.MILLISECOND = 0;
+SimileAjax.DateTime.SECOND = 1;
+SimileAjax.DateTime.MINUTE = 2;
+SimileAjax.DateTime.HOUR = 3;
+SimileAjax.DateTime.DAY = 4;
+SimileAjax.DateTime.WEEK = 5;
+SimileAjax.DateTime.MONTH = 6;
+SimileAjax.DateTime.QUARTER = 7;
+SimileAjax.DateTime.SEMESTER = 8;
+SimileAjax.DateTime.YEAR = 9;
+SimileAjax.DateTime.LUSTRUM = 10;
+SimileAjax.DateTime.DECADE = 11;
+SimileAjax.DateTime.HALFCENTURY = 12;
+SimileAjax.DateTime.CENTURY = 13;
+SimileAjax.DateTime.HALFMILLENNIUM = 14;
+SimileAjax.DateTime.MILLENNIUM = 15;
+
+SimileAjax.DateTime.Strings = {
+ "en" : ["milliseconds", "seconds", "minutes", "hours", "days", "weeks", "months", "quarters", "semester", "years", "5 years", "decades", "50 years", "centuries", "500 years", "millenniums"],
+ "de" : ["Millisekunden", "Sekunden", "Minuten", "Stunden", "Tage", "Wochen", "Monate", "Quartale", "Semester", "Jahre", "5 Jahre", "Dekaden", "50 Jahre", "Jahrhunderte", "500 Jahre", "Jahrtausende"]
+};
+
+SimileAjax.DateTime.gregorianUnitLengths = [];
+(function() {
+ var d = SimileAjax.DateTime;
+ var a = d.gregorianUnitLengths;
+
+ a[d.MILLISECOND] = 1;
+ a[d.SECOND] = 1000;
+ a[d.MINUTE] = a[d.SECOND] * 60;
+ a[d.HOUR] = a[d.MINUTE] * 60;
+ a[d.DAY] = a[d.HOUR] * 24;
+ a[d.WEEK] = a[d.DAY] * 7;
+ a[d.MONTH] = a[d.DAY] * 31;
+ a[d.QUARTER] = a[d.DAY] * 91;
+ a[d.SEMESTER] = a[d.DAY] * 182;
+ a[d.YEAR] = a[d.DAY] * 365;
+ a[d.LUSTRUM] = a[d.YEAR] * 5;
+ a[d.DECADE] = a[d.YEAR] * 10;
+ a[d.HALFCENTURY] = a[d.YEAR] * 50;
+ a[d.CENTURY] = a[d.YEAR] * 100;
+ a[d.HALFMILLENNIUM] = a[d.YEAR] * 500;
+ a[d.MILLENNIUM] = a[d.YEAR] * 1000;
+})();
+
+SimileAjax.DateTime.roundDownToInterval = function(date, intervalUnit, timeZone, multiple, firstDayOfWeek) {
+ timeZone = ( typeof timeZone == 'undefined') ? 0 : timeZone;
+ var timeShift = timeZone * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
+
+ var date2 = new Date(date.getTime() + timeShift);
+ var clearInDay = function(d) {
+ d.setUTCMilliseconds(0);
+ d.setUTCSeconds(0);
+ d.setUTCMinutes(0);
+ d.setUTCHours(0);
+ };
+ var clearInWeek = function(d) {
+ clearInDay(d);
+ var day = d.getDay();
+ var millies = d.getTime();
+ millies -= day * 1000 * 60 * 60 * 24;
+ d.setTime(millies);
+ };
+ var clearInYear = function(d) {
+ clearInDay(d);
+ d.setUTCDate(1);
+ d.setUTCMonth(0);
+ };
+
+ switch (intervalUnit) {
+ case SimileAjax.DateTime.MILLISECOND:
+ var x = date2.getUTCMilliseconds();
+ date2.setUTCMilliseconds(x - (x % multiple));
+ break;
+ case SimileAjax.DateTime.SECOND:
+ date2.setUTCMilliseconds(0);
+ var x = date2.getUTCSeconds();
+ date2.setUTCSeconds(x - (x % multiple));
+ break;
+ case SimileAjax.DateTime.MINUTE:
+ date2.setUTCMilliseconds(0);
+ date2.setUTCSeconds(0);
+ var x = date2.getUTCMinutes();
+ date2.setTime(date2.getTime() - (x % multiple) * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
+ break;
+ case SimileAjax.DateTime.HOUR:
+ date2.setUTCMilliseconds(0);
+ date2.setUTCSeconds(0);
+ date2.setUTCMinutes(0);
+ var x = date2.getUTCHours();
+ date2.setUTCHours(x - (x % multiple));
+ break;
+ case SimileAjax.DateTime.DAY:
+ clearInDay(date2);
+ break;
+ case SimileAjax.DateTime.WEEK:
+ clearInWeek(date2);
+ break;
+ case SimileAjax.DateTime.MONTH:
+ clearInDay(date2);
+ date2.setUTCDate(1);
+ var x = date2.getUTCMonth();
+ date2.setUTCMonth(x - (x % multiple));
+ break;
+ case SimileAjax.DateTime.QUARTER:
+ clearInDay(date2);
+ date2.setUTCDate(1);
+ var x = date2.getUTCMonth();
+ date2.setUTCMonth(x - (x % 3));
+ break;
+ case SimileAjax.DateTime.SEMESTER:
+ clearInDay(date2);
+ date2.setUTCDate(1);
+ var x = date2.getUTCMonth();
+ date2.setUTCMonth(x - (x % 6));
+ break;
+ case SimileAjax.DateTime.YEAR:
+ clearInYear(date2);
+ var x = date2.getUTCFullYear();
+ date2.setUTCFullYear(x - (x % multiple));
+ break;
+ case SimileAjax.DateTime.LUSTRUM:
+ clearInYear(date2);
+ date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 5) * 5);
+ break;
+ case SimileAjax.DateTime.DECADE:
+ clearInYear(date2);
+ date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 10) * 10);
+ break;
+ case SimileAjax.DateTime.HALFCENTURY:
+ clearInYear(date2);
+ date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 50) * 50);
+ break;
+ case SimileAjax.DateTime.CENTURY:
+ clearInYear(date2);
+ date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 100) * 100);
+ break;
+ case SimileAjax.DateTime.HALFMILLENNIUM:
+ clearInYear(date2);
+ date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 500) * 500);
+ break;
+ case SimileAjax.DateTime.MILLENNIUM:
+ clearInYear(date2);
+ date2.setUTCFullYear(Math.floor(date2.getUTCFullYear() / 1000) * 1000);
+ break;
+ }
+
+ date.setTime(date2.getTime() - timeShift);
+};
+
+SimileAjax.DateTime.incrementByInterval = function(date, intervalUnit, timeZone) {
+ timeZone = ( typeof timeZone == 'undefined') ? 0 : timeZone;
+
+ var timeShift = timeZone * SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR];
+
+ var date2 = new Date(date.getTime() + timeShift);
+
+ switch (intervalUnit) {
+ case SimileAjax.DateTime.MILLISECOND:
+ date2.setTime(date2.getTime() + 1)
+ break;
+ case SimileAjax.DateTime.SECOND:
+ date2.setTime(date2.getTime() + 1000);
+ break;
+ case SimileAjax.DateTime.MINUTE:
+ date2.setTime(date2.getTime() + SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.MINUTE]);
+ break;
+ case SimileAjax.DateTime.HOUR:
+ date2.setTime(date2.getTime() + SimileAjax.DateTime.gregorianUnitLengths[SimileAjax.DateTime.HOUR]);
+ break;
+ case SimileAjax.DateTime.DAY:
+ date2.setUTCDate(date2.getUTCDate() + 1);
+ break;
+ case SimileAjax.DateTime.WEEK:
+ date2.setUTCDate(date2.getUTCDate() + 7);
+ break;
+ case SimileAjax.DateTime.MONTH:
+ date2.setUTCMonth(date2.getUTCMonth() + 1);
+ break;
+ case SimileAjax.DateTime.QUARTER:
+ date2.setUTCMonth(date2.getUTCMonth() + 3);
+ break;
+ case SimileAjax.DateTime.SEMESTER:
+ date2.setUTCMonth(date2.getUTCMonth() + 6);
+ break;
+ case SimileAjax.DateTime.YEAR:
+ date2.setUTCFullYear(date2.getUTCFullYear() + 1);
+ break;
+ case SimileAjax.DateTime.LUSTRUM:
+ date2.setUTCFullYear(date2.getUTCFullYear() + 5);
+ break;
+ case SimileAjax.DateTime.DECADE:
+ date2.setUTCFullYear(date2.getUTCFullYear() + 10);
+ break;
+ case SimileAjax.DateTime.HALFCENTURY:
+ date2.setUTCFullYear(date2.getUTCFullYear() + 50);
+ break;
+ case SimileAjax.DateTime.CENTURY:
+ date2.setUTCFullYear(date2.getUTCFullYear() + 100);
+ break;
+ case SimileAjax.DateTime.HALFMILLENNIUM:
+ date2.setUTCFullYear(date2.getUTCFullYear() + 500);
+ break;
+ case SimileAjax.DateTime.MILLENNIUM:
+ date2.setUTCFullYear(date2.getUTCFullYear() + 1000);
+ break;
+ }
+ date.setTime(date2.getTime() - timeShift);
+};
+
+SimileAjax.DateTime.getTimeLabel = function(unit, t) {
+ var time = SimileAjax.DateTime;
+ var second = t.getUTCSeconds();
+ var minute = t.getUTCMinutes();
+ var hour = t.getUTCHours();
+ var day = t.getUTCDate();
+ var month = t.getUTCMonth() + 1;
+ var year = t.getUTCFullYear();
+ switch(unit) {
+ case time.SECOND:
+ return hour + ":" + ((minute < 10) ? "0" : "") + minute + ":" + ((second < 10) ? "0" : "") + second;
+ case time.MINUTE:
+ return hour + ":" + ((minute < 10) ? "0" : "") + minute;
+ case time.HOUR:
+ return hour + ":00";
+ case time.DAY:
+ case time.WEEK:
+ case time.MONTH:
+ case time.QUARTER:
+ case time.SEMESTER:
+ return year + "-" + ((month < 10) ? "0" : "") + month + "-" + ((day < 10) ? "0" : "") + day;
+ case time.YEAR:
+ case time.LUSTRUM:
+ case time.DECADE:
+ case time.HALFCENTURY:
+ case time.CENTURY:
+ case time.HALFMILLENNIUM:
+ case time.MILLENNIUM:
+ return year;
+ }
+};
+
+SimileAjax.DateTime.getTimeString = function(unit, t) {
+ var time = SimileAjax.DateTime;
+ switch(unit) {
+ case time.MILLISECOND:
+ case time.SECOND:
+ case time.MINUTE:
+ case time.HOUR:
+ var m = t.getUTCMonth() + 1;
+ var d = t.getUTCDate();
+ var h = t.getUTCHours();
+ var min = t.getUTCMinutes();
+ var s = t.getUTCSeconds();
+ return t.getUTCFullYear() + "-" + ((m < 10) ? "0" : "") + m + "-" + ((d < 10) ? "0" : "") + d + " " + ((h < 10) ? "0" : "") + h + ":" + ((min < 10) ? "0" : "") + min + ":" + ((s < 10) ? "0" : "") + s;
+ case time.DAY:
+ case time.WEEK:
+ case time.MONTH:
+ case time.QUARTER:
+ case time.SEMESTER:
+ var m = t.getUTCMonth() + 1;
+ var d = t.getUTCDate();
+ return t.getUTCFullYear() + "-" + ((m < 10) ? "0" : "") + m + "-" + ((d < 10) ? "0" : "") + d;
+ case time.YEAR:
+ case time.LUSTRUM:
+ case time.DECADE:
+ case time.HALFCENTURY:
+ case time.CENTURY:
+ case time.HALFMILLENNIUM:
+ case time.MILLENNIUM:
+ return t.getUTCFullYear();
+ }
+};
+
+Timeplot.DefaultEventSource.prototype.loadData = function(events) {
+
+ this._events.maxValues = new Array();
+ this._events.removeAll();
+ for (var i = 0; i < events.length; i++) {
+ var event = events[i];
+ var numericEvent = new Timeplot.DefaultEventSource.NumericEvent(event.date, event.value);
+ this._events.add(numericEvent);
+ }
+ this._fire("onAddMany", []);
+
+};
+
+Timeplot._Impl.prototype.resetPlots = function(plotInfos) {
+
+ this._plotInfos = plotInfos;
+ this._painters = {
+ background : [],
+ foreground : []
+ };
+ this._painter = null;
+
+ var timeplot = this;
+ var painter = {
+ onAddMany : function() {
+ timeplot.update();
+ },
+ onClear : function() {
+ timeplot.update();
+ }
+ }
+
+ for ( i = this._plots.length; i > 0; i--) {
+ this._plots[i - 1].opacityPlot.removeChild(this._plots[i - 1]._opacityCanvas);
+ this._plots[i - 1].dispose();
+ if (document.addEventListener) {
+ this._containerDiv.removeEventListener("mousemove", this._plots[i - 1].mousemove, false);
+ this._containerDiv.removeEventListener("mouseover", this._plots[i - 1].mouseover, false);
+ } else if (document.attachEvent) {
+ this._containerDiv.detachEvent("onmousemove", this._plots[i - 1].mousemove);
+ this._containerDiv.detachEvent("onmouseover", this._plots[i - 1].mouseover);
+ }
+ delete this._plots[i - 1];
+ }
+
+ this._plots = [];
+
+ for ( i = 0; i < this._plotInfos.length; i++) {
+ var plot = new Timeplot.Plot(this, this._plotInfos[i]);
+ var dataSource = plot.getDataSource();
+ if (dataSource) {
+ dataSource.addListener(painter);
+ }
+ this.addPainter("background", {
+ context : plot.getTimeGeometry(),
+ action : plot.getTimeGeometry().paint
+ });
+ this.addPainter("background", {
+ context : plot.getValueGeometry(),
+ action : plot.getValueGeometry().paint
+ });
+ this.addPainter("foreground", {
+ context : plot,
+ action : plot.paint
+ });
+ this._plots.push(plot);
+ plot.initialize();
+ }
+
+};
+
+Timeplot.DefaultTimeGeometry.prototype._calculateGrid = function() {
+ var grid = [];
+
+ var time = SimileAjax.DateTime;
+ var u = this._unit;
+ var p = this._period;
+
+ if (p == 0)
+ return grid;
+
+ var periodUnit = -1;
+ do {
+ periodUnit++;
+ } while (time.gregorianUnitLengths[periodUnit] < p);
+
+ periodUnit--;
+
+ var unit;
+ if (periodUnit < time.DAY) {
+ unit = time.HOUR;
+ } else if (periodUnit < time.WEEK) {
+ unit = time.DAY;
+ } else if (periodUnit < time.QUARTER) {
+ unit = time.WEEK;
+ } else if (periodUnit < time.YEAR) {
+ unit = time.MONTH;
+ } else if (periodUnit < time.DECADE) {
+ unit = time.YEAR;
+ } else if (periodUnit < time.CENTURY) {
+ unit = time.DECADE;
+ } else {
+ unit = time.CENTURY;
+ }
+
+ if (unit < this._granularity) {
+ unit = this._granularity;
+ }
+
+ var t = u.cloneValue(this._earliestDate);
+ var timeZone;
+ do {
+ time.roundDownToInterval(t, unit, timeZone, 1, 0);
+ var x = this.toScreen(u.toNumber(t));
+ var l = SimileAjax.DateTime.getTimeLabel(unit, t);
+ if (x > 0) {
+ grid.push({
+ x : x,
+ label : l
+ });
+ }
+ time.incrementByInterval(t, unit, timeZone);
+ } while (t.getTime() < this._latestDate.getTime());
+
+ return grid;
+
+};
+
+//modified function to prevent from drawing left and right axis
+Timeplot.DefaultValueGeometry.prototype.paint = function() {
+ if (this._timeplot) {
+ var ctx = this._canvas.getContext('2d');
+
+ ctx.lineJoin = 'miter';
+
+ // paint grid
+ if (this._gridColor) {
+ var gridGradient = ctx.createLinearGradient(0, 0, 0, this._canvas.height);
+ gridGradient.addColorStop(0, this._gridColor.toHexString());
+ gridGradient.addColorStop(0.3, this._gridColor.toHexString());
+ gridGradient.addColorStop(1, "rgba(255,255,255,0.5)");
+
+ ctx.lineWidth = this._gridLineWidth;
+ ctx.strokeStyle = gridGradient;
+
+ for (var i = 0; i < this._grid.length; i++) {
+ var tick = this._grid[i];
+ var y = Math.floor(tick.y) + 0.5;
+ if ( typeof tick.label != "undefined") {
+ if (this._axisLabelsPlacement == "left") {
+ var div = this._timeplot.putText(this._id + "-" + i, tick.label, "timeplot-grid-label", {
+ left : 4,
+ bottom : y + 2,
+ color : this._gridColor.toHexString(),
+ visibility : "hidden"
+ });
+ this._labels.push(div);
+ } else if (this._axisLabelsPlacement == "right") {
+ var div = this._timeplot.putText(this._id + "-" + i, tick.label, "timeplot-grid-label", {
+ right : 4,
+ bottom : y + 2,
+ color : this._gridColor.toHexString(),
+ visibility : "hidden"
+ });
+ this._labels.push(div);
+ }
+ if (y + div.clientHeight < this._canvas.height + 10) {
+ div.style.visibility = "visible";
+ // avoid the labels that would overflow
+ }
+ }
+
+ // draw grid
+ ctx.beginPath();
+ if (this._gridType == "long" || tick.label == 0) {
+ ctx.moveTo(0, y);
+ ctx.lineTo(this._canvas.width, y);
+ } else if (this._gridType == "short") {
+ if (this._axisLabelsPlacement == "left") {
+ ctx.moveTo(0, y);
+ ctx.lineTo(this._gridShortSize, y);
+ } else if (this._axisLabelsPlacement == "right") {
+ ctx.moveTo(this._canvas.width, y);
+ ctx.lineTo(this._canvas.width - this._gridShortSize, y);
+ }
+ }
+ ctx.stroke();
+ }
+ }
+ }
+};
+
+//modified function to prevent from drawing hidden labels
+Timeplot.DefaultTimeGeometry.prototype.paint = function() {
+ if (this._canvas) {
+ var unit = this._unit;
+ var ctx = this._canvas.getContext('2d');
+
+ var gradient = ctx.createLinearGradient(0, 0, 0, this._canvas.height);
+
+ ctx.strokeStyle = gradient;
+ ctx.lineWidth = this._gridLineWidth;
+ ctx.lineJoin = 'miter';
+
+ // paint grid
+ if (this._gridColor) {
+ gradient.addColorStop(0, this._gridColor.toString());
+ gradient.addColorStop(1, "rgba(255,255,255,0.9)");
+ for (var i = 0; i < this._grid.length; i++) {
+ var tick = this._grid[i];
+ var x = Math.floor(tick.x) + 0.5;
+ if (this._axisLabelsPlacement == "top") {
+ var div = this._timeplot.putText(this._id + "-" + i, tick.label, "timeplot-grid-label", {
+ left : x + 4,
+ top : 2,
+ visibility : "hidden"
+ });
+ this._labels.push(div);
+ } else if (this._axisLabelsPlacement == "bottom") {
+ var div = this._timeplot.putText(this._id + "-" + i, tick.label, "timeplot-grid-label", {
+ left : x + 4,
+ bottom : 2,
+ visibility : "hidden"
+ });
+ this._labels.push(div);
+ }
+ if (!this._hideLabels && x + div.clientWidth < this._canvas.width + 10) {
+ div.style.visibility = "visible";
+ // avoid the labels that would overflow
+ }
+
+ // draw separator
+ ctx.beginPath();
+ ctx.moveTo(x, 0);
+ ctx.lineTo(x, this._canvas.height);
+ ctx.stroke();
+ }
+ }
+ }
+};
+
+Timeplot.Plot.prototype.getSliceNumber = function() {
+ return this._dataSource.getData().times.length;
+};
+
+Timeplot.Plot.prototype.getSliceId = function(time) {
+ var data = this._dataSource.getData();
+ for (var k = 0; k < data.times.length; k++) {
+ if (data.times[k].getTime() == time.getTime()) {
+ return k;
+ }
+ }
+ return null;
+};
+
+Timeplot.Plot.prototype.getSliceTime = function(index) {
+ var data = this._dataSource.getData();
+ if (0 <= index && index < data.times.length) {
+ return data.times[index];
+ }
+ return null;
+};
+
+Timeplot.Plot.prototype.initialize = function() {
+ if (this._dataSource && this._dataSource.getValue) {
+ this._timeFlag = this._timeplot.putDiv("timeflag", "timeplot-timeflag");
+ this._valueFlag = this._timeplot.putDiv(this._id + "valueflag", "timeplot-valueflag");
+ this._pinValueFlag = this._timeplot.putDiv(this._id + "pinvalueflag", "timeplot-valueflag");
+ var pin = document.getElementById(this._timeplot._id + "-" + this._id + "pinvalueflag");
+ if (SimileAjax.Platform.browser.isIE && SimileAjax.Platform.browser.majorVersion < 9) {
+ var cssText = "border: 1px solid " + this._plotInfo.lineColor.toString() + "; background-color: " + this._plotInfo.fillColor.toString() + ";";
+ cssText = cssText.replace(/rgba\((\s*\d{1,3}),(\s*\d{1,3}),(\s*\d{1,3}),(\s*\d{1}|\s*\d{1}\.\d+)\)/g, 'rgb($1,$2,$3)');
+ pin.style.setAttribute("cssText", cssText);
+ } else {
+ pin.style.border = "1px solid " + this._plotInfo.lineColor.toString();
+ pin.style.backgroundColor = this._plotInfo.fillColor.toString();
+ }
+ this._valueFlagLineLeft = this._timeplot.putDiv(this._id + "valueflagLineLeft", "timeplot-valueflag-line");
+ this._valueFlagLineRight = this._timeplot.putDiv(this._id + "valueflagLineRight", "timeplot-valueflag-line");
+ this._pinValueFlagLineLeft = this._timeplot.putDiv(this._id + "pinValueflagLineLeft", "timeplot-valueflag-line");
+ this._pinValueFlagLineRight = this._timeplot.putDiv(this._id + "pinValueflagLineRight", "timeplot-valueflag-line");
+ if (!this._valueFlagLineLeft.firstChild) {
+ this._valueFlagLineLeft.appendChild(SimileAjax.Graphics.createTranslucentImage(Timeplot.urlPrefix + "images/line_left.png"));
+ this._valueFlagLineRight.appendChild(SimileAjax.Graphics.createTranslucentImage(Timeplot.urlPrefix + "images/line_right.png"));
+ }
+ if (!this._pinValueFlagLineLeft.firstChild) {
+ this._pinValueFlagLineLeft.appendChild(SimileAjax.Graphics.createTranslucentImage(GeoTemConfig.path + "plot-line_left.png"));
+ this._pinValueFlagLineRight.appendChild(SimileAjax.Graphics.createTranslucentImage(GeoTemConfig.path + "plot-line_right.png"));
+ }
+ this._valueFlagPole = this._timeplot.putDiv(this._id + "valuepole", "timeplot-valueflag-pole");
+
+ var opacity = this._plotInfo.valuesOpacity;
+
+ SimileAjax.Graphics.setOpacity(this._timeFlag, opacity);
+ SimileAjax.Graphics.setOpacity(this._valueFlag, opacity);
+ SimileAjax.Graphics.setOpacity(this._pinValueFlag, opacity);
+ SimileAjax.Graphics.setOpacity(this._valueFlagLineLeft, opacity);
+ SimileAjax.Graphics.setOpacity(this._valueFlagLineRight, opacity);
+ SimileAjax.Graphics.setOpacity(this._pinValueFlagLineLeft, opacity);
+ SimileAjax.Graphics.setOpacity(this._pinValueFlagLineRight, opacity);
+ SimileAjax.Graphics.setOpacity(this._valueFlagPole, opacity);
+
+ var plot = this;
+
+ var mouseOverHandler = function(elmt, evt, target) {
+ plot._timeFlag.style.visibility = "visible";
+ plot._valueFlag.style.visibility = "visible";
+ plot._pinValueFlag.style.visibility = "visible";
+ plot._valueFlagLineLeft.style.visibility = "visible";
+ plot._valueFlagLineRight.style.visibility = "visible";
+ plot._pinValueFlagLineLeft.style.visibility = "visible";
+ plot._pinValueFlagLineRight.style.visibility = "visible";
+ plot._valueFlagPole.style.visibility = "visible";
+ if (plot._plotInfo.showValues) {
+ plot._valueFlag.style.display = "block";
+ mouseMoveHandler(elmt, evt, target);
+ }
+ }
+ var mouseOutHandler = function(elmt, evt, target) {
+ plot._timeFlag.style.visibility = "hidden";
+ plot._valueFlag.style.visibility = "hidden";
+ plot._pinValueFlag.style.visibility = "hidden";
+ plot._valueFlagLineLeft.style.visibility = "hidden";
+ plot._valueFlagLineRight.style.visibility = "hidden";
+ plot._pinValueFlagLineLeft.style.visibility = "hidden";
+ plot._pinValueFlagLineRight.style.visibility = "hidden";
+ plot._valueFlagPole.style.visibility = "hidden";
+ }
+ var day = 24 * 60 * 60 * 1000;
+ var month = 30 * day;
+
+ var mouseMoveHandler = function(elmt, evt, target) {
+ if ( typeof SimileAjax != "undefined" && plot._plotInfo.showValues) {
+ var c = plot._canvas;
+ var x = Math.round(SimileAjax.DOM.getEventRelativeCoordinates(evt, plot._canvas).x);
+ if (x > c.width)
+ x = c.width;
+ if (isNaN(x) || x < 0)
+ x = 0;
+ var t = plot._timeGeometry.fromScreen(x);
+ if (t == 0) {// something is wrong
+ plot._valueFlag.style.display = "none";
+ return;
+ }
+
+ var v, validTime;
+ if (plot.style == 'bars') {
+ var time1 = plot._dataSource.getClosestValidTime(t);
+ var x1 = plot._timeGeometry.toScreen(time1);
+ var index_x1 = plot.getSliceId(time1);
+ var time2;
+ if (x < x1 && index_x1 > 0 || x >= x1 && index_x1 == plot.getSliceNumber() - 1) {
+ time2 = plot.getSliceTime(index_x1 - 1);
+ } else {
+ time2 = plot.getSliceTime(index_x1 + 1);
+ }
+ var x2 = plot._timeGeometry.toScreen(time2);
+
+ var t1 = new Date(time1);
+ var t2 = new Date(time2);
+ var unit = plot._timeGeometry.extendedDataSource.unit;
+ var l;
+ if (x1 < x2) {
+ l = SimileAjax.DateTime.getTimeLabel(unit, t1) + '-' + SimileAjax.DateTime.getTimeLabel(unit, t2);
+ validTime = time1;
+ } else {
+ l = SimileAjax.DateTime.getTimeLabel(unit, t2) + '-' + SimileAjax.DateTime.getTimeLabel(unit, t1);
+ validTime = time2;
+ }
+ v = plot._dataSource.getValue(validTime);
+ if (plot._plotInfo.roundValues)
+ v = Math.round(v);
+ plot._valueFlag.innerHTML = v;
+ plot._timeFlag.innerHTML = l;
+ x = (x1 + x2 ) / 2;
+ } else if (plot.style == 'graph') {
+ validTime = plot._dataSource.getClosestValidTime(t);
+ x = plot._timeGeometry.toScreen(validTime);
+ v = plot._dataSource.getValue(validTime);
+ if (plot._plotInfo.roundValues)
+ v = Math.round(v);
+ plot._valueFlag.innerHTML = v;
+ var t = new Date(validTime);
+ var unit = plot._timeGeometry.extendedDataSource.unit;
+ var l = SimileAjax.DateTime.getTimeLabel(unit, t);
+ plot._timeFlag.innerHTML = l;
+ }
+
+ var tw = plot._timeFlag.clientWidth;
+ var th = plot._timeFlag.clientHeight;
+ var tdw = Math.round(tw / 2);
+ var vw = plot._valueFlag.clientWidth;
+ var vh = plot._valueFlag.clientHeight;
+ var y = plot._valueGeometry.toScreen(v);
+
+ if (x + tdw > c.width) {
+ var tx = c.width - tdw;
+ } else if (x - tdw < 0) {
+ var tx = tdw;
+ } else {
+ var tx = x;
+ }
+
+ plot._timeplot.placeDiv(plot._valueFlagPole, {
+ left : x,
+ top : 0,
+ height : c.height,
+ display : "block"
+ });
+ plot._timeplot.placeDiv(plot._timeFlag, {
+ left : tx - tdw,
+ top : 0,
+ display : "block"
+ });
+
+ var sliceId = plot.getSliceId(validTime);
+ var pvw, pvh = 0, pinY;
+ if (plot.pins[sliceId].count > 0) {
+ plot._pinValueFlag.innerHTML = plot.pins[sliceId].count;
+ pvw = plot._pinValueFlag.clientWidth;
+ pvh = plot._pinValueFlag.clientHeight;
+ pinY = plot.pins[sliceId].height;
+ }
+ var rightOverflow = x + vw + 14 > c.width;
+ var leftOverflow = false;
+ if (plot.pins[sliceId].count > 0) {
+ if (x - pvw - 14 < 0) {
+ leftOverflow = true;
+ }
+ }
+ var shiftV, shiftP;
+ if (plot.pins[sliceId].count > 0) {
+ var cut = y - pinY < vh / 2 + pvh / 2;
+ if ((leftOverflow || rightOverflow ) && cut) {
+ shiftV = 0;
+ shiftP = pvh;
+ } else {
+ shiftV = vh / 2;
+ shiftP = pvh / 2;
+ }
+ } else {
+ shiftV = vh / 2;
+ }
+
+ if (x + vw + 14 > c.width && y + vh / 2 + 4 > c.height) {
+ plot._valueFlagLineLeft.style.display = "none";
+ plot._timeplot.placeDiv(plot._valueFlagLineRight, {
+ left : x - 14,
+ bottom : y - 14,
+ display : "block"
+ });
+ plot._timeplot.placeDiv(plot._valueFlag, {
+ left : x - vw - 13,
+ bottom : y - 13 - shiftV,
+ display : "block"
+ });
+ } else if (x + vw + 14 > c.width && y + vh / 2 + 4 < c.height) {
+ plot._valueFlagLineRight.style.display = "none";
+ plot._timeplot.placeDiv(plot._valueFlagLineLeft, {
+ left : x - 14,
+ bottom : y,
+ display : "block"
+ });
+ plot._timeplot.placeDiv(plot._valueFlag, {
+ left : x - vw - 13,
+ bottom : y + 13 - shiftV,
+ display : "block"
+ });
+ } else if (x + vw + 14 < c.width && y + vh / 2 + 4 > c.height) {
+ plot._valueFlagLineRight.style.display = "none";
+ plot._timeplot.placeDiv(plot._valueFlagLineLeft, {
+ left : x,
+ bottom : y - 13,
+ display : "block"
+ });
+ plot._timeplot.placeDiv(plot._valueFlag, {
+ left : x + 13,
+ bottom : y - 13 - shiftV,
+ display : "block"
+ });
+ } else {
+ plot._valueFlagLineLeft.style.display = "none";
+ plot._timeplot.placeDiv(plot._valueFlagLineRight, {
+ left : x,
+ bottom : y,
+ display : "block"
+ });
+ plot._timeplot.placeDiv(plot._valueFlag, {
+ left : x + 13,
+ bottom : y + 13 - shiftV,
+ display : "block"
+ });
+ }
+
+ if (plot.pins[sliceId].count > 0) {
+ if (x - pvw - 14 < 0 && pinY + pvh + 4 > c.height) {
+ plot._pinValueFlagLineLeft.style.display = "none";
+ plot._timeplot.placeDiv(plot._pinValueFlagLineRight, {
+ left : x,
+ bottom : pinY,
+ display : "block"
+ });
+ plot._timeplot.placeDiv(plot._pinValueFlag, {
+ left : x + 13,
+ bottom : pinY - 13 - shiftP,
+ display : "block"
+ });
+ } else if (x - pvw - 14 < 0 && pinY + pvh + 4 < c.height) {
+ plot._pinValueFlagLineLeft.style.display = "none";
+ plot._timeplot.placeDiv(plot._pinValueFlagLineRight, {
+ left : x,
+ bottom : pinY,
+ display : "block"
+ });
+ plot._timeplot.placeDiv(plot._pinValueFlag, {
+ left : x + 13,
+ bottom : pinY + 13 - shiftP,
+ display : "block"
+ });
+ } else if (x - pvw - 14 >= 0 && pinY + pvh + 4 > c.height) {
+ plot._pinValueFlagLineLeft.style.display = "none";
+ plot._timeplot.placeDiv(plot._pinValueFlagLineRight, {
+ left : x - 13,
+ bottom : pinY - 13,
+ display : "block"
+ });
+ plot._timeplot.placeDiv(plot._pinValueFlag, {
+ left : x - 15 - pvw,
+ bottom : pinY - 13 - shiftP,
+ display : "block"
+ });
+ } else {
+ plot._pinValueFlagLineRight.style.display = "none";
+ plot._timeplot.placeDiv(plot._pinValueFlagLineLeft, {
+ left : x - 14,
+ bottom : pinY,
+ display : "block"
+ });
+ plot._timeplot.placeDiv(plot._pinValueFlag, {
+ left : x - pvw - 15,
+ bottom : pinY + 13 - shiftP,
+ display : "block"
+ });
+ }
+ } else {
+ plot._pinValueFlagLineLeft.style.display = "none";
+ plot._pinValueFlagLineRight.style.display = "none";
+ plot._pinValueFlag.style.display = "none";
+ }
+
+ }
+
+ }
+ var timeplotElement = this._timeplot.getElement();
+ this.mouseover = SimileAjax.DOM.registerPlotEvent(timeplotElement, "mouseover", mouseOverHandler);
+ this.mouseout = SimileAjax.DOM.registerPlotEvent(timeplotElement, "mouseout", mouseOutHandler);
+ this.mousemove = SimileAjax.DOM.registerPlotEvent(timeplotElement, "mousemove", mouseMoveHandler);
+
+ this.opacityPlot = this._timeplot.putDiv("opacityPlot" + this._timeplot._plots.length, "opacityPlot");
+ SimileAjax.Graphics.setOpacity(this.opacityPlot, 50);
+ // this.opacityPlot.style.zIndex = this._timeplot._plots.length;
+ this._timeplot.placeDiv(this.opacityPlot, {
+ left : 0,
+ bottom : 0,
+ width : this._canvas.width,
+ height : this._canvas.height
+ });
+ this._opacityCanvas = document.createElement("canvas");
+ this.opacityPlot.appendChild(this._opacityCanvas);
+ if (!this._opacityCanvas.getContext && G_vmlCanvasManager)
+ this._opacityCanvas = G_vmlCanvasManager.initElement(this._opacityCanvas);
+ this._opacityCanvas.width = this._canvas.width;
+ this._opacityCanvas.height = this._canvas.height;
+ this._opacityCanvas.style.position = 'absolute';
+ this._opacityCanvas.style.left = '0px';
+ this.opacityPlot.style.visibility = "hidden";
+
+ }
+};
+
+SimileAjax.DOM.registerPlotEvent = function(elmt, eventName, handler) {
+ var handler2 = function(evt) {
+ evt = (evt) ? evt : ((event) ? event : null);
+ if (evt) {
+ var target = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
+ if (target) {
+ target = (target.nodeType == 1 || target.nodeType == 9) ? target : target.parentNode;
+ }
+
+ return handler(elmt, evt, target);
+ }
+ return true;
+ }
+ if (SimileAjax.Platform.browser.isIE) {
+ elmt.attachEvent("on" + eventName, handler2);
+ } else {
+ elmt.addEventListener(eventName, handler2, false);
+ }
+
+ return handler2;
+};
+
+SimileAjax.DOM.getEventRelativeCoordinates = function(evt, elmt) {
+ if (SimileAjax.Platform.browser.isIE) {
+ var coords = SimileAjax.DOM.getPageCoordinates(elmt);
+ return {
+ x : evt.clientX - coords.left,
+ y : evt.clientY - coords.top
+ };
+ } else {
+ var coords = SimileAjax.DOM.getPageCoordinates(elmt);
+
+ if ((evt.type == "DOMMouseScroll") && SimileAjax.Platform.browser.isFirefox && (SimileAjax.Platform.browser.majorVersion == 2)) {
+ // Due to: https://bugzilla.mozilla.org/show_bug.cgi?id=352179
+
+ return {
+ x : evt.screenX - coords.left,
+ y : evt.screenY - coords.top
+ };
+ } else {
+ return {
+ x : evt.pageX - coords.left,
+ y : evt.pageY - coords.top
+ };
+ }
+ }
+};
+
+SimileAjax.Graphics.setOpacity = function(elmt, opacity) {
+ if (SimileAjax.Platform.browser.isIE) {
+ elmt.style.filter = "alpha(opacity = " + opacity + ")";
+ } else {
+ var o = (opacity / 100).toString();
+ elmt.style.opacity = o;
+ elmt.style.MozOpacity = o;
+ }
+};
+
+Timeplot.Plot.prototype.fullOpacityPlot = function(left, right, lp, rp, c) {
+
+ var ctx = this._opacityCanvas.getContext('2d');
+
+ ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
+ ctx.lineWidth = this._plotInfo.lineWidth;
+ ctx.lineJoin = 'miter';
+
+ var h = this._canvas.height;
+ ctx.fillStyle = this._plotInfo.lineColor.toString();
+
+ var data = this._dataSource.getData();
+ var times = data.times;
+ var values = data.values;
+
+ var first = true;
+ ctx.beginPath();
+ ctx.fillStyle = this._plotInfo.lineColor.toString();
+ var lastX = 0, lastY = 0;
+ for (var t = 0; t < times.length; t++) {
+ if (!(times[t].getTime() < left.getTime() || times[t].getTime() > right.getTime())) {
+ var x = this._timeGeometry.toScreen(times[t]);
+ var y = this._valueGeometry.toScreen(values[t]);
+ if (first) {
+ ctx.moveTo(x, h);
+ first = false;
+ }
+ if (this.style == 'bars') {
+ ctx.lineTo(x, h - lastY);
+ }
+ ctx.lineTo(x, h - y);
+ if (times[t].getTime() == right.getTime() || t == times.length - 1)
+ ctx.lineTo(x, h);
+ lastX = x;
+ lastY = y;
+ }
+ }
+ ctx.fill();
+
+};
+
+Timeplot._Impl.prototype.regularGrid = function() {
+
+ var canvas = this.getCanvas();
+ var ctx = canvas.getContext('2d');
+ var gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
+ gradient.addColorStop(0, "rgb(0,0,0)");
+ gradient.addColorStop(1, "rgba(255,255,255,0.9)");
+ ctx.strokeStyle = gradient;
+ ctx.lineWidth = 0.5;
+ ctx.lineJoin = 'miter';
+
+ var xDist = canvas.width / 9;
+ var positions = [];
+ for (var i = 1; i < 9; i++) {
+ var x = i * xDist;
+ ctx.beginPath();
+ ctx.moveTo(x, 0);
+ ctx.lineTo(x, canvas.height);
+
+ ctx.stroke();
+ positions.push({
+ label : '',
+ x : x
+ });
+ }
+ return positions;
+
+};
+
+Timeplot.Plot.prototype._plot = function() {
+ var ctx = this._canvas.getContext('2d');
+ var data = this._dataSource.getData();
+ if (data) {
+ var times = data.times;
+ var values = data.values;
+ var T = times.length;
+ ctx.moveTo(0, 0);
+ var lastX = 0, lastY = 0;
+ for (var t = 0; t < T; t++) {
+ var x = this._timeGeometry.toScreen(times[t]);
+ var y = this._valueGeometry.toScreen(values[t]);
+ if (t > 0 && (values[t - 1] > 0 || values[t] > 0 )) {
+ if (this.style == 'graph') {
+ ctx.lineTo(x, y);
+ }
+ if (this.style == 'bars') {
+ if (values[t - 1] > 0) {
+ ctx.lineTo(x, lastY);
+ } else {
+ ctx.moveTo(x, lastY);
+ }
+ ctx.lineTo(x, y);
+ }
+ } else {
+ ctx.moveTo(x, y);
+ }
+ lastX = x;
+ lastY = y;
+ }
+ }
+};
+
+SimileAjax.DOM.registerEvent = function(elmt, eventName, handler) {
+ var handler2 = function(evt) {
+ evt = (evt) ? evt : ((event) ? event : null);
+ if (evt) {
+ var target = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
+ if (target) {
+ target = (target.nodeType == 1 || target.nodeType == 9) ? target : target.parentNode;
+ }
+
+ return handler(elmt, evt, target);
+ }
+ return true;
+ }
+ if (SimileAjax.Platform.browser.isIE) {
+ elmt.attachEvent("on" + eventName, handler2);
+ } else {
+ if (eventName == "mousewheel") {
+ eventName = "DOMMouseScroll";
+ }
+ elmt.addEventListener(eventName, handler2, false);
+ }
+};
+
+Timeplot._Impl.prototype._setUpright = function(ctx, canvas) {
+ // excanvas+IE requires this to be done only once, ever; actual canvas
+ // implementations reset and require this for each call to re-layout
+ // modified: problem does not exist for IE9
+ if (!SimileAjax.Platform.browser.isIE)
+ this._upright = false;
+ else if (SimileAjax.Platform.browser.majorVersion > 8)
+ this._upright = false;
+ if (!this._upright) {
+ this._upright = true;
+ ctx.translate(0, canvas.height);
+ ctx.scale(1, -1);
+ }
+};
+
+Timeplot._Impl.prototype._resizeCanvas = function() {
+ var canvas = this.getCanvas();
+ if (canvas.firstChild) {
+ canvas.firstChild.style.width = canvas.clientWidth + 'px';
+ canvas.firstChild.style.height = canvas.clientHeight + 'px';
+ }
+ for (var i = 0; i < this._plots.length; i++) {
+ var opacityCanvas = this._plots[i]._opacityCanvas;
+ if (opacityCanvas.firstChild) {
+ opacityCanvas.firstChild.style.width = opacityCanvas.clientWidth + 'px';
+ opacityCanvas.firstChild.style.height = opacityCanvas.clientHeight + 'px';
+ }
+ }
+};
+
+Timeplot._Impl.prototype.getWidth = function() {
+ var canvas = this.getCanvas();
+ if ( typeof canvas.width != 'undefined' && this._containerDiv.clientWidth == 0) {
+ return canvas.width;
+ }
+ return this._containerDiv.clientWidth;
+};
+
+Timeplot._Impl.prototype.getHeight = function() {
+ var canvas = this.getCanvas();
+ if ( typeof canvas.height != 'undefined' && this._containerDiv.clientHeight == 0) {
+ return canvas.height;
+ }
+ return this._containerDiv.clientHeight;
+};
+
+Timeplot._Impl.prototype._prepareCanvas = function() {
+ var canvas = this.getCanvas();
+
+ // using jQuery. note we calculate the average padding; if your
+ // padding settings are not symmetrical, the labels will be off
+ // since they expect to be centered on the canvas.
+ var con = SimileAjax.jQuery(this._containerDiv);
+ this._paddingX = (parseInt(con.css('paddingLeft')) + parseInt(con.css('paddingRight'))) / 2;
+ this._paddingY = (parseInt(con.css('paddingTop')) + parseInt(con.css('paddingBottom'))) / 2;
+
+ if (isNaN(this._paddingX)) {
+ this._paddingX = 0;
+ }
+ if (isNaN(this._paddingY)) {
+ this._paddingY = 0;
+ }
+
+ canvas.width = this.getWidth() - (this._paddingX * 2);
+ canvas.height = this.getHeight() - (this._paddingY * 2);
+
+ var ctx = canvas.getContext('2d');
+ this._setUpright(ctx, canvas);
+ ctx.globalCompositeOperation = 'source-over';
+};
+/*
+
+ OpenLayers.js -- OpenLayers Map Viewer Library
+
+ Copyright (c) 2006-2012 by OpenLayers Contributors
+ Published under the 2-clause BSD license.
+ See http://openlayers.org/dev/license.txt for the full text of the license, and http://openlayers.org/dev/authors.txt for full list of contributors.
+
+ Includes compressed code under the following licenses:
+
+ (For uncompressed versions of the code used, please see the
+ OpenLayers Github repository: )
+
+*/
+
+/**
+ * Contains XMLHttpRequest.js
+ * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+/**
+ * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
+ * Copyright (c) 2006, Yahoo! Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use of this software in source and binary forms, with or
+ * without modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Yahoo! Inc. nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission of Yahoo! Inc.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+var OpenLayers={VERSION_NUMBER:"Release 2.12",singleFile:!0,_getScriptLocation:function(){for(var a=/(^|(.*?\/))(OpenLayers[^\/]*?\.js)(\?|$)/,b=document.getElementsByTagName("script"),c,d="",e=0,f=b.length;ethis.right)this.right=b.right;if(null==this.top||b.top>this.top)this.top=b.top}}},containsLonLat:function(a,b){"boolean"===typeof b&&(b={inclusive:b});var b=b||{},c=this.contains(a.lon,a.lat,b.inclusive),d=b.worldBounds;d&&!c&&(c=d.getWidth(),d=Math.round((a.lon-(d.left+d.right)/2)/c),c=this.containsLonLat({lon:a.lon-d*c,lat:a.lat},{inclusive:b.inclusive}));return c},containsPixel:function(a,b){return this.contains(a.x,
+a.y,b)},contains:function(a,b,c){null==c&&(c=!0);if(null==a||null==b)return!1;var a=OpenLayers.Util.toFloat(a),b=OpenLayers.Util.toFloat(b),d=!1;return d=c?a>=this.left&&a<=this.right&&b>=this.bottom&&b<=this.top:a>this.left&&athis.bottom&&b=c.bottom&&a.top<=c.top||c.top>a.bottom&&c.top=c.left&&a.left<=c.right||c.left>=a.left&&c.left<=a.right,f=a.right>=c.left&&a.right<=c.right||c.right>=a.left&&c.right<=a.right,d=(a.bottom>=c.bottom&&a.bottom<=c.top||c.bottom>=a.bottom&&c.bottom<=a.top||d)&&(e||f);if(b.worldBounds&&!d){var g=b.worldBounds,e=g.getWidth(),f=!g.containsBounds(c),g=!g.containsBounds(a);f&&!g?(a=a.add(-e,0),d=c.intersectsBounds(a,
+{inclusive:b.inclusive})):g&&!f&&(c=c.add(-e,0),d=a.intersectsBounds(c,{inclusive:b.inclusive}))}return d},containsBounds:function(a,b,c){null==b&&(b=!1);null==c&&(c=!0);var d=this.contains(a.left,a.bottom,c),e=this.contains(a.right,a.bottom,c),f=this.contains(a.left,a.top,c),a=this.contains(a.right,a.top,c);return b?d||e||f||a:d&&e&&f&&a},determineQuadrant:function(a){var b="",c=this.getCenterLonLat(),b=b+(a.lat=a.right&&e.right>a.right;)e=e.add(-f,0);c=e.left+c;ca.left&&e.right-d>a.right)&&(e=e.add(-f,0))}return e},CLASS_NAME:"OpenLayers.Bounds"});OpenLayers.Bounds.fromString=function(a,b){var c=a.split(",");return OpenLayers.Bounds.fromArray(c,b)};OpenLayers.Bounds.fromArray=function(a,b){return!0===b?new OpenLayers.Bounds(a[1],a[0],a[3],a[2]):new OpenLayers.Bounds(a[0],a[1],a[2],a[3])};
+OpenLayers.Bounds.fromSize=function(a){return new OpenLayers.Bounds(0,a.h,a.w,0)};OpenLayers.Bounds.oppositeQuadrant=function(a){var b;b=""+("t"==a.charAt(0)?"b":"t");return b+="l"==a.charAt(1)?"r":"l"};OpenLayers.Element={visible:function(a){return"none"!=OpenLayers.Util.getElement(a).style.display},toggle:function(){for(var a=0,b=arguments.length;aa.right;)b.lon-=a.getWidth()}return b},CLASS_NAME:"OpenLayers.LonLat"});
+OpenLayers.LonLat.fromString=function(a){a=a.split(",");return new OpenLayers.LonLat(a[0],a[1])};OpenLayers.LonLat.fromArray=function(a){var b=OpenLayers.Util.isArray(a);return new OpenLayers.LonLat(b&&a[0],b&&a[1])};OpenLayers.Pixel=OpenLayers.Class({x:0,y:0,initialize:function(a,b){this.x=parseFloat(a);this.y=parseFloat(b)},toString:function(){return"x="+this.x+",y="+this.y},clone:function(){return new OpenLayers.Pixel(this.x,this.y)},equals:function(a){var b=!1;null!=a&&(b=this.x==a.x&&this.y==a.y||isNaN(this.x)&&isNaN(this.y)&&isNaN(a.x)&&isNaN(a.y));return b},distanceTo:function(a){return Math.sqrt(Math.pow(this.x-a.x,2)+Math.pow(this.y-a.y,2))},add:function(a,b){if(null==a||null==b)throw new TypeError("Pixel.add cannot receive null values");
+return new OpenLayers.Pixel(this.x+a,this.y+b)},offset:function(a){var b=this.clone();a&&(b=this.add(a.x,a.y));return b},CLASS_NAME:"OpenLayers.Pixel"});OpenLayers.Size=OpenLayers.Class({w:0,h:0,initialize:function(a,b){this.w=parseFloat(a);this.h=parseFloat(b)},toString:function(){return"w="+this.w+",h="+this.h},clone:function(){return new OpenLayers.Size(this.w,this.h)},equals:function(a){var b=!1;null!=a&&(b=this.w==a.w&&this.h==a.h||isNaN(this.w)&&isNaN(this.h)&&isNaN(a.w)&&isNaN(a.h));return b},CLASS_NAME:"OpenLayers.Size"});OpenLayers.Console={log:function(){},debug:function(){},info:function(){},warn:function(){},error:function(){},userError:function(a){alert(a)},assert:function(){},dir:function(){},dirxml:function(){},trace:function(){},group:function(){},groupEnd:function(){},time:function(){},timeEnd:function(){},profile:function(){},profileEnd:function(){},count:function(){},CLASS_NAME:"OpenLayers.Console"};
+(function(){for(var a=document.getElementsByTagName("script"),b=0,c=a.length;b=0;c--)a[c]==b&&a.splice(c,1);return a};OpenLayers.Util.indexOf=function(a,b){if(typeof a.indexOf=="function")return a.indexOf(b);for(var c=0,d=a.length;c=0&&parseFloat(h)<1){a.style.filter="alpha(opacity="+h*100+")";a.style.opacity=h}else if(parseFloat(h)==1){a.style.filter="";a.style.opacity=""}};
+OpenLayers.Util.createDiv=function(a,b,c,d,e,f,g,h){var i=document.createElement("div");if(d)i.style.backgroundImage="url("+d+")";a||(a=OpenLayers.Util.createUniqueID("OpenLayersDiv"));e||(e="absolute");OpenLayers.Util.modifyDOMElement(i,a,b,c,e,f,g,h);return i};
+OpenLayers.Util.createImage=function(a,b,c,d,e,f,g,h){var i=document.createElement("img");a||(a=OpenLayers.Util.createUniqueID("OpenLayersDiv"));e||(e="relative");OpenLayers.Util.modifyDOMElement(i,a,b,c,e,f,null,g);if(h){i.style.display="none";b=function(){i.style.display="";OpenLayers.Event.stopObservingElement(i)};OpenLayers.Event.observe(i,"load",b);OpenLayers.Event.observe(i,"error",b)}i.style.alt=a;i.galleryImg="no";if(d)i.src=d;return i};OpenLayers.IMAGE_RELOAD_ATTEMPTS=0;
+OpenLayers.Util.alphaHackNeeded=null;OpenLayers.Util.alphaHack=function(){if(OpenLayers.Util.alphaHackNeeded==null){var a=navigator.appVersion.split("MSIE"),a=parseFloat(a[1]),b=false;try{b=!!document.body.filters}catch(c){}OpenLayers.Util.alphaHackNeeded=b&&a>=5.5&&a<7}return OpenLayers.Util.alphaHackNeeded};
+OpenLayers.Util.modifyAlphaImageDiv=function(a,b,c,d,e,f,g,h,i){OpenLayers.Util.modifyDOMElement(a,b,c,d,f,null,null,i);b=a.childNodes[0];if(e)b.src=e;OpenLayers.Util.modifyDOMElement(b,a.id+"_innerImage",null,d,"relative",g);if(OpenLayers.Util.alphaHack()){if(a.style.display!="none")a.style.display="inline-block";h==null&&(h="scale");a.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+b.src+"', sizingMethod='"+h+"')";if(parseFloat(a.style.opacity)>=0&&parseFloat(a.style.opacity)<
+1)a.style.filter=a.style.filter+(" alpha(opacity="+a.style.opacity*100+")");b.style.filter="alpha(opacity=0)"}};OpenLayers.Util.createAlphaImageDiv=function(a,b,c,d,e,f,g,h,i){var j=OpenLayers.Util.createDiv(),i=OpenLayers.Util.createImage(null,null,null,null,null,null,null,i);i.className="olAlphaImg";j.appendChild(i);OpenLayers.Util.modifyAlphaImageDiv(j,a,b,c,d,e,f,g,h);return j};OpenLayers.Util.upperCaseObject=function(a){var b={},c;for(c in a)b[c.toUpperCase()]=a[c];return b};
+OpenLayers.Util.applyDefaults=function(a,b){var a=a||{},c=typeof window.Event=="function"&&b instanceof window.Event,d;for(d in b)if(a[d]===void 0||!c&&b.hasOwnProperty&&b.hasOwnProperty(d)&&!a.hasOwnProperty(d))a[d]=b[d];if(!c&&b&&b.hasOwnProperty&&b.hasOwnProperty("toString")&&!a.hasOwnProperty("toString"))a.toString=b.toString;return a};
+OpenLayers.Util.getParameterString=function(a){var b=[],c;for(c in a){var d=a[c];if(d!=null&&typeof d!="function"){if(typeof d=="object"&&d.constructor==Array){for(var e=[],f,g=0,h=d.length;g1.0E-12&&--m>0;){var n=Math.sin(k),o=Math.cos(k),p=Math.sqrt(h*n*h*n+(g*j-i*h*o)*(g*j-i*h*o));if(p==0)return 0;var o=i*j+g*h*o,q=Math.atan2(p,o),r=Math.asin(g*
+h*n/p),s=Math.cos(r)*Math.cos(r),n=o-2*i*j/s,t=c/16*s*(4+c*(4-3*s)),l=k,k=f+(1-t)*c*Math.sin(r)*(q+t*p*(n+t*o*(-1+2*n*n)))}if(m==0)return NaN;d=s*(d*d-e*e)/(e*e);c=d/1024*(256+d*(-128+d*(74-47*d)));return(e*(1+d/16384*(4096+d*(-768+d*(320-175*d))))*(q-c*p*(n+c/4*(o*(-1+2*n*n)-c/6*n*(-3+4*p*p)*(-3+4*n*n))))).toFixed(3)/1E3};
+OpenLayers.Util.destinationVincenty=function(a,b,c){for(var d=OpenLayers.Util,e=d.VincentyConstants,f=e.a,g=e.b,h=e.f,e=a.lon,a=a.lat,i=d.rad(b),b=Math.sin(i),i=Math.cos(i),a=(1-h)*Math.tan(d.rad(a)),j=1/Math.sqrt(1+a*a),k=a*j,l=Math.atan2(a,i),a=j*b,m=1-a*a,f=m*(f*f-g*g)/(g*g),n=1+f/16384*(4096+f*(-768+f*(320-175*f))),o=f/1024*(256+f*(-128+f*(74-47*f))),f=c/(g*n),p=2*Math.PI;Math.abs(f-p)>1.0E-12;)var q=Math.cos(2*l+f),r=Math.sin(f),s=Math.cos(f),t=o*r*(q+o/4*(s*(-1+2*q*q)-o/6*q*(-3+4*r*r)*(-3+4*
+q*q))),p=f,f=c/(g*n)+t;c=k*r-j*s*i;g=Math.atan2(k*s+j*r*i,(1-h)*Math.sqrt(a*a+c*c));b=Math.atan2(r*b,j*s-k*r*i);i=h/16*m*(4+h*(4-3*m));q=b-(1-i)*h*a*(f+i*r*(q+i*s*(-1+2*q*q)));Math.atan2(a,-c);return new OpenLayers.LonLat(e+d.deg(q),d.deg(g))};
+OpenLayers.Util.getParameters=function(a){var a=a===null||a===void 0?window.location.href:a,b="";if(OpenLayers.String.contains(a,"?"))var b=a.indexOf("?")+1,c=OpenLayers.String.contains(a,"#")?a.indexOf("#"):a.length,b=a.substring(b,c);for(var a={},b=b.split(/[&;]/),c=0,d=b.length;c1?1/a:a};OpenLayers.Util.getResolutionFromScale=function(a,b){var c;if(a){b==null&&(b="degrees");c=1/(OpenLayers.Util.normalizeScale(a)*OpenLayers.INCHES_PER_UNIT[b]*OpenLayers.DOTS_PER_INCH)}return c};
+OpenLayers.Util.getScaleFromResolution=function(a,b){b==null&&(b="degrees");return a*OpenLayers.INCHES_PER_UNIT[b]*OpenLayers.DOTS_PER_INCH};
+OpenLayers.Util.pagePosition=function(a){var b=[0,0],c=OpenLayers.Util.getViewportElement();if(!a||a==window||a==c)return b;var d=OpenLayers.IS_GECKO&&document.getBoxObjectFor&&OpenLayers.Element.getStyle(a,"position")=="absolute"&&(a.style.top==""||a.style.left==""),e=null;if(a.getBoundingClientRect){a=a.getBoundingClientRect();e=c.scrollTop;b[0]=a.left+c.scrollLeft;b[1]=a.top+e}else if(document.getBoxObjectFor&&!d){a=document.getBoxObjectFor(a);c=document.getBoxObjectFor(c);b[0]=a.screenX-c.screenX;
+b[1]=a.screenY-c.screenY}else{b[0]=a.offsetLeft;b[1]=a.offsetTop;e=a.offsetParent;if(e!=a)for(;e;){b[0]=b[0]+e.offsetLeft;b[1]=b[1]+e.offsetTop;e=e.offsetParent}c=OpenLayers.BROWSER_NAME;if(c=="opera"||c=="safari"&&OpenLayers.Element.getStyle(a,"position")=="absolute")b[1]=b[1]-document.body.offsetTop;for(e=a.offsetParent;e&&e!=document.body;){b[0]=b[0]-e.scrollLeft;if(c!="opera"||e.tagName!="TR")b[1]=b[1]-e.scrollTop;e=e.offsetParent}}return b};
+OpenLayers.Util.getViewportElement=function(){var a=arguments.callee.viewportElement;if(a==void 0){a=OpenLayers.BROWSER_NAME=="msie"&&document.compatMode!="CSS1Compat"?document.body:document.documentElement;arguments.callee.viewportElement=a}return a};
+OpenLayers.Util.isEquivalentUrl=function(a,b,c){c=c||{};OpenLayers.Util.applyDefaults(c,{ignoreCase:true,ignorePort80:true,ignoreHash:true});var a=OpenLayers.Util.createUrlObject(a,c),b=OpenLayers.Util.createUrlObject(b,c),d;for(d in a)if(d!=="args"&&a[d]!=b[d])return false;for(d in a.args){if(a.args[d]!=b.args[d])return false;delete b.args[d]}for(d in b.args)return false;return true};
+OpenLayers.Util.createUrlObject=function(a,b){b=b||{};if(!/^\w+:\/\//.test(a)){var c=window.location,d=c.port?":"+c.port:"",d=c.protocol+"//"+c.host.split(":").shift()+d;if(a.indexOf("/")===0)a=d+a;else{c=c.pathname.split("/");c.pop();a=d+c.join("/")+"/"+a}}b.ignoreCase&&(a=a.toLowerCase());c=document.createElement("a");c.href=a;d={};d.host=c.host.split(":").shift();d.protocol=c.protocol;d.port=b.ignorePort80?c.port=="80"||c.port=="0"?"":c.port:c.port==""||c.port=="0"?"80":c.port;d.hash=b.ignoreHash||
+c.hash==="#"?"":c.hash;var e=c.search;if(!e){e=a.indexOf("?");e=e!=-1?a.substr(e):""}d.args=OpenLayers.Util.getParameters(e);d.pathname=c.pathname.charAt(0)=="/"?c.pathname:"/"+c.pathname;return d};OpenLayers.Util.removeTail=function(a){var b=null,b=a.indexOf("?"),c=a.indexOf("#");return b=b==-1?c!=-1?a.substr(0,c):a:c!=-1?a.substr(0,Math.min(b,c)):a.substr(0,b)};OpenLayers.IS_GECKO=function(){var a=navigator.userAgent.toLowerCase();return a.indexOf("webkit")==-1&&a.indexOf("gecko")!=-1}();
+OpenLayers.CANVAS_SUPPORTED=function(){var a=document.createElement("canvas");return!(!a.getContext||!a.getContext("2d"))}();OpenLayers.BROWSER_NAME=function(){var a="",b=navigator.userAgent.toLowerCase();b.indexOf("opera")!=-1?a="opera":b.indexOf("msie")!=-1?a="msie":b.indexOf("safari")!=-1?a="safari":b.indexOf("mozilla")!=-1&&(a=b.indexOf("firefox")!=-1?"firefox":"mozilla");return a}();OpenLayers.Util.getBrowserName=function(){return OpenLayers.BROWSER_NAME};
+OpenLayers.Util.getRenderedDimensions=function(a,b,c){var d,e,f=document.createElement("div");f.style.visibility="hidden";for(var g=c&&c.containerElement?c.containerElement:document.body,h=false,i=null,j=g;j&&j.tagName.toLowerCase()!="body";){var k=OpenLayers.Element.getStyle(j,"position");if(k=="absolute"){h=true;break}else if(k&&k!="static")break;j=j.parentNode}if(h&&(g.clientHeight===0||g.clientWidth===0)){i=document.createElement("div");i.style.visibility="hidden";i.style.position="absolute";
+i.style.overflow="visible";i.style.width=document.body.clientWidth+"px";i.style.height=document.body.clientHeight+"px";i.appendChild(f)}f.style.position="absolute";if(b)if(b.w){d=b.w;f.style.width=d+"px"}else if(b.h){e=b.h;f.style.height=e+"px"}if(c&&c.displayClass)f.className=c.displayClass;b=document.createElement("div");b.innerHTML=a;b.style.overflow="visible";if(b.childNodes){a=0;for(c=b.childNodes.length;a=60){f=f-60;d=d+1;if(d>=60){d=d-60;e=e+1}}e<10&&(e="0"+e);e=e+"\u00b0";if(c.indexOf("dm")>=0){d<10&&(d="0"+d);e=e+(d+"'");if(c.indexOf("dms")>=0){f<10&&(f="0"+f);e=e+(f+'"')}}return e=b=="lon"?e+(a<0?OpenLayers.i18n("W"):OpenLayers.i18n("E")):e+(a<0?OpenLayers.i18n("S"):OpenLayers.i18n("N"))};OpenLayers.Format=OpenLayers.Class({options:null,externalProjection:null,internalProjection:null,data:null,keepData:!1,initialize:function(a){OpenLayers.Util.extend(this,a);this.options=a},destroy:function(){},read:function(){throw Error("Read not implemented.");},write:function(){throw Error("Write not implemented.");},CLASS_NAME:"OpenLayers.Format"});OpenLayers.Format.CSWGetRecords=function(a){var a=OpenLayers.Util.applyDefaults(a,OpenLayers.Format.CSWGetRecords.DEFAULTS),b=OpenLayers.Format.CSWGetRecords["v"+a.version.replace(/\./g,"_")];if(!b)throw"Unsupported CSWGetRecords version: "+a.version;return new b(a)};OpenLayers.Format.CSWGetRecords.DEFAULTS={version:"2.0.2"};OpenLayers.Control=OpenLayers.Class({id:null,map:null,div:null,type:null,allowSelection:!1,displayClass:"",title:"",autoActivate:!1,active:null,handler:null,eventListeners:null,events:null,initialize:function(a){this.displayClass=this.CLASS_NAME.replace("OpenLayers.","ol").replace(/\./g,"");OpenLayers.Util.extend(this,a);this.events=new OpenLayers.Events(this);if(this.eventListeners instanceof Object)this.events.on(this.eventListeners);null==this.id&&(this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+
+"_"))},destroy:function(){this.events&&(this.eventListeners&&this.events.un(this.eventListeners),this.events.destroy(),this.events=null);this.eventListeners=null;this.handler&&(this.handler.destroy(),this.handler=null);if(this.handlers){for(var a in this.handlers)this.handlers.hasOwnProperty(a)&&"function"==typeof this.handlers[a].destroy&&this.handlers[a].destroy();this.handlers=null}this.map&&(this.map.removeControl(this),this.map=null);this.div=null},setMap:function(a){this.map=a;this.handler&&
+this.handler.setMap(a)},draw:function(a){if(null==this.div&&(this.div=OpenLayers.Util.createDiv(this.id),this.div.className=this.displayClass,this.allowSelection||(this.div.className+=" olControlNoSelect",this.div.setAttribute("unselectable","on",0),this.div.onselectstart=OpenLayers.Function.False),""!=this.title))this.div.title=this.title;null!=a&&(this.position=a.clone());this.moveTo(this.position);return this.div},moveTo:function(a){null!=a&&null!=this.div&&(this.div.style.left=a.x+"px",this.div.style.top=
+a.y+"px")},activate:function(){if(this.active)return!1;this.handler&&this.handler.activate();this.active=!0;this.map&&OpenLayers.Element.addClass(this.map.viewPortDiv,this.displayClass.replace(/ /g,"")+"Active");this.events.triggerEvent("activate");return!0},deactivate:function(){return this.active?(this.handler&&this.handler.deactivate(),this.active=!1,this.map&&OpenLayers.Element.removeClass(this.map.viewPortDiv,this.displayClass.replace(/ /g,"")+"Active"),this.events.triggerEvent("deactivate"),
+!0):!1},CLASS_NAME:"OpenLayers.Control"});OpenLayers.Control.TYPE_BUTTON=1;OpenLayers.Control.TYPE_TOGGLE=2;OpenLayers.Control.TYPE_TOOL=3;OpenLayers.Event={observers:!1,KEY_SPACE:32,KEY_BACKSPACE:8,KEY_TAB:9,KEY_RETURN:13,KEY_ESC:27,KEY_LEFT:37,KEY_UP:38,KEY_RIGHT:39,KEY_DOWN:40,KEY_DELETE:46,element:function(a){return a.target||a.srcElement},isSingleTouch:function(a){return a.touches&&1==a.touches.length},isMultiTouch:function(a){return a.touches&&1this.minRatio&&b<=this.maxRatio&&this.ovmap.getExtent().containsBounds(a)},updateOverview:function(){var a=this.map.getResolution(),b=this.ovmap.getResolution(),c=b/a;c>this.maxRatio?
+b=this.minRatio*a:c<=this.minRatio&&(b=this.maxRatio*a);this.ovmap.getProjection()!=this.map.getProjection()?(a=this.map.center.clone(),a.transform(this.map.getProjectionObject(),this.ovmap.getProjectionObject())):a=this.map.center;this.ovmap.setCenter(a,this.ovmap.getZoomForResolution(b*this.resolutionFactor));this.updateRectToMap()},createMap:function(){var a=OpenLayers.Util.extend({controls:[],maxResolution:"auto",fallThrough:!1},this.mapOptions);this.ovmap=new OpenLayers.Map(this.mapDiv,a);this.ovmap.viewPortDiv.appendChild(this.extentRectangle);
+OpenLayers.Event.stopObserving(window,"unload",this.ovmap.unloadDestroy);this.ovmap.addLayers(this.layers);this.ovmap.zoomToMaxExtent();this.wComp=(this.wComp=parseInt(OpenLayers.Element.getStyle(this.extentRectangle,"border-left-width"))+parseInt(OpenLayers.Element.getStyle(this.extentRectangle,"border-right-width")))?this.wComp:2;this.hComp=(this.hComp=parseInt(OpenLayers.Element.getStyle(this.extentRectangle,"border-top-width"))+parseInt(OpenLayers.Element.getStyle(this.extentRectangle,"border-bottom-width")))?
+this.hComp:2;this.handlers.drag=new OpenLayers.Handler.Drag(this,{move:this.rectDrag,done:this.updateMapToRect},{map:this.ovmap});this.handlers.click=new OpenLayers.Handler.Click(this,{click:this.mapDivClick},{single:!0,"double":!1,stopSingle:!0,stopDouble:!0,pixelTolerance:1,map:this.ovmap});this.handlers.click.activate();this.rectEvents=new OpenLayers.Events(this,this.extentRectangle,null,!0);this.rectEvents.register("mouseover",this,function(){!this.handlers.drag.active&&!this.map.dragging&&this.handlers.drag.activate()});
+this.rectEvents.register("mouseout",this,function(){this.handlers.drag.dragging||this.handlers.drag.deactivate()});if(this.ovmap.getProjection()!=this.map.getProjection()){var a=this.map.getProjectionObject().getUnits()||this.map.units||this.map.baseLayer.units,b=this.ovmap.getProjectionObject().getUnits()||this.ovmap.units||this.ovmap.baseLayer.units;this.resolutionFactor=a&&b?OpenLayers.INCHES_PER_UNIT[a]/OpenLayers.INCHES_PER_UNIT[b]:1}},updateRectToMap:function(){var a=this.getRectBoundsFromMapBounds(this.ovmap.getProjection()!=
+this.map.getProjection()?this.map.getExtent().transform(this.map.getProjectionObject(),this.ovmap.getProjectionObject()):this.map.getExtent());a&&this.setRectPxBounds(a)},updateMapToRect:function(){var a=this.getMapBoundsFromRectBounds(this.rectPxBounds);this.ovmap.getProjection()!=this.map.getProjection()&&(a=a.transform(this.ovmap.getProjectionObject(),this.map.getProjectionObject()));this.map.panTo(a.getCenterLonLat())},setRectPxBounds:function(a){var b=Math.max(a.top,0),c=Math.max(a.left,0),d=
+Math.min(a.top+Math.abs(a.getHeight()),this.ovmap.size.h-this.hComp),a=Math.min(a.left+a.getWidth(),this.ovmap.size.w-this.wComp),e=Math.max(a-c,0),f=Math.max(d-b,0);ethis.duration&&this.stop()},CLASS_NAME:"OpenLayers.Tween"});OpenLayers.Easing={CLASS_NAME:"OpenLayers.Easing"};OpenLayers.Easing.Linear={easeIn:function(a,b,c,d){return c*a/d+b},easeOut:function(a,b,c,d){return c*a/d+b},easeInOut:function(a,b,c,d){return c*a/d+b},CLASS_NAME:"OpenLayers.Easing.Linear"};
+OpenLayers.Easing.Expo={easeIn:function(a,b,c,d){return 0==a?b:c*Math.pow(2,10*(a/d-1))+b},easeOut:function(a,b,c,d){return a==d?b+c:c*(-Math.pow(2,-10*a/d)+1)+b},easeInOut:function(a,b,c,d){return 0==a?b:a==d?b+c:1>(a/=d/2)?c/2*Math.pow(2,10*(a-1))+b:c/2*(-Math.pow(2,-10*--a)+2)+b},CLASS_NAME:"OpenLayers.Easing.Expo"};
+OpenLayers.Easing.Quad={easeIn:function(a,b,c,d){return c*(a/=d)*a+b},easeOut:function(a,b,c,d){return-c*(a/=d)*(a-2)+b},easeInOut:function(a,b,c,d){return 1>(a/=d/2)?c/2*a*a+b:-c/2*(--a*(a-2)-1)+b},CLASS_NAME:"OpenLayers.Easing.Quad"};OpenLayers.Projection=OpenLayers.Class({proj:null,projCode:null,titleRegEx:/\+title=[^\+]*/,initialize:function(a,b){OpenLayers.Util.extend(this,b);this.projCode=a;window.Proj4js&&(this.proj=new Proj4js.Proj(a))},getCode:function(){return this.proj?this.proj.srsCode:this.projCode},getUnits:function(){return this.proj?this.proj.units:null},toString:function(){return this.getCode()},equals:function(a){var b=!1;a&&(a instanceof OpenLayers.Projection||(a=new OpenLayers.Projection(a)),window.Proj4js&&
+this.proj.defData&&a.proj.defData?b=this.proj.defData.replace(this.titleRegEx,"")==a.proj.defData.replace(this.titleRegEx,""):a.getCode&&(b=this.getCode(),a=a.getCode(),b=b==a||!!OpenLayers.Projection.transforms[b]&&OpenLayers.Projection.transforms[b][a]===OpenLayers.Projection.nullTransform));return b},destroy:function(){delete this.proj;delete this.projCode},CLASS_NAME:"OpenLayers.Projection"});OpenLayers.Projection.transforms={};
+OpenLayers.Projection.defaults={"EPSG:4326":{units:"degrees",maxExtent:[-180,-90,180,90],yx:!0},"CRS:84":{units:"degrees",maxExtent:[-180,-90,180,90]},"EPSG:900913":{units:"m",maxExtent:[-2.003750834E7,-2.003750834E7,2.003750834E7,2.003750834E7]}};
+OpenLayers.Projection.addTransform=function(a,b,c){if(c===OpenLayers.Projection.nullTransform){var d=OpenLayers.Projection.defaults[a];d&&!OpenLayers.Projection.defaults[b]&&(OpenLayers.Projection.defaults[b]=d)}OpenLayers.Projection.transforms[a]||(OpenLayers.Projection.transforms[a]={});OpenLayers.Projection.transforms[a][b]=c};
+OpenLayers.Projection.transform=function(a,b,c){if(b&&c)if(b instanceof OpenLayers.Projection||(b=new OpenLayers.Projection(b)),c instanceof OpenLayers.Projection||(c=new OpenLayers.Projection(c)),b.proj&&c.proj)a=Proj4js.transform(b.proj,c.proj,a);else{var b=b.getCode(),c=c.getCode(),d=OpenLayers.Projection.transforms;if(d[b]&&d[b][c])d[b][c](a)}return a};OpenLayers.Projection.nullTransform=function(a){return a};
+(function(){function a(a){a.x=180*a.x/d;a.y=180/Math.PI*(2*Math.atan(Math.exp(a.y/d*Math.PI))-Math.PI/2);return a}function b(a){a.x=a.x*d/180;a.y=Math.log(Math.tan((90+a.y)*Math.PI/360))/Math.PI*d;return a}function c(c,d){var e=OpenLayers.Projection.addTransform,f=OpenLayers.Projection.nullTransform,g,m,n,o,p;g=0;for(m=d.length;gparseFloat(navigator.appVersion.split("MSIE")[1])?this.events.register("resize",this,this.updateSize):(this.updateSizeDestroy=OpenLayers.Function.bind(this.updateSize,this),OpenLayers.Event.observe(window,"resize",this.updateSizeDestroy));if(this.theme){for(var c=!0,d=document.getElementsByTagName("link"),
+e=0,f=d.length;eb?b=0:b>this.layers.length&&(b=this.layers.length);if(c!=b){this.layers.splice(c,1);this.layers.splice(b,0,a);for(var c=0,d=this.layers.length;c=this.minPx.x+h?Math.round(a):0;b=f<=this.maxPx.y-i&&f>=this.minPx.y+i?Math.round(b):0;if(a||b){this.dragging||(this.dragging=
+!0,this.events.triggerEvent("movestart"));this.center=null;a&&(this.layerContainerDiv.style.left=parseInt(this.layerContainerDiv.style.left)-a+"px",this.minPx.x-=a,this.maxPx.x-=a);b&&(this.layerContainerDiv.style.top=parseInt(this.layerContainerDiv.style.top)-b+"px",this.minPx.y-=b,this.maxPx.y-=b);d=0;for(e=this.layers.length;dc)for(var d=a|0,e=b.length;dthis.restrictedExtent.getWidth()?a=new OpenLayers.LonLat(g.lon,a.lat):f.leftthis.restrictedExtent.right&&
+(a=a.add(this.restrictedExtent.right-f.right,0));f.getHeight()>this.restrictedExtent.getHeight()?a=new OpenLayers.LonLat(a.lon,g.lat):f.bottomthis.restrictedExtent.top&&(a=a.add(0,this.restrictedExtent.top-f.top))}}e=e||this.isValidZoomLevel(b)&&b!=this.getZoom();f=this.isValidLonLat(a)&&!a.equals(this.center);if(e||f||d){d||this.events.triggerEvent("movestart");f&&(!e&&this.center&&this.centerLayerContainer(a),this.center=
+a.clone());a=e?this.getResolutionForZoom(b):this.getResolution();if(e||null==this.layerContainerOrigin){this.layerContainerOrigin=this.getCachedCenter();this.layerContainerDiv.style.left="0px";this.layerContainerDiv.style.top="0px";var f=this.getMaxExtent({restricted:!0}),h=f.getCenterLonLat(),g=this.center.lon-h.lon,h=h.lat-this.center.lat,i=Math.round(f.getWidth()/a),j=Math.round(f.getHeight()/a);this.minPx={x:(this.size.w-i)/2-g/a,y:(this.size.h-j)/2-h/a};this.maxPx={x:this.minPx.x+Math.round(f.getWidth()/
+a),y:this.minPx.y+Math.round(f.getHeight()/a)}}e&&(this.zoom=b,this.resolution=a);a=this.getExtent();this.baseLayer.visibility&&(this.baseLayer.moveTo(a,e,c.dragging),c.dragging||this.baseLayer.events.triggerEvent("moveend",{zoomChanged:e}));a=this.baseLayer.getExtent();for(b=this.layers.length-1;0<=b;--b)if(f=this.layers[b],f!==this.baseLayer&&!f.isBaseLayer&&(g=f.calculateInRange(),f.inRange!=g&&((f.inRange=g)||f.display(!1),this.events.triggerEvent("changelayer",{layer:f,property:"visibility"})),
+g&&f.visibility))f.moveTo(a,e,c.dragging),c.dragging||f.events.triggerEvent("moveend",{zoomChanged:e});this.events.triggerEvent("move");d||this.events.triggerEvent("moveend");if(e){b=0;for(c=this.popups.length;b=this.minResolution&&a<=this.maxResolution);return a},setIsBaseLayer:function(a){a!=this.isBaseLayer&&(this.isBaseLayer=a,null!=this.map&&this.map.events.triggerEvent("changebaselayer",{layer:this}))},initResolutions:function(){var a,
+b,c,d={},e=!0;a=0;for(b=this.RESOLUTION_PROPERTIES.length;a=a||"number"!==typeof d&&"number"!==typeof c)){b=Array(a);var e=2;"number"==typeof c&&"number"==typeof d&&(e=Math.pow(d/c,1/(a-1)));var f;if("number"===typeof d)for(f=0;f=a&&(f=h,e=c),h<=a){g=h;break}c=f-g;c=0f)break;f=e}else if(this.resolutions[c]=a.count&&
+this.removeGMapElements()}OpenLayers.Layer.EventPane.prototype.destroy.apply(this,arguments)},removeGMapElements:function(){var a=OpenLayers.Layer.Google.cache[this.map.id];if(a){var b=this.mapObject&&this.getMapContainer();b&&b.parentNode&&b.parentNode.removeChild(b);(b=a.termsOfUse)&&b.parentNode&&b.parentNode.removeChild(b);(a=a.poweredBy)&&a.parentNode&&a.parentNode.removeChild(a)}},removeMap:function(a){this.visibility&&this.mapObject&&this.setGMapVisibility(!1);var b=OpenLayers.Layer.Google.cache[a.id];
+b&&(1>=b.count?(this.removeGMapElements(),delete OpenLayers.Layer.Google.cache[a.id]):--b.count);delete this.termsOfUse;delete this.poweredBy;delete this.mapObject;delete this.dragObject;OpenLayers.Layer.EventPane.prototype.removeMap.apply(this,arguments)},getOLBoundsFromMapObjectBounds:function(a){var b=null;null!=a&&(b=a.getSouthWest(),a=a.getNorthEast(),this.sphericalMercator?(b=this.forwardMercator(b.lng(),b.lat()),a=this.forwardMercator(a.lng(),a.lat())):(b=new OpenLayers.LonLat(b.lng(),b.lat()),
+a=new OpenLayers.LonLat(a.lng(),a.lat())),b=new OpenLayers.Bounds(b.lon,b.lat,a.lon,a.lat));return b},getWarningHTML:function(){return OpenLayers.i18n("googleWarning")},getMapObjectCenter:function(){return this.mapObject.getCenter()},getMapObjectZoom:function(){return this.mapObject.getZoom()},getLongitudeFromMapObjectLonLat:function(a){return this.sphericalMercator?this.forwardMercator(a.lng(),a.lat()).lon:a.lng()},getLatitudeFromMapObjectLonLat:function(a){return this.sphericalMercator?this.forwardMercator(a.lng(),
+a.lat()).lat:a.lat()},getXFromMapObjectPixel:function(a){return a.x},getYFromMapObjectPixel:function(a){return a.y},CLASS_NAME:"OpenLayers.Layer.Google"});OpenLayers.Layer.Google.cache={};
+OpenLayers.Layer.Google.v2={termsOfUse:null,poweredBy:null,dragObject:null,loadMapObject:function(){this.type||(this.type=G_NORMAL_MAP);var a,b,c,d=OpenLayers.Layer.Google.cache[this.map.id];if(d)a=d.mapObject,b=d.termsOfUse,c=d.poweredBy,++d.count;else{var d=this.map.viewPortDiv,e=document.createElement("div");e.id=this.map.id+"_GMap2Container";e.style.position="absolute";e.style.width="100%";e.style.height="100%";d.appendChild(e);try{a=new GMap2(e),b=e.lastChild,d.appendChild(b),b.style.zIndex=
+"1100",b.style.right="",b.style.bottom="",b.className="olLayerGoogleCopyright",c=e.lastChild,d.appendChild(c),c.style.zIndex="1100",c.style.right="",c.style.bottom="",c.className="olLayerGooglePoweredBy gmnoprint"}catch(f){throw f;}OpenLayers.Layer.Google.cache[this.map.id]={mapObject:a,termsOfUse:b,poweredBy:c,count:1}}this.mapObject=a;this.termsOfUse=b;this.poweredBy=c;-1===OpenLayers.Util.indexOf(this.mapObject.getMapTypes(),this.type)&&this.mapObject.addMapType(this.type);"function"==typeof a.getDragObject?
+this.dragObject=a.getDragObject():this.dragPanMapObject=null;!1===this.isBaseLayer&&this.setGMapVisibility("none"!==this.div.style.display)},onMapResize:function(){if(this.visibility&&this.mapObject.isLoaded())this.mapObject.checkResize();else{if(!this._resized)var a=this,b=GEvent.addListener(this.mapObject,"load",function(){GEvent.removeListener(b);delete a._resized;a.mapObject.checkResize();a.moveTo(a.map.getCenter(),a.map.getZoom())});this._resized=!0}},setGMapVisibility:function(a){var b=OpenLayers.Layer.Google.cache[this.map.id];
+if(b){var c=this.mapObject.getContainer();!0===a?(this.mapObject.setMapType(this.type),c.style.display="",this.termsOfUse.style.left="",this.termsOfUse.style.display="",this.poweredBy.style.display="",b.displayed=this.id):(b.displayed===this.id&&delete b.displayed,b.displayed||(c.style.display="none",this.termsOfUse.style.display="none",this.termsOfUse.style.left="-9999px",this.poweredBy.style.display="none"))}},getMapContainer:function(){return this.mapObject.getContainer()},getMapObjectBoundsFromOLBounds:function(a){var b=
+null;null!=a&&(b=this.sphericalMercator?this.inverseMercator(a.bottom,a.left):new OpenLayers.LonLat(a.bottom,a.left),a=this.sphericalMercator?this.inverseMercator(a.top,a.right):new OpenLayers.LonLat(a.top,a.right),b=new GLatLngBounds(new GLatLng(b.lat,b.lon),new GLatLng(a.lat,a.lon)));return b},setMapObjectCenter:function(a,b){this.mapObject.setCenter(a,b)},dragPanMapObject:function(a,b){this.dragObject.moveBy(new GSize(-a,b))},getMapObjectLonLatFromMapObjectPixel:function(a){return this.mapObject.fromContainerPixelToLatLng(a)},
+getMapObjectPixelFromMapObjectLonLat:function(a){return this.mapObject.fromLatLngToContainerPixel(a)},getMapObjectZoomFromMapObjectBounds:function(a){return this.mapObject.getBoundsZoomLevel(a)},getMapObjectLonLatFromLonLat:function(a,b){var c;this.sphericalMercator?(c=this.inverseMercator(a,b),c=new GLatLng(c.lat,c.lon)):c=new GLatLng(b,a);return c},getMapObjectPixelFromXY:function(a,b){return new GPoint(a,b)}};OpenLayers.Format.XML=OpenLayers.Class(OpenLayers.Format,{namespaces:null,namespaceAlias:null,defaultPrefix:null,readers:{},writers:{},xmldom:null,initialize:function(a){window.ActiveXObject&&(this.xmldom=new ActiveXObject("Microsoft.XMLDOM"));OpenLayers.Format.prototype.initialize.apply(this,[a]);this.namespaces=OpenLayers.Util.extend({},this.namespaces);this.namespaceAlias={};for(var b in this.namespaces)this.namespaceAlias[this.namespaces[b]]=b},destroy:function(){this.xmldom=null;OpenLayers.Format.prototype.destroy.apply(this,
+arguments)},setNamespace:function(a,b){this.namespaces[a]=b;this.namespaceAlias[b]=a},read:function(a){var b=a.indexOf("<");0this.value;break;case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:b=a<=this.value;break;case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:b=
+a>=this.value;break;case OpenLayers.Filter.Comparison.BETWEEN:b=a>=this.lowerBoundary&&a<=this.upperBoundary;break;case OpenLayers.Filter.Comparison.LIKE:b=RegExp(this.value,"gi").test(a)}return b},value2regex:function(a,b,c){if("."==a)throw Error("'.' is an unsupported wildCard character for OpenLayers.Filter.Comparison");a=a?a:"*";b=b?b:".";this.value=this.value.replace(RegExp("\\"+(c?c:"!")+"(.|$)","g"),"\\$1");this.value=this.value.replace(RegExp("\\"+b,"g"),".");this.value=this.value.replace(RegExp("\\"+
+a,"g"),".*");this.value=this.value.replace(RegExp("\\\\.\\*","g"),"\\"+a);return this.value=this.value.replace(RegExp("\\\\\\.","g"),"\\"+b)},regex2value:function(){var a=this.value,a=a.replace(/!/g,"!!"),a=a.replace(/(\\)?\\\./g,function(a,c){return c?a:"!."}),a=a.replace(/(\\)?\\\*/g,function(a,c){return c?a:"!*"}),a=a.replace(/\\\\/g,"\\");return a=a.replace(/\.\*/g,"*")},clone:function(){return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison,this)},CLASS_NAME:"OpenLayers.Filter.Comparison"});
+OpenLayers.Filter.Comparison.EQUAL_TO="==";OpenLayers.Filter.Comparison.NOT_EQUAL_TO="!=";OpenLayers.Filter.Comparison.LESS_THAN="<";OpenLayers.Filter.Comparison.GREATER_THAN=">";OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO="<=";OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO=">=";OpenLayers.Filter.Comparison.BETWEEN="..";OpenLayers.Filter.Comparison.LIKE="~";OpenLayers.Format.Filter=OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC,{defaultVersion:"1.0.0",CLASS_NAME:"OpenLayers.Format.Filter"});OpenLayers.Filter.Function=OpenLayers.Class(OpenLayers.Filter,{name:null,params:null,CLASS_NAME:"OpenLayers.Filter.Function"});OpenLayers.Format.Filter.v1=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{ogc:"http://www.opengis.net/ogc",gml:"http://www.opengis.net/gml",xlink:"http://www.w3.org/1999/xlink",xsi:"http://www.w3.org/2001/XMLSchema-instance"},defaultPrefix:"ogc",schemaLocation:null,initialize:function(a){OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},read:function(a){var b={};this.readers.ogc.Filter.apply(this,[a,b]);return b.filter},readers:{ogc:{_expression:function(a){for(var b="",c=a.firstChild;c;c=
+c.nextSibling)switch(c.nodeType){case 1:a=this.readNode(c);a.property?b+="${"+a.property+"}":void 0!==a.value&&(b+=a.value);break;case 3:case 4:b+=c.nodeValue}return b},Filter:function(a,b){var c={fids:[],filters:[]};this.readChildNodes(a,c);0":"PropertyIsGreaterThan",
+"<=":"PropertyIsLessThanOrEqualTo",">=":"PropertyIsGreaterThanOrEqualTo","..":"PropertyIsBetween","~":"PropertyIsLike",BBOX:"BBOX",DWITHIN:"DWITHIN",WITHIN:"WITHIN",CONTAINS:"CONTAINS",INTERSECTS:"INTERSECTS",FID:"FeatureId"},CLASS_NAME:"OpenLayers.Format.Filter.v1"});OpenLayers.Geometry=OpenLayers.Class({id:null,parent:null,bounds:null,initialize:function(){this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},destroy:function(){this.bounds=this.id=null},clone:function(){return new OpenLayers.Geometry},setBounds:function(a){a&&(this.bounds=a.clone())},clearBounds:function(){this.bounds=null;this.parent&&this.parent.clearBounds()},extendBounds:function(a){this.getBounds()?this.bounds.extend(a):this.setBounds(a)},getBounds:function(){null==this.bounds&&this.calculateBounds();
+return this.bounds},calculateBounds:function(){},distanceTo:function(){},getVertices:function(){},atPoint:function(a,b,c){var d=!1;null!=this.getBounds()&&null!=a&&(b=null!=b?b:0,c=null!=c?c:0,d=(new OpenLayers.Bounds(this.bounds.left-b,this.bounds.bottom-c,this.bounds.right+b,this.bounds.top+c)).containsLonLat(a));return d},getLength:function(){return 0},getArea:function(){return 0},getCentroid:function(){return null},toString:function(){return OpenLayers.Format&&OpenLayers.Format.WKT?OpenLayers.Format.WKT.prototype.write(new OpenLayers.Feature.Vector(this)):
+Object.prototype.toString.call(this)},CLASS_NAME:"OpenLayers.Geometry"});OpenLayers.Geometry.fromWKT=function(a){var b;if(OpenLayers.Format&&OpenLayers.Format.WKT){var c=OpenLayers.Geometry.fromWKT.format;c||(c=new OpenLayers.Format.WKT,OpenLayers.Geometry.fromWKT.format=c);a=c.read(a);if(a instanceof OpenLayers.Feature.Vector)b=a.geometry;else if(OpenLayers.Util.isArray(a)){b=a.length;for(var c=Array(b),d=0;d=f&&0<=l&&1>=l)&&(d?(h=a.x1+f*h,l=a.y1+f*i,e=new OpenLayers.Geometry.Point(h,l)):e=!0));if(c)if(e){if(d){a=[a,b];b=0;a:for(;2>b;++b){f=a[b];for(i=1;3>i;++i)if(h=f["x"+i],l=f["y"+i],d=Math.sqrt(Math.pow(h-e.x,2)+Math.pow(l-e.y,2)),db;++b){h=a[b];l=a[(b+1)%2];for(i=1;3>i;++i)if(f={x:h["x"+i],y:h["y"+i]},g=OpenLayers.Geometry.distanceToSegment(f,l),g.distance=k||(1<=k?(e=g,f=h):(e+=k*i,f+=k*j));return{distance:Math.sqrt(Math.pow(e-c,2)+Math.pow(f-d,2)),x:e,y:f}};OpenLayers.Geometry.Point=OpenLayers.Class(OpenLayers.Geometry,{x:null,y:null,initialize:function(a,b){OpenLayers.Geometry.prototype.initialize.apply(this,arguments);this.x=parseFloat(a);this.y=parseFloat(b)},clone:function(a){null==a&&(a=new OpenLayers.Geometry.Point(this.x,this.y));OpenLayers.Util.applyDefaults(a,this);return a},calculateBounds:function(){this.bounds=new OpenLayers.Bounds(this.x,this.y,this.x,this.y)},distanceTo:function(a,b){var c=!(b&&!1===b.edge)&&b&&b.details,d,e,f,g,h;a instanceof
+OpenLayers.Geometry.Point?(e=this.x,f=this.y,g=a.x,h=a.y,d=Math.sqrt(Math.pow(e-g,2)+Math.pow(f-h,2)),d=!c?d:{x0:e,y0:f,x1:g,y1:h,distance:d}):(d=a.distanceTo(this,b),c&&(d={x0:d.x1,y0:d.y1,x1:d.x0,y1:d.y0,distance:d.distance}));return d},equals:function(a){var b=!1;null!=a&&(b=this.x==a.x&&this.y==a.y||isNaN(this.x)&&isNaN(this.y)&&isNaN(a.x)&&isNaN(a.y));return b},toShortString:function(){return this.x+", "+this.y},move:function(a,b){this.x+=a;this.y+=b;this.clearBounds()},rotate:function(a,b){var a=
+a*(Math.PI/180),c=this.distanceTo(b),d=a+Math.atan2(this.y-b.y,this.x-b.x);this.x=b.x+c*Math.cos(d);this.y=b.y+c*Math.sin(d);this.clearBounds()},getCentroid:function(){return new OpenLayers.Geometry.Point(this.x,this.y)},resize:function(a,b,c){this.x=b.x+a*(void 0==c?1:c)*(this.x-b.x);this.y=b.y+a*(this.y-b.y);this.clearBounds();return this},intersects:function(a){var b=!1;return b="OpenLayers.Geometry.Point"==a.CLASS_NAME?this.equals(a):a.intersects(this)},transform:function(a,b){a&&b&&(OpenLayers.Projection.transform(this,
+a,b),this.bounds=null);return this},getVertices:function(){return[this]},CLASS_NAME:"OpenLayers.Geometry.Point"});OpenLayers.Geometry.Collection=OpenLayers.Class(OpenLayers.Geometry,{components:null,componentTypes:null,initialize:function(a){OpenLayers.Geometry.prototype.initialize.apply(this,arguments);this.components=[];null!=a&&this.addComponents(a)},destroy:function(){this.components.length=0;this.components=null;OpenLayers.Geometry.prototype.destroy.apply(this,arguments)},clone:function(){for(var a=eval("new "+this.CLASS_NAME+"()"),b=0,c=this.components.length;bf)break;if(!(i.x2Math.max(g,h))&&!(Math.max(j,k)h&&(i>j.y1&&ij.y2))break;e=c?{distance:e.distance,x0:e.x,y0:e.y,x1:h,y1:i}:e.distance}else if(a instanceof OpenLayers.Geometry.LineString){var g=this.getSortedSegments(),h=a.getSortedSegments(),m,n,o=h.length,p={point:!0},k=0,l=g.length;a:for(;kb.length)return this;var c=function(a,b,d,i){for(var j=0,k=0,l=b,m;lj&&(j=m,k=l)}j>i&&k!=b&&(e.push(k),c(a,b,k,i),c(a,k,d,i))},d=b.length-1,e=[];e.push(0);for(e.push(d);b[0].equals(b[d]);)d--,e.push(d);c(b,0,d,a);a=[];e.sort(function(a,b){return a-b});for(d=0;d=g&&c<=h||g>=h&&c<=g&&c>=h)){j=-1;break}}else{i=b((a-f)*((h-g)/(f-e))+h,14);if(i==c&&(e=e&&a<=f||e>f&&a<=e&&a>=f)){j=-1;break}i<=c||g!=h&&(iMath.max(g,h))||(e=e&&af&&a=f)&&++j}return-1==j?1:!!(j&1)},intersects:function(a){var b=!1;if("OpenLayers.Geometry.Point"==
+a.CLASS_NAME)b=this.containsPoint(a);else if("OpenLayers.Geometry.LineString"==a.CLASS_NAME)b=a.intersects(this);else if("OpenLayers.Geometry.LinearRing"==a.CLASS_NAME)b=OpenLayers.Geometry.LineString.prototype.intersects.apply(this,[a]);else for(var c=0,d=a.components.length;c0)b.bounds=c.components[0]},Point:function(a,
+b){var c={points:[]};this.readChildNodes(a,c);if(!b.components)b.components=[];b.components.push(c.points[0])},coordinates:function(a,b){for(var c=this.getChildValue(a).replace(this.regExes.trimSpace,""),c=c.replace(this.regExes.trimComma,","),c=c.split(this.regExes.splitSpace),d,e=c.length,f=Array(e),g=0;ga?"0"+a:a}return'"'+a.getFullYear()+"-"+
+b(a.getMonth()+1)+"-"+b(a.getDate())+"T"+b(a.getHours())+":"+b(a.getMinutes())+":"+b(a.getSeconds())+'"'}},CLASS_NAME:"OpenLayers.Format.JSON"});OpenLayers.Format.GeoJSON=OpenLayers.Class(OpenLayers.Format.JSON,{ignoreExtraDims:!1,read:function(a,b,c){var b=b?b:"FeatureCollection",d=null,e=null;if(e="string"==typeof a?OpenLayers.Format.JSON.prototype.read.apply(this,[a,c]):a)if("string"!=typeof e.type)OpenLayers.Console.error("Bad GeoJSON - no type: "+a);else{if(this.isValidType(e,b))switch(b){case "Geometry":try{d=this.parseGeometry(e)}catch(f){OpenLayers.Console.error(f)}break;case "Feature":try{d=this.parseFeature(e),d.type="Feature"}catch(g){OpenLayers.Console.error(g)}break;
+case "FeatureCollection":switch(d=[],e.type){case "Feature":try{d.push(this.parseFeature(e))}catch(h){d=null,OpenLayers.Console.error(h)}break;case "FeatureCollection":a=0;for(b=e.features.length;athis.layer.opacity&&(a.filter="alpha(opacity="+
+100*this.layer.opacity+")");a.position="absolute";this.layerAlphaHack&&(a.paddingTop=a.height,a.height="0",a.width="100%");this.frame&&this.frame.appendChild(this.imgDiv)}return this.imgDiv},initImage:function(){this.events.triggerEvent(this._loadEvent);var a=this.getImage();if(this.url&&a.getAttribute("src")==this.url)this.onImageLoad();else{var b=OpenLayers.Function.bind(function(){OpenLayers.Event.stopObservingElement(a);OpenLayers.Event.observe(a,"load",OpenLayers.Function.bind(this.onImageLoad,
+this));OpenLayers.Event.observe(a,"error",OpenLayers.Function.bind(this.onImageError,this));this.imageReloadAttempts=0;this.setImgSrc(this.url)},this);a.getAttribute("src")==this.blankImageUrl?b():(OpenLayers.Event.observe(a,"load",b),OpenLayers.Event.observe(a,"error",b),this.crossOriginKeyword&&a.removeAttribute("crossorigin"),a.src=this.blankImageUrl)}},setImgSrc:function(a){var b=this.imgDiv;b.style.visibility="hidden";b.style.opacity=0;a&&(this.crossOriginKeyword&&("data:"!==a.substr(0,5)?b.setAttribute("crossorigin",
+this.crossOriginKeyword):b.removeAttribute("crossorigin")),b.src=a)},getTile:function(){return this.frame?this.frame:this.getImage()},createBackBuffer:function(){if(this.imgDiv&&!this.isLoading){var a;this.frame?(a=this.frame.cloneNode(!1),a.appendChild(this.imgDiv)):a=this.imgDiv;this.imgDiv=null;return a}},onImageLoad:function(){var a=this.imgDiv;OpenLayers.Event.stopObservingElement(a);a.style.visibility="inherit";a.style.opacity=this.layer.opacity;this.isLoading=!1;this.canvasContext=null;this.events.triggerEvent("loadend");
+if(7>parseFloat(navigator.appVersion.split("MSIE")[1])&&this.layer&&this.layer.div){var b=document.createElement("span");b.style.display="none";var c=this.layer.div;c.appendChild(b);window.setTimeout(function(){b.parentNode===c&&b.parentNode.removeChild(b)},0)}!0===this.layerAlphaHack&&(a.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+a.src+"', sizingMethod='scale')")},onImageError:function(){var a=this.imgDiv;null!=a.src&&(this.imageReloadAttempts++,this.imageReloadAttempts<=
+OpenLayers.IMAGE_RELOAD_ATTEMPTS?this.setImgSrc(this.layer.getURL(this.bounds)):(OpenLayers.Element.addClass(a,"olImageLoadError"),this.events.triggerEvent("loaderror"),this.onImageLoad()))},getCanvasContext:function(){if(OpenLayers.CANVAS_SUPPORTED&&this.imgDiv&&!this.isLoading){if(!this.canvasContext){var a=document.createElement("canvas");a.width=this.size.w;a.height=this.size.h;this.canvasContext=a.getContext("2d");this.canvasContext.drawImage(this.imgDiv,0,0)}return this.canvasContext}},CLASS_NAME:"OpenLayers.Tile.Image"});OpenLayers.Layer.Grid=OpenLayers.Class(OpenLayers.Layer.HTTPRequest,{tileSize:null,tileOriginCorner:"bl",tileOrigin:null,tileOptions:null,tileClass:OpenLayers.Tile.Image,grid:null,singleTile:!1,ratio:1.5,buffer:0,transitionEffect:null,numLoadingTiles:0,tileLoadingDelay:85,serverResolutions:null,moveTimerId:null,deferMoveGriddedTiles:null,tileQueueId:null,tileQueue:null,loading:!1,backBuffer:null,gridResolution:null,backBufferResolution:null,backBufferLonLat:null,backBufferTimerId:null,removeBackBufferDelay:null,
+className:null,initialize:function(a,b,c,d){OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this,arguments);this.grid=[];this.tileQueue=[];null===this.removeBackBufferDelay&&(this.removeBackBufferDelay=this.singleTile?0:2500);null===this.className&&(this.className=this.singleTile?"olLayerGridSingleTile":"olLayerGrid");OpenLayers.Animation.isNative||(this.deferMoveGriddedTiles=OpenLayers.Function.bind(function(){this.moveGriddedTiles(true);this.moveTimerId=null},this))},setMap:function(a){OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this,
+a);OpenLayers.Element.addClass(this.div,this.className)},removeMap:function(){null!==this.moveTimerId&&(window.clearTimeout(this.moveTimerId),this.moveTimerId=null);this.clearTileQueue();null!==this.backBufferTimerId&&(window.clearTimeout(this.backBufferTimerId),this.backBufferTimerId=null)},destroy:function(){this.removeBackBuffer();this.clearGrid();this.tileSize=this.grid=null;OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this,arguments)},clearGrid:function(){this.clearTileQueue();if(this.grid){for(var a=
+0,b=this.grid.length;aa){a=c;break}if(-1===b)throw"no appropriate resolution in serverResolutions";}return a},getServerZoom:function(){var a=this.getServerResolution();return this.serverResolutions?OpenLayers.Util.indexOf(this.serverResolutions,a):this.map.getZoomForResolution(a)+(this.zoomOffset||0)},transformDiv:function(a){this.div.style.width=100*a+"%";this.div.style.height=100*a+"%";var b=this.map.getSize(),c=parseInt(this.map.layerContainerDiv.style.left,10),d=(parseInt(this.map.layerContainerDiv.style.top,
+10)-b.h/2)*(a-1);this.div.style.left=(c-b.w/2)*(a-1)+"%";this.div.style.top=d+"%"},getResolutionScale:function(){return parseInt(this.div.style.width,10)/100},applyBackBuffer:function(a){null!==this.backBufferTimerId&&this.removeBackBuffer();var b=this.backBuffer;if(!b){b=this.createBackBuffer();if(!b)return;this.div.insertBefore(b,this.div.firstChild);this.backBuffer=b;var c=this.grid[0][0].bounds;this.backBufferLonLat={lon:c.left,lat:c.top};this.backBufferResolution=this.gridResolution}var c=b.style,
+d=this.backBufferResolution/a;c.width=100*d+"%";c.height=100*d+"%";a=this.getViewPortPxFromLonLat(this.backBufferLonLat,a);c=parseInt(this.map.layerContainerDiv.style.left,10);d=parseInt(this.map.layerContainerDiv.style.top,10);b.style.left=Math.round(a.x-c)+"%";b.style.top=Math.round(a.y-d)+"%"},createBackBuffer:function(){var a;if(0=a.bottom-j*this.buffer||m-e*(a-1))this.shiftColumn(!0);else if(c<-e*a)this.shiftColumn(!1);else if(d>-f*(a-1))this.shiftRow(!0);else if(d<-f*a)this.shiftRow(!1);else break}},shiftRow:function(a){for(var b=this.grid,c=b[a?0:this.grid.length-1],d=this.getServerResolution(),e=a?-this.tileSize.h:this.tileSize.h,d=d*-e,f=a?
+b.pop():b.shift(),g=0,h=c.length;ga;){var e=this.grid.pop();c=0;for(d=e.length;cb;)e=this.grid[c],f=e.pop(),this.destroyTile(f)},onMapResize:function(){this.singleTile&&(this.clearGrid(),this.setTileSize())},getTileBounds:function(a){var b=this.maxExtent,c=this.getResolution(),d=c*this.tileSize.w,c=c*this.tileSize.h,e=this.getLonLatFromViewPortPx(a),a=b.left+d*Math.floor((e.lon-b.left)/d),b=b.bottom+
+c*Math.floor((e.lat-b.bottom)/c);return new OpenLayers.Bounds(a,b,a+d,b+c)},CLASS_NAME:"OpenLayers.Layer.Grid"});OpenLayers.Format.ArcXML=OpenLayers.Class(OpenLayers.Format.XML,{fontStyleKeys:"antialiasing blockout font fontcolor fontsize fontstyle glowing interval outline printmode shadow transparency".split(" "),request:null,response:null,initialize:function(a){this.request=new OpenLayers.Format.ArcXML.Request;this.response=new OpenLayers.Format.ArcXML.Response;if(a)if("feature"==a.requesttype){this.request.get_image=null;var b=this.request.get_feature.query;this.addCoordSys(b.featurecoordsys,a.featureCoordSys);
+this.addCoordSys(b.filtercoordsys,a.filterCoordSys);a.polygon?(b.isspatial=!0,b.spatialfilter.polygon=a.polygon):a.envelope&&(b.isspatial=!0,b.spatialfilter.envelope={minx:0,miny:0,maxx:0,maxy:0},this.parseEnvelope(b.spatialfilter.envelope,a.envelope))}else"image"==a.requesttype?(this.request.get_feature=null,b=this.request.get_image.properties,this.parseEnvelope(b.envelope,a.envelope),this.addLayers(b.layerlist,a.layers),this.addImageSize(b.imagesize,a.tileSize),this.addCoordSys(b.featurecoordsys,
+a.featureCoordSys),this.addCoordSys(b.filtercoordsys,a.filterCoordSys)):this.request=null;OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},parseEnvelope:function(a,b){b&&4==b.length&&(a.minx=b[0],a.miny=b[1],a.maxx=b[2],a.maxy=b[3])},addLayers:function(a,b){for(var c=0,d=b.length;cg.where.length)continue;var h=null,h="boolean"==typeof g.spatialfilter&&g.spatialfilter?this.createElementNS("","SPATIALQUERY"):this.createElementNS("","QUERY");h.setAttribute("where",g.where);"number"==typeof g.accuracy&&0g.featurelimit&&h.setAttribute("featurelimit",
+g.featurelimit);"string"==typeof g.subfields&&"#ALL#"!=g.subfields&&h.setAttribute("subfields",g.subfields);"string"==typeof g.joinexpression&&0b.status)this.events.triggerEvent("success",a),e&&e(b);
+if(b.status&&(200>b.status||300<=b.status))this.events.triggerEvent("failure",a),f&&f(b)},GET:function(a){a=OpenLayers.Util.extend(a,{method:"GET"});return OpenLayers.Request.issue(a)},POST:function(a){a=OpenLayers.Util.extend(a,{method:"POST"});a.headers=a.headers?a.headers:{};"CONTENT-TYPE"in OpenLayers.Util.upperCaseObject(a.headers)||(a.headers["Content-Type"]="application/xml");return OpenLayers.Request.issue(a)},PUT:function(a){a=OpenLayers.Util.extend(a,{method:"PUT"});a.headers=a.headers?
+a.headers:{};"CONTENT-TYPE"in OpenLayers.Util.upperCaseObject(a.headers)||(a.headers["Content-Type"]="application/xml");return OpenLayers.Request.issue(a)},DELETE:function(a){a=OpenLayers.Util.extend(a,{method:"DELETE"});return OpenLayers.Request.issue(a)},HEAD:function(a){a=OpenLayers.Util.extend(a,{method:"HEAD"});return OpenLayers.Request.issue(a)},OPTIONS:function(a){a=OpenLayers.Util.extend(a,{method:"OPTIONS"});return OpenLayers.Request.issue(a)}};OpenLayers.Layer.ArcIMS=OpenLayers.Class(OpenLayers.Layer.Grid,{DEFAULT_PARAMS:{ClientVersion:"9.2",ServiceName:""},featureCoordSys:"4326",filterCoordSys:"4326",layers:null,async:!0,name:"ArcIMS",isBaseLayer:!0,DEFAULT_OPTIONS:{tileSize:new OpenLayers.Size(512,512),featureCoordSys:"4326",filterCoordSys:"4326",layers:null,isBaseLayer:!0,async:!0,name:"ArcIMS"},initialize:function(a,b,c){this.tileSize=new OpenLayers.Size(512,512);this.params=OpenLayers.Util.applyDefaults({ServiceName:c.serviceName},
+this.DEFAULT_PARAMS);this.options=OpenLayers.Util.applyDefaults(c,this.DEFAULT_OPTIONS);OpenLayers.Layer.Grid.prototype.initialize.apply(this,[a,b,this.params,c]);if(this.transparent&&(this.isBaseLayer||(this.isBaseLayer=!1),"image/jpeg"==this.format))this.format=OpenLayers.Util.alphaHack()?"image/gif":"image/png";null===this.options.layers&&(this.options.layers=[])},getURL:function(a){var b="",a=this.adjustBounds(a),a=new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options,{requesttype:"image",
+envelope:a.toArray(),tileSize:this.tileSize})),a=new OpenLayers.Request.POST({url:this.getFullRequestString(),data:a.write(),async:!1});if(null!=a){b=a.responseXML;if(!b||!b.documentElement)b=a.responseText;b=this.getUrlOrImage((new OpenLayers.Format.ArcXML).read(b).image.output)}return b},getURLasync:function(a,b,c){a=this.adjustBounds(a);a=new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options,{requesttype:"image",envelope:a.toArray(),tileSize:this.tileSize}));OpenLayers.Request.POST({url:this.getFullRequestString(),
+async:!0,data:a.write(),callback:function(a){var e=a.responseXML;if(!e||!e.documentElement)e=a.responseText;a=(new OpenLayers.Format.ArcXML).read(e);b.call(c,this.getUrlOrImage(a.image.output))},scope:this})},getUrlOrImage:function(a){var b="";a.url?b=a.url:a.data&&(b="data:image/"+a.type+";base64,"+a.data);return b},setLayerQuery:function(a,b){for(var c=0;cOpenStreetMap",sphericalMercator:!0,wrapDateLine:!0,tileOptions:null,initialize:function(a,b,c){OpenLayers.Layer.XYZ.prototype.initialize.apply(this,arguments);this.tileOptions=OpenLayers.Util.extend({crossOriginKeyword:"anonymous"},
+this.options&&this.options.tileOptions)},clone:function(a){null==a&&(a=new OpenLayers.Layer.OSM(this.name,this.url,this.getOptions()));return a=OpenLayers.Layer.XYZ.prototype.clone.apply(this,[a])},CLASS_NAME:"OpenLayers.Layer.OSM"});OpenLayers.Renderer=OpenLayers.Class({container:null,root:null,extent:null,locked:!1,size:null,resolution:null,map:null,featureDx:0,initialize:function(a,b){this.container=OpenLayers.Util.getElement(a);OpenLayers.Util.extend(this,b)},destroy:function(){this.map=this.resolution=this.size=this.extent=this.container=null},supported:function(){return!1},setExtent:function(a,b){this.extent=a.clone();if(this.map.baseLayer&&this.map.baseLayer.wrapDateLine){var c=a.getWidth()/this.map.getExtent().getWidth(),
+a=a.scale(1/c);this.extent=a.wrapDateLine(this.map.getMaxExtent()).scale(c)}b&&(this.resolution=null);return!0},setSize:function(a){this.size=a.clone();this.resolution=null},getResolution:function(){return this.resolution=this.resolution||this.map.getResolution()},drawFeature:function(a,b){null==b&&(b=a.style);if(a.geometry){var c=a.geometry.getBounds();if(c){var d;this.map.baseLayer&&this.map.baseLayer.wrapDateLine&&(d=this.map.getMaxExtent());c.intersectsBounds(this.extent,{worldBounds:d})?this.calculateFeatureDx(c,
+d):b={display:"none"};c=this.drawGeometry(a.geometry,b,a.id);if("none"!=b.display&&b.label&&!1!==c){d=a.geometry.getCentroid();if(b.labelXOffset||b.labelYOffset){var e=isNaN(b.labelXOffset)?0:b.labelXOffset,f=isNaN(b.labelYOffset)?0:b.labelYOffset,g=this.getResolution();d.move(e*g,f*g)}this.drawText(a.id,b,d)}else this.removeText(a.id);return c}}},calculateFeatureDx:function(a,b){this.featureDx=0;if(b){var c=b.getWidth();this.featureDx=Math.round(((a.left+a.right)/2-(this.extent.left+this.extent.right)/
+2)/c)*c}},drawGeometry:function(){},drawText:function(){},removeText:function(){},clear:function(){},getFeatureIdFromEvent:function(){},eraseFeatures:function(a){OpenLayers.Util.isArray(a)||(a=[a]);for(var b=0,c=a.length;bh.length)&&(a=this.getLocalXY(a),e=a[0],g=a[1],!isNaN(e)&&!isNaN(g))){this.canvas.lineCap="round";this.canvas.lineJoin="round";this.hitDetection&&(this.hitContext.lineCap="round",this.hitContext.lineJoin="round");if(b.graphicName in this.cachedSymbolBounds)d=this.cachedSymbolBounds[b.graphicName];else{d=new OpenLayers.Bounds;for(a=0;a=this.map.Z_INDEX_BASE.Feature?this.layer.setZIndex(a):this.map.setLayerZIndex(this.layer,this.map.getLayerIndex(this.layer))},CLASS_NAME:"OpenLayers.Handler.Feature"});OpenLayers.Control.DragFeature=OpenLayers.Class(OpenLayers.Control,{geometryTypes:null,onStart:function(){},onDrag:function(){},onComplete:function(){},onEnter:function(){},onLeave:function(){},documentDrag:!1,layer:null,feature:null,dragCallbacks:{},featureCallbacks:{},lastPixel:null,initialize:function(a,b){OpenLayers.Control.prototype.initialize.apply(this,[b]);this.layer=a;this.handlers={drag:new OpenLayers.Handler.Drag(this,OpenLayers.Util.extend({down:this.downFeature,move:this.moveFeature,
+up:this.upFeature,out:this.cancel,done:this.doneDragging},this.dragCallbacks),{documentDrag:this.documentDrag}),feature:new OpenLayers.Handler.Feature(this,this.layer,OpenLayers.Util.extend({click:this.clickFeature,clickout:this.clickoutFeature,over:this.overFeature,out:this.outFeature},this.featureCallbacks),{geometryTypes:this.geometryTypes})}},clickFeature:function(a){this.handlers.feature.touch&&(!this.over&&this.overFeature(a))&&(this.handlers.drag.dragstart(this.handlers.feature.evt),this.handlers.drag.stopDown=
+!1)},clickoutFeature:function(a){this.handlers.feature.touch&&this.over&&(this.outFeature(a),this.handlers.drag.stopDown=!0)},destroy:function(){this.layer=null;OpenLayers.Control.prototype.destroy.apply(this,[])},activate:function(){return this.handlers.feature.activate()&&OpenLayers.Control.prototype.activate.apply(this,arguments)},deactivate:function(){this.handlers.drag.deactivate();this.handlers.feature.deactivate();this.feature=null;this.dragging=!1;this.lastPixel=null;OpenLayers.Element.removeClass(this.map.viewPortDiv,
+this.displayClass+"Over");return OpenLayers.Control.prototype.deactivate.apply(this,arguments)},overFeature:function(a){var b=!1;this.handlers.drag.dragging?this.over=this.feature.id==a.id?!0:!1:(this.feature=a,this.handlers.drag.activate(),this.over=b=!0,OpenLayers.Element.addClass(this.map.viewPortDiv,this.displayClass+"Over"),this.onEnter(a));return b},downFeature:function(a){this.lastPixel=a;this.onStart(this.feature,a)},moveFeature:function(a){var b=this.map.getResolution();this.feature.geometry.move(b*
+(a.x-this.lastPixel.x),b*(this.lastPixel.y-a.y));this.layer.drawFeature(this.feature);this.lastPixel=a;this.onDrag(this.feature,a)},upFeature:function(){this.over||this.handlers.drag.deactivate()},doneDragging:function(a){this.onComplete(this.feature,a)},outFeature:function(a){this.handlers.drag.dragging?this.feature.id==a.id&&(this.over=!1):(this.over=!1,this.handlers.drag.deactivate(),OpenLayers.Element.removeClass(this.map.viewPortDiv,this.displayClass+"Over"),this.onLeave(a),this.feature=null)},
+cancel:function(){this.handlers.drag.deactivate();this.over=!1},setMap:function(a){this.handlers.drag.setMap(a);this.handlers.feature.setMap(a);OpenLayers.Control.prototype.setMap.apply(this,arguments)},CLASS_NAME:"OpenLayers.Control.DragFeature"});OpenLayers.StyleMap=OpenLayers.Class({styles:null,extendDefault:!0,initialize:function(a,b){this.styles={"default":new OpenLayers.Style(OpenLayers.Feature.Vector.style["default"]),select:new OpenLayers.Style(OpenLayers.Feature.Vector.style.select),temporary:new OpenLayers.Style(OpenLayers.Feature.Vector.style.temporary),"delete":new OpenLayers.Style(OpenLayers.Feature.Vector.style["delete"])};if(a instanceof OpenLayers.Style)this.styles["default"]=a,this.styles.select=a,this.styles.temporary=a,this.styles["delete"]=
+a;else if("object"==typeof a)for(var c in a)if(a[c]instanceof OpenLayers.Style)this.styles[c]=a[c];else if("object"==typeof a[c])this.styles[c]=new OpenLayers.Style(a[c]);else{this.styles["default"]=new OpenLayers.Style(a);this.styles.select=new OpenLayers.Style(a);this.styles.temporary=new OpenLayers.Style(a);this.styles["delete"]=new OpenLayers.Style(a);break}OpenLayers.Util.extend(this,b)},destroy:function(){for(var a in this.styles)this.styles[a].destroy();this.styles=null},createSymbolizer:function(a,
+b){a||(a=new OpenLayers.Feature.Vector);this.styles[b]||(b="default");a.renderIntent=b;var c={};this.extendDefault&&"default"!=b&&(c=this.styles["default"].createSymbolizer(a));return OpenLayers.Util.extend(c,this.styles[b].createSymbolizer(a))},addUniqueValueRules:function(a,b,c,d){var e=[],f;for(f in c)e.push(new OpenLayers.Rule({symbolizer:c[f],context:d,filter:new OpenLayers.Filter.Comparison({type:OpenLayers.Filter.Comparison.EQUAL_TO,property:b,value:f})}));this.styles[a].addRules(e)},CLASS_NAME:"OpenLayers.StyleMap"});OpenLayers.Layer.Vector=OpenLayers.Class(OpenLayers.Layer,{isBaseLayer:!1,isFixed:!1,features:null,filter:null,selectedFeatures:null,unrenderedFeatures:null,reportError:!0,style:null,styleMap:null,strategies:null,protocol:null,renderers:["SVG","VML","Canvas"],renderer:null,rendererOptions:null,geometryType:null,drawn:!1,ratio:1,initialize:function(a,b){OpenLayers.Layer.prototype.initialize.apply(this,arguments);(!this.renderer||!this.renderer.supported())&&this.assignRenderer();if(!this.renderer||
+!this.renderer.supported())this.renderer=null,this.displayError();this.styleMap||(this.styleMap=new OpenLayers.StyleMap);this.features=[];this.selectedFeatures=[];this.unrenderedFeatures={};if(this.strategies)for(var c=0,d=this.strategies.length;c
${copyrights}Terms of Use',
+metadata:null,type:"Road",culture:"en-US",metadataParams:null,tileOptions:null,initialize:function(a){a=OpenLayers.Util.applyDefaults({sphericalMercator:!0},a);OpenLayers.Layer.XYZ.prototype.initialize.apply(this,[a.name||"Bing "+(a.type||this.type),null,a]);this.tileOptions=OpenLayers.Util.extend({crossOriginKeyword:"anonymous"},this.options.tileOptions);this.loadMetadata()},loadMetadata:function(){this._callbackId="_callback_"+this.id.replace(/\./g,"_");window[this._callbackId]=OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata,
+this);var a=OpenLayers.Util.applyDefaults({key:this.key,jsonp:this._callbackId,include:"ImageryProviders"},this.metadataParams),a="http://dev.virtualearth.net/REST/v1/Imagery/Metadata/"+this.type+"?"+OpenLayers.Util.getParameterString(a),b=document.createElement("script");b.type="text/javascript";b.src=a;b.id=this._callbackId;document.getElementsByTagName("head")[0].appendChild(b)},initLayer:function(){var a=this.metadata.resourceSets[0].resources[0],b=a.imageUrl.replace("{quadkey}","${quadkey}"),
+b=b.replace("{culture}",this.culture);this.url=[];for(var c=0;c=l.zoomMin)&&(e+=f.attribution+" ")}this.attribution=OpenLayers.String.format(this.attributionTemplate,{type:this.type.toLowerCase(),logo:a.brandLogoUri,copyrights:e});this.map&&this.map.events.triggerEvent("changelayer",{layer:this,property:"attribution"})}},setMap:function(){OpenLayers.Layer.XYZ.prototype.setMap.apply(this,arguments);this.updateAttribution();this.map.events.register("moveend",
+this,this.updateAttribution)},clone:function(a){null==a&&(a=new OpenLayers.Layer.Bing(this.options));return a=OpenLayers.Layer.XYZ.prototype.clone.apply(this,[a])},destroy:function(){this.map&&this.map.events.unregister("moveend",this,this.updateAttribution);OpenLayers.Layer.XYZ.prototype.destroy.apply(this,arguments)},CLASS_NAME:"OpenLayers.Layer.Bing"});
+OpenLayers.Layer.Bing.processMetadata=function(a){this.metadata=a;this.initLayer();a=document.getElementById(this._callbackId);a.parentNode.removeChild(a);window[this._callbackId]=void 0;delete this._callbackId};OpenLayers.Layer.PointGrid=OpenLayers.Class(OpenLayers.Layer.Vector,{dx:null,dy:null,ratio:1.5,maxFeatures:250,rotation:0,origin:null,gridBounds:null,initialize:function(a){a=a||{};OpenLayers.Layer.Vector.prototype.initialize.apply(this,[a.name,a])},setMap:function(a){OpenLayers.Layer.Vector.prototype.setMap.apply(this,arguments);a.events.register("moveend",this,this.onMoveEnd)},removeMap:function(a){a.events.unregister("moveend",this,this.onMoveEnd);OpenLayers.Layer.Vector.prototype.removeMap.apply(this,
+arguments)},setRatio:function(a){this.ratio=a;this.updateGrid(!0)},setMaxFeatures:function(a){this.maxFeatures=a;this.updateGrid(!0)},setSpacing:function(a,b){this.dx=a;this.dy=b||a;this.updateGrid(!0)},setOrigin:function(a){this.origin=a;this.updateGrid(!0)},getOrigin:function(){this.origin||(this.origin=this.map.getExtent().getCenterLonLat());return this.origin},setRotation:function(a){this.rotation=a;this.updateGrid(!0)},onMoveEnd:function(){this.updateGrid()},getViewBounds:function(){var a=this.map.getExtent();
+if(this.rotation){var b=this.getOrigin(),b=new OpenLayers.Geometry.Point(b.lon,b.lat),a=a.toGeometry();a.rotate(-this.rotation,b);a=a.getBounds()}return a},updateGrid:function(a){if(a||this.invalidBounds()){var b=this.getViewBounds(),c=this.getOrigin(),a=new OpenLayers.Geometry.Point(c.lon,c.lat),d=b.getWidth(),e=b.getHeight(),f=d/e,g=Math.sqrt(this.dx*this.dy*this.maxFeatures/f),d=Math.min(d*this.ratio,g*f),e=Math.min(e*this.ratio,g),b=b.getCenterLonLat();this.gridBounds=new OpenLayers.Bounds(b.lon-
+d/2,b.lat-e/2,b.lon+d/2,b.lat+e/2);for(var b=Math.floor(e/this.dy),d=Math.floor(d/this.dx),e=c.lon+this.dx*Math.ceil((this.gridBounds.left-c.lon)/this.dx),c=c.lat+this.dy*Math.ceil((this.gridBounds.bottom-c.lat)/this.dy),g=Array(b*d),h,i=0;iwindow.opera.version()&&(b=-b)):a.detail&&(b=-a.detail/
+3),this.delta+=b,this.interval)?(window.clearTimeout(this._timeoutId),this._timeoutId=window.setTimeout(OpenLayers.Function.bind(function(){this.wheelZoom(a)},this),this.interval)):this.wheelZoom(a)),OpenLayers.Event.stop(a))}},wheelZoom:function(a){var b=this.delta;this.delta=0;b&&(this.mousePosition&&(a.xy=this.mousePosition),a.xy||(a.xy=this.map.getPixelFromLonLat(this.map.getCenter())),0>b?this.callback("down",[a,this.cumulative?b:-1]):this.callback("up",[a,this.cumulative?b:1]))},mousemove:function(a){this.mousePosition=
+a.xy},activate:function(a){if(OpenLayers.Handler.prototype.activate.apply(this,arguments)){var b=this.wheelListener;OpenLayers.Event.observe(window,"DOMMouseScroll",b);OpenLayers.Event.observe(window,"mousewheel",b);OpenLayers.Event.observe(document,"mousewheel",b);return!0}return!1},deactivate:function(a){if(OpenLayers.Handler.prototype.deactivate.apply(this,arguments)){var b=this.wheelListener;OpenLayers.Event.stopObserving(window,"DOMMouseScroll",b);OpenLayers.Event.stopObserving(window,"mousewheel",
+b);OpenLayers.Event.stopObserving(document,"mousewheel",b);return!0}return!1},CLASS_NAME:"OpenLayers.Handler.MouseWheel"});OpenLayers.Symbolizer=OpenLayers.Class({zIndex:0,initialize:function(a){OpenLayers.Util.extend(this,a)},clone:function(){return new (eval(this.CLASS_NAME))(OpenLayers.Util.extend({},this))},CLASS_NAME:"OpenLayers.Symbolizer"});OpenLayers.Symbolizer.Raster=OpenLayers.Class(OpenLayers.Symbolizer,{initialize:function(a){OpenLayers.Symbolizer.prototype.initialize.apply(this,arguments)},CLASS_NAME:"OpenLayers.Symbolizer.Raster"});OpenLayers.Rule=OpenLayers.Class({id:null,name:null,title:null,description:null,context:null,filter:null,elseFilter:!1,symbolizer:null,symbolizers:null,minScaleDenominator:null,maxScaleDenominator:null,initialize:function(a){this.symbolizer={};OpenLayers.Util.extend(this,a);this.symbolizers&&delete this.symbolizer;this.id=OpenLayers.Util.createUniqueID(this.CLASS_NAME+"_")},destroy:function(){for(var a in this.symbolizer)this.symbolizer[a]=null;this.symbolizer=null;delete this.symbolizers},evaluate:function(a){var b=
+this.getContext(a),c=!0;if(this.minScaleDenominator||this.maxScaleDenominator)var d=a.layer.map.getScale();this.minScaleDenominator&&(c=d>=OpenLayers.Style.createLiteral(this.minScaleDenominator,b));c&&this.maxScaleDenominator&&(c=dthis.maxZIndex&&(this.maxZIndex=b)},getNextElement:function(a){a+=1;if(aa.left&&f.righta.left&&f.lefta.length)throw Error("At least two point features have to be added to create a line from");for(var c=Array(a.length-1),d,e,f,g=0,h=a.length;gthis.opacity&&a.setOpacity(this.opacity);this.map&&this.map.getExtent()&&(a.map=this.map,this.drawMarker(a))},removeMarker:function(a){this.markers&&this.markers.length&&(OpenLayers.Util.removeItem(this.markers,a),a.erase())},clearMarkers:function(){if(null!=this.markers)for(;0=a.maxResolution)return null;for(var c=
+{node:this.getGeoTolerance(a.nodeTolerance,c),vertex:this.getGeoTolerance(a.vertexTolerance,c),edge:this.getGeoTolerance(a.edgeTolerance,c)},d=Math.max(c.node,c.vertex,c.edge),e={rank:Number.POSITIVE_INFINITY,dist:Number.POSITIVE_INFINITY},f=!1,g=a.layer.features,h,i,j,k,l,m,n=this.precedence.length,o=new OpenLayers.LonLat(b.x,b.y),p=0,q=g.length;p4?this._object.open(a,f,i,m,n):arguments.length>3?this._object.open(a,f,i,m):this._object.open(a,f,i);this.readyState=b.OPENED;c(this);this._object.onreadystatechange=function(){if(!g||i){o.readyState=o._object.readyState;d(o);if(o._aborted)o.readyState=b.UNSENT;else{if(o.readyState==b.DONE){delete o._data;e(o);h&&i&&window.detachEvent("onunload",
+q)}p!=o.readyState&&c(o);p=o.readyState}}}};b.prototype.send=function(a){b.onsend&&b.onsend.apply(this,arguments);arguments.length||(a=null);if(a&&a.nodeType){a=window.XMLSerializer?(new window.XMLSerializer).serializeToString(a):a.xml;this._headers["Content-Type"]||this._object.setRequestHeader("Content-Type","application/xml")}this._data=a;a:{this._object.send(this._data);if(g&&!this._async){this.readyState=b.OPENED;for(d(this);this.readyStateb.UNSENT)this._aborted=true;this._object.abort();e(this);this.readyState=b.UNSENT;delete this._data};b.prototype.getAllResponseHeaders=function(){return this._object.getAllResponseHeaders()};b.prototype.getResponseHeader=function(a){return this._object.getResponseHeader(a)};b.prototype.setRequestHeader=function(a,b){if(!this._headers)this._headers={};this._headers[a]=b;return this._object.setRequestHeader(a,
+b)};b.prototype.addEventListener=function(a,b,c){for(var d=0,e;e=this._listeners[d];d++)if(e[0]==a&&e[1]==b&&e[2]==c)return;this._listeners.push([a,b,c])};b.prototype.removeEventListener=function(a,b,c){for(var d=0,e;e=this._listeners[d];d++)if(e[0]==a&&e[1]==b&&e[2]==c)break;e&&this._listeners.splice(d,1)};b.prototype.dispatchEvent=function(a){a={type:a.type,target:this,currentTarget:this,eventPhase:2,bubbles:a.bubbles,cancelable:a.cancelable,timeStamp:a.timeStamp,stopPropagation:function(){},preventDefault:function(){},
+initEvent:function(){}};a.type=="readystatechange"&&this.onreadystatechange&&(this.onreadystatechange.handleEvent||this.onreadystatechange).apply(this,[a]);for(var b=0,c;c=this._listeners[b];b++)c[0]==a.type&&!c[2]&&(c[1].handleEvent||c[1]).apply(this,[a])};b.prototype.toString=function(){return"[object XMLHttpRequest]"};b.toString=function(){return"[XMLHttpRequest]"};window.Function.prototype.apply||(window.Function.prototype.apply=function(a,b){b||(b=[]);a.__func=this;a.__func(b[0],b[1],b[2],b[3],
+b[4]);delete a.__func});OpenLayers.Request.XMLHttpRequest=b})();OpenLayers.Format.KML=OpenLayers.Class(OpenLayers.Format.XML,{namespaces:{kml:"http://www.opengis.net/kml/2.2",gx:"http://www.google.com/kml/ext/2.2"},kmlns:"http://earth.google.com/kml/2.0",placemarksDesc:"No description available",foldersName:"OpenLayers export",foldersDesc:"Exported on "+new Date,extractAttributes:!0,kvpAttributes:!1,extractStyles:!1,extractTracks:!1,trackAttributes:null,internalns:null,features:null,styles:null,styleBaseUrl:"",fetched:null,maxDepth:0,initialize:function(a){this.regExes=
+{trimSpace:/^\s*|\s*$/g,removeSpace:/\s*/g,splitSpace:/\s+/,trimComma:/\s*,\s*/g,kmlColor:/(\w{2})(\w{2})(\w{2})(\w{2})/,kmlIconPalette:/root:\/\/icons\/palette-(\d+)(\.\w+)/,straightBracket:/\$\[(.*?)\]/g};this.externalProjection=new OpenLayers.Projection("EPSG:4326");OpenLayers.Format.XML.prototype.initialize.apply(this,[a])},read:function(a){this.features=[];this.styles={};this.fetched={};return this.parseData(a,{depth:0,styleBaseUrl:this.styleBaseUrl})},parseData:function(a,b){"string"==typeof a&&
+(a=OpenLayers.Format.XML.prototype.read.apply(this,[a]));for(var c=["Link","NetworkLink","Style","StyleMap","Placemark"],d=0,e=c.length;d=this.maxDepth)return!1;var c=OpenLayers.Util.extend({},b);c.depth++;for(var d=0,e=a.length;d=e.length)){switch(e.length){case 1:f=e[0];break;case 2:f=e[0];e=e[1];f=3==f.nodeType||4==f.nodeType?f:e;break;default:f=e[1]}if(3==f.nodeType||4==f.nodeType)if(d=d.prefix?d.nodeName.split(":")[1]:d.nodeName,f=OpenLayers.Util.getXmlNodeValue(f))f=f.replace(this.regExes.trimSpace,""),b[d]=f}return b},parseExtendedData:function(a){var b={},c,d,e,f,g=a.getElementsByTagName("Data");c=0;for(d=g.length;c"+this.contentDiv.innerHTML+"",b=this.map?this.map.div:document.body,c=OpenLayers.Util.getRenderedDimensions(a,
+null,{displayClass:this.displayClass,containerElement:b}),d=this.getSafeContentSize(c),e=null;d.equals(c)?e=c:(c={w:d.wa.w-this.map.paddingForPopups.right&&(c.x=a.w-this.map.paddingForPopups.right-this.size.w);
+b.ya.h-this.map.paddingForPopups.bottom&&(c.y=a.h-this.map.paddingForPopups.bottom-this.size.h);this.map.pan(b.x-c.x,b.y-c.y)},registerEvents:function(){this.events=new OpenLayers.Events(this,this.div,null,!0);this.events.on({mousedown:this.onmousedown,mousemove:this.onmousemove,mouseup:this.onmouseup,click:this.onclick,mouseout:this.onmouseout,dblclick:this.ondblclick,touchstart:function(a){OpenLayers.Event.stop(a,!0)},
+scope:this})},onmousedown:function(a){this.mousedown=!0;OpenLayers.Event.stop(a,!0)},onmousemove:function(a){this.mousedown&&OpenLayers.Event.stop(a,!0)},onmouseup:function(a){this.mousedown&&(this.mousedown=!1,OpenLayers.Event.stop(a,!0))},onclick:function(a){OpenLayers.Event.stop(a,!0)},onmouseout:function(){this.mousedown=!1},ondblclick:function(a){OpenLayers.Event.stop(a,!0)},CLASS_NAME:"OpenLayers.Popup"});OpenLayers.Popup.WIDTH=200;OpenLayers.Popup.HEIGHT=200;OpenLayers.Popup.COLOR="white";
+OpenLayers.Popup.OPACITY=1;OpenLayers.Popup.BORDER="0px";OpenLayers.Popup.Anchored=OpenLayers.Class(OpenLayers.Popup,{relativePosition:null,keepInMap:!0,anchor:null,initialize:function(a,b,c,d,e,f,g){OpenLayers.Popup.prototype.initialize.apply(this,[a,b,c,d,f,g]);this.anchor=null!=e?e:{size:new OpenLayers.Size(0,0),offset:new OpenLayers.Pixel(0,0)}},destroy:function(){this.relativePosition=this.anchor=null;OpenLayers.Popup.prototype.destroy.apply(this,arguments)},show:function(){this.updatePosition();OpenLayers.Popup.prototype.show.apply(this,arguments)},
+moveTo:function(a){var b=this.relativePosition;this.relativePosition=this.calculateRelativePosition(a);a=this.calculateNewPx(a);OpenLayers.Popup.prototype.moveTo.apply(this,Array(a));this.relativePosition!=b&&this.updateRelativePosition()},setSize:function(a){OpenLayers.Popup.prototype.setSize.apply(this,arguments);this.lonlat&&this.map&&this.moveTo(this.map.getLayerPxFromLonLat(this.lonlat))},calculateRelativePosition:function(a){a=this.map.getLonLatFromLayerPx(a);a=this.map.getExtent().determineQuadrant(a);
+return OpenLayers.Bounds.oppositeQuadrant(a)},updateRelativePosition:function(){},calculateNewPx:function(a){var a=a.offset(this.anchor.offset),b=this.size||this.contentSize,c="t"==this.relativePosition.charAt(0);a.y+=c?-b.h:this.anchor.size.h;c="l"==this.relativePosition.charAt(1);a.x+=c?-b.w:this.anchor.size.w;return a},CLASS_NAME:"OpenLayers.Popup.Anchored"});/*
+ Apache 2
+
+ Contains portions of Rico
+
+ Copyright 2005 Sabre Airline Solutions
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you
+ may not use this file except in compliance with the License. You
+ may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ permissions and limitations under the License.
+*/
+OpenLayers.Console.warn("OpenLayers.Rico is deprecated");OpenLayers.Rico=OpenLayers.Rico||{};
+OpenLayers.Rico.Color=OpenLayers.Class({initialize:function(a,b,c){this.rgb={r:a,g:b,b:c}},setRed:function(a){this.rgb.r=a},setGreen:function(a){this.rgb.g=a},setBlue:function(a){this.rgb.b=a},setHue:function(a){var b=this.asHSB();b.h=a;this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,b.b)},setSaturation:function(a){var b=this.asHSB();b.s=a;this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,b.b)},setBrightness:function(a){var b=this.asHSB();b.b=a;this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,b.b)},
+darken:function(a){var b=this.asHSB();this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,Math.max(b.b-a,0))},brighten:function(a){var b=this.asHSB();this.rgb=OpenLayers.Rico.Color.HSBtoRGB(b.h,b.s,Math.min(b.b+a,1))},blend:function(a){this.rgb.r=Math.floor((this.rgb.r+a.rgb.r)/2);this.rgb.g=Math.floor((this.rgb.g+a.rgb.g)/2);this.rgb.b=Math.floor((this.rgb.b+a.rgb.b)/2)},isBright:function(){this.asHSB();return 0.5c;c++)a+=b.charAt(c)+b.charAt(c);0==a.indexOf("#")&&(a=a.substring(1));b=a.substring(0,2);c=a.substring(2,4);a=a.substring(4,6);return new OpenLayers.Rico.Color(parseInt(b,16),parseInt(c,16),parseInt(a,16))};
+OpenLayers.Rico.Color.createColorFromBackground=function(a){var b=OpenLayers.Element.getStyle(OpenLayers.Util.getElement(a),"backgroundColor");return"transparent"==b&&a.parentNode?OpenLayers.Rico.Color.createColorFromBackground(a.parentNode):null==b?new OpenLayers.Rico.Color(255,255,255):0==b.indexOf("rgb(")?(a=b.substring(4,b.length-1).split(","),new OpenLayers.Rico.Color(parseInt(a[0]),parseInt(a[1]),parseInt(a[2]))):0==b.indexOf("#")?OpenLayers.Rico.Color.createFromHex(b):new OpenLayers.Rico.Color(255,
+255,255)};
+OpenLayers.Rico.Color.HSBtoRGB=function(a,b,c){var d=0,e=0,f=0;if(0==b)f=e=d=parseInt(255*c+0.5);else{var a=6*(a-Math.floor(a)),g=a-Math.floor(a),h=c*(1-b),i=c*(1-b*g),b=c*(1-b*(1-g));switch(parseInt(a)){case 0:d=255*c+0.5;e=255*b+0.5;f=255*h+0.5;break;case 1:d=255*i+0.5;e=255*c+0.5;f=255*h+0.5;break;case 2:d=255*h+0.5;e=255*c+0.5;f=255*b+0.5;break;case 3:d=255*h+0.5;e=255*i+0.5;f=255*c+0.5;break;case 4:d=255*b+0.5;e=255*h+0.5;f=255*c+0.5;break;case 5:d=255*c+0.5,e=255*h+0.5,f=255*i+0.5}}return{r:parseInt(d),g:parseInt(e),
+b:parseInt(f)}};OpenLayers.Rico.Color.RGBtoHSB=function(a,b,c){var d,e=a>b?a:b;c>e&&(e=c);var f=aa&&(a+=1)}return{h:a,s:d,b:e/255}};OpenLayers.Console.warn("OpenLayers.Rico is deprecated");OpenLayers.Rico=OpenLayers.Rico||{};
+OpenLayers.Rico.Corner={round:function(a,b){a=OpenLayers.Util.getElement(a);this._setOptions(b);var c=this.options.color;"fromElement"==this.options.color&&(c=this._background(a));var d=this.options.bgColor;"fromParent"==this.options.bgColor&&(d=this._background(a.offsetParent));this._roundCornersImpl(a,c,d)},changeColor:function(a,b){a.style.backgroundColor=b;for(var c=a.parentNode.getElementsByTagName("span"),d=0;d"+a.innerHTML+""},_roundTopCorners:function(a,b,c){for(var d=this._createCorner(c),e=0;ec.status?(c=this.parseResponse(c,b.readOptions))&&!1!==c.success?(b.readOptions&&"object"==b.readOptions.output?OpenLayers.Util.extend(a,c):a.features=c,a.code=OpenLayers.Protocol.Response.SUCCESS):(a.code=OpenLayers.Protocol.Response.FAILURE,a.error=c):a.code=OpenLayers.Protocol.Response.FAILURE;b.callback.call(b.scope,a)}},parseResponse:function(a,b){var c=a.responseXML;if(!c||!c.documentElement)c=a.responseText;if(!c||0>=c.length)return null;c=null!==this.readFormat?this.readFormat.read(c):
+this.format.read(c,b);if(!this.featureNS){var d=this.readFormat||this.format;this.featureNS=d.featureNS;d.autoConfig=!1;this.geometryName||this.setGeometryName(d.geometryName)}return c},commit:function(a,b){b=OpenLayers.Util.extend({},b);OpenLayers.Util.applyDefaults(b,this.options);var c=new OpenLayers.Protocol.Response({requestType:"commit",reqFeatures:a});c.priv=OpenLayers.Request.POST({url:b.url,headers:b.headers,data:this.format.write(a,b),callback:this.createCallback(this.handleCommit,c,b)});
+return c},handleCommit:function(a,b){if(b.callback){var c=a.priv,d=c.responseXML;if(!d||!d.documentElement)d=c.responseText;c=this.format.read(d)||{};a.insertIds=c.insertIds||[];c.success?a.code=OpenLayers.Protocol.Response.SUCCESS:(a.code=OpenLayers.Protocol.Response.FAILURE,a.error=c);b.callback.call(b.scope,a)}},filterDelete:function(a,b){b=OpenLayers.Util.extend({},b);OpenLayers.Util.applyDefaults(b,this.options);new OpenLayers.Protocol.Response({requestType:"commit"});var c=this.format.createElementNSPlus("wfs:Transaction",
+{attributes:{service:"WFS",version:this.version}}),d=this.format.createElementNSPlus("wfs:Delete",{attributes:{typeName:(b.featureNS?this.featurePrefix+":":"")+b.featureType}});b.featureNS&&d.setAttribute("xmlns:"+this.featurePrefix,b.featureNS);var e=this.format.writeNode("ogc:Filter",a);d.appendChild(e);c.appendChild(d);c=OpenLayers.Format.XML.prototype.write.apply(this.format,[c]);return OpenLayers.Request.POST({url:this.url,callback:b.callback||function(){},data:c})},abort:function(a){a&&a.priv.abort()},
+CLASS_NAME:"OpenLayers.Protocol.WFS.v1"});OpenLayers.Handler.Point=OpenLayers.Class(OpenLayers.Handler,{point:null,layer:null,multi:!1,citeCompliant:!1,mouseDown:!1,stoppedDown:null,lastDown:null,lastUp:null,persist:!1,stopDown:!1,stopUp:!1,layerOptions:null,pixelTolerance:5,touch:!1,lastTouchPx:null,initialize:function(a,b,c){if(!c||!c.layerOptions||!c.layerOptions.styleMap)this.style=OpenLayers.Util.extend(OpenLayers.Feature.Vector.style["default"],{});OpenLayers.Handler.prototype.initialize.apply(this,arguments)},activate:function(){if(!OpenLayers.Handler.prototype.activate.apply(this,
+arguments))return!1;var a=OpenLayers.Util.extend({displayInLayerSwitcher:!1,calculateInRange:OpenLayers.Function.True,wrapDateLine:this.citeCompliant},this.layerOptions);this.layer=new OpenLayers.Layer.Vector(this.CLASS_NAME,a);this.map.addLayer(this.layer);return!0},createFeature:function(a){a=this.layer.getLonLatFromViewPortPx(a);a=new OpenLayers.Geometry.Point(a.lon,a.lat);this.point=new OpenLayers.Feature.Vector(a);this.callback("create",[this.point.geometry,this.point]);this.point.geometry.clearBounds();
+this.layer.addFeatures([this.point],{silent:!0})},deactivate:function(){if(!OpenLayers.Handler.prototype.deactivate.apply(this,arguments))return!1;this.cancel();null!=this.layer.map&&(this.destroyFeature(!0),this.layer.destroy(!1));this.layer=null;this.touch=!1;return!0},destroyFeature:function(a){this.layer&&(a||!this.persist)&&this.layer.destroyFeatures();this.point=null},destroyPersistedFeature:function(){var a=this.layer;a&&1c&&(d=!1);return d},CLASS_NAME:"OpenLayers.Handler.Point"});OpenLayers.Handler.Path=OpenLayers.Class(OpenLayers.Handler.Point,{line:null,maxVertices:null,doubleTouchTolerance:20,freehand:!1,freehandToggle:"shiftKey",timerId:null,redoStack:null,createFeature:function(a){a=this.layer.getLonLatFromViewPortPx(a);a=new OpenLayers.Geometry.Point(a.lon,a.lat);this.point=new OpenLayers.Feature.Vector(a);this.line=new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([this.point.geometry]));this.callback("create",[this.point.geometry,this.getSketch()]);
+this.point.geometry.clearBounds();this.layer.addFeatures([this.line,this.point],{silent:!0})},destroyFeature:function(a){OpenLayers.Handler.Point.prototype.destroyFeature.call(this,a);this.line=null},destroyPersistedFeature:function(){var a=this.layer;a&&2this.nbPoints&&this.points.pop()},end:function(a){for(var b,c=(new Date).getTime(),d=0,e=this.points.length,f;dthis.delay)break;b=f}if(b&&(d=(new Date).getTime()-b.tick,c=Math.sqrt(Math.pow(a.x-b.xy.x,2)+Math.pow(a.y-b.xy.y,2)),d=c/d,!(0==d||d=-this.deceleration*a+c&&(OpenLayers.Animation.stop(this.timerId),this.timerId=null,l=!0);a=k-g;m=j-h;g=k;h=j;b(a,m,l)}},this))},CLASS_NAME:"OpenLayers.Kinetic"});OpenLayers.Layer.GeoRSS=OpenLayers.Class(OpenLayers.Layer.Markers,{location:null,features:null,formatOptions:null,selectedFeature:null,icon:null,popupSize:null,useFeedTitle:!0,initialize:function(a,b,c){OpenLayers.Layer.Markers.prototype.initialize.apply(this,[a,c]);this.location=b;this.features=[]},destroy:function(){OpenLayers.Layer.Markers.prototype.destroy.apply(this,arguments);this.clearFeatures();this.features=null},loadRSS:function(){this.loaded||(this.events.triggerEvent("loadstart"),OpenLayers.Request.GET({url:this.location,
+success:this.parseData,scope:this}),this.loaded=!0)},moveTo:function(a,b,c){OpenLayers.Layer.Markers.prototype.moveTo.apply(this,arguments);this.visibility&&!this.loaded&&this.loadRSS()},parseData:function(a){var b=a.responseXML;if(!b||!b.documentElement)b=OpenLayers.Format.XML.prototype.read(a.responseText);if(this.useFeedTitle){a=null;try{a=b.getElementsByTagNameNS("*","title")[0].firstChild.nodeValue}catch(c){a=b.getElementsByTagName("title")[0].firstChild.nodeValue}a&&this.setName(a)}a={};OpenLayers.Util.extend(a,
+this.formatOptions);this.map&&!this.projection.equals(this.map.getProjectionObject())&&(a.externalProjection=this.projection,a.internalProjection=this.map.getProjectionObject());for(var b=(new OpenLayers.Format.GeoRSS(a)).read(b),a=0,d=b.length;a';i&&(j+='');j+=g;i&&(j+="");j+="";j+='