From ac6345c5b5f0cb92330d38d2a05e180396c54c88 Mon Sep 17 00:00:00 2001 From: Kristjan Komlosi Date: Mon, 13 Jul 2020 13:42:08 +0200 Subject: [PATCH] modify gitignore --- .gitignore | 4 +- terahz/__init_.py | 2 +- terahz/templates/lib/bootstrap.bundle.min.js | 7 + terahz/templates/lib/bootstrap.min.css | 7 + .../lib/flot/jquery.canvaswrapper.js | 550 + .../templates/lib/flot/jquery.colorhelpers.js | 199 + .../lib/flot/jquery.flot.axislabels.js | 212 + .../templates/lib/flot/jquery.flot.browser.js | 98 + .../lib/flot/jquery.flot.categories.js | 202 + .../lib/flot/jquery.flot.composeImages.js | 330 + .../lib/flot/jquery.flot.crosshair.js | 202 + .../lib/flot/jquery.flot.drawSeries.js | 663 ++ .../lib/flot/jquery.flot.errorbars.js | 375 + .../lib/flot/jquery.flot.fillbetween.js | 254 + .../lib/flot/jquery.flot.flatdata.js | 47 + .../templates/lib/flot/jquery.flot.hover.js | 350 + .../templates/lib/flot/jquery.flot.image.js | 249 + terahz/templates/lib/flot/jquery.flot.js | 2787 +++++ .../templates/lib/flot/jquery.flot.legend.js | 437 + .../templates/lib/flot/jquery.flot.logaxis.js | 298 + .../lib/flot/jquery.flot.navigate.js | 798 ++ terahz/templates/lib/flot/jquery.flot.pie.js | 786 ++ .../templates/lib/flot/jquery.flot.resize.js | 60 + .../lib/flot/jquery.flot.saturated.js | 43 + .../lib/flot/jquery.flot.selection.js | 517 + .../templates/lib/flot/jquery.flot.stack.js | 220 + .../templates/lib/flot/jquery.flot.symbol.js | 98 + .../lib/flot/jquery.flot.threshold.js | 143 + terahz/templates/lib/flot/jquery.flot.time.js | 585 + .../templates/lib/flot/jquery.flot.touch.js | 320 + .../lib/flot/jquery.flot.touchNavigate.js | 360 + .../lib/flot/jquery.flot.uiConstants.js | 10 + terahz/templates/lib/flot/jquery.js | 9473 +++++++++++++++++ terahz/templates/lib/jquery-3.4.1.min.js | 2 + terahz/templates/lib/logo-sq.png | Bin 0 -> 138772 bytes 35 files changed, 20685 insertions(+), 3 deletions(-) create mode 100644 terahz/templates/lib/bootstrap.bundle.min.js create mode 100644 terahz/templates/lib/bootstrap.min.css create mode 100644 terahz/templates/lib/flot/jquery.canvaswrapper.js create mode 100644 terahz/templates/lib/flot/jquery.colorhelpers.js create mode 100644 terahz/templates/lib/flot/jquery.flot.axislabels.js create mode 100644 terahz/templates/lib/flot/jquery.flot.browser.js create mode 100644 terahz/templates/lib/flot/jquery.flot.categories.js create mode 100644 terahz/templates/lib/flot/jquery.flot.composeImages.js create mode 100644 terahz/templates/lib/flot/jquery.flot.crosshair.js create mode 100644 terahz/templates/lib/flot/jquery.flot.drawSeries.js create mode 100644 terahz/templates/lib/flot/jquery.flot.errorbars.js create mode 100644 terahz/templates/lib/flot/jquery.flot.fillbetween.js create mode 100644 terahz/templates/lib/flot/jquery.flot.flatdata.js create mode 100644 terahz/templates/lib/flot/jquery.flot.hover.js create mode 100644 terahz/templates/lib/flot/jquery.flot.image.js create mode 100644 terahz/templates/lib/flot/jquery.flot.js create mode 100644 terahz/templates/lib/flot/jquery.flot.legend.js create mode 100644 terahz/templates/lib/flot/jquery.flot.logaxis.js create mode 100644 terahz/templates/lib/flot/jquery.flot.navigate.js create mode 100644 terahz/templates/lib/flot/jquery.flot.pie.js create mode 100644 terahz/templates/lib/flot/jquery.flot.resize.js create mode 100644 terahz/templates/lib/flot/jquery.flot.saturated.js create mode 100644 terahz/templates/lib/flot/jquery.flot.selection.js create mode 100644 terahz/templates/lib/flot/jquery.flot.stack.js create mode 100644 terahz/templates/lib/flot/jquery.flot.symbol.js create mode 100644 terahz/templates/lib/flot/jquery.flot.threshold.js create mode 100644 terahz/templates/lib/flot/jquery.flot.time.js create mode 100644 terahz/templates/lib/flot/jquery.flot.touch.js create mode 100644 terahz/templates/lib/flot/jquery.flot.touchNavigate.js create mode 100644 terahz/templates/lib/flot/jquery.flot.uiConstants.js create mode 100644 terahz/templates/lib/flot/jquery.js create mode 100644 terahz/templates/lib/jquery-3.4.1.min.js create mode 100644 terahz/templates/lib/logo-sq.png diff --git a/.gitignore b/.gitignore index ef8154a..39f0798 100644 --- a/.gitignore +++ b/.gitignore @@ -128,5 +128,5 @@ dmypy.json # Pyre type checker .pyre/ -# Include frontend libraries though -!/frontend/lib +# Include frontend ibraries though +!terahz/templates/lib diff --git a/terahz/__init_.py b/terahz/__init_.py index f4b9ac3..35cdcec 100644 --- a/terahz/__init_.py +++ b/terahz/__init_.py @@ -2,7 +2,7 @@ '''Main TeraHz backend program''' # All code in this file is licensed under the ISC license, provided in LICENSE.txt from flask import Flask, jsonify -import sensors +import .sensors app = Flask(__name__) @app.route('/data') diff --git a/terahz/templates/lib/bootstrap.bundle.min.js b/terahz/templates/lib/bootstrap.bundle.min.js new file mode 100644 index 0000000..4320368 --- /dev/null +++ b/terahz/templates/lib/bootstrap.bundle.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],e):e((t=t||self).bootstrap={},t.jQuery)}(this,function(t,p){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)p(this._element).one(q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=n=i.clientWidth&&n>=i.clientHeight}),h=0l[t]&&!i.escapeWithReference&&(n=Math.min(h[e],l[t]-("right"===t?h.width:h.height))),Kt({},e,n)}};return c.forEach(function(t){var e=-1!==["left","top"].indexOf(t)?"primary":"secondary";h=Qt({},h,u[e](t))}),t.offsets.popper=h,t},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(t){var e=t.offsets,n=e.popper,i=e.reference,o=t.placement.split("-")[0],r=Math.floor,s=-1!==["top","bottom"].indexOf(o),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]r(i[a])&&(t.offsets.popper[l]=r(i[a])),t}},arrow:{order:500,enabled:!0,fn:function(t,e){var n;if(!fe(t.instance.modifiers,"arrow","keepTogether"))return t;var i=e.element;if("string"==typeof i){if(!(i=t.instance.popper.querySelector(i)))return t}else if(!t.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),t;var o=t.placement.split("-")[0],r=t.offsets,s=r.popper,a=r.reference,l=-1!==["left","right"].indexOf(o),c=l?"height":"width",h=l?"Top":"Left",u=h.toLowerCase(),f=l?"left":"top",d=l?"bottom":"right",p=Zt(i)[c];a[d]-ps[d]&&(t.offsets.popper[u]+=a[u]+p-s[d]),t.offsets.popper=Vt(t.offsets.popper);var m=a[u]+a[c]/2-p/2,g=Nt(t.instance.popper),_=parseFloat(g["margin"+h],10),v=parseFloat(g["border"+h+"Width"],10),y=m-t.offsets.popper[u]-_-v;return y=Math.max(Math.min(s[c]-p,y),0),t.arrowElement=i,t.offsets.arrow=(Kt(n={},u,Math.round(y)),Kt(n,f,""),n),t},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(p,m){if(oe(p.instance.modifiers,"inner"))return p;if(p.flipped&&p.placement===p.originalPlacement)return p;var g=Gt(p.instance.popper,p.instance.reference,m.padding,m.boundariesElement,p.positionFixed),_=p.placement.split("-")[0],v=te(_),y=p.placement.split("-")[1]||"",E=[];switch(m.behavior){case ge:E=[_,v];break;case _e:E=me(_);break;case ve:E=me(_,!0);break;default:E=m.behavior}return E.forEach(function(t,e){if(_!==t||E.length===e+1)return p;_=p.placement.split("-")[0],v=te(_);var n,i=p.offsets.popper,o=p.offsets.reference,r=Math.floor,s="left"===_&&r(i.right)>r(o.left)||"right"===_&&r(i.left)r(o.top)||"bottom"===_&&r(i.top)r(g.right),c=r(i.top)r(g.bottom),u="left"===_&&a||"right"===_&&l||"top"===_&&c||"bottom"===_&&h,f=-1!==["top","bottom"].indexOf(_),d=!!m.flipVariations&&(f&&"start"===y&&a||f&&"end"===y&&l||!f&&"start"===y&&c||!f&&"end"===y&&h);(s||u||d)&&(p.flipped=!0,(s||u)&&(_=E[e+1]),d&&(y="end"===(n=y)?"start":"start"===n?"end":n),p.placement=_+(y?"-"+y:""),p.offsets.popper=Qt({},p.offsets.popper,ee(p.instance.popper,p.offsets.reference,p.placement)),p=ie(p.instance.modifiers,p,"flip"))}),p},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(t){var e=t.placement,n=e.split("-")[0],i=t.offsets,o=i.popper,r=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return o[s?"left":"top"]=r[n]-(a?o[s?"width":"height"]:0),t.placement=te(e),t.offsets.popper=Vt(o),t}},hide:{order:800,enabled:!0,fn:function(t){if(!fe(t.instance.modifiers,"hide","preventOverflow"))return t;var e=t.offsets.reference,n=ne(t.instance.modifiers,function(t){return"preventOverflow"===t.name}).boundaries;if(e.bottomn.right||e.top>n.bottom||e.rightdocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:vn},Ln="show",xn="out",Pn={HIDE:"hide"+Tn,HIDDEN:"hidden"+Tn,SHOW:"show"+Tn,SHOWN:"shown"+Tn,INSERTED:"inserted"+Tn,CLICK:"click"+Tn,FOCUSIN:"focusin"+Tn,FOCUSOUT:"focusout"+Tn,MOUSEENTER:"mouseenter"+Tn,MOUSELEAVE:"mouseleave"+Tn},Hn="fade",jn="show",Rn=".tooltip-inner",Fn=".arrow",Mn="hover",Wn="focus",Un="click",Bn="manual",qn=function(){function i(t,e){if("undefined"==typeof be)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=p(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),p(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(p(this.getTipElement()).hasClass(jn))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),p.removeData(this.element,this.constructor.DATA_KEY),p(this.element).off(this.constructor.EVENT_KEY),p(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&p(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===p(this.element).css("display"))throw new Error("Please use show on visible elements");var t=p.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){p(this.element).trigger(t);var n=m.findShadowRoot(this.element),i=p.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=m.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&p(o).addClass(Hn);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();p(o).data(this.constructor.DATA_KEY,this),p.contains(this.element.ownerDocument.documentElement,this.tip)||p(o).appendTo(l),p(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new be(this.element,o,{placement:a,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Fn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}}),p(o).addClass(jn),"ontouchstart"in document.documentElement&&p(document.body).children().on("mouseover",null,p.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,p(e.element).trigger(e.constructor.Event.SHOWN),t===xn&&e._leave(null,e)};if(p(this.tip).hasClass(Hn)){var h=m.getTransitionDurationFromElement(this.tip);p(this.tip).one(m.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=p.Event(this.constructor.Event.HIDE),o=function(){e._hoverState!==Ln&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),p(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(p(this.element).trigger(i),!i.isDefaultPrevented()){if(p(n).removeClass(jn),"ontouchstart"in document.documentElement&&p(document.body).children().off("mouseover",null,p.noop),this._activeTrigger[Un]=!1,this._activeTrigger[Wn]=!1,this._activeTrigger[Mn]=!1,p(this.tip).hasClass(Hn)){var r=m.getTransitionDurationFromElement(n);p(n).one(m.TRANSITION_END,o).emulateTransitionEnd(r)}else o();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){p(this.getTipElement()).addClass(Dn+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(p(t.querySelectorAll(Rn)),this.getTitle()),p(t).removeClass(Hn+" "+jn)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=bn(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?p(e).parent().is(t)||t.empty().append(e):t.text(p(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:m.isElement(this.config.container)?p(this.config.container):p(document).find(this.config.container)},t._getAttachment=function(t){return Nn[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)p(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==Bn){var e=t===Mn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Mn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;p(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),p(this.element).closest(".modal").on("hide.bs.modal",function(){i.element&&i.hide()}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||p(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),p(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Wn:Mn]=!0),p(e.getTipElement()).hasClass(jn)||e._hoverState===Ln?e._hoverState=Ln:(clearTimeout(e._timeout),e._hoverState=Ln,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===Ln&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||p(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),p(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Wn:Mn]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=xn,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===xn&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=p(this.element).data();return Object.keys(e).forEach(function(t){-1!==An.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),m.typeCheckConfig(wn,t,this.constructor.DefaultType),t.sanitize&&(t.template=bn(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=p(this.getTipElement()),e=t.attr("class").match(In);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(p(t).removeClass(Hn),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=p(this).data(Cn),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),p(this).data(Cn,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.3.1"}},{key:"Default",get:function(){return kn}},{key:"NAME",get:function(){return wn}},{key:"DATA_KEY",get:function(){return Cn}},{key:"Event",get:function(){return Pn}},{key:"EVENT_KEY",get:function(){return Tn}},{key:"DefaultType",get:function(){return On}}]),i}();p.fn[wn]=qn._jQueryInterface,p.fn[wn].Constructor=qn,p.fn[wn].noConflict=function(){return p.fn[wn]=Sn,qn._jQueryInterface};var Kn="popover",Qn="bs.popover",Vn="."+Qn,Yn=p.fn[Kn],zn="bs-popover",Xn=new RegExp("(^|\\s)"+zn+"\\S+","g"),Gn=l({},qn.Default,{placement:"right",trigger:"click",content:"",template:''}),$n=l({},qn.DefaultType,{content:"(string|element|function)"}),Jn="fade",Zn="show",ti=".popover-header",ei=".popover-body",ni={HIDE:"hide"+Vn,HIDDEN:"hidden"+Vn,SHOW:"show"+Vn,SHOWN:"shown"+Vn,INSERTED:"inserted"+Vn,CLICK:"click"+Vn,FOCUSIN:"focusin"+Vn,FOCUSOUT:"focusout"+Vn,MOUSEENTER:"mouseenter"+Vn,MOUSELEAVE:"mouseleave"+Vn},ii=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var o=i.prototype;return o.isWithContent=function(){return this.getTitle()||this._getContent()},o.addAttachmentClass=function(t){p(this.getTipElement()).addClass(zn+"-"+t)},o.getTipElement=function(){return this.tip=this.tip||p(this.config.template)[0],this.tip},o.setContent=function(){var t=p(this.getTipElement());this.setElementContent(t.find(ti),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(ei),e),t.removeClass(Jn+" "+Zn)},o._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},o._cleanTipClass=function(){var t=p(this.getTipElement()),e=t.attr("class").match(Xn);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||tcode{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-sm .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-md .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-lg .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-xl .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #dee2e6;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:0s .6s opacity}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/terahz/templates/lib/flot/jquery.canvaswrapper.js b/terahz/templates/lib/flot/jquery.canvaswrapper.js new file mode 100644 index 0000000..355fddf --- /dev/null +++ b/terahz/templates/lib/flot/jquery.canvaswrapper.js @@ -0,0 +1,550 @@ +/** ## jquery.flot.canvaswrapper + +This plugin contains the function for creating and manipulating both the canvas +layers and svg layers. + +The Canvas object is a wrapper around an HTML5 canvas tag. +The constructor Canvas(cls, container) takes as parameters cls, +the list of classes to apply to the canvas adnd the containter, +element onto which to append the canvas. The canvas operations +don't work unless the canvas is attached to the DOM. + +### jquery.canvaswrapper.js API functions +*/ + +(function($) { + var Canvas = function(cls, container) { + var element = container.getElementsByClassName(cls)[0]; + + if (!element) { + element = document.createElement('canvas'); + element.className = cls; + element.style.direction = 'ltr'; + element.style.position = 'absolute'; + element.style.left = '0px'; + element.style.top = '0px'; + + container.appendChild(element); + + // If HTML5 Canvas isn't available, throw + + if (!element.getContext) { + throw new Error('Canvas is not available.'); + } + } + + this.element = element; + + var context = this.context = element.getContext('2d'); + this.pixelRatio = $.plot.browser.getPixelRatio(context); + + // Size the canvas to match the internal dimensions of its container + var width = $(container).width(); + var height = $(container).height(); + this.resize(width, height); + + // Collection of HTML div layers for text overlaid onto the canvas + + this.SVGContainer = null; + this.SVG = {}; + + // Cache of text fragments and metrics, so we can avoid expensively + // re-calculating them when the plot is re-rendered in a loop. + + this._textCache = {}; + } + + /** + - resize(width, height) + + Resizes the canvas to the given dimensions. + The width represents the new width of the canvas, meanwhile the height + is the new height of the canvas, both of them in pixels. + */ + + Canvas.prototype.resize = function(width, height) { + var minSize = 10; + width = width < minSize ? minSize : width; + height = height < minSize ? minSize : height; + + var element = this.element, + context = this.context, + pixelRatio = this.pixelRatio; + + // Resize the canvas, increasing its density based on the display's + // pixel ratio; basically giving it more pixels without increasing the + // size of its element, to take advantage of the fact that retina + // displays have that many more pixels in the same advertised space. + + // Resizing should reset the state (excanvas seems to be buggy though) + + if (this.width !== width) { + element.width = width * pixelRatio; + element.style.width = width + 'px'; + this.width = width; + } + + if (this.height !== height) { + element.height = height * pixelRatio; + element.style.height = height + 'px'; + this.height = height; + } + + // Save the context, so we can reset in case we get replotted. The + // restore ensure that we're really back at the initial state, and + // should be safe even if we haven't saved the initial state yet. + + context.restore(); + context.save(); + + // Scale the coordinate space to match the display density; so even though we + // may have twice as many pixels, we still want lines and other drawing to + // appear at the same size; the extra pixels will just make them crisper. + + context.scale(pixelRatio, pixelRatio); + }; + + /** + - clear() + + Clears the entire canvas area, not including any overlaid HTML text + */ + Canvas.prototype.clear = function() { + this.context.clearRect(0, 0, this.width, this.height); + }; + + /** + - render() + + Finishes rendering the canvas, including managing the text overlay. + */ + Canvas.prototype.render = function() { + var cache = this._textCache; + + // For each text layer, add elements marked as active that haven't + // already been rendered, and remove those that are no longer active. + + for (var layerKey in cache) { + if (hasOwnProperty.call(cache, layerKey)) { + var layer = this.getSVGLayer(layerKey), + layerCache = cache[layerKey]; + + var display = layer.style.display; + layer.style.display = 'none'; + + for (var styleKey in layerCache) { + if (hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey]; + for (var key in styleCache) { + if (hasOwnProperty.call(styleCache, key)) { + var val = styleCache[key], + positions = val.positions; + + for (var i = 0, position; positions[i]; i++) { + position = positions[i]; + if (position.active) { + if (!position.rendered) { + layer.appendChild(position.element); + position.rendered = true; + } + } else { + positions.splice(i--, 1); + if (position.rendered) { + while (position.element.firstChild) { + position.element.removeChild(position.element.firstChild); + } + position.element.parentNode.removeChild(position.element); + } + } + } + + if (positions.length === 0) { + if (val.measured) { + val.measured = false; + } else { + delete styleCache[key]; + } + } + } + } + } + } + + layer.style.display = display; + } + } + }; + + /** + - getSVGLayer(classes) + + Creates (if necessary) and returns the SVG overlay container. + The classes string represents the string of space-separated CSS classes + used to uniquely identify the text layer. It return the svg-layer div. + */ + Canvas.prototype.getSVGLayer = function(classes) { + var layer = this.SVG[classes]; + + // Create the SVG layer if it doesn't exist + + if (!layer) { + // Create the svg layer container, if it doesn't exist + + var svgElement; + + if (!this.SVGContainer) { + this.SVGContainer = document.createElement('div'); + this.SVGContainer.className = 'flot-svg'; + this.SVGContainer.style.position = 'absolute'; + this.SVGContainer.style.top = '0px'; + this.SVGContainer.style.left = '0px'; + this.SVGContainer.style.height = '100%'; + this.SVGContainer.style.width = '100%'; + this.SVGContainer.style.pointerEvents = 'none'; + this.element.parentNode.appendChild(this.SVGContainer); + + svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svgElement.style.width = '100%'; + svgElement.style.height = '100%'; + + this.SVGContainer.appendChild(svgElement); + } else { + svgElement = this.SVGContainer.firstChild; + } + + layer = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + layer.setAttribute('class', classes); + layer.style.position = 'absolute'; + layer.style.top = '0px'; + layer.style.left = '0px'; + layer.style.bottom = '0px'; + layer.style.right = '0px'; + svgElement.appendChild(layer); + this.SVG[classes] = layer; + } + + return layer; + }; + + /** + - getTextInfo(layer, text, font, angle, width) + + Creates (if necessary) and returns a text info object. + The object looks like this: + ```js + { + width //Width of the text's wrapper div. + height //Height of the text's wrapper div. + element //The HTML div containing the text. + positions //Array of positions at which this text is drawn. + } + ``` + The positions array contains objects that look like this: + ```js + { + active //Flag indicating whether the text should be visible. + rendered //Flag indicating whether the text is currently visible. + element //The HTML div containing the text. + text //The actual text and is identical with element[0].textContent. + x //X coordinate at which to draw the text. + y //Y coordinate at which to draw the text. + } + ``` + Each position after the first receives a clone of the original element. + The idea is that that the width, height, and general 'identity' of the + text is constant no matter where it is placed; the placements are a + secondary property. + + Canvas maintains a cache of recently-used text info objects; getTextInfo + either returns the cached element or creates a new entry. + + The layer parameter is string of space-separated CSS classes uniquely + identifying the layer containing this text. + Text is the text string to retrieve info for. + Font is either a string of space-separated CSS classes or a font-spec object, + defining the text's font and style. + Angle is the angle at which to rotate the text, in degrees. Angle is currently unused, + it will be implemented in the future. + The last parameter is the Maximum width of the text before it wraps. + The method returns a text info object. + */ + Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { + var textStyle, layerCache, styleCache, info; + + // Cast the value to a string, in case we were given a number or such + + text = '' + text; + + // If the font is a font-spec object, generate a CSS font definition + + if (typeof font === 'object') { + textStyle = font.style + ' ' + font.variant + ' ' + font.weight + ' ' + font.size + 'px/' + font.lineHeight + 'px ' + font.family; + } else { + textStyle = font; + } + + // Retrieve (or create) the cache for the text's layer and styles + + layerCache = this._textCache[layer]; + + if (layerCache == null) { + layerCache = this._textCache[layer] = {}; + } + + styleCache = layerCache[textStyle]; + + if (styleCache == null) { + styleCache = layerCache[textStyle] = {}; + } + + var key = generateKey(text); + info = styleCache[key]; + + // If we can't find a matching element in our cache, create a new one + + if (!info) { + var element = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + if (text.indexOf('
') !== -1) { + addTspanElements(text, element, -9999); + } else { + var textNode = document.createTextNode(text); + element.appendChild(textNode); + } + + element.style.position = 'absolute'; + element.style.maxWidth = width; + element.setAttributeNS(null, 'x', -9999); + element.setAttributeNS(null, 'y', -9999); + + if (typeof font === 'object') { + element.style.font = textStyle; + element.style.fill = font.fill; + } else if (typeof font === 'string') { + element.setAttribute('class', font); + } + + this.getSVGLayer(layer).appendChild(element); + var elementRect = element.getBBox(); + + info = styleCache[key] = { + width: elementRect.width, + height: elementRect.height, + measured: true, + element: element, + positions: [] + }; + + //remove elements from dom + while (element.firstChild) { + element.removeChild(element.firstChild); + } + element.parentNode.removeChild(element); + } + + info.measured = true; + return info; + }; + + function updateTransforms (element, transforms) { + element.transform.baseVal.clear(); + if (transforms) { + transforms.forEach(function(t) { + element.transform.baseVal.appendItem(t); + }); + } + } + + /** + - addText (layer, x, y, text, font, angle, width, halign, valign, transforms) + + Adds a text string to the canvas text overlay. + The text isn't drawn immediately; it is marked as rendering, which will + result in its addition to the canvas on the next render pass. + + The layer is string of space-separated CSS classes uniquely + identifying the layer containing this text. + X and Y represents the X and Y coordinate at which to draw the text. + and text is the string to draw + */ + Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign, transforms) { + var info = this.getTextInfo(layer, text, font, angle, width), + positions = info.positions; + + // Tweak the div's position to match the text's alignment + + if (halign === 'center') { + x -= info.width / 2; + } else if (halign === 'right') { + x -= info.width; + } + + if (valign === 'middle') { + y -= info.height / 2; + } else if (valign === 'bottom') { + y -= info.height; + } + + y += 0.75 * info.height; + + + // Determine whether this text already exists at this position. + // If so, mark it for inclusion in the next render pass. + + for (var i = 0, position; positions[i]; i++) { + position = positions[i]; + if (position.x === x && position.y === y && position.text === text) { + position.active = true; + // update the transforms + updateTransforms(position.element, transforms); + + return; + } else if (position.active === false) { + position.active = true; + position.text = text; + if (text.indexOf('
') !== -1) { + y -= 0.25 * info.height; + addTspanElements(text, position.element, x); + } else { + position.element.textContent = text; + } + position.element.setAttributeNS(null, 'x', x); + position.element.setAttributeNS(null, 'y', y); + position.x = x; + position.y = y; + // update the transforms + updateTransforms(position.element, transforms); + + return; + } + } + + // If the text doesn't exist at this position, create a new entry + + // For the very first position we'll re-use the original element, + // while for subsequent ones we'll clone it. + + position = { + active: true, + rendered: false, + element: positions.length ? info.element.cloneNode() : info.element, + text: text, + x: x, + y: y + }; + + positions.push(position); + + if (text.indexOf('
') !== -1) { + y -= 0.25 * info.height; + addTspanElements(text, position.element, x); + } else { + position.element.textContent = text; + } + + // Move the element to its final position within the container + position.element.setAttributeNS(null, 'x', x); + position.element.setAttributeNS(null, 'y', y); + position.element.style.textAlign = halign; + // update the transforms + updateTransforms(position.element, transforms); + }; + + var addTspanElements = function(text, element, x) { + var lines = text.split('
'), + tspan, i, offset; + + for (i = 0; i < lines.length; i++) { + if (!element.childNodes[i]) { + tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); + element.appendChild(tspan); + } else { + tspan = element.childNodes[i]; + } + tspan.textContent = lines[i]; + offset = i * 1 + 'em'; + tspan.setAttributeNS(null, 'dy', offset); + tspan.setAttributeNS(null, 'x', x); + } + } + + /** + - removeText (layer, x, y, text, font, angle) + + The function removes one or more text strings from the canvas text overlay. + If no parameters are given, all text within the layer is removed. + + Note that the text is not immediately removed; it is simply marked as + inactive, which will result in its removal on the next render pass. + This avoids the performance penalty for 'clear and redraw' behavior, + where we potentially get rid of all text on a layer, but will likely + add back most or all of it later, as when redrawing axes, for example. + + The layer is a string of space-separated CSS classes uniquely + identifying the layer containing this text. The following parameter are + X and Y coordinate of the text. + Text is the string to remove, while the font is either a string of space-separated CSS + classes or a font-spec object, defining the text's font and style. + */ + Canvas.prototype.removeText = function(layer, x, y, text, font, angle) { + var info, htmlYCoord; + if (text == null) { + var layerCache = this._textCache[layer]; + if (layerCache != null) { + for (var styleKey in layerCache) { + if (hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey]; + for (var key in styleCache) { + if (hasOwnProperty.call(styleCache, key)) { + var positions = styleCache[key].positions; + positions.forEach(function(position) { + position.active = false; + }); + } + } + } + } + } + } else { + info = this.getTextInfo(layer, text, font, angle); + positions = info.positions; + positions.forEach(function(position) { + htmlYCoord = y + 0.75 * info.height; + if (position.x === x && position.y === htmlYCoord && position.text === text) { + position.active = false; + } + }); + } + }; + + /** + - clearCache() + + Clears the cache used to speed up the text size measurements. + As an (unfortunate) side effect all text within the text Layer is removed. + Use this function before plot.setupGrid() and plot.draw() if the plot just + became visible or the styles changed. + */ + Canvas.prototype.clearCache = function() { + var cache = this._textCache; + for (var layerKey in cache) { + if (hasOwnProperty.call(cache, layerKey)) { + var layer = this.getSVGLayer(layerKey); + while (layer.firstChild) { + layer.removeChild(layer.firstChild); + } + } + }; + + this._textCache = {}; + }; + + function generateKey(text) { + return text.replace(/0|1|2|3|4|5|6|7|8|9/g, '0'); + } + + if (!window.Flot) { + window.Flot = {}; + } + + window.Flot.Canvas = Canvas; +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.colorhelpers.js b/terahz/templates/lib/flot/jquery.colorhelpers.js new file mode 100644 index 0000000..c59cf2f --- /dev/null +++ b/terahz/templates/lib/flot/jquery.colorhelpers.js @@ -0,0 +1,199 @@ +/* Plugin for jQuery for working with colors. + * + * Version 1.1. + * + * Inspiration from jQuery color animation plugin by John Resig. + * + * Released under the MIT license by Ole Laursen, October 2009. + * + * Examples: + * + * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() + * var c = $.color.extract($("#mydiv"), 'background-color'); + * console.log(c.r, c.g, c.b, c.a); + * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" + * + * Note that .scale() and .add() return the same modified object + * instead of making a new one. + * + * V. 1.1: Fix error handling so e.g. parsing an empty string does + * produce a color rather than just crashing. + */ + +(function($) { + $.color = {}; + + // construct color object with some convenient chainable helpers + $.color.make = function (r, g, b, a) { + var o = {}; + o.r = r || 0; + o.g = g || 0; + o.b = b || 0; + o.a = a != null ? a : 1; + + o.add = function (c, d) { + for (var i = 0; i < c.length; ++i) { + o[c.charAt(i)] += d; + } + + return o.normalize(); + }; + + o.scale = function (c, f) { + for (var i = 0; i < c.length; ++i) { + o[c.charAt(i)] *= f; + } + + return o.normalize(); + }; + + o.toString = function () { + if (o.a >= 1.0) { + return "rgb(" + [o.r, o.g, o.b].join(",") + ")"; + } else { + return "rgba(" + [o.r, o.g, o.b, o.a].join(",") + ")"; + } + }; + + o.normalize = function () { + function clamp(min, value, max) { + return value < min ? min : (value > max ? max : value); + } + + o.r = clamp(0, parseInt(o.r), 255); + o.g = clamp(0, parseInt(o.g), 255); + o.b = clamp(0, parseInt(o.b), 255); + o.a = clamp(0, o.a, 1); + return o; + }; + + o.clone = function () { + return $.color.make(o.r, o.b, o.g, o.a); + }; + + return o.normalize(); + } + + // extract CSS color property from element, going up in the DOM + // if it's "transparent" + $.color.extract = function (elem, css) { + var c; + + do { + c = elem.css(css).toLowerCase(); + // keep going until we find an element that has color, or + // we hit the body or root (have no parent) + if (c !== '' && c !== 'transparent') { + break; + } + + elem = elem.parent(); + } while (elem.length && !$.nodeName(elem.get(0), "body")); + + // catch Safari's way of signalling transparent + if (c === "rgba(0, 0, 0, 0)") { + c = "transparent"; + } + + return $.color.parse(c); + } + + // parse CSS color string (like "rgb(10, 32, 43)" or "#fff"), + // returns color object, if parsing failed, you get black (0, 0, + // 0) out + $.color.parse = function (str) { + var res, m = $.color.make; + + // Look for rgb(num,num,num) + res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str); + if (res) { + return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10)); + } + + // Look for rgba(num,num,num,num) + res = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str) + if (res) { + return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4])); + } + + // Look for rgb(num%,num%,num%) + res = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec(str); + if (res) { + return m(parseFloat(res[1]) * 2.55, parseFloat(res[2]) * 2.55, parseFloat(res[3]) * 2.55); + } + + // Look for rgba(num%,num%,num%,num) + res = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str); + if (res) { + return m(parseFloat(res[1]) * 2.55, parseFloat(res[2]) * 2.55, parseFloat(res[3]) * 2.55, parseFloat(res[4])); + } + + // Look for #a0b1c2 + res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str); + if (res) { + return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16)); + } + + // Look for #fff + res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str); + if (res) { + return m(parseInt(res[1] + res[1], 16), parseInt(res[2] + res[2], 16), parseInt(res[3] + res[3], 16)); + } + + // Otherwise, we're most likely dealing with a named color + var name = $.trim(str).toLowerCase(); + if (name === "transparent") { + return m(255, 255, 255, 0); + } else { + // default to black + res = lookupColors[name] || [0, 0, 0]; + return m(res[0], res[1], res[2]); + } + } + + var lookupColors = { + aqua: [0, 255, 255], + azure: [240, 255, 255], + beige: [245, 245, 220], + black: [0, 0, 0], + blue: [0, 0, 255], + brown: [165, 42, 42], + cyan: [0, 255, 255], + darkblue: [0, 0, 139], + darkcyan: [0, 139, 139], + darkgrey: [169, 169, 169], + darkgreen: [0, 100, 0], + darkkhaki: [189, 183, 107], + darkmagenta: [139, 0, 139], + darkolivegreen: [85, 107, 47], + darkorange: [255, 140, 0], + darkorchid: [153, 50, 204], + darkred: [139, 0, 0], + darksalmon: [233, 150, 122], + darkviolet: [148, 0, 211], + fuchsia: [255, 0, 255], + gold: [255, 215, 0], + green: [0, 128, 0], + indigo: [75, 0, 130], + khaki: [240, 230, 140], + lightblue: [173, 216, 230], + lightcyan: [224, 255, 255], + lightgreen: [144, 238, 144], + lightgrey: [211, 211, 211], + lightpink: [255, 182, 193], + lightyellow: [255, 255, 224], + lime: [0, 255, 0], + magenta: [255, 0, 255], + maroon: [128, 0, 0], + navy: [0, 0, 128], + olive: [128, 128, 0], + orange: [255, 165, 0], + pink: [255, 192, 203], + purple: [128, 0, 128], + violet: [128, 0, 128], + red: [255, 0, 0], + silver: [192, 192, 192], + white: [255, 255, 255], + yellow: [255, 255, 0] + }; +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.axislabels.js b/terahz/templates/lib/flot/jquery.flot.axislabels.js new file mode 100644 index 0000000..8c80828 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.axislabels.js @@ -0,0 +1,212 @@ +/* +Axis label plugin for flot + +Derived from: +Axis Labels Plugin for flot. +http://github.com/markrcote/flot-axislabels + +Original code is Copyright (c) 2010 Xuan Luo. +Original code was released under the GPLv3 license by Xuan Luo, September 2010. +Original code was rereleased under the MIT license by Xuan Luo, April 2012. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +(function($) { + "use strict"; + + var options = { + axisLabels: { + show: true + } + }; + + function AxisLabel(axisName, position, padding, placeholder, axisLabel, surface) { + this.axisName = axisName; + this.position = position; + this.padding = padding; + this.placeholder = placeholder; + this.axisLabel = axisLabel; + this.surface = surface; + this.width = 0; + this.height = 0; + this.elem = null; + } + + AxisLabel.prototype.calculateSize = function() { + var axisId = this.axisName + 'Label', + layerId = axisId + 'Layer', + className = axisId + ' axisLabels'; + + var info = this.surface.getTextInfo(layerId, this.axisLabel, className); + this.labelWidth = info.width; + this.labelHeight = info.height; + + if (this.position === 'left' || this.position === 'right') { + this.width = this.labelHeight + this.padding; + this.height = 0; + } else { + this.width = 0; + this.height = this.labelHeight + this.padding; + } + }; + + AxisLabel.prototype.transforms = function(degrees, x, y, svgLayer) { + var transforms = [], translate, rotate; + if (x !== 0 || y !== 0) { + translate = svgLayer.createSVGTransform(); + translate.setTranslate(x, y); + transforms.push(translate); + } + if (degrees !== 0) { + rotate = svgLayer.createSVGTransform(); + var centerX = Math.round(this.labelWidth / 2), + centerY = 0; + rotate.setRotate(degrees, centerX, centerY); + transforms.push(rotate); + } + + return transforms; + }; + + AxisLabel.prototype.calculateOffsets = function(box) { + var offsets = { + x: 0, + y: 0, + degrees: 0 + }; + if (this.position === 'bottom') { + offsets.x = box.left + box.width / 2 - this.labelWidth / 2; + offsets.y = box.top + box.height - this.labelHeight; + } else if (this.position === 'top') { + offsets.x = box.left + box.width / 2 - this.labelWidth / 2; + offsets.y = box.top; + } else if (this.position === 'left') { + offsets.degrees = -90; + offsets.x = box.left - this.labelWidth / 2; + offsets.y = box.height / 2 + box.top; + } else if (this.position === 'right') { + offsets.degrees = 90; + offsets.x = box.left + box.width - this.labelWidth / 2; + offsets.y = box.height / 2 + box.top; + } + offsets.x = Math.round(offsets.x); + offsets.y = Math.round(offsets.y); + + return offsets; + }; + + AxisLabel.prototype.cleanup = function() { + var axisId = this.axisName + 'Label', + layerId = axisId + 'Layer', + className = axisId + ' axisLabels'; + this.surface.removeText(layerId, 0, 0, this.axisLabel, className); + }; + + AxisLabel.prototype.draw = function(box) { + var axisId = this.axisName + 'Label', + layerId = axisId + 'Layer', + className = axisId + ' axisLabels', + offsets = this.calculateOffsets(box), + style = { + position: 'absolute', + bottom: '', + right: '', + display: 'inline-block', + 'white-space': 'nowrap' + }; + + var layer = this.surface.getSVGLayer(layerId); + var transforms = this.transforms(offsets.degrees, offsets.x, offsets.y, layer.parentNode); + + this.surface.addText(layerId, 0, 0, this.axisLabel, className, undefined, undefined, undefined, undefined, transforms); + this.surface.render(); + Object.keys(style).forEach(function(key) { + layer.style[key] = style[key]; + }); + }; + + function init(plot) { + plot.hooks.processOptions.push(function(plot, options) { + if (!options.axisLabels.show) { + return; + } + + var axisLabels = {}; + var defaultPadding = 2; // padding between axis and tick labels + + plot.hooks.axisReserveSpace.push(function(plot, axis) { + var opts = axis.options; + var axisName = axis.direction + axis.n; + + axis.labelHeight += axis.boxPosition.centerY; + axis.labelWidth += axis.boxPosition.centerX; + + if (!opts || !opts.axisLabel || !axis.show) { + return; + } + + var padding = opts.axisLabelPadding === undefined + ? defaultPadding + : opts.axisLabelPadding; + + var axisLabel = axisLabels[axisName]; + if (!axisLabel) { + axisLabel = new AxisLabel(axisName, + opts.position, padding, + plot.getPlaceholder()[0], opts.axisLabel, plot.getSurface()); + axisLabels[axisName] = axisLabel; + } + + axisLabel.calculateSize(); + + // Incrementing the sizes of the tick labels. + axis.labelHeight += axisLabel.height; + axis.labelWidth += axisLabel.width; + }); + + // TODO - use the drawAxis hook + plot.hooks.draw.push(function(plot, ctx) { + $.each(plot.getAxes(), function(flotAxisName, axis) { + var opts = axis.options; + if (!opts || !opts.axisLabel || !axis.show) { + return; + } + + var axisName = axis.direction + axis.n; + axisLabels[axisName].draw(axis.box); + }); + }); + + plot.hooks.shutdown.push(function(plot, eventHolder) { + for (var axisName in axisLabels) { + axisLabels[axisName].cleanup(); + } + }); + }); + }; + + $.plot.plugins.push({ + init: init, + options: options, + name: 'axisLabels', + version: '3.0' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.browser.js b/terahz/templates/lib/flot/jquery.flot.browser.js new file mode 100644 index 0000000..e50a629 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.browser.js @@ -0,0 +1,98 @@ +/** ## jquery.flot.browser.js + +This plugin is used to make available some browser-related utility functions. + +### Methods +*/ + +(function ($) { + 'use strict'; + + var browser = { + /** + - getPageXY(e) + + Calculates the pageX and pageY using the screenX, screenY properties of the event + and the scrolling of the page. This is needed because the pageX and pageY + properties of the event are not correct while running tests in Edge. */ + getPageXY: function (e) { + // This code is inspired from https://stackoverflow.com/a/3464890 + var doc = document.documentElement, + pageX = e.clientX + (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), + pageY = e.clientY + (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); + return { X: pageX, Y: pageY }; + }, + + /** + - getPixelRatio(context) + + This function returns the current pixel ratio defined by the product of desktop + zoom and page zoom. + Additional info: https://www.html5rocks.com/en/tutorials/canvas/hidpi/ + */ + getPixelRatio: function(context) { + var devicePixelRatio = window.devicePixelRatio || 1, + backingStoreRatio = + context.webkitBackingStorePixelRatio || + context.mozBackingStorePixelRatio || + context.msBackingStorePixelRatio || + context.oBackingStorePixelRatio || + context.backingStorePixelRatio || 1; + return devicePixelRatio / backingStoreRatio; + }, + + /** + - isSafari, isMobileSafari, isOpera, isFirefox, isIE, isEdge, isChrome, isBlink + + This is a collection of functions, used to check if the code is running in a + particular browser or Javascript engine. + */ + isSafari: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + // Safari 3.0+ "[object HTMLElementConstructor]" + return /constructor/i.test(window.top.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window.top['safari'] || (typeof window.top.safari !== 'undefined' && window.top.safari.pushNotification)); + }, + + isMobileSafari: function() { + //isMobileSafari adapted from https://stackoverflow.com/questions/3007480/determine-if-user-navigated-from-mobile-safari + return navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/); + }, + + isOpera: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + //Opera 8.0+ + return (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; + }, + + isFirefox: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + // Firefox 1.0+ + return typeof InstallTrigger !== 'undefined'; + }, + + isIE: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + // Internet Explorer 6-11 + return /*@cc_on!@*/false || !!document.documentMode; + }, + + isEdge: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + // Edge 20+ + return !browser.isIE() && !!window.StyleMedia; + }, + + isChrome: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + // Chrome 1+ + return !!window.chrome && !!window.chrome.webstore; + }, + + isBlink: function() { + // *** https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser + return (browser.isChrome() || browser.isOpera()) && !!window.CSS; + } + }; + + $.plot.browser = browser; +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.categories.js b/terahz/templates/lib/flot/jquery.flot.categories.js new file mode 100644 index 0000000..af16f78 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.categories.js @@ -0,0 +1,202 @@ +/* Flot plugin for plotting textual data or categories. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +Consider a dataset like [["February", 34], ["March", 20], ...]. This plugin +allows you to plot such a dataset directly. + +To enable it, you must specify mode: "categories" on the axis with the textual +labels, e.g. + + $.plot("#placeholder", data, { xaxis: { mode: "categories" } }); + +By default, the labels are ordered as they are met in the data series. If you +need a different ordering, you can specify "categories" on the axis options +and list the categories there: + + xaxis: { + mode: "categories", + categories: ["February", "March", "April"] + } + +If you need to customize the distances between the categories, you can specify +"categories" as an object mapping labels to values + + xaxis: { + mode: "categories", + categories: { "February": 1, "March": 3, "April": 4 } + } + +If you don't specify all categories, the remaining categories will be numbered +from the max value plus 1 (with a spacing of 1 between each). + +Internally, the plugin works by transforming the input data through an auto- +generated mapping where the first category becomes 0, the second 1, etc. +Hence, a point like ["February", 34] becomes [0, 34] internally in Flot (this +is visible in hover and click events that return numbers rather than the +category labels). The plugin also overrides the tick generator to spit out the +categories as ticks instead of the values. + +If you need to map a value back to its label, the mapping is always accessible +as "categories" on the axis object, e.g. plot.getAxes().xaxis.categories. + +*/ + +(function ($) { + var options = { + xaxis: { + categories: null + }, + yaxis: { + categories: null + } + }; + + function processRawData(plot, series, data, datapoints) { + // if categories are enabled, we need to disable + // auto-transformation to numbers so the strings are intact + // for later processing + + var xCategories = series.xaxis.options.mode === "categories", + yCategories = series.yaxis.options.mode === "categories"; + + if (!(xCategories || yCategories)) { + return; + } + + var format = datapoints.format; + + if (!format) { + // FIXME: auto-detection should really not be defined here + var s = series; + format = []; + format.push({ x: true, number: true, required: true, computeRange: true}); + format.push({ y: true, number: true, required: true, computeRange: true }); + + if (s.bars.show || (s.lines.show && s.lines.fill)) { + var autoScale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero)); + format.push({ y: true, number: true, required: false, defaultValue: 0, computeRange: autoScale }); + if (s.bars.horizontal) { + delete format[format.length - 1].y; + format[format.length - 1].x = true; + } + } + + datapoints.format = format; + } + + for (var m = 0; m < format.length; ++m) { + if (format[m].x && xCategories) { + format[m].number = false; + } + + if (format[m].y && yCategories) { + format[m].number = false; + format[m].computeRange = false; + } + } + } + + function getNextIndex(categories) { + var index = -1; + + for (var v in categories) { + if (categories[v] > index) { + index = categories[v]; + } + } + + return index + 1; + } + + function categoriesTickGenerator(axis) { + var res = []; + for (var label in axis.categories) { + var v = axis.categories[label]; + if (v >= axis.min && v <= axis.max) { + res.push([v, label]); + } + } + + res.sort(function (a, b) { return a[0] - b[0]; }); + + return res; + } + + function setupCategoriesForAxis(series, axis, datapoints) { + if (series[axis].options.mode !== "categories") { + return; + } + + if (!series[axis].categories) { + // parse options + var c = {}, o = series[axis].options.categories || {}; + if ($.isArray(o)) { + for (var i = 0; i < o.length; ++i) { + c[o[i]] = i; + } + } else { + for (var v in o) { + c[v] = o[v]; + } + } + + series[axis].categories = c; + } + + // fix ticks + if (!series[axis].options.ticks) { + series[axis].options.ticks = categoriesTickGenerator; + } + + transformPointsOnAxis(datapoints, axis, series[axis].categories); + } + + function transformPointsOnAxis(datapoints, axis, categories) { + // go through the points, transforming them + var points = datapoints.points, + ps = datapoints.pointsize, + format = datapoints.format, + formatColumn = axis.charAt(0), + index = getNextIndex(categories); + + for (var i = 0; i < points.length; i += ps) { + if (points[i] == null) { + continue; + } + + for (var m = 0; m < ps; ++m) { + var val = points[i + m]; + + if (val == null || !format[m][formatColumn]) { + continue; + } + + if (!(val in categories)) { + categories[val] = index; + ++index; + } + + points[i + m] = categories[val]; + } + } + } + + function processDatapoints(plot, series, datapoints) { + setupCategoriesForAxis(series, "xaxis", datapoints); + setupCategoriesForAxis(series, "yaxis", datapoints); + } + + function init(plot) { + plot.hooks.processRawData.push(processRawData); + plot.hooks.processDatapoints.push(processDatapoints); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'categories', + version: '1.0' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.composeImages.js b/terahz/templates/lib/flot/jquery.flot.composeImages.js new file mode 100644 index 0000000..0b7ab78 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.composeImages.js @@ -0,0 +1,330 @@ +/** ## jquery.flot.composeImages.js + +This plugin is used to expose a function used to overlap several canvases and +SVGs, for the purpose of creating a snaphot out of them. + +### When composeImages is used: +When multiple canvases and SVGs have to be overlapped into a single image +and their offset on the page, must be preserved. + +### Where can be used: +In creating a downloadable snapshot of the plots, axes, cursors etc of a graph. + +### How it works: +The entry point is composeImages function. It expects an array of objects, +which should be either canvases or SVGs (or a mix). It does a prevalidation +of them, by verifying if they will be usable or not, later in the flow. +After selecting only usable sources, it passes them to getGenerateTempImg +function, which generates temporary images out of them. This function +expects that some of the passed sources (canvas or SVG) may still have +problems being converted to an image and makes sure the promises system, +used by composeImages function, moves forward. As an example, SVGs with +missing information from header or with unsupported content, may lead to +failure in generating the temporary image. Temporary images are required +mostly on extracting content from SVGs, but this is also where the x/y +offsets are extracted for each image which will be added. For SVGs in +particular, their CSS rules have to be applied. +After all temporary images are generated, they are overlapped using +getExecuteImgComposition function. This is where the destination canvas +is set to the proper dimensions. It is then output by composeImages. +This function returns a promise, which can be used to wait for the whole +composition process. It requires to be asynchronous, because this is how +temporary images load their data. +*/ + +(function($) { + "use strict"; + const GENERALFAILURECALLBACKERROR = -100; //simply a negative number + const SUCCESSFULIMAGEPREPARATION = 0; + const EMPTYARRAYOFIMAGESOURCES = -1; + const NEGATIVEIMAGESIZE = -2; + var pixelRatio = 1; + var browser = $.plot.browser; + var getPixelRatio = browser.getPixelRatio; + + function composeImages(canvasOrSvgSources, destinationCanvas) { + var validCanvasOrSvgSources = canvasOrSvgSources.filter(isValidSource); + pixelRatio = getPixelRatio(destinationCanvas.getContext('2d')); + + var allImgCompositionPromises = validCanvasOrSvgSources.map(function(validCanvasOrSvgSource) { + var tempImg = new Image(); + var currentPromise = new Promise(getGenerateTempImg(tempImg, validCanvasOrSvgSource)); + return currentPromise; + }); + + var lastPromise = Promise.all(allImgCompositionPromises).then(getExecuteImgComposition(destinationCanvas), failureCallback); + return lastPromise; + } + + function isValidSource(canvasOrSvgSource) { + var isValidFromCanvas = true; + var isValidFromContent = true; + if ((canvasOrSvgSource === null) || (canvasOrSvgSource === undefined)) { + isValidFromContent = false; + } else { + if (canvasOrSvgSource.tagName === 'CANVAS') { + if ((canvasOrSvgSource.getBoundingClientRect().right === canvasOrSvgSource.getBoundingClientRect().left) || + (canvasOrSvgSource.getBoundingClientRect().bottom === canvasOrSvgSource.getBoundingClientRect().top)) { + isValidFromCanvas = false; + } + } + } + return isValidFromContent && isValidFromCanvas && (window.getComputedStyle(canvasOrSvgSource).visibility === 'visible'); + } + + function getGenerateTempImg(tempImg, canvasOrSvgSource) { + tempImg.sourceDescription = ''; + tempImg.sourceComponent = canvasOrSvgSource; + + return function doGenerateTempImg(successCallbackFunc, failureCallbackFunc) { + tempImg.onload = function(evt) { + tempImg.successfullyLoaded = true; + successCallbackFunc(tempImg); + }; + + tempImg.onabort = function(evt) { + tempImg.successfullyLoaded = false; + console.log('Can\'t generate temp image from ' + tempImg.sourceDescription + '. It is possible that it is missing some properties or its content is not supported by this browser. Source component:', tempImg.sourceComponent); + successCallbackFunc(tempImg); //call successCallback, to allow snapshot of all working images + }; + + tempImg.onerror = function(evt) { + tempImg.successfullyLoaded = false; + console.log('Can\'t generate temp image from ' + tempImg.sourceDescription + '. It is possible that it is missing some properties or its content is not supported by this browser. Source component:', tempImg.sourceComponent); + successCallbackFunc(tempImg); //call successCallback, to allow snapshot of all working images + }; + + generateTempImageFromCanvasOrSvg(canvasOrSvgSource, tempImg); + }; + } + + function getExecuteImgComposition(destinationCanvas) { + return function executeImgComposition(tempImgs) { + var compositionResult = copyImgsToCanvas(tempImgs, destinationCanvas); + return compositionResult; + }; + } + + function copyCanvasToImg(canvas, img) { + img.src = canvas.toDataURL('image/png'); + } + + function getCSSRules(document) { + var styleSheets = document.styleSheets, + rulesList = []; + for (var i = 0; i < styleSheets.length; i++) { + // CORS requests for style sheets throw and an exception on Chrome > 64 + try { + // in Chrome, the external CSS files are empty when the page is directly loaded from disk + var rules = styleSheets[i].cssRules || []; + for (var j = 0; j < rules.length; j++) { + var rule = rules[j]; + rulesList.push(rule.cssText); + } + } catch (e) { + console.log('Failed to get some css rules'); + } + } + return rulesList; + } + + function embedCSSRulesInSVG(rules, svg) { + var text = [ + '', + '', + svg.innerHTML, + '' + ].join('\n'); + return text; + } + + function copySVGToImgMostBrowsers(svg, img) { + var rules = getCSSRules(document), + source = embedCSSRulesInSVG(rules, svg); + + source = patchSVGSource(source); + + var blob = new Blob([source], {type: "image/svg+xml;charset=utf-8"}), + domURL = self.URL || self.webkitURL || self, + url = domURL.createObjectURL(blob); + img.src = url; + } + + function copySVGToImgSafari(svg, img) { + // Use this method to convert a string buffer array to a binary string. + // Do so by breaking up large strings into smaller substrings; this is necessary to avoid the + // "maximum call stack size exceeded" exception that can happen when calling 'String.fromCharCode.apply' + // with a very long array. + function buildBinaryString (arrayBuffer) { + var binaryString = ""; + const utf8Array = new Uint8Array(arrayBuffer); + const blockSize = 16384; + for (var i = 0; i < utf8Array.length; i = i + blockSize) { + const binarySubString = String.fromCharCode.apply(null, utf8Array.subarray(i, i + blockSize)); + binaryString = binaryString + binarySubString; + } + return binaryString; + }; + + var rules = getCSSRules(document), + source = embedCSSRulesInSVG(rules, svg), + data, + utf8BinaryString; + + source = patchSVGSource(source); + + // Encode the string as UTF-8 and convert it to a binary string. The UTF-8 encoding is required to + // capture unicode characters correctly. + utf8BinaryString = buildBinaryString(new (TextEncoder || TextEncoderLite)('utf-8').encode(source)); + + data = "data:image/svg+xml;base64," + btoa(utf8BinaryString); + img.src = data; + } + + function patchSVGSource(svgSource) { + var source = ''; + //add name spaces. + if (!svgSource.match(/^]+xmlns="http:\/\/www\.w3\.org\/2000\/svg"/)) { + source = svgSource.replace(/^]+"http:\/\/www\.w3\.org\/1999\/xlink"/)) { + source = svgSource.replace(/^\r\n' + source; + } + + function copySVGToImg(svg, img) { + if (browser.isSafari() || browser.isMobileSafari()) { + copySVGToImgSafari(svg, img); + } else { + copySVGToImgMostBrowsers(svg, img); + } + } + + function adaptDestSizeToZoom(destinationCanvas, sources) { + function containsSVGs(source) { + return source.srcImgTagName === 'svg'; + } + + if (sources.find(containsSVGs) !== undefined) { + if (pixelRatio < 1) { + destinationCanvas.width = destinationCanvas.width * pixelRatio; + destinationCanvas.height = destinationCanvas.height * pixelRatio; + } + } + } + + function prepareImagesToBeComposed(sources, destination) { + var result = SUCCESSFULIMAGEPREPARATION; + if (sources.length === 0) { + result = EMPTYARRAYOFIMAGESOURCES; //nothing to do if called without sources + } else { + var minX = sources[0].genLeft; + var minY = sources[0].genTop; + var maxX = sources[0].genRight; + var maxY = sources[0].genBottom; + var i = 0; + + for (i = 1; i < sources.length; i++) { + if (minX > sources[i].genLeft) { + minX = sources[i].genLeft; + } + + if (minY > sources[i].genTop) { + minY = sources[i].genTop; + } + } + + for (i = 1; i < sources.length; i++) { + if (maxX < sources[i].genRight) { + maxX = sources[i].genRight; + } + + if (maxY < sources[i].genBottom) { + maxY = sources[i].genBottom; + } + } + + if ((maxX - minX <= 0) || (maxY - minY <= 0)) { + result = NEGATIVEIMAGESIZE; //this might occur on hidden images + } else { + destination.width = Math.round(maxX - minX); + destination.height = Math.round(maxY - minY); + + for (i = 0; i < sources.length; i++) { + sources[i].xCompOffset = sources[i].genLeft - minX; + sources[i].yCompOffset = sources[i].genTop - minY; + } + + adaptDestSizeToZoom(destination, sources); + } + } + return result; + } + + function copyImgsToCanvas(sources, destination) { + var prepareImagesResult = prepareImagesToBeComposed(sources, destination); + if (prepareImagesResult === SUCCESSFULIMAGEPREPARATION) { + var destinationCtx = destination.getContext('2d'); + + for (var i = 0; i < sources.length; i++) { + if (sources[i].successfullyLoaded === true) { + destinationCtx.drawImage(sources[i], sources[i].xCompOffset * pixelRatio, sources[i].yCompOffset * pixelRatio); + } + } + } + return prepareImagesResult; + } + + function adnotateDestImgWithBoundingClientRect(srcCanvasOrSvg, destImg) { + destImg.genLeft = srcCanvasOrSvg.getBoundingClientRect().left; + destImg.genTop = srcCanvasOrSvg.getBoundingClientRect().top; + + if (srcCanvasOrSvg.tagName === 'CANVAS') { + destImg.genRight = destImg.genLeft + srcCanvasOrSvg.width; + destImg.genBottom = destImg.genTop + srcCanvasOrSvg.height; + } + + if (srcCanvasOrSvg.tagName === 'svg') { + destImg.genRight = srcCanvasOrSvg.getBoundingClientRect().right; + destImg.genBottom = srcCanvasOrSvg.getBoundingClientRect().bottom; + } + } + + function generateTempImageFromCanvasOrSvg(srcCanvasOrSvg, destImg) { + if (srcCanvasOrSvg.tagName === 'CANVAS') { + copyCanvasToImg(srcCanvasOrSvg, destImg); + } + + if (srcCanvasOrSvg.tagName === 'svg') { + copySVGToImg(srcCanvasOrSvg, destImg); + } + + destImg.srcImgTagName = srcCanvasOrSvg.tagName; + adnotateDestImgWithBoundingClientRect(srcCanvasOrSvg, destImg); + } + + function failureCallback() { + return GENERALFAILURECALLBACKERROR; + } + + // used for testing + $.plot.composeImages = composeImages; + + function init(plot) { + // used to extend the public API of the plot + plot.composeImages = composeImages; + } + + $.plot.plugins.push({ + init: init, + name: 'composeImages', + version: '1.0' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.crosshair.js b/terahz/templates/lib/flot/jquery.flot.crosshair.js new file mode 100644 index 0000000..385c705 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.crosshair.js @@ -0,0 +1,202 @@ +/* Flot plugin for showing crosshairs when the mouse hovers over the plot. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin supports these options: + + crosshair: { + mode: null or "x" or "y" or "xy" + color: color + lineWidth: number + } + +Set the mode to one of "x", "y" or "xy". The "x" mode enables a vertical +crosshair that lets you trace the values on the x axis, "y" enables a +horizontal crosshair and "xy" enables them both. "color" is the color of the +crosshair (default is "rgba(170, 0, 0, 0.80)"), "lineWidth" is the width of +the drawn lines (default is 1). + +The plugin also adds four public methods: + + - setCrosshair( pos ) + + Set the position of the crosshair. Note that this is cleared if the user + moves the mouse. "pos" is in coordinates of the plot and should be on the + form { x: xpos, y: ypos } (you can use x2/x3/... if you're using multiple + axes), which is coincidentally the same format as what you get from a + "plothover" event. If "pos" is null, the crosshair is cleared. + + - clearCrosshair() + + Clear the crosshair. + + - lockCrosshair(pos) + + Cause the crosshair to lock to the current location, no longer updating if + the user moves the mouse. Optionally supply a position (passed on to + setCrosshair()) to move it to. + + Example usage: + + var myFlot = $.plot( $("#graph"), ..., { crosshair: { mode: "x" } } }; + $("#graph").bind( "plothover", function ( evt, position, item ) { + if ( item ) { + // Lock the crosshair to the data point being hovered + myFlot.lockCrosshair({ + x: item.datapoint[ 0 ], + y: item.datapoint[ 1 ] + }); + } else { + // Return normal crosshair operation + myFlot.unlockCrosshair(); + } + }); + + - unlockCrosshair() + + Free the crosshair to move again after locking it. +*/ + +(function ($) { + var options = { + crosshair: { + mode: null, // one of null, "x", "y" or "xy", + color: "rgba(170, 0, 0, 0.80)", + lineWidth: 1 + } + }; + + function init(plot) { + // position of crosshair in pixels + var crosshair = {x: -1, y: -1, locked: false, highlighted: false}; + + plot.setCrosshair = function setCrosshair(pos) { + if (!pos) { + crosshair.x = -1; + } else { + var o = plot.p2c(pos); + crosshair.x = Math.max(0, Math.min(o.left, plot.width())); + crosshair.y = Math.max(0, Math.min(o.top, plot.height())); + } + + plot.triggerRedrawOverlay(); + }; + + plot.clearCrosshair = plot.setCrosshair; // passes null for pos + + plot.lockCrosshair = function lockCrosshair(pos) { + if (pos) { + plot.setCrosshair(pos); + } + + crosshair.locked = true; + }; + + plot.unlockCrosshair = function unlockCrosshair() { + crosshair.locked = false; + crosshair.rect = null; + }; + + function onMouseOut(e) { + if (crosshair.locked) { + return; + } + + if (crosshair.x !== -1) { + crosshair.x = -1; + plot.triggerRedrawOverlay(); + } + } + + function onMouseMove(e) { + var offset = plot.offset(); + if (crosshair.locked) { + var mouseX = Math.max(0, Math.min(e.pageX - offset.left, plot.width())); + var mouseY = Math.max(0, Math.min(e.pageY - offset.top, plot.height())); + + if ((mouseX > crosshair.x - 4) && (mouseX < crosshair.x + 4) && (mouseY > crosshair.y - 4) && (mouseY < crosshair.y + 4)) { + if (!crosshair.highlighted) { + crosshair.highlighted = true; + plot.triggerRedrawOverlay(); + } + } else { + if (crosshair.highlighted) { + crosshair.highlighted = false; + plot.triggerRedrawOverlay(); + } + } + return; + } + + if (plot.getSelection && plot.getSelection()) { + crosshair.x = -1; // hide the crosshair while selecting + return; + } + + crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width())); + crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height())); + plot.triggerRedrawOverlay(); + } + + plot.hooks.bindEvents.push(function (plot, eventHolder) { + if (!plot.getOptions().crosshair.mode) { + return; + } + + eventHolder.mouseout(onMouseOut); + eventHolder.mousemove(onMouseMove); + }); + + plot.hooks.drawOverlay.push(function (plot, ctx) { + var c = plot.getOptions().crosshair; + if (!c.mode) { + return; + } + + var plotOffset = plot.getPlotOffset(); + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + if (crosshair.x !== -1) { + var adj = plot.getOptions().crosshair.lineWidth % 2 ? 0.5 : 0; + + ctx.strokeStyle = c.color; + ctx.lineWidth = c.lineWidth; + ctx.lineJoin = "round"; + + ctx.beginPath(); + if (c.mode.indexOf("x") !== -1) { + var drawX = Math.floor(crosshair.x) + adj; + ctx.moveTo(drawX, 0); + ctx.lineTo(drawX, plot.height()); + } + if (c.mode.indexOf("y") !== -1) { + var drawY = Math.floor(crosshair.y) + adj; + ctx.moveTo(0, drawY); + ctx.lineTo(plot.width(), drawY); + } + if (crosshair.locked) { + if (crosshair.highlighted) ctx.fillStyle = 'orange'; + else ctx.fillStyle = c.color; + ctx.fillRect(Math.floor(crosshair.x) + adj - 4, Math.floor(crosshair.y) + adj - 4, 8, 8); + } + ctx.stroke(); + } + ctx.restore(); + }); + + plot.hooks.shutdown.push(function (plot, eventHolder) { + eventHolder.unbind("mouseout", onMouseOut); + eventHolder.unbind("mousemove", onMouseMove); + }); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'crosshair', + version: '1.0' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.drawSeries.js b/terahz/templates/lib/flot/jquery.flot.drawSeries.js new file mode 100644 index 0000000..bf1272f --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.drawSeries.js @@ -0,0 +1,663 @@ +/** +## jquery.flot.drawSeries.js + +This plugin is used by flot for drawing lines, plots, bars or area. + +### Public methods +*/ + +(function($) { + "use strict"; + + function DrawSeries() { + function plotLine(datapoints, xoffset, yoffset, axisx, axisy, ctx, steps) { + var points = datapoints.points, + ps = datapoints.pointsize, + prevx = null, + prevy = null; + var x1 = 0.0, + y1 = 0.0, + x2 = 0.0, + y2 = 0.0, + mx = null, + my = null, + i = 0; + + ctx.beginPath(); + for (i = ps; i < points.length; i += ps) { + x1 = points[i - ps]; + y1 = points[i - ps + 1]; + x2 = points[i]; + y2 = points[i + 1]; + + if (x1 === null || x2 === null) { + mx = null; + my = null; + continue; + } + + if (isNaN(x1) || isNaN(x2) || isNaN(y1) || isNaN(y2)) { + prevx = null; + prevy = null; + continue; + } + + if(steps){ + if (mx !== null && my !== null) { + // if middle point exists, transfer p2 -> p1 and p1 -> mp + x2 = x1; + y2 = y1; + x1 = mx; + y1 = my; + + // 'remove' middle point + mx = null; + my = null; + + // subtract pointsize from i to have current point p1 handled again + i -= ps; + } else if (y1 !== y2 && x1 !== x2) { + // create a middle point + y2 = y1; + mx = x2; + my = y1; + } + } + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min) { + if (y2 < axisy.min) { + // line segment is outside + continue; + } + // compute new intersection point + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.min; + } else if (y2 <= y1 && y2 < axisy.min) { + if (y1 < axisy.min) { + continue; + } + + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.min; + } + + // clip with ymax + if (y1 >= y2 && y1 > axisy.max) { + if (y2 > axisy.max) { + continue; + } + + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.max; + } else if (y2 >= y1 && y2 > axisy.max) { + if (y1 > axisy.max) { + continue; + } + + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.max; + } + + // clip with xmin + if (x1 <= x2 && x1 < axisx.min) { + if (x2 < axisx.min) { + continue; + } + + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.min; + } else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) { + continue; + } + + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.min; + } + + // clip with xmax + if (x1 >= x2 && x1 > axisx.max) { + if (x2 > axisx.max) { + continue; + } + + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.max; + } else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) { + continue; + } + + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.max; + } + + if (x1 !== prevx || y1 !== prevy) { + ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); + } + + prevx = x2; + prevy = y2; + ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset); + } + ctx.stroke(); + } + + function plotLineArea(datapoints, axisx, axisy, fillTowards, ctx, steps) { + var points = datapoints.points, + ps = datapoints.pointsize, + bottom = fillTowards > axisy.min ? Math.min(axisy.max, fillTowards) : axisy.min, + i = 0, + ypos = 1, + areaOpen = false, + segmentStart = 0, + segmentEnd = 0, + mx = null, + my = null; + + // we process each segment in two turns, first forward + // direction to sketch out top, then once we hit the + // end we go backwards to sketch the bottom + while (true) { + if (ps > 0 && i > points.length + ps) { + break; + } + + i += ps; // ps is negative if going backwards + + var x1 = points[i - ps], + y1 = points[i - ps + ypos], + x2 = points[i], + y2 = points[i + ypos]; + + if (ps === -2) { + /* going backwards and no value for the bottom provided in the series*/ + y1 = y2 = bottom; + } + + if (areaOpen) { + if (ps > 0 && x1 != null && x2 == null) { + // at turning point + segmentEnd = i; + ps = -ps; + ypos = 2; + continue; + } + + if (ps < 0 && i === segmentStart + ps) { + // done with the reverse sweep + ctx.fill(); + areaOpen = false; + ps = -ps; + ypos = 1; + i = segmentStart = segmentEnd + ps; + continue; + } + } + + if (x1 == null || x2 == null) { + mx = null; + my = null; + continue; + } + + if(steps){ + if (mx !== null && my !== null) { + // if middle point exists, transfer p2 -> p1 and p1 -> mp + x2 = x1; + y2 = y1; + x1 = mx; + y1 = my; + + // 'remove' middle point + mx = null; + my = null; + + // subtract pointsize from i to have current point p1 handled again + i -= ps; + } else if (y1 !== y2 && x1 !== x2) { + // create a middle point + y2 = y1; + mx = x2; + my = y1; + } + } + + // clip x values + + // clip with xmin + if (x1 <= x2 && x1 < axisx.min) { + if (x2 < axisx.min) { + continue; + } + + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.min; + } else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) { + continue; + } + + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.min; + } + + // clip with xmax + if (x1 >= x2 && x1 > axisx.max) { + if (x2 > axisx.max) { + continue; + } + + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.max; + } else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) { + continue; + } + + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.max; + } + + if (!areaOpen) { + // open area + ctx.beginPath(); + ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); + areaOpen = true; + } + + // now first check the case where both is outside + if (y1 >= axisy.max && y2 >= axisy.max) { + ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); + continue; + } else if (y1 <= axisy.min && y2 <= axisy.min) { + ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); + continue; + } + + // else it's a bit more complicated, there might + // be a flat maxed out rectangle first, then a + // triangular cutout or reverse; to find these + // keep track of the current x values + var x1old = x1, + x2old = x2; + + // clip the y values, without shortcutting, we + // go through all cases in turn + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.min; + } else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.min; + } + + // clip with ymax + if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.max; + } else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.max; + } + + // if the x value was changed we got a rectangle + // to fill + if (x1 !== x1old) { + ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); + // it goes to (x1, y1), but we fill that below + } + + // fill triangular section, this sometimes result + // in redundant points if (x1, y1) hasn't changed + // from previous line to, but we just ignore that + ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + + // fill the other rectangle if it's there + if (x2 !== x2old) { + ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); + } + } + } + + /** + - drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) + + This function is used for drawing lines or area fill. In case the series has line decimation function + attached, before starting to draw, as an optimization the points will first be decimated. + + The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and + plotHeight are the corresponding parameters of flot used to determine the drawing surface. + The function getColorOrGradient is used to compute the fill style of lines and area. + */ + function drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) { + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + ctx.lineJoin = "round"; + + if (series.lines.dashes && ctx.setLineDash) { + ctx.setLineDash(series.lines.dashes); + } + + var datapoints = { + format: series.datapoints.format, + points: series.datapoints.points, + pointsize: series.datapoints.pointsize + }; + + if (series.decimate) { + datapoints.points = series.decimate(series, series.xaxis.min, series.xaxis.max, plotWidth, series.yaxis.min, series.yaxis.max, plotHeight); + } + + var lw = series.lines.lineWidth; + + ctx.lineWidth = lw; + ctx.strokeStyle = series.color; + var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight, getColorOrGradient); + if (fillStyle) { + ctx.fillStyle = fillStyle; + plotLineArea(datapoints, series.xaxis, series.yaxis, series.lines.fillTowards || 0, ctx, series.lines.steps); + } + + if (lw > 0) { + plotLine(datapoints, 0, 0, series.xaxis, series.yaxis, ctx, series.lines.steps); + } + + ctx.restore(); + } + + /** + - drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) + + This function is used for drawing points using a given symbol. In case the series has points decimation + function attached, before starting to draw, as an optimization the points will first be decimated. + + The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and + plotHeight are the corresponding parameters of flot used to determine the drawing surface. + The function drawSymbol is used to compute and draw the symbol chosen for the points. + */ + function drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) { + function drawCircle(ctx, x, y, radius, shadow, fill) { + ctx.moveTo(x + radius, y); + ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); + } + drawCircle.fill = true; + function plotPoints(datapoints, radius, fill, offset, shadow, axisx, axisy, drawSymbolFn) { + var points = datapoints.points, + ps = datapoints.pointsize; + + ctx.beginPath(); + for (var i = 0; i < points.length; i += ps) { + var x = points[i], + y = points[i + 1]; + if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) { + continue; + } + + x = axisx.p2c(x); + y = axisy.p2c(y) + offset; + + drawSymbolFn(ctx, x, y, radius, shadow, fill); + } + if (drawSymbolFn.fill && !shadow) { + ctx.fill(); + } + ctx.stroke(); + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var datapoints = { + format: series.datapoints.format, + points: series.datapoints.points, + pointsize: series.datapoints.pointsize + }; + + if (series.decimatePoints) { + datapoints.points = series.decimatePoints(series, series.xaxis.min, series.xaxis.max, plotWidth, series.yaxis.min, series.yaxis.max, plotHeight); + } + + var lw = series.points.lineWidth, + radius = series.points.radius, + symbol = series.points.symbol, + drawSymbolFn; + + if (symbol === 'circle') { + drawSymbolFn = drawCircle; + } else if (typeof symbol === 'string' && drawSymbol && drawSymbol[symbol]) { + drawSymbolFn = drawSymbol[symbol]; + } else if (typeof drawSymbol === 'function') { + drawSymbolFn = drawSymbol; + } + + // If the user sets the line width to 0, we change it to a very + // small value. A line width of 0 seems to force the default of 1. + + if (lw === 0) { + lw = 0.0001; + } + + ctx.lineWidth = lw; + ctx.fillStyle = getFillStyle(series.points, series.color, null, null, getColorOrGradient); + ctx.strokeStyle = series.color; + plotPoints(datapoints, radius, + true, 0, false, + series.xaxis, series.yaxis, drawSymbolFn); + ctx.restore(); + } + + function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { + var left = x + barLeft, + right = x + barRight, + bottom = b, top = y, + drawLeft, drawRight, drawTop, drawBottom = false, + tmp; + + drawLeft = drawRight = drawTop = true; + + // in horizontal mode, we start the bar from the left + // instead of from the bottom so it appears to be + // horizontal rather than vertical + if (horizontal) { + drawBottom = drawRight = drawTop = true; + drawLeft = false; + left = b; + right = x; + top = y + barLeft; + bottom = y + barRight; + + // account for negative bars + if (right < left) { + tmp = right; + right = left; + left = tmp; + drawLeft = true; + drawRight = false; + } + } + else { + drawLeft = drawRight = drawTop = true; + drawBottom = false; + left = x + barLeft; + right = x + barRight; + bottom = b; + top = y; + + // account for negative bars + if (top < bottom) { + tmp = top; + top = bottom; + bottom = tmp; + drawBottom = true; + drawTop = false; + } + } + + // clip + if (right < axisx.min || left > axisx.max || + top < axisy.min || bottom > axisy.max) { + return; + } + + if (left < axisx.min) { + left = axisx.min; + drawLeft = false; + } + + if (right > axisx.max) { + right = axisx.max; + drawRight = false; + } + + if (bottom < axisy.min) { + bottom = axisy.min; + drawBottom = false; + } + + if (top > axisy.max) { + top = axisy.max; + drawTop = false; + } + + left = axisx.p2c(left); + bottom = axisy.p2c(bottom); + right = axisx.p2c(right); + top = axisy.p2c(top); + + // fill the bar + if (fillStyleCallback) { + c.fillStyle = fillStyleCallback(bottom, top); + c.fillRect(left, top, right - left, bottom - top) + } + + // draw outline + if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { + c.beginPath(); + + // FIXME: inline moveTo is buggy with excanvas + c.moveTo(left, bottom); + if (drawLeft) { + c.lineTo(left, top); + } else { + c.moveTo(left, top); + } + + if (drawTop) { + c.lineTo(right, top); + } else { + c.moveTo(right, top); + } + + if (drawRight) { + c.lineTo(right, bottom); + } else { + c.moveTo(right, bottom); + } + + if (drawBottom) { + c.lineTo(left, bottom); + } else { + c.moveTo(left, bottom); + } + + c.stroke(); + } + } + + /** + - drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) + + This function is used for drawing series represented as bars. In case the series has decimation + function attached, before starting to draw, as an optimization the points will first be decimated. + + The series parameter contains the series to be drawn on ctx context. The plotOffset, plotWidth and + plotHeight are the corresponding parameters of flot used to determine the drawing surface. + The function getColorOrGradient is used to compute the fill style of bars. + */ + function drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, drawSymbol, getColorOrGradient) { + function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { + var points = datapoints.points, + ps = datapoints.pointsize, + fillTowards = series.bars.fillTowards || 0, + defaultBottom = fillTowards > axisy.min ? Math.min(axisy.max, fillTowards) : axisy.min; + + for (var i = 0; i < points.length; i += ps) { + if (points[i] == null) { + continue; + } + + // Use third point as bottom if pointsize is 3 + var bottom = ps === 3 ? points[i + 2] : defaultBottom; + drawBar(points[i], points[i + 1], bottom, barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var datapoints = { + format: series.datapoints.format, + points: series.datapoints.points, + pointsize: series.datapoints.pointsize + }; + + if (series.decimate) { + datapoints.points = series.decimate(series, series.xaxis.min, series.xaxis.max, plotWidth); + } + + ctx.lineWidth = series.bars.lineWidth; + ctx.strokeStyle = series.color; + + var barLeft; + var barWidth = series.bars.barWidth[0] || series.bars.barWidth; + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -barWidth; + break; + default: + barLeft = -barWidth / 2; + } + + var fillStyleCallback = series.bars.fill ? function(bottom, top) { + return getFillStyle(series.bars, series.color, bottom, top, getColorOrGradient); + } : null; + + plotBars(datapoints, barLeft, barLeft + barWidth, fillStyleCallback, series.xaxis, series.yaxis); + ctx.restore(); + } + + function getFillStyle(filloptions, seriesColor, bottom, top, getColorOrGradient) { + var fill = filloptions.fill; + if (!fill) { + return null; + } + + if (filloptions.fillColor) { + return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); + } + + var c = $.color.parse(seriesColor); + c.a = typeof fill === "number" ? fill : 0.4; + c.normalize(); + return c.toString(); + } + + this.drawSeriesLines = drawSeriesLines; + this.drawSeriesPoints = drawSeriesPoints; + this.drawSeriesBars = drawSeriesBars; + this.drawBar = drawBar; + }; + + $.plot.drawSeries = new DrawSeries(); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.errorbars.js b/terahz/templates/lib/flot/jquery.flot.errorbars.js new file mode 100644 index 0000000..956562e --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.errorbars.js @@ -0,0 +1,375 @@ +/* Flot plugin for plotting error bars. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +Error bars are used to show standard deviation and other statistical +properties in a plot. + +* Created by Rui Pereira - rui (dot) pereira (at) gmail (dot) com + +This plugin allows you to plot error-bars over points. Set "errorbars" inside +the points series to the axis name over which there will be error values in +your data array (*even* if you do not intend to plot them later, by setting +"show: null" on xerr/yerr). + +The plugin supports these options: + + series: { + points: { + errorbars: "x" or "y" or "xy", + xerr: { + show: null/false or true, + asymmetric: null/false or true, + upperCap: null or "-" or function, + lowerCap: null or "-" or function, + color: null or color, + radius: null or number + }, + yerr: { same options as xerr } + } + } + +Each data point array is expected to be of the type: + + "x" [ x, y, xerr ] + "y" [ x, y, yerr ] + "xy" [ x, y, xerr, yerr ] + +Where xerr becomes xerr_lower,xerr_upper for the asymmetric error case, and +equivalently for yerr. Eg., a datapoint for the "xy" case with symmetric +error-bars on X and asymmetric on Y would be: + + [ x, y, xerr, yerr_lower, yerr_upper ] + +By default no end caps are drawn. Setting upperCap and/or lowerCap to "-" will +draw a small cap perpendicular to the error bar. They can also be set to a +user-defined drawing function, with (ctx, x, y, radius) as parameters, as eg. + + function drawSemiCircle( ctx, x, y, radius ) { + ctx.beginPath(); + ctx.arc( x, y, radius, 0, Math.PI, false ); + ctx.moveTo( x - radius, y ); + ctx.lineTo( x + radius, y ); + ctx.stroke(); + } + +Color and radius both default to the same ones of the points series if not +set. The independent radius parameter on xerr/yerr is useful for the case when +we may want to add error-bars to a line, without showing the interconnecting +points (with radius: 0), and still showing end caps on the error-bars. +shadowSize and lineWidth are derived as well from the points series. + +*/ + +(function ($) { + var options = { + series: { + points: { + errorbars: null, //should be 'x', 'y' or 'xy' + xerr: {err: 'x', show: null, asymmetric: null, upperCap: null, lowerCap: null, color: null, radius: null}, + yerr: {err: 'y', show: null, asymmetric: null, upperCap: null, lowerCap: null, color: null, radius: null} + } + } + }; + + function processRawData(plot, series, data, datapoints) { + if (!series.points.errorbars) { + return; + } + + // x,y values + var format = [ + { x: true, number: true, required: true }, + { y: true, number: true, required: true } + ]; + + var errors = series.points.errorbars; + // error bars - first X then Y + if (errors === 'x' || errors === 'xy') { + // lower / upper error + if (series.points.xerr.asymmetric) { + format.push({ x: true, number: true, required: true }); + format.push({ x: true, number: true, required: true }); + } else { + format.push({ x: true, number: true, required: true }); + } + } + if (errors === 'y' || errors === 'xy') { + // lower / upper error + if (series.points.yerr.asymmetric) { + format.push({ y: true, number: true, required: true }); + format.push({ y: true, number: true, required: true }); + } else { + format.push({ y: true, number: true, required: true }); + } + } + datapoints.format = format; + } + + function parseErrors(series, i) { + var points = series.datapoints.points; + + // read errors from points array + var exl = null, + exu = null, + eyl = null, + eyu = null; + var xerr = series.points.xerr, + yerr = series.points.yerr; + + var eb = series.points.errorbars; + // error bars - first X + if (eb === 'x' || eb === 'xy') { + if (xerr.asymmetric) { + exl = points[i + 2]; + exu = points[i + 3]; + if (eb === 'xy') { + if (yerr.asymmetric) { + eyl = points[i + 4]; + eyu = points[i + 5]; + } else { + eyl = points[i + 4]; + } + } + } else { + exl = points[i + 2]; + if (eb === 'xy') { + if (yerr.asymmetric) { + eyl = points[i + 3]; + eyu = points[i + 4]; + } else { + eyl = points[i + 3]; + } + } + } + // only Y + } else { + if (eb === 'y') { + if (yerr.asymmetric) { + eyl = points[i + 2]; + eyu = points[i + 3]; + } else { + eyl = points[i + 2]; + } + } + } + + // symmetric errors? + if (exu == null) exu = exl; + if (eyu == null) eyu = eyl; + + var errRanges = [exl, exu, eyl, eyu]; + // nullify if not showing + if (!xerr.show) { + errRanges[0] = null; + errRanges[1] = null; + } + if (!yerr.show) { + errRanges[2] = null; + errRanges[3] = null; + } + return errRanges; + } + + function drawSeriesErrors(plot, ctx, s) { + var points = s.datapoints.points, + ps = s.datapoints.pointsize, + ax = [s.xaxis, s.yaxis], + radius = s.points.radius, + err = [s.points.xerr, s.points.yerr], + tmp; + + //sanity check, in case some inverted axis hack is applied to flot + var invertX = false; + if (ax[0].p2c(ax[0].max) < ax[0].p2c(ax[0].min)) { + invertX = true; + tmp = err[0].lowerCap; + err[0].lowerCap = err[0].upperCap; + err[0].upperCap = tmp; + } + + var invertY = false; + if (ax[1].p2c(ax[1].min) < ax[1].p2c(ax[1].max)) { + invertY = true; + tmp = err[1].lowerCap; + err[1].lowerCap = err[1].upperCap; + err[1].upperCap = tmp; + } + + for (var i = 0; i < s.datapoints.points.length; i += ps) { + //parse + var errRanges = parseErrors(s, i); + + //cycle xerr & yerr + for (var e = 0; e < err.length; e++) { + var minmax = [ax[e].min, ax[e].max]; + + //draw this error? + if (errRanges[e * err.length]) { + //data coordinates + var x = points[i], + y = points[i + 1]; + + //errorbar ranges + var upper = [x, y][e] + errRanges[e * err.length + 1], + lower = [x, y][e] - errRanges[e * err.length]; + + //points outside of the canvas + if (err[e].err === 'x') { + if (y > ax[1].max || y < ax[1].min || upper < ax[0].min || lower > ax[0].max) { + continue; + } + } + + if (err[e].err === 'y') { + if (x > ax[0].max || x < ax[0].min || upper < ax[1].min || lower > ax[1].max) { + continue; + } + } + + // prevent errorbars getting out of the canvas + var drawUpper = true, + drawLower = true; + + if (upper > minmax[1]) { + drawUpper = false; + upper = minmax[1]; + } + if (lower < minmax[0]) { + drawLower = false; + lower = minmax[0]; + } + + //sanity check, in case some inverted axis hack is applied to flot + if ((err[e].err === 'x' && invertX) || (err[e].err === 'y' && invertY)) { + //swap coordinates + tmp = lower; + lower = upper; + upper = tmp; + tmp = drawLower; + drawLower = drawUpper; + drawUpper = tmp; + tmp = minmax[0]; + minmax[0] = minmax[1]; + minmax[1] = tmp; + } + + // convert to pixels + x = ax[0].p2c(x); + y = ax[1].p2c(y); + upper = ax[e].p2c(upper); + lower = ax[e].p2c(lower); + minmax[0] = ax[e].p2c(minmax[0]); + minmax[1] = ax[e].p2c(minmax[1]); + + //same style as points by default + var lw = err[e].lineWidth ? err[e].lineWidth : s.points.lineWidth, + sw = s.points.shadowSize != null ? s.points.shadowSize : s.shadowSize; + + //shadow as for points + if (lw > 0 && sw > 0) { + var w = sw / 2; + ctx.lineWidth = w; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w + w / 2, minmax); + + ctx.strokeStyle = "rgba(0,0,0,0.2)"; + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w / 2, minmax); + } + + ctx.strokeStyle = err[e].color + ? err[e].color + : s.color; + ctx.lineWidth = lw; + //draw it + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, 0, minmax); + } + } + } + } + + function drawError(ctx, err, x, y, upper, lower, drawUpper, drawLower, radius, offset, minmax) { + //shadow offset + y += offset; + upper += offset; + lower += offset; + + // error bar - avoid plotting over circles + if (err.err === 'x') { + if (upper > x + radius) drawPath(ctx, [[upper, y], [Math.max(x + radius, minmax[0]), y]]); + else drawUpper = false; + + if (lower < x - radius) drawPath(ctx, [[Math.min(x - radius, minmax[1]), y], [lower, y]]); + else drawLower = false; + } else { + if (upper < y - radius) drawPath(ctx, [[x, upper], [x, Math.min(y - radius, minmax[0])]]); + else drawUpper = false; + + if (lower > y + radius) drawPath(ctx, [[x, Math.max(y + radius, minmax[1])], [x, lower]]); + else drawLower = false; + } + + //internal radius value in errorbar, allows to plot radius 0 points and still keep proper sized caps + //this is a way to get errorbars on lines without visible connecting dots + radius = err.radius != null + ? err.radius + : radius; + + // upper cap + if (drawUpper) { + if (err.upperCap === '-') { + if (err.err === 'x') drawPath(ctx, [[upper, y - radius], [upper, y + radius]]); + else drawPath(ctx, [[x - radius, upper], [x + radius, upper]]); + } else if ($.isFunction(err.upperCap)) { + if (err.err === 'x') err.upperCap(ctx, upper, y, radius); + else err.upperCap(ctx, x, upper, radius); + } + } + // lower cap + if (drawLower) { + if (err.lowerCap === '-') { + if (err.err === 'x') drawPath(ctx, [[lower, y - radius], [lower, y + radius]]); + else drawPath(ctx, [[x - radius, lower], [x + radius, lower]]); + } else if ($.isFunction(err.lowerCap)) { + if (err.err === 'x') err.lowerCap(ctx, lower, y, radius); + else err.lowerCap(ctx, x, lower, radius); + } + } + } + + function drawPath(ctx, pts) { + ctx.beginPath(); + ctx.moveTo(pts[0][0], pts[0][1]); + for (var p = 1; p < pts.length; p++) { + ctx.lineTo(pts[p][0], pts[p][1]); + } + + ctx.stroke(); + } + + function draw(plot, ctx) { + var plotOffset = plot.getPlotOffset(); + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + $.each(plot.getData(), function (i, s) { + if (s.points.errorbars && (s.points.xerr.show || s.points.yerr.show)) { + drawSeriesErrors(plot, ctx, s); + } + }); + ctx.restore(); + } + + function init(plot) { + plot.hooks.processRawData.push(processRawData); + plot.hooks.draw.push(draw); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'errorbars', + version: '1.0' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.fillbetween.js b/terahz/templates/lib/flot/jquery.flot.fillbetween.js new file mode 100644 index 0000000..96cb292 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.fillbetween.js @@ -0,0 +1,254 @@ +/* Flot plugin for computing bottoms for filled line and bar charts. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The case: you've got two series that you want to fill the area between. In Flot +terms, you need to use one as the fill bottom of the other. You can specify the +bottom of each data point as the third coordinate manually, or you can use this +plugin to compute it for you. + +In order to name the other series, you need to give it an id, like this: + + var dataset = [ + { data: [ ... ], id: "foo" } , // use default bottom + { data: [ ... ], fillBetween: "foo" }, // use first dataset as bottom + ]; + + $.plot($("#placeholder"), dataset, { lines: { show: true, fill: true }}); + +As a convenience, if the id given is a number that doesn't appear as an id in +the series, it is interpreted as the index in the array instead (so fillBetween: +0 can also mean the first series). + +Internally, the plugin modifies the datapoints in each series. For line series, +extra data points might be inserted through interpolation. Note that at points +where the bottom line is not defined (due to a null point or start/end of line), +the current line will show a gap too. The algorithm comes from the +jquery.flot.stack.js plugin, possibly some code could be shared. + +*/ + +(function ($) { + var options = { + series: { + fillBetween: null // or number + } + }; + + function init(plot) { + function findBottomSeries(s, allseries) { + var i; + + for (i = 0; i < allseries.length; ++i) { + if (allseries[ i ].id === s.fillBetween) { + return allseries[ i ]; + } + } + + if (typeof s.fillBetween === "number") { + if (s.fillBetween < 0 || s.fillBetween >= allseries.length) { + return null; + } + return allseries[ s.fillBetween ]; + } + + return null; + } + + function computeFormat(plot, s, data, datapoints) { + if (s.fillBetween == null) { + return; + } + + format = datapoints.format; + var plotHasId = function(id) { + var plotData = plot.getData(); + for (i = 0; i < plotData.length; i++) { + if (plotData[i].id === id) { + return true; + } + } + + return false; + } + + if (!format) { + format = []; + + format.push({ + x: true, + number: true, + computeRange: s.xaxis.options.autoScale !== 'none', + required: true + }); + format.push({ + y: true, + number: true, + computeRange: s.yaxis.options.autoScale !== 'none', + required: true + }); + + if (s.fillBetween !== undefined && s.fillBetween !== '' && plotHasId(s.fillBetween) && s.fillBetween !== s.id) { + format.push({ + x: false, + y: true, + number: true, + required: false, + computeRange: s.yaxis.options.autoScale !== 'none', + defaultValue: 0 + }); + } + + datapoints.format = format; + } + } + + function computeFillBottoms(plot, s, datapoints) { + if (s.fillBetween == null) { + return; + } + + var other = findBottomSeries(s, plot.getData()); + + if (!other) { + return; + } + + var ps = datapoints.pointsize, + points = datapoints.points, + otherps = other.datapoints.pointsize, + otherpoints = other.datapoints.points, + newpoints = [], + px, py, intery, qx, qy, bottom, + withlines = s.lines.show, + withbottom = ps > 2 && datapoints.format[2].y, + withsteps = withlines && s.lines.steps, + fromgap = true, + i = 0, + j = 0, + l, m; + + while (true) { + if (i >= points.length) { + break; + } + + l = newpoints.length; + + if (points[ i ] == null) { + // copy gaps + for (m = 0; m < ps; ++m) { + newpoints.push(points[ i + m ]); + } + + i += ps; + } else if (j >= otherpoints.length) { + // for lines, we can't use the rest of the points + if (!withlines) { + for (m = 0; m < ps; ++m) { + newpoints.push(points[ i + m ]); + } + } + + i += ps; + } else if (otherpoints[ j ] == null) { + // oops, got a gap + for (m = 0; m < ps; ++m) { + newpoints.push(null); + } + + fromgap = true; + j += otherps; + } else { + // cases where we actually got two points + px = points[ i ]; + py = points[ i + 1 ]; + qx = otherpoints[ j ]; + qy = otherpoints[ j + 1 ]; + bottom = 0; + + if (px === qx) { + for (m = 0; m < ps; ++m) { + newpoints.push(points[ i + m ]); + } + + //newpoints[ l + 1 ] += qy; + bottom = qy; + + i += ps; + j += otherps; + } else if (px > qx) { + // we got past point below, might need to + // insert interpolated extra point + + if (withlines && i > 0 && points[ i - ps ] != null) { + intery = py + (points[ i - ps + 1 ] - py) * (qx - px) / (points[ i - ps ] - px); + newpoints.push(qx); + newpoints.push(intery); + for (m = 2; m < ps; ++m) { + newpoints.push(points[ i + m ]); + } + bottom = qy; + } + + j += otherps; + } else { + // px < qx + // if we come from a gap, we just skip this point + + if (fromgap && withlines) { + i += ps; + continue; + } + + for (m = 0; m < ps; ++m) { + newpoints.push(points[ i + m ]); + } + + // we might be able to interpolate a point below, + // this can give us a better y + + if (withlines && j > 0 && otherpoints[ j - otherps ] != null) { + bottom = qy + (otherpoints[ j - otherps + 1 ] - qy) * (px - qx) / (otherpoints[ j - otherps ] - qx); + } + + //newpoints[l + 1] += bottom; + + i += ps; + } + + fromgap = false; + + if (l !== newpoints.length && withbottom) { + newpoints[ l + 2 ] = bottom; + } + } + + // maintain the line steps invariant + + if (withsteps && l !== newpoints.length && l > 0 && + newpoints[ l ] !== null && + newpoints[ l ] !== newpoints[ l - ps ] && + newpoints[ l + 1 ] !== newpoints[ l - ps + 1 ]) { + for (m = 0; m < ps; ++m) { + newpoints[ l + ps + m ] = newpoints[ l + m ]; + } + newpoints[ l + 1 ] = newpoints[ l - ps + 1 ]; + } + } + + datapoints.points = newpoints; + } + + plot.hooks.processRawData.push(computeFormat); + plot.hooks.processDatapoints.push(computeFillBottoms); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: "fillbetween", + version: "1.0" + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.flatdata.js b/terahz/templates/lib/flot/jquery.flot.flatdata.js new file mode 100644 index 0000000..b91d168 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.flatdata.js @@ -0,0 +1,47 @@ +/* Support for flat 1D data series. + +A 1D flat data series is a data series in the form of a regular 1D array. The +main reason for using a flat data series is that it performs better, consumes +less memory and generates less garbage collection than the regular flot format. + +Example: + + plot.setData([[[0,0], [1,1], [2,2], [3,3]]]); // regular flot format + plot.setData([{flatdata: true, data: [0, 1, 2, 3]}]); // flatdata format + +Set series.flatdata to true to enable this plugin. + +You can use series.start to specify the starting index of the series (default is 0) +You can use series.step to specify the interval between consecutive indexes of the series (default is 1) +*/ + +/* global jQuery*/ + +(function ($) { + 'use strict'; + + function process1DRawData(plot, series, data, datapoints) { + if (series.flatdata === true) { + var start = series.start || 0; + var step = typeof series.step === 'number' ? series.step : 1; + datapoints.pointsize = 2; + for (var i = 0, j = 0; i < data.length; i++, j += 2) { + datapoints.points[j] = start + (i * step); + datapoints.points[j + 1] = data[i]; + } + if (datapoints.points !== undefined) { + datapoints.points.length = data.length * 2; + } else { + datapoints.points = []; + } + } + } + + $.plot.plugins.push({ + init: function(plot) { + plot.hooks.processRawData.push(process1DRawData); + }, + name: 'flatdata', + version: '0.0.2' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.hover.js b/terahz/templates/lib/flot/jquery.flot.hover.js new file mode 100644 index 0000000..fadea8f --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.hover.js @@ -0,0 +1,350 @@ +/* global jQuery */ + +/** +## jquery.flot.hover.js + +This plugin is used for mouse hover and tap on a point of plot series. +It supports the following options: +```js +grid: { + hoverable: false, //to trigger plothover event on mouse hover or tap on a point + clickable: false //to trigger plotclick event on mouse hover +} +``` + +It listens to native mouse move event or click, as well as artificial generated +tap and touchevent. + +When the mouse is over a point or a tap on a point is performed, that point or +the correscponding bar will be highlighted and a "plothover" event will be generated. + +Custom "touchevent" is triggered when any touch interaction is made. Hover plugin +handles this events by unhighlighting all of the previously highlighted points and generates +"plothovercleanup" event to notify any part that is handling plothover (for exemple to cleanup +the tooltip from webcharts). +*/ + +(function($) { + 'use strict'; + + var options = { + grid: { + hoverable: false, + clickable: false + } + }; + + var browser = $.plot.browser; + + var eventType = { + click: 'click', + hover: 'hover' + } + + function init(plot) { + var lastMouseMoveEvent; + var highlights = []; + + function bindEvents(plot, eventHolder) { + var o = plot.getOptions(); + + if (o.grid.hoverable || o.grid.clickable) { + eventHolder[0].addEventListener('touchevent', triggerCleanupEvent, false); + eventHolder[0].addEventListener('tap', generatePlothoverEvent, false); + } + + if (o.grid.clickable) { + eventHolder.bind("click", onClick); + } + + if (o.grid.hoverable) { + eventHolder.bind("mousemove", onMouseMove); + + // Use bind, rather than .mouseleave, because we officially + // still support jQuery 1.2.6, which doesn't define a shortcut + // for mouseenter or mouseleave. This was a bug/oversight that + // was fixed somewhere around 1.3.x. We can return to using + // .mouseleave when we drop support for 1.2.6. + + eventHolder.bind("mouseleave", onMouseLeave); + } + } + + function shutdown(plot, eventHolder) { + eventHolder[0].removeEventListener('tap', generatePlothoverEvent); + eventHolder[0].removeEventListener('touchevent', triggerCleanupEvent); + eventHolder.unbind("mousemove", onMouseMove); + eventHolder.unbind("mouseleave", onMouseLeave); + eventHolder.unbind("click", onClick); + highlights = []; + } + + + function generatePlothoverEvent(e) { + var o = plot.getOptions(), + newEvent = new CustomEvent('mouseevent'); + + //transform from touch event to mouse event format + newEvent.pageX = e.detail.changedTouches[0].pageX; + newEvent.pageY = e.detail.changedTouches[0].pageY; + newEvent.clientX = e.detail.changedTouches[0].clientX; + newEvent.clientY = e.detail.changedTouches[0].clientY; + + if (o.grid.hoverable) { + doTriggerClickHoverEvent(newEvent, eventType.hover, 30); + } + return false; + } + + function doTriggerClickHoverEvent(event, eventType, searchDistance) { + var series = plot.getData(); + if (event !== undefined + && series.length > 0 + && series[0].xaxis.c2p !== undefined + && series[0].yaxis.c2p !== undefined) { + var eventToTrigger = "plot" + eventType; + var seriesFlag = eventType + "able"; + triggerClickHoverEvent(eventToTrigger, event, + function(i) { + return series[i][seriesFlag] !== false; + }, searchDistance); + } + } + + function onMouseMove(e) { + lastMouseMoveEvent = e; + plot.getPlaceholder()[0].lastMouseMoveEvent = e; + doTriggerClickHoverEvent(e, eventType.hover); + } + + function onMouseLeave(e) { + lastMouseMoveEvent = undefined; + plot.getPlaceholder()[0].lastMouseMoveEvent = undefined; + triggerClickHoverEvent("plothover", e, + function(i) { + return false; + }); + } + + function onClick(e) { + doTriggerClickHoverEvent(e, eventType.click); + } + + function triggerCleanupEvent() { + plot.unhighlight(); + plot.getPlaceholder().trigger('plothovercleanup'); + } + + // trigger click or hover event (they send the same parameters + // so we share their code) + function triggerClickHoverEvent(eventname, event, seriesFilter, searchDistance) { + var options = plot.getOptions(), + offset = plot.offset(), + page = browser.getPageXY(event), + canvasX = page.X - offset.left, + canvasY = page.Y - offset.top, + pos = plot.c2p({ + left: canvasX, + top: canvasY + }), + distance = searchDistance !== undefined ? searchDistance : options.grid.mouseActiveRadius; + + pos.pageX = page.X; + pos.pageY = page.Y; + + var item = plot.findNearbyItem(canvasX, canvasY, seriesFilter, distance); + + if (item) { + // fill in mouse pos for any listeners out there + item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left, 10); + item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top, 10); + } + + if (options.grid.autoHighlight) { + // clear auto-highlights + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if ((h.auto === eventname && + !(item && h.series === item.series && + h.point[0] === item.datapoint[0] && + h.point[1] === item.datapoint[1])) || !item) { + unhighlight(h.series, h.point); + } + } + + if (item) { + highlight(item.series, item.datapoint, eventname); + } + } + + plot.getPlaceholder().trigger(eventname, [pos, item]); + } + + function highlight(s, point, auto) { + if (typeof s === "number") { + s = plot.getData()[s]; + } + + if (typeof point === "number") { + var ps = s.datapoints.pointsize; + point = s.datapoints.points.slice(ps * point, ps * (point + 1)); + } + + var i = indexOfHighlight(s, point); + if (i === -1) { + highlights.push({ + series: s, + point: point, + auto: auto + }); + + plot.triggerRedrawOverlay(); + } else if (!auto) { + highlights[i].auto = false; + } + } + + function unhighlight(s, point) { + if (s == null && point == null) { + highlights = []; + plot.triggerRedrawOverlay(); + return; + } + + if (typeof s === "number") { + s = plot.getData()[s]; + } + + if (typeof point === "number") { + var ps = s.datapoints.pointsize; + point = s.datapoints.points.slice(ps * point, ps * (point + 1)); + } + + var i = indexOfHighlight(s, point); + if (i !== -1) { + highlights.splice(i, 1); + + plot.triggerRedrawOverlay(); + } + } + + function indexOfHighlight(s, p) { + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.series === s && + h.point[0] === p[0] && + h.point[1] === p[1]) { + return i; + } + } + + return -1; + } + + function processDatapoints() { + triggerCleanupEvent(); + doTriggerClickHoverEvent(lastMouseMoveEvent, eventType.hover); + } + + function setupGrid() { + doTriggerClickHoverEvent(lastMouseMoveEvent, eventType.hover); + } + + function drawOverlay(plot, octx, overlay) { + var plotOffset = plot.getPlotOffset(), + i, hi; + + octx.save(); + octx.translate(plotOffset.left, plotOffset.top); + for (i = 0; i < highlights.length; ++i) { + hi = highlights[i]; + + if (hi.series.bars.show) drawBarHighlight(hi.series, hi.point, octx); + else drawPointHighlight(hi.series, hi.point, octx, plot); + } + octx.restore(); + } + + function drawPointHighlight(series, point, octx, plot) { + var x = point[0], + y = point[1], + axisx = series.xaxis, + axisy = series.yaxis, + highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(); + + if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) { + return; + } + + var pointRadius = series.points.radius + series.points.lineWidth / 2; + octx.lineWidth = pointRadius; + octx.strokeStyle = highlightColor; + var radius = 1.5 * pointRadius; + x = axisx.p2c(x); + y = axisy.p2c(y); + + octx.beginPath(); + var symbol = series.points.symbol; + if (symbol === 'circle') { + octx.arc(x, y, radius, 0, 2 * Math.PI, false); + } else if (typeof symbol === 'string' && plot.drawSymbol && plot.drawSymbol[symbol]) { + plot.drawSymbol[symbol](octx, x, y, radius, false); + } + + octx.closePath(); + octx.stroke(); + } + + function drawBarHighlight(series, point, octx) { + var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(), + fillStyle = highlightColor, + barLeft; + + var barWidth = series.bars.barWidth[0] || series.bars.barWidth; + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -barWidth; + break; + default: + barLeft = -barWidth / 2; + } + + octx.lineWidth = series.bars.lineWidth; + octx.strokeStyle = highlightColor; + + var fillTowards = series.bars.fillTowards || 0, + bottom = fillTowards > series.yaxis.min ? Math.min(series.yaxis.max, fillTowards) : series.yaxis.min; + + $.plot.drawSeries.drawBar(point[0], point[1], point[2] || bottom, barLeft, barLeft + barWidth, + function() { + return fillStyle; + }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); + } + + function initHover(plot, options) { + plot.highlight = highlight; + plot.unhighlight = unhighlight; + if (options.grid.hoverable || options.grid.clickable) { + plot.hooks.drawOverlay.push(drawOverlay); + plot.hooks.processDatapoints.push(processDatapoints); + plot.hooks.setupGrid.push(setupGrid); + } + + lastMouseMoveEvent = plot.getPlaceholder()[0].lastMouseMoveEvent; + } + + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + plot.hooks.processOptions.push(initHover); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'hover', + version: '0.1' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.image.js b/terahz/templates/lib/flot/jquery.flot.image.js new file mode 100644 index 0000000..ae98fb4 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.image.js @@ -0,0 +1,249 @@ +/* Flot plugin for plotting images. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The data syntax is [ [ image, x1, y1, x2, y2 ], ... ] where (x1, y1) and +(x2, y2) are where you intend the two opposite corners of the image to end up +in the plot. Image must be a fully loaded Javascript image (you can make one +with new Image()). If the image is not complete, it's skipped when plotting. + +There are two helpers included for retrieving images. The easiest work the way +that you put in URLs instead of images in the data, like this: + + [ "myimage.png", 0, 0, 10, 10 ] + +Then call $.plot.image.loadData( data, options, callback ) where data and +options are the same as you pass in to $.plot. This loads the images, replaces +the URLs in the data with the corresponding images and calls "callback" when +all images are loaded (or failed loading). In the callback, you can then call +$.plot with the data set. See the included example. + +A more low-level helper, $.plot.image.load(urls, callback) is also included. +Given a list of URLs, it calls callback with an object mapping from URL to +Image object when all images are loaded or have failed loading. + +The plugin supports these options: + + series: { + images: { + show: boolean + anchor: "corner" or "center" + alpha: [ 0, 1 ] + } + } + +They can be specified for a specific series: + + $.plot( $("#placeholder"), [{ + data: [ ... ], + images: { ... } + ]) + +Note that because the data format is different from usual data points, you +can't use images with anything else in a specific data series. + +Setting "anchor" to "center" causes the pixels in the image to be anchored at +the corner pixel centers inside of at the pixel corners, effectively letting +half a pixel stick out to each side in the plot. + +A possible future direction could be support for tiling for large images (like +Google Maps). + +*/ + +(function ($) { + var options = { + series: { + images: { + show: false, + alpha: 1, + anchor: "corner" // or "center" + } + } + }; + + $.plot.image = {}; + + $.plot.image.loadDataImages = function (series, options, callback) { + var urls = [], points = []; + + var defaultShow = options.series.images.show; + + $.each(series, function (i, s) { + if (!(defaultShow || s.images.show)) { + return; + } + + if (s.data) { + s = s.data; + } + + $.each(s, function (i, p) { + if (typeof p[0] === "string") { + urls.push(p[0]); + points.push(p); + } + }); + }); + + $.plot.image.load(urls, function (loadedImages) { + $.each(points, function (i, p) { + var url = p[0]; + if (loadedImages[url]) { + p[0] = loadedImages[url]; + } + }); + + callback(); + }); + } + + $.plot.image.load = function (urls, callback) { + var missing = urls.length, loaded = {}; + if (missing === 0) { + callback({}); + } + + $.each(urls, function (i, url) { + var handler = function () { + --missing; + loaded[url] = this; + + if (missing === 0) { + callback(loaded); + } + }; + + $('').load(handler).error(handler).attr('src', url); + }); + }; + + function drawSeries(plot, ctx, series) { + var plotOffset = plot.getPlotOffset(); + + if (!series.images || !series.images.show) { + return; + } + + var points = series.datapoints.points, + ps = series.datapoints.pointsize; + + for (var i = 0; i < points.length; i += ps) { + var img = points[i], + x1 = points[i + 1], y1 = points[i + 2], + x2 = points[i + 3], y2 = points[i + 4], + xaxis = series.xaxis, yaxis = series.yaxis, + tmp; + + // actually we should check img.complete, but it + // appears to be a somewhat unreliable indicator in + // IE6 (false even after load event) + if (!img || img.width <= 0 || img.height <= 0) { + continue; + } + + if (x1 > x2) { + tmp = x2; + x2 = x1; + x1 = tmp; + } + if (y1 > y2) { + tmp = y2; + y2 = y1; + y1 = tmp; + } + + // if the anchor is at the center of the pixel, expand the + // image by 1/2 pixel in each direction + if (series.images.anchor === "center") { + tmp = 0.5 * (x2 - x1) / (img.width - 1); + x1 -= tmp; + x2 += tmp; + tmp = 0.5 * (y2 - y1) / (img.height - 1); + y1 -= tmp; + y2 += tmp; + } + + // clip + if (x1 === x2 || y1 === y2 || + x1 >= xaxis.max || x2 <= xaxis.min || + y1 >= yaxis.max || y2 <= yaxis.min) { + continue; + } + + var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height; + if (x1 < xaxis.min) { + sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1); + x1 = xaxis.min; + } + + if (x2 > xaxis.max) { + sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1); + x2 = xaxis.max; + } + + if (y1 < yaxis.min) { + sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1); + y1 = yaxis.min; + } + + if (y2 > yaxis.max) { + sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1); + y2 = yaxis.max; + } + + x1 = xaxis.p2c(x1); + x2 = xaxis.p2c(x2); + y1 = yaxis.p2c(y1); + y2 = yaxis.p2c(y2); + + // the transformation may have swapped us + if (x1 > x2) { + tmp = x2; + x2 = x1; + x1 = tmp; + } + if (y1 > y2) { + tmp = y2; + y2 = y1; + y1 = tmp; + } + + tmp = ctx.globalAlpha; + ctx.globalAlpha *= series.images.alpha; + ctx.drawImage(img, + sx1, sy1, sx2 - sx1, sy2 - sy1, + x1 + plotOffset.left, y1 + plotOffset.top, + x2 - x1, y2 - y1); + ctx.globalAlpha = tmp; + } + } + + function processRawData(plot, series, data, datapoints) { + if (!series.images.show) { + return; + } + + // format is Image, x1, y1, x2, y2 (opposite corners) + datapoints.format = [ + { required: true }, + { x: true, number: true, required: true }, + { y: true, number: true, required: true }, + { x: true, number: true, required: true }, + { y: true, number: true, required: true } + ]; + } + + function init(plot) { + plot.hooks.processRawData.push(processRawData); + plot.hooks.drawSeries.push(drawSeries); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'image', + version: '1.1' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.js b/terahz/templates/lib/flot/jquery.flot.js new file mode 100644 index 0000000..3750dfe --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.js @@ -0,0 +1,2787 @@ +/* Javascript plotting library for jQuery, version 3.0.0. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ + +// the actual Flot code +(function($) { + "use strict"; + + var Canvas = window.Flot.Canvas; + + function defaultTickGenerator(axis) { + var ticks = [], + start = $.plot.saturated.saturate($.plot.saturated.floorInBase(axis.min, axis.tickSize)), + i = 0, + v = Number.NaN, + prev; + + if (start === -Number.MAX_VALUE) { + ticks.push(start); + start = $.plot.saturated.floorInBase(axis.min + axis.tickSize, axis.tickSize); + } + + do { + prev = v; + //v = start + i * axis.tickSize; + v = $.plot.saturated.multiplyAdd(axis.tickSize, i, start); + ticks.push(v); + ++i; + } while (v < axis.max && v !== prev); + + return ticks; + } + + function defaultTickFormatter(value, axis, precision) { + var oldTickDecimals = axis.tickDecimals, + expPosition = ("" + value).indexOf("e"); + + if (expPosition !== -1) { + return expRepTickFormatter(value, axis, precision); + } + + if (precision > 0) { + axis.tickDecimals = precision; + } + + var factor = axis.tickDecimals ? parseFloat('1e' + axis.tickDecimals) : 1, + formatted = "" + Math.round(value * factor) / factor; + + // If tickDecimals was specified, ensure that we have exactly that + // much precision; otherwise default to the value's own precision. + if (axis.tickDecimals != null) { + var decimal = formatted.indexOf("."), + decimalPrecision = decimal === -1 ? 0 : formatted.length - decimal - 1; + if (decimalPrecision < axis.tickDecimals) { + var decimals = ("" + factor).substr(1, axis.tickDecimals - decimalPrecision); + formatted = (decimalPrecision ? formatted : formatted + ".") + decimals; + } + } + + axis.tickDecimals = oldTickDecimals; + return formatted; + }; + + function expRepTickFormatter(value, axis, precision) { + var expPosition = ("" + value).indexOf("e"), + exponentValue = parseInt(("" + value).substr(expPosition + 1)), + tenExponent = expPosition !== -1 ? exponentValue : (value > 0 ? Math.floor(Math.log(value) / Math.LN10) : 0), + roundWith = parseFloat('1e' + tenExponent), + x = value / roundWith; + + if (precision) { + var updatedPrecision = recomputePrecision(value, precision); + return (value / roundWith).toFixed(updatedPrecision) + 'e' + tenExponent; + } + + if (axis.tickDecimals > 0) { + return x.toFixed(recomputePrecision(value, axis.tickDecimals)) + 'e' + tenExponent; + } + return x.toFixed() + 'e' + tenExponent; + } + + function recomputePrecision(num, precision) { + //for numbers close to zero, the precision from flot will be a big number + //while for big numbers, the precision will be negative + var log10Value = Math.log(Math.abs(num)) * Math.LOG10E, + newPrecision = Math.abs(log10Value + precision); + + return newPrecision <= 20 ? Math.floor(newPrecision) : 20; + } + + /////////////////////////////////////////////////////////////////////////// + // The top-level container for the entire plot. + function Plot(placeholder, data_, options_, plugins) { + // data is on the form: + // [ series1, series2 ... ] + // where series is either just the data as [ [x1, y1], [x2, y2], ... ] + // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... } + + var series = [], + options = { + // the color theme used for graphs + colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], + xaxis: { + show: null, // null = auto-detect, true = always, false = never + position: "bottom", // or "top" + mode: null, // null or "time" + font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" } + color: null, // base color, labels, ticks + tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)" + transform: null, // null or f: number -> number to transform axis + inverseTransform: null, // if transform is set, this should be the inverse function + min: null, // min. value to show, null means set automatically + max: null, // max. value to show, null means set automatically + autoScaleMargin: null, // margin in % to add if autoScale option is on "loose" mode, + autoScale: "exact", // Available modes: "none", "loose", "exact", "sliding-window" + windowSize: null, // null or number. This is the size of sliding-window. + growOnly: null, // grow only, useful for smoother auto-scale, the scales will grow to accomodate data but won't shrink back. + ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks + tickFormatter: null, // fn: number -> string + showTickLabels: "major", // "none", "endpoints", "major", "all" + labelWidth: null, // size of tick labels in pixels + labelHeight: null, + reserveSpace: null, // whether to reserve space even if axis isn't shown + tickLength: null, // size in pixels of major tick marks + showMinorTicks: null, // true = show minor tick marks, false = hide minor tick marks + showTicks: null, // true = show tick marks, false = hide all tick marks + gridLines: null, // true = show grid lines, false = hide grid lines + alignTicksWithAxis: null, // axis number or null for no sync + tickDecimals: null, // no. of decimals, null means auto + tickSize: null, // number or [number, "unit"] + minTickSize: null, // number or [number, "unit"] + offset: { below: 0, above: 0 }, // the plot drawing offset. this is calculated by the flot.navigate for each axis + boxPosition: { centerX: 0, centerY: 0 } //position of the axis on the corresponding axis box + }, + yaxis: { + autoScaleMargin: 0.02, // margin in % to add if autoScale option is on "loose" mode + autoScale: "loose", // Available modes: "none", "loose", "exact" + growOnly: null, // grow only, useful for smoother auto-scale, the scales will grow to accomodate data but won't shrink back. + position: "left", // or "right" + showTickLabels: "major", // "none", "endpoints", "major", "all" + offset: { below: 0, above: 0 }, // the plot drawing offset. this is calculated by the flot.navigate for each axis + boxPosition: { centerX: 0, centerY: 0 } //position of the axis on the corresponding axis box + }, + xaxes: [], + yaxes: [], + series: { + points: { + show: false, + radius: 3, + lineWidth: 2, // in pixels + fill: true, + fillColor: "#ffffff", + symbol: 'circle' // or callback + }, + lines: { + // we don't put in show: false so we can see + // whether lines were actively disabled + lineWidth: 1, // in pixels + fill: false, + fillColor: null, + steps: false + // Omit 'zero', so we can later default its value to + // match that of the 'fill' option. + }, + bars: { + show: false, + lineWidth: 2, // in pixels + // barWidth: number or [number, absolute] + // when 'absolute' is false, 'number' is relative to the minimum distance between points for the series + // when 'absolute' is true, 'number' is considered to be in units of the x-axis + horizontal: false, + barWidth: 0.8, + fill: true, + fillColor: null, + align: "left", // "left", "right", or "center" + zero: true + }, + shadowSize: 3, + highlightColor: null + }, + grid: { + show: true, + aboveData: false, + color: "#545454", // primary color used for outline and labels + backgroundColor: null, // null for transparent, else color + borderColor: null, // set if different from the grid color + tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)" + margin: 0, // distance from the canvas edge to the grid + labelMargin: 5, // in pixels + axisMargin: 8, // in pixels + borderWidth: 1, // in pixels + minBorderMargin: null, // in pixels, null means taken from points radius + markings: null, // array of ranges or fn: axes -> array of ranges + markingsColor: "#f4f4f4", + markingsLineWidth: 2, + // interactive stuff + clickable: false, + hoverable: false, + autoHighlight: true, // highlight in case mouse is near + mouseActiveRadius: 15 // how far the mouse can be away to activate an item + }, + interaction: { + redrawOverlayInterval: 1000 / 60 // time between updates, -1 means in same flow + }, + hooks: {} + }, + surface = null, // the canvas for the plot itself + overlay = null, // canvas for interactive stuff on top of plot + eventHolder = null, // jQuery object that events should be bound to + ctx = null, + octx = null, + xaxes = [], + yaxes = [], + plotOffset = { + left: 0, + right: 0, + top: 0, + bottom: 0 + }, + plotWidth = 0, + plotHeight = 0, + hooks = { + processOptions: [], + processRawData: [], + processDatapoints: [], + processOffset: [], + setupGrid: [], + adjustSeriesDataRange: [], + setRange: [], + drawBackground: [], + drawSeries: [], + drawAxis: [], + draw: [], + axisReserveSpace: [], + bindEvents: [], + drawOverlay: [], + resize: [], + shutdown: [] + }, + plot = this; + + var eventManager = {}; + + // interactive features + + var redrawTimeout = null; + + // public functions + plot.setData = setData; + plot.setupGrid = setupGrid; + plot.draw = draw; + plot.getPlaceholder = function() { + return placeholder; + }; + plot.getCanvas = function() { + return surface.element; + }; + plot.getSurface = function() { + return surface; + }; + plot.getEventHolder = function() { + return eventHolder[0]; + }; + plot.getPlotOffset = function() { + return plotOffset; + }; + plot.width = function() { + return plotWidth; + }; + plot.height = function() { + return plotHeight; + }; + plot.offset = function() { + var o = eventHolder.offset(); + o.left += plotOffset.left; + o.top += plotOffset.top; + return o; + }; + plot.getData = function() { + return series; + }; + plot.getAxes = function() { + var res = {}; + $.each(xaxes.concat(yaxes), function(_, axis) { + if (axis) { + res[axis.direction + (axis.n !== 1 ? axis.n : "") + "axis"] = axis; + } + }); + return res; + }; + plot.getXAxes = function() { + return xaxes; + }; + plot.getYAxes = function() { + return yaxes; + }; + plot.c2p = canvasToCartesianAxisCoords; + plot.p2c = cartesianAxisToCanvasCoords; + plot.getOptions = function() { + return options; + }; + plot.triggerRedrawOverlay = triggerRedrawOverlay; + plot.pointOffset = function(point) { + return { + left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10), + top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10) + }; + }; + plot.shutdown = shutdown; + plot.destroy = function() { + shutdown(); + placeholder.removeData("plot").empty(); + + series = []; + options = null; + surface = null; + overlay = null; + eventHolder = null; + ctx = null; + octx = null; + xaxes = []; + yaxes = []; + hooks = null; + plot = null; + }; + + plot.resize = function() { + var width = placeholder.width(), + height = placeholder.height(); + surface.resize(width, height); + overlay.resize(width, height); + + executeHooks(hooks.resize, [width, height]); + }; + + plot.clearTextCache = function () { + surface.clearCache(); + overlay.clearCache(); + }; + + plot.autoScaleAxis = autoScaleAxis; + plot.computeRangeForDataSeries = computeRangeForDataSeries; + plot.adjustSeriesDataRange = adjustSeriesDataRange; + plot.findNearbyItem = findNearbyItem; + plot.findNearbyInterpolationPoint = findNearbyInterpolationPoint; + plot.computeValuePrecision = computeValuePrecision; + plot.computeTickSize = computeTickSize; + plot.addEventHandler = addEventHandler; + + // public attributes + plot.hooks = hooks; + + // initialize + var MINOR_TICKS_COUNT_CONSTANT = $.plot.uiConstants.MINOR_TICKS_COUNT_CONSTANT; + var TICK_LENGTH_CONSTANT = $.plot.uiConstants.TICK_LENGTH_CONSTANT; + initPlugins(plot); + setupCanvases(); + parseOptions(options_); + setData(data_); + setupGrid(true); + draw(); + bindEvents(); + + function executeHooks(hook, args) { + args = [plot].concat(args); + for (var i = 0; i < hook.length; ++i) { + hook[i].apply(this, args); + } + } + + function initPlugins() { + // References to key classes, allowing plugins to modify them + + var classes = { + Canvas: Canvas + }; + + for (var i = 0; i < plugins.length; ++i) { + var p = plugins[i]; + p.init(plot, classes); + if (p.options) { + $.extend(true, options, p.options); + } + } + } + + function parseOptions(opts) { + $.extend(true, options, opts); + + // $.extend merges arrays, rather than replacing them. When less + // colors are provided than the size of the default palette, we + // end up with those colors plus the remaining defaults, which is + // not expected behavior; avoid it by replacing them here. + + if (opts && opts.colors) { + options.colors = opts.colors; + } + + if (options.xaxis.color == null) { + options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + } + + if (options.yaxis.color == null) { + options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + } + + if (options.xaxis.tickColor == null) { + // grid.tickColor for back-compatibility + options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color; + } + + if (options.yaxis.tickColor == null) { + // grid.tickColor for back-compatibility + options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color; + } + + if (options.grid.borderColor == null) { + options.grid.borderColor = options.grid.color; + } + + if (options.grid.tickColor == null) { + options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + } + + // Fill in defaults for axis options, including any unspecified + // font-spec fields, if a font-spec was provided. + + // If no x/y axis options were provided, create one of each anyway, + // since the rest of the code assumes that they exist. + + var i, axisOptions, axisCount, + fontSize = placeholder.css("font-size"), + fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13, + fontDefaults = { + style: placeholder.css("font-style"), + size: Math.round(0.8 * fontSizeDefault), + variant: placeholder.css("font-variant"), + weight: placeholder.css("font-weight"), + family: placeholder.css("font-family") + }; + + axisCount = options.xaxes.length || 1; + for (i = 0; i < axisCount; ++i) { + axisOptions = options.xaxes[i]; + if (axisOptions && !axisOptions.tickColor) { + axisOptions.tickColor = axisOptions.color; + } + + axisOptions = $.extend(true, {}, options.xaxis, axisOptions); + options.xaxes[i] = axisOptions; + + if (axisOptions.font) { + axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); + if (!axisOptions.font.color) { + axisOptions.font.color = axisOptions.color; + } + if (!axisOptions.font.lineHeight) { + axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); + } + } + } + + axisCount = options.yaxes.length || 1; + for (i = 0; i < axisCount; ++i) { + axisOptions = options.yaxes[i]; + if (axisOptions && !axisOptions.tickColor) { + axisOptions.tickColor = axisOptions.color; + } + + axisOptions = $.extend(true, {}, options.yaxis, axisOptions); + options.yaxes[i] = axisOptions; + + if (axisOptions.font) { + axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); + if (!axisOptions.font.color) { + axisOptions.font.color = axisOptions.color; + } + if (!axisOptions.font.lineHeight) { + axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); + } + } + } + + // save options on axes for future reference + for (i = 0; i < options.xaxes.length; ++i) { + getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i]; + } + + for (i = 0; i < options.yaxes.length; ++i) { + getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i]; + } + + //process boxPosition options used for axis.box size + $.each(allAxes(), function(_, axis) { + axis.boxPosition = axis.options.boxPosition || {centerX: 0, centerY: 0}; + }); + + // add hooks from options + for (var n in hooks) { + if (options.hooks[n] && options.hooks[n].length) { + hooks[n] = hooks[n].concat(options.hooks[n]); + } + } + + executeHooks(hooks.processOptions, [options]); + } + + function setData(d) { + var oldseries = series; + series = parseData(d); + fillInSeriesOptions(); + processData(oldseries); + } + + function parseData(d) { + var res = []; + for (var i = 0; i < d.length; ++i) { + var s = $.extend(true, {}, options.series); + + if (d[i].data != null) { + s.data = d[i].data; // move the data instead of deep-copy + delete d[i].data; + + $.extend(true, s, d[i]); + + d[i].data = s.data; + } else { + s.data = d[i]; + } + + res.push(s); + } + + return res; + } + + function axisNumber(obj, coord) { + var a = obj[coord + "axis"]; + if (typeof a === "object") { + // if we got a real axis, extract number + a = a.n; + } + + if (typeof a !== "number") { + a = 1; // default to first axis + } + + return a; + } + + function allAxes() { + // return flat array without annoying null entries + return xaxes.concat(yaxes).filter(function(a) { + return a; + }); + } + + // canvas to axis for cartesian axes + function canvasToCartesianAxisCoords(pos) { + // return an object with x/y corresponding to all used axes + var res = {}, + i, axis; + for (i = 0; i < xaxes.length; ++i) { + axis = xaxes[i]; + if (axis && axis.used) { + res["x" + axis.n] = axis.c2p(pos.left); + } + } + + for (i = 0; i < yaxes.length; ++i) { + axis = yaxes[i]; + if (axis && axis.used) { + res["y" + axis.n] = axis.c2p(pos.top); + } + } + + if (res.x1 !== undefined) { + res.x = res.x1; + } + + if (res.y1 !== undefined) { + res.y = res.y1; + } + + return res; + } + + // axis to canvas for cartesian axes + function cartesianAxisToCanvasCoords(pos) { + // get canvas coords from the first pair of x/y found in pos + var res = {}, + i, axis, key; + + for (i = 0; i < xaxes.length; ++i) { + axis = xaxes[i]; + if (axis && axis.used) { + key = "x" + axis.n; + if (pos[key] == null && axis.n === 1) { + key = "x"; + } + + if (pos[key] != null) { + res.left = axis.p2c(pos[key]); + break; + } + } + } + + for (i = 0; i < yaxes.length; ++i) { + axis = yaxes[i]; + if (axis && axis.used) { + key = "y" + axis.n; + if (pos[key] == null && axis.n === 1) { + key = "y"; + } + + if (pos[key] != null) { + res.top = axis.p2c(pos[key]); + break; + } + } + } + + return res; + } + + function getOrCreateAxis(axes, number) { + if (!axes[number - 1]) { + axes[number - 1] = { + n: number, // save the number for future reference + direction: axes === xaxes ? "x" : "y", + options: $.extend(true, {}, axes === xaxes ? options.xaxis : options.yaxis) + }; + } + + return axes[number - 1]; + } + + function fillInSeriesOptions() { + var neededColors = series.length, + maxIndex = -1, + i; + + // Subtract the number of series that already have fixed colors or + // color indexes from the number that we still need to generate. + + for (i = 0; i < series.length; ++i) { + var sc = series[i].color; + if (sc != null) { + neededColors--; + if (typeof sc === "number" && sc > maxIndex) { + maxIndex = sc; + } + } + } + + // If any of the series have fixed color indexes, then we need to + // generate at least as many colors as the highest index. + + if (neededColors <= maxIndex) { + neededColors = maxIndex + 1; + } + + // Generate all the colors, using first the option colors and then + // variations on those colors once they're exhausted. + + var c, colors = [], + colorPool = options.colors, + colorPoolSize = colorPool.length, + variation = 0, + definedColors = Math.max(0, series.length - neededColors); + + for (i = 0; i < neededColors; i++) { + c = $.color.parse(colorPool[(definedColors + i) % colorPoolSize] || "#666"); + + // Each time we exhaust the colors in the pool we adjust + // a scaling factor used to produce more variations on + // those colors. The factor alternates negative/positive + // to produce lighter/darker colors. + + // Reset the variation after every few cycles, or else + // it will end up producing only white or black colors. + + if (i % colorPoolSize === 0 && i) { + if (variation >= 0) { + if (variation < 0.5) { + variation = -variation - 0.2; + } else variation = 0; + } else variation = -variation; + } + + colors[i] = c.scale('rgb', 1 + variation); + } + + // Finalize the series options, filling in their colors + + var colori = 0, + s; + for (i = 0; i < series.length; ++i) { + s = series[i]; + + // assign colors + if (s.color == null) { + s.color = colors[colori].toString(); + ++colori; + } else if (typeof s.color === "number") { + s.color = colors[s.color].toString(); + } + + // turn on lines automatically in case nothing is set + if (s.lines.show == null) { + var v, show = true; + for (v in s) { + if (s[v] && s[v].show) { + show = false; + break; + } + } + + if (show) { + s.lines.show = true; + } + } + + // If nothing was provided for lines.zero, default it to match + // lines.fill, since areas by default should extend to zero. + + if (s.lines.zero == null) { + s.lines.zero = !!s.lines.fill; + } + + // setup axes + s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x")); + s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y")); + } + } + + function processData(prevSeries) { + var topSentry = Number.POSITIVE_INFINITY, + bottomSentry = Number.NEGATIVE_INFINITY, + i, j, k, m, + s, points, ps, val, f, p, + data, format; + + function updateAxis(axis, min, max) { + if (min < axis.datamin && min !== -Infinity) { + axis.datamin = min; + } + + if (max > axis.datamax && max !== Infinity) { + axis.datamax = max; + } + } + + function reusePoints(prevSeries, i) { + if (prevSeries && prevSeries[i] && prevSeries[i].datapoints && prevSeries[i].datapoints.points) { + return prevSeries[i].datapoints.points; + } + + return []; + } + + $.each(allAxes(), function(_, axis) { + // init axis + if (axis.options.growOnly !== true) { + axis.datamin = topSentry; + axis.datamax = bottomSentry; + } else { + if (axis.datamin === undefined) { + axis.datamin = topSentry; + } + if (axis.datamax === undefined) { + axis.datamax = bottomSentry; + } + } + axis.used = false; + }); + + for (i = 0; i < series.length; ++i) { + s = series[i]; + s.datapoints = { + points: [] + }; + + if (s.datapoints.points.length === 0) { + s.datapoints.points = reusePoints(prevSeries, i); + } + + executeHooks(hooks.processRawData, [s, s.data, s.datapoints]); + } + + // first pass: clean and copy data + for (i = 0; i < series.length; ++i) { + s = series[i]; + + data = s.data; + format = s.datapoints.format; + + if (!format) { + format = []; + // find out how to copy + format.push({ + x: true, + y: false, + number: true, + required: true, + computeRange: s.xaxis.options.autoScale !== 'none', + defaultValue: null + }); + + format.push({ + x: false, + y: true, + number: true, + required: true, + computeRange: s.yaxis.options.autoScale !== 'none', + defaultValue: null + }); + + if (s.stack || s.bars.show || (s.lines.show && s.lines.fill)) { + var expectedPs = s.datapoints.pointsize != null ? s.datapoints.pointsize : (s.data && s.data[0] && s.data[0].length ? s.data[0].length : 3); + if (expectedPs > 2) { + format.push({ + x: false, + y: true, + number: true, + required: false, + computeRange: s.yaxis.options.autoScale !== 'none', + defaultValue: 0 + }); + } + } + + s.datapoints.format = format; + } + + s.xaxis.used = s.yaxis.used = true; + + if (s.datapoints.pointsize != null) continue; // already filled in + + s.datapoints.pointsize = format.length; + ps = s.datapoints.pointsize; + points = s.datapoints.points; + + var insertSteps = s.lines.show && s.lines.steps; + + for (j = k = 0; j < data.length; ++j, k += ps) { + p = data[j]; + + var nullify = p == null; + if (!nullify) { + for (m = 0; m < ps; ++m) { + val = p[m]; + f = format[m]; + + if (f) { + if (f.number && val != null) { + val = +val; // convert to number + if (isNaN(val)) { + val = null; + } + } + + if (val == null) { + if (f.required) nullify = true; + + if (f.defaultValue != null) val = f.defaultValue; + } + } + + points[k + m] = val; + } + } + + if (nullify) { + for (m = 0; m < ps; ++m) { + val = points[k + m]; + if (val != null) { + f = format[m]; + // extract min/max info + if (f.computeRange) { + if (f.x) { + updateAxis(s.xaxis, val, val); + } + if (f.y) { + updateAxis(s.yaxis, val, val); + } + } + } + points[k + m] = null; + } + } + } + + points.length = k; //trims the internal buffer to the correct length + } + + // give the hooks a chance to run + for (i = 0; i < series.length; ++i) { + s = series[i]; + + executeHooks(hooks.processDatapoints, [s, s.datapoints]); + } + + // second pass: find datamax/datamin for auto-scaling + for (i = 0; i < series.length; ++i) { + s = series[i]; + format = s.datapoints.format; + + if (format.every(function (f) { return !f.computeRange; })) { + continue; + } + + var range = plot.adjustSeriesDataRange(s, + plot.computeRangeForDataSeries(s)); + + executeHooks(hooks.adjustSeriesDataRange, [s, range]); + + updateAxis(s.xaxis, range.xmin, range.xmax); + updateAxis(s.yaxis, range.ymin, range.ymax); + } + + $.each(allAxes(), function(_, axis) { + if (axis.datamin === topSentry) { + axis.datamin = null; + } + + if (axis.datamax === bottomSentry) { + axis.datamax = null; + } + }); + } + + function setupCanvases() { + // Make sure the placeholder is clear of everything except canvases + // from a previous plot in this container that we'll try to re-use. + + placeholder.css("padding", 0) // padding messes up the positioning + .children().filter(function() { + return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base'); + }).remove(); + + if (placeholder.css("position") === 'static') { + placeholder.css("position", "relative"); // for positioning labels and overlay + } + + surface = new Canvas("flot-base", placeholder[0]); + overlay = new Canvas("flot-overlay", placeholder[0]); // overlay canvas for interactive features + + ctx = surface.context; + octx = overlay.context; + + // define which element we're listening for events on + eventHolder = $(overlay.element).unbind(); + + // If we're re-using a plot object, shut down the old one + + var existing = placeholder.data("plot"); + + if (existing) { + existing.shutdown(); + overlay.clear(); + } + + // save in case we get replotted + placeholder.data("plot", plot); + } + + function bindEvents() { + executeHooks(hooks.bindEvents, [eventHolder]); + } + + function addEventHandler(event, handler, eventHolder, priority) { + var key = eventHolder + event; + var eventList = eventManager[key] || []; + + eventList.push({"event": event, "handler": handler, "eventHolder": eventHolder, "priority": priority}); + eventList.sort((a, b) => b.priority - a.priority ); + eventList.forEach( eventData => { + eventData.eventHolder.unbind(eventData.event, eventData.handler); + eventData.eventHolder.bind(eventData.event, eventData.handler); + }); + + eventManager[key] = eventList; + } + + function shutdown() { + if (redrawTimeout) { + clearTimeout(redrawTimeout); + } + + executeHooks(hooks.shutdown, [eventHolder]); + } + + function setTransformationHelpers(axis) { + // set helper functions on the axis, assumes plot area + // has been computed already + + function identity(x) { + return x; + } + + var s, m, t = axis.options.transform || identity, + it = axis.options.inverseTransform; + + // precompute how much the axis is scaling a point + // in canvas space + if (axis.direction === "x") { + if (isFinite(t(axis.max) - t(axis.min))) { + s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); + } else { + s = axis.scale = 1 / Math.abs($.plot.saturated.delta(t(axis.min), t(axis.max), plotWidth)); + } + m = Math.min(t(axis.max), t(axis.min)); + } else { + if (isFinite(t(axis.max) - t(axis.min))) { + s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); + } else { + s = axis.scale = 1 / Math.abs($.plot.saturated.delta(t(axis.min), t(axis.max), plotHeight)); + } + s = -s; + m = Math.max(t(axis.max), t(axis.min)); + } + + // data point to canvas coordinate + if (t === identity) { + // slight optimization + axis.p2c = function(p) { + if (isFinite(p - m)) { + return (p - m) * s; + } else { + return (p / 4 - m / 4) * s * 4; + } + }; + } else { + axis.p2c = function(p) { + var tp = t(p); + + if (isFinite(tp - m)) { + return (tp - m) * s; + } else { + return (tp / 4 - m / 4) * s * 4; + } + }; + } + + // canvas coordinate to data point + if (!it) { + axis.c2p = function(c) { + return m + c / s; + }; + } else { + axis.c2p = function(c) { + return it(m + c / s); + }; + } + } + + function measureTickLabels(axis) { + var opts = axis.options, + ticks = opts.showTickLabels !== 'none' && axis.ticks ? axis.ticks : [], + showMajorTickLabels = opts.showTickLabels === 'major' || opts.showTickLabels === 'all', + showEndpointsTickLabels = opts.showTickLabels === 'endpoints' || opts.showTickLabels === 'all', + labelWidth = opts.labelWidth || 0, + labelHeight = opts.labelHeight || 0, + legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", + layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, + font = opts.font || "flot-tick-label tickLabel"; + + for (var i = 0; i < ticks.length; ++i) { + var t = ticks[i]; + var label = t.label; + + if (!t.label || + (showMajorTickLabels === false && i > 0 && i < ticks.length - 1) || + (showEndpointsTickLabels === false && (i === 0 || i === ticks.length - 1))) { + continue; + } + + if (typeof t.label === 'object') { + label = t.label.name; + } + + var info = surface.getTextInfo(layer, label, font); + + labelWidth = Math.max(labelWidth, info.width); + labelHeight = Math.max(labelHeight, info.height); + } + + axis.labelWidth = opts.labelWidth || labelWidth; + axis.labelHeight = opts.labelHeight || labelHeight; + } + + function allocateAxisBoxFirstPhase(axis) { + // find the bounding box of the axis by looking at label + // widths/heights and ticks, make room by diminishing the + // plotOffset; this first phase only looks at one + // dimension per axis, the other dimension depends on the + // other axes so will have to wait + + // here reserve additional space + executeHooks(hooks.axisReserveSpace, [axis]); + + var lw = axis.labelWidth, + lh = axis.labelHeight, + pos = axis.options.position, + isXAxis = axis.direction === "x", + tickLength = axis.options.tickLength, + showTicks = axis.options.showTicks, + showMinorTicks = axis.options.showMinorTicks, + gridLines = axis.options.gridLines, + axisMargin = options.grid.axisMargin, + padding = options.grid.labelMargin, + innermost = true, + outermost = true, + found = false; + + // Determine the axis's position in its direction and on its side + + $.each(isXAxis ? xaxes : yaxes, function(i, a) { + if (a && (a.show || a.reserveSpace)) { + if (a === axis) { + found = true; + } else if (a.options.position === pos) { + if (found) { + outermost = false; + } else { + innermost = false; + } + } + } + }); + + // The outermost axis on each side has no margin + if (outermost) { + axisMargin = 0; + } + + // Set the default tickLength if necessary + if (tickLength == null) { + tickLength = TICK_LENGTH_CONSTANT; + } + + // By default, major tick marks are visible + if (showTicks == null) { + showTicks = true; + } + + // By default, minor tick marks are visible + if (showMinorTicks == null) { + showMinorTicks = true; + } + + // By default, grid lines are visible + if (gridLines == null) { + if (innermost) { + gridLines = true; + } else { + gridLines = false; + } + } + + if (!isNaN(+tickLength)) { + padding += showTicks ? +tickLength : 0; + } + + if (isXAxis) { + lh += padding; + + if (pos === "bottom") { + plotOffset.bottom += lh + axisMargin; + axis.box = { + top: surface.height - plotOffset.bottom, + height: lh + }; + } else { + axis.box = { + top: plotOffset.top + axisMargin, + height: lh + }; + plotOffset.top += lh + axisMargin; + } + } else { + lw += padding; + + if (pos === "left") { + axis.box = { + left: plotOffset.left + axisMargin, + width: lw + }; + plotOffset.left += lw + axisMargin; + } else { + plotOffset.right += lw + axisMargin; + axis.box = { + left: surface.width - plotOffset.right, + width: lw + }; + } + } + + // save for future reference + axis.position = pos; + axis.tickLength = tickLength; + axis.showMinorTicks = showMinorTicks; + axis.showTicks = showTicks; + axis.gridLines = gridLines; + axis.box.padding = padding; + axis.innermost = innermost; + } + + function allocateAxisBoxSecondPhase(axis) { + // now that all axis boxes have been placed in one + // dimension, we can set the remaining dimension coordinates + if (axis.direction === "x") { + axis.box.left = plotOffset.left - axis.labelWidth / 2; + axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth; + } else { + axis.box.top = plotOffset.top - axis.labelHeight / 2; + axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight; + } + } + + function adjustLayoutForThingsStickingOut() { + // possibly adjust plot offset to ensure everything stays + // inside the canvas and isn't clipped off + + var minMargin = options.grid.minBorderMargin, + i; + + // check stuff from the plot (FIXME: this should just read + // a value from the series, otherwise it's impossible to + // customize) + if (minMargin == null) { + minMargin = 0; + for (i = 0; i < series.length; ++i) { + minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth / 2)); + } + } + + var a, offset = {}, + margins = { + left: minMargin, + right: minMargin, + top: minMargin, + bottom: minMargin + }; + + // check axis labels, note we don't check the actual + // labels but instead use the overall width/height to not + // jump as much around with replots + $.each(allAxes(), function(_, axis) { + if (axis.reserveSpace && axis.ticks && axis.ticks.length) { + if (axis.direction === "x") { + margins.left = Math.max(margins.left, axis.labelWidth / 2); + margins.right = Math.max(margins.right, axis.labelWidth / 2); + } else { + margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2); + margins.top = Math.max(margins.top, axis.labelHeight / 2); + } + } + }); + + for (a in margins) { + offset[a] = margins[a] - plotOffset[a]; + } + $.each(xaxes.concat(yaxes), function(_, axis) { + alignAxisWithGrid(axis, offset, function (offset) { + return offset > 0; + }); + }); + + plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left)); + plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right)); + plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top)); + plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom)); + } + + function alignAxisWithGrid(axis, offset, isValid) { + if (axis.direction === "x") { + if (axis.position === "bottom" && isValid(offset.bottom)) { + axis.box.top -= Math.ceil(offset.bottom); + } + if (axis.position === "top" && isValid(offset.top)) { + axis.box.top += Math.ceil(offset.top); + } + } else { + if (axis.position === "left" && isValid(offset.left)) { + axis.box.left += Math.ceil(offset.left); + } + if (axis.position === "right" && isValid(offset.right)) { + axis.box.left -= Math.ceil(offset.right); + } + } + } + + function setupGrid(autoScale) { + var i, a, axes = allAxes(), + showGrid = options.grid.show; + + // Initialize the plot's offset from the edge of the canvas + + for (a in plotOffset) { + plotOffset[a] = 0; + } + + executeHooks(hooks.processOffset, [plotOffset]); + + // If the grid is visible, add its border width to the offset + for (a in plotOffset) { + if (typeof (options.grid.borderWidth) === "object") { + plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0; + } else { + plotOffset[a] += showGrid ? options.grid.borderWidth : 0; + } + } + + $.each(axes, function(_, axis) { + var axisOpts = axis.options; + axis.show = axisOpts.show == null ? axis.used : axisOpts.show; + axis.reserveSpace = axisOpts.reserveSpace == null ? axis.show : axisOpts.reserveSpace; + setupTickFormatter(axis); + executeHooks(hooks.setRange, [axis, autoScale]); + setRange(axis, autoScale); + }); + + if (showGrid) { + plotWidth = surface.width - plotOffset.left - plotOffset.right; + plotHeight = surface.height - plotOffset.bottom - plotOffset.top; + + var allocatedAxes = $.grep(axes, function(axis) { + return axis.show || axis.reserveSpace; + }); + + $.each(allocatedAxes, function(_, axis) { + // make the ticks + setupTickGeneration(axis); + setMajorTicks(axis); + snapRangeToTicks(axis, axis.ticks, series); + + //for computing the endpoints precision, transformationHelpers are needed + setTransformationHelpers(axis); + setEndpointTicks(axis, series); + + // find labelWidth/Height for axis + measureTickLabels(axis); + }); + + // with all dimensions calculated, we can compute the + // axis bounding boxes, start from the outside + // (reverse order) + for (i = allocatedAxes.length - 1; i >= 0; --i) { + allocateAxisBoxFirstPhase(allocatedAxes[i]); + } + + // make sure we've got enough space for things that + // might stick out + adjustLayoutForThingsStickingOut(); + + $.each(allocatedAxes, function(_, axis) { + allocateAxisBoxSecondPhase(axis); + }); + } + + //adjust axis and plotOffset according to grid.margins + if (options.grid.margin) { + for (a in plotOffset) { + var margin = options.grid.margin || 0; + plotOffset[a] += typeof margin === "number" ? margin : (margin[a] || 0); + } + $.each(xaxes.concat(yaxes), function(_, axis) { + alignAxisWithGrid(axis, options.grid.margin, function(offset) { + return offset !== undefined && offset !== null; + }); + }); + } + + //after adjusting the axis, plot width and height will be modified + plotWidth = surface.width - plotOffset.left - plotOffset.right; + plotHeight = surface.height - plotOffset.bottom - plotOffset.top; + + // now we got the proper plot dimensions, we can compute the scaling + $.each(axes, function(_, axis) { + setTransformationHelpers(axis); + }); + + if (showGrid) { + drawAxisLabels(); + } + + executeHooks(hooks.setupGrid, []); + } + + function widenMinMax(minimum, maximum) { + var min = (minimum === undefined ? null : minimum); + var max = (maximum === undefined ? null : maximum); + var delta = max - min; + if (delta === 0.0) { + // degenerate case + var widen = max === 0 ? 1 : 0.01; + var wmin = null; + if (min == null) { + wmin -= widen; + } + + // always widen max if we couldn't widen min to ensure we + // don't fall into min == max which doesn't work + if (max == null || min != null) { + max += widen; + } + + if (wmin != null) { + min = wmin; + } + } + + return { + min: min, + max: max + }; + } + + function autoScaleAxis(axis) { + var opts = axis.options, + min = opts.min, + max = opts.max, + datamin = axis.datamin, + datamax = axis.datamax, + delta; + + switch (opts.autoScale) { + case "none": + min = +(opts.min != null ? opts.min : datamin); + max = +(opts.max != null ? opts.max : datamax); + break; + case "loose": + if (datamin != null && datamax != null) { + min = datamin; + max = datamax; + delta = $.plot.saturated.saturate(max - min); + var margin = ((typeof opts.autoScaleMargin === 'number') ? opts.autoScaleMargin : 0.02); + min = $.plot.saturated.saturate(min - delta * margin); + max = $.plot.saturated.saturate(max + delta * margin); + + // make sure we don't go below zero if all values are positive + if (min < 0 && datamin >= 0) { + min = 0; + } + } else { + min = opts.min; + max = opts.max; + } + break; + case "exact": + min = (datamin != null ? datamin : opts.min); + max = (datamax != null ? datamax : opts.max); + break; + case "sliding-window": + if (datamax > max) { + // move the window to fit the new data, + // keeping the axis range constant + max = datamax; + min = Math.max(datamax - (opts.windowSize || 100), min); + } + break; + } + + var widenedMinMax = widenMinMax(min, max); + min = widenedMinMax.min; + max = widenedMinMax.max; + + // grow loose or grow exact supported + if (opts.growOnly === true && opts.autoScale !== "none" && opts.autoScale !== "sliding-window") { + min = (min < datamin) ? min : (datamin !== null ? datamin : min); + max = (max > datamax) ? max : (datamax !== null ? datamax : max); + } + + axis.autoScaledMin = min; + axis.autoScaledMax = max; + } + + function setRange(axis, autoScale) { + var min = typeof axis.options.min === 'number' ? axis.options.min : axis.min, + max = typeof axis.options.max === 'number' ? axis.options.max : axis.max, + plotOffset = axis.options.offset; + + if (autoScale) { + autoScaleAxis(axis); + min = axis.autoScaledMin; + max = axis.autoScaledMax; + } + + min = (min != null ? min : -1) + (plotOffset.below || 0); + max = (max != null ? max : 1) + (plotOffset.above || 0); + + if (min > max) { + var tmp = min; + min = max; + max = tmp; + axis.options.offset = { above: 0, below: 0 }; + } + + axis.min = $.plot.saturated.saturate(min); + axis.max = $.plot.saturated.saturate(max); + } + + function computeValuePrecision (min, max, direction, ticks, tickDecimals) { + var noTicks = fixupNumberOfTicks(direction, surface, ticks); + + var delta = $.plot.saturated.delta(min, max, noTicks), + dec = -Math.floor(Math.log(delta) / Math.LN10); + + //if it is called with tickDecimals, then the precision should not be greather then that + if (tickDecimals && dec > tickDecimals) { + dec = tickDecimals; + } + + var magn = parseFloat('1e' + (-dec)), + norm = delta / magn; + + if (norm > 2.25 && norm < 3 && (dec + 1) <= tickDecimals) { + //we need an extra decimals when tickSize is 2.5 + ++dec; + } + + return isFinite(dec) ? dec : 0; + }; + + function computeTickSize (min, max, noTicks, tickDecimals) { + var delta = $.plot.saturated.delta(min, max, noTicks), + dec = -Math.floor(Math.log(delta) / Math.LN10); + + //if it is called with tickDecimals, then the precision should not be greather then that + if (tickDecimals && dec > tickDecimals) { + dec = tickDecimals; + } + + var magn = parseFloat('1e' + (-dec)), + norm = delta / magn, // norm is between 1.0 and 10.0 + size; + + if (norm < 1.5) { + size = 1; + } else if (norm < 3) { + size = 2; + if (norm > 2.25 && (tickDecimals == null || (dec + 1) <= tickDecimals)) { + size = 2.5; + } + } else if (norm < 7.5) { + size = 5; + } else { + size = 10; + } + + size *= magn; + return size; + } + + function getAxisTickSize(min, max, direction, options, tickDecimals) { + var noTicks; + + if (typeof options.ticks === "number" && options.ticks > 0) { + noTicks = options.ticks; + } else { + // heuristic based on the model a*sqrt(x) fitted to + // some data points that seemed reasonable + noTicks = 0.3 * Math.sqrt(direction === "x" ? surface.width : surface.height); + } + + var size = computeTickSize(min, max, noTicks, tickDecimals); + + if (options.minTickSize != null && size < options.minTickSize) { + size = options.minTickSize; + } + + return options.tickSize || size; + }; + + function fixupNumberOfTicks(direction, surface, ticksOption) { + var noTicks; + + if (typeof ticksOption === "number" && ticksOption > 0) { + noTicks = ticksOption; + } else { + noTicks = 0.3 * Math.sqrt(direction === "x" ? surface.width : surface.height); + } + + return noTicks; + } + + function setupTickFormatter(axis) { + var opts = axis.options; + if (!axis.tickFormatter) { + if (typeof opts.tickFormatter === 'function') { + axis.tickFormatter = function() { + var args = Array.prototype.slice.call(arguments); + return "" + opts.tickFormatter.apply(null, args); + }; + } else { + axis.tickFormatter = defaultTickFormatter; + } + } + } + + function setupTickGeneration(axis) { + var opts = axis.options; + var noTicks; + + noTicks = fixupNumberOfTicks(axis.direction, surface, opts.ticks); + + axis.delta = $.plot.saturated.delta(axis.min, axis.max, noTicks); + var precision = plot.computeValuePrecision(axis.min, axis.max, axis.direction, noTicks, opts.tickDecimals); + + axis.tickDecimals = Math.max(0, opts.tickDecimals != null ? opts.tickDecimals : precision); + axis.tickSize = getAxisTickSize(axis.min, axis.max, axis.direction, opts, opts.tickDecimals); + + // Flot supports base-10 axes; any other mode else is handled by a plug-in, + // like flot.time.js. + + if (!axis.tickGenerator) { + if (typeof opts.tickGenerator === 'function') { + axis.tickGenerator = opts.tickGenerator; + } else { + axis.tickGenerator = defaultTickGenerator; + } + } + + if (opts.alignTicksWithAxis != null) { + var otherAxis = (axis.direction === "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; + if (otherAxis && otherAxis.used && otherAxis !== axis) { + // consider snapping min/max to outermost nice ticks + var niceTicks = axis.tickGenerator(axis, plot); + if (niceTicks.length > 0) { + if (opts.min == null) { + axis.min = Math.min(axis.min, niceTicks[0]); + } + + if (opts.max == null && niceTicks.length > 1) { + axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]); + } + } + + axis.tickGenerator = function(axis) { + // copy ticks, scaled to this axis + var ticks = [], + v, i; + for (i = 0; i < otherAxis.ticks.length; ++i) { + v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min); + v = axis.min + v * (axis.max - axis.min); + ticks.push(v); + } + return ticks; + }; + + // we might need an extra decimal since forced + // ticks don't necessarily fit naturally + if (!axis.mode && opts.tickDecimals == null) { + var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1), + ts = axis.tickGenerator(axis, plot); + + // only proceed if the tick interval rounded + // with an extra decimal doesn't give us a + // zero at end + if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) { + axis.tickDecimals = extraDec; + } + } + } + } + } + + function setMajorTicks(axis) { + var oticks = axis.options.ticks, + ticks = []; + if (oticks == null || (typeof oticks === "number" && oticks > 0)) { + ticks = axis.tickGenerator(axis, plot); + } else if (oticks) { + if ($.isFunction(oticks)) { + // generate the ticks + ticks = oticks(axis); + } else { + ticks = oticks; + } + } + + // clean up/labelify the supplied ticks, copy them over + var i, v; + axis.ticks = []; + for (i = 0; i < ticks.length; ++i) { + var label = null; + var t = ticks[i]; + if (typeof t === "object") { + v = +t[0]; + if (t.length > 1) { + label = t[1]; + } + } else { + v = +t; + } + + if (!isNaN(v)) { + axis.ticks.push( + newTick(v, label, axis, 'major')); + } + } + } + + function newTick(v, label, axis, type) { + if (label === null) { + switch (type) { + case 'min': + case 'max': + //improving the precision of endpoints + var precision = getEndpointPrecision(v, axis); + label = isFinite(precision) ? axis.tickFormatter(v, axis, precision, plot) : axis.tickFormatter(v, axis, precision, plot); + break; + case 'major': + label = axis.tickFormatter(v, axis, undefined, plot); + } + } + return { + v: v, + label: label + }; + } + + function snapRangeToTicks(axis, ticks, series) { + var anyDataInSeries = function(series) { + return series.some(e => e.datapoints.points.length > 0); + } + + if (axis.options.autoScale === "loose" && ticks.length > 0 && anyDataInSeries(series)) { + // snap to ticks + axis.min = Math.min(axis.min, ticks[0].v); + axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); + } + } + + function getEndpointPrecision(value, axis) { + var canvas1 = Math.floor(axis.p2c(value)), + canvas2 = axis.direction === "x" ? canvas1 + 1 : canvas1 - 1, + point1 = axis.c2p(canvas1), + point2 = axis.c2p(canvas2), + precision = computeValuePrecision(point1, point2, axis.direction, 1); + + return precision; + } + + function setEndpointTicks(axis, series) { + if (isValidEndpointTick(axis, series)) { + axis.ticks.unshift(newTick(axis.min, null, axis, 'min')); + axis.ticks.push(newTick(axis.max, null, axis, 'max')); + } + } + + function isValidEndpointTick(axis, series) { + if (axis.options.showTickLabels === 'endpoints') { + return true; + } + if (axis.options.showTickLabels === 'all') { + var associatedSeries = series.filter(function(s) { + return s.xaxis === axis; + }), + notAllBarSeries = associatedSeries.some(function(s) { + return !s.bars.show; + }); + return associatedSeries.length === 0 || notAllBarSeries; + } + if (axis.options.showTickLabels === 'major' || axis.options.showTickLabels === 'none') { + return false; + } + } + + function draw() { + surface.clear(); + executeHooks(hooks.drawBackground, [ctx]); + + var grid = options.grid; + + // draw background, if any + if (grid.show && grid.backgroundColor) { + drawBackground(); + } + + if (grid.show && !grid.aboveData) { + drawGrid(); + } + + for (var i = 0; i < series.length; ++i) { + executeHooks(hooks.drawSeries, [ctx, series[i], i, getColorOrGradient]); + drawSeries(series[i]); + } + + executeHooks(hooks.draw, [ctx]); + + if (grid.show && grid.aboveData) { + drawGrid(); + } + + surface.render(); + + // A draw implies that either the axes or data have changed, so we + // should probably update the overlay highlights as well. + triggerRedrawOverlay(); + } + + function extractRange(ranges, coord) { + var axis, from, to, key, axes = allAxes(); + + for (var i = 0; i < axes.length; ++i) { + axis = axes[i]; + if (axis.direction === coord) { + key = coord + axis.n + "axis"; + if (!ranges[key] && axis.n === 1) { + // support x1axis as xaxis + key = coord + "axis"; + } + + if (ranges[key]) { + from = ranges[key].from; + to = ranges[key].to; + break; + } + } + } + + // backwards-compat stuff - to be removed in future + if (!ranges[key]) { + axis = coord === "x" ? xaxes[0] : yaxes[0]; + from = ranges[coord + "1"]; + to = ranges[coord + "2"]; + } + + // auto-reverse as an added bonus + if (from != null && to != null && from > to) { + var tmp = from; + from = to; + to = tmp; + } + + return { + from: from, + to: to, + axis: axis + }; + } + + function drawBackground() { + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)"); + ctx.fillRect(0, 0, plotWidth, plotHeight); + ctx.restore(); + } + + function drawMarkings() { + // draw markings + var markings = options.grid.markings, + axes; + + if (markings) { + if ($.isFunction(markings)) { + axes = plot.getAxes(); + // xmin etc. is backwards compatibility, to be + // removed in the future + axes.xmin = axes.xaxis.min; + axes.xmax = axes.xaxis.max; + axes.ymin = axes.yaxis.min; + axes.ymax = axes.yaxis.max; + + markings = markings(axes); + } + + var i; + for (i = 0; i < markings.length; ++i) { + var m = markings[i], + xrange = extractRange(m, "x"), + yrange = extractRange(m, "y"); + + // fill in missing + if (xrange.from == null) { + xrange.from = xrange.axis.min; + } + + if (xrange.to == null) { + xrange.to = xrange.axis.max; + } + + if (yrange.from == null) { + yrange.from = yrange.axis.min; + } + + if (yrange.to == null) { + yrange.to = yrange.axis.max; + } + + // clip + if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || + yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) { + continue; + } + + xrange.from = Math.max(xrange.from, xrange.axis.min); + xrange.to = Math.min(xrange.to, xrange.axis.max); + yrange.from = Math.max(yrange.from, yrange.axis.min); + yrange.to = Math.min(yrange.to, yrange.axis.max); + + var xequal = xrange.from === xrange.to, + yequal = yrange.from === yrange.to; + + if (xequal && yequal) { + continue; + } + + // then draw + xrange.from = Math.floor(xrange.axis.p2c(xrange.from)); + xrange.to = Math.floor(xrange.axis.p2c(xrange.to)); + yrange.from = Math.floor(yrange.axis.p2c(yrange.from)); + yrange.to = Math.floor(yrange.axis.p2c(yrange.to)); + + if (xequal || yequal) { + var lineWidth = m.lineWidth || options.grid.markingsLineWidth, + subPixel = lineWidth % 2 ? 0.5 : 0; + ctx.beginPath(); + ctx.strokeStyle = m.color || options.grid.markingsColor; + ctx.lineWidth = lineWidth; + if (xequal) { + ctx.moveTo(xrange.to + subPixel, yrange.from); + ctx.lineTo(xrange.to + subPixel, yrange.to); + } else { + ctx.moveTo(xrange.from, yrange.to + subPixel); + ctx.lineTo(xrange.to, yrange.to + subPixel); + } + ctx.stroke(); + } else { + ctx.fillStyle = m.color || options.grid.markingsColor; + ctx.fillRect(xrange.from, yrange.to, + xrange.to - xrange.from, + yrange.from - yrange.to); + } + } + } + } + + function findEdges(axis) { + var box = axis.box, + x = 0, + y = 0; + + // find the edges + if (axis.direction === "x") { + x = 0; + y = box.top - plotOffset.top + (axis.position === "top" ? box.height : 0); + } else { + y = 0; + x = box.left - plotOffset.left + (axis.position === "left" ? box.width : 0) + axis.boxPosition.centerX; + } + + return { + x: x, + y: y + }; + }; + + function alignPosition(lineWidth, pos) { + return ((lineWidth % 2) !== 0) ? Math.floor(pos) + 0.5 : pos; + }; + + function drawTickBar(axis) { + ctx.lineWidth = 1; + var edges = findEdges(axis), + x = edges.x, + y = edges.y; + + // draw tick bar + if (axis.show) { + var xoff = 0, + yoff = 0; + + ctx.strokeStyle = axis.options.color; + ctx.beginPath(); + if (axis.direction === "x") { + xoff = plotWidth + 1; + } else { + yoff = plotHeight + 1; + } + + if (axis.direction === "x") { + y = alignPosition(ctx.lineWidth, y); + } else { + x = alignPosition(ctx.lineWidth, x); + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); + ctx.stroke(); + } + }; + + function drawTickMarks(axis) { + var t = axis.tickLength, + minorTicks = axis.showMinorTicks, + minorTicksNr = MINOR_TICKS_COUNT_CONSTANT, + edges = findEdges(axis), + x = edges.x, + y = edges.y, + i = 0; + + // draw major tick marks + ctx.strokeStyle = axis.options.color; + ctx.beginPath(); + + for (i = 0; i < axis.ticks.length; ++i) { + var v = axis.ticks[i].v, + xoff = 0, + yoff = 0, + xminor = 0, + yminor = 0, + j; + + if (!isNaN(v) && v >= axis.min && v <= axis.max) { + if (axis.direction === "x") { + x = axis.p2c(v); + yoff = t; + + if (axis.position === "top") { + yoff = -yoff; + } + } else { + y = axis.p2c(v); + xoff = t; + + if (axis.position === "left") { + xoff = -xoff; + } + } + + if (axis.direction === "x") { + x = alignPosition(ctx.lineWidth, x); + } else { + y = alignPosition(ctx.lineWidth, y); + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); + } + + //draw minor tick marks + if (minorTicks === true && i < axis.ticks.length - 1) { + var v1 = axis.ticks[i].v, + v2 = axis.ticks[i + 1].v, + step = (v2 - v1) / (minorTicksNr + 1); + + for (j = 1; j <= minorTicksNr; j++) { + // compute minor tick position + if (axis.direction === "x") { + yminor = t / 2; // minor ticks are half length + x = alignPosition(ctx.lineWidth, axis.p2c(v1 + j * step)) + + if (axis.position === "top") { + yminor = -yminor; + } + + // don't go over the plot borders + if ((x < 0) || (x > plotWidth)) { + continue; + } + } else { + xminor = t / 2; // minor ticks are half length + y = alignPosition(ctx.lineWidth, axis.p2c(v1 + j * step)); + + if (axis.position === "left") { + xminor = -xminor; + } + + // don't go over the plot borders + if ((y < 0) || (y > plotHeight)) { + continue; + } + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xminor, y + yminor); + } + } + } + + ctx.stroke(); + }; + + function drawGridLines(axis) { + // check if the line will be overlapped with a border + var overlappedWithBorder = function (value) { + var bw = options.grid.borderWidth; + return (((typeof bw === "object" && bw[axis.position] > 0) || bw > 0) && (value === axis.min || value === axis.max)); + }; + + ctx.strokeStyle = options.grid.tickColor; + ctx.beginPath(); + var i; + for (i = 0; i < axis.ticks.length; ++i) { + var v = axis.ticks[i].v, + xoff = 0, + yoff = 0, + x = 0, + y = 0; + + if (isNaN(v) || v < axis.min || v > axis.max) continue; + + // skip those lying on the axes if we got a border + if (overlappedWithBorder(v)) continue; + + if (axis.direction === "x") { + x = axis.p2c(v); + y = plotHeight; + yoff = -plotHeight; + } else { + x = 0; + y = axis.p2c(v); + xoff = plotWidth; + } + + if (axis.direction === "x") { + x = alignPosition(ctx.lineWidth, x); + } else { + y = alignPosition(ctx.lineWidth, y); + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); + } + + ctx.stroke(); + }; + + function drawBorder() { + // If either borderWidth or borderColor is an object, then draw the border + // line by line instead of as one rectangle + var bw = options.grid.borderWidth, + bc = options.grid.borderColor; + + if (typeof bw === "object" || typeof bc === "object") { + if (typeof bw !== "object") { + bw = { + top: bw, + right: bw, + bottom: bw, + left: bw + }; + } + if (typeof bc !== "object") { + bc = { + top: bc, + right: bc, + bottom: bc, + left: bc + }; + } + + if (bw.top > 0) { + ctx.strokeStyle = bc.top; + ctx.lineWidth = bw.top; + ctx.beginPath(); + ctx.moveTo(0 - bw.left, 0 - bw.top / 2); + ctx.lineTo(plotWidth, 0 - bw.top / 2); + ctx.stroke(); + } + + if (bw.right > 0) { + ctx.strokeStyle = bc.right; + ctx.lineWidth = bw.right; + ctx.beginPath(); + ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top); + ctx.lineTo(plotWidth + bw.right / 2, plotHeight); + ctx.stroke(); + } + + if (bw.bottom > 0) { + ctx.strokeStyle = bc.bottom; + ctx.lineWidth = bw.bottom; + ctx.beginPath(); + ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2); + ctx.lineTo(0, plotHeight + bw.bottom / 2); + ctx.stroke(); + } + + if (bw.left > 0) { + ctx.strokeStyle = bc.left; + ctx.lineWidth = bw.left; + ctx.beginPath(); + ctx.moveTo(0 - bw.left / 2, plotHeight + bw.bottom); + ctx.lineTo(0 - bw.left / 2, 0); + ctx.stroke(); + } + } else { + ctx.lineWidth = bw; + ctx.strokeStyle = options.grid.borderColor; + ctx.strokeRect(-bw / 2, -bw / 2, plotWidth + bw, plotHeight + bw); + } + }; + + function drawGrid() { + var axes, bw; + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + drawMarkings(); + + axes = allAxes(); + bw = options.grid.borderWidth; + + for (var j = 0; j < axes.length; ++j) { + var axis = axes[j]; + + if (!axis.show) { + continue; + } + + drawTickBar(axis); + if (axis.showTicks === true) { + drawTickMarks(axis); + } + + if (axis.gridLines === true) { + drawGridLines(axis, bw); + } + } + + // draw border + if (bw) { + drawBorder(); + } + + ctx.restore(); + } + + function drawAxisLabels() { + $.each(allAxes(), function(_, axis) { + var box = axis.box, + legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", + layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, + font = axis.options.font || "flot-tick-label tickLabel", + i, x, y, halign, valign, info, + margin = 3, + nullBox = {x: NaN, y: NaN, width: NaN, height: NaN}, newLabelBox, labelBoxes = [], + overlapping = function(x11, y11, x12, y12, x21, y21, x22, y22) { + return ((x11 <= x21 && x21 <= x12) || (x21 <= x11 && x11 <= x22)) && + ((y11 <= y21 && y21 <= y12) || (y21 <= y11 && y11 <= y22)); + }, + overlapsOtherLabels = function(newLabelBox, previousLabelBoxes) { + return previousLabelBoxes.some(function(labelBox) { + return overlapping( + newLabelBox.x, newLabelBox.y, newLabelBox.x + newLabelBox.width, newLabelBox.y + newLabelBox.height, + labelBox.x, labelBox.y, labelBox.x + labelBox.width, labelBox.y + labelBox.height); + }); + }, + drawAxisLabel = function (tick, labelBoxes) { + if (!tick || !tick.label || tick.v < axis.min || tick.v > axis.max) { + return nullBox; + } + + info = surface.getTextInfo(layer, tick.label, font); + + if (axis.direction === "x") { + halign = "center"; + x = plotOffset.left + axis.p2c(tick.v); + if (axis.position === "bottom") { + y = box.top + box.padding - axis.boxPosition.centerY; + } else { + y = box.top + box.height - box.padding + axis.boxPosition.centerY; + valign = "bottom"; + } + newLabelBox = {x: x - info.width / 2 - margin, y: y - margin, width: info.width + 2 * margin, height: info.height + 2 * margin}; + } else { + valign = "middle"; + y = plotOffset.top + axis.p2c(tick.v); + if (axis.position === "left") { + x = box.left + box.width - box.padding - axis.boxPosition.centerX; + halign = "right"; + } else { + x = box.left + box.padding + axis.boxPosition.centerX; + } + newLabelBox = {x: x - info.width / 2 - margin, y: y - margin, width: info.width + 2 * margin, height: info.height + 2 * margin}; + } + + if (overlapsOtherLabels(newLabelBox, labelBoxes)) { + return nullBox; + } + + surface.addText(layer, x, y, tick.label, font, null, null, halign, valign); + + return newLabelBox; + }; + + // Remove text before checking for axis.show and ticks.length; + // otherwise plugins, like flot-tickrotor, that draw their own + // tick labels will end up with both theirs and the defaults. + + surface.removeText(layer); + + executeHooks(hooks.drawAxis, [axis, surface]); + + if (!axis.show) { + return; + } + + switch (axis.options.showTickLabels) { + case 'none': + break; + case 'endpoints': + labelBoxes.push(drawAxisLabel(axis.ticks[0], labelBoxes)); + labelBoxes.push(drawAxisLabel(axis.ticks[axis.ticks.length - 1], labelBoxes)); + break; + case 'major': + labelBoxes.push(drawAxisLabel(axis.ticks[0], labelBoxes)); + labelBoxes.push(drawAxisLabel(axis.ticks[axis.ticks.length - 1], labelBoxes)); + for (i = 1; i < axis.ticks.length - 1; ++i) { + labelBoxes.push(drawAxisLabel(axis.ticks[i], labelBoxes)); + } + break; + case 'all': + labelBoxes.push(drawAxisLabel(axis.ticks[0], [])); + labelBoxes.push(drawAxisLabel(axis.ticks[axis.ticks.length - 1], labelBoxes)); + for (i = 1; i < axis.ticks.length - 1; ++i) { + labelBoxes.push(drawAxisLabel(axis.ticks[i], labelBoxes)); + } + break; + } + }); + } + + function drawSeries(series) { + if (series.lines.show) { + $.plot.drawSeries.drawSeriesLines(series, ctx, plotOffset, plotWidth, plotHeight, plot.drawSymbol, getColorOrGradient); + } + + if (series.bars.show) { + $.plot.drawSeries.drawSeriesBars(series, ctx, plotOffset, plotWidth, plotHeight, plot.drawSymbol, getColorOrGradient); + } + + if (series.points.show) { + $.plot.drawSeries.drawSeriesPoints(series, ctx, plotOffset, plotWidth, plotHeight, plot.drawSymbol, getColorOrGradient); + } + } + + function computeRangeForDataSeries(series, force, isValid) { + var points = series.datapoints.points, + ps = series.datapoints.pointsize, + format = series.datapoints.format, + topSentry = Number.POSITIVE_INFINITY, + bottomSentry = Number.NEGATIVE_INFINITY, + range = { + xmin: topSentry, + ymin: topSentry, + xmax: bottomSentry, + ymax: bottomSentry + }; + + for (var j = 0; j < points.length; j += ps) { + if (points[j] === null) { + continue; + } + + if (typeof (isValid) === 'function' && !isValid(points[j])) { + continue; + } + + for (var m = 0; m < ps; ++m) { + var val = points[j + m], + f = format[m]; + if (f === null || f === undefined) { + continue; + } + + if (typeof (isValid) === 'function' && !isValid(val)) { + continue; + } + + if ((!force && !f.computeRange) || val === Infinity || val === -Infinity) { + continue; + } + + if (f.x === true) { + if (val < range.xmin) { + range.xmin = val; + } + + if (val > range.xmax) { + range.xmax = val; + } + } + + if (f.y === true) { + if (val < range.ymin) { + range.ymin = val; + } + + if (val > range.ymax) { + range.ymax = val; + } + } + } + } + + return range; + }; + + function adjustSeriesDataRange(series, range) { + if (series.bars.show) { + // make sure we got room for the bar on the dancing floor + var delta; + + // update bar width if needed + var useAbsoluteBarWidth = series.bars.barWidth[1]; + if (series.datapoints && series.datapoints.points && !useAbsoluteBarWidth) { + computeBarWidth(series); + } + + var barWidth = series.bars.barWidth[0] || series.bars.barWidth; + switch (series.bars.align) { + case "left": + delta = 0; + break; + case "right": + delta = -barWidth; + break; + default: + delta = -barWidth / 2; + } + + if (series.bars.horizontal) { + range.ymin += delta; + range.ymax += delta + barWidth; + } + else { + range.xmin += delta; + range.xmax += delta + barWidth; + } + } + + if ((series.bars.show && series.bars.zero) || (series.lines.show && series.lines.zero)) { + var ps = series.datapoints.pointsize; + + // make sure the 0 point is included in the computed y range when requested + if (ps <= 2) { + /*if ps > 0 the points were already taken into account for autoScale */ + range.ymin = Math.min(0, range.ymin); + range.ymax = Math.max(0, range.ymax); + } + } + + return range; + }; + + function computeBarWidth(series) { + var xValues = []; + var pointsize = series.datapoints.pointsize, minDistance = Number.MAX_VALUE; + + if (series.datapoints.points.length <= pointsize) { + minDistance = 1; + } + + var start = series.bars.horizontal ? 1 : 0; + for (var j = start; j < series.datapoints.points.length; j += pointsize) { + if (isFinite(series.datapoints.points[j]) && series.datapoints.points[j] !== null) { + xValues.push(series.datapoints.points[j]); + } + } + + function onlyUnique(value, index, self) { + return self.indexOf(value) === index; + } + + xValues = xValues.filter( onlyUnique ); + xValues.sort(function(a, b){return a - b}); + + for (var j = 1; j < xValues.length; j++) { + var distance = Math.abs(xValues[j] - xValues[j - 1]); + if (distance < minDistance && isFinite(distance)) { + minDistance = distance; + } + } + + if (typeof series.bars.barWidth === "number") { + series.bars.barWidth = series.bars.barWidth * minDistance; + } else { + series.bars.barWidth[0] = series.bars.barWidth[0] * minDistance; + } + } + + // returns the data item the mouse is over/ the cursor is closest to, or null if none is found + function findNearbyItem(mouseX, mouseY, seriesFilter, radius, computeDistance) { + var i, j, + item = null, + smallestDistance = radius * radius + 1; + + for (var i = series.length - 1; i >= 0; --i) { + if (!seriesFilter(i)) continue; + + var s = series[i]; + if (!s.datapoints) return; + + if (s.lines.show || s.points.show) { + var found = findNearbyPoint(s, mouseX, mouseY, radius, smallestDistance, computeDistance); + if (found) { + smallestDistance = found.distance; + item = [i, found.dataIndex]; + } + } + + if (s.bars.show && !item) { // no other point can be nearby + var foundIndex = findNearbyBar(s, mouseX, mouseY); + if (foundIndex >= 0) item = [i, foundIndex]; + } + } + + if (item) { + i = item[0]; + j = item[1]; + var ps = series[i].datapoints.pointsize; + + return { + datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), + dataIndex: j, + series: series[i], + seriesIndex: i + }; + } + + return null; + } + + function findNearbyPoint (series, mouseX, mouseY, maxDistance, smallestDistance, computeDistance) { + var mx = series.xaxis.c2p(mouseX), + my = series.yaxis.c2p(mouseY), + maxx = maxDistance / series.xaxis.scale, + maxy = maxDistance / series.yaxis.scale, + points = series.datapoints.points, + ps = series.datapoints.pointsize; + + // with inverse transforms, we can't use the maxx/maxy + // optimization, sadly + if (series.xaxis.options.inverseTransform) { + maxx = Number.MAX_VALUE; + } + + if (series.yaxis.options.inverseTransform) { + maxy = Number.MAX_VALUE; + } + + var found = null; + for (var j = 0; j < points.length; j += ps) { + var x = points[j]; + var y = points[j + 1]; + if (x == null) { + continue; + } + + if (x - mx > maxx || x - mx < -maxx || + y - my > maxy || y - my < -maxy) { + continue; + } + + // We have to calculate distances in pixels, not in + // data units, because the scales of the axes may be different + var dx = Math.abs(series.xaxis.p2c(x) - mouseX); + var dy = Math.abs(series.yaxis.p2c(y) - mouseY); + var dist = computeDistance ? computeDistance(dx, dy) : dx * dx + dy * dy; + + // use <= to ensure last point takes precedence + // (last generally means on top of) + if (dist < smallestDistance) { + smallestDistance = dist; + found = { dataIndex: j / ps, distance: dist }; + } + } + + return found; + } + + function findNearbyBar (series, mouseX, mouseY) { + var barLeft, barRight, + barWidth = series.bars.barWidth[0] || series.bars.barWidth, + mx = series.xaxis.c2p(mouseX), + my = series.yaxis.c2p(mouseY), + points = series.datapoints.points, + ps = series.datapoints.pointsize; + + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -barWidth; + break; + default: + barLeft = -barWidth / 2; + } + + barRight = barLeft + barWidth; + + var fillTowards = series.bars.fillTowards || 0; + var defaultBottom = fillTowards > series.yaxis.min ? Math.min(series.yaxis.max, fillTowards) : series.yaxis.min; + + var foundIndex = -1; + for (var j = 0; j < points.length; j += ps) { + var x = points[j], y = points[j + 1]; + if (x == null) + continue; + + var bottom = ps === 3 ? points[j + 2] : defaultBottom; + // for a bar graph, the cursor must be inside the bar + if (series.bars.horizontal ? + (mx <= Math.max(bottom, x) && mx >= Math.min(bottom, x) && + my >= y + barLeft && my <= y + barRight) : + (mx >= x + barLeft && mx <= x + barRight && + my >= Math.min(bottom, y) && my <= Math.max(bottom, y))) + foundIndex = j / ps; + } + + return foundIndex; + } + + function findNearbyInterpolationPoint(posX, posY, seriesFilter) { + var i, j, dist, dx, dy, ps, + item, + smallestDistance = Number.MAX_VALUE; + + for (i = 0; i < series.length; ++i) { + if (!seriesFilter(i)) { + continue; + } + var points = series[i].datapoints.points; + ps = series[i].datapoints.pointsize; + + // if the data is coming from positive -> negative, reverse the comparison + const comparer = points[points.length - ps] < points[0] + ? function (x1, x2) { return x1 > x2 } + : function (x1, x2) { return x2 > x1 }; + + // do not interpolate outside the bounds of the data. + if (comparer(posX, points[0])) { + continue; + } + + // Find the nearest points, x-wise + for (j = ps; j < points.length; j += ps) { + if (comparer(posX, points[j])) { + break; + } + } + + // Now Interpolate + var y, + p1x = points[j - ps], + p1y = points[j - ps + 1], + p2x = points[j], + p2y = points[j + 1]; + + if ((p1x === undefined) || (p2x === undefined) || + (p1y === undefined) || (p2y === undefined)) { + continue; + } + + if (p1x === p2x) { + y = p2y + } else { + y = p1y + (p2y - p1y) * (posX - p1x) / (p2x - p1x); + } + + posY = y; + + dx = Math.abs(series[i].xaxis.p2c(p2x) - posX); + dy = Math.abs(series[i].yaxis.p2c(p2y) - posY); + dist = dx * dx + dy * dy; + + if (dist < smallestDistance) { + smallestDistance = dist; + item = [posX, posY, i, j]; + } + } + + if (item) { + i = item[2]; + j = item[3]; + ps = series[i].datapoints.pointsize; + points = series[i].datapoints.points; + p1x = points[j - ps]; + p1y = points[j - ps + 1]; + p2x = points[j]; + p2y = points[j + 1]; + + return { + datapoint: [item[0], item[1]], + leftPoint: [p1x, p1y], + rightPoint: [p2x, p2y], + seriesIndex: i + }; + } + + return null; + } + + function triggerRedrawOverlay() { + var t = options.interaction.redrawOverlayInterval; + if (t === -1) { // skip event queue + drawOverlay(); + return; + } + + if (!redrawTimeout) { + redrawTimeout = setTimeout(function() { + drawOverlay(plot); + }, t); + } + } + + function drawOverlay(plot) { + redrawTimeout = null; + + if (!octx) { + return; + } + overlay.clear(); + executeHooks(hooks.drawOverlay, [octx, overlay]); + var event = new CustomEvent('onDrawingDone'); + plot.getEventHolder().dispatchEvent(event); + plot.getPlaceholder().trigger('drawingdone'); + } + + function getColorOrGradient(spec, bottom, top, defaultColor) { + if (typeof spec === "string") { + return spec; + } else { + // assume this is a gradient spec; IE currently only + // supports a simple vertical gradient properly, so that's + // what we support too + var gradient = ctx.createLinearGradient(0, top, 0, bottom); + + for (var i = 0, l = spec.colors.length; i < l; ++i) { + var c = spec.colors[i]; + if (typeof c !== "string") { + var co = $.color.parse(defaultColor); + if (c.brightness != null) { + co = co.scale('rgb', c.brightness); + } + + if (c.opacity != null) { + co.a *= c.opacity; + } + + c = co.toString(); + } + gradient.addColorStop(i / (l - 1), c); + } + + return gradient; + } + } + } + + // Add the plot function to the top level of the jQuery object + + $.plot = function(placeholder, data, options) { + var plot = new Plot($(placeholder), data, options, $.plot.plugins); + return plot; + }; + + $.plot.version = "3.0.0"; + + $.plot.plugins = []; + + // Also add the plot function as a chainable property + $.fn.plot = function(data, options) { + return this.each(function() { + $.plot(this, data, options); + }); + }; + + $.plot.linearTickGenerator = defaultTickGenerator; + $.plot.defaultTickFormatter = defaultTickFormatter; + $.plot.expRepTickFormatter = expRepTickFormatter; +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.legend.js b/terahz/templates/lib/flot/jquery.flot.legend.js new file mode 100644 index 0000000..eb48ac9 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.legend.js @@ -0,0 +1,437 @@ +/* Flot plugin for drawing legends. + +*/ + +(function($) { + var defaultOptions = { + legend: { + show: false, + noColumns: 1, + labelFormatter: null, // fn: string -> string + container: null, // container (as jQuery object) to put legend in, null means default on top of graph + position: 'ne', // position of default legend container within plot + margin: 5, // distance from grid edge to default legend container within plot + sorted: null // default to no legend sorting + } + }; + + function insertLegend(plot, options, placeholder, legendEntries) { + // clear before redraw + if (options.legend.container != null) { + $(options.legend.container).html(''); + } else { + placeholder.find('.legend').remove(); + } + + if (!options.legend.show) { + return; + } + + // Save the legend entries in legend options + var entries = options.legend.legendEntries = legendEntries, + plotOffset = options.legend.plotOffset = plot.getPlotOffset(), + html = [], + entry, labelHtml, iconHtml, + j = 0, + i, + pos = "", + p = options.legend.position, + m = options.legend.margin, + shape = { + name: '', + label: '', + xPos: '', + yPos: '' + }; + + html[j++] = ''; + html[j++] = ''; + html[j++] = svgShapeDefs; + + var left = 0; + var columnWidths = []; + var style = window.getComputedStyle(document.querySelector('body')); + for (i = 0; i < entries.length; ++i) { + var columnIndex = i % options.legend.noColumns; + entry = entries[i]; + shape.label = entry.label; + var info = plot.getSurface().getTextInfo('', shape.label, { + style: style.fontStyle, + variant: style.fontVariant, + weight: style.fontWeight, + size: parseInt(style.fontSize), + lineHeight: parseInt(style.lineHeight), + family: style.fontFamily + }); + + var labelWidth = info.width; + // 36px = 1.5em + 6px margin + var iconWidth = 48; + if (columnWidths[columnIndex]) { + if (labelWidth > columnWidths[columnIndex]) { + columnWidths[columnIndex] = labelWidth + iconWidth; + } + } else { + columnWidths[columnIndex] = labelWidth + iconWidth; + } + } + + // Generate html for icons and labels from a list of entries + for (i = 0; i < entries.length; ++i) { + var columnIndex = i % options.legend.noColumns; + entry = entries[i]; + iconHtml = ''; + shape.label = entry.label; + shape.xPos = (left + 3) + 'px'; + left += columnWidths[columnIndex]; + if ((i + 1) % options.legend.noColumns === 0) { + left = 0; + } + shape.yPos = Math.floor(i / options.legend.noColumns) * 1.5 + 'em'; + // area + if (entry.options.lines.show && entry.options.lines.fill) { + shape.name = 'area'; + shape.fillColor = entry.color; + iconHtml += getEntryIconHtml(shape); + } + // bars + if (entry.options.bars.show) { + shape.name = 'bar'; + shape.fillColor = entry.color; + iconHtml += getEntryIconHtml(shape); + } + // lines + if (entry.options.lines.show && !entry.options.lines.fill) { + shape.name = 'line'; + shape.strokeColor = entry.color; + shape.strokeWidth = entry.options.lines.lineWidth; + iconHtml += getEntryIconHtml(shape); + } + // points + if (entry.options.points.show) { + shape.name = entry.options.points.symbol; + shape.strokeColor = entry.color; + shape.fillColor = entry.options.points.fillColor; + shape.strokeWidth = entry.options.points.lineWidth; + iconHtml += getEntryIconHtml(shape); + } + + labelHtml = '' + shape.label + '' + html[j++] = '' + iconHtml + labelHtml + ''; + } + + html[j++] = ''; + if (m[0] == null) { + m = [m, m]; + } + + if (p.charAt(0) === 'n') { + pos += 'top:' + (m[1] + plotOffset.top) + 'px;'; + } else if (p.charAt(0) === 's') { + pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;'; + } + + if (p.charAt(1) === 'e') { + pos += 'right:' + (m[0] + plotOffset.right) + 'px;'; + } else if (p.charAt(1) === 'w') { + pos += 'left:' + (m[0] + plotOffset.left) + 'px;'; + } + + var width = 6; + for (i = 0; i < columnWidths.length; ++i) { + width += columnWidths[i]; + } + + var legendEl, + height = Math.ceil(entries.length / options.legend.noColumns) * 1.6; + if (!options.legend.container) { + legendEl = $('
' + html.join('') + '
').appendTo(placeholder); + legendEl.css('width', width + 'px'); + legendEl.css('height', height + 'em'); + legendEl.css('pointerEvents', 'none'); + } else { + legendEl = $(html.join('')).appendTo(options.legend.container)[0]; + options.legend.container.style.width = width + 'px'; + options.legend.container.style.height = height + 'em'; + } + } + + // Generate html for a shape + function getEntryIconHtml(shape) { + var html = '', + name = shape.name, + x = shape.xPos, + y = shape.yPos, + fill = shape.fillColor, + stroke = shape.strokeColor, + width = shape.strokeWidth; + switch (name) { + case 'circle': + html = ''; + break; + case 'diamond': + html = ''; + break; + case 'cross': + html = ''; + break; + case 'rectangle': + html = ''; + break; + case 'plus': + html = ''; + break; + case 'bar': + html = ''; + break; + case 'area': + html = ''; + break; + case 'line': + html = ''; + break; + default: + // default is circle + html = ''; + } + + return html; + } + + // Define svg symbols for shapes + var svgShapeDefs = '' + + '' + + '' + + '' + + '' + + + '' + + '' + + '' + + + '' + + '' + + '' + + + '' + + '' + + '' + + '' + + '' + + '' + + + '' + + '' + + '' + + '' + + '' + + '' + + + '' + + '' + + '' + + '' + + '' + + '' + + + '' + + '' + + '' + + '' + + '' + + '' + + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + + // Generate a list of legend entries in their final order + function getLegendEntries(series, labelFormatter, sorted) { + var lf = labelFormatter, + legendEntries = series.reduce(function(validEntries, s, i) { + var labelEval = (lf ? lf(s.label, s) : s.label) + if (s.hasOwnProperty("label") ? labelEval : true) { + var entry = { + label: labelEval || 'Plot ' + (i + 1), + color: s.color, + options: { + lines: s.lines, + points: s.points, + bars: s.bars + } + } + validEntries.push(entry) + } + return validEntries; + }, []); + + // Sort the legend using either the default or a custom comparator + if (sorted) { + if ($.isFunction(sorted)) { + legendEntries.sort(sorted); + } else if (sorted === 'reverse') { + legendEntries.reverse(); + } else { + var ascending = (sorted !== 'descending'); + legendEntries.sort(function(a, b) { + return a.label === b.label + ? 0 + : ((a.label < b.label) !== ascending ? 1 : -1 // Logical XOR + ); + }); + } + } + + return legendEntries; + } + + // return false if opts1 same as opts2 + function checkOptions(opts1, opts2) { + for (var prop in opts1) { + if (opts1.hasOwnProperty(prop)) { + if (opts1[prop] !== opts2[prop]) { + return true; + } + } + } + return false; + } + + // Compare two lists of legend entries + function shouldRedraw(oldEntries, newEntries) { + if (!oldEntries || !newEntries) { + return true; + } + + if (oldEntries.length !== newEntries.length) { + return true; + } + var i, newEntry, oldEntry, newOpts, oldOpts; + for (i = 0; i < newEntries.length; i++) { + newEntry = newEntries[i]; + oldEntry = oldEntries[i]; + + if (newEntry.label !== oldEntry.label) { + return true; + } + + if (newEntry.color !== oldEntry.color) { + return true; + } + + // check for changes in lines options + newOpts = newEntry.options.lines; + oldOpts = oldEntry.options.lines; + if (checkOptions(newOpts, oldOpts)) { + return true; + } + + // check for changes in points options + newOpts = newEntry.options.points; + oldOpts = oldEntry.options.points; + if (checkOptions(newOpts, oldOpts)) { + return true; + } + + // check for changes in bars options + newOpts = newEntry.options.bars; + oldOpts = oldEntry.options.bars; + if (checkOptions(newOpts, oldOpts)) { + return true; + } + } + + return false; + } + + function init(plot) { + plot.hooks.setupGrid.push(function (plot) { + var options = plot.getOptions(); + var series = plot.getData(), + labelFormatter = options.legend.labelFormatter, + oldEntries = options.legend.legendEntries, + oldPlotOffset = options.legend.plotOffset, + newEntries = getLegendEntries(series, labelFormatter, options.legend.sorted), + newPlotOffset = plot.getPlotOffset(); + + if (shouldRedraw(oldEntries, newEntries) || + checkOptions(oldPlotOffset, newPlotOffset)) { + insertLegend(plot, options, plot.getPlaceholder(), newEntries); + } + }); + } + + $.plot.plugins.push({ + init: init, + options: defaultOptions, + name: 'legend', + version: '1.0' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.logaxis.js b/terahz/templates/lib/flot/jquery.flot.logaxis.js new file mode 100644 index 0000000..2804aa1 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.logaxis.js @@ -0,0 +1,298 @@ +/* Pretty handling of log axes. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Copyright (c) 2015 Ciprian Ceteras cipix2000@gmail.com. +Copyright (c) 2017 Raluca Portase +Licensed under the MIT license. + +Set axis.mode to "log" to enable. +*/ + +/* global jQuery*/ + +/** +## jquery.flot.logaxis +This plugin is used to create logarithmic axis. This includes tick generation, +formatters and transformers to and from logarithmic representation. + +### Methods and hooks +*/ + +(function ($) { + 'use strict'; + + var options = { + xaxis: {} + }; + + /*tick generators and formatters*/ + var PREFERRED_LOG_TICK_VALUES = computePreferedLogTickValues(Number.MAX_VALUE, 10), + EXTENDED_LOG_TICK_VALUES = computePreferedLogTickValues(Number.MAX_VALUE, 4); + + function computePreferedLogTickValues(endLimit, rangeStep) { + var log10End = Math.floor(Math.log(endLimit) * Math.LOG10E) - 1, + log10Start = -log10End, + val, range, vals = []; + + for (var power = log10Start; power <= log10End; power++) { + range = parseFloat('1e' + power); + for (var mult = 1; mult < 9; mult += rangeStep) { + val = range * mult; + vals.push(val); + } + } + return vals; + } + + /** + - logTickGenerator(plot, axis, noTicks) + + Generates logarithmic ticks, depending on axis range. + In case the number of ticks that can be generated is less than the expected noTicks/4, + a linear tick generation is used. + */ + var logTickGenerator = function (plot, axis, noTicks) { + var ticks = [], + minIdx = -1, + maxIdx = -1, + surface = plot.getCanvas(), + logTickValues = PREFERRED_LOG_TICK_VALUES, + min = clampAxis(axis, plot), + max = axis.max; + + if (!noTicks) { + noTicks = 0.3 * Math.sqrt(axis.direction === "x" ? surface.width : surface.height); + } + + PREFERRED_LOG_TICK_VALUES.some(function (val, i) { + if (val >= min) { + minIdx = i; + return true; + } else { + return false; + } + }); + + PREFERRED_LOG_TICK_VALUES.some(function (val, i) { + if (val >= max) { + maxIdx = i; + return true; + } else { + return false; + } + }); + + if (maxIdx === -1) { + maxIdx = PREFERRED_LOG_TICK_VALUES.length - 1; + } + + if (maxIdx - minIdx <= noTicks / 4 && logTickValues.length !== EXTENDED_LOG_TICK_VALUES.length) { + //try with multiple of 5 for tick values + logTickValues = EXTENDED_LOG_TICK_VALUES; + minIdx *= 2; + maxIdx *= 2; + } + + var lastDisplayed = null, + inverseNoTicks = 1 / noTicks, + tickValue, pixelCoord, tick; + + // Count the number of tick values would appear, if we can get at least + // nTicks / 4 accept them. + if (maxIdx - minIdx >= noTicks / 4) { + for (var idx = maxIdx; idx >= minIdx; idx--) { + tickValue = logTickValues[idx]; + pixelCoord = (Math.log(tickValue) - Math.log(min)) / (Math.log(max) - Math.log(min)); + tick = tickValue; + + if (lastDisplayed === null) { + lastDisplayed = { + pixelCoord: pixelCoord, + idealPixelCoord: pixelCoord + }; + } else { + if (Math.abs(pixelCoord - lastDisplayed.pixelCoord) >= inverseNoTicks) { + lastDisplayed = { + pixelCoord: pixelCoord, + idealPixelCoord: lastDisplayed.idealPixelCoord - inverseNoTicks + }; + } else { + tick = null; + } + } + + if (tick) { + ticks.push(tick); + } + } + // Since we went in backwards order. + ticks.reverse(); + } else { + var tickSize = plot.computeTickSize(min, max, noTicks), + customAxis = {min: min, max: max, tickSize: tickSize}; + ticks = $.plot.linearTickGenerator(customAxis); + } + + return ticks; + }; + + var clampAxis = function (axis, plot) { + var min = axis.min, + max = axis.max; + + if (min <= 0) { + //for empty graph if axis.min is not strictly positive make it 0.1 + if (axis.datamin === null) { + min = axis.min = 0.1; + } else { + min = processAxisOffset(plot, axis); + } + + if (max < min) { + axis.max = axis.datamax !== null ? axis.datamax : axis.options.max; + axis.options.offset.below = 0; + axis.options.offset.above = 0; + } + } + + return min; + } + + /** + - logTickFormatter(value, axis, precision) + + This is the corresponding tickFormatter of the logaxis. + For a number greater that 10^6 or smaller than 10^(-3), this will be drawn + with e representation + */ + var logTickFormatter = function (value, axis, precision) { + var tenExponent = value > 0 ? Math.floor(Math.log(value) / Math.LN10) : 0; + + if (precision) { + if ((tenExponent >= -4) && (tenExponent <= 7)) { + return $.plot.defaultTickFormatter(value, axis, precision); + } else { + return $.plot.expRepTickFormatter(value, axis, precision); + } + } + if ((tenExponent >= -4) && (tenExponent <= 7)) { + //if we have float numbers, return a limited length string(ex: 0.0009 is represented as 0.000900001) + var formattedValue = tenExponent < 0 ? value.toFixed(-tenExponent) : value.toFixed(tenExponent + 2); + if (formattedValue.indexOf('.') !== -1) { + var lastZero = formattedValue.lastIndexOf('0'); + + while (lastZero === formattedValue.length - 1) { + formattedValue = formattedValue.slice(0, -1); + lastZero = formattedValue.lastIndexOf('0'); + } + + //delete the dot if is last + if (formattedValue.indexOf('.') === formattedValue.length - 1) { + formattedValue = formattedValue.slice(0, -1); + } + } + return formattedValue; + } else { + return $.plot.expRepTickFormatter(value, axis); + } + }; + + /*logaxis caracteristic functions*/ + var logTransform = function (v) { + if (v < PREFERRED_LOG_TICK_VALUES[0]) { + v = PREFERRED_LOG_TICK_VALUES[0]; + } + + return Math.log(v); + }; + + var logInverseTransform = function (v) { + return Math.exp(v); + }; + + var invertedTransform = function (v) { + return -v; + } + + var invertedLogTransform = function (v) { + return -logTransform(v); + } + + var invertedLogInverseTransform = function (v) { + return logInverseTransform(-v); + } + + /** + - setDataminRange(plot, axis) + + It is used for clamping the starting point of a logarithmic axis. + This will set the axis datamin range to 0.1 or to the first datapoint greater then 0. + The function is usefull since the logarithmic representation can not show + values less than or equal to 0. + */ + function setDataminRange(plot, axis) { + if (axis.options.mode === 'log' && axis.datamin <= 0) { + if (axis.datamin === null) { + axis.datamin = 0.1; + } else { + axis.datamin = processAxisOffset(plot, axis); + } + } + } + + function processAxisOffset(plot, axis) { + var series = plot.getData(), + range = series + .filter(function(series) { + return series.xaxis === axis || series.yaxis === axis; + }) + .map(function(series) { + return plot.computeRangeForDataSeries(series, null, isValid); + }), + min = axis.direction === 'x' + ? Math.min(0.1, range && range[0] ? range[0].xmin : 0.1) + : Math.min(0.1, range && range[0] ? range[0].ymin : 0.1); + + axis.min = min; + + return min; + } + + function isValid(a) { + return a > 0; + } + + function init(plot) { + plot.hooks.processOptions.push(function (plot) { + $.each(plot.getAxes(), function (axisName, axis) { + var opts = axis.options; + if (opts.mode === 'log') { + axis.tickGenerator = function (axis) { + var noTicks = 11; + return logTickGenerator(plot, axis, noTicks); + }; + if (typeof axis.options.tickFormatter !== 'function') { + axis.options.tickFormatter = logTickFormatter; + } + axis.options.transform = opts.inverted ? invertedLogTransform : logTransform; + axis.options.inverseTransform = opts.inverted ? invertedLogInverseTransform : logInverseTransform; + axis.options.autoScaleMargin = 0; + plot.hooks.setRange.push(setDataminRange); + } else if (opts.inverted) { + axis.options.transform = invertedTransform; + axis.options.inverseTransform = invertedTransform; + } + }); + }); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'log', + version: '0.1' + }); + + $.plot.logTicksGenerator = logTickGenerator; + $.plot.logTickFormatter = logTickFormatter; +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.navigate.js b/terahz/templates/lib/flot/jquery.flot.navigate.js new file mode 100644 index 0000000..c1be1cf --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.navigate.js @@ -0,0 +1,798 @@ +/* Flot plugin for adding the ability to pan and zoom the plot. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Copyright (c) 2016 Ciprian Ceteras. +Copyright (c) 2017 Raluca Portase. +Licensed under the MIT license. + +*/ + +/** +## jquery.flot.navigate.js + +This flot plugin is used for adding the ability to pan and zoom the plot. +A higher level overview is available at [interactions](interactions.md) documentation. + +The default behaviour is scrollwheel up/down to zoom in, drag +to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and +plot.pan( offset ) so you easily can add custom controls. It also fires +"plotpan" and "plotzoom" events, useful for synchronizing plots. + +The plugin supports these options: +```js + zoom: { + interactive: false, + active: false, + amount: 1.5 // 2 = 200% (zoom in), 0.5 = 50% (zoom out) + } + + pan: { + interactive: false, + active: false, + cursor: "move", // CSS mouse cursor value used when dragging, e.g. "pointer" + frameRate: 60, + mode: "smart" // enable smart pan mode + } + + xaxis: { + axisZoom: true, //zoom axis when mouse over it is allowed + plotZoom: true, //zoom axis is allowed for plot zoom + axisPan: true, //pan axis when mouse over it is allowed + plotPan: true //pan axis is allowed for plot pan + } + + yaxis: { + axisZoom: true, //zoom axis when mouse over it is allowed + plotZoom: true, //zoom axis is allowed for plot zoom + axisPan: true, //pan axis when mouse over it is allowed + plotPan: true //pan axis is allowed for plot pan + } +``` +**interactive** enables the built-in drag/click behaviour. If you enable +interactive for pan, then you'll have a basic plot that supports moving +around; the same for zoom. + +**active** is true after a touch tap on plot. This enables plot navigation. +Once activated, zoom and pan cannot be deactivated. When the plot becomes active, +"plotactivated" event is triggered. + +**amount** specifies the default amount to zoom in (so 1.5 = 150%) relative to +the current viewport. + +**cursor** is a standard CSS mouse cursor string used for visual feedback to the +user when dragging. + +**frameRate** specifies the maximum number of times per second the plot will +update itself while the user is panning around on it (set to null to disable +intermediate pans, the plot will then not update until the mouse button is +released). + +**mode** a string specifies the pan mode for mouse interaction. Accepted values: +'manual': no pan hint or direction snapping; +'smart': The graph shows pan hint bar and the pan movement will snap +to one direction when the drag direction is close to it; +'smartLock'. The graph shows pan hint bar and the pan movement will always +snap to a direction that the drag diorection started with. + +Example API usage: +```js + plot = $.plot(...); + + // zoom default amount in on the pixel ( 10, 20 ) + plot.zoom({ center: { left: 10, top: 20 } }); + + // zoom out again + plot.zoomOut({ center: { left: 10, top: 20 } }); + + // zoom 200% in on the pixel (10, 20) + plot.zoom({ amount: 2, center: { left: 10, top: 20 } }); + + // pan 100 pixels to the left (changing x-range in a positive way) and 20 down + plot.pan({ left: -100, top: 20 }) +``` + +Here, "center" specifies where the center of the zooming should happen. Note +that this is defined in pixel space, not the space of the data points (you can +use the p2c helpers on the axes in Flot to help you convert between these). + +**amount** is the amount to zoom the viewport relative to the current range, so +1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You +can set the default in the options. +*/ + +/* eslint-enable */ +(function($) { + 'use strict'; + + var options = { + zoom: { + interactive: false, + active: false, + amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out) + }, + pan: { + interactive: false, + active: false, + cursor: "move", + frameRate: 60, + mode: 'smart' + }, + recenter: { + interactive: true + }, + xaxis: { + axisZoom: true, //zoom axis when mouse over it is allowed + plotZoom: true, //zoom axis is allowed for plot zoom + axisPan: true, //pan axis when mouse over it is allowed + plotPan: true //pan axis is allowed for plot pan + }, + yaxis: { + axisZoom: true, + plotZoom: true, + axisPan: true, + plotPan: true + } + }; + + var saturated = $.plot.saturated; + var browser = $.plot.browser; + var SNAPPING_CONSTANT = $.plot.uiConstants.SNAPPING_CONSTANT; + var PANHINT_LENGTH_CONSTANT = $.plot.uiConstants.PANHINT_LENGTH_CONSTANT; + + function init(plot) { + plot.hooks.processOptions.push(initNevigation); + } + + function initNevigation(plot, options) { + var panAxes = null; + var canDrag = false; + var useManualPan = options.pan.mode === 'manual', + smartPanLock = options.pan.mode === 'smartLock', + useSmartPan = smartPanLock || options.pan.mode === 'smart'; + + function onZoomClick(e, zoomOut, amount) { + var page = browser.getPageXY(e); + + var c = plot.offset(); + c.left = page.X - c.left; + c.top = page.Y - c.top; + + var ec = plot.getPlaceholder().offset(); + ec.left = page.X - ec.left; + ec.top = page.Y - ec.top; + + var axes = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) { + var box = axis.box; + if (box !== undefined) { + return (ec.left > box.left) && (ec.left < box.left + box.width) && + (ec.top > box.top) && (ec.top < box.top + box.height); + } + }); + + if (axes.length === 0) { + axes = undefined; + } + + if (zoomOut) { + plot.zoomOut({ + center: c, + axes: axes, + amount: amount + }); + } else { + plot.zoom({ + center: c, + axes: axes, + amount: amount + }); + } + } + + var prevCursor = 'default', + panHint = null, + panTimeout = null, + plotState, + prevDragPosition = { x: 0, y: 0 }, + isPanAction = false; + + function onMouseWheel(e, delta) { + var maxAbsoluteDeltaOnMac = 1, + isMacScroll = Math.abs(e.originalEvent.deltaY) <= maxAbsoluteDeltaOnMac, + defaultNonMacScrollAmount = null, + macMagicRatio = 50, + amount = isMacScroll ? 1 + Math.abs(e.originalEvent.deltaY) / macMagicRatio : defaultNonMacScrollAmount; + + if (isPanAction) { + onDragEnd(e); + } + + if (plot.getOptions().zoom.active) { + e.preventDefault(); + onZoomClick(e, delta < 0, amount); + return false; + } + } + + plot.navigationState = function(startPageX, startPageY) { + var axes = this.getAxes(); + var result = {}; + Object.keys(axes).forEach(function(axisName) { + var axis = axes[axisName]; + result[axisName] = { + navigationOffset: { below: axis.options.offset.below || 0, + above: axis.options.offset.above || 0}, + axisMin: axis.min, + axisMax: axis.max, + diagMode: false + } + }); + + result.startPageX = startPageX || 0; + result.startPageY = startPageY || 0; + return result; + } + + function onMouseDown(e) { + canDrag = true; + } + + function onMouseUp(e) { + canDrag = false; + } + + function isLeftMouseButtonPressed(e) { + return e.button === 0; + } + + function onDragStart(e) { + if (!canDrag || !isLeftMouseButtonPressed(e)) { + return false; + } + + isPanAction = true; + var page = browser.getPageXY(e); + + var ec = plot.getPlaceholder().offset(); + ec.left = page.X - ec.left; + ec.top = page.Y - ec.top; + + panAxes = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) { + var box = axis.box; + if (box !== undefined) { + return (ec.left > box.left) && (ec.left < box.left + box.width) && + (ec.top > box.top) && (ec.top < box.top + box.height); + } + }); + + if (panAxes.length === 0) { + panAxes = undefined; + } + + var c = plot.getPlaceholder().css('cursor'); + if (c) { + prevCursor = c; + } + + plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor); + + if (useSmartPan) { + plotState = plot.navigationState(page.X, page.Y); + } else if (useManualPan) { + prevDragPosition.x = page.X; + prevDragPosition.y = page.Y; + } + } + + function onDrag(e) { + if (!isPanAction) { + return; + } + + var page = browser.getPageXY(e); + var frameRate = plot.getOptions().pan.frameRate; + + if (frameRate === -1) { + if (useSmartPan) { + plot.smartPan({ + x: plotState.startPageX - page.X, + y: plotState.startPageY - page.Y + }, plotState, panAxes, false, smartPanLock); + } else if (useManualPan) { + plot.pan({ + left: prevDragPosition.x - page.X, + top: prevDragPosition.y - page.Y, + axes: panAxes + }); + prevDragPosition.x = page.X; + prevDragPosition.y = page.Y; + } + return; + } + + if (panTimeout || !frameRate) return; + + panTimeout = setTimeout(function() { + if (useSmartPan) { + plot.smartPan({ + x: plotState.startPageX - page.X, + y: plotState.startPageY - page.Y + }, plotState, panAxes, false, smartPanLock); + } else if (useManualPan) { + plot.pan({ + left: prevDragPosition.x - page.X, + top: prevDragPosition.y - page.Y, + axes: panAxes + }); + prevDragPosition.x = page.X; + prevDragPosition.y = page.Y; + } + + panTimeout = null; + }, 1 / frameRate * 1000); + } + + function onDragEnd(e) { + if (!isPanAction) { + return; + } + + if (panTimeout) { + clearTimeout(panTimeout); + panTimeout = null; + } + + isPanAction = false; + var page = browser.getPageXY(e); + + plot.getPlaceholder().css('cursor', prevCursor); + + if (useSmartPan) { + plot.smartPan({ + x: plotState.startPageX - page.X, + y: plotState.startPageY - page.Y + }, plotState, panAxes, false, smartPanLock); + plot.smartPan.end(); + } else if (useManualPan) { + plot.pan({ + left: prevDragPosition.x - page.X, + top: prevDragPosition.y - page.Y, + axes: panAxes + }); + prevDragPosition.x = 0; + prevDragPosition.y = 0; + } + } + + function onDblClick(e) { + plot.activate(); + var o = plot.getOptions() + + if (!o.recenter.interactive) { return; } + + var axes = plot.getTouchedAxis(e.clientX, e.clientY), + event; + + plot.recenter({ axes: axes[0] ? axes : null }); + + if (axes[0]) { + event = new $.Event('re-center', { detail: { + axisTouched: axes[0] + }}); + } else { + event = new $.Event('re-center', { detail: e }); + } + plot.getPlaceholder().trigger(event); + } + + function onClick(e) { + plot.activate(); + + if (isPanAction) { + onDragEnd(e); + } + + return false; + } + + plot.activate = function() { + var o = plot.getOptions(); + if (!o.pan.active || !o.zoom.active) { + o.pan.active = true; + o.zoom.active = true; + plot.getPlaceholder().trigger("plotactivated", [plot]); + } + } + + function bindEvents(plot, eventHolder) { + var o = plot.getOptions(); + if (o.zoom.interactive) { + eventHolder.mousewheel(onMouseWheel); + } + + if (o.pan.interactive) { + plot.addEventHandler("dragstart", onDragStart, eventHolder, 0); + plot.addEventHandler("drag", onDrag, eventHolder, 0); + plot.addEventHandler("dragend", onDragEnd, eventHolder, 0); + eventHolder.bind("mousedown", onMouseDown); + eventHolder.bind("mouseup", onMouseUp); + } + + eventHolder.dblclick(onDblClick); + eventHolder.click(onClick); + } + + plot.zoomOut = function(args) { + if (!args) { + args = {}; + } + + if (!args.amount) { + args.amount = plot.getOptions().zoom.amount; + } + + args.amount = 1 / args.amount; + plot.zoom(args); + }; + + plot.zoom = function(args) { + if (!args) { + args = {}; + } + + var c = args.center, + amount = args.amount || plot.getOptions().zoom.amount, + w = plot.width(), + h = plot.height(), + axes = args.axes || plot.getAxes(); + + if (!c) { + c = { + left: w / 2, + top: h / 2 + }; + } + + var xf = c.left / w, + yf = c.top / h, + minmax = { + x: { + min: c.left - xf * w / amount, + max: c.left + (1 - xf) * w / amount + }, + y: { + min: c.top - yf * h / amount, + max: c.top + (1 - yf) * h / amount + } + }; + + for (var key in axes) { + if (!axes.hasOwnProperty(key)) { + continue; + } + + var axis = axes[key], + opts = axis.options, + min = minmax[axis.direction].min, + max = minmax[axis.direction].max, + navigationOffset = axis.options.offset; + + //skip axis without axisZoom when zooming only on certain axis or axis without plotZoom for zoom on entire plot + if ((!opts.axisZoom && args.axes) || (!args.axes && !opts.plotZoom)) { + continue; + } + + min = $.plot.saturated.saturate(axis.c2p(min)); + max = $.plot.saturated.saturate(axis.c2p(max)); + if (min > max) { + // make sure min < max + var tmp = min; + min = max; + max = tmp; + } + + var offsetBelow = $.plot.saturated.saturate(navigationOffset.below - (axis.min - min)); + var offsetAbove = $.plot.saturated.saturate(navigationOffset.above - (axis.max - max)); + opts.offset = { below: offsetBelow, above: offsetAbove }; + }; + + plot.setupGrid(true); + plot.draw(); + + if (!args.preventEvent) { + plot.getPlaceholder().trigger("plotzoom", [plot, args]); + } + }; + + plot.pan = function(args) { + var delta = { + x: +args.left, + y: +args.top + }; + + if (isNaN(delta.x)) delta.x = 0; + if (isNaN(delta.y)) delta.y = 0; + + $.each(args.axes || plot.getAxes(), function(_, axis) { + var opts = axis.options, + d = delta[axis.direction]; + + //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot + if ((!opts.axisPan && args.axes) || (!opts.plotPan && !args.axes)) { + return; + } + + if (d !== 0) { + var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axis.min) + d) - axis.c2p(axis.p2c(axis.min))), + navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axis.max) + d) - axis.c2p(axis.p2c(axis.max))); + + if (!isFinite(navigationOffsetBelow)) { + navigationOffsetBelow = 0; + } + + if (!isFinite(navigationOffsetAbove)) { + navigationOffsetAbove = 0; + } + + opts.offset = { + below: saturated.saturate(navigationOffsetBelow + (opts.offset.below || 0)), + above: saturated.saturate(navigationOffsetAbove + (opts.offset.above || 0)) + }; + } + }); + + plot.setupGrid(true); + plot.draw(); + if (!args.preventEvent) { + plot.getPlaceholder().trigger("plotpan", [plot, args]); + } + }; + + plot.recenter = function(args) { + $.each(args.axes || plot.getAxes(), function(_, axis) { + if (args.axes) { + if (this.direction === 'x') { + axis.options.offset = { below: 0 }; + } else if (this.direction === 'y') { + axis.options.offset = { above: 0 }; + } + } else { + axis.options.offset = { below: 0, above: 0 }; + } + }); + plot.setupGrid(true); + plot.draw(); + }; + + var shouldSnap = function(delta) { + return (Math.abs(delta.y) < SNAPPING_CONSTANT && Math.abs(delta.x) >= SNAPPING_CONSTANT) || + (Math.abs(delta.x) < SNAPPING_CONSTANT && Math.abs(delta.y) >= SNAPPING_CONSTANT); + } + + // adjust delta so the pan action is constrained on the vertical or horizontal direction + // it the movements in the other direction are small + var adjustDeltaToSnap = function(delta) { + if (Math.abs(delta.x) < SNAPPING_CONSTANT && Math.abs(delta.y) >= SNAPPING_CONSTANT) { + return {x: 0, y: delta.y}; + } + + if (Math.abs(delta.y) < SNAPPING_CONSTANT && Math.abs(delta.x) >= SNAPPING_CONSTANT) { + return {x: delta.x, y: 0}; + } + + return delta; + } + + var lockedDirection = null; + var lockDeltaDirection = function(delta) { + if (!lockedDirection && Math.max(Math.abs(delta.x), Math.abs(delta.y)) >= SNAPPING_CONSTANT) { + lockedDirection = Math.abs(delta.x) < Math.abs(delta.y) ? 'y' : 'x'; + } + + switch (lockedDirection) { + case 'x': + return { x: delta.x, y: 0 }; + case 'y': + return { x: 0, y: delta.y }; + default: + return { x: 0, y: 0 }; + } + } + + var isDiagonalMode = function(delta) { + if (Math.abs(delta.x) > 0 && Math.abs(delta.y) > 0) { + return true; + } + return false; + } + + var restoreAxisOffset = function(axes, initialState, delta) { + var axis; + Object.keys(axes).forEach(function(axisName) { + axis = axes[axisName]; + if (delta[axis.direction] === 0) { + axis.options.offset.below = initialState[axisName].navigationOffset.below; + axis.options.offset.above = initialState[axisName].navigationOffset.above; + } + }); + } + + var prevDelta = { x: 0, y: 0 }; + plot.smartPan = function(delta, initialState, panAxes, preventEvent, smartLock) { + var snap = smartLock ? true : shouldSnap(delta), + axes = plot.getAxes(), + opts; + delta = smartLock ? lockDeltaDirection(delta) : adjustDeltaToSnap(delta); + + if (isDiagonalMode(delta)) { + initialState.diagMode = true; + } + + if (snap && initialState.diagMode === true) { + initialState.diagMode = false; + restoreAxisOffset(axes, initialState, delta); + } + + if (snap) { + panHint = { + start: { + x: initialState.startPageX - plot.offset().left + plot.getPlotOffset().left, + y: initialState.startPageY - plot.offset().top + plot.getPlotOffset().top + }, + end: { + x: initialState.startPageX - delta.x - plot.offset().left + plot.getPlotOffset().left, + y: initialState.startPageY - delta.y - plot.offset().top + plot.getPlotOffset().top + } + } + } else { + panHint = { + start: { + x: initialState.startPageX - plot.offset().left + plot.getPlotOffset().left, + y: initialState.startPageY - plot.offset().top + plot.getPlotOffset().top + }, + end: false + } + } + + if (isNaN(delta.x)) delta.x = 0; + if (isNaN(delta.y)) delta.y = 0; + + if (panAxes) { + axes = panAxes; + } + + var axis, axisMin, axisMax, p, d; + Object.keys(axes).forEach(function(axisName) { + axis = axes[axisName]; + axisMin = axis.min; + axisMax = axis.max; + opts = axis.options; + + d = delta[axis.direction]; + p = prevDelta[axis.direction]; + + //skip axis without axisPan when panning only on certain axis or axis without plotPan for pan the entire plot + if ((!opts.axisPan && panAxes) || (!panAxes && !opts.plotPan)) { + return; + } + + if (d !== 0) { + var navigationOffsetBelow = saturated.saturate(axis.c2p(axis.p2c(axisMin) - (p - d)) - axis.c2p(axis.p2c(axisMin))), + navigationOffsetAbove = saturated.saturate(axis.c2p(axis.p2c(axisMax) - (p - d)) - axis.c2p(axis.p2c(axisMax))); + + if (!isFinite(navigationOffsetBelow)) { + navigationOffsetBelow = 0; + } + + if (!isFinite(navigationOffsetAbove)) { + navigationOffsetAbove = 0; + } + + axis.options.offset.below = saturated.saturate(navigationOffsetBelow + (axis.options.offset.below || 0)); + axis.options.offset.above = saturated.saturate(navigationOffsetAbove + (axis.options.offset.above || 0)); + } + }); + + prevDelta = delta; + plot.setupGrid(true); + plot.draw(); + + if (!preventEvent) { + plot.getPlaceholder().trigger("plotpan", [plot, delta, panAxes, initialState]); + } + }; + + plot.smartPan.end = function() { + panHint = null; + lockedDirection = null; + prevDelta = { x: 0, y: 0 }; + plot.triggerRedrawOverlay(); + } + + function shutdown(plot, eventHolder) { + eventHolder.unbind("mousewheel", onMouseWheel); + eventHolder.unbind("mousedown", onMouseDown); + eventHolder.unbind("mouseup", onMouseUp); + eventHolder.unbind("dragstart", onDragStart); + eventHolder.unbind("drag", onDrag); + eventHolder.unbind("dragend", onDragEnd); + eventHolder.unbind("dblclick", onDblClick); + eventHolder.unbind("click", onClick); + + if (panTimeout) clearTimeout(panTimeout); + } + + function drawOverlay(plot, ctx) { + if (panHint) { + ctx.strokeStyle = 'rgba(96, 160, 208, 0.7)'; + ctx.lineWidth = 2; + ctx.lineJoin = "round"; + var startx = Math.round(panHint.start.x), + starty = Math.round(panHint.start.y), + endx, endy; + + if (panAxes) { + if (panAxes[0].direction === 'x') { + endy = Math.round(panHint.start.y); + endx = Math.round(panHint.end.x); + } else if (panAxes[0].direction === 'y') { + endx = Math.round(panHint.start.x); + endy = Math.round(panHint.end.y); + } + } else { + endx = Math.round(panHint.end.x); + endy = Math.round(panHint.end.y); + } + + ctx.beginPath(); + + if (panHint.end === false) { + ctx.moveTo(startx, starty - PANHINT_LENGTH_CONSTANT); + ctx.lineTo(startx, starty + PANHINT_LENGTH_CONSTANT); + + ctx.moveTo(startx + PANHINT_LENGTH_CONSTANT, starty); + ctx.lineTo(startx - PANHINT_LENGTH_CONSTANT, starty); + } else { + var dirX = starty === endy; + + ctx.moveTo(startx - (dirX ? 0 : PANHINT_LENGTH_CONSTANT), starty - (dirX ? PANHINT_LENGTH_CONSTANT : 0)); + ctx.lineTo(startx + (dirX ? 0 : PANHINT_LENGTH_CONSTANT), starty + (dirX ? PANHINT_LENGTH_CONSTANT : 0)); + + ctx.moveTo(startx, starty); + ctx.lineTo(endx, endy); + + ctx.moveTo(endx - (dirX ? 0 : PANHINT_LENGTH_CONSTANT), endy - (dirX ? PANHINT_LENGTH_CONSTANT : 0)); + ctx.lineTo(endx + (dirX ? 0 : PANHINT_LENGTH_CONSTANT), endy + (dirX ? PANHINT_LENGTH_CONSTANT : 0)); + } + + ctx.stroke(); + } + } + + plot.getTouchedAxis = function(touchPointX, touchPointY) { + var ec = plot.getPlaceholder().offset(); + ec.left = touchPointX - ec.left; + ec.top = touchPointY - ec.top; + + var axis = plot.getXAxes().concat(plot.getYAxes()).filter(function (axis) { + var box = axis.box; + if (box !== undefined) { + return (ec.left > box.left) && (ec.left < box.left + box.width) && + (ec.top > box.top) && (ec.top < box.top + box.height); + } + }); + + return axis; + } + + plot.hooks.drawOverlay.push(drawOverlay); + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'navigate', + version: '1.3' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.pie.js b/terahz/templates/lib/flot/jquery.flot.pie.js new file mode 100644 index 0000000..ec734a1 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.pie.js @@ -0,0 +1,786 @@ +/* Flot plugin for rendering pie charts. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin assumes that each series has a single data value, and that each +value is a positive integer or zero. Negative numbers don't make sense for a +pie chart, and have unpredictable results. The values do NOT need to be +passed in as percentages; the plugin will calculate the total and per-slice +percentages internally. + +* Created by Brian Medendorp + +* Updated with contributions from btburnett3, Anthony Aragues and Xavi Ivars + +The plugin supports these options: + + series: { + pie: { + show: true/false + radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto' + innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect + startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result + tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show) + offset: { + top: integer value to move the pie up or down + left: integer value to move the pie left or right, or 'auto' + }, + stroke: { + color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF') + width: integer pixel width of the stroke + }, + label: { + show: true/false, or 'auto' + formatter: a user-defined function that modifies the text/style of the label text + radius: 0-1 for percentage of fullsize, or a specified pixel length + background: { + color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000') + opacity: 0-1 + }, + threshold: 0-1 for the percentage value at which to hide labels (if they're too small) + }, + combine: { + threshold: 0-1 for the percentage value at which to combine slices (if they're too small) + color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined + label: any text value of what the combined slice should be labeled + } + highlight: { + opacity: 0-1 + } + } + } + +More detail and specific examples can be found in the included HTML file. + +*/ + +(function($) { + // Maximum redraw attempts when fitting labels within the plot + + var REDRAW_ATTEMPTS = 10; + + // Factor by which to shrink the pie when fitting labels within the plot + + var REDRAW_SHRINK = 0.95; + + function init(plot) { + var canvas = null, + target = null, + options = null, + maxRadius = null, + centerLeft = null, + centerTop = null, + processed = false, + ctx = null; + + // interactive variables + + var highlights = []; + + // add hook to determine if pie plugin in enabled, and then perform necessary operations + + plot.hooks.processOptions.push(function(plot, options) { + if (options.series.pie.show) { + options.grid.show = false; + + // set labels.show + + if (options.series.pie.label.show === "auto") { + if (options.legend.show) { + options.series.pie.label.show = false; + } else { + options.series.pie.label.show = true; + } + } + + // set radius + + if (options.series.pie.radius === "auto") { + if (options.series.pie.label.show) { + options.series.pie.radius = 3 / 4; + } else { + options.series.pie.radius = 1; + } + } + + // ensure sane tilt + + if (options.series.pie.tilt > 1) { + options.series.pie.tilt = 1; + } else if (options.series.pie.tilt < 0) { + options.series.pie.tilt = 0; + } + } + }); + + plot.hooks.bindEvents.push(function(plot, eventHolder) { + var options = plot.getOptions(); + if (options.series.pie.show) { + if (options.grid.hoverable) { + eventHolder.unbind("mousemove").mousemove(onMouseMove); + } + if (options.grid.clickable) { + eventHolder.unbind("click").click(onClick); + } + } + }); + + plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) { + var options = plot.getOptions(); + if (options.series.pie.show) { + processDatapoints(plot, series, data, datapoints); + } + }); + + plot.hooks.drawOverlay.push(function(plot, octx) { + var options = plot.getOptions(); + if (options.series.pie.show) { + drawOverlay(plot, octx); + } + }); + + plot.hooks.draw.push(function(plot, newCtx) { + var options = plot.getOptions(); + if (options.series.pie.show) { + draw(plot, newCtx); + } + }); + + function processDatapoints(plot, series, datapoints) { + if (!processed) { + processed = true; + canvas = plot.getCanvas(); + target = $(canvas).parent(); + options = plot.getOptions(); + plot.setData(combine(plot.getData())); + } + } + + function combine(data) { + var total = 0, + combined = 0, + numCombined = 0, + color = options.series.pie.combine.color, + newdata = [], + i, + value; + + // Fix up the raw data from Flot, ensuring the data is numeric + + for (i = 0; i < data.length; ++i) { + value = data[i].data; + + // If the data is an array, we'll assume that it's a standard + // Flot x-y pair, and are concerned only with the second value. + + // Note how we use the original array, rather than creating a + // new one; this is more efficient and preserves any extra data + // that the user may have stored in higher indexes. + + if ($.isArray(value) && value.length === 1) { + value = value[0]; + } + + if ($.isArray(value)) { + // Equivalent to $.isNumeric() but compatible with jQuery < 1.7 + if (!isNaN(parseFloat(value[1])) && isFinite(value[1])) { + value[1] = +value[1]; + } else { + value[1] = 0; + } + } else if (!isNaN(parseFloat(value)) && isFinite(value)) { + value = [1, +value]; + } else { + value = [1, 0]; + } + + data[i].data = [value]; + } + + // Sum up all the slices, so we can calculate percentages for each + + for (i = 0; i < data.length; ++i) { + total += data[i].data[0][1]; + } + + // Count the number of slices with percentages below the combine + // threshold; if it turns out to be just one, we won't combine. + + for (i = 0; i < data.length; ++i) { + value = data[i].data[0][1]; + if (value / total <= options.series.pie.combine.threshold) { + combined += value; + numCombined++; + if (!color) { + color = data[i].color; + } + } + } + + for (i = 0; i < data.length; ++i) { + value = data[i].data[0][1]; + if (numCombined < 2 || value / total > options.series.pie.combine.threshold) { + newdata.push( + $.extend(data[i], { /* extend to allow keeping all other original data values + and using them e.g. in labelFormatter. */ + data: [[1, value]], + color: data[i].color, + label: data[i].label, + angle: value * Math.PI * 2 / total, + percent: value / (total / 100) + }) + ); + } + } + + if (numCombined > 1) { + newdata.push({ + data: [[1, combined]], + color: color, + label: options.series.pie.combine.label, + angle: combined * Math.PI * 2 / total, + percent: combined / (total / 100) + }); + } + + return newdata; + } + + function draw(plot, newCtx) { + if (!target) { + return; // if no series were passed + } + + var canvasWidth = plot.getPlaceholder().width(), + canvasHeight = plot.getPlaceholder().height(), + legendWidth = target.children().filter(".legend").children().width() || 0; + + ctx = newCtx; + + // WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE! + + // When combining smaller slices into an 'other' slice, we need to + // add a new series. Since Flot gives plugins no way to modify the + // list of series, the pie plugin uses a hack where the first call + // to processDatapoints results in a call to setData with the new + // list of series, then subsequent processDatapoints do nothing. + + // The plugin-global 'processed' flag is used to control this hack; + // it starts out false, and is set to true after the first call to + // processDatapoints. + + // Unfortunately this turns future setData calls into no-ops; they + // call processDatapoints, the flag is true, and nothing happens. + + // To fix this we'll set the flag back to false here in draw, when + // all series have been processed, so the next sequence of calls to + // processDatapoints once again starts out with a slice-combine. + // This is really a hack; in 0.9 we need to give plugins a proper + // way to modify series before any processing begins. + + processed = false; + + // calculate maximum radius and center point + maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2; + centerTop = canvasHeight / 2 + options.series.pie.offset.top; + centerLeft = canvasWidth / 2; + + if (options.series.pie.offset.left === "auto") { + if (options.legend.position.match("w")) { + centerLeft += legendWidth / 2; + } else { + centerLeft -= legendWidth / 2; + } + if (centerLeft < maxRadius) { + centerLeft = maxRadius; + } else if (centerLeft > canvasWidth - maxRadius) { + centerLeft = canvasWidth - maxRadius; + } + } else { + centerLeft += options.series.pie.offset.left; + } + + var slices = plot.getData(), + attempts = 0; + + // Keep shrinking the pie's radius until drawPie returns true, + // indicating that all the labels fit, or we try too many times. + do { + if (attempts > 0) { + maxRadius *= REDRAW_SHRINK; + } + attempts += 1; + clear(); + if (options.series.pie.tilt <= 0.8) { + drawShadow(); + } + } while (!drawPie() && attempts < REDRAW_ATTEMPTS) + + if (attempts >= REDRAW_ATTEMPTS) { + clear(); + target.prepend("
Could not draw pie with labels contained inside canvas
"); + } + + if (plot.setSeries && plot.insertLegend) { + plot.setSeries(slices); + plot.insertLegend(); + } + + // we're actually done at this point, just defining internal functions at this point + function clear() { + ctx.clearRect(0, 0, canvasWidth, canvasHeight); + target.children().filter(".pieLabel, .pieLabelBackground").remove(); + } + + function drawShadow() { + var shadowLeft = options.series.pie.shadow.left; + var shadowTop = options.series.pie.shadow.top; + var edge = 10; + var alpha = options.series.pie.shadow.alpha; + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + + if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) { + return; // shadow would be outside canvas, so don't draw it + } + + ctx.save(); + ctx.translate(shadowLeft, shadowTop); + ctx.globalAlpha = alpha; + ctx.fillStyle = "#000"; + + // center and rotate to starting position + ctx.translate(centerLeft, centerTop); + ctx.scale(1, options.series.pie.tilt); + + //radius -= edge; + for (var i = 1; i <= edge; i++) { + ctx.beginPath(); + ctx.arc(0, 0, radius, 0, Math.PI * 2, false); + ctx.fill(); + radius -= i; + } + + ctx.restore(); + } + + function drawPie() { + var startAngle = Math.PI * options.series.pie.startAngle; + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + var i; + // center and rotate to starting position + + ctx.save(); + ctx.translate(centerLeft, centerTop); + ctx.scale(1, options.series.pie.tilt); + //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera + + // draw slices + ctx.save(); + + var currentAngle = startAngle; + for (i = 0; i < slices.length; ++i) { + slices[i].startAngle = currentAngle; + drawSlice(slices[i].angle, slices[i].color, true); + } + + ctx.restore(); + + // draw slice outlines + if (options.series.pie.stroke.width > 0) { + ctx.save(); + ctx.lineWidth = options.series.pie.stroke.width; + currentAngle = startAngle; + for (i = 0; i < slices.length; ++i) { + drawSlice(slices[i].angle, options.series.pie.stroke.color, false); + } + + ctx.restore(); + } + + // draw donut hole + drawDonutHole(ctx); + + ctx.restore(); + + // Draw the labels, returning true if they fit within the plot + if (options.series.pie.label.show) { + return drawLabels(); + } else return true; + + function drawSlice(angle, color, fill) { + if (angle <= 0 || isNaN(angle)) { + return; + } + + if (fill) { + ctx.fillStyle = color; + } else { + ctx.strokeStyle = color; + ctx.lineJoin = "round"; + } + + ctx.beginPath(); + if (Math.abs(angle - Math.PI * 2) > 0.000000001) { + ctx.moveTo(0, 0); // Center of the pie + } + + //ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera + ctx.arc(0, 0, radius, currentAngle, currentAngle + angle / 2, false); + ctx.arc(0, 0, radius, currentAngle + angle / 2, currentAngle + angle, false); + ctx.closePath(); + //ctx.rotate(angle); // This doesn't work properly in Opera + currentAngle += angle; + + if (fill) { + ctx.fill(); + } else { + ctx.stroke(); + } + } + + function drawLabels() { + var currentAngle = startAngle; + var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius; + + for (var i = 0; i < slices.length; ++i) { + if (slices[i].percent >= options.series.pie.label.threshold * 100) { + if (!drawLabel(slices[i], currentAngle, i)) { + return false; + } + } + currentAngle += slices[i].angle; + } + + return true; + + function drawLabel(slice, startAngle, index) { + if (slice.data[0][1] === 0) { + return true; + } + + // format label text + var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter; + + if (lf) { + text = lf(slice.label, slice); + } else { + text = slice.label; + } + + if (plf) { + text = plf(text, slice); + } + + var halfAngle = ((startAngle + slice.angle) + startAngle) / 2; + var x = centerLeft + Math.round(Math.cos(halfAngle) * radius); + var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt; + + var html = "" + text + ""; + target.append(html); + + var label = target.children("#pieLabel" + index); + var labelTop = (y - label.height() / 2); + var labelLeft = (x - label.width() / 2); + + label.css("top", labelTop); + label.css("left", labelLeft); + + // check to make sure that the label is not outside the canvas + if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) { + return false; + } + + if (options.series.pie.label.background.opacity !== 0) { + // put in the transparent background separately to avoid blended labels and label boxes + var c = options.series.pie.label.background.color; + if (c == null) { + c = slice.color; + } + + var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;"; + $("
") + .css("opacity", options.series.pie.label.background.opacity) + .insertBefore(label); + } + + return true; + } // end individual label function + } // end drawLabels function + } // end drawPie function + } // end draw function + + // Placed here because it needs to be accessed from multiple locations + + function drawDonutHole(layer) { + if (options.series.pie.innerRadius > 0) { + // subtract the center + layer.save(); + var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius; + layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color + layer.beginPath(); + layer.fillStyle = options.series.pie.stroke.color; + layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); + layer.fill(); + layer.closePath(); + layer.restore(); + + // add inner stroke + layer.save(); + layer.beginPath(); + layer.strokeStyle = options.series.pie.stroke.color; + layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); + layer.stroke(); + layer.closePath(); + layer.restore(); + + // TODO: add extra shadow inside hole (with a mask) if the pie is tilted. + } + } + + //-- Additional Interactive related functions -- + + function isPointInPoly(poly, pt) { + for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) { + ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || + (poly[j][1] <= pt[1] && pt[1] < poly[i][1])) && + (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) && + (c = !c); + } + return c; + } + + function findNearbySlice(mouseX, mouseY) { + var slices = plot.getData(), + options = plot.getOptions(), + radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius, + x, y; + + for (var i = 0; i < slices.length; ++i) { + var s = slices[i]; + if (s.pie.show) { + ctx.save(); + ctx.beginPath(); + ctx.moveTo(0, 0); // Center of the pie + //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here. + ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false); + ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false); + ctx.closePath(); + x = mouseX - centerLeft; + y = mouseY - centerTop; + + if (ctx.isPointInPath) { + if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) { + ctx.restore(); + return { + datapoint: [s.percent, s.data], + dataIndex: 0, + series: s, + seriesIndex: i + }; + } + } else { + // excanvas for IE doesn;t support isPointInPath, this is a workaround. + var p1X = radius * Math.cos(s.startAngle), + p1Y = radius * Math.sin(s.startAngle), + p2X = radius * Math.cos(s.startAngle + s.angle / 4), + p2Y = radius * Math.sin(s.startAngle + s.angle / 4), + p3X = radius * Math.cos(s.startAngle + s.angle / 2), + p3Y = radius * Math.sin(s.startAngle + s.angle / 2), + p4X = radius * Math.cos(s.startAngle + s.angle / 1.5), + p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5), + p5X = radius * Math.cos(s.startAngle + s.angle), + p5Y = radius * Math.sin(s.startAngle + s.angle), + arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]], + arrPoint = [x, y]; + + // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt? + + if (isPointInPoly(arrPoly, arrPoint)) { + ctx.restore(); + return { + datapoint: [s.percent, s.data], + dataIndex: 0, + series: s, + seriesIndex: i + }; + } + } + + ctx.restore(); + } + } + + return null; + } + + function onMouseMove(e) { + triggerClickHoverEvent("plothover", e); + } + + function onClick(e) { + triggerClickHoverEvent("plotclick", e); + } + + // trigger click or hover event (they send the same parameters so we share their code) + + function triggerClickHoverEvent(eventname, e) { + var offset = plot.offset(); + var canvasX = parseInt(e.pageX - offset.left); + var canvasY = parseInt(e.pageY - offset.top); + var item = findNearbySlice(canvasX, canvasY); + + if (options.grid.autoHighlight) { + // clear auto-highlights + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.auto === eventname && !(item && h.series === item.series)) { + unhighlight(h.series); + } + } + } + + // highlight the slice + + if (item) { + highlight(item.series, eventname); + } + + // trigger any hover bind events + + var pos = { pageX: e.pageX, pageY: e.pageY }; + target.trigger(eventname, [pos, item]); + } + + function highlight(s, auto) { + //if (typeof s == "number") { + // s = series[s]; + //} + + var i = indexOfHighlight(s); + + if (i === -1) { + highlights.push({ series: s, auto: auto }); + plot.triggerRedrawOverlay(); + } else if (!auto) { + highlights[i].auto = false; + } + } + + function unhighlight(s) { + if (s == null) { + highlights = []; + plot.triggerRedrawOverlay(); + } + + //if (typeof s == "number") { + // s = series[s]; + //} + + var i = indexOfHighlight(s); + + if (i !== -1) { + highlights.splice(i, 1); + plot.triggerRedrawOverlay(); + } + } + + function indexOfHighlight(s) { + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.series === s) { + return i; + } + } + return -1; + } + + function drawOverlay(plot, octx) { + var options = plot.getOptions(); + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + + octx.save(); + octx.translate(centerLeft, centerTop); + octx.scale(1, options.series.pie.tilt); + + for (var i = 0; i < highlights.length; ++i) { + drawHighlight(highlights[i].series); + } + + drawDonutHole(octx); + + octx.restore(); + + function drawHighlight(series) { + if (series.angle <= 0 || isNaN(series.angle)) { + return; + } + + //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString(); + octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor + octx.beginPath(); + if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) { + octx.moveTo(0, 0); // Center of the pie + } + octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false); + octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false); + octx.closePath(); + octx.fill(); + } + } + } // end init (plugin body) + + // define pie specific options and their default values + var options = { + series: { + pie: { + show: false, + radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value) + innerRadius: 0, /* for donut */ + startAngle: 3 / 2, + tilt: 1, + shadow: { + left: 5, // shadow left offset + top: 15, // shadow top offset + alpha: 0.02 // shadow alpha + }, + offset: { + top: 0, + left: "auto" + }, + stroke: { + color: "#fff", + width: 1 + }, + label: { + show: "auto", + formatter: function(label, slice) { + return "
" + label + "
" + Math.round(slice.percent) + "%
"; + }, // formatter function + radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value) + background: { + color: null, + opacity: 0 + }, + threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow) + }, + combine: { + threshold: -1, // percentage at which to combine little slices into one larger slice + color: null, // color to give the new slice (auto-generated if null) + label: "Other" // label to give the new slice + }, + highlight: { + //color: "#fff", // will add this functionality once parseColor is available + opacity: 0.5 + } + } + } + }; + + $.plot.plugins.push({ + init: init, + options: options, + name: "pie", + version: "1.1" + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.resize.js b/terahz/templates/lib/flot/jquery.flot.resize.js new file mode 100644 index 0000000..930c68e --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.resize.js @@ -0,0 +1,60 @@ +/* eslint-disable */ +/* Flot plugin for automatically redrawing plots as the placeholder resizes. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +It works by listening for changes on the placeholder div (through the jQuery +resize event plugin) - if the size changes, it will redraw the plot. + +There are no options. If you need to disable the plugin for some plots, you +can just fix the size of their placeholders. + +*/ + +/* Inline dependency: + * jQuery resize event - v1.1 - 3/14/2010 + * http://benalman.com/projects/jquery-resize-plugin/ + * + * Copyright (c) 2010 "Cowboy" Ben Alman + * Dual licensed under the MIT and GPL licenses. + * http://benalman.com/about/license/ + */ +(function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this); + +/* eslint-enable */ +(function ($) { + var options = { }; // no options + + function init(plot) { + function onResize() { + var placeholder = plot.getPlaceholder(); + + // somebody might have hidden us and we can't plot + // when we don't have the dimensions + if (placeholder.width() === 0 || placeholder.height() === 0) return; + + plot.resize(); + plot.setupGrid(); + plot.draw(); + } + + function bindEvents(plot, eventHolder) { + plot.getPlaceholder().resize(onResize); + } + + function shutdown(plot, eventHolder) { + plot.getPlaceholder().unbind("resize", onResize); + } + + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'resize', + version: '1.0' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.saturated.js b/terahz/templates/lib/flot/jquery.flot.saturated.js new file mode 100644 index 0000000..34b9c50 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.saturated.js @@ -0,0 +1,43 @@ +(function ($) { + 'use strict'; + var saturated = { + saturate: function (a) { + if (a === Infinity) { + return Number.MAX_VALUE; + } + + if (a === -Infinity) { + return -Number.MAX_VALUE; + } + + return a; + }, + delta: function(min, max, noTicks) { + return ((max - min) / noTicks) === Infinity ? (max / noTicks - min / noTicks) : (max - min) / noTicks + }, + multiply: function (a, b) { + return saturated.saturate(a * b); + }, + // returns c * bInt * a. Beahves properly in the case where c is negative + // and bInt * a is bigger that Number.MAX_VALUE (Infinity) + multiplyAdd: function (a, bInt, c) { + if (isFinite(a * bInt)) { + return saturated.saturate(a * bInt + c); + } else { + var result = c; + + for (var i = 0; i < bInt; i++) { + result += a; + } + + return saturated.saturate(result); + } + }, + // round to nearby lower multiple of base + floorInBase: function(n, base) { + return base * Math.floor(n / base); + } + }; + + $.plot.saturated = saturated; +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.selection.js b/terahz/templates/lib/flot/jquery.flot.selection.js new file mode 100644 index 0000000..9c9fada --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.selection.js @@ -0,0 +1,517 @@ +/* Flot plugin for selecting regions of a plot. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin supports these options: + +selection: { + mode: null or "x" or "y" or "xy" or "smart", + color: color, + shape: "round" or "miter" or "bevel", + visualization: "fill" or "focus", + minSize: number of pixels +} + +Selection support is enabled by setting the mode to one of "x", "y" or "xy". +In "x" mode, the user will only be able to specify the x range, similarly for +"y" mode. For "xy", the selection becomes a rectangle where both ranges can be +specified. "color" is color of the selection (if you need to change the color +later on, you can get to it with plot.getOptions().selection.color). "shape" +is the shape of the corners of the selection. + +The way how the selection is visualized, can be changed by using the option +"visualization". Flot currently supports two modes: "focus" and "fill". The +option "focus" draws a colored bezel around the selected area while keeping +the selected area clear. The option "fill" highlights (i.e., fills) the +selected area with a colored highlight. + +"minSize" is the minimum size a selection can be in pixels. This value can +be customized to determine the smallest size a selection can be and still +have the selection rectangle be displayed. When customizing this value, the +fact that it refers to pixels, not axis units must be taken into account. +Thus, for example, if there is a bar graph in time mode with BarWidth set to 1 +minute, setting "minSize" to 1 will not make the minimum selection size 1 +minute, but rather 1 pixel. Note also that setting "minSize" to 0 will prevent +"plotunselected" events from being fired when the user clicks the mouse without +dragging. + +When selection support is enabled, a "plotselected" event will be emitted on +the DOM element you passed into the plot function. The event handler gets a +parameter with the ranges selected on the axes, like this: + + placeholder.bind( "plotselected", function( event, ranges ) { + alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to) + // similar for yaxis - with multiple axes, the extra ones are in + // x2axis, x3axis, ... + }); + +The "plotselected" event is only fired when the user has finished making the +selection. A "plotselecting" event is fired during the process with the same +parameters as the "plotselected" event, in case you want to know what's +happening while it's happening, + +A "plotunselected" event with no arguments is emitted when the user clicks the +mouse to remove the selection. As stated above, setting "minSize" to 0 will +destroy this behavior. + +The plugin allso adds the following methods to the plot object: + +- setSelection( ranges, preventEvent ) + + Set the selection rectangle. The passed in ranges is on the same form as + returned in the "plotselected" event. If the selection mode is "x", you + should put in either an xaxis range, if the mode is "y" you need to put in + an yaxis range and both xaxis and yaxis if the selection mode is "xy", like + this: + + setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } }); + + setSelection will trigger the "plotselected" event when called. If you don't + want that to happen, e.g. if you're inside a "plotselected" handler, pass + true as the second parameter. If you are using multiple axes, you can + specify the ranges on any of those, e.g. as x2axis/x3axis/... instead of + xaxis, the plugin picks the first one it sees. + +- clearSelection( preventEvent ) + + Clear the selection rectangle. Pass in true to avoid getting a + "plotunselected" event. + +- getSelection() + + Returns the current selection in the same format as the "plotselected" + event. If there's currently no selection, the function returns null. + +*/ + +(function ($) { + function init(plot) { + var selection = { + first: {x: -1, y: -1}, + second: {x: -1, y: -1}, + show: false, + currentMode: 'xy', + active: false + }; + + var SNAPPING_CONSTANT = $.plot.uiConstants.SNAPPING_CONSTANT; + + // FIXME: The drag handling implemented here should be + // abstracted out, there's some similar code from a library in + // the navigation plugin, this should be massaged a bit to fit + // the Flot cases here better and reused. Doing this would + // make this plugin much slimmer. + var savedhandlers = {}; + + function onDrag(e) { + if (selection.active) { + updateSelection(e); + + plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]); + } + } + + function onDragStart(e) { + var o = plot.getOptions(); + // only accept left-click + if (e.which !== 1 || o.selection.mode === null) return; + + // reinitialize currentMode + selection.currentMode = 'xy'; + + // cancel out any text selections + document.body.focus(); + + // prevent text selection and drag in old-school browsers + if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) { + savedhandlers.onselectstart = document.onselectstart; + document.onselectstart = function () { return false; }; + } + if (document.ondrag !== undefined && savedhandlers.ondrag == null) { + savedhandlers.ondrag = document.ondrag; + document.ondrag = function () { return false; }; + } + + setSelectionPos(selection.first, e); + + selection.active = true; + } + + function onDragEnd(e) { + // revert drag stuff for old-school browsers + if (document.onselectstart !== undefined) { + document.onselectstart = savedhandlers.onselectstart; + } + + if (document.ondrag !== undefined) { + document.ondrag = savedhandlers.ondrag; + } + + // no more dragging + selection.active = false; + updateSelection(e); + + if (selectionIsSane()) { + triggerSelectedEvent(); + } else { + // this counts as a clear + plot.getPlaceholder().trigger("plotunselected", [ ]); + plot.getPlaceholder().trigger("plotselecting", [ null ]); + } + + return false; + } + + function getSelection() { + if (!selectionIsSane()) return null; + + if (!selection.show) return null; + + var r = {}, + c1 = {x: selection.first.x, y: selection.first.y}, + c2 = {x: selection.second.x, y: selection.second.y}; + + if (selectionDirection(plot) === 'x') { + c1.y = 0; + c2.y = plot.height(); + } + + if (selectionDirection(plot) === 'y') { + c1.x = 0; + c2.x = plot.width(); + } + + $.each(plot.getAxes(), function (name, axis) { + if (axis.used) { + var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]); + r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) }; + } + }); + return r; + } + + function triggerSelectedEvent() { + var r = getSelection(); + + plot.getPlaceholder().trigger("plotselected", [ r ]); + + // backwards-compat stuff, to be removed in future + if (r.xaxis && r.yaxis) { + plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]); + } + } + + function clamp(min, value, max) { + return value < min ? min : (value > max ? max : value); + } + + function selectionDirection(plot) { + var o = plot.getOptions(); + + if (o.selection.mode === 'smart') { + return selection.currentMode; + } else { + return o.selection.mode; + } + } + + function updateMode(pos) { + if (selection.first) { + var delta = { + x: pos.x - selection.first.x, + y: pos.y - selection.first.y + }; + + if (Math.abs(delta.x) < SNAPPING_CONSTANT) { + selection.currentMode = 'y'; + } else if (Math.abs(delta.y) < SNAPPING_CONSTANT) { + selection.currentMode = 'x'; + } else { + selection.currentMode = 'xy'; + } + } + } + + function setSelectionPos(pos, e) { + var offset = plot.getPlaceholder().offset(); + var plotOffset = plot.getPlotOffset(); + pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width()); + pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height()); + + if (pos !== selection.first) updateMode(pos); + + if (selectionDirection(plot) === "y") { + pos.x = pos === selection.first ? 0 : plot.width(); + } + + if (selectionDirection(plot) === "x") { + pos.y = pos === selection.first ? 0 : plot.height(); + } + } + + function updateSelection(pos) { + if (pos.pageX == null) return; + + setSelectionPos(selection.second, pos); + if (selectionIsSane()) { + selection.show = true; + plot.triggerRedrawOverlay(); + } else clearSelection(true); + } + + function clearSelection(preventEvent) { + if (selection.show) { + selection.show = false; + selection.currentMode = ''; + plot.triggerRedrawOverlay(); + if (!preventEvent) { + plot.getPlaceholder().trigger("plotunselected", [ ]); + } + } + } + + // function taken from markings support in Flot + function extractRange(ranges, coord) { + var axis, from, to, key, axes = plot.getAxes(); + + for (var k in axes) { + axis = axes[k]; + if (axis.direction === coord) { + key = coord + axis.n + "axis"; + if (!ranges[key] && axis.n === 1) { + // support x1axis as xaxis + key = coord + "axis"; + } + + if (ranges[key]) { + from = ranges[key].from; + to = ranges[key].to; + break; + } + } + } + + // backwards-compat stuff - to be removed in future + if (!ranges[key]) { + axis = coord === "x" ? plot.getXAxes()[0] : plot.getYAxes()[0]; + from = ranges[coord + "1"]; + to = ranges[coord + "2"]; + } + + // auto-reverse as an added bonus + if (from != null && to != null && from > to) { + var tmp = from; + from = to; + to = tmp; + } + + return { from: from, to: to, axis: axis }; + } + + function setSelection(ranges, preventEvent) { + var range; + + if (selectionDirection(plot) === "y") { + selection.first.x = 0; + selection.second.x = plot.width(); + } else { + range = extractRange(ranges, "x"); + selection.first.x = range.axis.p2c(range.from); + selection.second.x = range.axis.p2c(range.to); + } + + if (selectionDirection(plot) === "x") { + selection.first.y = 0; + selection.second.y = plot.height(); + } else { + range = extractRange(ranges, "y"); + selection.first.y = range.axis.p2c(range.from); + selection.second.y = range.axis.p2c(range.to); + } + + selection.show = true; + plot.triggerRedrawOverlay(); + if (!preventEvent && selectionIsSane()) { + triggerSelectedEvent(); + } + } + + function selectionIsSane() { + var minSize = plot.getOptions().selection.minSize; + return Math.abs(selection.second.x - selection.first.x) >= minSize && + Math.abs(selection.second.y - selection.first.y) >= minSize; + } + + plot.clearSelection = clearSelection; + plot.setSelection = setSelection; + plot.getSelection = getSelection; + + plot.hooks.bindEvents.push(function(plot, eventHolder) { + var o = plot.getOptions(); + if (o.selection.mode != null) { + plot.addEventHandler("dragstart", onDragStart, eventHolder, 0); + plot.addEventHandler("drag", onDrag, eventHolder, 0); + plot.addEventHandler("dragend", onDragEnd, eventHolder, 0); + } + }); + + function drawSelectionDecorations(ctx, x, y, w, h, oX, oY, mode) { + var spacing = 3; + var fullEarWidth = 15; + var earWidth = Math.max(0, Math.min(fullEarWidth, w / 2 - 2, h / 2 - 2)); + ctx.fillStyle = '#ffffff'; + + if (mode === 'xy') { + ctx.beginPath(); + ctx.moveTo(x, y + earWidth); + ctx.lineTo(x - 3, y + earWidth); + ctx.lineTo(x - 3, y - 3); + ctx.lineTo(x + earWidth, y - 3); + ctx.lineTo(x + earWidth, y); + ctx.lineTo(x, y); + ctx.closePath(); + + ctx.moveTo(x, y + h - earWidth); + ctx.lineTo(x - 3, y + h - earWidth); + ctx.lineTo(x - 3, y + h + 3); + ctx.lineTo(x + earWidth, y + h + 3); + ctx.lineTo(x + earWidth, y + h); + ctx.lineTo(x, y + h); + ctx.closePath(); + + ctx.moveTo(x + w, y + earWidth); + ctx.lineTo(x + w + 3, y + earWidth); + ctx.lineTo(x + w + 3, y - 3); + ctx.lineTo(x + w - earWidth, y - 3); + ctx.lineTo(x + w - earWidth, y); + ctx.lineTo(x + w, y); + ctx.closePath(); + + ctx.moveTo(x + w, y + h - earWidth); + ctx.lineTo(x + w + 3, y + h - earWidth); + ctx.lineTo(x + w + 3, y + h + 3); + ctx.lineTo(x + w - earWidth, y + h + 3); + ctx.lineTo(x + w - earWidth, y + h); + ctx.lineTo(x + w, y + h); + ctx.closePath(); + + ctx.stroke(); + ctx.fill(); + } + + x = oX; + y = oY; + + if (mode === 'x') { + ctx.beginPath(); + ctx.moveTo(x, y + fullEarWidth); + ctx.lineTo(x, y - fullEarWidth); + ctx.lineTo(x - spacing, y - fullEarWidth); + ctx.lineTo(x - spacing, y + fullEarWidth); + ctx.closePath(); + + ctx.moveTo(x + w, y + fullEarWidth); + ctx.lineTo(x + w, y - fullEarWidth); + ctx.lineTo(x + w + spacing, y - fullEarWidth); + ctx.lineTo(x + w + spacing, y + fullEarWidth); + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + } + + if (mode === 'y') { + ctx.beginPath(); + + ctx.moveTo(x - fullEarWidth, y); + ctx.lineTo(x + fullEarWidth, y); + ctx.lineTo(x + fullEarWidth, y - spacing); + ctx.lineTo(x - fullEarWidth, y - spacing); + ctx.closePath(); + + ctx.moveTo(x - fullEarWidth, y + h); + ctx.lineTo(x + fullEarWidth, y + h); + ctx.lineTo(x + fullEarWidth, y + h + spacing); + ctx.lineTo(x - fullEarWidth, y + h + spacing); + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + } + } + + plot.hooks.drawOverlay.push(function (plot, ctx) { + // draw selection + if (selection.show && selectionIsSane()) { + var plotOffset = plot.getPlotOffset(); + var o = plot.getOptions(); + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var c = $.color.parse(o.selection.color); + var visualization = o.selection.visualization; + + var scalingFactor = 1; + + // use a dimmer scaling factor if visualization is "fill" + if (visualization === "fill") { + scalingFactor = 0.8; + } + + ctx.strokeStyle = c.scale('a', scalingFactor).toString(); + ctx.lineWidth = 1; + ctx.lineJoin = o.selection.shape; + ctx.fillStyle = c.scale('a', 0.4).toString(); + + var x = Math.min(selection.first.x, selection.second.x) + 0.5, + oX = x, + y = Math.min(selection.first.y, selection.second.y) + 0.5, + oY = y, + w = Math.abs(selection.second.x - selection.first.x) - 1, + h = Math.abs(selection.second.y - selection.first.y) - 1; + + if (selectionDirection(plot) === 'x') { + h += y; + y = 0; + } + + if (selectionDirection(plot) === 'y') { + w += x; + x = 0; + } + + if (visualization === "fill") { + ctx.fillRect(x, y, w, h); + ctx.strokeRect(x, y, w, h); + } else { + ctx.fillRect(0, 0, plot.width(), plot.height()); + ctx.clearRect(x, y, w, h); + drawSelectionDecorations(ctx, x, y, w, h, oX, oY, selectionDirection(plot)); + } + + ctx.restore(); + } + }); + + plot.hooks.shutdown.push(function (plot, eventHolder) { + eventHolder.unbind("dragstart", onDragStart); + eventHolder.unbind("drag", onDrag); + eventHolder.unbind("dragend", onDragEnd); + }); + } + + $.plot.plugins.push({ + init: init, + options: { + selection: { + mode: null, // one of null, "x", "y" or "xy" + visualization: "focus", // "focus" or "fill" + color: "#888888", + shape: "round", // one of "round", "miter", or "bevel" + minSize: 5 // minimum number of pixels + } + }, + name: 'selection', + version: '1.1' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.stack.js b/terahz/templates/lib/flot/jquery.flot.stack.js new file mode 100644 index 0000000..f4b88e7 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.stack.js @@ -0,0 +1,220 @@ +/* Flot plugin for stacking data sets rather than overlaying them. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin assumes the data is sorted on x (or y if stacking horizontally). +For line charts, it is assumed that if a line has an undefined gap (from a +null point), then the line above it should have the same gap - insert zeros +instead of "null" if you want another behaviour. This also holds for the start +and end of the chart. Note that stacking a mix of positive and negative values +in most instances doesn't make sense (so it looks weird). + +Two or more series are stacked when their "stack" attribute is set to the same +key (which can be any number or string or just "true"). To specify the default +stack, you can set the stack option like this: + + series: { + stack: null/false, true, or a key (number/string) + } + +You can also specify it for a single series, like this: + + $.plot( $("#placeholder"), [{ + data: [ ... ], + stack: true + }]) + +The stacking order is determined by the order of the data series in the array +(later series end up on top of the previous). + +Internally, the plugin modifies the datapoints in each series, adding an +offset to the y value. For line series, extra data points are inserted through +interpolation. If there's a second y value, it's also adjusted (e.g for bar +charts or filled areas). + +*/ + +(function ($) { + var options = { + series: { stack: null } // or number/string + }; + + function init(plot) { + function findMatchingSeries(s, allseries) { + var res = null; + for (var i = 0; i < allseries.length; ++i) { + if (s === allseries[i]) break; + + if (allseries[i].stack === s.stack) { + res = allseries[i]; + } + } + + return res; + } + + function addBottomPoints (s, datapoints) { + var formattedPoints = []; + for (var i = 0; i < datapoints.points.length; i += 2) { + formattedPoints.push(datapoints.points[i]); + formattedPoints.push(datapoints.points[i + 1]); + formattedPoints.push(0); + } + + datapoints.format.push({ + x: false, + y: true, + number: true, + required: false, + computeRange: s.yaxis.options.autoScale !== 'none', + defaultValue: 0 + }); + datapoints.points = formattedPoints; + datapoints.pointsize = 3; + } + + function stackData(plot, s, datapoints) { + if (s.stack == null || s.stack === false) return; + + var needsBottom = s.bars.show || (s.lines.show && s.lines.fill); + var hasBottom = datapoints.pointsize > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y); + // Series data is missing bottom points - need to format + if (needsBottom && !hasBottom) { + addBottomPoints(s, datapoints); + } + + var other = findMatchingSeries(s, plot.getData()); + if (!other) return; + + var ps = datapoints.pointsize, + points = datapoints.points, + otherps = other.datapoints.pointsize, + otherpoints = other.datapoints.points, + newpoints = [], + px, py, intery, qx, qy, bottom, + withlines = s.lines.show, + horizontal = s.bars.horizontal, + withsteps = withlines && s.lines.steps, + fromgap = true, + keyOffset = horizontal ? 1 : 0, + accumulateOffset = horizontal ? 0 : 1, + i = 0, j = 0, l, m; + + while (true) { + if (i >= points.length) break; + + l = newpoints.length; + + if (points[i] == null) { + // copy gaps + for (m = 0; m < ps; ++m) { + newpoints.push(points[i + m]); + } + + i += ps; + } else if (j >= otherpoints.length) { + // for lines, we can't use the rest of the points + if (!withlines) { + for (m = 0; m < ps; ++m) { + newpoints.push(points[i + m]); + } + } + + i += ps; + } else if (otherpoints[j] == null) { + // oops, got a gap + for (m = 0; m < ps; ++m) { + newpoints.push(null); + } + + fromgap = true; + j += otherps; + } else { + // cases where we actually got two points + px = points[i + keyOffset]; + py = points[i + accumulateOffset]; + qx = otherpoints[j + keyOffset]; + qy = otherpoints[j + accumulateOffset]; + bottom = 0; + + if (px === qx) { + for (m = 0; m < ps; ++m) { + newpoints.push(points[i + m]); + } + + newpoints[l + accumulateOffset] += qy; + bottom = qy; + + i += ps; + j += otherps; + } else if (px > qx) { + // we got past point below, might need to + // insert interpolated extra point + if (withlines && i > 0 && points[i - ps] != null) { + intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px); + newpoints.push(qx); + newpoints.push(intery + qy); + for (m = 2; m < ps; ++m) { + newpoints.push(points[i + m]); + } + + bottom = qy; + } + + j += otherps; + } else { // px < qx + if (fromgap && withlines) { + // if we come from a gap, we just skip this point + i += ps; + continue; + } + + for (m = 0; m < ps; ++m) { + newpoints.push(points[i + m]); + } + + // we might be able to interpolate a point below, + // this can give us a better y + if (withlines && j > 0 && otherpoints[j - otherps] != null) { + bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx); + } + + newpoints[l + accumulateOffset] += bottom; + + i += ps; + } + + fromgap = false; + + if (l !== newpoints.length && needsBottom) { + newpoints[l + 2] += bottom; + } + } + + // maintain the line steps invariant + if (withsteps && l !== newpoints.length && l > 0 && + newpoints[l] !== null && + newpoints[l] !== newpoints[l - ps] && + newpoints[l + 1] !== newpoints[l - ps + 1]) { + for (m = 0; m < ps; ++m) { + newpoints[l + ps + m] = newpoints[l + m]; + } + + newpoints[l + 1] = newpoints[l - ps + 1]; + } + } + + datapoints.points = newpoints; + } + + plot.hooks.processDatapoints.push(stackData); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'stack', + version: '1.2' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.symbol.js b/terahz/templates/lib/flot/jquery.flot.symbol.js new file mode 100644 index 0000000..0e06513 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.symbol.js @@ -0,0 +1,98 @@ +/* Flot plugin that adds some extra symbols for plotting points. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The symbols are accessed as strings through the standard symbol options: + + series: { + points: { + symbol: "square" // or "diamond", "triangle", "cross", "plus", "ellipse", "rectangle" + } + } + +*/ + +(function ($) { + // we normalize the area of each symbol so it is approximately the + // same as a circle of the given radius + + var square = function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.rect(x - size, y - size, size + size, size + size); + }, + rectangle = function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.rect(x - size, y - size, size + size, size + size); + }, + diamond = function (ctx, x, y, radius, shadow) { + // pi * r^2 = 2s^2 => s = r * sqrt(pi/2) + var size = radius * Math.sqrt(Math.PI / 2); + ctx.moveTo(x - size, y); + ctx.lineTo(x, y - size); + ctx.lineTo(x + size, y); + ctx.lineTo(x, y + size); + ctx.lineTo(x - size, y); + ctx.lineTo(x, y - size); + }, + triangle = function (ctx, x, y, radius, shadow) { + // pi * r^2 = 1/2 * s^2 * sin (pi / 3) => s = r * sqrt(2 * pi / sin(pi / 3)) + var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3)); + var height = size * Math.sin(Math.PI / 3); + ctx.moveTo(x - size / 2, y + height / 2); + ctx.lineTo(x + size / 2, y + height / 2); + if (!shadow) { + ctx.lineTo(x, y - height / 2); + ctx.lineTo(x - size / 2, y + height / 2); + ctx.lineTo(x + size / 2, y + height / 2); + } + }, + cross = function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.moveTo(x - size, y - size); + ctx.lineTo(x + size, y + size); + ctx.moveTo(x - size, y + size); + ctx.lineTo(x + size, y - size); + }, + ellipse = function(ctx, x, y, radius, shadow, fill) { + if (!shadow) { + ctx.moveTo(x + radius, y); + ctx.arc(x, y, radius, 0, Math.PI * 2, false); + } + }, + plus = function (ctx, x, y, radius, shadow) { + var size = radius * Math.sqrt(Math.PI / 2); + ctx.moveTo(x - size, y); + ctx.lineTo(x + size, y); + ctx.moveTo(x, y + size); + ctx.lineTo(x, y - size); + }, + handlers = { + square: square, + rectangle: rectangle, + diamond: diamond, + triangle: triangle, + cross: cross, + ellipse: ellipse, + plus: plus + }; + + square.fill = true; + rectangle.fill = true; + diamond.fill = true; + triangle.fill = true; + ellipse.fill = true; + + function init(plot) { + plot.drawSymbol = handlers; + } + + $.plot.plugins.push({ + init: init, + name: 'symbols', + version: '1.0' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.threshold.js b/terahz/templates/lib/flot/jquery.flot.threshold.js new file mode 100644 index 0000000..db5a59c --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.threshold.js @@ -0,0 +1,143 @@ +/* Flot plugin for thresholding data. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin supports these options: + + series: { + threshold: { + below: number + color: colorspec + } + } + +It can also be applied to a single series, like this: + + $.plot( $("#placeholder"), [{ + data: [ ... ], + threshold: { ... } + }]) + +An array can be passed for multiple thresholding, like this: + + threshold: [{ + below: number1 + color: color1 + },{ + below: number2 + color: color2 + }] + +These multiple threshold objects can be passed in any order since they are +sorted by the processing function. + +The data points below "below" are drawn with the specified color. This makes +it easy to mark points below 0, e.g. for budget data. + +Internally, the plugin works by splitting the data into two series, above and +below the threshold. The extra series below the threshold will have its label +cleared and the special "originSeries" attribute set to the original series. +You may need to check for this in hover events. + +*/ + +(function ($) { + var options = { + series: { threshold: null } // or { below: number, color: color spec} + }; + + function init(plot) { + function thresholdData(plot, s, datapoints, below, color) { + var ps = datapoints.pointsize, i, x, y, p, prevp, + thresholded = $.extend({}, s); // note: shallow copy + + thresholded.datapoints = { points: [], pointsize: ps, format: datapoints.format }; + thresholded.label = null; + thresholded.color = color; + thresholded.threshold = null; + thresholded.originSeries = s; + thresholded.data = []; + + var origpoints = datapoints.points, + addCrossingPoints = s.lines.show; + + var threspoints = []; + var newpoints = []; + var m; + + for (i = 0; i < origpoints.length; i += ps) { + x = origpoints[i]; + y = origpoints[i + 1]; + + prevp = p; + if (y < below) p = threspoints; + else p = newpoints; + + if (addCrossingPoints && prevp !== p && + x !== null && i > 0 && + origpoints[i - ps] != null) { + var interx = x + (below - y) * (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]); + prevp.push(interx); + prevp.push(below); + for (m = 2; m < ps; ++m) { + prevp.push(origpoints[i + m]); + } + + p.push(null); // start new segment + p.push(null); + for (m = 2; m < ps; ++m) { + p.push(origpoints[i + m]); + } + + p.push(interx); + p.push(below); + for (m = 2; m < ps; ++m) { + p.push(origpoints[i + m]); + } + } + + p.push(x); + p.push(y); + for (m = 2; m < ps; ++m) { + p.push(origpoints[i + m]); + } + } + + datapoints.points = newpoints; + thresholded.datapoints.points = threspoints; + + if (thresholded.datapoints.points.length > 0) { + var origIndex = $.inArray(s, plot.getData()); + // Insert newly-generated series right after original one (to prevent it from becoming top-most) + plot.getData().splice(origIndex + 1, 0, thresholded); + } + + // FIXME: there are probably some edge cases left in bars + } + + function processThresholds(plot, s, datapoints) { + if (!s.threshold) return; + if (s.threshold instanceof Array) { + s.threshold.sort(function(a, b) { + return a.below - b.below; + }); + + $(s.threshold).each(function(i, th) { + thresholdData(plot, s, datapoints, th.below, th.color); + }); + } else { + thresholdData(plot, s, datapoints, s.threshold.below, s.threshold.color); + } + } + + plot.hooks.processDatapoints.push(processThresholds); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'threshold', + version: '1.2' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.time.js b/terahz/templates/lib/flot/jquery.flot.time.js new file mode 100644 index 0000000..012d658 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.time.js @@ -0,0 +1,585 @@ +/* Pretty handling of time axes. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +Set axis.mode to "time" to enable. See the section "Time series data" in +API.txt for details. +*/ + +(function($) { + 'use strict'; + + var options = { + xaxis: { + timezone: null, // "browser" for local to the client or timezone for timezone-js + timeformat: null, // format string to use + twelveHourClock: false, // 12 or 24 time in time mode + monthNames: null, // list of names of months + timeBase: 'seconds' // are the values in given in mircoseconds, milliseconds or seconds + }, + yaxis: { + timeBase: 'seconds' + } + }; + + var floorInBase = $.plot.saturated.floorInBase; + + // Method to provide microsecond support to Date like classes. + var CreateMicroSecondDate = function(dateType, microEpoch) { + var newDate = new dateType(microEpoch); + + var oldSetTime = newDate.setTime.bind(newDate); + newDate.update = function(microEpoch) { + oldSetTime(microEpoch); + + // Round epoch to 3 decimal accuracy + microEpoch = Math.round(microEpoch*1000)/1000; + + // Microseconds are stored as integers + this.microseconds = 1000 * (microEpoch - Math.floor(microEpoch)); + }; + + var oldGetTime = newDate.getTime.bind(newDate); + newDate.getTime = function () { + var microEpoch = oldGetTime() + this.microseconds / 1000; + return microEpoch; + }; + + newDate.setTime = function (microEpoch) { + this.update(microEpoch); + }; + + newDate.getMicroseconds = function() { + return this.microseconds; + }; + + newDate.setMicroseconds = function(microseconds) { + var epochWithoutMicroseconds = oldGetTime(); + var newEpoch = epochWithoutMicroseconds + microseconds/1000; + this.update(newEpoch); + }; + + newDate.setUTCMicroseconds = function(microseconds) { this.setMicroseconds(microseconds); } + + newDate.getUTCMicroseconds = function() { return this.getMicroseconds(); } + + newDate.microseconds = null; + newDate.microEpoch = null; + newDate.update(microEpoch); + return newDate; + } + + // Returns a string with the date d formatted according to fmt. + // A subset of the Open Group's strftime format is supported. + + function formatDate(d, fmt, monthNames, dayNames) { + if (typeof d.strftime === "function") { + return d.strftime(fmt); + } + + var leftPad = function(n, pad) { + n = "" + n; + pad = "" + (pad == null ? "0" : pad); + return n.length == 1 ? pad + n : n; + }; + + var formatSubSeconds = function(milliseconds, microseconds, numberDecimalPlaces) { + var totalMicroseconds = milliseconds * 1000 + microseconds; + var formattedString; + if (numberDecimalPlaces < 6 && numberDecimalPlaces > 0) { + var magnitude = parseFloat('1e' + (numberDecimalPlaces - 6)); + totalMicroseconds = Math.round(Math.round(totalMicroseconds * magnitude) / magnitude); + formattedString = ('00000' + totalMicroseconds).slice(-6,-(6 - numberDecimalPlaces)); + } else { + totalMicroseconds = Math.round(totalMicroseconds) + formattedString = ('00000' + totalMicroseconds).slice(-6); + } + return formattedString; + }; + + var r = []; + var escape = false; + var hours = d.getHours(); + var isAM = hours < 12; + + if (!monthNames) { + monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + } + + if (!dayNames) { + dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + } + + var hours12; + if (hours > 12) { + hours12 = hours - 12; + } else if (hours == 0) { + hours12 = 12; + } else { + hours12 = hours; + } + + var decimals = -1; + for (var i = 0; i < fmt.length; ++i) { + var c = fmt.charAt(i); + + if (!isNaN(Number(c)) && Number(c) > 0) { + decimals = Number(c); + } else if (escape) { + switch (c) { + case 'a': c = "" + dayNames[d.getDay()]; break; + case 'b': c = "" + monthNames[d.getMonth()]; break; + case 'd': c = leftPad(d.getDate()); break; + case 'e': c = leftPad(d.getDate(), " "); break; + case 'h': // For back-compat with 0.7; remove in 1.0 + case 'H': c = leftPad(hours); break; + case 'I': c = leftPad(hours12); break; + case 'l': c = leftPad(hours12, " "); break; + case 'm': c = leftPad(d.getMonth() + 1); break; + case 'M': c = leftPad(d.getMinutes()); break; + // quarters not in Open Group's strftime specification + case 'q': + c = "" + (Math.floor(d.getMonth() / 3) + 1); break; + case 'S': c = leftPad(d.getSeconds()); break; + case 's': c = "" + formatSubSeconds(d.getMilliseconds(), d.getMicroseconds(), decimals); break; + case 'y': c = leftPad(d.getFullYear() % 100); break; + case 'Y': c = "" + d.getFullYear(); break; + case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break; + case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break; + case 'w': c = "" + d.getDay(); break; + } + r.push(c); + escape = false; + } else { + if (c == "%") { + escape = true; + } else { + r.push(c); + } + } + } + + return r.join(""); + } + + // To have a consistent view of time-based data independent of which time + // zone the client happens to be in we need a date-like object independent + // of time zones. This is done through a wrapper that only calls the UTC + // versions of the accessor methods. + + function makeUtcWrapper(d) { + function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) { + sourceObj[sourceMethod] = function() { + return targetObj[targetMethod].apply(targetObj, arguments); + }; + } + + var utc = { + date: d + }; + + // support strftime, if found + if (d.strftime !== undefined) { + addProxyMethod(utc, "strftime", d, "strftime"); + } + + addProxyMethod(utc, "getTime", d, "getTime"); + addProxyMethod(utc, "setTime", d, "setTime"); + + var props = ["Date", "Day", "FullYear", "Hours", "Minutes", "Month", "Seconds", "Milliseconds", "Microseconds"]; + + for (var p = 0; p < props.length; p++) { + addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]); + addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]); + } + + return utc; + } + + // select time zone strategy. This returns a date-like object tied to the + // desired timezone + function dateGenerator(ts, opts) { + var maxDateValue = 8640000000000000; + + if (opts && opts.timeBase === 'seconds') { + ts *= 1000; + } else if (opts.timeBase === 'microseconds') { + ts /= 1000; + } + + if (ts > maxDateValue) { + ts = maxDateValue; + } else if (ts < -maxDateValue) { + ts = -maxDateValue; + } + + if (opts.timezone === "browser") { + return CreateMicroSecondDate(Date, ts); + } else if (!opts.timezone || opts.timezone === "utc") { + return makeUtcWrapper(CreateMicroSecondDate(Date, ts)); + } else if (typeof timezoneJS !== "undefined" && typeof timezoneJS.Date !== "undefined") { + var d = CreateMicroSecondDate(timezoneJS.Date, ts); + // timezone-js is fickle, so be sure to set the time zone before + // setting the time. + d.setTimezone(opts.timezone); + d.setTime(ts); + return d; + } else { + return makeUtcWrapper(CreateMicroSecondDate(Date, ts)); + } + } + + // map of app. size of time units in seconds + var timeUnitSizeSeconds = { + "microsecond": 0.000001, + "millisecond": 0.001, + "second": 1, + "minute": 60, + "hour": 60 * 60, + "day": 24 * 60 * 60, + "month": 30 * 24 * 60 * 60, + "quarter": 3 * 30 * 24 * 60 * 60, + "year": 365.2425 * 24 * 60 * 60 + }; + + // map of app. size of time units in milliseconds + var timeUnitSizeMilliseconds = { + "microsecond": 0.001, + "millisecond": 1, + "second": 1000, + "minute": 60 * 1000, + "hour": 60 * 60 * 1000, + "day": 24 * 60 * 60 * 1000, + "month": 30 * 24 * 60 * 60 * 1000, + "quarter": 3 * 30 * 24 * 60 * 60 * 1000, + "year": 365.2425 * 24 * 60 * 60 * 1000 + }; + + // map of app. size of time units in microseconds + var timeUnitSizeMicroseconds = { + "microsecond": 1, + "millisecond": 1000, + "second": 1000000, + "minute": 60 * 1000000, + "hour": 60 * 60 * 1000000, + "day": 24 * 60 * 60 * 1000000, + "month": 30 * 24 * 60 * 60 * 1000000, + "quarter": 3 * 30 * 24 * 60 * 60 * 1000000, + "year": 365.2425 * 24 * 60 * 60 * 1000000 + }; + + // the allowed tick sizes, after 1 year we use + // an integer algorithm + + var baseSpec = [ + [1, "microsecond"], [2, "microsecond"], [5, "microsecond"], [10, "microsecond"], + [25, "microsecond"], [50, "microsecond"], [100, "microsecond"], [250, "microsecond"], [500, "microsecond"], + [1, "millisecond"], [2, "millisecond"], [5, "millisecond"], [10, "millisecond"], + [25, "millisecond"], [50, "millisecond"], [100, "millisecond"], [250, "millisecond"], [500, "millisecond"], + [1, "second"], [2, "second"], [5, "second"], [10, "second"], + [30, "second"], + [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], + [30, "minute"], + [1, "hour"], [2, "hour"], [4, "hour"], + [8, "hour"], [12, "hour"], + [1, "day"], [2, "day"], [3, "day"], + [0.25, "month"], [0.5, "month"], [1, "month"], + [2, "month"] + ]; + + // we don't know which variant(s) we'll need yet, but generating both is + // cheap + + var specMonths = baseSpec.concat([[3, "month"], [6, "month"], + [1, "year"]]); + var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"], + [1, "year"]]); + + + function dateTickGenerator(axis) { + var opts = axis.options, + ticks = [], + d = dateGenerator(axis.min, opts), + minSize = 0; + + // make quarter use a possibility if quarters are + // mentioned in either of these options + var spec = (opts.tickSize && opts.tickSize[1] === + "quarter") || + (opts.minTickSize && opts.minTickSize[1] === + "quarter") ? specQuarters : specMonths; + + var timeUnitSize; + if (opts.timeBase === 'seconds') { + timeUnitSize = timeUnitSizeSeconds; + } else if (opts.timeBase === 'microseconds') { + timeUnitSize = timeUnitSizeMicroseconds; + } else { + timeUnitSize = timeUnitSizeMilliseconds; + } + + if (opts.minTickSize !== null && opts.minTickSize !== undefined) { + if (typeof opts.tickSize === "number") { + minSize = opts.tickSize; + } else { + minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]]; + } + } + + for (var i = 0; i < spec.length - 1; ++i) { + if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]] + + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 && + spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) { + break; + } + } + + var size = spec[i][0]; + var unit = spec[i][1]; + // special-case the possibility of several years + if (unit === "year") { + // if given a minTickSize in years, just use it, + // ensuring that it's an integer + + if (opts.minTickSize !== null && opts.minTickSize !== undefined && opts.minTickSize[1] === "year") { + size = Math.floor(opts.minTickSize[0]); + } else { + var magn = parseFloat('1e' + Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10)); + var norm = (axis.delta / timeUnitSize.year) / magn; + + if (norm < 1.5) { + size = 1; + } else if (norm < 3) { + size = 2; + } else if (norm < 7.5) { + size = 5; + } else { + size = 10; + } + + size *= magn; + } + + // minimum size for years is 1 + + if (size < 1) { + size = 1; + } + } + + axis.tickSize = opts.tickSize || [size, unit]; + var tickSize = axis.tickSize[0]; + unit = axis.tickSize[1]; + + var step = tickSize * timeUnitSize[unit]; + + if (unit === "microsecond") { + d.setMicroseconds(floorInBase(d.getMicroseconds(), tickSize)); + } else if (unit === "millisecond") { + d.setMilliseconds(floorInBase(d.getMilliseconds(), tickSize)); + } else if (unit === "second") { + d.setSeconds(floorInBase(d.getSeconds(), tickSize)); + } else if (unit === "minute") { + d.setMinutes(floorInBase(d.getMinutes(), tickSize)); + } else if (unit === "hour") { + d.setHours(floorInBase(d.getHours(), tickSize)); + } else if (unit === "month") { + d.setMonth(floorInBase(d.getMonth(), tickSize)); + } else if (unit === "quarter") { + d.setMonth(3 * floorInBase(d.getMonth() / 3, + tickSize)); + } else if (unit === "year") { + d.setFullYear(floorInBase(d.getFullYear(), tickSize)); + } + + // reset smaller components + + if (step >= timeUnitSize.millisecond) { + if (step >= timeUnitSize.second) { + d.setMicroseconds(0); + } else { + d.setMicroseconds(d.getMilliseconds()*1000); + } + } + if (step >= timeUnitSize.minute) { + d.setSeconds(0); + } + if (step >= timeUnitSize.hour) { + d.setMinutes(0); + } + if (step >= timeUnitSize.day) { + d.setHours(0); + } + if (step >= timeUnitSize.day * 4) { + d.setDate(1); + } + if (step >= timeUnitSize.month * 2) { + d.setMonth(floorInBase(d.getMonth(), 3)); + } + if (step >= timeUnitSize.quarter * 2) { + d.setMonth(floorInBase(d.getMonth(), 6)); + } + if (step >= timeUnitSize.year) { + d.setMonth(0); + } + + var carry = 0; + var v = Number.NaN; + var v1000; + var prev; + do { + prev = v; + v1000 = d.getTime(); + if (opts && opts.timeBase === 'seconds') { + v = v1000 / 1000; + } else if (opts && opts.timeBase === 'microseconds') { + v = v1000 * 1000; + } else { + v = v1000; + } + + ticks.push(v); + + if (unit === "month" || unit === "quarter") { + if (tickSize < 1) { + // a bit complicated - we'll divide the + // month/quarter up but we need to take + // care of fractions so we don't end up in + // the middle of a day + d.setDate(1); + var start = d.getTime(); + d.setMonth(d.getMonth() + + (unit === "quarter" ? 3 : 1)); + var end = d.getTime(); + d.setTime((v + carry * timeUnitSize.hour + (end - start) * tickSize)); + carry = d.getHours(); + d.setHours(0); + } else { + d.setMonth(d.getMonth() + + tickSize * (unit === "quarter" ? 3 : 1)); + } + } else if (unit === "year") { + d.setFullYear(d.getFullYear() + tickSize); + } else { + if (opts.timeBase === 'seconds') { + d.setTime((v + step) * 1000); + } else if (opts.timeBase === 'microseconds') { + d.setTime((v + step) / 1000); + } else { + d.setTime(v + step); + } + } + } while (v < axis.max && v !== prev); + + return ticks; + }; + + function init(plot) { + plot.hooks.processOptions.push(function (plot) { + $.each(plot.getAxes(), function(axisName, axis) { + var opts = axis.options; + if (opts.mode === "time") { + axis.tickGenerator = dateTickGenerator; + + axis.tickFormatter = function (v, axis) { + var d = dateGenerator(v, axis.options); + + // first check global format + if (opts.timeformat != null) { + return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames); + } + + // possibly use quarters if quarters are mentioned in + // any of these places + var useQuarters = (axis.options.tickSize && + axis.options.tickSize[1] == "quarter") || + (axis.options.minTickSize && + axis.options.minTickSize[1] == "quarter"); + + var timeUnitSize; + if (opts.timeBase === 'seconds') { + timeUnitSize = timeUnitSizeSeconds; + } else if (opts.timeBase === 'microseconds') { + timeUnitSize = timeUnitSizeMicroseconds; + } else { + timeUnitSize = timeUnitSizeMilliseconds; + } + + var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]]; + var span = axis.max - axis.min; + var suffix = (opts.twelveHourClock) ? " %p" : ""; + var hourCode = (opts.twelveHourClock) ? "%I" : "%H"; + var factor; + var fmt; + + if (opts.timeBase === 'seconds') { + factor = 1; + } else if (opts.timeBase === 'microseconds') { + factor = 1000000 + } else { + factor = 1000; + } + + if (t < timeUnitSize.second) { + var decimals = -Math.floor(Math.log10(t/factor)) + + // the two-and-halves require an additional decimal + if (String(t).indexOf('25') > -1) { + decimals++; + } + + fmt = "%S.%" + decimals + "s"; + } else + if (t < timeUnitSize.minute) { + fmt = hourCode + ":%M:%S" + suffix; + } else if (t < timeUnitSize.day) { + if (span < 2 * timeUnitSize.day) { + fmt = hourCode + ":%M" + suffix; + } else { + fmt = "%b %d " + hourCode + ":%M" + suffix; + } + } else if (t < timeUnitSize.month) { + fmt = "%b %d"; + } else if ((useQuarters && t < timeUnitSize.quarter) || + (!useQuarters && t < timeUnitSize.year)) { + if (span < timeUnitSize.year) { + fmt = "%b"; + } else { + fmt = "%b %Y"; + } + } else if (useQuarters && t < timeUnitSize.year) { + if (span < timeUnitSize.year) { + fmt = "Q%q"; + } else { + fmt = "Q%q %Y"; + } + } else { + fmt = "%Y"; + } + + var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames); + + return rt; + }; + } + }); + }); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'time', + version: '1.0' + }); + + // Time-axis support used to be in Flot core, which exposed the + // formatDate function on the plot object. Various plugins depend + // on the function, so we need to re-expose it here. + + $.plot.formatDate = formatDate; + $.plot.dateGenerator = dateGenerator; + $.plot.dateTickGenerator = dateTickGenerator; + $.plot.makeUtcWrapper = makeUtcWrapper; +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.touch.js b/terahz/templates/lib/flot/jquery.flot.touch.js new file mode 100644 index 0000000..492cf68 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.touch.js @@ -0,0 +1,320 @@ + +/* global jQuery */ + +(function($) { + 'use strict'; + + var options = { + propagateSupportedGesture: false + }; + + function init(plot) { + plot.hooks.processOptions.push(initTouchNavigation); + } + + function initTouchNavigation(plot, options) { + var gestureState = { + twoTouches: false, + currentTapStart: { x: 0, y: 0 }, + currentTapEnd: { x: 0, y: 0 }, + prevTap: { x: 0, y: 0 }, + currentTap: { x: 0, y: 0 }, + interceptedLongTap: false, + isUnsupportedGesture: false, + prevTapTime: null, + tapStartTime: null, + longTapTriggerId: null + }, + maxDistanceBetweenTaps = 20, + maxIntervalBetweenTaps = 500, + maxLongTapDistance = 20, + minLongTapDuration = 1500, + pressedTapDuration = 125, + mainEventHolder; + + function interpretGestures(e) { + var o = plot.getOptions(); + + if (!o.pan.active && !o.zoom.active) { + return; + } + + updateOnMultipleTouches(e); + mainEventHolder.dispatchEvent(new CustomEvent('touchevent', { detail: e })); + + if (isPinchEvent(e)) { + executeAction(e, 'pinch'); + } else { + executeAction(e, 'pan'); + if (!wasPinchEvent(e)) { + if (isDoubleTap(e)) { + executeAction(e, 'doubleTap'); + } + executeAction(e, 'tap'); + executeAction(e, 'longTap'); + } + } + } + + function executeAction(e, gesture) { + switch (gesture) { + case 'pan': + pan[e.type](e); + break; + case 'pinch': + pinch[e.type](e); + break; + case 'doubleTap': + doubleTap.onDoubleTap(e); + break; + case 'longTap': + longTap[e.type](e); + break; + case 'tap': + tap[e.type](e); + break; + } + } + + function bindEvents(plot, eventHolder) { + mainEventHolder = eventHolder[0]; + eventHolder[0].addEventListener('touchstart', interpretGestures, false); + eventHolder[0].addEventListener('touchmove', interpretGestures, false); + eventHolder[0].addEventListener('touchend', interpretGestures, false); + } + + function shutdown(plot, eventHolder) { + eventHolder[0].removeEventListener('touchstart', interpretGestures); + eventHolder[0].removeEventListener('touchmove', interpretGestures); + eventHolder[0].removeEventListener('touchend', interpretGestures); + if (gestureState.longTapTriggerId) { + clearTimeout(gestureState.longTapTriggerId); + gestureState.longTapTriggerId = null; + } + } + + var pan = { + touchstart: function(e) { + updatePrevForDoubleTap(); + updateCurrentForDoubleTap(e); + updateStateForLongTapStart(e); + + mainEventHolder.dispatchEvent(new CustomEvent('panstart', { detail: e })); + }, + + touchmove: function(e) { + preventEventBehaviors(e); + + updateCurrentForDoubleTap(e); + updateStateForLongTapEnd(e); + + if (!gestureState.isUnsupportedGesture) { + mainEventHolder.dispatchEvent(new CustomEvent('pandrag', { detail: e })); + } + }, + + touchend: function(e) { + preventEventBehaviors(e); + + if (wasPinchEvent(e)) { + mainEventHolder.dispatchEvent(new CustomEvent('pinchend', { detail: e })); + mainEventHolder.dispatchEvent(new CustomEvent('panstart', { detail: e })); + } else if (noTouchActive(e)) { + mainEventHolder.dispatchEvent(new CustomEvent('panend', { detail: e })); + } + } + }; + + var pinch = { + touchstart: function(e) { + mainEventHolder.dispatchEvent(new CustomEvent('pinchstart', { detail: e })); + }, + + touchmove: function(e) { + preventEventBehaviors(e); + gestureState.twoTouches = isPinchEvent(e); + if (!gestureState.isUnsupportedGesture) { + mainEventHolder.dispatchEvent(new CustomEvent('pinchdrag', { detail: e })); + } + }, + + touchend: function(e) { + preventEventBehaviors(e); + } + }; + + var doubleTap = { + onDoubleTap: function(e) { + preventEventBehaviors(e); + mainEventHolder.dispatchEvent(new CustomEvent('doubletap', { detail: e })); + } + }; + + var longTap = { + touchstart: function(e) { + longTap.waitForLongTap(e); + }, + + touchmove: function(e) { + }, + + touchend: function(e) { + if (gestureState.longTapTriggerId) { + clearTimeout(gestureState.longTapTriggerId); + gestureState.longTapTriggerId = null; + } + }, + + isLongTap: function(e) { + var currentTime = new Date().getTime(), + tapDuration = currentTime - gestureState.tapStartTime; + if (tapDuration >= minLongTapDuration && !gestureState.interceptedLongTap) { + if (distance(gestureState.currentTapStart.x, gestureState.currentTapStart.y, gestureState.currentTapEnd.x, gestureState.currentTapEnd.y) < maxLongTapDistance) { + gestureState.interceptedLongTap = true; + return true; + } + } + return false; + }, + + waitForLongTap: function(e) { + var longTapTrigger = function() { + if (longTap.isLongTap(e)) { + mainEventHolder.dispatchEvent(new CustomEvent('longtap', { detail: e })); + } + gestureState.longTapTriggerId = null; + }; + if (!gestureState.longTapTriggerId) { + gestureState.longTapTriggerId = setTimeout(longTapTrigger, minLongTapDuration); + } + } + }; + + var tap = { + touchstart: function(e) { + gestureState.tapStartTime = new Date().getTime(); + }, + + touchmove: function(e) { + }, + + touchend: function(e) { + if (tap.isTap(e)) { + mainEventHolder.dispatchEvent(new CustomEvent('tap', { detail: e })); + preventEventBehaviors(e); + } + }, + + isTap: function(e) { + var currentTime = new Date().getTime(), + tapDuration = currentTime - gestureState.tapStartTime; + if (tapDuration <= pressedTapDuration) { + if (distance(gestureState.currentTapStart.x, gestureState.currentTapStart.y, gestureState.currentTapEnd.x, gestureState.currentTapEnd.y) < maxLongTapDistance) { + return true; + } + } + return false; + } + }; + + if (options.pan.enableTouch === true || options.zoom.enableTouch) { + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + }; + + function updatePrevForDoubleTap() { + gestureState.prevTap = { + x: gestureState.currentTap.x, + y: gestureState.currentTap.y + }; + }; + + function updateCurrentForDoubleTap(e) { + gestureState.currentTap = { + x: e.touches[0].pageX, + y: e.touches[0].pageY + }; + } + + function updateStateForLongTapStart(e) { + gestureState.tapStartTime = new Date().getTime(); + gestureState.interceptedLongTap = false; + gestureState.currentTapStart = { + x: e.touches[0].pageX, + y: e.touches[0].pageY + }; + gestureState.currentTapEnd = { + x: e.touches[0].pageX, + y: e.touches[0].pageY + }; + }; + + function updateStateForLongTapEnd(e) { + gestureState.currentTapEnd = { + x: e.touches[0].pageX, + y: e.touches[0].pageY + }; + }; + + function isDoubleTap(e) { + var currentTime = new Date().getTime(), + intervalBetweenTaps = currentTime - gestureState.prevTapTime; + + if (intervalBetweenTaps >= 0 && intervalBetweenTaps < maxIntervalBetweenTaps) { + if (distance(gestureState.prevTap.x, gestureState.prevTap.y, gestureState.currentTap.x, gestureState.currentTap.y) < maxDistanceBetweenTaps) { + e.firstTouch = gestureState.prevTap; + e.secondTouch = gestureState.currentTap; + return true; + } + } + gestureState.prevTapTime = currentTime; + return false; + } + + function preventEventBehaviors(e) { + if (!gestureState.isUnsupportedGesture) { + e.preventDefault(); + if (!plot.getOptions().propagateSupportedGesture) { + e.stopPropagation(); + } + } + } + + function distance(x1, y1, x2, y2) { + return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + } + + function noTouchActive(e) { + return (e.touches && e.touches.length === 0); + } + + function wasPinchEvent(e) { + return (gestureState.twoTouches && e.touches.length === 1); + } + + function updateOnMultipleTouches(e) { + if (e.touches.length >= 3) { + gestureState.isUnsupportedGesture = true; + } else { + gestureState.isUnsupportedGesture = false; + } + } + + function isPinchEvent(e) { + if (e.touches && e.touches.length >= 2) { + if (e.touches[0].target === plot.getEventHolder() && + e.touches[1].target === plot.getEventHolder()) { + return true; + } + } + return false; + } + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'navigateTouch', + version: '0.3' + }); +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.touchNavigate.js b/terahz/templates/lib/flot/jquery.flot.touchNavigate.js new file mode 100644 index 0000000..1cc5ea7 --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.touchNavigate.js @@ -0,0 +1,360 @@ +/* global jQuery */ + +(function($) { + 'use strict'; + + var options = { + zoom: { + enableTouch: false + }, + pan: { + enableTouch: false, + touchMode: 'manual' + }, + recenter: { + enableTouch: true + } + }; + + var ZOOM_DISTANCE_MARGIN = $.plot.uiConstants.ZOOM_DISTANCE_MARGIN; + + function init(plot) { + plot.hooks.processOptions.push(initTouchNavigation); + } + + function initTouchNavigation(plot, options) { + var gestureState = { + zoomEnable: false, + prevDistance: null, + prevTapTime: 0, + prevPanPosition: { x: 0, y: 0 }, + prevTapPosition: { x: 0, y: 0 } + }, + navigationState = { + prevTouchedAxis: 'none', + currentTouchedAxis: 'none', + touchedAxis: null, + navigationConstraint: 'unconstrained', + initialState: null, + }, + useManualPan = options.pan.interactive && options.pan.touchMode === 'manual', + smartPanLock = options.pan.touchMode === 'smartLock', + useSmartPan = options.pan.interactive && (smartPanLock || options.pan.touchMode === 'smart'), + pan, pinch, doubleTap; + + function bindEvents(plot, eventHolder) { + var o = plot.getOptions(); + + if (o.zoom.interactive && o.zoom.enableTouch) { + eventHolder[0].addEventListener('pinchstart', pinch.start, false); + eventHolder[0].addEventListener('pinchdrag', pinch.drag, false); + eventHolder[0].addEventListener('pinchend', pinch.end, false); + } + + if (o.pan.interactive && o.pan.enableTouch) { + eventHolder[0].addEventListener('panstart', pan.start, false); + eventHolder[0].addEventListener('pandrag', pan.drag, false); + eventHolder[0].addEventListener('panend', pan.end, false); + } + + if ((o.recenter.interactive && o.recenter.enableTouch)) { + eventHolder[0].addEventListener('doubletap', doubleTap.recenterPlot, false); + } + } + + function shutdown(plot, eventHolder) { + eventHolder[0].removeEventListener('panstart', pan.start); + eventHolder[0].removeEventListener('pandrag', pan.drag); + eventHolder[0].removeEventListener('panend', pan.end); + eventHolder[0].removeEventListener('pinchstart', pinch.start); + eventHolder[0].removeEventListener('pinchdrag', pinch.drag); + eventHolder[0].removeEventListener('pinchend', pinch.end); + eventHolder[0].removeEventListener('doubletap', doubleTap.recenterPlot); + } + + pan = { + start: function(e) { + presetNavigationState(e, 'pan', gestureState); + updateData(e, 'pan', gestureState, navigationState); + + if (useSmartPan) { + var point = getPoint(e, 'pan'); + navigationState.initialState = plot.navigationState(point.x, point.y); + } + }, + + drag: function(e) { + presetNavigationState(e, 'pan', gestureState); + + if (useSmartPan) { + var point = getPoint(e, 'pan'); + plot.smartPan({ + x: navigationState.initialState.startPageX - point.x, + y: navigationState.initialState.startPageY - point.y + }, navigationState.initialState, navigationState.touchedAxis, false, smartPanLock); + } else if (useManualPan) { + plot.pan({ + left: -delta(e, 'pan', gestureState).x, + top: -delta(e, 'pan', gestureState).y, + axes: navigationState.touchedAxis + }); + updatePrevPanPosition(e, 'pan', gestureState, navigationState); + } + }, + + end: function(e) { + presetNavigationState(e, 'pan', gestureState); + + if (useSmartPan) { + plot.smartPan.end(); + } + + if (wasPinchEvent(e, gestureState)) { + updateprevPanPosition(e, 'pan', gestureState, navigationState); + } + } + }; + + var pinchDragTimeout; + pinch = { + start: function(e) { + if (pinchDragTimeout) { + clearTimeout(pinchDragTimeout); + pinchDragTimeout = null; + } + presetNavigationState(e, 'pinch', gestureState); + setPrevDistance(e, gestureState); + updateData(e, 'pinch', gestureState, navigationState); + }, + + drag: function(e) { + if (pinchDragTimeout) { + return; + } + pinchDragTimeout = setTimeout(function() { + presetNavigationState(e, 'pinch', gestureState); + plot.pan({ + left: -delta(e, 'pinch', gestureState).x, + top: -delta(e, 'pinch', gestureState).y, + axes: navigationState.touchedAxis + }); + updatePrevPanPosition(e, 'pinch', gestureState, navigationState); + + var dist = pinchDistance(e); + + if (gestureState.zoomEnable || Math.abs(dist - gestureState.prevDistance) > ZOOM_DISTANCE_MARGIN) { + zoomPlot(plot, e, gestureState, navigationState); + + //activate zoom mode + gestureState.zoomEnable = true; + } + pinchDragTimeout = null; + }, 1000 / 60); + }, + + end: function(e) { + if (pinchDragTimeout) { + clearTimeout(pinchDragTimeout); + pinchDragTimeout = null; + } + presetNavigationState(e, 'pinch', gestureState); + gestureState.prevDistance = null; + } + }; + + doubleTap = { + recenterPlot: function(e) { + if (e && e.detail && e.detail.type === 'touchstart') { + // only do not recenter for touch start; + recenterPlotOnDoubleTap(plot, e, gestureState, navigationState); + } + } + }; + + if (options.pan.enableTouch === true || options.zoom.enableTouch === true) { + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + } + + function presetNavigationState(e, gesture, gestureState) { + navigationState.touchedAxis = getAxis(plot, e, gesture, navigationState); + if (noAxisTouched(navigationState)) { + navigationState.navigationConstraint = 'unconstrained'; + } else { + navigationState.navigationConstraint = 'axisConstrained'; + } + } + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'navigateTouch', + version: '0.3' + }); + + function recenterPlotOnDoubleTap(plot, e, gestureState, navigationState) { + checkAxesForDoubleTap(plot, e, navigationState); + if ((navigationState.currentTouchedAxis === 'x' && navigationState.prevTouchedAxis === 'x') || + (navigationState.currentTouchedAxis === 'y' && navigationState.prevTouchedAxis === 'y') || + (navigationState.currentTouchedAxis === 'none' && navigationState.prevTouchedAxis === 'none')) { + var event; + + plot.recenter({ axes: navigationState.touchedAxis }); + + if (navigationState.touchedAxis) { + event = new $.Event('re-center', { detail: { axisTouched: navigationState.touchedAxis } }); + } else { + event = new $.Event('re-center', { detail: e }); + } + plot.getPlaceholder().trigger(event); + } + } + + function checkAxesForDoubleTap(plot, e, navigationState) { + var axis = plot.getTouchedAxis(e.detail.firstTouch.x, e.detail.firstTouch.y); + if (axis[0] !== undefined) { + navigationState.prevTouchedAxis = axis[0].direction; + } + + axis = plot.getTouchedAxis(e.detail.secondTouch.x, e.detail.secondTouch.y); + if (axis[0] !== undefined) { + navigationState.touchedAxis = axis; + navigationState.currentTouchedAxis = axis[0].direction; + } + + if (noAxisTouched(navigationState)) { + navigationState.touchedAxis = null; + navigationState.prevTouchedAxis = 'none'; + navigationState.currentTouchedAxis = 'none'; + } + } + + function zoomPlot(plot, e, gestureState, navigationState) { + var offset = plot.offset(), + center = { + left: 0, + top: 0 + }, + zoomAmount = pinchDistance(e) / gestureState.prevDistance, + dist = pinchDistance(e); + + center.left = getPoint(e, 'pinch').x - offset.left; + center.top = getPoint(e, 'pinch').y - offset.top; + + // send the computed touched axis to the zoom function so that it only zooms on that one + plot.zoom({ + center: center, + amount: zoomAmount, + axes: navigationState.touchedAxis + }); + gestureState.prevDistance = dist; + } + + function wasPinchEvent(e, gestureState) { + return (gestureState.zoomEnable && e.detail.touches.length === 1); + } + + function getAxis(plot, e, gesture, navigationState) { + if (e.type === 'pinchstart') { + var axisTouch1 = plot.getTouchedAxis(e.detail.touches[0].pageX, e.detail.touches[0].pageY); + var axisTouch2 = plot.getTouchedAxis(e.detail.touches[1].pageX, e.detail.touches[1].pageY); + + if (axisTouch1.length === axisTouch2.length && axisTouch1.toString() === axisTouch2.toString()) { + return axisTouch1; + } + } else if (e.type === 'panstart') { + return plot.getTouchedAxis(e.detail.touches[0].pageX, e.detail.touches[0].pageY); + } else if (e.type === 'pinchend') { + //update axis since instead on pinch, a pan event is made + return plot.getTouchedAxis(e.detail.touches[0].pageX, e.detail.touches[0].pageY); + } else { + return navigationState.touchedAxis; + } + } + + function noAxisTouched(navigationState) { + return (!navigationState.touchedAxis || navigationState.touchedAxis.length === 0); + } + + function setPrevDistance(e, gestureState) { + gestureState.prevDistance = pinchDistance(e); + } + + function updateData(e, gesture, gestureState, navigationState) { + var axisDir, + point = getPoint(e, gesture); + + switch (navigationState.navigationConstraint) { + case 'unconstrained': + navigationState.touchedAxis = null; + gestureState.prevTapPosition = { + x: gestureState.prevPanPosition.x, + y: gestureState.prevPanPosition.y + }; + gestureState.prevPanPosition = { + x: point.x, + y: point.y + }; + break; + case 'axisConstrained': + axisDir = navigationState.touchedAxis[0].direction; + navigationState.currentTouchedAxis = axisDir; + gestureState.prevTapPosition[axisDir] = gestureState.prevPanPosition[axisDir]; + gestureState.prevPanPosition[axisDir] = point[axisDir]; + break; + default: + break; + } + } + + function distance(x1, y1, x2, y2) { + return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); + } + + function pinchDistance(e) { + var t1 = e.detail.touches[0], + t2 = e.detail.touches[1]; + return distance(t1.pageX, t1.pageY, t2.pageX, t2.pageY); + } + + function updatePrevPanPosition(e, gesture, gestureState, navigationState) { + var point = getPoint(e, gesture); + + switch (navigationState.navigationConstraint) { + case 'unconstrained': + gestureState.prevPanPosition.x = point.x; + gestureState.prevPanPosition.y = point.y; + break; + case 'axisConstrained': + gestureState.prevPanPosition[navigationState.currentTouchedAxis] = + point[navigationState.currentTouchedAxis]; + break; + default: + break; + } + } + + function delta(e, gesture, gestureState) { + var point = getPoint(e, gesture); + + return { + x: point.x - gestureState.prevPanPosition.x, + y: point.y - gestureState.prevPanPosition.y + } + } + + function getPoint(e, gesture) { + if (gesture === 'pinch') { + return { + x: (e.detail.touches[0].pageX + e.detail.touches[1].pageX) / 2, + y: (e.detail.touches[0].pageY + e.detail.touches[1].pageY) / 2 + } + } else { + return { + x: e.detail.touches[0].pageX, + y: e.detail.touches[0].pageY + } + } + } +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.flot.uiConstants.js b/terahz/templates/lib/flot/jquery.flot.uiConstants.js new file mode 100644 index 0000000..627847d --- /dev/null +++ b/terahz/templates/lib/flot/jquery.flot.uiConstants.js @@ -0,0 +1,10 @@ +(function ($) { + 'use strict'; + $.plot.uiConstants = { + SNAPPING_CONSTANT: 20, + PANHINT_LENGTH_CONSTANT: 10, + MINOR_TICKS_COUNT_CONSTANT: 4, + TICK_LENGTH_CONSTANT: 10, + ZOOM_DISTANCE_MARGIN: 25 + }; +})(jQuery); diff --git a/terahz/templates/lib/flot/jquery.js b/terahz/templates/lib/flot/jquery.js new file mode 100644 index 0000000..f9f969a --- /dev/null +++ b/terahz/templates/lib/flot/jquery.js @@ -0,0 +1,9473 @@ +/*! + * jQuery JavaScript Library v1.8.3 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time) + */ +/* eslint-disable */ +(function( window, undefined ) { +var + // A central reference to the root jQuery(document) + rootjQuery, + + // The deferred used on DOM ready + readyList, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + location = window.location, + navigator = window.navigator, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // Save a reference to some core methods + core_push = Array.prototype.push, + core_slice = Array.prototype.slice, + core_indexOf = Array.prototype.indexOf, + core_toString = Object.prototype.toString, + core_hasOwn = Object.prototype.hasOwnProperty, + core_trim = String.prototype.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source, + + // Used for detecting and trimming whitespace + core_rnotwhite = /\S/, + core_rspace = /\s+/, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // The ready event handler and self cleanup method + DOMContentLoaded = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + } else if ( document.readyState === "complete" ) { + // we're here because readyState === "complete" in oldIE + // which is good enough for us to call the dom ready! + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context && context.nodeType ? context.ownerDocument || context : document ); + + // scripts is true for back-compat + selector = jQuery.parseHTML( match[1], doc, true ); + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + this.attr.call( selector, context, true ); + } + + return jQuery.merge( this, selector ); + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.8.3", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ), + "slice", core_slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ core_toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // scripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, scripts ) { + var parsed; + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + scripts = context; + context = 0; + } + context = context || document; + + // Single tag + if ( (parsed = rsingleTag.exec( data )) ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] ); + return jQuery.merge( [], + (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes ); + }, + + parseJSON: function( data ) { + if ( !data || typeof data !== "string") { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && core_rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var name, + i = 0, + length = obj.length, + isObj = length === undefined || jQuery.isFunction( obj ); + + if ( args ) { + if ( isObj ) { + for ( name in obj ) { + if ( callback.apply( obj[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( obj[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in obj ) { + if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var type, + ret = results || []; + + if ( arr != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + type = jQuery.type( arr ); + + if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) { + core_push.call( ret, arr ); + } else { + jQuery.merge( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, + ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, pass ) { + var exec, + bulk = key == null, + i = 0, + length = elems.length; + + // Sets many values + if ( key && typeof key === "object" ) { + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); + } + chainable = 1; + + // Sets one value + } else if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = pass === undefined && jQuery.isFunction( value ); + + if ( bulk ) { + // Bulk operations only iterate when executing function values + if ( exec ) { + exec = fn; + fn = function( elem, key, value ) { + return exec.call( jQuery( elem ), value ); + }; + + // Otherwise they run against the entire set + } else { + fn.call( elems, value ); + fn = null; + } + } + + if ( fn ) { + for (; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + } + + chainable = 1; + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready, 1 ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.split( core_rspace ), function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + return jQuery.inArray( fn, list ) > -1; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( list && ( !fired || stack ) ) { + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ]( jQuery.isFunction( fn ) ? + function() { + var returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + } : + newDefer[ action ] + ); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] = list.fire + deferred[ tuple[0] ] = list.fire; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + fragment, + eventName, + i, + isSupported, + clickFn, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
a"; + + // Support tests won't run in some limited or non-browser environments + all = div.getElementsByTagName("*"); + a = div.getElementsByTagName("a")[ 0 ]; + if ( !all || !a || !all.length ) { + return {}; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.5/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form (#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", + + // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode + boxModel: ( document.compatMode === "CSS1Compat" ), + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + boxSizingReliable: true, + pixelPosition: false + }; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", clickFn = function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent("onclick"); + div.detachEvent( "onclick", clickFn ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + input.setAttribute( "checked", "checked" ); + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for ( i in { + submit: true, + change: true, + focusin: true + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + // Run tests that need a body at doc ready + jQuery(function() { + var container, div, tds, marginDiv, + divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "
t
"; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + support.boxSizing = ( div.offsetWidth === 4 ); + support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); + + // NOTE: To any future maintainer, we've window.getComputedStyle + // because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = document.createElement("div"); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = "block"; + div.style.overflow = "visible"; + div.innerHTML = "
"; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + container.style.zoom = 1; + } + + // Null elements to avoid leaks in IE + body.removeChild( container ); + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + fragment.removeChild( div ); + all = a = select = opt = input = fragment = div = null; + + return support; +})(); +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + deletedIds: [], + + // Remove at next major release (1.9/2.0) + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, part, attr, name, l, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attr = elem.attributes; + for ( l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( !name.indexOf( "data-" ) ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split( ".", 2 ); + parts[1] = parts[1] ? "." + parts[1] : ""; + part = parts[1] + "!"; + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + data = this.triggerHandler( "getData" + part, [ parts[0] ] ); + + // Try to fetch any internally stored data first + if ( data === undefined && elem ) { + data = jQuery.data( elem, key ); + data = dataAttr( elem, key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } + + parts[1] = value; + this.each(function() { + var self = jQuery( this ); + + self.triggerHandler( "setData" + part, parts ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + part, parts ); + }); + }, null, value, arguments.length > 1, null, false ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery.removeData( elem, type + "queue", true ); + jQuery.removeData( elem, key, true ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, fixSpecified, + rclass = /[\t\r\n]/g, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea|)$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( core_rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var removes, className, elem, c, cl, i, l; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + if ( (value && typeof value === "string") || value === undefined ) { + removes = ( value || "" ).split( core_rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + if ( elem.nodeType === 1 && elem.className ) { + + className = (" " + elem.className + " ").replace( rclass, " " ); + + // loop over each item in the removal list + for ( c = 0, cl = removes.length; c < cl; c++ ) { + // Remove until there is nothing to remove, + while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) { + className = className.replace( " " + removes[ c ] + " " , " " ); + } + } + elem.className = value ? jQuery.trim( className ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( core_rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val, + self = jQuery(this); + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9 + attrFn: {}, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, isBool, + i = 0; + + if ( value && elem.nodeType === 1 ) { + + attrNames = value.split( core_rspace ); + + for ( ; i < attrNames.length; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); + + // See #9699 for explanation of this approach (setting first, then removal) + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( isBool && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true, + coords: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ? + ret.value : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.value = value + "" ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/, + rhoverHack = /(?:^|\s)hover(\.\S+|)\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var t, tns, type, origType, namespaces, origCount, + j, events, special, eventType, handleObj, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, "events", true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType, + type = event.type || event, + namespaces = []; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + for ( old = elem; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old === (elem.ownerDocument || document) ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related, + handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = core_slice.call( arguments ), + run_all = !event.exclusive && !event.namespace, + special = jQuery.event.special[ event.type ] || {}, + handlerQueue = []; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers that should run if there are delegated events + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !(event.button && event.type === "click") ) { + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + + // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + selMatch = {}; + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8) + event.metaKey = !!event.metaKey; + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === "undefined" ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "_submit_attached" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "_submit_attached", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "_change_attached", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { // && selector != null + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); +/*! + * Sizzle CSS Selector Engine + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license + * http://sizzlejs.com/ + */ +(function( window, undefined ) { + +var cachedruns, + assertGetIdNotName, + Expr, + getText, + isXML, + contains, + compile, + sortOrder, + hasDuplicate, + outermostContext, + + baseHasDuplicate = true, + strundefined = "undefined", + + expando = ( "sizcache" + Math.random() ).replace( ".", "" ), + + Token = String, + document = window.document, + docElem = document.documentElement, + dirruns = 0, + done = 0, + pop = [].pop, + push = [].push, + slice = [].slice, + // Use a stripped-down indexOf if a native one is unavailable + indexOf = [].indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + // Augment a function for special use by Sizzle + markFunction = function( fn, value ) { + fn[ expando ] = value == null || value; + return fn; + }, + + createCache = function() { + var cache = {}, + keys = []; + + return markFunction(function( key, value ) { + // Only keep the most recent entries + if ( keys.push( key ) > Expr.cacheLength ) { + delete cache[ keys.shift() ]; + } + + // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157) + return (cache[ key + " " ] = value); + }, cache ); + }, + + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + + // Regex + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + operators = "([*^$|!~]?=)", + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments not in parens/brackets, + // then attribute selectors and non-pseudos (denoted by :), + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", + + // For matchExpr.POS and matchExpr.needsContext + pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), + rpseudo = new RegExp( pseudos ), + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, + + rnot = /^:not/, + rsibling = /[\x20\t\r\n\f]*[+~]/, + rendsWithNot = /:not\($/, + + rheader = /h\d/i, + rinputs = /input|select|textarea|button/i, + + rbackslash = /\\(?!\\)/g, + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "POS": new RegExp( pos, "i" ), + "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + // For use in libraries implementing .is() + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) + }, + + // Support + + // Used for testing something on an element + assert = function( fn ) { + var div = document.createElement("div"); + + try { + return fn( div ); + } catch (e) { + return false; + } finally { + // release memory in IE + div = null; + } + }, + + // Check if getElementsByTagName("*") returns only elements + assertTagNameNoComments = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }), + + // Check if getAttribute returns normalized href attributes + assertHrefNotNormalized = assert(function( div ) { + div.innerHTML = ""; + return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && + div.firstChild.getAttribute("href") === "#"; + }), + + // Check if attributes should be retrieved by attribute nodes + assertAttributes = assert(function( div ) { + div.innerHTML = ""; + var type = typeof div.lastChild.getAttribute("multiple"); + // IE8 returns a string for some attributes even when not present + return type !== "boolean" && type !== "string"; + }), + + // Check if getElementsByClassName can be trusted + assertUsableClassName = assert(function( div ) { + // Opera can't find a second classname (in 9.6) + div.innerHTML = ""; + if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { + return false; + } + + // Safari 3.2 caches class attributes and doesn't catch changes + div.lastChild.className = "e"; + return div.getElementsByClassName("e").length === 2; + }), + + // Check if getElementById returns elements by name + // Check if getElementsByName privileges form controls or returns elements by ID + assertUsableName = assert(function( div ) { + // Inject content + div.id = expando + 0; + div.innerHTML = "
"; + docElem.insertBefore( div, docElem.firstChild ); + + // Test + var pass = document.getElementsByName && + // buggy browsers will return fewer than the correct 2 + document.getElementsByName( expando ).length === 2 + + // buggy browsers will return more than the correct 0 + document.getElementsByName( expando + 0 ).length; + assertGetIdNotName = !document.getElementById( expando ); + + // Cleanup + docElem.removeChild( div ); + + return pass; + }); + +// If slice is not available, provide a backup +try { + slice.call( docElem.childNodes, 0 )[0].nodeType; +} catch ( e ) { + slice = function( i ) { + var elem, + results = []; + for ( ; (elem = this[i]); i++ ) { + results.push( elem ); + } + return results; + }; +} + +function Sizzle( selector, context, results, seed ) { + results = results || []; + context = context || document; + var match, elem, xml, m, + nodeType = context.nodeType; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( nodeType !== 1 && nodeType !== 9 ) { + return []; + } + + xml = isXML( context ); + + if ( !xml && !seed ) { + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { + push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); + return results; + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed, xml ); +} + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + return Sizzle( expr, null, null, [ elem ] ).length > 0; +}; + +// Returns a function to use in pseudos for input types +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +// Returns a function to use in pseudos for buttons +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +// Returns a function to use in pseudos for positionals +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + } else { + + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } + return ret; +}; + +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Element contains another +contains = Sizzle.contains = docElem.contains ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); + } : + docElem.compareDocumentPosition ? + function( a, b ) { + return b && !!( a.compareDocumentPosition( b ) & 16 ); + } : + function( a, b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + return false; + }; + +Sizzle.attr = function( elem, name ) { + var val, + xml = isXML( elem ); + + if ( !xml ) { + name = name.toLowerCase(); + } + if ( (val = Expr.attrHandle[ name ]) ) { + return val( elem ); + } + if ( xml || assertAttributes ) { + return elem.getAttribute( name ); + } + val = elem.getAttributeNode( name ); + return val ? + typeof elem[ name ] === "boolean" ? + elem[ name ] ? name : null : + val.specified ? val.value : null : + null; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + // IE6/7 return a modified href + attrHandle: assertHrefNotNormalized ? + {} : + { + "href": function( elem ) { + return elem.getAttribute( "href", 2 ); + }, + "type": function( elem ) { + return elem.getAttribute("type"); + } + }, + + find: { + "ID": assertGetIdNotName ? + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + } : + function( id, context, xml ) { + if ( typeof context.getElementById !== strundefined && !xml ) { + var m = context.getElementById( id ); + + return m ? + m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? + [m] : + undefined : + []; + } + }, + + "TAG": assertTagNameNoComments ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + var elem, + tmp = [], + i = 0; + + for ( ; (elem = results[i]); i++ ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }, + + "NAME": assertUsableName && function( tag, context ) { + if ( typeof context.getElementsByName !== strundefined ) { + return context.getElementsByName( name ); + } + }, + + "CLASS": assertUsableClassName && function( className, context, xml ) { + if ( typeof context.getElementsByClassName !== strundefined && !xml ) { + return context.getElementsByClassName( className ); + } + } + }, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( rbackslash, "" ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 3 xn-component of xn+y argument ([+-]?\d*n|) + 4 sign of xn-component + 5 x of xn-component + 6 sign of y-component + 7 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1] === "nth" ) { + // nth-child requires argument + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); + match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); + + // other types prohibit arguments + } else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var unquoted, excess; + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + if ( match[3] ) { + match[2] = match[3]; + } else if ( (unquoted = match[4]) ) { + // Only check arguments that contain a pseudo + if ( rpseudo.test(unquoted) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + unquoted = unquoted.slice( 0, excess ); + match[0] = match[0].slice( 0, excess ); + } + match[2] = unquoted; + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + "ID": assertGetIdNotName ? + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + return elem.getAttribute("id") === id; + }; + } : + function( id ) { + id = id.replace( rbackslash, "" ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === id; + }; + }, + + "TAG": function( nodeName ) { + if ( nodeName === "*" ) { + return function() { return true; }; + } + nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); + + return function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ expando ][ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem, context ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.substr( result.length - check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, argument, first, last ) { + + if ( type === "nth" ) { + return function( elem ) { + var node, diff, + parent = elem.parentNode; + + if ( first === 1 && last === 0 ) { + return true; + } + + if ( parent ) { + diff = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + diff++; + if ( elem === node ) { + break; + } + } + } + } + + // Incorporate the offset (or cast to NaN), then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + }; + } + + return function( elem ) { + var node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + var nodeType; + elem = elem.firstChild; + while ( elem ) { + if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { + return false; + } + elem = elem.nextSibling; + } + return true; + }, + + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "text": function( elem ) { + var type, attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + (type = elem.type) === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); + }, + + // Input types + "radio": createInputPseudo("radio"), + "checkbox": createInputPseudo("checkbox"), + "file": createInputPseudo("file"), + "password": createInputPseudo("password"), + "image": createInputPseudo("image"), + + "submit": createButtonPseudo("submit"), + "reset": createButtonPseudo("reset"), + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "focus": function( elem ) { + var doc = elem.ownerDocument; + return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + "active": function( elem ) { + return elem === elem.ownerDocument.activeElement; + }, + + // Positional types + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 0; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + for ( var i = 1; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +function siblingCheck( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; +} + +sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? + a.compareDocumentPosition : + a.compareDocumentPosition(b) & 4 + ) ? -1 : 1; + } : + function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + +// Always assume the presence of duplicates if sort doesn't +// pass them to our comparison function (as in Google Chrome). +[0, 0].sort( sortOrder ); +baseHasDuplicate = !hasDuplicate; + +// Document sorting and removing duplicates +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + i = 1, + j = 0; + + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( ; (elem = results[i]); i++ ) { + if ( elem === results[ i - 1 ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ expando ][ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + + // Cast descendant combinators to space + matched.type = match[0].replace( rtrim, " " ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + + tokens.push( matched = new Token( match.shift() ) ); + soFar = soFar.slice( matched.length ); + matched.type = type; + matched.matches = match; + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && combinator.dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( !xml ) { + var cache, + dirkey = dirruns + " " + doneName + " ", + cachedkey = dirkey + cachedruns; + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( (cache = elem[ expando ]) === cachedkey ) { + return elem.sizset; + } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { + if ( elem.sizset ) { + return elem; + } + } else { + elem[ expando ] = cachedkey; + if ( matcher( elem, context, xml ) ) { + elem.sizset = true; + return elem; + } + elem.sizset = false; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( checkNonElements || elem.nodeType === 1 ) { + if ( matcher( elem, context, xml ) ) { + return elem; + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && tokens.join("") + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Nested matchers should use non-integer dirruns + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = superMatcher.el; + } + + // Add elements passing elementMatchers directly to results + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + for ( j = 0; (matcher = elementMatchers[j]); j++ ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++superMatcher.el; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + for ( j = 0; (matcher = setMatchers[j]); j++ ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + superMatcher.el = 0; + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ expando ][ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed, xml ) { + var i, tokens, token, type, find, + match = tokenize( selector ), + j = match.length; + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && !xml && + Expr.relative[ tokens[1].type ] ) { + + context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0]; + if ( !context ) { + return results; + } + + selector = selector.slice( tokens.shift().length ); + } + + // Fetch a seed set for right-to-left matching + for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( rbackslash, "" ), + rsibling.test( tokens[0].type ) && context.parentNode || context, + xml + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && tokens.join(""); + if ( !selector ) { + push.apply( results, slice.call( seed, 0 ) ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + xml, + results, + rsibling.test( selector ) + ); + return results; +} + +if ( document.querySelectorAll ) { + (function() { + var disconnectedMatch, + oldSelect = select, + rescape = /'|\\/g, + rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, + + // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA + // A support test would require too much code (would include document ready) + rbuggyQSA = [ ":focus" ], + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + // A support test would require too much code (would include document ready) + // just skip matchesSelector for :active + rbuggyMatches = [ ":active" ], + matches = docElem.matchesSelector || + docElem.mozMatchesSelector || + docElem.webkitMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector; + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explictly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // IE8 - Some boolean attributes are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here (do not put tests after this one) + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Opera 10-12/IE9 - ^= $= *= and empty values + // Should not select anything + div.innerHTML = "

"; + if ( div.querySelectorAll("[test^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here (do not put tests after this one) + div.innerHTML = ""; + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push(":enabled", ":disabled"); + } + }); + + // rbuggyQSA always contains :focus, so no need for a length check + rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); + + select = function( selector, context, results, seed, xml ) { + // Only use querySelectorAll when not filtering, + // when this is not xml, + // and when no QSA bugs apply + if ( !seed && !xml && !rbuggyQSA.test( selector ) ) { + var groups, i, + old = true, + nid = expando, + newContext = context, + newSelector = context.nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + groups[i].join(""); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, slice.call( newContext.querySelectorAll( + newSelector + ), 0 ) ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + + return oldSelect( selector, context, results, seed, xml ); + }; + + if ( matches ) { + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + try { + matches.call( div, "[test!='']:sizzle" ); + rbuggyMatches.push( "!=", pseudos ); + } catch ( e ) {} + }); + + // rbuggyMatches always contains :active and :focus, so no need for a length check + rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); + + Sizzle.matchesSelector = function( elem, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + // rbuggyMatches always contains :active, so no need for an existence check + if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) { + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, null, null, [ elem ] ).length > 0; + }; + } + })(); +} + +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Back-compat +function setFilters() {} +Expr.filters = setFilters.prototype = Expr.pseudos; +Expr.setFilters = new setFilters(); + +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +var runtil = /Until$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + isSimple = /^.[^:#\[\.,]*$/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, l, length, n, r, ret, + self = this; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + ret = this.pushStack( "", "find", selector ); + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + rneedsContext.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + cur = this[i]; + + while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + } + cur = cur.parentNode; + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +jQuery.fn.andSelf = jQuery.fn.addBack; + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( this.length > 1 && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, core_slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /]", "i"), + rcheckableType = /^(?:checkbox|radio)$/, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /\/(java|ecma)script/i, + rcleanScript = /^\s*\s*$/g, + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + col: [ 2, "", "
" ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, +// unless wrapped in a div with non-breaking characters in front of it. +if ( !jQuery.support.htmlSerialize ) { + wrapMap._default = [ 1, "X
", "
" ]; +} + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + }, + + append: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 ) { + this.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 ) { + this.insertBefore( elem, this.firstChild ); + } + }); + }, + + before: function() { + if ( !isDisconnected( this[0] ) ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this ); + }); + } + + if ( arguments.length ) { + var set = jQuery.clean( arguments ); + return this.pushStack( jQuery.merge( set, this ), "before", this.selector ); + } + }, + + after: function() { + if ( !isDisconnected( this[0] ) ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + } + + if ( arguments.length ) { + var set = jQuery.clean( arguments ); + return this.pushStack( jQuery.merge( this, set ), "after", this.selector ); + } + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + jQuery.cleanData( [ elem ] ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName( "*" ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function( value ) { + if ( !isDisconnected( this[0] ) ) { + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this), old = self.html(); + self.replaceWith( value.call( this, i, old ) ); + }); + } + + if ( typeof value !== "string" ) { + value = jQuery( value ).detach(); + } + + return this.each(function() { + var next = this.nextSibling, + parent = this.parentNode; + + jQuery( this ).remove(); + + if ( next ) { + jQuery(next).before( value ); + } else { + jQuery(parent).append( value ); + } + }); + } + + return this.length ? + this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : + this; + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, table, callback ) { + + // Flatten any nested arrays + args = [].concat.apply( [], args ); + + var results, first, fragment, iNoClone, + i = 0, + value = args[0], + scripts = [], + l = this.length; + + // We can't cloneNode fragments that contain checked, in WebKit + if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) { + return this.each(function() { + jQuery(this).domManip( args, table, callback ); + }); + } + + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + args[0] = value.call( this, i, table ? self.html() : undefined ); + self.domManip( args, table, callback ); + }); + } + + if ( this[0] ) { + results = jQuery.buildFragment( args, this, scripts ); + fragment = results.fragment; + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + // Fragments from the fragment cache must always be cloned and never used in place. + for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) { + callback.call( + table && jQuery.nodeName( this[i], "table" ) ? + findOrAppend( this[i], "tbody" ) : + this[i], + i === iNoClone ? + fragment : + jQuery.clone( fragment, true, true ) + ); + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + + if ( scripts.length ) { + jQuery.each( scripts, function( i, elem ) { + if ( elem.src ) { + if ( jQuery.ajax ) { + jQuery.ajax({ + url: elem.src, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } else { + jQuery.error("no ajax"); + } + } else { + jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + }); + } + } + + return this; + } +}); + +function findOrAppend( elem, tag ) { + return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) ); +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function cloneFixAttributes( src, dest ) { + var nodeName; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + // clearAttributes removes the attributes, which we don't want, + // but also removes the attachEvent events, which we *do* want + if ( dest.clearAttributes ) { + dest.clearAttributes(); + } + + // mergeAttributes, in contrast, only merges back on the + // original attributes, not the events + if ( dest.mergeAttributes ) { + dest.mergeAttributes( src ); + } + + nodeName = dest.nodeName.toLowerCase(); + + if ( nodeName === "object" ) { + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + + // IE blanks contents when cloning scripts + } else if ( nodeName === "script" && dest.text !== src.text ) { + dest.text = src.text; + } + + // Event data gets referenced instead of copied if the expando + // gets copied too + dest.removeAttribute( jQuery.expando ); +} + +jQuery.buildFragment = function( args, context, scripts ) { + var fragment, cacheable, cachehit, + first = args[ 0 ]; + + // Set context from what may come in as undefined or a jQuery collection or a node + // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 & + // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception + context = context || document; + context = !context.nodeType && context[0] || context; + context = context.ownerDocument || context; + + // Only cache "small" (1/2 KB) HTML strings that are associated with the main document + // Cloning options loses the selected state, so don't cache them + // IE 6 doesn't like it when you put or elements in a fragment + // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache + // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501 + if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document && + first.charAt(0) === "<" && !rnocache.test( first ) && + (jQuery.support.checkClone || !rchecked.test( first )) && + (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) { + + // Mark cacheable and look for a hit + cacheable = true; + fragment = jQuery.fragments[ first ]; + cachehit = fragment !== undefined; + } + + if ( !fragment ) { + fragment = context.createDocumentFragment(); + jQuery.clean( args, context, fragment, scripts ); + + // Update the cache, but only store false + // unless this is a second parsing of the same content + if ( cacheable ) { + jQuery.fragments[ first ] = cachehit && fragment; + } + } + + return { fragment: fragment, cacheable: cacheable }; +}; + +jQuery.fragments = {}; + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + l = insert.length, + parent = this.length === 1 && this[0].parentNode; + + if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) { + insert[ original ]( this[0] ); + return this; + } else { + for ( ; i < l; i++ ) { + elems = ( i > 0 ? this.clone(true) : this ).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +}); + +function getAll( elem ) { + if ( typeof elem.getElementsByTagName !== "undefined" ) { + return elem.getElementsByTagName( "*" ); + + } else if ( typeof elem.querySelectorAll !== "undefined" ) { + return elem.querySelectorAll( "*" ); + + } else { + return []; + } +} + +// Used in clean, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var srcElements, + destElements, + i, + clone; + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + // IE copies events bound via attachEvent when using cloneNode. + // Calling detachEvent on the clone will also remove the events + // from the original. In order to get around this, we use some + // proprietary methods to clear the events. Thanks to MooTools + // guys for this hotness. + + cloneFixAttributes( elem, clone ); + + // Using Sizzle here is crazy slow, so we use getElementsByTagName instead + srcElements = getAll( elem ); + destElements = getAll( clone ); + + // Weird iteration because IE will replace the length property + // with an element if you are cloning the body and one of the + // elements on the page has a name or id of "length" + for ( i = 0; srcElements[i]; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + cloneFixAttributes( srcElements[i], destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + cloneCopyEvent( elem, clone ); + + if ( deepDataAndEvents ) { + srcElements = getAll( elem ); + destElements = getAll( clone ); + + for ( i = 0; srcElements[i]; ++i ) { + cloneCopyEvent( srcElements[i], destElements[i] ); + } + } + } + + srcElements = destElements = null; + + // Return the cloned set + return clone; + }, + + clean: function( elems, context, fragment, scripts ) { + var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags, + safe = context === document && safeFragment, + ret = []; + + // Ensure that context is a document + if ( !context || typeof context.createDocumentFragment === "undefined" ) { + context = document; + } + + // Use the already-created safe fragment if context permits + for ( i = 0; (elem = elems[i]) != null; i++ ) { + if ( typeof elem === "number" ) { + elem += ""; + } + + if ( !elem ) { + continue; + } + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + if ( !rhtml.test( elem ) ) { + elem = context.createTextNode( elem ); + } else { + // Ensure a safe container in which to render the html + safe = safe || createSafeFragment( context ); + div = context.createElement("div"); + safe.appendChild( div ); + + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(rxhtmlTag, "<$1>"); + + // Go to html and back, then peel off extra wrappers + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + depth = wrap[0]; + div.innerHTML = wrap[1] + elem + wrap[2]; + + // Move to the right depth + while ( depth-- ) { + div = div.lastChild; + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + hasBody = rtbody.test(elem); + tbody = tag === "table" && !hasBody ? + div.firstChild && div.firstChild.childNodes : + + // String was a bare or + wrap[1] === "
" && !hasBody ? + div.childNodes : + []; + + for ( j = tbody.length - 1; j >= 0 ; --j ) { + if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { + tbody[ j ].parentNode.removeChild( tbody[ j ] ); + } + } + } + + // IE completely kills leading whitespace when innerHTML is used + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); + } + + elem = div.childNodes; + + // Take out of fragment container (we need a fresh div each time) + div.parentNode.removeChild( div ); + } + } + + if ( elem.nodeType ) { + ret.push( elem ); + } else { + jQuery.merge( ret, elem ); + } + } + + // Fix #11356: Clear elements from safeFragment + if ( div ) { + elem = div = safe = null; + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + for ( i = 0; (elem = ret[i]) != null; i++ ) { + if ( jQuery.nodeName( elem, "input" ) ) { + fixDefaultChecked( elem ); + } else if ( typeof elem.getElementsByTagName !== "undefined" ) { + jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked ); + } + } + } + + // Append elements to a provided document fragment + if ( fragment ) { + // Special handling of each script element + handleScript = function( elem ) { + // Check if we consider it executable + if ( !elem.type || rscriptType.test( elem.type ) ) { + // Detach the script and store it in the scripts array (if provided) or the fragment + // Return truthy to indicate that it has been handled + return scripts ? + scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) : + fragment.appendChild( elem ); + } + }; + + for ( i = 0; (elem = ret[i]) != null; i++ ) { + // Check if we're done after handling an executable script + if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) { + // Append to fragment and handle embedded scripts + fragment.appendChild( elem ); + if ( typeof elem.getElementsByTagName !== "undefined" ) { + // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration + jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript ); + + // Splice the scripts into ret after their former ancestor and advance our index beyond them + ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); + i += jsTags.length; + } + } + } + } + + return ret; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var data, id, elem, type, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + jQuery.deletedIds.push( id ); + } + } + } + } + } +}); +// Limit scope pollution from any deprecated API +(function() { + +var matched, browser; + +// Use of jQuery.browser is frowned upon. +// More details: http://api.jquery.com/jQuery.browser +// jQuery.uaMatch maintained for back-compat +jQuery.uaMatch = function( ua ) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || + /(webkit)[ \/]([\w.]+)/.exec( ua ) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || + /(msie) ([\w.]+)/.exec( ua ) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; +}; + +matched = jQuery.uaMatch( navigator.userAgent ); +browser = {}; + +if ( matched.browser ) { + browser[ matched.browser ] = true; + browser.version = matched.version; +} + +// Chrome is Webkit, but Webkit is also Safari. +if ( browser.chrome ) { + browser.webkit = true; +} else if ( browser.webkit ) { + browser.safari = true; +} + +jQuery.browser = browser; + +jQuery.sub = function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; +}; + +})(); +var curCSS, iframe, iframeDoc, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity=([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ], + + eventsToggle = jQuery.fn.toggle; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var elem, display, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + values[ index ] = jQuery._data( elem, "olddisplay" ); + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && elem.style.display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + display = curCSS( elem, "display" ); + + if ( !values[ index ] && display !== "none" ) { + jQuery._data( elem, "olddisplay", display ); + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state, fn2 ) { + var bool = typeof state === "boolean"; + + if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) { + return eventsToggle.apply( this, arguments ); + } + + return this.each(function() { + if ( bool ? state : isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + + } + } + } + }, + + // Exclude the following css properties to add px + cssNumber: { + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, numeric, extra ) { + var val, num, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( numeric || extra !== undefined ) { + num = parseFloat( val ); + return numeric || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +// NOTE: To any future maintainer, we've window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + curCSS = function( elem, name ) { + var ret, width, minWidth, maxWidth, + computed = window.getComputedStyle( elem, null ), + style = elem.style; + + if ( computed ) { + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + curCSS = function( elem, name ) { + var left, rsLeft, + ret = elem.currentStyle && elem.currentStyle[ name ], + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + elem.runtimeStyle.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + elem.runtimeStyle.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + // we use jQuery.css instead of curCSS here + // because of the reliableMarginRight CSS hook! + val += jQuery.css( elem, extra + cssExpand[ i ], true ); + } + + // From this point on we use curCSS for maximum performance (relevant in animations) + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + } else { + // at this point, extra isn't content, so add padding + val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0; + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + valueIsBorderBox = true, + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox + ) + ) + "px"; +} + + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + if ( elemdisplay[ nodeName ] ) { + return elemdisplay[ nodeName ]; + } + + var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ), + display = elem.css("display"); + elem.remove(); + + // If the simple way fails, + // get element's real default display by attaching it to a temp iframe + if ( display === "none" || display === "" ) { + // Use the already-created iframe if possible + iframe = document.body.appendChild( + iframe || jQuery.extend( document.createElement("iframe"), { + frameBorder: 0, + width: 0, + height: 0 + }) + ); + + // Create a cacheable copy of the iframe document on first call. + // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML + // document to it; WebKit & Firefox won't allow reusing the iframe document. + if ( !iframeDoc || !iframe.createElement ) { + iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; + iframeDoc.write(""); + iframeDoc.close(); + } + + elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) ); + + display = curCSS( elem, "display" ); + document.body.removeChild( iframe ); + } + + // Store the correct default display + elemdisplay[ nodeName ] = display; + + return display; +} + +jQuery.each([ "height", "width" ], function( i, name ) { + jQuery.cssHooks[ name ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + // certain elements can have dimension info if we invisibly show them + // however, it must have a current display style that would benefit from this + if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) { + return jQuery.swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); + }); + } else { + return getWidthOrHeight( elem, name, extra ); + } + } + }, + + set: function( elem, value, extra ) { + return setPositiveNumber( elem, value, extra ? + augmentWidthOrHeight( + elem, + name, + extra, + jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box" + ) : 0 + ); + } + }; +}); + +if ( !jQuery.support.opacity ) { + jQuery.cssHooks.opacity = { + get: function( elem, computed ) { + // IE uses filters for opacity + return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? + ( 0.01 * parseFloat( RegExp.$1 ) ) + "" : + computed ? "1" : ""; + }, + + set: function( elem, value ) { + var style = elem.style, + currentStyle = elem.currentStyle, + opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", + filter = currentStyle && currentStyle.filter || style.filter || ""; + + // IE has trouble with opacity if it does not have layout + // Force it by setting the zoom level + style.zoom = 1; + + // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 + if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" && + style.removeAttribute ) { + + // Setting style.filter to null, "" & " " still leave "filter:" in the cssText + // if "filter:" is present at all, clearType is disabled, we want to avoid this + // style.removeAttribute is IE Only, but so apparently is this code path... + style.removeAttribute( "filter" ); + + // if there there is no filter style applied in a css rule, we are done + if ( currentStyle && !currentStyle.filter ) { + return; + } + } + + // otherwise, set new filter values + style.filter = ralpha.test( filter ) ? + filter.replace( ralpha, opacity ) : + filter + " " + opacity; + } + }; +} + +// These hooks cannot be added until DOM ready because the support test +// for it is not run until after DOM ready +jQuery(function() { + if ( !jQuery.support.reliableMarginRight ) { + jQuery.cssHooks.marginRight = { + get: function( elem, computed ) { + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + // Work around by temporarily setting element display to inline-block + return jQuery.swap( elem, { "display": "inline-block" }, function() { + if ( computed ) { + return curCSS( elem, "marginRight" ); + } + }); + } + }; + } + + // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 + // getComputedStyle returns percent when specified for top/left/bottom/right + // rather than make the css module depend on the offset module, we just check for it here + if ( !jQuery.support.pixelPosition && jQuery.fn.position ) { + jQuery.each( [ "top", "left" ], function( i, prop ) { + jQuery.cssHooks[ prop ] = { + get: function( elem, computed ) { + if ( computed ) { + var ret = curCSS( elem, prop ); + // if curCSS returns percentage, fallback to offset + return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret; + } + } + }; + }); + } + +}); + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.hidden = function( elem ) { + return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none"); + }; + + jQuery.expr.filters.visible = function( elem ) { + return !jQuery.expr.filters.hidden( elem ); + }; +} + +// These hooks are used by animate to expand properties +jQuery.each({ + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i, + + // assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [ value ], + expanded = {}; + + for ( i = 0; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( !rmargin.test( prefix ) ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +}); +var r20 = /%20/g, + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, + rselectTextarea = /^(?:select|textarea)/i; + +jQuery.fn.extend({ + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map(function(){ + return this.elements ? jQuery.makeArray( this.elements ) : this; + }) + .filter(function(){ + return this.name && !this.disabled && + ( this.checked || rselectTextarea.test( this.nodeName ) || + rinput.test( this.type ) ); + }) + .map(function( i, elem ){ + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val, i ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + } +}); + +//Serialize an array of form elements or a set of +//key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value ); + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); +}; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( jQuery.isArray( obj ) ) { + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + // If array item is non-scalar (array or object), encode its + // numeric index to resolve deserialization ambiguity issues. + // Note that rack (as of 1.0.0) can't currently deserialize + // nested arrays properly, and attempting to do so may cause + // a server error. Possible fixes are to modify rack's + // deserialization algorithm or to provide an option or flag + // to force array serialization to be shallow. + buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); + } + }); + + } else if ( !traditional && jQuery.type( obj ) === "object" ) { + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + // Serialize scalar item. + add( prefix, obj ); + } +} +var + // Document location + ajaxLocParts, + ajaxLocation, + + rhash = /#.*$/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + rquery = /\?/, + rscript = /)<[^<]*)*<\/script>/gi, + rts = /([?&])_=[^&]*/, + rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/, + + // Keep a copy of the old load method + _load = jQuery.fn.load, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = ["*/"] + ["*"]; + +// #8138, IE may throw an exception when accessing +// a field from window.location if document.domain has been set +try { + ajaxLocation = location.href; +} catch( e ) { + // Use the href attribute of an A element + // since IE will modify it given document.location + ajaxLocation = document.createElement( "a" ); + ajaxLocation.href = ""; + ajaxLocation = ajaxLocation.href; +} + +// Segment location into parts +ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, list, placeBefore, + dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ), + i = 0, + length = dataTypes.length; + + if ( jQuery.isFunction( func ) ) { + // For each dataType in the dataTypeExpression + for ( ; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR, + dataType /* internal */, inspected /* internal */ ) { + + dataType = dataType || options.dataTypes[ 0 ]; + inspected = inspected || {}; + + inspected[ dataType ] = true; + + var selection, + list = structure[ dataType ], + i = 0, + length = list ? list.length : 0, + executeOnly = ( structure === prefilters ); + + for ( ; i < length && ( executeOnly || !selection ); i++ ) { + selection = list[ i ]( options, originalOptions, jqXHR ); + // If we got redirected to another dataType + // we try there if executing only and not done already + if ( typeof selection === "string" ) { + if ( !executeOnly || inspected[ selection ] ) { + selection = undefined; + } else { + options.dataTypes.unshift( selection ); + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, selection, inspected ); + } + } + } + // If we're only executing or nothing was selected + // we try the catchall dataType if not done already + if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) { + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, "*", inspected ); + } + // unnecessary when only executing (prefilters) + // but it'll be ignored by the caller in that case + return selection; +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } +} + +jQuery.fn.load = function( url, params, callback ) { + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + } + + // Don't do a request if no elements are being requested + if ( !this.length ) { + return this; + } + + var selector, type, response, + self = this, + off = url.indexOf(" "); + + if ( off >= 0 ) { + selector = url.slice( off, url.length ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( jQuery.isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // Request the remote document + jQuery.ajax({ + url: url, + + // if "type" variable is undefined, then "GET" method will be used + type: type, + dataType: "html", + data: params, + complete: function( jqXHR, status ) { + if ( callback ) { + self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] ); + } + } + }).done(function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + // See if a selector was specified + self.html( selector ? + + // Create a dummy div to hold the results + jQuery("
") + + // inject the contents of the document in, removing the scripts + // to avoid any 'Permission Denied' errors in IE + .append( responseText.replace( rscript, "" ) ) + + // Locate the specified elements + .find( selector ) : + + // If not, just inject the full result + responseText ); + + }); + + return this; +}; + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){ + jQuery.fn[ o ] = function( f ){ + return this.on( o, f ); + }; +}); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +}); + +jQuery.extend({ + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + if ( settings ) { + // Building a settings object + ajaxExtend( target, jQuery.ajaxSettings ); + } else { + // Extending ajaxSettings + settings = target; + target = jQuery.ajaxSettings; + } + ajaxExtend( target, settings ); + return target; + }, + + ajaxSettings: { + url: ajaxLocation, + isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), + global: true, + type: "GET", + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + processData: true, + async: true, + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + xml: "application/xml, text/xml", + html: "text/html", + text: "text/plain", + json: "application/json, text/javascript", + "*": allTypes + }, + + contents: { + xml: /xml/, + html: /html/, + json: /json/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText" + }, + + // List of data converters + // 1) key format is "source_type destination_type" (a single space in-between) + // 2) the catchall symbol "*" can be used for source_type + converters: { + + // Convert anything to text + "* text": window.String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": jQuery.parseJSON, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + context: true, + url: true + } + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var // ifModified key + ifModifiedKey, + // Response headers + responseHeadersString, + responseHeaders, + // transport + transport, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events + // It's the callbackContext if one was provided in the options + // and if it's a DOM node or a jQuery collection + globalEventContext = callbackContext !== s && + ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? + jQuery( callbackContext ) : jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // The jqXHR state + state = 0, + // Default abort message + strAbort = "canceled", + // Fake xhr + jqXHR = { + + readyState: 0, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( !state ) { + var lname = name.toLowerCase(); + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match === undefined ? null : match; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + statusText = statusText || strAbort; + if ( transport ) { + transport.abort( statusText ); + } + done( 0, statusText ); + return this; + } + }; + + // Callback for when everything is done + // It is defined here because jslint complains if it is declared + // at the end of the function (which would be more logical and readable) + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + + modified = jqXHR.getResponseHeader("Last-Modified"); + if ( modified ) { + jQuery.lastModified[ ifModifiedKey ] = modified; + } + modified = jqXHR.getResponseHeader("Etag"); + if ( modified ) { + jQuery.etag[ ifModifiedKey ] = modified; + } + } + + // If not modified + if ( status === 304 ) { + + statusText = "notmodified"; + isSuccess = true; + + // If we have data + } else { + + isSuccess = ajaxConvert( s, response ); + statusText = isSuccess.state; + success = isSuccess.data; + error = isSuccess.error; + isSuccess = !error; + } + } else { + // We extract error from statusText + // then normalize statusText and status for non-aborts + error = statusText; + if ( !statusText || status ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + // Attach deferreds + deferred.promise( jqXHR ); + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + jqXHR.complete = completeDeferred.add; + + // Status-dependent callbacks + jqXHR.statusCode = function( map ) { + if ( map ) { + var tmp; + if ( state < 2 ) { + for ( tmp in map ) { + statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; + } + } else { + tmp = map[ jqXHR.status ]; + jqXHR.always( tmp ); + } + } + return this; + }; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) + // We also use the url parameter if available + s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace ); + + // A cross-domain request is in order when we have a protocol:host:port mismatch + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( state === 2 ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + fireGlobals = s.global; + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Get ifModifiedKey before adding the anti-cache parameter + ifModifiedKey = s.url; + + // Add anti-cache in url if needed + if ( s.cache === false ) { + + var ts = jQuery.now(), + // try replacing _= if it is there + ret = s.url.replace( rts, "$1_=" + ts ); + + // if nothing was replaced, add timestamp to the end + s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + ifModifiedKey = ifModifiedKey || s.url; + if ( jQuery.lastModified[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); + } + if ( jQuery.etag[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); + } + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already and return + return jqXHR.abort(); + + } + + // aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout( function(){ + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch (e) { + // Propagate exception as error if not done + if ( state < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + throw e; + } + } + } + + return jqXHR; + }, + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {} + +}); + +/* Handles responses to an ajax request: + * - sets all responseXXX fields accordingly + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes, + responseFields = s.responseFields; + + // Fill responseXXX fields + for ( type in responseFields ) { + if ( type in responses ) { + jqXHR[ responseFields[type] ] = responses[ type ]; + } + } + + // Remove auto dataType and get content-type in the process + while( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "content-type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +// Chain conversions given the request and the original response +function ajaxConvert( s, response ) { + + var conv, conv2, current, tmp, + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(), + prev = dataTypes[ 0 ], + converters = {}, + i = 0; + + // Apply the dataFilter if provided + if ( s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + // Convert to each sequential dataType, tolerating list modification + for ( ; (current = dataTypes[++i]); ) { + + // There's only work to do if current dataType is non-auto + if ( current !== "*" ) { + + // Convert response if prev dataType is non-auto and differs from current + if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split(" "); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.splice( i--, 0, current ); + } + + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s["throws"] ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current }; + } + } + } + } + + // Update prev for next iteration + prev = current; + } + } + + return { state: "success", data: response }; +} +var oldCallbacks = [], + rquestion = /\?/, + rjsonp = /(=)\?(?=&|$)|\?\?/, + nonce = jQuery.now(); + +// Default jsonp settings +jQuery.ajaxSetup({ + jsonp: "callback", + jsonpCallback: function() { + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); + this[ callback ] = true; + return callback; + } +}); + +// Detect, normalize options and install callbacks for jsonp requests +jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var callbackName, overwritten, responseContainer, + data = s.data, + url = s.url, + hasCallback = s.jsonp !== false, + replaceInUrl = hasCallback && rjsonp.test( url ), + replaceInData = hasCallback && !replaceInUrl && typeof data === "string" && + !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && + rjsonp.test( data ); + + // Handle iff the expected data type is "jsonp" or we have a parameter to set + if ( s.dataTypes[ 0 ] === "jsonp" || replaceInUrl || replaceInData ) { + + // Get callback name, remembering preexisting value associated with it + callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ? + s.jsonpCallback() : + s.jsonpCallback; + overwritten = window[ callbackName ]; + + // Insert callback into url or form data + if ( replaceInUrl ) { + s.url = url.replace( rjsonp, "$1" + callbackName ); + } else if ( replaceInData ) { + s.data = data.replace( rjsonp, "$1" + callbackName ); + } else if ( hasCallback ) { + s.url += ( rquestion.test( url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + } + + // Use data converter to retrieve json after script execution + s.converters["script json"] = function() { + if ( !responseContainer ) { + jQuery.error( callbackName + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // force json dataType + s.dataTypes[ 0 ] = "json"; + + // Install callback + window[ callbackName ] = function() { + responseContainer = arguments; + }; + + // Clean-up function (fires after converters) + jqXHR.always(function() { + // Restore preexisting value + window[ callbackName ] = overwritten; + + // Save back as free + if ( s[ callbackName ] ) { + // make sure that re-using the options doesn't screw things around + s.jsonpCallback = originalSettings.jsonpCallback; + + // save the callback name for future use + oldCallbacks.push( callbackName ); + } + + // Call if it was a function and we have a response + if ( responseContainer && jQuery.isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + }); + + // Delegate to script + return "script"; + } +}); +// Install script dataType +jQuery.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /javascript|ecmascript/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +}); + +// Handle cache's special case and global +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + s.global = false; + } +}); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function(s) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + + var script, + head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement; + + return { + + send: function( _, callback ) { + + script = document.createElement( "script" ); + + script.async = "async"; + + if ( s.scriptCharset ) { + script.charset = s.scriptCharset; + } + + script.src = s.url; + + // Attach handlers for all browsers + script.onload = script.onreadystatechange = function( _, isAbort ) { + + if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; + + // Remove the script + if ( head && script.parentNode ) { + head.removeChild( script ); + } + + // Dereference the script + script = undefined; + + // Callback if not abort + if ( !isAbort ) { + callback( 200, "success" ); + } + } + }; + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709 and #4378). + head.insertBefore( script, head.firstChild ); + }, + + abort: function() { + if ( script ) { + script.onload( 0, 1 ); + } + } + }; + } +}); +var xhrCallbacks, + // #5280: Internet Explorer will keep connections alive if we don't abort on unload + xhrOnUnloadAbort = window.ActiveXObject ? function() { + // Abort all pending requests + for ( var key in xhrCallbacks ) { + xhrCallbacks[ key ]( 0, 1 ); + } + } : false, + xhrId = 0; + +// Functions to create xhrs +function createStandardXHR() { + try { + return new window.XMLHttpRequest(); + } catch( e ) {} +} + +function createActiveXHR() { + try { + return new window.ActiveXObject( "Microsoft.XMLHTTP" ); + } catch( e ) {} +} + +// Create the request object +// (This is still attached to ajaxSettings for backward compatibility) +jQuery.ajaxSettings.xhr = window.ActiveXObject ? + /* Microsoft failed to properly + * implement the XMLHttpRequest in IE7 (can't request local files), + * so we use the ActiveXObject when it is available + * Additionally XMLHttpRequest can be disabled in IE7/IE8 so + * we need a fallback. + */ + function() { + return !this.isLocal && createStandardXHR() || createActiveXHR(); + } : + // For all other browsers, use the standard XMLHttpRequest object + createStandardXHR; + +// Determine support properties +(function( xhr ) { + jQuery.extend( jQuery.support, { + ajax: !!xhr, + cors: !!xhr && ( "withCredentials" in xhr ) + }); +})( jQuery.ajaxSettings.xhr() ); + +// Create transport if the browser can provide an xhr +if ( jQuery.support.ajax ) { + + jQuery.ajaxTransport(function( s ) { + // Cross domain only allowed if supported through XMLHttpRequest + if ( !s.crossDomain || jQuery.support.cors ) { + + var callback; + + return { + send: function( headers, complete ) { + + // Get a new xhr + var handle, i, + xhr = s.xhr(); + + // Open the socket + // Passing null username, generates a login popup on Opera (#2865) + if ( s.username ) { + xhr.open( s.type, s.url, s.async, s.username, s.password ); + } else { + xhr.open( s.type, s.url, s.async ); + } + + // Apply custom fields if provided + if ( s.xhrFields ) { + for ( i in s.xhrFields ) { + xhr[ i ] = s.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( s.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( s.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !s.crossDomain && !headers["X-Requested-With"] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Need an extra try/catch for cross domain requests in Firefox 3 + try { + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + } catch( _ ) {} + + // Do send the request + // This may raise an exception which is actually + // handled in jQuery.ajax (so no try/catch here) + xhr.send( ( s.hasContent && s.data ) || null ); + + // Listener + callback = function( _, isAbort ) { + + var status, + statusText, + responseHeaders, + responses, + xml; + + // Firefox throws exceptions when accessing properties + // of an xhr when a network error occurred + // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) + try { + + // Was never called and is aborted or complete + if ( callback && ( isAbort || xhr.readyState === 4 ) ) { + + // Only called once + callback = undefined; + + // Do not keep as active anymore + if ( handle ) { + xhr.onreadystatechange = jQuery.noop; + if ( xhrOnUnloadAbort ) { + delete xhrCallbacks[ handle ]; + } + } + + // If it's an abort + if ( isAbort ) { + // Abort it manually if needed + if ( xhr.readyState !== 4 ) { + xhr.abort(); + } + } else { + status = xhr.status; + responseHeaders = xhr.getAllResponseHeaders(); + responses = {}; + xml = xhr.responseXML; + + // Construct response list + if ( xml && xml.documentElement /* #4958 */ ) { + responses.xml = xml; + } + + // When requesting binary data, IE6-9 will throw an exception + // on any attempt to access responseText (#11426) + try { + responses.text = xhr.responseText; + } catch( e ) { + } + + // Firefox throws an exception when accessing + // statusText for faulty cross-domain requests + try { + statusText = xhr.statusText; + } catch( e ) { + // We normalize with Webkit giving an empty statusText + statusText = ""; + } + + // Filter status for non standard behaviors + + // If the request is local and we have data: assume a success + // (success with no data won't get notified, that's the best we + // can do given current implementations) + if ( !status && s.isLocal && !s.crossDomain ) { + status = responses.text ? 200 : 404; + // IE - #1450: sometimes returns 1223 when it should be 204 + } else if ( status === 1223 ) { + status = 204; + } + } + } + } catch( firefoxAccessException ) { + if ( !isAbort ) { + complete( -1, firefoxAccessException ); + } + } + + // Call complete if needed + if ( responses ) { + complete( status, statusText, responses, responseHeaders ); + } + }; + + if ( !s.async ) { + // if we're in sync mode we fire the callback + callback(); + } else if ( xhr.readyState === 4 ) { + // (IE6 & IE7) if it's in cache and has been + // retrieved directly we need to fire the callback + setTimeout( callback, 0 ); + } else { + handle = ++xhrId; + if ( xhrOnUnloadAbort ) { + // Create the active xhrs callbacks list if needed + // and attach the unload handler + if ( !xhrCallbacks ) { + xhrCallbacks = {}; + jQuery( window ).unload( xhrOnUnloadAbort ); + } + // Add to list of active xhrs callbacks + xhrCallbacks[ handle ] = callback; + } + xhr.onreadystatechange = callback; + } + }, + + abort: function() { + if ( callback ) { + callback(0,1); + } + } + }; + } + }); +} +var fxNow, timerId, + rfxtypes = /^(?:toggle|show|hide)$/, + rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ), + rrun = /queueHooks$/, + animationPrefilters = [ defaultPrefilter ], + tweeners = { + "*": [function( prop, value ) { + var end, unit, + tween = this.createTween( prop, value ), + parts = rfxnum.exec( value ), + target = tween.cur(), + start = +target || 0, + scale = 1, + maxIterations = 20; + + if ( parts ) { + end = +parts[2]; + unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + + // We need to compute starting value + if ( unit !== "px" && start ) { + // Iteratively approximate from a nonzero starting point + // Prefer the current property, because this process will be trivial if it uses the same units + // Fallback to end or a simple constant + start = jQuery.css( tween.elem, prop, true ) || end || 1; + + do { + // If previous iteration zeroed out, double until we get *something* + // Use a string for doubling factor so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + start = start / scale; + jQuery.style( tween.elem, prop, start + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // And breaking the loop if scale is unchanged or perfect, or if we've just had enough + } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations ); + } + + tween.unit = unit; + tween.start = start; + // If a +=/-= token was provided, we're doing a relative animation + tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end; + } + return tween; + }] + }; + +// Animations created synchronously will run synchronously +function createFxNow() { + setTimeout(function() { + fxNow = undefined; + }, 0 ); + return ( fxNow = jQuery.now() ); +} + +function createTweens( animation, props ) { + jQuery.each( props, function( prop, value ) { + var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( collection[ index ].call( animation, prop, value ) ) { + + // we're done with this property + return; + } + } + }); +} + +function Animation( elem, properties, options ) { + var result, + index = 0, + tweenerIndex = 0, + length = animationPrefilters.length, + deferred = jQuery.Deferred().always( function() { + // don't match elem in the :animated selector + delete tick.elem; + }), + tick = function() { + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ]); + + if ( percent < 1 && length ) { + return remaining; + } else { + deferred.resolveWith( elem, [ animation ] ); + return false; + } + }, + animation = deferred.promise({ + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { specialEasing: {} }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end, easing ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + // if we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + + for ( ; index < length ; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // resolve when we played the last frame + // otherwise, reject + if ( gotoEnd ) { + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + }), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length ; index++ ) { + result = animationPrefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + return result; + } + } + + createTweens( animation, props ); + + if ( jQuery.isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + jQuery.fx.timer( + jQuery.extend( tick, { + anim: animation, + queue: animation.opts.queue, + elem: elem + }) + ); + + // attach callbacks from options + return animation.progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = jQuery.camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( jQuery.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // not quite $.extend, this wont overwrite keys already present. + // also - reusing 'index' from above because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweener: function( props, callback ) { + if ( jQuery.isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.split(" "); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length ; index++ ) { + prop = props[ index ]; + tweeners[ prop ] = tweeners[ prop ] || []; + tweeners[ prop ].unshift( callback ); + } + }, + + prefilter: function( callback, prepend ) { + if ( prepend ) { + animationPrefilters.unshift( callback ); + } else { + animationPrefilters.push( callback ); + } + } +}); + +function defaultPrefilter( elem, props, opts ) { + var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire, + anim = this, + style = elem.style, + orig = {}, + handled = [], + hidden = elem.nodeType && isHidden( elem ); + + // handle queue: false promises + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always(function() { + // doing this makes sure that the complete handler will be called + // before this completes + anim.always(function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + }); + }); + } + + // height/width overflow pass + if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE does not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height animated + if ( jQuery.css( elem, "display" ) === "inline" && + jQuery.css( elem, "float" ) === "none" ) { + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) { + style.display = "inline-block"; + + } else { + style.zoom = 1; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + if ( !jQuery.support.shrinkWrapBlocks ) { + anim.done(function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + }); + } + } + + + // show/hide pass + for ( index in props ) { + value = props[ index ]; + if ( rfxtypes.exec( value ) ) { + delete props[ index ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + continue; + } + handled.push( index ); + } + } + + length = handled.length; + if ( length ) { + dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} ); + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + + // store state if its toggle - enables .stop().toggle() to "reverse" + if ( toggle ) { + dataShow.hidden = !hidden; + } + if ( hidden ) { + jQuery( elem ).show(); + } else { + anim.done(function() { + jQuery( elem ).hide(); + }); + } + anim.done(function() { + var prop; + jQuery.removeData( elem, "fxshow", true ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + }); + for ( index = 0 ; index < length ; index++ ) { + prop = handled[ index ]; + tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 ); + orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop ); + + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = tween.start; + if ( hidden ) { + tween.end = tween.start; + tween.start = prop === "width" || prop === "height" ? 1 : 0; + } + } + } + } +} + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || "swing"; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + if ( tween.elem[ tween.prop ] != null && + (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) { + return tween.elem[ tween.prop ]; + } + + // passing any value as a 4th parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails + // so, simple values such as "10px" are parsed to Float. + // complex values such as "rotate(1rad)" are returned as is. + result = jQuery.css( tween.elem, tween.prop, false, "" ); + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + // use step hook for back compat - use cssHook if its there - use .style if its + // available and use plain properties where available + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Remove in 2.0 - this supports IE8's panic based approach +// to setting things on disconnected nodes + +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.each([ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" || + // special check for .toggle( handler, handler, ... ) + ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +}); + +jQuery.fn.extend({ + fadeTo: function( speed, to, easing, callback ) { + + // show any hidden elements after setting opacity to 0 + return this.filter( isHidden ).css( "opacity", 0 ).show() + + // animate to the value specified + .end().animate({ opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations resolve immediately + if ( empty ) { + anim.stop( true ); + } + }; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each(function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = jQuery._data( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // start the next in the queue if the last step wasn't forced + // timers currently will call their complete callbacks, which will dequeue + // but only if they were gotoEnd + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + }); + } +}); + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + attrs = { height: type }, + i = 0; + + // if we include width, step value is 1 to do all cssExpand values, + // if we don't include width, step value is 2 to skip over Left and Right + includeWidth = includeWidth? 1 : 0; + for( ; i < 4 ; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +// Generate shortcuts for custom animations +jQuery.each({ + slideDown: genFx("show"), + slideUp: genFx("hide"), + slideToggle: genFx("toggle"), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +}); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + + // normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p*Math.PI ) / 2; + } +}; + +jQuery.timers = []; +jQuery.fx = Tween.prototype.init; +jQuery.fx.tick = function() { + var timer, + timers = jQuery.timers, + i = 0; + + fxNow = jQuery.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + // Checks the timer has not already been removed + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + if ( timer() && jQuery.timers.push( timer ) && !timerId ) { + timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval ); + } +}; + +jQuery.fx.interval = 13; + +jQuery.fx.stop = function() { + clearInterval( timerId ); + timerId = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + // Default speed + _default: 400 +}; + +// Back Compat <1.8 extension point +jQuery.fx.step = {}; + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.animated = function( elem ) { + return jQuery.grep(jQuery.timers, function( fn ) { + return elem === fn.elem; + }).length; + }; +} +var rroot = /^(?:body|html)$/i; + +jQuery.fn.offset = function( options ) { + if ( arguments.length ) { + return options === undefined ? + this : + this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft, + box = { top: 0, left: 0 }, + elem = this[ 0 ], + doc = elem && elem.ownerDocument; + + if ( !doc ) { + return; + } + + if ( (body = doc.body) === elem ) { + return jQuery.offset.bodyOffset( elem ); + } + + docElem = doc.documentElement; + + // Make sure it's not a disconnected DOM node + if ( !jQuery.contains( docElem, elem ) ) { + return box; + } + + // If we don't have gBCR, just use 0,0 rather than error + // BlackBerry 5, iOS 3 (original iPhone) + if ( typeof elem.getBoundingClientRect !== "undefined" ) { + box = elem.getBoundingClientRect(); + } + win = getWindow( doc ); + clientTop = docElem.clientTop || body.clientTop || 0; + clientLeft = docElem.clientLeft || body.clientLeft || 0; + scrollTop = win.pageYOffset || docElem.scrollTop; + scrollLeft = win.pageXOffset || docElem.scrollLeft; + return { + top: box.top + scrollTop - clientTop, + left: box.left + scrollLeft - clientLeft + }; +}; + +jQuery.offset = { + + bodyOffset: function( body ) { + var top = body.offsetTop, + left = body.offsetLeft; + + if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) { + top += parseFloat( jQuery.css(body, "marginTop") ) || 0; + left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; + } + + return { top: top, left: left }; + }, + + setOffset: function( elem, options, i ) { + var position = jQuery.css( elem, "position" ); + + // set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + var curElem = jQuery( elem ), + curOffset = curElem.offset(), + curCSSTop = jQuery.css( elem, "top" ), + curCSSLeft = jQuery.css( elem, "left" ), + calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, + props = {}, curPosition = {}, curTop, curLeft; + + // need to be able to calculate position if either top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( jQuery.isFunction( options ) ) { + options = options.call( elem, i, curOffset ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + } else { + curElem.css( props ); + } + } +}; + + +jQuery.fn.extend({ + + position: function() { + if ( !this[0] ) { + return; + } + + var elem = this[0], + + // Get *real* offsetParent + offsetParent = this.offsetParent(), + + // Get correct offsets + offset = this.offset(), + parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; + offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; + + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; + parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; + }, + + offsetParent: function() { + return this.map(function() { + var offsetParent = this.offsetParent || document.body; + while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent || document.body; + }); + } +}); + + +// Create scrollLeft and scrollTop methods +jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { + var top = /Y/.test( prop ); + + jQuery.fn[ method ] = function( val ) { + return jQuery.access( this, function( elem, method, val ) { + var win = getWindow( elem ); + + if ( val === undefined ) { + return win ? (prop in win) ? win[ prop ] : + win.document.documentElement[ method ] : + elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : jQuery( win ).scrollLeft(), + top ? val : jQuery( win ).scrollTop() + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length, null ); + }; +}); + +function getWindow( elem ) { + return jQuery.isWindow( elem ) ? + elem : + elem.nodeType === 9 ? + elem.defaultView || elem.parentWindow : + false; +} +// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) { + // margin is only for outerHeight, outerWidth + jQuery.fn[ funcName ] = function( margin, value ) { + var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), + extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); + + return jQuery.access( this, function( elem, type, value ) { + var doc; + + if ( jQuery.isWindow( elem ) ) { + // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there + // isn't a whole lot we can do. See pull request at this URL for discussion: + // https://github.com/jquery/jquery/pull/764 + return elem.document.documentElement[ "client" + name ]; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + doc = elem.documentElement; + + // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest + // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it. + return Math.max( + elem.body[ "scroll" + name ], doc[ "scroll" + name ], + elem.body[ "offset" + name ], doc[ "offset" + name ], + doc[ "client" + name ] + ); + } + + return value === undefined ? + // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css( elem, type, value, extra ) : + + // Set width or height on the element + jQuery.style( elem, type, value, extra ); + }, type, chainable ? margin : undefined, chainable, null ); + }; + }); +}); +// Expose jQuery to the global object +window.jQuery = window.$ = jQuery; + +// Expose jQuery as an AMD module, but only for AMD loaders that +// understand the issues with loading multiple versions of jQuery +// in a page that all might call define(). The loader will indicate +// they have special allowances for multiple jQuery versions by +// specifying define.amd.jQuery = true. Register as a named module, +// since jQuery can be concatenated with other files that may use define, +// but not use a proper concatenation script that understands anonymous +// AMD modules. A named AMD is safest and most robust way to register. +// Lowercase jquery is used because AMD module names are derived from +// file names, and jQuery is normally delivered in a lowercase file name. +// Do this after creating the global so that if an AMD module wants to call +// noConflict to hide this version of jQuery, it will work. +if ( typeof define === "function" && define.amd && define.amd.jQuery ) { + define( "jquery", [], function () { return jQuery; } ); +} + +})( window ); diff --git a/terahz/templates/lib/jquery-3.4.1.min.js b/terahz/templates/lib/jquery-3.4.1.min.js new file mode 100644 index 0000000..a1c07fd --- /dev/null +++ b/terahz/templates/lib/jquery-3.4.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.4.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],E=C.document,r=Object.getPrototypeOf,s=t.slice,g=t.concat,u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.4.1",k=function(e,t){return new k.fn.init(e,t)},p=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function d(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp($),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+$),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ne=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(m.childNodes),m.childNodes),t[m.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&((e?e.ownerDocument||e:m)!==C&&T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!A[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&U.test(t)){(s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=k),o=(l=h(t)).length;while(o--)l[o]="#"+s+" "+xe(l[o]);c=l.join(","),f=ee.test(t)&&ye(e.parentNode)||e}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){A(t,!0)}finally{s===k&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[k]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:m;return r!==C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),m!==C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=k,!C.getElementsByName||!C.getElementsByName(k).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+k+"-]").length||v.push("~="),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+k+"+*").length||v.push(".#.+[+~]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",$)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e===C||e.ownerDocument===m&&y(m,e)?-1:t===C||t.ownerDocument===m&&y(m,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===C?-1:t===C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]===m?-1:s[r]===m?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if((e.ownerDocument||e)!==C&&T(e),d.matchesSelector&&E&&!A[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){A(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=p[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&p(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?k.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?k.grep(e,function(e){return e===n!==r}):"string"!=typeof n?k.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(k.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:L.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof k?t[0]:t,k.merge(this,k.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),D.test(r[1])&&k.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(k):k.makeArray(e,this)}).prototype=k.fn,q=k(E);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}k.fn.extend({has:function(e){var t=k(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"
","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?k.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;nx",y.noCloneChecked=!!me.cloneNode(!0).lastChild.defaultValue;var Te=/^key/,Ce=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ee=/^([^.]*)(?:\.(.+)|)/;function ke(){return!0}function Se(){return!1}function Ne(e,t){return e===function(){try{return E.activeElement}catch(e){}}()==("focus"===t)}function Ae(e,t,n,r,i,o){var a,s;if("object"==typeof t){for(s in"string"!=typeof n&&(r=r||n,n=void 0),t)Ae(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=Se;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return k().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=k.guid++)),e.each(function(){k.event.add(this,t,i,r,n)})}function De(e,i,o){o?(Q.set(e,i,!1),k.event.add(e,i,{namespace:!1,handler:function(e){var t,n,r=Q.get(this,i);if(1&e.isTrigger&&this[i]){if(r.length)(k.event.special[i]||{}).delegateType&&e.stopPropagation();else if(r=s.call(arguments),Q.set(this,i,r),t=o(this,i),this[i](),r!==(n=Q.get(this,i))||t?Q.set(this,i,!1):n={},r!==n)return e.stopImmediatePropagation(),e.preventDefault(),n.value}else r.length&&(Q.set(this,i,{value:k.event.trigger(k.extend(r[0],k.Event.prototype),r.slice(1),this)}),e.stopImmediatePropagation())}})):void 0===Q.get(e,i)&&k.event.add(e,i,ke)}k.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.get(t);if(v){n.handler&&(n=(o=n).handler,i=o.selector),i&&k.find.matchesSelector(ie,i),n.guid||(n.guid=k.guid++),(u=v.events)||(u=v.events={}),(a=v.handle)||(a=v.handle=function(e){return"undefined"!=typeof k&&k.event.triggered!==e.type?k.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(R)||[""]).length;while(l--)d=g=(s=Ee.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=k.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=k.event.special[d]||{},c=k.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&k.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(d,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),k.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,v=Q.hasData(e)&&Q.get(e);if(v&&(u=v.events)){l=(t=(t||"").match(R)||[""]).length;while(l--)if(d=g=(s=Ee.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d){f=k.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,v.handle)||k.removeEvent(e,d,v.handle),delete u[d])}else for(d in u)k.event.remove(e,d+t[l],n,r,!0);k.isEmptyObject(u)&&Q.remove(e,"handle events")}},dispatch:function(e){var t,n,r,i,o,a,s=k.event.fix(e),u=new Array(arguments.length),l=(Q.get(this,"events")||{})[s.type]||[],c=k.event.special[s.type]||{};for(u[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,qe=/\s*$/g;function Oe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&k(e).children("tbody")[0]||e}function Pe(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Re(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Me(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(Q.hasData(e)&&(o=Q.access(e),a=Q.set(t,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n")},clone:function(e,t,n){var r,i,o,a,s,u,l,c=e.cloneNode(!0),f=oe(e);if(!(y.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||k.isXMLDoc(e)))for(a=ve(c),r=0,i=(o=ve(e)).length;r").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Vt,Gt=[],Yt=/(=)\?(?=&|$)|\?\?/;k.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Gt.pop()||k.expando+"_"+kt++;return this[e]=!0,e}}),k.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Yt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Yt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Yt,"$1"+r):!1!==e.jsonp&&(e.url+=(St.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||k.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?k(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Gt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Vt=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Vt.childNodes.length),k.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=D.exec(e))?[t.createElement(i[1])]:(i=we([e],t,o),o&&o.length&&k(o).remove(),k.merge([],i.childNodes)));var r,i,o},k.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(k.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},k.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){k.fn[t]=function(e){return this.on(t,e)}}),k.expr.pseudos.animated=function(t){return k.grep(k.timers,function(e){return t===e.elem}).length},k.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=k.css(e,"position"),c=k(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=k.css(e,"top"),u=k.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,k.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},k.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){k.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===k.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===k.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=k(e).offset()).top+=k.css(e,"borderTopWidth",!0),i.left+=k.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-k.css(r,"marginTop",!0),left:t.left-i.left-k.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===k.css(e,"position"))e=e.offsetParent;return e||ie})}}),k.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;k.fn[t]=function(e){return _(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),k.each(["top","left"],function(e,n){k.cssHooks[n]=ze(y.pixelPosition,function(e,t){if(t)return t=_e(e,n),$e.test(t)?k(e).position()[n]+"px":t})}),k.each({Height:"height",Width:"width"},function(a,s){k.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){k.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return _(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?k.css(e,t,i):k.style(e,t,n,i)},s,n?e:void 0,n)}})}),k.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){k.fn[n]=function(e,t){return 0sC~7Ba z?^qF$;GgRE_diaObKVm-&-*<0e(vXf?tR}sFw$Y5<)$SeAz{$d)iNa^A;Ezh#!p!3z_JS7^L-t$ayHq^|#ck^1Lrye3|xdZ}lqO|?kLNzE;P zM5S<_cH)ysec|l*(%I>npW92pK=Z5~3)5R_GA3L{ForL?W5KOQG()a;E5s|(D*@ONM5zLy@{P6{sTxsM;xyc7h z&ZSDvFP)=D$iOW7?fnFML%IqJTI{@rn#f+`G~`jEP0CkDOT?B zGfdBfv%ouMP2HU^X3a=JH}aR(E4BA9sH@3w$4@c}tc%My2o52A<1kpsd8X+p)*wTe zpZN6`K;oNLHrE>cOY)Y=T|djLP@_0s4l)fAlzA}1VFlf{CQtJJUwmjrS;kOCUS@NO z&U6AV=)Vj!)u-6KzAGeMe1fhlIk&4Az5YL=V}|!jy5U1Iuvfj|ze;bEsT@0jF(RZe zQkccLT5-#r+tmE;KF!h*ZBuAo&e9lWTCAByOo7 zmiVUiHIc-fc>m8jR-C|VvdZ!TB%`5?A$uJ<<6qlv7itsjE!h*_y!DfxzXym{$ z{-OgvB!iKI$k7k=&tsJ(|KFDX{obQqrXr3L!_e2tk^;{PEc?%npZ__hhPVISk&A6v zO2jdQmX~UzlQO)ru1S>dAI{UeiP_*Bt#TB@=tqlrKGJe?yNTgp8_jYYc`?Ju=_2Z0 zvoB4)e310O%Bp_0CX6nr#@|sWn!~)twbPUXSmMDtSsqt zqC^9rdqh08i?b{H!A8nMLSE_&_02M|JNsHb!!h!_UdFoaxmNUV2G^02L<`T_;6_)w z`_UHpiSiXXiv<|hUuEhs>4^_4Ok=~v{0|C95OCPuz|DsFIKWi6|^d2G05ZE!jfg0yjs^#pPD7!?|J+VjEhChr zL$~J75r%HLL*=!YbGf~zRjVJ}@2#mfQtk@G-y2*TZu(#lqg@a+qQp&-v>^M+KEUF* zJ+oiYVZia%w_8D{IQoLKx~5NvS%I2t{LsT-;Y}Ey@zxF z!c@!EP0!h+SJRDE4-%cX07be@{8{4r)dmiSB7kucxDKUYh1sua}{ zs{}xJiBcs)hwHZ|2_+w}W=0gCHuW%0KKy&j%+rs3D3KFug3fq+Fi_fN;`4uN9g~#l zcsxJGwsS*2m7p04Zlj^+@f@Z!GnfM1gTNZ}K? zIR+A_!TOtiV*EYZTUgZCo6XhOJ>zNr4tXKb_pu>ZVrt|lc_Z-hzp#rutmdBLJLw8x ztY#PF^N`JvRoNQP*!DwfrpMo7bW#_Ki!DtlqwB7AxQ2g!_xNq!*+7&=z)Cj2euO5z&H+W#i&ziCFm=fqO~l=R!76vQO1=hM}R$E|j)GB1X_>cpRaB1<(k z(?vv09P^MbFb+0K;C-zInmC0I2*gFMsX5d-nRpbmU-D;dqVTtx$p4>X>EC_qFz=e> zV5`}_U12u`8B3#|X>iB19W=Z{)+yar7SxdJE{tB(?SHur{fayO*f0b<2{!Gc8#?W{ zp4M_FPR)=#)5 z@ZO@RfMwXigmD0}0=&81duGdVFm@fax3&iIU!LmosfQ1cdXZNMsc^ymhhh4MR7Pa4 zsqWjNJr>KSt9$OaS05nMTW?;nUM-3@U6D!0>$ zk>I;>${x+B40?y-ZsKj?c~sU@|Gyk>@N}NK<_~t;cE@$}&i0j8wxHLu#VQQ$SGj=2 zNSr2*t#P;82{*~F-)112BX^EdjpC*EJ&5HBcai-a_ffm^$L*gOCfcuvPGE>|5)USl zs9{>Aq^;WllD6ACcHh7C+0M~Pp!e5UGJf{*j0m26SF~6mk`ItetL-i4&;*4t9g3jf#m-N zEb(pCftVOInt|z98?|Lk*zZq4Gpv;h@@L^8C0wyWF>uTut@9Wfv*zXfA^-g|kX`-Y z4MCoY;G5ER6U5B?7u$1~-59=TzrGZ-X^P4kSgZ;z>xnC@)aj{_p)v)$f|qXRusJpe zs;@7|emup~1nbz~E2A7y(196kyY~HCxBz63SIhBJFZljt%h7eoOI{>rbORQ9*Zv)~ z=%pVaPeWuw3mRlWM1ZFPpkTSin>w-^g%8$7S>plbOLiP_rJ8%2g-W1_uK*WUgxv_t znBu7y{>`RTP^_4S)Qi2v&*T&2oTR?ywnQ9${+F%XyC0fcnveLrJ%9&ICoK-`3^C5U zF7(gu@hkX919#hF(_WHCLFU{pMaElx&V{f?tpU$ZCH4Xqra;X{Z)gns{lcRGP!4bY zDYrw8O?6_3k82Xaje(6kH^jHk2;}mam@gKV4iufDG0I%D<;i5(#t8$Q(?M7_$%0d| zS%(zW_(aG=4(NCK`H7BM69ER;-#AFHBXlH$29x@c>$Cie$;fwH2CwZ`)f;5_5c-@( zt=>-+Y)i{SCtG&%jqk4+I;+PV%vJu-u7G_FG90zI)vLnbi0cF&XyDyACQe<*uqgGu zZewK{B`W_%-BHY%MFqsz@u^|-7OR4fN7PV(uhuAtoP+QFwk{77DmIQDZjHy~t+rVD zq#-CnlvbJ=CGmREQ18BT$Dg)dElXxzh^?n3TiE(``Y&WbC1BzjAMy}anp`KM0y?ZeN%L4(~WOWrhuPLn=(;|A5Sv%B9^Hx zpY|QB`c6#&yeD2=-YYFTo74|g={;IHFL|Q#FM1pk!aIt`VfPgB&*m3vP1^}CUrPn9 zFEXFV^#-MB?wb#^faoJ-OrO!Pum<=*^;psaCMqa zxQTiKMoV<=VKw5;{e>Rs0>An(kf+%OlTra^p z^sO-2=^a4`wINe23ICmGg23eXec)dNIc=cBwSDnFfaT# zq?}%HHc0;L73P^oGcN}1$oXxNq9sxQY*RJprzpn$Kiy|;lM)Q zoxQ~?=Rf4qle=^};yNP4zyI6INA+&hU+y(f6=lZIx0cVP_*Txo$q27}kF^X_WH5zs zWE)%RA_{Rkn+D_|4xh4dL|-JtpU-Y;yjWqW-0hkOo<3QZ2;~W>4aKz-{$~=yMPp0# zvL?stTr@-WcITwMYZR@qcXz9wwE3Hf~b`tW4DFVl0R_M&E zW;3JBa#|HbawODF1_STl-y9eP#h{*9^H`sA)-cp_n-gmWVw#LJfA@3TRO4&Z8}`m> zmx`g{b7bVRvBn!1y~Ft#=9497r2aTD%#$&i9`w7{^IqO+hkSvbm@{f28wOUB2pW$D zx?SKsnDbLE6w-NL(3@9UyF23_IeL{q?|owzkTq~G{I4S%R7`!58cf&SD8Ju6)nLDn zyX5lHFTa4agDLfgUmX(X&9y1Ndm2Q4cnBh%t8E-xUyDT8B=Gm3AfOQUDsH;QN-jh#( zobW?8mcV2gK!bm+3l(ai3cCm}QPmUDM)J*>UJvx$i4_e+uREIy;jbQSj0zoQE{0;Z-UnGnneg<) zB^7`5f!|6(|E8adLg@LIX+-gO5S4g z>o$>{aoT)s3thc^QZ}NTZ;CESR#(sV10I-YA)QUEIHMgk@&hby&A|Za-*i~^tNvm^ zFyZuuk@7CPIv2nqhQFl|UtKhpv(3nuoo)(b3aNOVo1U3>@c`j6a?|KTiJQv!rB`eR4UJJjD1V zkoJPl$4Nznm{lEmo&LmQ=J!<^f3m!Bn8-uIhg;)td0Oi47i9YcW`G@#@B9P>XHYAtiek#15KOLb{^37+s&=8RA(A=I#yq#b+Xi4CQ+iDnhvXs6 zfe`L6^$!CgYKF)X+rtyFeno>8@zQ`+P6#I)x1K=QgBdLX@a+S6?}I0svXB(_NF7R* z*frT?w3R6|Bgwv#eM*m0XNfyecd(NCK#qt{lR3-&ht z)SfOIK1Unxz@&ohgM)j5X2z2$^%gZ#Uf$WSjsx+da-@Fhd}%+3n!(3axE%MP!lG3_jL&LYK>s}6`>xn7aQ>lyk*T^<#82B-LkcgS&&n;EqMwAc?S2Kc54UCt z3|ZVPT98xQN2?fJemNr-iHLqxfL`vAz-bJn_tW4J0}?|94LZ2u6x0gOze|TwscTG8Ks;8AGm{wYrhl9DoLZ6PM*MGE8v1&Io2ICz@TaOQv0VDCs@mlmjCGAyMK@sa&w3j-!lvN&Ajj%u#Y)=<%j#D(|b4zz~tGh_mpzyGK7CtP(!^Un=k(2^(S`CLsTN|HYg zT)*+nHJx?x*JXujTlrz^A-_xkLR-l2vcG=L7Y`H5vT6F^bN0#+3Px$FLwiwKQo0Db zJqPF%=#1>+)PZv*a2eoA_6fdB?|?sw-v0+?)%WiL=kJwqiRdVjw9X+PB)dw=pXC*F|9W z01Ud(i!z`KL>5@>mczep54qSB1t9EN|WR% zswM`DsBESI`-rl^&yAjUuV7f2k2RA|MB#@%G!W~%`*yTJcA_8c6u2IC``fzl-bBTI zYWSgKcOrx;KOSh!Bwj{hFu`#dzqqMOih*keJ`(y_-h)E4oop<4T_&O9{Yh3KARKcN6?E zzeeDfJeba#{y{PLAFmptNF%|LT<>}|{B%xu_LLCicpM+-X9`QI3L@4I=1U2p_ZDYe zSpVQnuvW|dFt@ex*k9Dw`V_5>FqQ2-9lI{a`=hr^q<(PrAtKHrO2{v~Bj=M9oj>g( zSr5wAt4+cabMlGz67z0vdD15(=Z%So9X5bh4Wp|>1<*N7W&PS!uo~4;-ff8rmIJ#; z=(He8y9kltynN$N3iG??j+O+#_=_)`|);l6zfKNrvB)oMF3p(ws3_5-dXZV&@4Pq3G={d(Bv($=V_Zt|@9rj$;%` zy491vTc~}w8MUOtFdkb2^j#0e*Shgm%`Fb3a0H6u^E=*m)Rzcf6jG6OmjmY83>&<9 z=7wUk?A%f*92@6s9M()>M^!l_(Y*j^QTHuqmm>D|GO#+8e$=BBS*$Z#z>Ri8CO`|bCsC;M;A`|dmnIBV+C%= zvVe!GNIOeSHq>(4WCQjU`y|$sEjQnK)ox)Kr*-GAcsQ7)Nek>OyJ zjRnkR_e+@L@rNIcKm88rYpXp!J(acMXftrLwT*?}EAIjv#~yy;q=3wJ-1Ns z{+wFoXlEkVYyCp3Mopn^bp47~gNE)IL&rQ5w!7Q$okO=?!;*JPH64loaT$8gl>b%a zc_GLS+1rKE@6#K_RuG(i&kmqhd8xrCn!5>~11*2?H4gXOM8O9IOuNhH?u953wKp%G zh_#-pNodpkQH~OT-3bc6&v|rppGy`>Ge>%^Uq5d=?5jzgJsr+0phrm>n~>3uY%|Xa zcIWedMWrn7wQzfKajo1qVXUw^SzWgX!tJE~+$SZ-XG2?wQX>wx<=NkkR*I3FGG27W z6)=#veIf`3?zy8@lEap_)Yim^%K7yz3R}i4%C@5C#I74LfOX&4g@QwVNx%1-_A~Tq zahbwxdg=vb69ZTb^Ga`Rk+#pz%qj3yk?89QUFL~vfeovT#<^=E*KuLQ2sS+7H0kq+ zKzt)_G^Pb-14-}2WoTMZ^M4o#3Dk_N`f_QOGB~xF#l@jPEZI)sQy`mT@UZH{6F|^o zL>g5Zt;z%Y;pK+W8?91XC)eKop}4v`;PhC|=51xzWp7{ytmR!Rnr8y-isv3s6m^Iv zU!T&w95k<1dk1SkHBsu83V50tZ)j-{9;b46&1_|lNYT1}_xn+GF=jo6#10!^4b +N)8Xue(x2NjX*wIn&0r0o7#UKaNV>Lp8$WKZKY+En0dRm^ z$V4HHAGeCCow!l=E3Wqe-#vif9Z!rAhmurY*cb{-uke_5!BDfp3j8+dtTybzmt6!36vZ+ZX!`uF@4afKKDxT}C^8#|Yw)?! z0MK~^{SpsL^4oh0I_S(>-;!Trd1h2e51vAi^L{A!aaptI88N;LuFv0m?3(h;9rOHq z7idc}dBu?YJ~$1$X$QB2J-ZNV079sby`}y!xuhM&gGGPKV{RdhpV+;{oo*(?U~K!4 zbE72Rr{H(QiGzQ$0DsbfD`D^fhifW`K{7Sjcg!UaYJQL54_goY(!=!HFuNaT4+zw0 zrcrPtt`18q1u;Gm$U;M6!7K-?C({F$b?06i;`-X@-1cN)Y z@V^!Q?YkiWna|N;=mqA#rALKO`-8ifx$t$MqgW3BBZEaAI!{IilS%WYQ!)K!B2neetGDY;JL^xsd1TqyJqw42U;(e zyr2VF89XU_!;X6car}8}zTq;)mPNCu zf&X1^cVLJ9ZT-=A&Vw?lCnsLxW^>K-T|*+dL|wZzuu^^76c9Vy`WaW$?P%Iv9DRC1 z1L4$IIc#u7)vrxZ^u->jLzM&fwlN#Q(r(JFUKr~3A$oapB)UU-p4{B#UxG9|6d~Tk zS2@R4MvT7)A@gu!j8+AH;KP*0X-SRf=L?!K1!zw%5=#cgzDj-VYFLiyTrX5L2hhOM zQLxl%%coNWoqf9`zjAs z_|u0y%DS8TUFrqDTuvxhnpm*$BjT@BX~6lP@t$GDFYI~ zRe<*r*a>3G2UCu@RJff|9BU9!C%&o~+3P{6%6V@K?=>0TgF?Rl-R;cpq_Qqe5WKs) z89%0TIS>y&cqK2WkuZNfN4(%PPhJjXQHhX<13LA^UVo_J>3AYhm}+kI(}*+zP!RrY--HqmQ&kkR{ z*FLigUzwSbg1~WZaRenz%qT|zOk)ss!x6R(dot^rK(ze2gC`JiA%&G$d(gJsj^x8{ zzux@57SWiS$VykUEl#gt2>+{w^@w47s3F`$o=F#!7VSE5eZ#Wb-($}Zyeh()1besC z%6u+&xDY`zs))j}ir;c-=k#Adf&`B*n418QC*dkP>i8-i!e|r0<8|epiT$4j{*$mV z8;2s(_7WOM%9EjjZd&ZbYiv*VEXYJ_bbwN$F~=so+s&zNfp21rP3>!>Z(gh~IQj z45jeQB{wbVmhGC=5kbR>Q|-QfVLkNB?_0%UaL^w2F0Wmo$quLH?1W<`1@xOsM2zQK zse=QrRSHqM9PLnCQokxFOvEIdMdkI=jRBk^<&*@)Ybtxng6kJfyCDb(u?$HnpkY#N z;GD2md}rWP&1Bm7CF9pnF{k&0XmU;?|KH&;eMY5GsSKJ5HgZ3^5p6rb?XhjTu+tF!O9 zrpZ=7HU4mAG0}hG%@;W-ghOJGZ7;&bf9^>TXbDlNI(s-d^NCP2)Fj#;q(5|4HFInc zhA3DBD3?k2a@<(TZ48!&zvm`vjmy}0O&k{?O367!!wmjcU0u?4KLsEDDJc#AWL5Fi zv7O8e47$aC#8ZG3{)i0ZQG48wul^-Si}R@JiBnBR@xaWE+H0{LdwN(??5+Fi6;2vI zUU7NCTR%ZK;{krL5X52~djY5>MlS_11yy_8`$=lRt}TTLDf6)gbZLuTm64FW@#(L` zsO31Cxz*N~VQ`r*_~^toa4+lg!&|n?BCto71!4(meD+1Iq}N_5i8QUx-8b6UZsYNg z@IciH9yA2L%Z>83{-(mvvto5-=h2HO_uEyQ{oou|w{*En_NE6Aq0?kfkap%yy3~H; zsrq<#UOuJKi3QN+Jo^^KHOc4CC2Co(d$9x3Zj^18%ATg}7L86UwsSJpNzEQ1D@7_N zS1XzCZI_E^PG_y#=|j$j!80{a&}u89GH6Y%F6tL{f**GY((4}m>eVQo5zQkzUBv3H z6_-%EH2nC^NB-V32wlLK)bWhRsE|h_ldYi61m z(u&n@0*|e{&}Z=V9Sf`7MB82C^4e}Xr3{O%y{U^P#;@N(L>fy60@ z+oSLBe9cl5DCxRzzb(&b#QJ;?u8~H=H8VQOIq_a7{fN#;qZr1KYQLshy=b_V4IX65 z9Z^;os`PQ{nJc1v+cOfHh%w1^x4jX2q7F^`PLcqb=W`m|#$|zfr{T^@LO_o3Aq!R}-Ge!vtBTNV>^}e3l`!u+qx~a+M5(XM& zUs}0bL039a4H6m#XBj|s+~DEL7Ht`W>b;#2p$l0@>#Hp}uWOoXA!K4HNu@F0wb|a2 z^9iW*YhvXSj@QEoQ8>mie|8BTsKWjV|MsOeJ2Y7neSgXBN2?_XAQ^R&@sDxB+RW#m zp@3Kjb6@OJWz__ihEnO%Yi_gDP|8>qm81Dq*vjs5;rBP~Qd82WE)P9fFX+993%N^* z@H77i=J{Q?zX#Adm9}h`% zXyZc&YmV3aIvcf#%Oq3N%pn5#!j*~-;=_4g%;>>Z7u*4+VVELzv|!Eg^0r2TYM<4J zB9QiL9*t?8(+xJmg1433=5oSzW#Lo7zf78;AKW1QGb=L%;+iMYn*?65Ai9854;yxV zB{5Lg4y)epW#oFXFZeMU7FN4HZNQ z&@%&VumX9D-nK@*z0n_Ri(t==HQrsVP1#lqco&P3<o_-gri@7rDzsx?GY$@N=r`yHO1Wji%tUeX`~?X%@o1sqZKLD zux;Z;i^025ZFHTl_n?rpuHD=vOZ-Ib94mj8OY_Qqb~X;L1ENi91jc`3~4TTM)S9y_U$gsF0b^YpvfsE zA21jJBsMCm1^0J!z8Ndfw|~03ySUNmcOldyP!m;UcZUmxGA-pb(l|=;peg%AFMJ)( zc*Nrj2tEgY^^@iLg;!6yA%k%FPBugSInbWQs^y>YiTr5a4Tn#Nj#=u1;A+ILmz11m^@V!GGm+BITwT# zK8U=`58xC)TTBv>bMfP*Uv<6RSa?X*!3@3{x%rGCLSdyEd$*t-6U6kwpx_Olz!pcgh1%!bQx{*1gv^w zJ|Xg(IJqg8Cd#l~_CER%&6Bqg^PHJuJ^Y7{CZ#?QRyih%tZkJ5_L^ComHxBwvlzy5F zKyuZP)w3O}xC3#eiaKi+KHx+^&vc#~?J3rP>(1Iv>bVZ}lde`r7giOw)*bl8JG!!;QD?31}^VL6& z>~F^=qS}w`ejGUL;Asd`p#j z99LZ!OGQbl|4{PsUx=q3lI(C4rkQ?K)KlsJ=-pu8p<=dp4~JC8{(g(T6-lBSJ67l! zs4BpCmSmw9%QF*02JL;Q*9)B$fbsQV%dwOCyW6zT!Oa4oxXWh!&PsA$v;nA^Cp6dm zKl?p<9pl0@#3q;h*%QzS_E9-w6l4;+>DlXV3dcx+cMbCqUexgBff-S8`Gc|yN)nX! zre+!n`O_A2bRnA#+7pz*kOob3cXl;!j>6;<&B;H6zB9P#WV`KOY;NA|;XMeIVVZ>Q?zM0+8lPG4^}(?-Du zL@8DdLG%>BgRp{ z@_KR`O-RD#)}W7o#^>IcF zM(Yz!S^#ABVN*b%y_VsR$il0|{garMpJ7Wn;1w-!gLbM6u%v8;bBE{`h6)}-V^#;a zE%IEkeQwfp(x)N$B@Hxj6|9e_&2%BPF%J%si80x>By zUOy=fA8XA(BP)g@SONz90KwO9rv#OhEpLk`w8I58X6IiM^Y3=tA+h{UWv81g>B%rS z4X9opd@b!=SOU7R-#^OapYHJpdMzqS?K`BNY10pso;6wMqZMSC`=_wD5$RN=YGVpB z077190`emt?rm6{8UFe%cH(wL66Ms~mjw-x`B)#j~=g?O1p?|WY`*&%Ezdv{KJhvD5=`B?d{cC zkW^0Ho%c_RW5tOxKLNn;cIOs~xf??~l|23IoEq%?jVD-baMc7f-J0d}^r$Hli^8dg zA5dadg9Ldzf0Z-pLMI8H2Fb|{afgfS1S;4`)Lp+II@m zfnf%qztP%yxzd{#`g61Xnd4xELtBwd!{SltbCqOyhP>5@7wCr+lL`bGrpoXG^O@T| z2*UYjSX*z~Ie^!;Kn)jm{DhaCrxN~%Km$HGX3x_4_Ji9M{BGtq7?Bh3C zzXIm3nv%NIf5K%${s_~A?=(@a5`1f!YiumPsc+S3OYq(OJ|9$mf07rjf^8w>ex|yD zQ8nvFr%C`h&6bw$Z$5t+G}I~b^XZMmdn~|(S6^Pfjb7j4PW`l=`>_a+XHdK65nKA< zy{Xak^AttUp*p%vlB>y6WRwmh=w0)}_-|G#`{6-G=oh@xBhT&K|8yqas(7CzKj(q# z6F@yH$I)qEAHuVksc|Sv6QI44tLy$He#hD=Aq}M-^M-e8+8|;{C!~4&zB#D;{A3R+08PabZmKnYM z1kmy#i6-tIkxet%m$T!D&EJBHs%c=teqyE7k2tHvYMy61VN-=99L{wKk0{;&?->fJ z8UUdW_884pnnWXL9d^@4Lq>+sS_QppZzL)zCh8vG#I#-*L-@6bqUWL$%9w5d!$iLv zq6XH!K~U4L$+%bDGkx>n!yEYl@KMVjKJ6x(wR62len6$X#tRs9!E6VR0M7a|ceK$X z)e2(%Q;pc)uLc?{RoI}c+;CeI6C+u7;JvsBsAme>-BfDSEe{{ge9o2%jw$E@4v&aM zuNq;|zJQely3D@RAtB41?vWm*p#a`Ys!YonT!qYuR6n1e=i6O*DdihzZ1D3f8v5C1 zb6@a3oZ5I-WjEOT>LGq$@}(&r58(UIBU{EAPG^S^N_4B|K;akv4v7NA!`u$9k_{Rb z4ekfVkxoi|54VT{NaHv4__c^Dslm>H*v$^c)y@s!6@{ z#nPV%?^%1Jz9H>`?~4joq=6F+5>l0(GxUEbXXzSZ=ihFpdM!TBf;Ul4Mb`im>Le;v zUY;X@j*lv5!K0kQ$(t1&H#;yTJ2Wgn+I~&}r~;XkoZc`fSHbr6_U-qOqVtHWwqobK zJ4cl7WLUC_&?A8yN0#^&Vz=o|*UrIJ<4;w@Zn%2I-F?e2?;uOm6dlectWRYClY~x- z*^j!P;&MB27o4!FJ4T9qfu8UplIEI@*8y6+`*J@JIbmlGK8x1^%TYfL?|5GsHa1uP z>-l5O+~inN%o}0X2P-~Uy6d8L>*GNQ_9Nz2#DbpModfU93NUZ5d$_iJA07Z5LAoprG3Ck-P$t7nq@=xWor;GH3i z^&YIh@tb{R5L-2tl@-k6J2_KyDZJR0V$SmW1DV{1y$MMt(qEt4U88eDfX#0CscFMX zzlYX^t<12UN@Gdl(1ZTqt;Mx&k=U*2K&JKwmo$DLWGE+WcVuw;o0KE1!S16xTd%V+ zO5R-Dtps+;HOqPU66U_z4itKc!6I-7sTK7Wx#7kF2Hw7VPkpb^#Dq>fSb>{9NA-=} z(frJ(Kpyy^l=h7>(eM1DVOqQ~3{p<0-x%jKbq5f1HXcylgM1By?s%$-9E`2Hf1*A( zucL+zpI#a&Ol0*6er^+`|6ym_qQlOW*6YH(NR7xEZt8i@opxE(diH;!sMhP|HzdO9 zi)XD=!XC@YYxqn@K>Oa&d_;0!x~5YLagYo;mKkKpzwE{h?n8%k^O`E7HywqZ3=6CSn&fYMN{@+bS0 zsEXpq$+D_dWvT*GG{vWwB1fE~kYw4l=Loz%;%VY=5DzN=!~NmKc54!$YACJ@WYdKH1~xA8Tc}%Xnp%m())LQ8ij#D~ru9GD zZU)6+_ur`fk@BckygcjN?k>EzckCNco|v=!-0*IsZz3{~tU%tOVXr{VpV0g{S5W_c z6dA8eN@wTSirQ-A;TJGRFptf`7db>OdOpHn(?-1#(S<44d?C+$EU(^3_`F{%c0PTy ze&t4~*iew$IhM#143dL+m_T9Z9g5?hxTAzV-!hXk02I}Ua8EgX*I!7JKYR3zk$w~V zsc*Z9&s*3VQS$z;(~7eFrZQ60^IqvDpKNmwxqFMDR*I&&$9tv;Y+wbh(Qg6~~&bb$x7pz?nC9RDHs+&v2cfzynxd3ji+p zr0GH*?J-SrG0S-xE^uqL`GU-957TJj?wv%i-=Nr_d<6XFMF+qL6bGC-)eGyL*8S%y^B1l+ILe% z$&&^<5Siq;b!o}IRh{cic6NQMmb$2g9C{(l{UXcCl$;#V(eSCA?pX0Z&7)FQrhW0< z>^F*w1QYrmnA&Q&y$!teGgL)fT->UyDPBllyx0FqRJ80EP%=9&Z}JxwbA9O#mF`pS zuV@wK)2hv{_Lg@9QFT4)MaL2ENvMBCgQQj^^eY+o8^w-Iu!fo6KS(psG$R~#!$WJ& zo3B*1D}^!j7%T3@jyUD=a28Kr+@72->9EAE;iI9&qr!&VJPWbd;4I!D>)zm&ZsgCR ze#dV0;wnNGYNlcgXlLC$Cgyv5*6M)UKM)Jr!#%Wz?wJwkV&kud@@YAzwJJne{m+fE znOgF>md0G)WCc0b2SDL$KhAgubMtOvjh{=k#=%d1C7T5}Z;5;eKA8F3AmMKN_4nJr zTKjV^;{RRGXsyeJ;5q7^&%HAcPCK+T#|<1~{0h!xBKi_s4ltHxxOq6}_;3-Bs_@Dy z#!X=+hwAjiFV_SAd5^Dkz68w)WL<0Gy5jHzVEb{Au}ehL{tkF^?*8e(72kl(7`<8969#+#*H5xQ zx4i*O)0i5RBV1jgg{A$9&XLBIJ&?h6e}{Q$*7W~W0;{CSgCgA~5msv_C8EcjxtHGDqj>2y@*x^@<(tM) zaQBjcGD8;2i>v>e1@LF}2mGBBi%c{DG*O7sradM;-gxVAr8>uw(vL(xR&ZG)bW%D9 zt9Ja1|M+zt^!ETx^=P;Ci~kmvgyKY9@qyCf5&pRRQZg44}^)?0qif0Z5hBejd1 z4$>5Qw2?D^d7mqXsmgB`dg{LuhrWVeG8X2O;a;PNg`co`aJS8yIWMRGlA7Gl|EvyFKNquMP?|}b ztn7v7_S7zaU-dq_#U$gqV!JiuXZ#H%gfA|7^kdHN3CUh1%b6YiXNe2j-tR*Nj$Ov}u@#y(5v&mk7SjESo^gOQq2?Z;wL&AX{> zs2oe!(N^=&%FE4a?!okAlS+FQ{iGYq*jvkl-BWr#sJAN2C?!ocHX7(3#hHmuvDr}# zCjvGPQlbGGn^s(A`=j1cy?d$<1;BT?ZX{0eLWg5Q8`0IzcwQqt%>pqy1_=cWZj)7f z^LwkXXi=cUw;_$6^Ti^_#Z$lF-c(~8f%a(b^uCd@r(oeZ%1h2|n)#Ye<&$C5*G9q& z1>~Og6%5k(5!5q)0t`S&9ADucKy?{gGC?QN@!8oCL(|{) zJ^~UVAt;EHv?AS&gn~+liZr8<2FWo-r<6$7NCD~Y4Ulf>-jEtG7&&6ZZ@xd@-~NHu z-M#0Y=Q+nV9%+ zIvqjE9kae$S#-FijCL8n(3(m|6wwq`xgtco&6egJq^h``UJpuioq?TN_JQH%+0{qa zqgP0NRMSx532HiWb#(#GH8|KES+qY~If*>DEX{V?eie5#ol+n74cx=2C1TObVg%eX z6Lnh3`J8r=6|xy_`=|Qa4_r}Kp*lo)Bk#8$wX#BPJ5g{)ys-XV%8Kf|*LOvdd}ig7 zI~dGjIrrG!1(TM-x$&RuR%REOdDp1q|H>lbpWfmT5iU0YGt4WRy}sa*1qPjsk4EV{ zPp&z7;60zb8eVsKEe5j~p)}y7V**$azw2Oj3Rqy+#|=8M#V!nf*2)n@rjC*{};porpd_GO{mgxCJV+E?H4ERCAksbj&D@SZN2g zZ!hdNtS*sx7MeiW?3GMvSAwCxc8u=H2Vxi;zqJ`*J0!lsoSq!^;511XpcLr3$q^FPE&`WRkfc7ON(z0LFW8wtKLcI$C-Xd75S z)nKjbBe((!5L(j_7uv{1uSdz*FFs{Iv8=Mj!HG)g=NRQ7P=ys49sl3UQUJvXxiie| zVQIa+!BBO^J^7&l^x*8&w+TnKD_2s#gXd|7@*R9ys1W7mHsI3cy zQlIB4lA<{fs_~{fUmm)z#e*=WMP~Ly4|SBvqJ%fH$f_lhRwR4iU7B6WtWi)K7vC4o zhWgEkQ3Ad}NXQK@QXAoMd-x&(dhyhv+(7JaO(~`Iu44U}cnS_=yVsm7_F2eCPs2Iav?f3H9H&l2>K?$~RdwroZ{5Y&&=yZHmltom6}*)Z~A( zY}i0VaB?4$L3%DLG_Q4FlZ(bK`5o^rDQtG(nYXhrI81R`HdCRRQTmQK;&SqnXlnO% z#UZcbz@y(Y%9tfHPxUU~=kdX}|zL{iO^28fq1!3N>HIjZs&;CcE?Q{GW`@QDP;v+wh9NGr*M z71ooT$<7^u-BBU#abS!%?aw)jj>27+zSK20@g`Z9vE4f*Y~vvc^5b##Spy;xJJFBL zviUl*ftApsM@8A^U*KRC@KrLZ8Qn{f-EK?en?b&w7Jv{y$>*t(nt0v5f09UVj9esW zE&hfxg@?%kDw64VDY!TX)xcWk{GRsBpyT}<8)trO3kT+6)ISwM?!$vS+tB?0HOy$)8d%Kq>yT7Gw;W5Xz`q&evqHK4{;P8q*X*roN*_6MM z8zHSXmuK32eHRcKt<(CDd6MSIaiB+?naAC zy~6eSHp`O|8?z8LQ=UP6b$#@xa=9d+-QvdW8ugJy7=*dP>!aE*_Ddm{oyNfB2>n^w zdHz8E5)TS^d0%KL44Y5E8rPQ*Tp1pl(QdDKMgCZSfA1dQoFkXPdMVy@W>1;jC=%pX zcZOa}N_Oq^2$lp+z2Ks|nC{TaF`Cx+=U%!y?{@4cO2Ez4h%ty-FT6;h2k0+9EMZPM z4?()qi3#J*N36FPKTc`*EOO{_nivHsJ)#3qqdbVy42M}Mk zUo|6ZSYTk5OXoi$Pj6TL4%xfgcHxeBr1_Si0vKP(t%2QLAfbgZ$@0q5HO9y%Yi2DH zp3AwlFTzVaKH5{3!@;vI9I5kKXjn6%6uh5Csgy}+egVCwwKamCH;qxMcSeSKqhTe3 zNSx2xOygDZ0Tx9E+Csr+q%k ze+xda-wOowA?1E+hu=4_z9$MYySTOJj%9FNtG94-I{q!fQaviobi6dGJqjFgkocM( z(e9kQIWp*d9x0M+I8&v4-PZScriqpA%By`NxqSSmamN}dohJO}ke$>l!UJ1sD_?N_Qjv1RXi7sz5zKP#V0bs5~vR!TzvPZEonlhlrGN?Ac7~r}I ztyRHg(k%;s5?0C#GGyPPaq+j#Kk#y14!Ui%{CERCI?tg43C$};NpL%)#{QQ_VU8sf zzu)4D6V6)o zhaAXj6m*~N2Fpqz^L6eX!Ge_%1ZkS-b^{T0hR+4HDcS~KM$fs@tTE1PO(Z91Ax6x* zv~P$4FMo16G@nYh9UAQY-JdMHZirq2u|A=77;^^baw?>rH#$uwCTOw1mXdK|nuo>4 z)zKIoCBR=SOU|=R2;KNt)eje<#TuxRUdwpJXUGCYLh@;H7`AC>Gu;Y-{#MYeUtyi zb9Y?6aY^es1_G64fG{9hP?hl9=S(t_xv#<9cx@?4<3kcPyxVv`fPOv z6eiS$2y$POpT<*WmSAfhCp6IP zo22x}4>Qi*%6!hiy;N|*&ev{=Y*t*M-&HhYc7QO8rsj5C#)p-LXVXW_L+q5$S2{Y= z_khIoj&PBi7$VAD<%Gf*rJD+q4lQaz8F7IG?78*TB`Cnx^=L75S*sr4HeP9A*tcgU zjs&M#)yvu2`72bJzT}iZ`xTis_i4hF7c%BgGm5FN!b! z7TFx%$FS%S%KBZ}TIb{shplJCqKuDQjM$eOZ5Mgb)i@iHu^vljSy_nTj}C6wW>EaG zi{ISdm+jAgFExCix!Qx-Ev_va)O%MTWH?;PWRcucF=fO5y02@kHjdlvk9_0CHLVO9 z9>*TX5RJ6rm2*y@zIxWTxl`X*739o*yW)|RoJMrD`z?@*&i{c+Qc0fDx~kcQ(z=b7 zX=x4v<>NUB*b8m-?6?9^%dK+L!1I{vV(Mes_qB_0q3G|sQNN0f1}6r8e$ttKm}mlecA33F zcHDn${u|2etRSjzR(v=Fx7VpR$q)-iWkg%EY&MeL#)9jtnvp5lVs6ZR%&w~Nd;nD0 zg1^OG;0d3^u5umKrKR$T#xd7QLxa*$Fhvz|^&0ebQ^F1bUjiM|wcTzCC<@#Jb~tsg zmn8o?7dBqtL1p1x>6TuN zC(=omx8qgXwmEBQ(pKW&OHWqsIWNg4Nn#zE&Si!rCBA-H^5NNjS4rr0Y^ZblMd%s) z;v0^{O>Fz6@ctq>zg}Y$Q$Re%OH1WY7SuGXLJ?U4zjW1RK8-=(UyB>8McVw49{`(*Ii|%9v zJNcf!x!#)oRUcT85#ch`8^|#e`Uva$n{MOwO>_jB{%zSnbDZ-$9St`QwDw+O@{5c6 zw-!hlY(KQuas1K8p3WlJjV3#B3V@#-A*>LrnI;@{=q>gO%8{i~oLv|kjC;IOwR88^}yI@Eir{I$Ld96r5$#9-|}CeM02 z$bDX5Y5Zp|Xq2Sts~@!&zb zKRY&4ksLTf<*NhfG{Yu2G2^cgwc_S}j2~{#j()x?eSCJ*-KKiFG@1P;f9>z{_-fiDQ8NEt$TrgQSh| zPI>p*hpMigLNCN|W+T*!svZ+qm@FLm8xia@0;;pGTnuv)m`j>yXLDS|HCegm2h$}c zNz+O_ThoP^tbuO*F61PN5^HZ;9~ChR^KX&AC(0ydjQ#i4N9E-uJ~834_cE;falOAY zfve^&2oqvtvj)9UqQwfLXNyk3*z~y4yqXeI=S@K2b#jg+D02P!SWD!*uX;B8UOU1e zs(yQ}nY`Rkf?T%ZPDiPL|1cKqN4Jt{Op!-tWfY3*%wlhR^ZojqLm~ETUyhdFXt>!% zzP}MMJPJx|TmnAcTM(lZbZ&FZ6%g(85~_s8Tx@F`QPwOE+EU0gvE)}~_akYn??TTh zi7M`L{il*V2zWQ338ju0LO)RvqDO_VwI&{UBWK9De}z87DB@%y_Dkl02(YKVRnJ5a zG_6P@jp;*^99g^eRm>fPpGXW-gIrX$UnOFFF0k2Bhcv;LSTjA61TIRXPw8gU2Z$~ zys&BVUGc)wH*MQ(=+bKB!LQ zw5~kzB!PJtl&H$0@EwX-cKC@#6Wgh)wXYzP&1f!0xo;b0o4s!#0mw#-y-(>4)4VHM z42bI=rM_z?@N40pX-62Hb^lb97=73Y)OB`xx>?b682k;od*_4$(@8W+#*Q%ym0J+? z=IrVG7KPi}9~sUH{2gN3Ew0at-CKO=5TV_(5Sv%8IO!n%0@?oJoX-3f%#tx#p39l5 zt55H|h4olSkg9qKwL{6rDQRlZZ;$ftSJ=pZxQY8OMw>;-)o6$t28)c%r~UYmmvmHo z(OS6^lzoB+su%0%W`OwL)Mp$|1tV67{mD=tOu=@PbX4X^?D-#`c7Kh=_aRz7Z>kkH zjoBPg14zOK)J|Q$$(0t*fnvMiB6_rSiaDECL2sV&8dVh}+solNbHmI=>%Mw}kS(5| z#z(`21^IfBn~|~Ry+XR<{q0M`SgS_4#(P&83z7|{z!ur|!r6d54>1IR$tlZmZ@e$| zxJ*G|5Gk;FlF%y%+Wg(7>N7%4JXvFIdy=3}z^CKsstdm*AXIBXCh?!ny=hwFeyduhgU0;Cp`69mE86G+98|8{2cmQ zoOV9JSLO`17Y1wpRT5-h=@V-Mxe`T>FCYKW#>oo5lF-M8pR6dESSIigy#?)M`%$_R(=Ke~j#pF3F2z93LIX&Nq8l?=OMipiwz)b`g3x zp9XEUQLD)IGF#ZF2GO)Fj~cA3*Ti98uXe{dT56BA4^A%AAjjXj!-4w=j{2Lho(s>I z2-AMyn(TgvpIrA_$%5N`{o;tyf#ab`M$A8ctPh zpK~DcqKchr!N9$spP%wqCCtZs5IkzKiAMH__>n3r1dNTm03Osf0NWA4@TgTk;k_^q zwSbTnK|<>45|7;AzDTG>Ad-H&Dw^ueN5vx`_TDQ*^L1u5Xja}x9?aj>t4(b!=Sxvj zGZBhZ^nc(xgLRqZ(H!yNe^Er(G{nLFpiy2j0@+neutH63MM}taMu?9$MdTu7zsY>P zyIPQ2{)om++-wZ$hVhYIt0nt5oi$fWa_%E`PRm>8pr8HAtJQOVjYGVI^r3SL=Rw=) z5AUcoKov*CMJ1mrq}BY(9$%;brnsaxJfURd?ic2EX*HNZ4EW6(s zOGA6RQxOkXbLNbaWw#L=xMV0I&^BQ&;pAm0^9Z=Bp9WqxB1SnKo$ zugq00qUl2|VilCkM?EuEqDJRi$|h81BpOE%yJBPQwljE;IO$!gPHw7*B^r|H$ESGe zW+Sp!iPn-Oh8BT6GX+vG6#m#`@;zc7VWsb1{Js4R?+0Ztbi>WtC|w=F8=!Js0fwR& z)W+OUefxEJ;MAhMDkbAHMKd#@dt{WauP8$^7c4oi?MSMA&Iv7du2L6L)6;MN*_N%G z%VnX}s}CrY+iZ1A9&3MIu)`3SWOH%0>pH3*Cxfx+-eOMmIdrY!gQ#Z>1tem-1V%CZsutG#lL=c6G@NlBIU95U(f?J)GiC#*NmVB%fm52x7W?Qd=KxL zEzzGn`UsNm>+Oe_>+1~*5|!BVQ@@@$e`Oo(6AlcS;6##6B9 z3`s`PNK!^i<3p z;&(2{=-C5*#P#-EhLixVgI%G=!(rf~GqICmneXu~igUL=R&&NkU|V4g+s9{Rjy)}O z^DXneesMFWt`n)BWmzzl*b{n(Nxk{7D9AZx5!V~IBq>AkvN8%aB(E!G@nNDH|ko1_5apQK90v| zfF-5#c)jS;j88^)OE2OX_!9ZM!ocEbLg7$G^z^!>fhM5-aXZk*VrNfm@ry z+}iybl>x|0re{2z?3AN%=?nEB5nSDbS}N61#@x_%Z`Yu##KZ_88wz&^vvK5*n8T;U zYXEka>+Y%I-`16Dh#qO{PoR2j#S3ABtF1Bf`HAUNzrT#g(}T7X0W|Eg0u4O*|zZSrl+Jp4p?br#nzf&O0CqU~m zk_C?DHHC}z(LIPz@4G$~sixx$vg=o1_(drIQfW+1^&J;#kn=Tzvz48@LSLJIO2$l2 z0IJ*vc8bAGhsmyLnwk%}PIhu<7~6%3DCB<&jkQU!SRhBC23%8d3YXboNP-dcn|gLR z=kLDp&1RE@S*c$kwFFR4rk%etGGi)scGOc}P#5CzP^|+E$vKfEt$LvR&~dc!O-tIf z8c6FYRWmnLUY@*n9cEHG-`2BXU8!)wT_Y3;&ME|!DFt|E?U>&O1U}`W}(Fb+G1jq)hmf1*;}rq6hZqww_B9+sqYx?YH3#6(s|%5UVBloY$!5~ zneRg=FNrE#g0Em0=Owp3|3_4ILN%4iTgpRv9x(n%RVh9{v%iW!pBfUOsJmONslR`te%Uv%a-wAg6xoCrBUpPPtS#Ezxtyc8!q@^5=Tv?YA18&*TG|64a~8#ud{@xkO0Zyh7_9K(cg`TGHuOh zIM9vW33hW)>=>(HLlQ=Tc8d5j^ut?B?cKXB4UtjT;j_LL$0mzjH%XVAx`|dwk|=^> z03eze4N_hcnYe&lIVGd7`1)p6Q~n;0eO_63VdCzvo%b%bbIz4OYz$OG2&m)Jp)l5A z(xK63&`Djd9VFmc{JZGz1(GmQ3eBc?nxO*ths3OYu0jFog;u@z6>a5ln4Uq1UWs^L z_^NM9*6!Vhr=i!4{*h2Q+xNNUl|Fo8V)Tv6a(9Bm=g17XDcVU9yoNtRNJ5oZ+m2^+ zCEeDSwHNJ{3z@B#JorC+OrR%~qfDDp{IJ(6Kjy#EM01&^DuDd1ttETO&)kHzLM`fY zELjVZv|(pHs6UqZ_@iTp%7saVWy#{hh)0?;KqboA?7F2~5{dGcX_zZ@b~ExnE>>ox z<4Zv*X#4XE+lLK@)vBc%BGDIocX_ux&m|}%>(9jQsI!j$@0_#>@*U_>NaGDa$*td!Mam9;bzLWdp?+3hF>q|l2m7=T!o_Np$B|F!fk&=2k*B@O5TuHv2<|yo4oAJmSL&~dZ^*ld7)|O&`(4-0e3K6;$uu_nc>xIVXseEKO;~T*>aov z&yrv0V4)e+i0ua8&GCZR)?}k&QfcP_p!2CU{8W0sX+x~=P+JHcA}TP>>adW?BBj8b z8C*~h5`isV$bLVE{@&Gk@3~s(rGx(NgFP>`92U0I`J_m=rdkUV4B6zlsOerRlVYQ$ zNqq1)vNL2SK*fhJ#4d;|+>aJN*Ie54_+QYKZsxbt>4X?!boE;9NvlI6>$l z59hqR45yq^qsq#jsJ(rQ8(u;c8cMAFy`81K@|xit?QIC%CZ9V;bY(kOrp3d#N2-CR z$WXoe55;1`X03MWd$j{Y)2^n-zTD!ir~FwMl8-l7YgiE0I@$51hAjHAsPj0Ul$2O} zW@bc|%yIQaHk0>S{j0e;SDPvFmh$KLZx8mw8NGKBIgy?Sa(nPsD|6=$RSg4;hb|j?VDEmg1)dGQcVM@fQd{uo5={vi zzs-pE@8Zu}e~KHriwa&8LPtf;kB}H%P6HpE*l%+ZcTOIFpoIK0GF7SjigMKo(d+9f z(N9FEhIHjZ4bhVC7NMA}X=>+oe;H=~%bK^{CR5mEmJYfoc`A~Xx=G@{eLBa2cEc|> zplPRnUU{F!$nZP>P3J{s-rf<@TC5@EE*dg}#0sr>RmXu%8tQ-pv#n10kb@Ll+kQvZt1c^4&mv7`gpgOGfYNtO9bSdVX>oM+!N;+~EZl2Y-Kq4hHh{B{AnFIBFH- zz4TQe-cRgszckOWmCGB`lX0-UN-X5GD)@`p+Op7e^E%;hmqoyAfiwSmynff8W4Da0 zQnm{xDzoI~7!sIoy6+Njh`_$NOzHOz6XJ`9oVsv$M$yqIUf6dv^$4#&6tub0D>QiO zANI7nGFGnR`ds+`6lQJx@+{__js0mX`N5+C{#-P(SXm)WKJv=CnNGy|Ty~a%nYY*3 zUfk?-hx~j$L(Kbjx$?F`Lz-$8u#_axnd~n)+(aBP2v_^O)N5!RiPGrJUY&q>9DnbM6Y-t@uI)3fKd5IGAbv z1?D&1Zg^Z(mZ-UM@no?7Xl|TR`waZUlMh;nCqwo5@(}1EE4ob`rN1`YspUD z*cMw`!!_V5%h$t7u%UNE1Xej({xf$>?YkTTf7mJUfKA|;ak#IyY`g~1yr&8;m1W)8 zqw*6ebNUpFNR~Vhfp%NVHD)AD!ow#YvUC zc$3asJ(F96cph(5{&%S1QFt7+mSu=%^!cyrbX2CY==j|f<^n1SzQDh;@{Uv^mm~Zve^t_Cz1idLGkWxxnPk>3Uh%Me~zgLW?JS* zVx=k9^9+0*t!syz*HYOkyll8FsXt8)s>a(EvVG%6x6`f)Z&~I#&$A-4%-a1W?N!+|4#}|DpC8R=-8I!3)`rNVFHAftxHn}?>AwA8+Ym}*w1n7< z5+2-(918I!u-zv;{AOE5FVIxB$Z9TdG#~NZ$>i^H43lTV=D7(gM!0*1T~u+aJyg_Ys!d}p!Ykb% zAH6o^F;`#cg$8Wm>D()u+k~Sufgz3jLZtx)YCR#pjO+R%34^jYYTyt1>p%Xf}ka#I8def-y}sNhsuXZ1^%w^TpIh#B3TV5Woh%t0aBs({=CHakMjJPL;aiWupA9 zV4zNZXUe2n`cpmG5I`s`0Mc)Wf7|w{z0Fo$6sjuGn}d=dAV-L!xfIXcB2evbg2oKu zc!)tf;`r+`TJ;QjEf2|^45SRy0z%*Do~+ugD}ARFR?3WL#b-4UeArRMOHY%)Tc#k% zG_MxVab`jaF5-5`2H$#wL60Y-Kq60kfsRmI4EmbPw(;Si^x$M}^_nq{)mZ)|1FLS< zmehte4ZPyMEbGZ!MW~TSI`@b0tbDn~%q?I~Cj@rj&ALtH*^;>%8N#0UOTzPJSu$XA zATF;U6(}!#uOo%yHO=as0-VRD;i|UIlq`?JH?O2_QJ-2LJ#Mv$)#`?{^LweEZj4dx ziiakP&Fem>;JecQB^GlQv?ef?|Wo+1al-$LphIY6u9#~CP+s(mqQ1uz5f-L(=Ajz%5L1(b*^hqnIAluk_TPEFJbWPI&jrW|rp%d=E7lM&u#eKi{#4b-ta z)cJyXHR!DvTq&<&qSo4L@)Kk9nSdE9RZN~xBqM5qx>C4;-6b%Dc1RCA#Q#EKN*6gv z*dF}v=52rn#gT=Emr885?f*P7p z0)_EsHD118Fx@O_s6ct`FzUEi|FxM(}{1ory-8EcKAEHby%EVw=KERkgXk5+ zZ_-z!+eGexV1BvotEQ9mjiEFMP68C@YoKe@#n@~=s`ZU>lZ`U2m3_D6@xYg626wSA z?mxf+<&!+5Ep80SxiLX*=j(F9Bi!A0`+2jeC?)T8(DBlLoFq7RLvX68{@_rYQQWuk z>es2yWwVT-ck?|)qNdX}4Tr&EhZ%fvoLdfBv4p?c_!D~Bu1fVxuA6wn>ypWUgYnE` z9v+34th0=}kr`XzMnVI#d!}QCn-_Ljy8|;lv#-_qhqK3I6aBMvI=j!t>@z2A$ z?Siru|M}Pz#+m3fE-yuCYR-&F4U`qOU?`H=p2ce9P$%^@bv15Y-?a4Qy#TFN+X-F; zj6`RVHZ6HPa`xJD9Aq{$c6O!nbD*ypDKZ@%6 zc5gZqmf)4gz5DFDT=viDuP?*eu#t1M@%Q|Go2q&~)%(2Rf!r7~=PD{pHW1*QWexTS zdHmJ8GoHDW%j8qk*s8-A*%Oe?6W_@N@XU8dPmg}Pwo*)2otxZb?%}WkB=RJ!R1%`wW5F~m=*lxk`q8bfvWNv9Felef<##{jVrgbk-6+r*4S^PB@pGkz= z(1lO63lQ~0U8bfI5R5Kk{Y+mI^D}c+^pfYYXrI2OUEmAj$1)(neMH+ndScyKn;hn& z3dGp9j3IV;@@v{EsyyI-;AeKTc*Z`P)z)M9F2a#B|M_1~YDc>3)vGMi-qO}awKE2mtSd zNFDoDO3!`-w(oaC5%fGNwYc#e_k=ZMf8ssKNA+_DuE_i|2pe@%Rt%oYg1%$C#m~#F znNJTuGc)c|<07`P0$RT#=SoGp=s0(!#m;LCd>-)CMsp#K4z3DjBO%|;SgmU+-&qLc z_cV^JIg`bGtoAH zxd$!vh-9vqmVFI;H@N4raGx%p+F)wlWekpYQ!@W%dzXmI$7PRvR@{0;TFM)EX5b5o z$I?gzkEM)aZgRb4YfLQWNopU0+i}dZ>~u?Tz=nNxKLc4B?U^5y7&WB#H96QpcxFC9 z=3wbQaOA&F-_Mr`!0^)VPHA+1&&MT;#82(_E*9Moxy-E%%Aei|q+1J7gO~%8U%6~t zlm|V=+hGhFpysmq;H*W2!~93CuP#9vPd*>q3f0xxmb* z^lcgNSRwLptH%8YL6wFUm6Ni7f@pqZ)BeB{lRt27);kuXa!OH|Z~jXQtn2<5N3Fwx zN@VEz`9Q#DbqvT?MG%W#AJ2mN&pN4$+ap&utND ze->Id<^i8ebN6p}w=p^=;c^;FA}}e(bddFVN#p~`x-w1M!qw%{hyBY+{qHZn7rMim z&RDl4G3Ktjdux@oGc;eD=*F84d4`A_FP|9{?WCS4_S%#P)5;oRc{e=z;?69VunL}g zO2|wbJ?`>yyeIh2SPwH+=>FYniTgI37%-7onF{Kf5ZY}5(`qqaC>87rw_GlaloNRF znJ_n$`d2x}=%nTM-Wz+?k z#RYfszS7FjnnW*=Cz`%Kqt2g8X?-x2W9f2~9j4UK|ClA%)U!ZhbIH>nve9a3Age^? zyVPXihX|_ee60l9R4x9UwOTgVm2M#3ba|E*reXVlfIH=aeTP zO<^gK_$mIT1Y!DeM@C%r0C;pnmSXr;z+EFTs zOcCpGQ$46nOpQKr!mmP#DWh_qQQBuZWfj1k5BDkC*^O^?;{Eh%D!X_AbOVi|{@hLv ze3O8=-hQX)K-k;WN~e(qqgvV%C%v}GJ~yUi#y4z|odP*~vOxeUys8H;!O7ZCn76bY zcfo_^S=jrMHi<`m_lI$NdKitiF|QoGkN9zd8|23eD0%~9!N7y@t<=4TDQ(+E?Mf<# z)DapqU}LTeHs%wk6-2=KlK;rpa`<>a)ZhNluTC8p?eTVjFh^)zIgE>?OeGk9Y@P@5B5mG_CRNfhAMN-D!)pP(Za+!#$#;P_jCrX$|mrf={nA-`||t= z{+33b8m;fYY$V^CIz57bJ+bZYbU+T(~?~__FCu3SRsM4F^}wO zl33wo66FCCq*Zm_wX( z!$*bNtk>BbON8TX_VN+K#~avITYve5Y40B%s|I8_Vv%cvmAF`dP$7o~$Hlzd*?YR> zA-Ugh1uErZ^zjSAzDQIqtHz`1M#)I%y-72s1s}nakL%L*=?R55MWd=5S-uy$Jh&Z>Hm!-%Yj=h1G}KWC>PQ&(dXQSw3DCR_{7dKS2Sq>pMT= zEqqtg{0E5rYnC(7MVZ@}Hbdjlh0+$Ane73vo3E$2vr@2tGOF_pN{8m5T4uR~D-ocZ zH+$s1F>cQ@Z2s7i!>DW$&MqgQe8yJsq5nPoM?F~dr5nah{di+lC&MMv`&f4J z|JMQ(4IVy5i_tvs^%E9QZGsAiN1SRzqm;+7!bR0|498rIdxzj6^Su&D78VesMl`IJ z<^}t%k=FF4l57@K0Qf6dzoz9WDR7G;S@+X_CGYc9n(zR%hkTd$Pe2dRDI zMXN6+7B??=uTO3l%F#^_nX?5p35N0oo9fRv{N<0_{O`MxnwCsOIECexWBepqO{c3tS?j|T@BtE9_%_(NQp1}2q(vR6L)I>>eq$8@D;7t{ z#Xwuo9dbcq5Zy*`KqwG~>wJ|zaDLnmF*G~ucKNMly$8k*uy`Z-AJOXWbinuc-T(O> ztx{0rD`WWfclz?^pQl$v-sw&AWu)emL`q#sE=w~j%6D>+tUV^;eY)ei4Q(hDoI{!| z?P;u2$@`;xz_+G(;+~Go7@r$T`$2Ouz=AnK9Bv%t8#E`r{AV~J?DQRi$wcH@+`R=Y{_?wG8aPR5>B`6f4Fs&9t+n8pWKGo)3uEILldiN&ZO zAnU{r<9+E@DG3l^xcU&M7$`25YunKIHZiTJDR`}(HQs1XU}-7R2;?0UJ^#;u0%@-C zENd)f{Zv34&y34FLK`BzQa#m+-?`AtyZ_O;=$}I#1;l-c6EgP{L=QabIwX^a`wWIx6&%JJF4tKp?JW>49Nt%H0g!=g|M0m9(VMY zzJ*)^JfCq8>pzw1ywkVh*J>LyMRq|~q8z%hTKn-uhrW^x&#ClmLX;w(+y3Xf)pV@1 zg`($q1rRZcR)d7At1B=wb(2TxX6*y82QHI*UP?ckgy!@|%a0(q<4$9Td-Fzt+C{j- z6pL8D>+JAFGHuiC!ZFY7e+G*eYi2;rx3cwjq>i_SHVdK4Q>?d;N&Smvop-~)AJ{43 z7XeKXJ!dOC``Y~}`doJ2e!(G)1pv zT{WA^cj&-3A~i$KC!)1~!2UuxDGu78I zW2h9Yt?ESupT(nqWi|{C2{onmDo7lFy0=GzuEjc$9s`PALx~#3pG-N5qjqh~{d;z2& zITPjJHa9S$N5+l!_-&r_Kd9FsBD3+ibJeQX#0t8piG6HzBrOgF)%n*dt zDMF5$LY+38LI01Y?~bSP{r^{#lo3L<5S2Z$SGKIAY)7)PL+0VA$Q~hk71=9fI|tbr z$KKAdk9|1CaU6%=-TU+XJs!v3?#FfA_jSEq&-J3uVy#XeUkU!TbSFk`Pn4(~&pxDH zH0&yV*LyOP2td;V(SLH*RF-F6GB?0%dg)CLmP3d>di5f|;HrWu&%m$6^i9;YTFd=m zg0Vr~(})&6(ycjuMjx6~IW5%we+v}Hm?ceupcXiCq1TWc?&*z=%#4hsu|#Y5EE~JV z!E^N&J|#20W!L)7uXZx`mYH}8(||d96A5NTd$@Be)mXGcC0MU3~b69P;)&$P{Cu| zIKB3F>07HZPC2=gn#`p#-jkY81sMZSZ2+sDy#EuTrJP{PI9fd>Zlz}3^i6OlKk!P` zx?F6Ye1n6~k-Svx*;tEPL{fycox@cO^1CECTbVm0+W=>_m)$YU95yvmww-GnM85T4 zwRB$p;}>h*dV7J9`$~K?v;VD}1Iti`$0I!MQRNLdX2Pu|Mk^bCr*^&c?n)S~LN?BJ zY~tO4fU7?m^sS>PP^COQy+CRO z1U%Vq1m7lFDxwQs3Ug-DuwK3vOJX&N183VJo6trJ?{*z2rspBMC!T%(R_GsWI9ZG4 zNQrTkvCoG%g<-neF%$lNZBk?76?N13j@1^UOFMVJ`70xuw06}rsRjsr8B5V$)#VTO zt-$NVgAmM9qV_C*Uw|OqU?GvcT%DIqwHXkq?4Abh=mYdAYsPB->@*S4C4QwSTy8UZ zqB|ose5tlPy&0v4xJ(ByMDJ0vyD6$^cSeP6&dnu0jqu|K0u;bXlx<$pSdnKu$1Onx!uJU9gPElpc36;F6dA0POQCQ>1BDwA{T23#Y7bfxd(E)s-h8v- zK^l+Qn$h)t3)2Ush+{(B-%nfHxggg{`fhapYml40_*sf4D&UFkkvA%KA+NyQa>$=t^65J@W`WR9*fu)d5f zWMaPA=fk|+WhE?p!#*$T}U*yPkpW zg0nn9Tj*mqKBm@TL38(oCkw@xe%gJ1jpv`*CDsc3uUg8>C;rHi5)%^l)G-k~zFn7M z20PIIna3(SeqmouH+5r#=D!l>!F7VE7@PD@>cRB29gE)!2vcK~G{Voi=>`v*zbv`E zpK?qx@LjNN+1oS6@eU#FzfECcqn1!7*8OHeKjvG%VK1kM$g_yXf#G1MZPY{jQXs5Z zQ(?d~5CD))+GuHMh3)2wF=dh#_9pGKOy$T^rPA1>H1k#aqyP0#9hVUdiyBYtGCt(mups{-V41JZR0(qn#3h@%1>R(qa7mc?YUnDkR^OwLQ-^gc$GBBZ zx}|Lxe}86nlS$I%bWe3HXNi$}jElpMWY8hyoOSUP?(6zwAt4vtO%4*tih?NW%?aToJ7l zd?{6bYU~E8118ZXxB>L$LIFjy&(6nBApv~Lc;ya+hgfD?*!+B6VqR%3;IM# zBR?qX+DIw(si$@tkQOmpsWByEl0^Y;7aJ^Lf3bg!tk#aMJFxTj9@@u8DfB`BSAR>> zY2<18$zq9Wxy0)RAyLFku2)-rrcGxppXu#-(ihC_J~SvYP~tZ-5=jx7_8opkb}&v# zM=C#>!~b=KhXh@?(Ig}AhWAecG>)XSZH1uf?-w_UuXs5V zIyDrYe8nceA$5UTCw*fI5ih?<%*0iGG5jHUw{1LdXR~z-K3V%{LQB4 z42fsL{Z*84$E~gX9Y$5H(y##Q#h3SF^Z|Z2Q3-bH7m)!Q@f$|L1pt|DC>{;w*>%FQ zWlL$bmbfcR0C&XY8{vp1cxzZbXhd@ZvFhYFezq?J8n`pBq zu^MSG#BC#5W??Z12G_|otFT*boVmr^NYLf*7l1z8^wTG;7J4)j|0Nd^0{lh86$m$( zCEb=eY}}j=mNhr|QVDHWgwT^6iR7Xj&+WV7@4gm&>MHZd=ryVe0-ip!$qUrGrq%Gi z!RV`1gm;_!lAi|WbXQNh>1V63a|$P$wasViZAi$GcDX5mfvaP5Yi3-V2@lkulC}Hh zuviIhD=Q-UIJxG>&ZjwDW&eBf4M#8QI@kX@V3A(J2aMO_HE1;ax0HFsusq8`~LG z&B-BQgY)h|Q6&tBCl{~X<{EDVh@WEUIX7-Wx{*cMkPjx6La7&hQpn;A?X$loKdZ6r zW_0g?d6lu=nI%=ENeg5|eb-0AR0ICK1=lvaGeiOtpRzM`FW%6yH<)hvY3y4873?tj zBCWtzJ#C!<*V_&0xC`>D{6jUmR>Eaa1z~kdEa;7fxV%K`cyd*H@8|(yTW#yPGGxfSUd*MvxVdnf1*Y&hSate6<>BDC`;W%PZy`$qi9weD zM5hevPC7+sGZV|qzj|hUJUN|j6_oQl;M0G-|1bTpMf{Xub9=Gmp`=|R`c#>{YL13B zE~fQ+M|hZcPV2HZe9r3X&wx>IG8Imtm*M_?4 zQ4cP@d{Jv$!L;NTdT)+g=TfKal}2U9p?DJSNp;+Q|8mtBCOWeTc6+EMRUSsmU| zdoQd99Ne+;3pp;t7=L{H;S*W|)xb#cU|8larVYEV9C+GagDEq%0$5Mq6gz$`evf`xRUH7T8RsO^A>>jn>mzz;d`wLdy9%%n7iKeNH zIRC2ioY#u_)iT@Mh4~(};x1?TFVf|-IW|cEU6UIZof21C5t`uwIubTa(q%V4R!2CJ(G zO~>&1c_Ud7(pUwk0jFt=1-SV~I2!*!*4+ zB!5SI&Q!Cm)bg2Uzy3_4^&OSqg{N>yugZnBS+HVxeBqRsd*92_^_KUzar(zZb4l;? z0hp|Kc=Jz2H5hsRS=Vn=4(XZ{*f>&{dV{CxUHq$z)ty`651(u1(c*!#4z&S$4T0)` z=$lza)u~5m#E__I^4cQ80YQkmiAkcAR2qKT3?r4Mx=>RiJw%gk!go<`@!kz^i^A3j}l${t5@m zNRaHJ%RH?hmPe!@@X0s^X$%Sy6cu%P#AD&o6_@)6<9HC#!LiYHXfRXvECl?3mpAj1 z1$gI<&DOk-;#HJ84D<=l-bmflBy9n|O*A$(CJec7TyINCq@i=^E0*j?P@%<4_8ul@t=!5%eMEk2YfLHL^ir`wztyJmH6MwhYw4VTw+c~FQ4h898OI#_ImolYLGQt_uL;Agys{-f)lD^@CDE71 z>1%oY_d>vaz3|yUP+JB_yb|``A^%jA*%&#&I&#pQMeo-pJLaXmN#pM(10N}s9I84W z+MBifIPVtJdf|%_tQh%Wqa7Q{C$mN4JACsXZT$+@+Y`a!I#>U(5r8}0Aa>io#S#(|S@IQAh;bX!O9PopM~*+H z%S{YnfI$9qdI03E%JXg2U*1yxR>HrRVQ)xdftW{re%iI|C^Y z_Jo%WAXjyoL2blqH)D=^Yt;| zn4h>DLX$t3xE&Mu7`hGlV9krQzzF<r z0`E^Q$%09&YR{=!59b`#jpNzIXn~xOWKLcFpE?V*%`CG*{QQ3wq^s1{iY;YRmrYB@ z>IWjoKcAG@}@lfn|CWKaQpi&XDdM*S&PrQ zGiMy=HhLr3Uk)KR4>mab9#3W%o8eBtj(Kwt=TX3m)^d*8ce8ozk?KkWVwuR+x|{0+|gO7JCS&;k`tD zm8I9QLZj!|`yojFDFx!p5IW2^VUP$tNg>D@U|| zzV!Hb(|##LACwUC3K<S0qhjPF9M`IpmbaTB((yK3RH$_=TbNX}1={ z&NDYFi~kU2GIXpK9F3y${bl^=c5e(_1bH~Gwv#U03Uc%Y-7|;s9uE`>>q^Skbd{a7 z7s$H({YK>ZzsK1QjVouIXE!ikUU`$3wXHv>@38sd_RCV1t)?5Slvf{aL(1)DSU*>Q zpp@=yO^RSy1s$LbNb-~CdJRP92^A5Em2 z3;#L2`+`3F(l6YXwJ>-czWxjql(J)ghPX#AV2pSE0~nh)wkJsMu*2B*HQg~2qs3Nj zan^u#KvOqog816El$5s}pTAI-JawK5P3o_AqY=_RUyp?BI^f2%-B*)S8B`x&pCIy? zNvx1K{NNxH_2WB=g!;tZK+B_W;|-B27n;qTmi1@6zK9sPpAD}6qbZeLgr%ALOOqm6 ze==q8defoy7A*!#Ye^yWpJoJ-^wOn9p5SfNL(rxdu?n?0KT0Tc`uxe##XiV;)`OV8R4m|x)txOEifl#4zTX}YwT1;Vf?-n9rOxZre zFDU?vj)n!^vHSe_>TnlJp6os}mvsj>Sqo(+Ye9HnDV%LN|4~Z{7hThEmF#R zHaMy>PN$h_8%dbixjYbo^3}48$J5LCfvoH}M|JpTxznSu{l!t0L_6E5yT=Jj!}rUS zA{^r=)P@|oi&qb7@4O{6Q``@pDxiLtl$hwU{VQ+6QF@K+%C)0(%pnIuuM(U!{s(`T z=&o&Xr?PtN@8YJ@-No?1jHfIE?3c2wK?A3jz1Tsf9qAdU7afvmpoO7CU(IT&#oQ!@@rQ5x#}?*cxSR(zo=YD3D@JxrR`rMSgNxH0tFz4z zp~IzehF^}W^~H4){R7J5g%k&E=s!=fK=3wTOS=@}yvlSB43k8jkrN5;tyDLjjYYm2 zt*IUWDAx)09)$djgf0a!*2g7}X^tW)8&^5kkTpZjRYO%AYot{4tHZKvJ>l&9adXdK zzNMKFUkeNcC%Y8A`Ykb70o?UXwN zhSb_qaK7|K|78eVZHE8eNwZo6F~4e9?behn+&L(;n69y;)Gq*}ZvRMkPj-dKDa^>{ zW#1uK)ctCRXg@Yn5;Be*sOqq4+jqC{o(iLaJ>l+YR+w zSvCmsp4@sV*RS6niuV5)Il<0TGdhUL6nA{2mN=``Xhs?kNU-6>l&@4 z6&Zyrx^a4T6rkB^(%8R=!)&Y5#N7_25@!gofhJVVW1w7CSEaC+Nh!Jy{ikXKNI+w^EO7yw5D@lEQu3x)!f%&OWdf zI6bU66l63+PN|G8)KYL~H;IHW*1OJsy2ES9`t;SC*WVoXZJ>8%hnNjMSE*#Om$`yH z1jL?8wYr-7jHFE;hU%)+(MBHZA$TU11e45v_AE);J2?Pdz#Xi5D5} zwZf~|fDE5xhPu=0+~pPcRrn>ecXcvVF}6?o{d?cQ>SKR$X2jJ9?ywJ|SaJ6Bk0YXP zT3^}TUVV`5cyF45OM@yt{qi_Th>!bJ5BaF2UV%O#);=XSBju)qZiD&Z{_C$n_}_Vd zh#R+hf|tt*kYcfeU0te~Sk9Gu24PFJ!#gP}xF@F;iHR93Ul!2cGKc|*JM_26Qu5P2 zK6-x$_in&?3%%=3jnPb&D&)WBMTxeMv6>>g!52(jFZXW6{jsl17jKqn&eX>e>UM4I z5WlT%tjFNjl9~FaiCJ;?{lzcw+ifE?uW9G;!Kkl{L3ZSkZRyp?+{Z*#qZ9P)Ipg)<;ECB;c4#XqvO(&Oh}aYa#q`{XOty*Zs(vqECn zV$NZCJf3=f$@;>FZIKla|JDR{2W0C+WeNrx#0fe8Rg7 zJOy+8qqOV`AKQLc(g27>2-I^QbOkNNtd11>>rR!Q5dHkR`R~+m?^~rdkPhwjj zPUyb$it_%brF9B?W%mVp2ZMI)Z@A^-#|di?9JY-$O4u6H+RP3hPSzRjDK1G4;K}tr zNR^+M{`xmuG=%?=-q*w(L9~Zx9eB$+KRjy^xL}WXaECEtV;pQaVD$Xfd2fE9q{}N( zuN)|`_s)il0#KOH$enbRQBRXnZg(H=CM6}k{}UNvGo|RhAGnLO`k6kXA=&AGhn1_q ze~XvaHim|(UQ>3LK|o8-PU#$jub_-5z1EnL6OUECID2uYS5%*r+O1dH$vYazop${> zL%DfWbE%@EwO3pUJ?F)d%s-KwP2Js|^h^!K{f7RA|MhepG4_LNu!jR{TXu%R1H9|6 zgkm6=_9{az+p&b43#1VA7tyrU(Yrxe@JX!cyUAv<@md0|2zh|}gZ|@rvO9TgZ?54D zgfy0Ex;AL+)Ef5cF8=*Xcz%Uq%p2i*b-rZlsyb76Iljlfim;$(9SRmf>#S#v-A=tm z_VRvrhUJ4@aOjkZjJa0gWRhHgs`Ur4gk{m?Om@0kDL3V1(3>C1QQkvxaxcTTPC#nX z?tT8c8D6F-G=RzqaA!SDkF*z3*z`n zKg9jT4 zPCp9^L!npYr`Sr8s~AhT zfIgJYm0~B0C}@hW>sVvck!E>nmw{%RE%d?`&fmJ~$@pDd@oN9!zuJ5yV?om8&yEg! zNUb@*oun&Oi!1uB9q3Mhw)54B7uvd`Mcd|dV-~fqI&Qz~simd$*j|wk&~*}ticwNl zzP?KPXlFHkz<73JpidL!cU2lGAAeKv_VVr7bzdE_MZ9~|Gmr$oU}yaE+(mtW5@OV^x!+7(=)U^xO9~zbL>1V!>IelHc*`` zfUy?yb5(7JpNp$&_kJxwyQ!ypvi$io#9jMN*y`ODt>^U`_V*H!v-=MOXCpyBC4K1k zTA}p{7#D&yC%rAlN$Kihndp$O1Q6~d*s0@Y`Zu_s zuwd4WTQ%5Mj>L+4=;fK~-%93B&lMiMO#lmJC&iz*weHO#7FV4vP0*{oB{^Oo%7?Eg zKfe0kDR^sRao{{w^q&6qz5o%zBuh2>?)j*#zJr`Kz=N>v1;Y+xThB2?BICR{zMgch zEnbQZ?#Nh1Nkw{tX+|4;xep3CK?@aD4Wkn1LtgSYj}1`FQi(`s{Gj0mog#e`!_$H3 zJqvvW7{TiZ)KQMl`QYL>u}SMOU6h`!Sx_wA zAFO!$o#^M5$FF`jxm!`Y!Ill?7g8+(B#szyooCwv?0F#cY?&D9X4sKG-fdCVGmgvj zEnNF}pt4(HfJ$_NG1}#5=u`D&KlSB|KRu+L#89wxuDXYk)ok^W)J+8_&n8eNuS}NL zL<9tqafAaP!16GEOb+?-?6t8c!i33YE!%Nu;9V0q;RL5I43a9>s~4i@9>3N&D^a`C)`>v}N6&6FMgopo)Re_ZY6^)6 ze#@9^J44(~e0#bI7_-#f{m^f@Ns=|fn(vo0$WEtbU2cQ)`ZXZfMk-JBORbbo)luu( z;+Tnq5fUIDWcAoRD96D$*gstFCW@a8I<&UYH0f(;QTqw;lUVuA$TI#Jz*;h-ep7`4 zrOWqLs+69uUWj8CebjzQtmcF>&=&vu_1O%WNQOI%mB$sHe?TBY-`F+LSNXa+rf4xQirD;9RkZ>M6i}BAw zZH+E9xXwRWF-xzNg46~|@5x@x__~$lwaNPIc1=7s{=pI4X0zztDowiB;#(b5kH<0? zabo)|(Bbg@oH?=66m5E0QmT6z7CVa##Mp-2MQMSYsZc*TUv?zV+31V5XFU4d-jUb; zJO8M4o;^!46!^*2PpksEQhsajNTGYZl|daIJK|q`dAZ+`Y*K%WN~`nUL)?T0_-<4N zcYv)_E2_ZpaSQOJ7PQe!p_O=0Ae}}q=bRB;SI(DB^J4P`y$M}<6efW?Gf1a6-rapV z9)NKhafRt7t?$xpuwbE4?9 z?;|C}(!dq2U4jr*#={{X@TF`xTNb;N!pt7o2$`(=*Q;jkumP2>i2FWs{T}PnUy`d{ zWcRsGtI;C7MMkPl)&>azB@SH2E2@_Dm469$z9{=`_xt;u_oG16tV$f@?1 z;ev{7-!Go;4zVzPhHt-#&)sdIrh+|++E8fl5pZ2ICCG1+dpI>y)~=T~io{E@)*C3e zFP;xZ6X#kkjLpB^a{b-h{2e%I5WL}#lGM%eG+HAM`<+=phvi>+e7m3n|B8Al(z!&5hF9k z59db3v9q)3mBZ491?j%%cNy5f{uLBh{u@ryLq<=o{)-#+(AxJp8oQ^{dDswfW=?dM z&jH8N^;PgChc?ZVgOi$E7v~($(MJkh$5rt+Lk5^dt~F1Vg{M~oKE>C zDzlyOm6gvuPXk z1K}*15all3Kv~=R{J+ojZt5G+fhtz@eed*^-aA^7)wjTVeV=YyYkAKX>R`HGm@#!l z!`pz5_S<;e2K||Fg+=%cw0cfM+eWiiU%6>W^qI?S4Qm79;+HEsAeidJAppHN9k&$k z-_g*a%m%5i0SSKC(v4g7^^)$0sH0(MXvq}LT|T_t)x0-QeKcovdy zT;tq3X#(S&A|XCL+#O~O`}(9W-J=Gh69@!6c6H9#t&<9QwPw<3SnFWj_ccH57GW9r z@L_>92-`V?vHP>s{rrw5q-6s>W86JeA})zoV=zh z21Gog&QQuA`k^+C?i2Z6^1(plY~6e zdkI(hbCSN1ni6)fM6AEm%>eRZ@in#*qoC!@>4$y-;9;gy*~gC~^X1}0pe9~1&0?qL zbyz!-79W+aI)ENlM=_4u&PRdUgzR+(SDO@@*A(Myey+WU2hR1ozI{Yr`x2{-_9mn* zV^+FfA;mjRQ#cB0DYZkC2q)90%j5rv*H&^~Zby(G0(D|UY#!0;b3XGHpdm)?Pnr#+{Z$|nNTo*i#bb8J7q)((Qf;;Je zRJSOP3yxECVq)T>HBf91Xcv#E?18y-7TiNZ2mq*CmHs39r}^YWEtF0YwFI@dUJ^2U z$(NeGP(-ZB^W>kfCW6;i#G!m$mAv))ArEgLf^!BU=kBh~M^Aq9@bm)is_nBm^SP`5 z=s$C^#T1?9+Mhw&^Xrf;4MbvYIN!l~QPFWgMPk5$Rub210=`Y3icLeSXU~36RQqr) za&lXWyMWX#f#DsBG4fuCHjxspW=lW9(a(&G3W*;?UA8=F($~DD2@|6}3e=v(zH^Kd zYjd}<{;*-@-;ZgnV?Jz!{Lg=_RyY*>p<5__2{|YUV4Xp}$qo@g67k@$9oLq*t zZBpY$+R{gCXTC1T*w=hlzlX+uSz8LORNDhnkqGu{Vb9I< z++1zQ2SH_&;Rc9EraO+mUHbhic6aZu!9FHU93}dtQ+(9CIGI+@@}A-@G|LS>)Dm|b zd*l?9Vsoa%_lF-@Gm+@0o!JBG&Dvd0Rc?s?6Z7%8y6SXGf({o zG2TJBbSLP=%=iJJmTNoH1R&fh*%-Rav2@Cd@* zm{`pkGn++paEEgr9Qltd&!6inFA*Dtva6&RVPAasnck2~dX|vh{jkdZKQ&eamoVDI zJ|$BchvRQyP%x#vlNE{Dx7)cUsjE2~It2w|XH!@pI*Qy6ABs*x!M+Ont%Sp3>3EhA z)^Z*k5D6A9cPv-$y2x6)&gg3a*}PC_qm{2maoJ?J>x206^>f*%AD-^^ld{y!08Zfw zT4-Y8%fVI@(r;(zMw~vbvc>(+7MK3bOyi3g`}5cRK^`6=wmKL-{%ib|=m22N(!7x_ zADn{8fm@O8lSsjQHNx7nO>t#z#5#!j7Rk>I%7$AlsjSof()Y5}P)igF*cykGz?g6U z)a&vOARUkmg?Vktlh$A67KCyl{@dL}<#7ajw9B}8gWKQN*qSPc(ldx{c4z!vIDk3` zBl{Vm+qwr^a--4}P_yZq3r!UxwF$l%8{c}E^r5@YTTWd$ofcq0q$>E6*;XJPc4}R3I2TABb`TxTT zK3RI@_RE-_OOk`++%3|!;uNF z67m?FEpn$CY{eS=EGQ5|L=(LD1Su800A)JczHjZjQPBl2Z2muPuFk2NRrav}h4oe{ za`|@>ymv+<^$j42`fQ__e40a%c{R)CQK2@VPJ`4sdANI63rS$HQKl?U*QGqUg;3#? zzyA4w?JtL8!{2v>SXepWghLKZvkVe+S11WyjU!=^`)c~>&fx}guG-LS1DaY#o?lR~ zCkQ*-^EY`XuaJrrLDewWDE{2VD*^RL`TGP7n5Y=}9i++!d@zwSa{;3s55>a%NPe2C zvk z8Zy@b7B?*L1P2EyUI!DjSBLjK$fOZRVH8)Y-&|&jE8i^Mz1&w;_Kv7`&C5!iKTr<- zuuegA>s?I#U2OKJwu2E)@sI3wb&jl)3YC+r4B*4rEh63+yP10L7Ml_(#Q0v`e9De zLtb?X8j@+ippaaZQ)R~H_=S~Z+(>E4kImBPvwhcD{u^X+v*QuB|7$GBbdt=>3dg_3 zI2ek}#RxZadB z7wWN?gY%2t(Q;ER3S^Q3g@X3(X;zjQHTMyK=jVNYldG$k)u({=9WvTz{}htuv9oB> z_&(AG(aYAx-We}Fx9pFhy$E^TN4Sure{+~u#e!Vio^%#Djp_G8FX~?-@75f22P-)FG$to(%2Md(~Db-Zmo-R8BP1_Ew> z=w)Z!_e!>s5MCWEVe6;7{I;EetJg-($qWH8pVTiv00AKH41L70?q85q@i|%uWLCoc#cW=fIV|U(ljyr2K z(Us&eluV_jE7T|Sr!dxhq};zV&6_0TfgE<%++dtXmofI70^>r4#jqP$&wM2lk-WJf zAFMc6?`$1&H!{h;#q0Dm6c`vCNBz_tQ9}m^fW0?E>~`_z2O;_%Tiw%A8?wt?Ht)xW zrh%@qg`MA3gZTuj&{3ypi$B&B=MLUuyPQg5pC8vuJs|>p7KTxs2+5{wkm! zZFjDMusq%SP2K5&|NUX>YWe3X?+=bPVa~m8hT%)gz4qS$eOnB6psa4Xr~~!j^y3$L zfef{YYL8B&64GR^FCid>BLDC8J)yR8;H`-B;U6zHAeO^y68eURZvt5|Q%JqHr@>YM z;}U=xlyX=atutSglxbd;=pJtJbN=|EQ~%c$4(!}j2SAdViLT?e&F2a4m08Fdp1vfy z_c$qpjKuUS5X5nBW_Y29_cpn1aQ1`RmGi)?V6A4l-XCW&A6!0>p|b9YO^@rt!id#K zZKe)^!q+Bc50P&(u~Se`0NiR+!hY?I#a)*vjN>BcFQ&;6P`7b=Z>B))cJVf?adklB zOPx~Vx$aafJkXGLVZ(Yv&+oEWLxIW#QyZqIT4jgZum2#d=^o@S8Y(O4iThuE(Ew(; z=TP;0bCGS&cp9v_7Pr4J7LVHZH!Buo?&3!K@wzX4UHH2zk_+S^0Ft4`>q*ZQp2|n& z-0L7^CI|T(GkoEHpT0Tw|Fr-QxtGTHdb;@!XL*rt|HCzG(1_~`Uen_S1D4)<{(Tf) zGsZ@4_*2Wqy%Y^$pFuimyY02fKl5n$NM^ST`e!~+*$orc&2BwyAkAHEoaajc>}Z%2 z@GVoK8p*OiUtns#@rfL0G5_g25`_CNSC78ZmMg@A^PYOyvw>j{tLpQLt~~ zMC=}8Dh_df+&e3?{HLn>hK4Yc7e7I&1zravUYa(k=8+4uF&_spWQFbbS{0;Cdm%}J zob+&!ePWLV(kfTsw8zsWUe1F-ml9c$%TvW$4skFh?Kb~w0BS?oayj&$lzG3{E>6+< zjmbP9q_A0b7VBahu8D^W^gOR>mKa;stPVBj7CJc>-^Ce^nU1FvX-OEVrA z0P-xeUcf_j8sL&E!~(GUVSp__NoW4ALx$oTC94JVt-n;rs`k@8S<3E+hBU-u=5zldti#c`FUley16OE3}fY|09`fW`HFLx!~1k9{-fVm7X z+GH2|i;0ep)H&l-xNU@BgmVo!%t7=&2@4m7&G&+6!S6`?cz>MklV_515YIRmOTR?8 zk-LmYk6$jLE8`k7uDqadhvKYp=-!;PNRL&GeD&B5^C%ZG@PrV45vDR9k;N=|8Qa<_ z*X!%7sMjT-hJpu^PFpEtHMztx-V$q|0v(SKJS;b}b$J=+qb>?lhVY$T%zm++P+LKf8DDE}iO)d!zaV zB{O#oNYw6jAquo;sc-xYIR^d2GCjW@%Ki0g=In3z~( zxmLt4=!tc$vTa|t=cvWSfwJhtIq4~96 z1?um*GUVtgxKJ}tarqxC*?P+*0~`$PlizFN^3z6+HjpbSDtbOnW7#Y*kL;)H&b9FZ>oO9MTxdm^>VA>AX*$GvRnZAP@_` zf1}9Y+2%MdalC_M*qsYd917xivVHp;%ig7)<)EuFTVr>xFD5Zvp+)O*(V2ih>FA*_ zKZGZ~cH24qyM#KNbG}kp40tNQ#2Yg?$Y(u0tRmRUgV{;TYT@_k#})vNbX;mzgIr* z_uPH%za+)F+JIPDEO0;+PX0NuyP0AGvTtv`M9S-&MlU<2eYQ6BJw}y;lPlk2%EfD`BMW+%^Y@mrYmUM|Ci&kiVnRnJu!@!1-^{@`uc*93(R-VGd)nLkIYTht-|n{`<0p>vS5d=i zK;dA@qZZe0{!^2BqTEy->0hM!{22zcWi#b0-DddmWwqm~5kQeq;IXXAPG&M02;^M> zGjX+n6fCfB+c^5r8ykV9kUMu@^ckofw-&)&mFu>XU#$rQS-wcuaB^b$o4U1xiFKQQ zN-ALf^lKpfmTwl4M65dY&xdQi) z4RFqK(1$p?%|(*$Vx!kSkA8p6{n_-*f|t8SZD7rHj+qK{aYbxn45!-X(Vvc%;*Mt1 z1#B%oY|6d$#1D^f8+hz!z60+)0s6RKzh0c-`oFl3p9f;P6u7vEX?Y=N+-i)S~a9^0zzH zR1|lV33#W*Ao>oJ&H>pJJP2l8LJfo&e;!i$ad4|JHdtp3f#mF_)4dG!{5pd}gfbrW%o+C`qrEB68(A0Ch#;cbz9HdtL)q4lkMfu6)Jg ziGny_l&n=AK$CELUxn)K_nXEOu66FQ(|T ze6q$vFyD_so;sFb)0;2Y!43A2F3YV-DQo!MxbmMqeH&VP)b*w4@{f)M)!89)s5ek% zpzqjmhuuuKOKrbmNE4YDMpvtF_5ahr!L;!hmU@$65l&*{Lzz^PI8`HBa{)rU#HG3k z1Qf!@S|pQxzIbs$jQ$9)MG;q2(+t#R@=q@7xW??l^&8B@YC+fV_4#iC#`21aoLXYZ z>>pYCPC8;1r5jux6OuH3VtX`^x_(07BS!gb=SXZTt0jBL zZS~p+xi7Zia&ye8-X_tSy&+1trc_obT|a1*Y>+i>{6N$ZS2z&TmbCeuJK_7*PLf6t zyg``EKEvSUaMLNE@k=*V4WS{nt@O4rY6PK9m<5`GF=|mi_-|!^BAtbF%8l4?sgHxO z9S-WN6i1(bY!XieyHEb|`ZGjK`VFcrT^_Qvt8}=}(7LOs?uuLx$!bNMByUoulxsYp z65&I}di?>92`dsG&1|m3K2&-@P2&?YzsYg{Ej1X;SFh+WFb9)l2<*FoOYQ31ll@7a z3RZxp{0zT+a}km-T?Mn~9aD#F&IQ7J4XI{76l**^xzF4ogl9#4|LL)%U99o4`KWt% zeyzq%V0nJ#gfcjN%9J|B2KJ@WN84GNqsK#^ayL!Pih}3-11{*{nqH{nWshy=tB0avX=N|0Ou$esb~4h_LlCeuR<9bll534}KfH*m zv|V4fkc8Nj%^zgLn;gy^}gGL-lDXhYgte zrz#xYe}qfztz(IS=6+60H!)wF(zIgV3$xg+OGk<+YDQ{mqRK zh@qTI=vOxeAWAd^nz0kw%N<-I=P4KiMmm*(qAKBz3N&c_DGn|fzdtrzJB&;gsb`U7 zfqzZOuzdK5o~g!6NCcvPz4Lkq64u&{CvteAUiEP_`L!=TlgbUs0$4Pd+w!f!BB<%} zb|gE#TlkYq{Ey?t~FeNkI)Wc{cL`oqgeF(3*VEUH*QV=Bf2Up4N}HBp=9cT_pj9|qi#Oj^0P$zN6V zLJw^cb<0;KB z=3s_^JOG3PiTJ0v@mQ|Ixr~MefqV>g*x9ZNu{JFAn6DA|1k1}NYG&m<$(S|4i_pWp zx5x9(oRXKU67WK?*DH%u)#0sm#+z*KRnw38-zKui`?M7i<3;Kl=6pqAb5l__*ZyNY zETHrd_n7jl7~U!m(zPF)x#e$Gn(H@ApQ}sEWhtfdUO|PITf^3@Wj9Nvio?CnY;(LK zy0NgarGUuNm&_Ige4jZMKL}ZjngwreZ+UBzoP8@lHt~}RpT4*`9eZy-S4;GOjCJ69 ztQ3cSsVc>gmzvb1g;KT&=xa(*hS94^bSJtW%*?6NwWOlVXh zV7gqZQm<;wgxvmLsc0jNACFGAN3Gbtk6kc*K4`#OG1zp0kT|02?B_trTt6-SzG&AN zcU`|=x9`fl8i3M#a)9-4DJZR*2`$ps=MIQZ2)(s#nTeBd46D#iFYR}493b#QsLtHz z!7+chVCp@zsK|1;gu`lsIwqSFRW2MI{K|`ARpszaeids0aG+Hl360aI^51QXDqazc zFtj{^X(^aP6v8U{x@zw&+U>Y2_Xp3S*|1QbQVFvQ(~y#WGV>o6+0%de^Zk!VA=e@_ z6n@9~2Vz5jF@1TLfw=lLsp*JGkCVH1^x#fQ5?@K3*~08clmO?o&>X#!Yg{tSW-r`) zq42-YI)V+{t8RD+uuhW*eWK@!F2gq<=y-H{=U$M(j5}Ab>eBT2Y;01}*T4DCol7o- za2<_9pMoY=tab)PmWtFtq7oGnD9fb+3UvJZ$#I9m-zllp0nMHGA|E~rW5lqhRkH** z>Xoze5$VwtI*R0OZ4{@)bd_wTjZu#9ES8PUfWh{E%an&@N9vnWqnpgn5e^ifG2H2< z!6dV}sh09WC3smTKP@dvj_FInHTlLUEU0&>tA++`cMMI$Xn`^vP)+;YdCg{#_B9t? zz9IMFs|2*|JKeR_W{4|?!5n>{ttzkGtxO&3d80bSZ~vFxEH zXZkw$H;<F6;oc2TdRum;(Mn1GmlzP zdC)@jzw!6-2SrPz#Kd@|1X0!b_X>)zJ!Vrg6E4iGBSc|G5d1H>fmmg_z5sA3w>%9!kn>e2S;9=3F6WmbD@}= zrs^E2v>X`m9cWPfh+d?#V`LIen|f4?_mE{5gDsd`{Kp)@2R6AU-My(hsb5l4Ga@VZ z_w&dcCJj{%{I8se6OV z)}K+P&yA9F7mttg`f%>BW{N%wyn~;$HX)$F0RbrF@J~j})3~VU`9<6b*9^8?09!E# z9tXCr4N%b#6r~#)=eHVnMh?k1mW$u+<<A)-eu$ECqNzEub=!H5g2K@)ue^=WZPgZfw+ z28YOy4ueS9Wo5bXWq}&LbK04Hq{>;< zLzkJLqacr|tk-=e96HOt*3!l{E6uuu4I5nWnAJ1eTI{@h`0H~mOXNR|*Hgd#&`1Jw zuH9%^`X%0w{kiiT%{5^xJl0&hImH&^=d{5=(Fpwo5lyw->SDuMn1f%#u>(EnP4mVn zMv>J1)_6hm+*70WJk`yrr!NK{?a>QMa*_kQkyH%#!CST`7;kBH@vqBo0k|Fg`n1Hfqft>^dQop zzUsHvT4!%k05L6}uk$?pB}yn=UJvF_o;0gzgwFu#jcBCeyE%=lA3$V74~oIA5lH1j z=GQz>ay%Iww?uUbo8?s)rII9l4g4J%O8wOm2lvbBOfCgSh*oAFT zuJ{T2j{yVkE7qDQw|wc>10UP`xA_@dq;JD#LnKWwLkqU?Y}pQU)*XvR(OIVN;{3oL^4`l9V^(ocJg|dT;20%-*vV1RELkx`N+bG)d8)Be- z5&GehoSCifsKlzM^08}|wapRi_);OdPXJZaMV^T`N#D6^r(1GxqV*2P!&xt<#MC2{ zGpbi*lS^R2?nj@N;jxpaLjSnKTzdI|yI+TUH*^rPG&VX)D(lAJxw*%%tj|zYV8dHO z`tVP!dwt!L0~l(Hy?86N<_JX71;MUM6FNNbpQRzwESP{o%;)M9cGAam2^A3qP7rd&#%R0MoZJqVj+)x`WSxCKomfC<@80~2OPPTd&>i}_KRQ>Vq>T5hlCEvdotkl;L+ z+S;2z9rXQg|Bat$4`Z(_(*Ix?p^je66lD#R#fVsY=vO1(O(mMmofw(3>1@}H4W8hP z*L_l<9d0_`5;kh-W-Kx)x_jUc9m>z6wmyH(dlg=nv7V5ejc$7a={Zpe&thuUsc~SN z&Ie&p!q*nHVB(hrWoSi*Oo{)rS|ChF#*{PvxxKI@2`$1#9PV0SJB|_JFV^p>j*5=PMuTp^f$mx5 zh8G3qWxjT&qNh*oy|w&DE;5Q#ER-c#EiS;#e%+fG|L3=L98sG&-$@_3E=?44QanH9 z@sf>XORZWJsHHOg8{Z(`B4-P@o)gTK{BNKe*ed)-&PY+J*5FZ`cG|X=qF^C^RfIKI zoY^(#XRPa+o=faT?J6I(PNQgxPk2b{4H4o`_BltX8>ZR*)Z4;VbQDwNR@~)i|7|iI zT5;mJ1fdwVl!j`F&C+a$1*O;3#8aW`gO2DN^R2;oOVEV122b8|{ek0})D00fTZJmI z!|PRn&A~a`Pft&?Hb>Y=jKVf&h5z?lO_=bjhc_mEa+&xPUto>2^o(G3vEw`0;T=^| zlZH$GnGh|~#)FNmy7N}WObg|HIrf(L5it@}CUCi0?asRt_oOTBsnPJHI-bj^GA1#)usU`xIt0LFI6o?@&I~8$3F*QEuZM6j!-+1d^-R)irWIF7+3Rk5YGo6F-ER=fC~P zX7Vr;$KMQ*f<)!6BNid@?ZI^gHC3D&4v_sbW69;07XYf(72nnW9avF@TJ_Dr5Tqfh3KpuAH&{TC`lhAJ5vo%q|@Utb+}Zk(>T zAOA%%O>lc#W^mQPD$>w?)saAEGBmfK5e&AQ2jfM~fN1 zT^r{OK+S?w;0TLmEmXHPqiaY1YCONURMz>f5F?I-gClohh2)8KU~Gq{C$B?=gcc*R z*jRO!A}Vj8y|-u2$cZD1z%5}Ce#VY#prs5FQb5=BUK;vW6^TTA0Dq`dl%=LG|4+$AqOGZz{Y;t8@^ELZj z@z#k@vB{Lyrb2r9=rjC$gFwJnwWcQH@1_LIPwX+rQ}Uqv{JE`#Yc5=hyC;-SLk)uV z-~O41^(_Dw^pDH$oo(S;H5-UslZBdWVB-4wqIjPJcK3&?tw`s6{a>N}_uVm-yxC+6 zPhqUA>+0YQPfGQd)te7H3d=;_9=ARpMvlkn68)4Zbma}3yQBhBo05EHzEz8WvGS!G zo{?WiM&kO2I?|o*En=dO-fxqlV_`9>m@n;_-gOcwGf{B_7H1_shK z=4ccYN->wGh?pDHA3wImrMGO<3Wa81h>JaJtBBDI6Wm*sht+J)j$*%b#+B_g0pDUJ zmQqOw1g{Zp(_$79|QDMKD+Pm_k z>D>{XQ-4H|O}ekwc!>^LiwXrGr7Hn?ad+YS6*C5HQ3R6E74UH>hfB~cQP0&sywmsJ z*;-*x&Rq*F-{uU{s3!;Rdhm-Tp{CI$(JIloxg3egbBbNP|K|e4i=UB$C0j@GdbkcR z+fV||_YLwqvU>!v+@;Khq%USh`xCrfc`t%6&tE`caWHicnFwvNk60k4-lO|l6>HKabCs`O1@;SS@;q{RW6<~nW!S$}>(g8cfZ3_wzw2zNZXVK?4Fd&5yHSfvFA0iv0#Uqnr+|PToA`M8#(t57zUG(g* z5BA-3Uzrzs-fEl~$_^`E|44bzH^w4YJywT^em%-o=@sMg{1btNb?6~et@o`y8Ak7v zSa44H*-@vI=|*3AKW#Tk0>GbSLc}t_qE}Fm^PNvV(COF3xM~k(yO-eaBQ_nX%rSvN zhYV$gVdo!0kJqD1NHy;|k&m6YEON9A{TR#F50J%gM{P7kG7fUL;1cw}eRhK|BKVRBT{sds{scgG9SIN*L zadk+ZqBu928kS%);1!+wxJ5kdYnSdi6TH_idnQ;ipqo*C&GFRd!{q8BDO-u6Ua*Io% zz6nM4kk4g3WeD}xY=%R(T>f#&O8B4cHw)P>zbawo#qKQI`G*cdOc>2&X)fYayFc=gjS81CLc`a3u$S|mcrp}W*A~oi1OLGkiyx|}B1nB9LrW@^h7^uYk=uF#i@$4-FGJbA?6!^d z{jM9bhA#TGGSqzbOcec(RxRI`&+;NwmS8dFcR|}uf|QYCRcTsv+V42TJ#M{m?+h!@ zLD3f)wSSTF&=4;3B`FOJQ+ZT4!c!fRxFdIv)MpNtAwU+1b4WYy-~q4ys>h z)EBIk!m? z#H6F;AFJBdRa)+#I0V;C$ja1pif0gwC_QnC%B5Nd=Gj^=*|=%6*1t+i=8N-RA2-Vo zr^@YJE(7S{X;n;P<{b2-LWSn@tP?JGo;~o;G`Q6hr?|F-Lw?FqPm3tkWQlg3g{gO=e z65FtIZtjVPeBEn;xC&>ba{01TZw=#+(6RHhtavO^M|5^FL?Li8Z#!+O{0{WtR)afD zhFL=9T$w}m88NTisdb>{A_0g$FDCQF3Ryxof>e(G%_H#&r8rkV6RyVX6sRuuUhg%- zowJf~aDw*5<|uM%1}?KQeJCg<5v?I-B9*4MYO-=4Cr(v@tU|rtPK`gixxIec5Ifi7 zEfxeuu$4$AdVLee3Dj|wAXS3avjM{;tAW%|9V5SprXEJ-0@~FZ#;nDRbdV}%!TrhU zm0(|0L6(CgFSjy9Rhg=Zen?&mc}2Mbvwhj0m2u8X9)4^yvS?}V!%;Kg?q;sE_4 zqW|dJ0h8e>bF7uCb4>MS-#vSw?+}kH9MX(XIy3u?-%wd zqyK0f@Nna|<@=faIv?F0=+2ypaE&phI)2la#9EM6jNKRu++IHwl{Y|GD(pXFQYGgk zw4z^~+a*I7UELh56&=1ukno*#0)s9$Ur^8n1`1Ej`DhD;K-vDgCL67Lc`K{rwxy<- zHAo%Mi7K;*BbJt0veoZyVq_n(ZxF{CX~3MyUq2*YcABE7zh-O7H&_x^eoZX?_3~a? zgf6GQ<-rk=y8F71e342?Dkz>q?hvRXJ}d5-<=alYjg(Cb-P*kzEaB@XiA_%ruUkxX z&wLm>i_SEOJceH23cES78bo9Wth#~ykJE*VdVB;v{7h8%F)HU8gHI*+rOKK^Pjdc@ zdMs0Ts$*(XP(4{_ud3C_Nr0z89n~pB795oHT@Z<05*L-}i*$J2dt27eq}}g$v>f%b ziR@^5Li}q{Po|CzKJsYv%<-hzuV1@RyrrP;%445DC7lo20vJlB-HVLVyF+~p!L{b~ znFcj3S<0$L;9JY!V00OA81##bq}wM-f$Bmxu@CJP20#}x)tu9?@_BP`Zmd$Ag#oC^la1X{b zCWZs$m})xjKLq(6l8zl<&ieGc*ce6!O<2zW}fF!uCOQv>m#lq z28sKI41u+yTaU4iWdMZwQ&O1C>JnpY$0ok~J^s-pg}^Gnn#w~#S)ksIlu_Vct5Px_ zE>z`7WYL`eI*o?f-YyMj)$Y0|F4GGD;yk=*g{2~|IqJ)$J{H!#)_y~YfC<7Q5w%7Evu{g(raEf zQG%A-Nj2j}Oy-PC6cT)(CwbSu7`i6=nXdB1>O)yAZv6Y?i= zZ|k5vU$v;EK)0nSC^DIF-DmZ}Ypffst(Aqkn!se;N9FqZ#2h}!0{Qz_>fXujr_migl5Xi%kc zyg;N~G#1tspvuE#YH!9D#n|r*8<<)TK?w*c zDI+%_;Md4{jT~w6rKEJbpp83+Qvos~-sez=XY2ZWZRs_hu13Iqw=_JHg5^2jxyCk$ ztk1}rUqW{Z{7>5Zr8TeB=ptgCGVG1a;3&XC^hH!fkXWiO@WAcTL7Clm?2~2AZutNm zm=*7`&jv*g*h0gCaaTr0!=hCwJnA!dCrf$$Rxj;!E)>IZ4MUn5li%o3HX+NTsy3oN zaC{pt*5gB}XqcEzAQTt%JAUMXW|Y4y9R*sk3*B9v7%M6(3pht}dy7^@EIkKRo8-x% zF}=lBFE;nxRCT<>SgQxQ@po$z5b=mPHEv{?ycz%bQHH5SboI@rXi)i-*qgJy zh8CBk@K2M?TtIUyozVd5si+thb)limN2}Z)#X}t6{n;UBjMkyRB&!^#pljH3hh!m(?<_d~qJ zSHBzbH<9=j)HX&)e?6u@Y+9-C!b5bFbFj0b86X#(%H@!4uqY75YOXccNDmD$(xj9g%X+n)X1a15WNiJq8y_ z5-DS8Q8O)U?n*OdzC3sqeYinz?>FLgu02L02}!RE2yJDSZ0N7OW4#Kzk27_Q1hKKP zSH1DCo`L=7>}$JJx4DtuhN#3pcQ2RE{<5w%_Cx);dg9{s1mllbRdX7KOW^~FjJBTk zSt~FMaCZ$}L2D#S2=P!E&^~CYqu=%*pH@fz;!lwSP3hj&BB|Ia_jipEadQmR=PTiy zF9&x2Zn0sz4ZvJ>AoD1cMXooV@%cASijfq$zxsEH*R=khMES^XPESwgc>)6ig`a_I zZiCsfOnyH$4hSG*41dWx;C8gO#3w?F6%=TWVK44T-muQGOEytR{yLfxei-k$L` zu!WrYZh#-^wI+Npw^(TfGm8e2xSAbVLK@a@_HE26{t zBg&tdp;(0joNdKDFXG@+&K?1yFUFPc^hO$3)5SJ2_|#G@h$iWu*^8qUwpgNi}$ zw^xDm3Y~t^%84vb_h-FJ^*u{3KRK5%XY;eNQjl=SZ_p#ZsxY1J-KueZYdu+pfWyOw zvc%omLe_$7k7N&LF-*nY%zhi*)vI%@6ma6>R@G@p`UjMLus2ox(}GxZBnq2J$g{RX zV#JINh=@rBSe3MV&tq~Ke66G4UH)`C!e#xf^OI8VXS{sq*&ogEQD$albQ@ftEWj9= zlv#@SeI$u@`dZcGR{>GT=UIsjjt9CM2`$o_5X^=n1N_R4v0eyHN=!`G2wuB_UIJZ@ za-W`$&-BMCj_Gci$fTT$<}brle+cI$onNii1w33FZG9J$yNkbGij?6JcK4pIE#x=C zDCW26ca2X)sVCY(lRR%z;Zi;~Y|RqKb}6n<$X2^z*%`CdFLdLqG{2!sgSz#@Z9pm`cGLgylc zS313zk&8p}{qw3#5qQR?SdV`!cWwFRJ8B?oPCt1xj*gzOLK(n<^5VI|SdBFT_uy%C zSZhx1+t1m@zSnyDSXegv?D7OB>3EZnkZ?Y>lFsos zmQbH68LMtK*w%;$ndu7jf98@cE4d|KpS*~Fi7!tGmg?ZymXlqtIY%MTXV5(!G6?I% z!ojcH)?FK~j2b^RKybmQ{JGld5JbzJwQkmW2?{9nxc?luXd?zu9ZYIO@Y2! zrNzI-%G6(77YY8T^63}z?ar=67b*%Ut*>Pv!5->Y0)~u_<7SG#2`zE%W3E`_`<-`;Ue@~csOywb^D`8KZ1dm=R37gq;)6+?-&%*=b0IDq`!_* zTBh3YSy<52G{Ij$>HiRL=@?0xajeN&yeX5jtXQnaMhBSPfOAe$JebK|*Gnq2H^8ii zLZ^1|%t^r6fXa1otc?OmO@RK$TFtoAV`)GJsxLO2yItk!H7xk7k^?9d)A`DoUsraZ zA(baBdUMY_x4(`Q8{|ntSBfh@cPCow@&CeCQV@Cv5QPR2YGRcs^dD?{1n-<>V`oYZHaA0`;`0(OxSm+$ z>O?99F{b-MRgZhoT5b;#)S67?0FH@8{KKXnEvJc?U3(O35~gX}ZWt`-`be}FT=_zu zo-O)puSkxFR+0dnQrPp!7vnop8otl<8E##{i`RyVfzwrQY9(FopMA!U=}C^Zd|L9Zpn1zs z+9y}FhoZ>QJYH-AL*(GP&czRDZ|zO7@H&d&e^7DYqZE`(Q>A*xE-wq(esojG{l9+{ zGWWQIysZUA{G`+m1J$F{30erAeXUbak%}n1J)DayU>8aP*vvfEF+DZ)gQT}BtbiUY z_S=+Kfz$P#D(Qj?jQf8yIdWyg%z!f$tY1@N_NT&6(97`!_4cCC?_~J7FHXzNjRMd# z>tn;nL+x+$Z-UXC>xbSZ_3^}NGOjwh({R0B6stH`Vj~qlm6;)b{`&cMmzVOKVbiGe<>CRPE&jNn0OrT3TA*nh<`Y+?Xx94J{_HE>HsW4sG)?7&540?_<&@Mso<`E%9w6ev-DoIW6S@Tr2Ka1EQ+)3s zbmgBoKxo-YcP; zD2b6Kq79B4$_cyoiKU3&1r&0d)O(})wNLNv{5<`Z*BrA=Mmv9ei)^;#Y{MM4JvvFa zsTZu=;1v{9M3d5{Tl5xL1?s9-gIU_2g#q30DlYR`)D0tp;#eA8N-A>YJ+HpevNn=b z5|HaC#^dl_Uqiznwr(Qd-n*x7alV0|vtTk`Qz^GPc{c*MXwvZMx&k3`Kh;TzylMRgc<@nIXwLY&w-=Ds|b`N8C$M?-{B_jl&F6fso z{J7xlixh`7fXtaHxHu7KG-120aStO8hil)%(to?n*PPIP`NAW{d;X?!d?De6Ri`X$ z2m|9qh_C}R-7h!gl{hGCzDTcj&$tNoHlcyp4p{Ykn%@ly!+7&~l87)aE08 zQcI1q&zqb9*!C%G`a*PTMYZpICkZb9vy=X7is#zSX#IiM)IF65?tKblf zCf{_o`vVhN6WeILu)Kt@zcfNFZ-eGBxrt0Ie>;9Qp5j=9qC5(HN+pxcY_wjc1d$Pw z>JZ_Ln6_K8F7LMaWMcAiq(KIANQxkGG#MO0FIK-_KFLln{k3xrIAypEj%?YKN<*g) zj$-I91hJN%jImt9h4Sgfa(n-<6XlFC!P{9;+FPxWOxE7rwllx0vM%m2E-wymvl+{Q z#Sy|We~M6mvo6{DnGc{Nw%j#qe1^&NY^b=g849ArxFfb|%c`u{rQW_xk487fGfiZ^ zoSD&!A6f;-?Uuxa)u~;(w{U}6Ua7`wkFy;&T=ENIEn~_s`~11!s`09rR_2)ifGX&bht#unj$kf zJ^#g7sjlLt@UT=1?CILu-+Rj(sp2-eLV7z*PPCrCZ1SF=e*Z8FHqUmroB!bH|2M|b z^~!bRIRHL{{qP6Czo!}ejkX6FeB}|sZYA8vu_rJ3|A^~J->^NEE=)%MoteONsaAB_ zbaH}peF@XYu5ReyGWhsJ+`z@l6?AV1IxH0q#+_E^RcTC3Xw=;zfMC`{2X>qUaCxoj$ z5b{D5TSR+FGG`qBK+)b>&DhIke~qOeK|o3&sKj>-l-ZHj@?0%ySq7$i%pf|JVoq-E z4`3k?9o(tPp|mbj+JFvTko`G9Fv6@#B*R= z-50DMdx6CVY5#vNKvf~~Xq+C=;J!Z}vw0n(G0^gUd-Y;FMO||>I$4}v3}nj8Tm7vY z$(Svl)fGx@({{1{I|_U-aJ<0AT^WPmJ1(k(@yTuD%qm6&49s<#Ku-r|G4#r@#P0kDu0}DK-^bIi zGLssg*sGJ%+0Fj^KGt;N={ud`sO90C&)6t78&a7`X}W(=>8e}j9d&mzJJw%8q_YH3pvPlStT(xQ) zqcSGtN6buzdo;-rLj0?U$u_;RJTd0^(9s0?Q7a-=*f9^!*wIWdqQZ8asVh?lbv6=c^&FD)kjHa#+=kIVd517b$#60zT|<#+eCXRP)dKM}UVe(yedGws~c z;!F;pQPs{g#O%|{=h1;2?h3%oApN7fhyWPsqyifxnnu8$)c(+&r+d)nmvryW!>GNg zWnf~+zDw-FPkSGG%fW-tu_%qcx91Db92ezKpp<(JlfLJ_T8`Eex5GWo{C-l%N0Ube zph#%y{rKT!p#LQZ)eY=1J8)P-y(_m@P~H$Tv^?xEe;8RgoUln;)i(~QTRt*);d*dw zEuNxuNB18Vvv4S2*KUcUM#lSUW?^qoOKc7tv}s<7!`(c~=HS3Q)dN~m>q;Gw=-;@S zrBLr!T58j+%d``zrHOBz{pIEBIO?Kn+TuG_c=nwsq=BDJob?Faz@&bV^o@sOMn`6O zqTP3a-*GzdWw}prstnY7;;2i9hW5=H*<){gKLAQDcFFmQTriN^^V%RodTn%7rO+Yu zyIJ|nEQ^I6m@`(_Y$a?g*FNt$kMF9GU4Wmf+14^X01N$0y+SZb zm~l`}Y)8|tf*I2QWSqh9gnM(WG~2TNoA_gS4~ zR0nt_80%F7oL`K1z&`ph8S<)UPJuQ$%S_TuBCa23fZX-Lx*`mG!f`^f- z$Y7v()=#JSJ)r(8>g64Ws3ub66;j$vEMq|$3 zEAeQV;lW=FJ?N#0`!6KYezJ0!owdJNc0|#%1Cs6qwnGf+l4^298aF$=7%M7^EmiHo zOm|6Xa_uqgeID}}#No}(=&-Y`^=!KFGGOkVL7l_5Mz<2ySSmNGt01c=Xev4hD-#p) zQgvuusm3Tc6PZoLs$<_d2U62enJT-zC6!oFMaLkqvaJga?wnuxii61sC2?2AJ>k39 zgB;~tr6+E{bRFvz#d@2`&?mI2&msK&-tnUCXm_x>Pea%c)ISUlj!2T61UR}xcYM1Q z@_5iVaJ#5Pbrv5NV*jbpJ~YmEq=vo8qc@`opZZF$FPXJXvn*iGp4m$6S|L&AlP&?U zpVvjJ(zMZZnjQ7qjq*<4ANzL^NuZYbPmc0H*e1j*mlYek?z{N=tO;Kc#QmSR1kY72 zOHA(^)3yt@S@gs=Uihxpatb{U0P|=4)XI>plV{*F@*9J?&dNsRn>=5Plz_BS4fB3m}>IS&X zYEh&bsb6F4Szb#Su32D-(zH#29tChLZEfk^fQA(dGf8ewaBSCw ziQoUU9;(qQI?jAY^7_4EDAFS82aK!Js4-f=aXx;_f8~iofu;SJt((OJz{!|gEKB!Q z2VAz7I{o35EQZmFR?tgMQhL5;|9Yr16C=4n*I$?zZNrO1@g|pjP!2oo@wyz7%I>=Z z#Gn2Vpa#j_`-n&Wev^sZPJ2IbV7Dz)|Mg@8?F?X;6=Q!3$d3gAXWJ=WkUF;~@0m6z z+5hD#H)bz?cyS2_^MhJKOlIU4%)~meTW$wYVGqYH*)4<-e5lX;sFA}mxqOf;SBTsP zrh9HXws9LB7Dlq5*X)^^v-X5Wlc5x9Jp^|b=(YY)!g>c8w2W^FxT5_xC>2e`oRf3K zf0+SJnpHgUs?r`PJ>MuvsWmKbBrAnZo%jm5`7NQULF!oyN`T^AGVM3g30Un|T_ZYb zE6$b$+|;#+&k!YTr}iF8nt;9t?8>axo(hcg(q-H>V+4&Nj}va1?N(OITxzV5&jhn( zG(hPf{zrYR0wN+5OFn#PCmZY^ns>9}I94tL)w%!fh6f$Z;=Fib;zRQ0vSeb&j`3V= z==h>MOXB~C`U<}$+c(|`NJ>k0qjX7^w3Lc;gLHR{MoLOR8U|t@CEaXvi7=4P5dum$ zV6f4g$M<*6=llnY=eh6e`Xo9^r(vnng3{Ix6y_iY;e|NN zm@9I)*BST>X&UCCs)&O}<48S(gKu(&^kkIr9`YF7;vv!4evH{u8kvC@Xq+Td3$TYM z2nbll_n^ortH4fiH(QNkZJYPGY+&3 zEC*Pwpq+yvo&EjE!U?4_^NiMiJ>iVQ^8J-PDc*68-$gEWF~~3Y1FB99Yjt=^IRE#n zNZZV<-6@sTK!&JbM|tT#doR8CO&+cZLtFLXvn zA2ANBGB0%Q3STOszSwmDVvDVPjV(} za+$Z791E)es;RAPn+jZn*bYap5z&&7#^H;?#;faylF&RJKGD(tkR6?YlIq4Y#qJVS zte4U}3|?a6t!8!g06xN3qM>W$`U5ri$8%t{ZRNTi&C&NZ+;o)gMl+kg3@OoSB$=qg|R(_ zREV|k_FN(B-lzzUyuStVUWAVg9}hp*__J?nx+pkIlAy9J`?9KLmuGVmwl1Euu)7}E zz~F1mKcggy6n9<`gS3TqbI1!Ie;DYVWTRk@KG|LLO;_J{z&=k_^@!nN2VAS`9};ou z=#-oW$z{xYHbcsfHlw&fj50d>BuR+S-SQx5v+v>!f=2j~5!?#kHF+!?sWz0au{Am( zWJ|rwo#2CQG_Pr8USDGebFHPop(b+MxWu2bQctG#pUD+-D6~3P<5Wu$+<=LFaG~4+ zyWZ^yQ&4>2{jiOif+F#W2Lk{jstE1^y**U;g|Qza=>W;!>n?daEJ0O^7do9b8t;h``P8x77%8+rO~H=nl9Z5cspSp3(o&Z_;gT|*YJ{g=9?vzgsaih?eE*qo6SPbFD|sm24mi+@YOq+`)fXxJIw%sUzV`6Y&%u0Y?Osvzk-)D^s(If@J_m+u9cHdyvi?puH9c~@>lQA+5ykjB#<0M_a6UH1pia?k(xpT5( zl3Fugy$eLq%r-PM2wyZ>AHg%v-nu9jpL0Wg-w0ziRg`^;vhyw1r3@%5fMYc#GOBy5 zJ;X1^Zs$~O0e1M#ef1xAV(A~cF*rQcuB{Eo%IGu?7RAp;wOZ8z4pu%d7T?E*$ermp z=@Y}`o6ttGKeyZ>do%B2K@md2k&lhtuNFD&{`d9m$)i|6Zys^2bP5nUm2Ew*a(_h9 zmtB&#r~ANEq9K&xxmDR=&1A!R=^;~{ka!(hWjplCr%$6fuRc1e4Tb( zsTT8X`~_59?S4aXP*=d10$kpl$xWSca&Xwns#j!{k)h7& z4#pd_3}eAx=tj12)(csG03e%HVgNo?b#}%F22h_Zbu2)Vzmpkp@w3JR=FbPvD%AfD z&e-^7ehz?gT(b zl=t}W|F`E0Gqv8kCbl8d-n=kKw_DLDHl%i7$qEUVm@+F_G;YFN3SfP0ggy6y2;rZ^ z=sTMv_T@x(*#n{|Q?XpF9n>|E{5ld|rW?d6_=K4wUO&*hoUOm4Pwa*zJJ^f=&6;~E zR8Fhb;&aUJmDwth2cO1z7ZuM2bH>3(-?dpDe{ox^yvMBOnx{Zg^b@P{~4dAHS{c>zK z&*|EYf!`;3mM_R=8tnpbQ}tW}%w*m-OT<%thE-O~uKj!U84^!o9xZ6bBBjm1R|g0h zcehJ;Lc5}$%i$f)yQl0=dJU9;xME0}^g--ZAD*HzM z`APLH$Zz|Zcs%S<1XPMKAmxkDz9P9v&9x3E4A?Wu;zWbicXyS}=&6pU+Su~Y%O)?F zi3ySkdk`3Zf?+ybc$HTmf{7)hzo>zdjpVfb|n{G)0U?_%8)jU$YaT%KvVS zzWtvb8HcF;eu|H#eVJIj>#Fp`=&&XnZ3ZPSgbJ4$o5<;(=((pDhXpM08`b3Vy`>_} z7U+d8!c2bO1&s@iz7M|)eERvj0~J9mD}J8zAxju)J8U!zFcw6Wlt&~|aP@I%m5EMG zA^2cVGKEV0=|qlKJ28nw$5j65Uhw5%)UDT=PS2$AFzXX;<>LuMw{jrmoj&t5By}=* z`l0xkb+erqplP1&OiWH5K4RPQVPhXAH7ab;eLd>78fy5W{RC>0YK=VdD@8>8rM)z> zenHF~`NW_}*ft0E?p);fp~)9_g4ADy*{mm70;nMi)Q-YFaC(4uTJ0D>)@$0vkf#

#LvBf*MluTF}*`diwI6)jC!Zc}w61DP=eGHEuD414Gg4e|5eufp|{ zycY|0F)@M~r2v>UG;IGFW34z{%g0EvJs2%f6}Wz8Yru)(wf+D|>u<3DsIL%dS}f{; z>zwWZ82sXXX(NK*@*B>pEg56&ma-&CcB@1bkxN6ww{cOYr+`}vE$hD2W?cUU8A%^2 zNZPvbRmi;SDh4NHw2XM6Aw=lC4obhIBkESaJ2rdFk6&9MUzpKBm5A?K$Z5nLo20qc z%5W_y{?Tx0ZfKJ0@o-nORNdXDKmX$3C?kbd4t$$m{Skiu*f52UWxFohY=@U)_vOze z1};B*tFpzC8*Jb>mF9f)&p(epXV2|xm-*Q4<}Io%jL#}gxNLXe_1S=P6LoQJ-qH)O zmpUW3`DtnvUpFu*lq&Z>_B0TYpR1l3jZTBAb$@&Ywa?hy?(eX4X zifTG7*o_qzI+NfR9bE$z6%|#j2vt;I)TXZO7s%zs#scmh@7ooJu)9MG@vqB7g->pC z!DmT4oxHF{^;FkKf(M#$9aYBN^saLhS1%Wux9Tbem_eJH1Ju+2Jnrjco@^!nWvrhE zGh{6KRWLQ7*EuII&50I*_Dh#uenNH9Mp72eTiA?qGccOV|LY#diScPmDa!k#lXt^0 z_V}CW)l3Z}I5qG4p-E6svXq%FUNb>Q%!dzOeba7kZiH=YBwK@ccs_4EdzHff!3XdY z+$$LG zEEG8Q)r@Pc(__v2LVKgSXRBIm2HYqgjzxR|cjiEH+HkR?2n0qVoJ(m0|5~^o=&AzNq6q z4$k_hf~9IZ;1Q5QP-Dp_Lc>_7SXp;2bzf9|!OTz9*_4LsQ1BySk*9eVBPg3VFP1Yu zv=@SD^O5r`Q%L52dZGo;v+;6Q7Q#zE%rqfI__;9#F z?)gyy{e9_=>+FEp8ZQBlvyt?aY{WHxXaGxD-nX#0pXYxQM0kz(QyL)$pRAv=;u7uD z4&|1>`)U>M;<9)K=-Xd@PPUy)P9K9+&1*d9$)HSr-e}~&o4?Vy9|=VIQGS*Ja}!on zxX1wNo*ck|GF#U%G)u~Px#m{9_0=?XRnB1Ogk^n=6qvlq!z~FDN*G<(mxAv z|5NtZFz{oOjb{o29Dz}-V%1Y&5!cOzEIe=(kjHi<7qf$w{3&h-I3&*Yj0gRMkN_Bh;I3Yg)Y#Pgsdo`iYTmx&(Mjo)j?10Nd4NI*zTBCk{uXk# z_D_i8cyXergZ6h=W997QQoYqme8R^+CUcA4iRzLP6X)tZBvV-FpV{>aaelwzNGNX=8&(lg-Mik23oZ2JS?kyUFYa`zsHs0^MBIKD^|)L z{<(qfh~9<*xBHJ~lU2bTinNXQnQE|-40kTvaS#YZwB%aok_Q39JX6niDp^icdv;!m z7Ud@nY$(b~5l~^gdbN|LaWNc9+JNBkpv&3)ESPXvoiG+)mb! z&3+sj81?!mIWQFXUFW>NSo6^2vM|t&6jUhrH%L07!`f*j>q>WTTM16GbFMD%9!yj* z=LMm1pC#GbuCxB)?Q-Y@8UeyR?&Ftovj&rYxkpP7uT~}CCbSv+nt#uj|8xJq*7~`} zERLWdjT7w4W;n?<0$5O9DbSd!lEM{ILU(10)Xn1pq*@K*s5nmY;QPQa{>fvm{3iOYKB)sOdL;l@5-{_1p=`>?hui57i z5DEQ}>^ya%04!i46rqp$_-C!|m$3ZX>?^aEg<C0DfqzS0!0If_8s0$J>V$ujS}( zO7@e0K-K{Ei_QIjy(Er%7D;abb&2b!h&AHQ=~yQ0{8sYqpG)H3Y2;qSZvY43GrOVO zziVJ^4sm|`Q7spXf!o-0Tn=7dGPw=nSqi|VoZ1%rqO#-cF`{1GmTf+4_@%l2H>)J7 z*$r6ytb_pTSAy0GNZx)$?>znGM6SenXmk1n5#e8It%!oI1fIfXkYEPB?mv)!fwH?U z^GaHgWnkD^KG7RTG*eeheBzpO0Xmes-MZR@aTFqP9@iefsYYh_>8GDrrRv;vZBrtU3n&x(M!>lS^O8W<|N4T#RIjM*5y~ZgJuPM*}iXxl@ zf_aC<^r4^gzWGcq<%w^vh9xsEeRt4ep*6v$RA7jfjMumOe%LC#bqu%zSy_8n)ZO+` zC7r<=mA}8+rz?67`}PFektSl6b00Is?@4@DfjWf+TpiR%4$o&b?Q6vxRyVgUWk4}`x z)CEBQtT&UxOHr}oFRB8!W$CS%DZ`9Xq5JJADLU6rZdke$4p1w8^5B4;H(Fk$dc~E3 zK?;)<(tx=wX|&`f2kbf26#4w4qr<rRr7>ir+T5F<#qmjMJQO!az zYHDg*7q>{!&&AUFV(-TT^mv@lAs0X8mTP4J;w<*F_LFufz*j*=W!^l-Fe{?ws}d-=V-?Jd^>_LEsP~WWz*o;4|VbT z%sl*-B>DTSktFo{Y-5D$-shm=?IoQ@`ietWzG?rM!TjkAsqhog2}tU|C?yR!RR|ML zn)UlrUu&{}eZ!}CnrhM}+C8~J177?Wz>gj~(&gI$9JRo>&H!8-gSX+s896y=+?aWlWNPBc zG6!tH?Xo@5M{W)ab>2r^P6WKFJ<^uWMi`bE8tey*4Q{x_D%GQ)5x zhhs8ubVbBoHQvl8KT*La__*1~B=BLX_orR)KkdKv_uoXROLX@S`e{{T&uM}TG~Ky_ zDK-6od>JJ{KQ)XfftpS_%-a8RkA5eO81ODd$Pkr z1vmmYYr9Yxz@_u;`yh72i3I?w6nC3Kr*}UA_7znOGZPRctub`foEiGWjugmPI4I(s z?fi(Fr+yG!>Pc68VBdxc4X4YV&X3%sEAGfW@{N0ZtuGPK|fedV*Zo;TqlDc&No=Q-Nlm^ zB-oJ1_aF7$IM7j1v3Po&MRf9xtbpZAdYJHY{lrK(I`oslmMZ)~nVuQAidqo{BuMaF zgn=YAU;m2&Ba_2MG;ptU?O>%@BpPXHx-_p{-zWRn1T+UqvKOD>C~|W%TkBb|nxX&& zfXen`hxSB##Scy#H+P5f@f)!`okdpxm$a`c%=Dfv2-?8-@E01d5_#d1b?o#>UDs!U zOu)=@Yk&LfwUg7nX4~2A{7a#^rc3gOke6`!*0q~zkO+U9RzaB z?owC+e>6tO$9WfifX*AN`xB7n_LCZ{OB5QU%oILB9H zsgS%}OCh*S>0VV)nX53rFVwxH`&|+)lg7^e&1U0TAio00)=(NRpb{Vh%kFS2`h79v z39Z!ZEH(p2zQeYN@Uym8K=c`eaYPk!KK>8GY$vieC7QUlR}@@J6f=EyQ}WuaJK+_|Y~Em7``xlfx| zMg*)r$C^y&<1t=84T6eK)8_5vkcb(9fUCK1Mw}(V6QQl8hUW8ks(TVc=+$WwCb_tb z{>UJ02F&smaP22&*?MQ01r^CE9cOdP%w|3tRA=(ze+1N zDL-R9Zk9cXY-Z+ulaY|Pm+LZF_hhNXEAs+;Qi59d|H8+*9+iq+_cq2YZ#FR<4gCycAowzANOt@ ztjtxPH;e)4S7dYi1ZxLaH<4~Ze_9HBzRg=;KSDCXFrgdf=#A3~&DJ6~>>!I0S%yId2Q{<-c@ZL7l0Y{%rG8GBUnt#YInCLw!S}fCx^!JMU7Sjt@Ns z`qe4NS7dBw?(|{0ejBYNcfHr`rfH|J^A5#O`Kzy=Q~5LMeJ@|VS>9P5E`cSos}X9k zqJhX+U(=1ieH>v>C?8mV3A0ss6?E z7r|rRvm=f(VuF>keIHuMK-{I~P$uItJ2CAh4+K8nT-q?Sxr&ZVRm?*{F(mx!Aa>hb z^qwhBSpzXi-i12V;Uo-OMV8@g*K+g%h=|vp^vnTwMIf3>~d1CJa^jnzQjq5~)x|R2Y>brux$&Jpf-_Nu{xL{yyut!IO&) zyNKZ=qxC!Sd{0dkz%*T6bL9Nlb(YX-->NJ4exoD*a4a!+tGIPV_jveL^x?EqWUMQ5 zJ;Ji%)09OaT-^lVU3(MtabMrwo`0-QOiDA<`C;fmX5#bBN#}QJ^J#-7^cK@{+im%K z>hPu-b2_RWjJ(;+5VEd2!p;6YWNfiQ9QH&sIrsV;rS$%h!HT+_kP}IkNTe&pZs@)n zF%fyTRv60l+#=!=JJS|yZNR`mE=r0b_21U1W zm9Gkc?V&IYIG%yZkyA0L9W_tDUj=`~p()sI=v;uk@l5mZ`dkHI&}c0LY3%Gafkah; zA2&e~xFqMe*<*_FyP?Ez@h!l+BOFmI{9u7Z0B-kh&aZV$lFy9Vnlk2K;sjee-Cba*d*QsYL{~DYlH7= zhp{g=Z4}wIiAJ!eKK_xFpI_UNroongR`oB^FiW>Iq4|j}-tb_BtkEpY=Fhm-bKGrd zt`zv~RLZCL_^tOmrS<=ezC2GL-#@+~OMLTY2Lk8peA1Z(AzdeYt znd?57-|;fOyQ>b6yHAK`fKdI$2Pwb1Hi_8nA|-zDLWK5goWIsM;+vOj>jJuKzLV>{ zw9bBy_~EZ46!T4I*af{+SOks{KO%{-2x~}U+{uH z%p70%p&V2E7FU%;G}Z-B#wKKc_T7Wd4<3{?^+ngFyU4CtKAMc)2lcX@{(e~q zDG<(Zg|zy3AKb#5-nG8xVQKycK~2-IR5#u#})SI#68{QYC3 zVby99z+`_^{%FQ)QU@ZWexSVA!1cUChu}TG;lshVRIm_*rC!aQX2wq@`lX~c?A7Y8 z?>;2#eYo4#-<$_dtRZ4B;P5AD^1zf15KB0m44yX4(GGOWDgvgIA8IR>7b2h@jv9KKwX%BrsoAw$ZwY?}-EU*d-}HFIkWe~mZ3oHGcXNc2D!FGQXU62;da zs_DL0(@g*3ve3Ajr%{1*5!)N#d1oX8mp(WMaop{-{K9ECc3f-s**8!8ObpbsBq3Yp ze;gX`V>i#DnD2yA>$&_PK7{GJ67>7zEcgS`E}FGq{Ou+6hAa;OB^WInwBwK?uv(yX zpb<7_Ojn;PiyRq2;rWGG%S3V!!z&B3mfhaVdhFOiveUCh8wM50kIk zRKoQi)C{ybaa^`SR4_ctr9mZnpr~H2$b_$a^EaWQv;MdFvD@t?A-7#fbCu9gnW4KA zp-{yefPYgPyQ=fwHklctu6(z9MIqLYvD(?2HR(dl(Snqw9VMnBm5dz*tSE~jXMR

F)@!D2Q z*^}QUcOb%)hD<`m)-2A*O1>AK=%G052LE@hg58WWG=HllJy$maGU}i9%Z<-wWf{;V zQ3{Ppj#08*t|fWgVqFNIG|s=M^e0!fXnT9y{Ub)(?@y+4pr}B48_IhA8g>Esco6iJ zjY}G6tdpnnBBs3$`aRnza0|J(|Je8ukJ8ZK);w5yS{y(}C#^b~YuN)&nfx5I)O z7jE5z5v2);d;;HfQOQx_&s|3@hGPwUE#p3`sI|cSNiiGZ%oOI{cKh z&f88dvJCnVP#8~pg(3s>nAb)ojg0Ur7CyC%QHAX0f0Z0dPfP2U#n!Mr{YnRl66y{i zE{Qm%@9QHJ4P&@Q2q4uLw#Vj=ciiYEpo+A&{GkfdL%4RNL>?7^r94i5jSMxi_PmC2 zhwrYJGuC3LH0mRF9Uu&sSikC`{;3&9PxJp3-+sT!?ESd*zV6za+XY zf70Y#4^x18YP(4XOMBt0kzef9TaXE}n{R^LIk`Tsf5b%gZ>a1Z|M06h{yS-fPe|Al zy>PW8j`$o5Y3Wc%WK3`Dzc@Jj&>W=PKObgx^$~Q-m67}3_q+0Dx0lQ%0Jmz`O;y^r z$v0zwW|=+QH;FbJkAGAsMAoixTEAKMS`E|0e3u8yvum1}{8hF{wl4QHOrPO<;RD#} z`GBR2CkcEIu26r>Gna# zxUbhx6OeYG)N(r zWIf6t2@}Hkj@pMU2Y(Wx+^=kJnE;MNUv~wZkKs9SUtG?yocOS;2tn7p-~MB56rt{Z zr7sfs4FjUcn7>n-vitA=^SSkX;`?Jr4vKj#9Qo|!a(e+3*Mg6Kid+n>>$M!-0X&FNLzNfq{zdzA2aj>l=Uwj%S> zmAz8bs#-8x>5uxKx44T?zC^m&3GIK)NR);ZZ<`eV9`zS=ap08W9Ki!(=Pnxw-EOc9}RY?+ihHDAHJXG|K;?{vW+aR zGAPg>0wej;h(nln@{Cd0wx7vSBtHxFl6pUVKw!Wb!qh$d7OGEvAayMhL-t;K z1Zo%q_w~rf3@mA6?2oY>?-9TZDFn0D3q7h$*pPG0=T%0YD&>K(0mJ)aH!@ojo!^0g z5hik)Z#I~}9~T<=#%9%vJF<^wgS^TN_thnJ8$xn5B$h(G0?Iya9c@gq{A-sx4f&ug z$a5kWP)&Jxa75hh6r^-9|9;y9>dFISI+J;p^Vsg`!3C3fB?r3fwbA{0W97?5{I3#N z@W}80pTnEvm2wUy(wTFD^7O$*l3x3f$)zkUh5x=2!=@>~2@|D5ev%8xV?Zb0&{ng} zgCnrW!d(V=1Bk+9=6Z4y3+rS&eP*}EzOvli5Bws9!N2nDzS*L*3MW)@bM4Y^9}+v5 z5IVT+59X`_a=e435L!}+S|`-cn#GlPnZ|1%E#%`~vHpVUUu@c|oa`2WFI$O>G<@S2 zdf>!`W>?@3Q$9#dEao*eQrANF(#32xEF=ws(;Z3_JFPhcmb z^Fg{bJQOrnU_o2azKIz@4*yzv4A30VhQIp$lzoDz>lM8tQ_YCKoaZ1i8b55loCN#6 z^9z$!$MnAoOy?5@zbF4Fi#rE-uCK4tyrnPoOiSBIqg$BXNC!Q8o!5&WS4Efb#hIG}}lrOetJPvfw?d3QV>20i@6Sz0*?JgY)a z-46r54TpxGaav7WfWZV%`Q0P6D)V?4~@LvwD>gmuo`Z2=#7^V<}4J1o@H1r)>#aycYumB#u* zPKG}TD$*Fb02_Sz>>P#L6#9vfN6Se0Na8DzZ28cP)wd8BuI%{VL3|i)I-ei!IX^qO(1cQ9!L!r{teER*R?mG1!a56-q%&YA6kzhN+-Uc$ zThxYIy~|k$Bn@-lJ4V&4?~vqQkAtZR_Bwe^OmsBKLFW@t5Y?k0B_&i08gh1d>kbjJ zw~t9r++`wG;QN#7@};)mSD`>OEZGZ*&<^;U9#oq)W6p+VcRUXPestDLJLc<~dB&3L zm0i7>o5eim83Ab$K^{~XiX|$CbBF?e?eG!7n`J6w<1>g}d3;9f+_Q(i4iL+&!?;AF zXbhx!eu`g~X|w%Xid-H(GBBj}$=#vlSN zm|i0!=TbfLcI64bf_~~TG;0S(aC>i;wRdoJMG+;iJljAS0jx!IK(E&uX$e~uApvK-}YS}pXL*Yq&n;~Yjc3C&_9uYnP5nuRfM z3S~}Ij<-HNRU1(tdW7M6eX9F~+B)kf&EZqBC#bE*ah${L$I_WOb$vpd+uNo;qP$#S zd$Vzw%14=`2k#BUozIV&4do1rK=E0>pZ#a>IrV+_kwbpe%&O;+7J8@L_oRSmnRf*L+9HF? zu5~l56F+;$y=7Yna3p(5=I{5s!Y9m0;~?bD_uKH2J{{cD2;yU!;6f`cTfCQ_>w_;l zXYbQ8Y+&rz;R8`I1L8-t-960;BpCKwxZ8cVR#o!*<)=9KM1LIJq%OE+e)}j0fjV^? z%A7RzCqZWSt9)jHs*sN~hp`QZ)>rqD@fQmQ_ zJnTV(u%h-c*4`N>fOcnu8ri|;n90FaA&$tw6k(MkGgx4mQ-FOh;g4pt;%yrps+?ul z<&?<#T#KE|ljhwHMCm9k29ySB%lhnn`wqmI#`NZ8Z=6nIxll94{fsF`t8EtizMkCn zlyT-=;4xW<9Q+6KQ)Eq=iiy3KTCx|-DCYhYd~B?)AQ)3n7L0M7u93+T?~Jsj4CfoL zq7I?)(6|eJ&uu9tBz9W#!dmU}54a}$LFS9b1yapKp9Vcza*!*iO?mTi^!k6t###n0 zD{jvge_Zh&dH`fIYpryBDp%t+H$G-M50T}coiBZ9FXZTAqXD6csWpogDo^XD_y7;- zroT3@_tI-&YIO($)8Rn6VPAyTprJ2?zU0$P&t1j*URFC`t6(Ch3~GF$`n{eE+=8?$ z7PH8w5A&G-8>F9lh@M{|<3rP$N<8P9n5b)6v|m`Jx77 z;vKfDB)&0+YS1x|m%)2k@L}1v-;n4)~?cDDAlnuj-?n^?L8>byc_)P60`eg);a$C z`w4hT@=`#%hlUu+Rj?z)AckSLXTyYe556LrdEf&id#jcMbDPzdnH!#Ru^LNL1hdFdE);JN0_^=ewYOAhZ ztv*XehZ>3}MilPF2m9n!x}&B+O0YlB129F%5WB-`FPwI%Ze>k+je>#t;`u~*_ld^F zqP0e&ZB_UlZdCh0eF#FDzYrYbRB(aa!$zS+fMfs8R3W-2o)DCF58w9DN}JmK>g!ss zFIwMXW!;Mn69RozrY~AUU{_vOOb4SiRcor&ModT-bpBqUfbG%4KL8ua3er+leg#BG zU9iKT{R!GFUE_GVzNVeyC%|WQ$PAqmmleN+v7_Yz3}q+nx!UDE&6D_r8TophX~xti zgb~x5DT)&S$9tU~t`_L8x;nlbRFiwNDj*Udg7Bp@`s8N*KZ;7E+TLq65kiCv1 zOf4_jt?N2w<&cAX{-xKz&jYRX3Kc9T#Z8qz=Oj$4yDi=adgmo+@O@h9T zTQ8JXL{Nran1y{j1@$_N12@1`(^j=2nA)4wHSsQugtuDPE*_lq=|47VR_EyjY+;082L0>)EP@31K;B z2N_La3`x3?BwG>t5}58a!V?unytnnEYI1$qDO1QgR>+M~sSDDdIb( zfw6Pg!dBHFNn7@yDlHr?vTF?)Rk7TfgBC&h&R**7*NI@!QOWQFQ+twx z;ljT$Ats_zb*^R1sF1|UqB=h`Ww7o^cz>U4T_*hGCb4{9gQV{1reVkr9y_5NCt#+u z2ny%3UfJuW6zsY)7_fH!0;*=>+go4j*vFRJ7Dp}3_Uy8t`_l~lgZyXhrOfxAxRc-6)uZDZ2KYR z;*O_5YX=N1_e95d$OfLX^cV?W*6_ZP0$r8imigaK7GlHyq2BZ4BJkW*yn2QF?RwdT zt#(|b*CO|&WB7dGjv=Je$qUXl&W~_@B(~h2^G8#^*yVQaC{`-slra?ZXMNY;W3BFq z>Gw9Ay_3($&U<%|7J4ea93U9CdBZ&YLNNa7n5%wX>e3S<4GytI%3bkKGnO=y`WF3G z;~xB|H_DPxG~%ijL>6f5G09N-)gxot4`sP8ye6Nw}XSBe&;xqHMU zPO?GD2?*aDaeSET45>|S)P;&R?A23}+xt0W_*-SUHFn53_~v9e6x7=O*%SzF`va1) zytSzmQuzos*B_FlkniZNt)P+UowuN0Y6@mS-LB&6PP;SqU!sz3edyWIVY>$x^P0LH zXN8a3do4UeP>X7E067xDZm0j>+<7o049Ybj63X{ujD#7YvrMAi3pg)z#tYiZXC6*hI{0Qbu)$Z2Gz zIlA%k)4$~Rc$-sS@TKH0nP@l7xc%Z5S~u+Kb8>6xvONnHXwKu7K(}CRf7sxI;$UYG z!Q3u>1C@G7Ap}Xw31DLeGTNP!Pbq1_?v7|Jb~-A@`u$*?Fe&?gV&2>~ z+)_{!HE`ha%SV?Zz9yI5C!~F+qsyKo`MBKFbD1qJWm)-X{Kt}?P%lRBF( zplA%EF7v`$ge%Q#JIl@QQx+)%&{=j$_U~GEzg=HvRfOD%GwgTvP{XYRKjdocEo!#* zZ}i-dz8VC2!hH^YtG)4(eluZN$jV3M>FX#m8+jA$)TM?3@_jqVC@aB{ND z47KdG<5h)|RlvdSSTDOtFUXk|zEkt(v6MtC;(@$v5FuewzeoX0{2BdNR~qWt=XQ$? z9ay-NTr*lwQnO#CcWUANAu>HqQ-8aF1--%vT&un8|3=f1Xv!XHkWfQfm0!6XxdkU^ zyub*j)M-hu-`(o-zac1L`-%91Z6^F%@BBLkarEAgQ^QZA+CiH0bB#ImWwY~Mkwb_o zVY>xRP|q91gi^!8%l6=B4_Cu%rY0I&guV?LS5@GdJQ;hI{W#`?>MhU-eh6547#|xw zjteDy6?eW$> zYbOLa?X+nR1QbyC7ELvh%%0B|-_SM&H|HL4W4@-S#ITa}Ieg7q!##n^iw_!-HUP2~CdXH!G_Hycf=3t&et1hn}n+U1o ziBZ;BFwVa?D@0_$EX2Z@4Zz;-vndgf@z>VkicWM^e!ljNC%-QQnZS&#Cx3@ZC#H=BWZyDdq zCdtDKHq(K@tBEI299bzwE)M92oyD@YPs0|W|>W=Sk%-nrvmjgFQgI-^tUO2B_ z7ym$PMKHXPMLs5$cDgj|xgT=;_d{uyVA|99aZ^Qleq zsfEI9s;L6A*`ME^j7#!#XL0k7Uf)s57kr3_rI*>7vc6R<)d~*W+^#nD8@Z&eNllGU zt@^0liVKS3dm$<8aY7$^p*8aW!6I=!^F@ZzJJNrc&sP;LP$%f2eT}!%=a>cmP|#bB0h_N4QuAWp?FDFhO!O4t zY`TTatQAbA^kIz(N!Lb(!w0a_zX@ld>e@-JsZfd3E|m@rc^KE($VOvZw^8>G#MoR| za!&e6iFUcH?AEH=11|AokCC4<*bU<6Ksb=v@RO3)Uzp{NDJx$=``;amPR%91J6X{= zaJYyi7a7i-qFE$4J4Id;azc6|n3Uu}yP7({kNa;ih>n}z8Z1h2p{i}(NM%IwycuVS~V<%!*iV6JMq1x@((Dk4bHeQi z(l6Q8@&(Yq(Y(HebzU+7WyZJ%@+@$bFX41Z$Z@_eRNp&7zoF@B(}4lt(>tqjN3U<- zKT zy*-2i(jcI8ib{ucNh1ORA`Q|YQqm35Eg;<}Azd=m%+Mh%N;5+@%#Z^N@t%9{@Be<` z6Cci;v-jF-J^KkO&?}JrNC%rME3I0M2LLX|hHe^^^yi=D%KBqSp4n~qUSS&mitwl* zO7Sokh^*l7pllr9x2jl)s>>?c!1MEt6;x59PYIGV3kJJ|GSLcma3*O4XCM1zucCZz zzjuvi@R3ExpIA*^Gk`)o9Z3qu)sqTUlRp!U_hu*-fQb#qPJ9rlvBb8FmbC=uN>8%v zxXNDkMD@J0?C-tl2=f;X5n!jKz~3cPObU^ElZXaM;TOs{MhFx*y&P>TnZyiXJXA9X z4I@RBNx;ICtiNkog1exTw8sskjd1<Uwqh~ zVb%k|Xqkc+U!R=$HCWCy1~Yvan_IAfY?;|@cHCU-3|Dt31mT6C)!5m|{(K$-(2w)N z=Yp%P)E3obkG4Cr75IvS&W`UZq;Fh9h^l=PxO@eV4Wng@7wpCriwIJ%af-lke~j5M*`-C}LI|t#c+^N!+ze-teZ72&sua2Z5m~tu$!UP+ z82o9{0Ti9z79NGn3ON<7@nxm%!~tn0i>We~-^&Bq%>6DkP%Y}MtHg^_XITbq2Lf#D zQlcr@xp{2Hl{aY)B`>hF8=n?dNVY#W>IdB0xy;TCe=Chk4-eW&&Nu}@7yK8v(w)4A z1Fz<$Klj3oVK&!8^USF0g%%50BPp?ouYhpT+x#vJR$8;(68Q@$-|zjwbFpRF)!wH< z-yQr1HL4Qc-##zs8#=r2)7FsVoNS-wPJ%HlJ0!ol6yVr{d=h{i@b4wNVj!=%KELT)sxKKoU$kvna`R|D#f}BAb<12tO0Zt94Z zl`jeI5%H~DU^0JvaOE@a`KMM(wp^m;V4h4dKThNXglIV?=M(9FL9^?ICb$zga?iC& zSort`KI^G?P}|Hs@mKbJE{`2U_@=dBxsrcj>j!5=-wfvPA|_#Yqq}LSQc%~-x0>M> zT$}#ZDxHrL8u<1tQP7P;z483bS%oi0Z-Cv^0gj6~DT7oTFk?1(i~`n9xkz@=oDGGH zKX$=T^pg1;Pw?sm6_8XDb>ZtTxmC8PxZfv||5tJ%^(V{wpz4x4h5@|V>UD-@2T(pX zkgFr}?|N{Rd@m&U`m_~g5;ZsLb%8z8Jp!JH%O;2=c$dw8GNDIQLsRSf!ZA%{ z=1>E59alZspy$-%Rb1QW24$KQgo+HK>DB$lA(kaNPYorwpf_;SEw|MRrLOww-^uU; z`F$G(X)DtyJ;~A0&YCrv$;u@na;pMSP!*?kl3X1V!2Xa!F$7_`%7z40QRVK?`u)s` z{2685V;T`BC*=jv&Nrijt% zxzGM&NA$Jr)W5xK6m`MC+XB;6CK`VnL{Qd-bkMfSl6I9rxdp@7%6Lawqir@*pfuxJ zkjZ-bbd|4Dq)5Pll6jTZOl{vvp6CD;Xx_6tiUHHVa#Cw9KZWO5aCTECWzr%Ien&#P9TLIJVr}Ivz`iH z>c4s25=e6rbdW6#{8NY>rm1%w7P$(Spvs!(F%o_l7Ip5buCAq(fiin5KCP>PbF;ZzFRZ?N1=B7Tng}2b_5n;2amz3$n&K|KdZ6(h4j)bjGjYjayhHn$`sB%f)uF1^I3Q(x{%4_K_Q z-kU60z;SkM~nf zl7|#^qy^nL(W5sb_Z{|M(h0t^xtEeU#h&PkKhi2VHe<|jEEof+ID6%B^A%!d_Z1T~qI`XE zNuA7cM-t-8-u<6V-)Bx`tcw`%ZxxrS?)5DHOlsF47R89oE^~aDxHsj76cR58%)TGs zyW8nmjjJD9S*;$;Mek(69A~OSveEE+Zo2kVisp$9w{t@IcKXN~ZN}RTF)etceBY}Q zZJ33p7(Gc>b4Xp?myyLUUjjZO2T2C}yi)zpZocTH!>vQg)v1QO_KX980`D3sj`KQx z&S4YMeWNmK@d-*&8w?tT;r}#2n;i3A3TTwu{`bT(_puB*jUD7_?`6V{@z--<;FJLc zlyA74cIZfjoZSt$<(qU?{_z3O*r!~hcvOI7rxJ^O*}`u$#MSX9XT`l*)N?bg``f-r zIvc|oO^Stqcbx7Dos^GDFXo0IBMFZmZ=qJ$bddQ$Dy^1`C7O(|@9#@Y!8>HEW%Ahu4Fqp##F7(>q*MJ50B zrVqvg!{VPF_s+{&!E?xcT~sy49;D>~=i9pdmQ8@@6@p#%2N)VkO-~;(z*vh+Z+7S* z=vCxY&84%>UP18oGSc5P{3dbC$GD4q+Ii0CB0&&Cpa~D957gytRb-|8Z?B`Qm_kSf zqgjUI%Lj#}rd?Sz&0)Mx-;^JNxHW&fGj(lAM~(m1%6FXmQE$g7Brr*Ev6^b=>;RZk z^D`^Y5u`=WLGgD|?XsC3@2DN_Nxz|L9S_a_JBkMt(P>EaH+8vu-o17d9K8D7Y;3l@ z%=TVVu6jl>#~Ar?uIvFS8j0-9A25d5OIoITl+~CjY$*pX|Jry9f0>V4NEm{{p8dZK zBi4;2MLgE%^bfFPbrT~-L({{e8oe14$&m7c;0W(??JhkuI!neSn<_!NUF8UvFc#@| zJohH?skqY4eV$~8Z2key`ys6Ty8m%G~>-Pi7vDP<^z$$R>8TgR`7BjBPb;C@n@SZS@ELBm9A5<=ijC3rS#4 z`QJl&a$}>gYaa#zXo7@j{^!AUAA;@(K4B`s2yRJyr~yN(?bYp%dOTsVJk6w&El9o- z2_)L#Q`2Qd)7eMd^WLL4#veAwr_Mr0=cXh6lg*8(n194yP^NeCNQ#RmFN)kOJOk36 z?d`lpUSC$GY~hg$1+;Izk=*or>}liV5myb-|4lK`dh|9|I*VB_+WcN!rlP>VXTIE* zCgo!%{KgP*iO%r{h)5| zI(*7i#_HK&+w*yolZ(CU>W?^6NiEBbzgnhjN<)Ef6$X;e1OOW zOr-e70@R(}9chV7ZIJ^^xsiLSf@C6Z2^oD8mMg`hY6%K_&r}j1{#VBwhl!^^H&1og z^6e1~xzH2C4n_0Qfq-#|f88>XsHb`@t}o-l=Xj&Mz53h7yW?Rq7U%J!W`mFsZ@crn zx(_>gDSpjI)U?#A+LoggI22NwIhgDBqCMcSZ2*_8?G~2Z%dS^_3C6qYUJ2A@4Ljok zeN#PU{dZ8p^?dYTSquIO<89WTI(nx}uXAs(nXmiqf(_6#el-8{y5P*OA_j&&-QM;g zq7z)=`Kb=>CrjXQuFWE0B#Dn#AX@T{hT-9h>iH((RdzUB`nMhQ<)D9qybI?Ym3A;Hj#W1`jquFZiO^) z4?P0ypL@AbeR7s1@8qXw#SdSMVD`hsm9GiAzf1*+z>@0HbzA5s+@805zF8ei5|OM` z<`6aJ@=Yq=%V&7^Zo06G#b5^GLbHgLav^yAt`_T1FdtnyW6pp6*bE+8n%b|65R!iO z?B3)IrR&WVF*`>oGY1E;zqfyOnK_I)i6p8%Kh$i|q zHz;OTu+Zc4pX^M(Uk`&+H!QE#s5G=26Qb&tE6`0BTe)l|pgBT=5JHtqq z!k+6P`N{qCC<4iWs5;Ik{q1WN^7jZYzNlihO66XAVBd!v(XsOSD5LRhKb6~Y7NKAK z@YdopcnU+?(WXWNw;1kCT%VfFsAFhY#&`*wW~n~um7e8X0;cevokz=JC@?mle4J%= z?YMvatjP8iXhRM`n>apS%T^DoGbT!g9x8txSNIsHf?68NB@%T08GQU}@bBs{%PqjB z@2ywmHgBV|Qs$BGjS#Cv7!Cctr(JLPY=1Fmng`-LygOTWn5lB<>+ba)o*VDJTML;3 zoXEW7(<*E{qkr0}wq3@^@Xd)nxpu3MVRQi8od(UB2R%|*`6zs(zn7M-4UgMe_Wg#K zorHyrXp*%(@-H>qhdo`Vq^s~4uv z?A@+pk*rUAADq)aj<~z|Gq9I&Iy{?JZt6sKn+LfE2-wL!Id9%xj}YV&ezZG-m@Ys_ z-Rc426EQ8EII5>>A`<31sZWEv9gpGSKM%grZvHIYvC#-)YeqXy2Wy}fQV$HYf;-+- zy#2+-`Q$3{YwPF0Lv6!IZ!_Bh%uUqnNma?K=$p-)UYRUKE^HR`KS;j(p$cmVFX$b% zJ-a;W?8SxnJ;KMd{w~(967S!o7na@v2N;1&TtDNNZ+&=gGj|JQsAAl#(pR>a^nE+} zj;S74tXixqU`vSkcXBq zs>jqJ1nfEheU9#*JM828s~`_hT?Y8yr3&dYpX=DhjOFs9@jq6`OVb&S#*GEFFP` zuPixGKxwpzVcag4S37;NG{@}dNzwPPW;2gN${4Z~x@m&Hjt}x(&0ExZjyN%Vq zx;~tgANtK=&|--nncyQBwaNc2uc^r+{?0eTIB%lv{T~Rdc;*(k8F~u6EJ`|@`P;Za z+Twa!fou6Bb$^fYcF$}~if*6gp~0;k(?jdGj|_qJpn@9TnQ@}%Y~TK#V+QbybJ5XRC` z+GxP}h_<=W^_C>5Eve~;lWH+G4i3tsAid3e7;Mhl83FWt6yiY*wJH*|W@qutWE3e( z@E12yn%CC^0qE6F4i}z_6tAxWc}}Q4H#!{lA@W|!*FGC;OwX5vZB0fv>aVRxIzfnt zp;c>d0198-MDwNRHyxKk-JYRY*h#}+r!@95IVeNb`oE?kTbE724yyypp82+c&rKr{_7|JqVh3{=EgrI6latR#{i1r>S={Pvc%7;?w&ASEZB$3h z)Pbha&nqmH>p~Z2PvG1ZccDvLWrJ_Cd&L#beEs$4%AxG%yCR)zsgx7-UDouAlpL@2 zjmhgd|Lugr7s8jBWIc$TcH;-Kw(T#*)+c6g&A%tTn@`&aomVBa`*@t4zS+Bt%vkP0 z+xp({W(!xwBZC7N`{JUqn;{N2j{S4herf7Exrkh%!qS*R zR@z!5YCBf&-EHe0vYz*=%!~~T6gw>uh&7lGX6OI!PJwwb5%&&IX?ys*$JR81Ea5{d z+%I;8&-ZD?11;uvUJqz}%UE3_-SoL({H2fIbD?&l-9Ng}?%kc7f7pwJRhc9i9n419v%M8+Q7T#N4k}no?e5wwH|$OJP6yqyjqBwEl5aEit?j$ zyYk`whc)-lbx2DBK@c6j&!lYd6LN;VyTotFGsj|G;5B;a>)+$~{m%yhb5??$$m?$y zp4SCJ_!??A{KCDp z!?6zs`!d~hl=SXsRast8=Fv4L$-WSJ^XA|#U!fqw$nLggb#vyW?sdK%c~HI-{FF$Z zo@_a9u{}3?$b{K{j684ZPdMGEdhhLEU!}b+Mn6~l*~9un=8&kES;~I{73AS^?6@y3 z$hT5Z+c#_V=(p_gqdx95U0~kRA&NJ?xYm=xR=PJYDoF|vgsC2g{qSdefXKgMq}S@` za~>my*&%|utLx=RZ`u2{va1}qTc_>{-cgT8f!o~TUjvJwem zm|B^-6}wTDjiM?z=#hBFu>Xx*E&B-5>UfoH8_;{ zd#Nph-BXkAh3f#uW8V1WWXRtImY>xdPwVf6f1rbMoFCTjiylbF zou55fy-M>)4sFl*zdf*SYJxXG-T4k4E4rGy{U(^=T~xs@8(aj<7V7M(LMLt<`q(vY z9nq5fnZ@NUBHRZlZv~``$k5IaU!LH}BYfYJsQA%INzZ{D%n9`>(pH#NJ2!>}A`#zu zSi*XL@Q;p{Z~ZfmpE*HIfZ($5}6Rktm>`%m$BA3T<_HKZl`+r{p$$DST zAC%+ERa)k-J^v9HYs9#}6G`@Xe%T$ZHV zX8sRNvtPgb&@i!BNAQ>yxKq(9(@(Ap zecjyDX505ZuXr1jzrlLhV4LIs&5!byb`Sj4yf69}Je@Q|HkKVpn_#R|` z>#6~6*3oGg42;%C5JD$bjfZFihD9IfmHS@Sj08v-6bMy~n-{wmnI@k2Oh3|iR$m|< zOq%a3jiXsM@yMLC5C?F2@%-;&tekfdgYvMd9wy(I`+tz>zkwREblF`V@FkvGf2jN; zIis$wJ^-Ap`~iImQJybvue+Xv_nU&u8tlO;F^pQGou*Xkf?u+#I=Hm^pIoA)-(V@l zQa@yV-?5P~7w{5@P-&xUB^|PJ>*;$X zZf=hnRfV9fL^Sg>*tFaO%||8QRb{>%nLDrM>VOSs!DBPp#v-1GJj}rHc_=6KKWx!; zfd%mU1gR@NCp`eue+ydtQigxa6RMp^_MCP3m#Wa=aNCP0)OfsU8}jUuCZ||C^#i?P zA8^uJ9qpie8O62EK0Jt;!8yL_-5z;nh0sd!s`8$ipEX}ya3uQvT(T8yH{d%hLQ{I% zO8pHcRG`^*W*^dp^j3QFrclLLP3=e4vwIJ4)fYoncsNt|US0I5Leh6SCr1Xt0uT)Q z4!mk7Wm(NKXiALB9hwQg)2`^j&x?x^3t^{l`oGt&BnXUEwsCJ61zf3QZmpFVnJ=D? zt?ZrY{iHUB@x_Ry2(0!56Crl?a28mYCoCvrhNWFj`mM|s{0c4foVy#n#|?XK|N0j; zC|M4u!!Olp7+byDsy;GbteI0!*T9oRuQY@q?fxIq`oOZMFAWT^YMnUjIMFPZ+)$?W zp0%Ut7m~UzuXlxv-Dn*S^~5HvmPS0DWvK)r$;}U4h-qbL>*))iJ%Kuh)VA2$B+LTolT>DGUnQHNPNIUN?bl46ftb8Cr zMHHhrcAmGx`PZnAPCm?-#BN|7HSUcepM$_7kMXxhScwlj*Gajo3cNhnvK0MJX z=!j>F;@vw(9`15z|Il%`a16lgmz(vtQ?g=wKivV|?Ot~?HGL}yw=fnKc&_nCrX>Cu zW4Yz5t)=BFkxxwAM@yFyaEQ9Z511Y&M_=lq=*Lg`&1?eKClLnOR8ElEfZ~h9S;-ao zJN>Pd3>R=LNq+(86zhv7D^aK%DC8fY2%9D9kH90jhat7TT;thIOayMvGLN;i9uDT3 z34;>pGu~V-+(QQr=U%cp+2W^@qzb|pLLCLePPJZPvwa6 z1`9fH_}>1wC&VEy?%MWyRm0^>b`=wwBYJ{ zdKtr~x~0)7G3(!jLs@oUs%n}&oJ^yg0G#l8IOuX|%BM;lXUe}V`AhEQ9e|8-OA%^V zijT@e)ywni`{K9!2V)+1Q;W`pzdwj8Z_ba{ZYUWNH%FTH%G}%WDVmWr;9slTMq%!E zV%VF>@Wc22x#^XgprnOAw4es0L_L%TWi0T6?E+bQlwMvBjdTv5Zk&t9Qq^fzu#-T5 zcNjeIwAB04$==fMV$*iUq~snch8<$Yc}4*6+9d-atvP{CJ@wAOZgwEb=ugO<{a%80 zRm;{5CubRJZ7+D{AB&SVY|0>0N2Bw;AkuGlA?IyS{024_nPH~Pc0(CTgXou7#=Ki;;Vr~#ifp*$WVI1b!raV00w-)V1B z6CFCEc)6V%2Y^E1&UgQ`>HZ(mY1r)=hM?jrd^$~ER1)@|Z1o>pq-H_UP;S#EN(I2w zqsgmr^DqB#Tp2`c-~=A@dL&^acRApt2+Z6t9Nv~HB_dz5v?vS=+W9N2)#Gf_n_Rcz zI!l*+2y%=)d(m)@Zk_FrT5{Jm?gVM)Qwr_>)#1*#XQ2GBkZ<`6IcyV9IJ?QK*ha&U zhKf7^z8hL!7Qs3-d z6~+v@7(a9!3=IhVj8xbZwTPIY3&y-tyW#2W^HY}PO8(KB;fU2MsuJVm5t~;F zDEr@u;`IibYsRPy+)u6Zyzx!$DtNH`7(}Tm%<|uH|M{scGuvB6@BPIdOz}kXm``q- z*OIM#?XSuF^49W?JTe7^haPPxG(eubu`=Dm$}R0wAyIli)Q!U^j)WoziXsSI$xF(I z>n}Vm0(xXbTns61w^X&5;E5cH%KgmX0Wc6{{SrnKWcysw5&r&dts9;`MkUGQ*LAR~ zEA#afJ5zkP*&j7Vgh7WFPgm+<27W{UWxzLIf<^ao6QKYML(TEE3>{^d7h0?LHZMad z()2+{KFBs%RsO#khg)etw5$zs>!2FXjaO;M1a)<}XE;4MeJ(5#5^R5TiFO9KQa~pVRO3FvFYyA&oN~%z7_9xEpc1Jhk z-h9Kb3F2BJCT8fueo%OS$+N5JClD?gl3xA6-E=(b(Bq`7Kx>8@@vA?cNS?2zWJ^+O zT;xfuqOaztQXEy2-13Y5Q6gYwEZz&dW;2q4C7HB+_Cnvprm7Sx-M7UK(FAQKKmETS zVrW2G_#&jTgNlt!XR;aY^HDMYZY$46VQjWJ_h!y0KtT5fsKd0-f@R~%p;sy zKF+jMycDc*%QheRkH^D~1G4H2XlL62$7{yWe|}N4yBaHwB(0qBKXS7FTUsCYK`8zZ ze0Z$e%+%2~4rRU9f?5s%q+E#OvF+8Td+*nXAKY9S_arcg{@i}BkrXY}W7^`1 zRW8((0e1T_O=KRiCUq`B=T-2s9mBbNMODOqnhY|+#^;<2llu>&s&K`Za|bQf1nH)} zkh)!dJ7VrTA!nfDd6+!&I<>?!WU1+`Ep7Xg(Tz_QU9ff{g!-ueu-lhJBI3jKm_11I z*+dVTKlLdwt>qfU(awOo2L`z23Ewia=M4>+TT|r2m`_?pbfiO*zpyTr*4{R|NGEO; zFxt)U0nFUqawbf1-B^F*+)c^G#3J7P`>H-#RW{Ww^!4O^t+tn<&OUefvAzMpUO>j0 zQ{*p!9=w1pF|FVOqbmft)bzF%J7=5X^jssz2sEq03b|zxZ#0cnLR@iMD2BB2lNS=p=_bDh+zU0tv=gE3g1?FES|Jd(! za9IWrUHLzPEYeFiOXBo`@T?IR<${Uf7Z6d-Gus>`rPxTGKlsj~)n{KV{UMLdz zY;@6Mrzjk|WPWw)axc*R&M+>DRrN z5q5a@cqK=(y<}j3^KlbSY4d@+i3tUqPWYakW|ky&j))_OR=CR2VohGb$jB(MRh}|{ z=?W21>d^9ugOPlzmSt6581PK`97prbZS$1Mr0r3{iCt}x!I6Bq&QD>$SSJ3L-m8^E zTJPnWSPme3%kUw0CL(08@l?&(Oul2w@bH*Qp4`?x-9^MUKyBA>u*od@dZO&Vfx`QW zmz?2~B$fxrH9`^aK==o!VV+Ic1I;t{#JM=ae<@&i1~%ddE<6+^fvqX}xgZtygGBFF zw9#Yi>YgSZxG?{s!hLZe`8}@}E)qOK3vz?1NeOD*`USkF^Y5mjD(LXX4i8#QX!{ck zLo2nfZs>03D4&v1RGnP=MEh!n7DVkEvs}x4>;Ee?7-;g5Rdna<-tVcri`zMzJcLr|{Fk97nG*?YF~lBZT;lq08Hxcj{oqCH1P< zD^C(affL0$M?JZllN`tu&IF{8cc{z*VJY;1XAHO54Ooa^r<%}!8f2J{)tjgs>)-Rr zikgB2Uo)}IMg#oXC<~G*RV_Jx4<0Nud?aX3eVchs(T4sQUdWt`8~~c_doBWp0F8)Qb+?+_=S=0RV$<1f z4D>#6PbA+!Qh^Kk%_$AeHWo7h?*CTB#iM7U_fiQ!SEzTlXYEeW37TDeFklRPGPw%S zn0E&Sr_lMfrg7u1u7z;Tgeo`d>adCE@E`90)Zd}tkEipRNa$#TxU7)KFwBpG5!oT2 z2iB^EPGUpV-M$l&dFC6hXFEbqMXj6%(;2WO@`&VFZ= z!E8G8;1fuV3rdd$u04IS8Tl*=y^pa7z-;K8`m_a+lzqzIYLvya(HVg&!)Vcqp#VtB zh?oIJ#}!Pyn?b{S(fb{%x1-Qmf7aq+%cH9tr==>KKV*J49m5%x(!VR-@|1k}v+#1? zCTpdV^IWq9H60R>d40k=xgv72*_NeTjQhXpcztgNWTqIoStJYg5VGJ)p_oDqm}{{z zVT%t+6mHT7X{Olqy+%lnW>XhLUEsV7?N{Vi&G_om9K65HF;cL$+JiQa2fKtJW+ zpm~iNrJxsIzfpClZ2l|o(Hi{mXf%OfDe3w&nRN?Zo4q&*s~&eMP0yukY4l zR8J4ZBY*7IQeuicc36L3Zomy*{V#Sy@VYl}8AF5iROb)B16ON{A&ua1`bZj2@ ze|JK7Vc*g&$Xxkh!vj2vi}=Eg4O*^E|5WL{-13cGGS>VA^O)pqG0`mD3OxHje;<0z zIhOmYF5Z5A-?`^luQJ2p;$6exN>@~c)d=6X&sU#dl!pn7dK{G(>h|#@;Yb0GRqBrGLF`W#vv<_>TLkx>TLppNagsZ1&-u1dS>6$cU@7hcI|WD)4qZlG-0zel?{E0D zq9z6Rd%pUG`z3!^xL_46>s)<)HQ8O22U1a-5Xcd=`M@b6LQs%ou>3QjEZ>1Bs>kA$ zeLCM<5*TmfbYA)LK~d%rlCIp~2RjanHCw{IqWM31vmi9G#OLS@aH`Dl@B~abu(PuZ zoxWVx!hP(itNR-@7ad&ZJSs3nYZY{uFn#breKylI*h?I;F`mQHe5@-id1eP!J z*Xpb-`tSc4&*vpahkl++|tW?5&o@C@sB4Z)@j%ZnEb z?zRV>>Rg6xoul}V0FP-*M~_T~;T*f?v+S+Q=pbyJ$+byX-G@nnNi0#^&pFHgzj9#9 zVsFP0xd-xoG;^m;dXX3JSVO0CPw+@^^Jx7N_pL@obSy#t)sD@t$Re+$-)-Z5D;*H= zrV{wVT=Pb_zKS2{4a|Z=m$lJx;IYS@tp66nma<#jioS$e*_7H$9Hw>k)xdjx$RqyD zM1pX@t{yjqEQM5FUX}UF8;Vz*nU>7&u`|txMDAOCBs!EqdqLs;+H*IzVu(wR55~6o zDK|2=J9uD8p?0H#u@NC5N%!+93x#zmv7A_S!8LtUB8=3WZ?>KL!k31g{7h7K=T8hRt(P?sZEggi9V2anT~|gQ ze`$uym5{kUogb5F(BFr%2h=ZhYJZ06Xazm391KesM1H$liuTHNU*s}4y(HlwwFh-3 zfEaD4Ldh&~1Ge(XEZJ*|{S6E5uw5%+S}YyJHD%X10#19<9xab->rYbLFhYNOoW5=@ zv(A|(K$E%qzlx=jg?)H%2$2k^^>>V8+9>gSN~=vKz@U~QuvLQW?uQ+2TJhJRA;+k# zCO;d3F)8r?)APEau%p#V(d7^~oNobTu2jx}LYq;k}HF=OQCI?&R}!n&oF6`Mg%@}XC@kj zcCM{?kcLqp<@?PtzuQcA=PX@V4K{-`aS`aQId+MwjCai!R5UHZ%|HN_nxls!kH3*m zmQppMfY$Lv;<#4Ew75ADk4atI{QI_Y^Eu?El}3DOJsYGqQfdOaAqETW;Jkoc9}tsb z$5`dNL*>cn4t$+wR>PaWz7)ZvbNz3Nw7CWm!CG{^%{u&ta6k35hiBfIi$RI8z*P4U6(YUBGJP-lgky zUIMN;G@woBk=36Y-+K-%$Mb7!rBZ?YEMmY*cqHB+7xob;t_)P|%;Ap+qBbby zN8V$F=z{+INN6c(At__Hz;2%T`>Z@V>&0tXlRjOHKYEvIVmERku|SLr2-j5Xb(Gp` zRodP?J-amPLL5}`O{>KF53VR#Br)Kt`k)V(+;;2l@VU-|ecnshj<#X}FW}`J=Im9} zSS6h;U({up*?Suzo?oF0l#A)mq84Em#*y8MCyO4Vgt6UF5%hVTI_6pX^sgGo-wKy3 z*qpnw{7T43^Z=cJAX5~h2z{0z?;+!?ica`mv3JF{ z__iVaCXfoTjcj}_`R-NhJut%a(|UTw)( z+dQ?D&mE@4ax;#_?f5~i&(=3%xAD6?;-mqGklsq5#X|lWCcjl;;~5RjagpS(Ur=GH z3Sn2{_bTq#_VDm=Lm=VZTEpH0M3`eb(dF`QDv$l3g4Qk4nxlvZ@Pb_V+Xza9tu$Z{pNm3)JSg-1Q zs*x)L4=5dS9&QGJ0v)HLpm7KW5`g+Cu8mNjx>@VjUDv|n}>8GS%2yuCIA*5+z^d~gNyw&jjr+atkj34WdQ~d zaRpSUB3&e@szH0>gRR$D3E9tU(-%I`gy>>@yvGv$=8%8|^VAqb4^}}9l*bsD?({sv zgNBeoVYDh5kBa3`O85{R?W9v)8D_t3$Y2VS>9w7i#N$y7h8D~@y4wsvnRvyyIRbfL zmmkRd`?n6uupm5>((HDTk&Z;hdX{m>zK2vDyp{zh!sE0=Bf|5%HVv*1p^XBU<=PgJ zrUNM+XUJ)=u=nazlKvVKY4B%H$L9+2F5ER;c*yFV8_bX$Ur&iXec0*g+lWs^&f6_S zkF7X~h~~_UjH0fwlVZIweMh(Lva7fLh`7gxb{Sgzd`JnLXV2dCA@oRb@NBvc>Xl5g zSW;#Y{&%f`eq(*V0`6QTo1;iZW$udEJXG5yejtlFs#vTSzK7j+{DMR+*eKwdeg8xE z?26*u$7ciaKlqA%^w(~@gNGjKPkM!r$v;Jhe+kS@k^U^p)EWALwfO~}>s3*sfXfk; znA5 z@Sy*6vgmX2GtDA6YHI9Jk_(MfcLNfUC2M-holNcKE3JW)_1-tO}IP&rBE!_ShTSZ$$HSy`wBpE)md6k!7e$fS|X(dnVV(HhD1_3D;oq~t~2%Zt5lvI<6R z3g9L#Na>FsBG;ELG^;h{9VJhv32V*5vN9M6a(I-A$}2N2p%*t4GIt(MgXiDM5~p3I z&g2oI@J)@b+GiV7-!0d-r5*_xGJ+KRPzn3hGrZ?<`f1A@KWYmbO{aR3=_Zz}%t_?& z1s)UfVX?3$TH{<0h3pEzMB4AU1mC#ldW}2{FOcaiW^SLNP#zVjTt4k~0hi-1G(svG z9y38;w87B3p1PFUv=ug?qc};eKDDy=fz0{{RW&v8H$;6TYiIXS$5k$<%7yb+D|1_# z-C?BWO?Fw31Lw0evv+p!|2m-6X#TG2vuZNZ>SLyUXOooRlWeJiRC` zG5@i=M-$AGf|#C;;K`||cTl+3?EIrYa`nO2L+8%OGribI<)oZ1QSQ8AxOfaw4}l2r z{z2`_K);>8)Z+7su*RM!ygM;cEvYNLFW1tigRgxAyR|0XwY4)^MJ>@a1p4=V^S3po zF%Ng1Hwf+NM=&*27w=j@t!c6fr$Ln;>3u`Zk_X_o>NZ+HW{KLWxjhdGB5y zu%a|QysligSCqt72b0B)%e5Z~dC7(A-;1Y9L88?!{yNoKoSz`@4sP6T0v|DL{v`Aq zNO{|Lu#K|tS&@D@zTFMT6Owh6@xS^(x}73tpt+xNJ|BL}o6FKtUH-#H4q#c=RlWk7OHey=ju zd;5R%xnRmT-6JN2oz8{FgemBJbT|yTEm`X3Bb|}ndg+S!t0kNL0`U%TM_f8(}+LH!t?zQ>wN@1e)Dp?DH7w?;dp$0i6sx`Es~l_!q?@!p=UB8MbTdyu_Ub z&1l%Qi0h>ip2FOBFC)&;DWQeHmd9t`81&V$lfjk7GA_vT-CgSIljH~szO%F$#!!nM z_it~Dd9H8>Ok!#1wMC9>5nZMWW=bZz2aaaDlTy_(YkHmSYR_- ziwp9?{d&%;d<(DlJ*QbyoidgKpI}A&4tP_-P9N#OPf^AMEod7Ksg#F|ri9Hk zWBval>Z_xo`o6aZ5Tv9#r39n|q*FR1rKP(|q)Vhbq@_Wcp_`!_B&55$YiQn!AHTnM zE!O{cAiA|#pAv7fU`;j&x^;vrPlm39)$H?bexozJ0nrlN$eKsQwGUs3d;vx}U)q4+=tB5j4w3sB_MDVEONZn~K!VT-rBWZks%x6ljd?A~X>FkC^% zeP$-XtE(EhGmLMfS_p4a`qXnN(K}!ICO$P`)#+wy9%R7T(=-z2B2iEq{3mc*3ZSED ztW$Cpxn4PSg`+C*eSha{+NrQ=o#T?*y}F=OSD;GcEeY@A>WNT~-bfetSU^;=B0IM) zWN~X@Zag3d?FQS~7Crq(WA4^*-|&}S{w0@X;9gFE$joutiU`lKl9hlbrdFu(I z{eE5A^XBFasI3r~klsYTOlV;4osBc==tnMk-{lU#Pg1&$1o;%*TU?hZ*aP#NnC`ux1`%FbY7(EQNc%`DPsmIqzh$j)vU)%)VZ)$ zENB;9NIIXAqa%riaS-jI$~UOXU#ZngpSpu4X4rMqGO6-L#q4yROzP2owJ3i5PqXKJeCJ0Hp(HD?=IYSn=$tWS@g zYSA;q^Pk8qn&v33O`$624VX z$W_4PD`W(_Wjs=v191V{9=Yjezs5#27^v3;Pyw}!QN4Mwi@i>FX?ey#xYcaOA}P@N z)Z9$sqWzdPm3qf)Zf*VMuMzV<*vtTVXLX}_yLC$@onO5Tz*Sblm7$`|VJpjqSZ>-{ zFD?*V@h&;dpW1Vmu#U6qUKAIno@`k$h+IXI#FMK{yd5AuZRJW_pXv8$2OlG*zj^@p zbGwz@a=22?`Pa}KcZaWe`D+lNwCOu&#^bTTd(LX0W`9rk-W__GTgaK7Ql9ndqoe|9 zK&zm$ykdb}>(aOF$C^>!JD+37)|UMJ=C>#hDF5To4ISM@m(rfN0`l9&ERO4YU3Vk+ zBghtZDc80Qh|EmsBVyJ9wn#8RI*dJ4Hkcka;j<|uifg-cN`puBQ^R|s0*C`HR6$UK z_>@Rqz`5G*!tn4@m5^uBFfoH@ZJO87i;Yc>*V1e$MDu`Og?ilD1l!tp_$r+2-b4HA zu53%^^*P|4nogO_=-#|5YrT2lDv|)f7SQH*t@Xxsr>?)=kl@5I zZ{=JQ(~TOE{B8NWX=OCp#^>P={!B%S+GrIsfihj^^1Fb(bcGZ|)cCz824G9mcoz}}* zq*z5oMTFCOHeEz!A+)3(k zMgaVI1_;FexMRkBGZFGIrJ#VY@j_PJhF|(?qiC3;{g8vW#+l8@&}FgrTmUB37moDL z8(um(-u#!>OX{YNXFE52!*c2v>J!e=;;!s(#^8Rmym`IeGQzd;D{RZ6CC2;PQJ{&) zf2Ro3T%SLF>G!t%nQ(aFPen1+pA3ZWy?Bm@+MhHMoUvE#ljP$M^AKFCOqtlP1lnc2O z4z^il0|!S&sjpx_(=e_9;!G6Ovnzan*J-?&CxH7-^>}3v%YL`e&Y->WP*E^BO>=NH z#PdnjjrTK``f4YX)4xU6$8*QY*p!C~S!zSGF(*y(>0zLwuQHsYctAuX_t#wJLz*Q= zOMUi|61sejxhpde)xrXmo40d%UY!yVJfSt~_H&+*dJ9+-K6sf-LJiO@40JO9c|xL= zR*sH5+ul7_m|ia)se=I>dDs)7W$=F#UNW#wH~h`!8gssPBU`)LvA*n!qv(Z>0MZt% zp#2+V=by=wLz1}q1Y zSg^vy!y`!R^L-Y4vTAYpF7Bg7cJWUXW?mAZ);&Z}8$hd3I5?Py#CU;`*7z}ygk`z? zlG`kx;U~bFn2Q2mtyo-@cPu>(2(R34MFMO6^b4&$qK-8Golea>%=(*SoEB8wi~F0V zNt*g=v0+0WTwfkWt$(Zr9V{Y>4q0Yc|49!vyu2Eq!&MvuZcy&hvmuIo(|^2kvbIYL zK_j+0`|%;^okjH94~k2vVBHHFPKJf_(Yk(KJ}=k+dH)jz%$b#QsjqVmPv5sGz-=Lw z<7z|hS5|$MKf9vxb`W46sF)Kxo8ZInZN81*kGf?z9ot!2qtAZ1-k?+!AwT4aF+pMx zgpv-(G)0|{>FNb-O=BKVHAUehL_0d__d$CDQ2yJ)DYG#D0T@bH&ei<4jD}e2fBWI|pm~eMck1 zp<;Q*oPj}g=k9Tmv)wswNo;>KRy8(9FzWbyIrK;xIEClvCIwPk80biaxUhCE>Puuw z#3GX%>)R}M_tS_n0;}Da{mAqFm;02usYa2+c_LjoD6w>7LI=U4(1@^URXPj-FT(;k zsmZ~o{`|;Z>=A8LQ2qJTw36y9wQx1I0Rd#kD({gW<)v#QjlETTAVQ}8-u~Rl=605& z&jc%#>JoG{BWI4g`WOxL}#;IwuX7$sh7vN^DEIk4L0I6YA>pdz4tRiWZ8+ z5A0r=b^j;m)T^$@Z90}z@ke`Y;W@hE5n#Cw0V);>iL(M%do-=T8VR2~H&V-4nOkoa zabAr~y*{fjkxkoq88pjx;Kni{@W7$q$F>o}M+W z1!$AY*wT?)Y_Gx%bb|JD&bxJ}Z!Ri9^(dInDoveQzi@j)t|A| z&WBbHa^kRkhh5y4>t!DD--i=oUn{dv>M6%@^^kF>GSnFLD1oz#J&F@juXI5QRXfyw zb(Wgdjil|tlFDXr)Hh82nni0gFLS)`id1zB*MRjI^M40Vudm6Xe|o(y<8gi&>}}5* zhekwFSy$VL0x%yCQ)fxobi6@(BC(206Q;m|2mFj}-AcO9&yWnguiUymo09zJ8;OY5 z4+z0kTC7}W*Y048gL*OrXc78xa|@8R2WbUu)YW`lZSY}1M!M|!!ybcaV1kr4BKsPn z?T-RumlX@R%fc+OM~|G(ZluyhD?@Zl*-qtqUhFxVTos{CJ#eTq)nyWca?qEcsPevl zZ&l%j(WW;cal3oxsO`R`?bJ?N4NH_kcDIA;EseZ#sh%j=jJN9EcNx-5Fhmc`eVl{o(CYFj#FnJup`0@oDQG3m{aCB6&stzs?GZBvqy@0MkU!Dxb>8*z;d{2O7@GZH}E?|RyBeA0F-)GM7I(e|Z^Mb!Hn@tkfz$fZnVa@=NNKYl#dPv?#x z*RZK%y^3B|6Zyr&k=|w2F&;I$pfpS-G82S@NfdGEB8f zM~tZAO7klYHXf-%e8BD#vGuar_Z;*GkG@J*z^hm%e@Y3#mM04f4__fu(M(WMn+txm z&v4RO=iOn&0^F@vzn>mw4;A60cVB4%KdH>ZBV?d5sluDA**=X(rj=(rm#+o@i%xt zM1#NldZ0B-va;jH0)nG$+bEeO)y7Mn9sjEZz${iv(7y~VT6%~>8|W2{()Ep#Ck*?6 zQ@moRWIZ+q1ObH>&Y2^5-Koo(x01XLFyrN9*Bg9%BAVwV9?etw1YX-pzg5RwN)V>+}JG^@2`yB4|f)QgR;|J>2}SxaCqJg zNIrMHZheC;C>ZYSxxlnm+0b=p_})#Y*ZJv$UV>$VrY?;AtF^j|7ypIw#a_~b9#K{d zYcR>ZPEi*;4>w*Hs-#D}jlXvtXvU1SOW$G9tHqwyzG8E>wNtxKMI%rCCd6-#_dCS_ zgRN?OO+}?h0o<|}7B3X9zvqwx6?L0V)phxqXvWlT!_~~R!5GO`f!O-cxy${S%lbeR zG?8w%oOfBfx*{6bb|}rExQ8u$6(7a6b652HU|MY@QQfm-(=4?vpX8)Uuw6F`XP7&MSi>T>3y{&|u7u6w_$gd)k3}y(EO& z(^2WT%knntLsjUg*xRB_YHs=K`pZk3nZ^d9&z!18hbe4~_l{IkrAmUB^GLMly+CHR z>y(z%7by;*!&YI8>0<7RwrN807Huq#3?%ru8XiC6;#b1_Is?cIhJyVU)_=q>76ae+ z2BZ>UC10U=(!E#gu;EXE{6a5cgP7@DD`=E9e`l~VylB95LZvRdv=jv()wy}IF{%4K zZ6A`Q6ci#v$NDIzt|eeKMNu1|=@u48Vqd<{&S^D`&dtFCL*6okK=iMH^jBxH>Gwes zNnOaxSsrG%fJMZtsQd}ZxiW7=Z*&IE#PuI=W}U~X52-A0_HTXF1YQ1lh*KU7`=^8$+ycj{% zQhrp>L=Zah5!d{zVpLr3(@PH3BFPCboIR|sJD$(KAMij$I3e|=M{Dws%d6D+qO>AI7*uh?5bep+H#q$ZLjpYnZez! zH#K#gXrNCpDnQ2M3cLO{5Tf1C$6`P9IV2A&E^i&&z6~wM;lD z>Y*5axdv5nj5}Stle#m3UzWAFmFXlPRqMB7@bfZwoo20AR4Kh}?92km6^7LOA|E01 zM3SMMjx+Lax+@zwU9uvcc-%?bN$0nt3FG}waIVm0K-LKMSwvO07ScS(k_`?s?Qlk+ znugMl*}ESy5TXhW<(1398k*G?i-wKCy^C;JfjzC%*7ZcU}UDZ?Pjvs||=?qzo5QE&XN^Qhc4Ak?qZjCb1cDK1w# z>$0))NzRZ%)u#&{ScPWdd|qCE#1y$Q<7Ujj5NWhZCeQY8ebmp3*(>5JFRVwy*X8$) z`c1EQF6iBPxlVsG)=@wT0gGohK)lWdnwBoY_qwAv7IZDN`YA8ngO;fbiLi{dz1#V& zANQ4OgpSxi>>yXoADGW67Yq?l8~O?rC=t}BNMs@FKsxSd!+l$jm0~>a+CM!9inh+S z`*qMOI*y(7L0CT9h>)j2Hyfn|&mk&t;&|~9>ciM`kguX(uLrZDQlLHe1JDCiZs%wW z5kDT(wmOiurQ@=J%uYyFwYN>KYl1OHmqc=Wc2Z)n*T{t?7gPYCNi*Lao7#Wk>(0V7T57{a)G8 z2>vpX{A21~3dqDU8CHxp8`_{c*S%on416rq z2JJ+t5&^6e8`hbSmP9XCxnS~YEiAz^M_l*fg`316#N1))HIEU|tLk64EXgmizGy(` z3WElmjQLO6rjzc*-D`w^o)3r=F{dC6SpC=(DLe14n7?^%K$C1N z5@#OeZ_4|^$Wp+ybX9xxG~tY#aSc)l*zaxn+l>0(1hC3n(@Z*CptT=29ah=ZuB~Bj z6#@PKFeI-sle@A1nN}tv&^;n9`;OVxe7V#7Fu_`XaLi&6IkVl^`!2y*@n!SbX@+}7 zW$YXWVYch~5H{*%0Mkhn@M?c9E!oh>$#Gj=e=)aFsbTb@^6iAN-aZ#Wr0AJ6MWis+ zGez-bn9vaZ6hS`ylO6qAW~fu{Kv~M+BCd_5$@pt>j{SJ( zlR0L$dB%gzlDIB$X=`H1x~?A*v@!j`@Z7i0Eh3>=_UOwSHrvJ>(HcV_e3f7bxKm@R zKokK3D^(6u3Hqo+l;vc`K7M|XXEuFwx$~|jA z7$Sz91~>i~**~`g9G|}A1*&~s{Dzi6o}bR995j?laU&Cy7Pb?i@MG!LwbYKq>b|Q= zKzH2r_i40i>FN4d7`QG`Pvii4vd(ZRuy8jIKG|*hJh|kA53A9g4Lp3Pa25lA*1inT z0{Bgbi6?@q3YN}%DEu?KCu0nlP&|v=W)4& zu055d?9*M&BW#;I9xEUdWN3r5{uo|E@b(qsZB)JP<<{#F_SwfxqiU}-tCNp68R(gR zRO8Z$*m+T+4XgWvH_mb5Y(9xA&8$)_YE-Pc*mH*ym6$no`p(AfcNgR9jmHPN9siM> zuY>YAbwpA13PzEpexA_22U;}R3%ia1-<=&ummZD_wg~8PC~9oNxwJ0 zGbWf%B`;NG$(GcUtA8}8@mFVXlz@6Nqy|7BWpWOoe06o%c@1!LX1h^P=u~QC3^aPl zz9Kz!$71I=4}bDI`7P<=)i#Y~H-x0^6{9k6l+S^c(`>x9NlV6!iaSW}oQ`Vf+rBx5 z`8jAZ%Wb8vy;dh&4;m(y=}gnGDaZR}*CB#PyRK+Ysv}jZfTe?b%6ljsb7}iJx+nPR zWeb@wX&BCbfnq8C8`!olp;i@a_Prymz7u?!30nR5lk>l{sed5JF~(pn7L$Y1-E&^3 z-#8$k4hZXh6CG0;X|ZPna$6r0zUF|F)2}Hv^-70iQ~r!iue@=`QQY8{@$=_nej=%C z%y?J^37RMYn0dQm4X7?du05?ZSj^QC>mvc0{>cHx9qA!fhD$AS-Dcv-KtEVU>dulJ^G>S}6L zX7|9eL#367U>lP!2i^{OJI1GkHKHsY&)#W-WBkYTuwcl2OFdrbVJ;6XhcFxoBHE@> z1(Nj0o?$Cp499xqi0k+?8ue`DdCFdRf82N> zB@1LB%;zfCpiknm9wlC}fv#**2PHidnBanjOgYe-OpgRl;258?$I?=^exXQLk$g4; zf;Vee@S0V#v39?9Xp0U~m9&p!#VdZc0b{AQhB4jG^B_TlW{?1|5QEZ}Z+0(EV8c*E z5Dg;yFhsBj8GOY-k*Y)nN7MK#L)a2X(PtXt#Cl^2B(wOMFWzV=6>WKLyx?=;iQMgy z-CC!=7=zTEy%*9dhfhprQyA_B3Q4YCG{gK0I#T;rfYjimRIyx#{81S{`@kI-(5Mzi zTOAJL)0~~|O-{ePlfaE&w2ZL&I2xC3+~?4B@tc_GKDNg->&`RUe#2jLrZG!;m*xzX z8)p&B0&p};#J?Qdr~qyS=AEka%54fWPPqN*i70~bg0Y(nZT_D>sVr>aM6*%xruCP@ z*ly*|0`TWaaPjxD}5jByK@mQ1Z@n1v?Z;jiy2$WCexEN9+% z0VhpSVuB$eHEsY`j@%|D{_POY+O`$`hBFZqqnfm<^V@636vWi?hoDy!+ww zr;nk7JcEG%52m@$S}&egKeXzdxiDLW(nHjUK_oBQ(1E80KG#$rvM$Lx!_})O=_8fr zYdNf)cB00*3AEf~i!Bq(PBR0OMyPtP%NMm5*D(*Zh?Y-Y6FZYsnGPI#kVfF%Lhi`C zPdpek6@P7Y=)mi(6vDA!RlB9%WWLY9%MS3_)-*4Bsbo`br1k1T1rhA>ajv2M9dd zYBVd*LO~bagCHSu-K$c<#^~O z+gR7mt|^$zD+gXMy<>!OiqVZrZzBT3C&y!_8_5fuX{Q5bb|kY#>9j9=7F4z_02qdz z&i@`(1F}^$?9(A-t2|2s2x2C8XtSokEx^bH$7%_fckh&`e68*GD*Lzbz`CAP9y^yv zPp#?KNaxGTc^BC)Mg^9o1gd<1jU;ns0Fb4j873fZ9~ZUSz~+X@zTFm};;t^bDp3&4 zN|rLj>T)_c3^)9-zSq61CDgA;XGiM~PYlL=&6Ganxpv&Cj|s$(0l>x0jB<3-vuD8Nh5q9W<@Of9YoqGKsM*_S)W|x-S)zy)y{;OXWQ_ z!!hM_6TX8_Ffw?PFnIg%~oK7q8{L{kdBDEHF@R5}Ue zxk6sbGYNLZBk$WZhfYv-IBNWCJz?jO`0pg}uNI#4xSj5tosa%&TJqyYDXb0Y)bHdl z4Z6?EYEV=Q(2%cW+QjW7zrcZfH_PukzlANbCxQeryxF-pty8_%2^1u6ax~}ZR z5`iDr@c|eTvj{6`ms+AKIh9T)aBCSx!E`tl!XL5((S9+A^s_eMIu>TPw<$<)>hb9K zAi&>YFQGLRX~o9$7*I3810YWyeSM1N`<4c#c;MlB=a0?9+a={6$R7O5t?ER-e7Iv; z@?ypna-}b@DkKGze$y|-?e7wb!(ykC<}6jA4}aeBsuz!#QHM?HElK;2Xjitb!k@&B zx2@7vhtj+b;bdHvnw_S)?`Pv*mZDI|{el;tju*ovvxRMESr%A1aq|-@3Z@S#W3AlXuz6=yq}R20{JISo}Wt( zV`k^eDzm;+2)~tXH{n1MFf-lOz;f2$p6hw2AwUCq@_5@9y}60}@iGWLGj|!F@MEIq zS(GJ80#y}}7Ger@5Hcv>XdEmkMU|#n0SPcz&4lP??4uCgz=9Z345z11qRniV*0V7| zyWypREkB=+K*l!u`Y)`62$|)0= zEPm)P6A>4s`RCsqglWK7!@bj%UI|DwT?>Xo*y)>#)4Zs`)4}^7^l1%egwN&u%Eh{# zh?|93*So5%pAoXmLUbWwi-_N*zd$7VeJByVF0N(680_c7y5pAYUcQqUMHfe;phmKM z3$QB6={7x8BG;n>xzRdE%aXyJR!4A(y~48|ioH6Y;+@djzd3r~znd5@FhmDU>z!gI zuq;M&=|OcjY9@i_FNT@Qm*j8XEHxb;CaS#Z_X&9RsGrr6VzK$x`;|L8>m79_uJwn{ z?8~Lo3>ua}@?lu&NTwX1_Y*EF$+T*3k?%6{eS*(RZ6?0Pyt+OVA81V;Tn|YxGdk8I zA70Ge>UE9N!@3o9bxE9%n`D)agNJ>+y>DyUZ zFR!3AD#+#BoA+E_X}a9PuwB`wni37}D_taz1u|<^?QVMB{z!C<*Ih6Hak8TT3JX-q z`bW1km%U#Jva|U^aYmU(q2#`Q8JsP(`u{{>&*+mg+5Q`rrd=GxU z?=}dNXk6)+;fb6L%JE+^s8-Eigkm3&BZI+_Qg9(DM<22 za_z%stJLzmdevR{M;KxFD%<}&5=5j;fjpSjTYLQKRy`KOSpFK=N}*T7T;9p`KpO{} zX;RKFyp@g&x!`@b6qg4vPS{{nmd=Ghc7Zz+&zn_d4ty`ND(n9}YJ^V)&ilq7BzvIo zamiiD(6II(Y-g3j$Q4oxG;?JHMd1&H*FPn#BcFB@Z&Pwj$G@($z2M-e#B3|LQ>z}` zCr@bQ(t9q8lBzUV1$vdD{f$t3$0}g+erYN$d1I4EjYYTYFM@v|D$PZfI!9hN)?L+sQ*M+gNGELffe-J4;!CIe0THn3$?Lc+(NY}eXUmOnFMN#o?T!SG~hB9^)rYaTYo({x_ zQYY?k_PmGX)W*BawC0VK?W@?Iflk}YD4HbJ8&Qs%iNm2X0C5~v$^rH+%@0u|vOm3d z?DKjQFB>K1$*uDt$m@j0ON~I-Lefv;#`AJDViHUfW>N+Ebty|-z+zkCza=ToZcyvY zj$6C+5{es=GFXfMcm6w`@R_pGZ8Rth9cmNZQjIYph6x97{Y{XTpKat-t;Gcw;n6-E z(DH&nwuce@`ews^*lz4)4;V&6HMxIkIA$RV#rnj0b2Z3z-H8}*w&dC3$L#Lq(NfuJ z2e!-bq@H(&%8b4}Hx;r{jAyyVK(t^C?|_1|fSBQ5pP0I~jsa-{I-pTg$XjGiov^bX z?^W7Cew=(>gJ?$y%yB&4Uo38U5(B%~m5f3kB85u9B5mO7l*(zK0`YIW80Rcv+;O}^nK0j82xxR7!?+uL&JKpp zCV?}^?ZgsQsSb?56AL9j^pOt+`eUSl0#wcE+QHjgA=k=V}xmgufc+B}%)vZ4sZZ|aR(p03|8xIF*HI4&98@+D9& z(T>&KL}Osv9}!?$&5z;vqr2EM#g z35K?~m#&pb%tWM!DMZb@QSS7`$zLCEkf%*&vkM=Enyk+udWLO*k< z&qqDSI;@PTK2Q!XWN3??nM@YHCt$e%jlv+IwEn2@^d_=SaMCDM(T|H83`H^G^~KKE|&QNEN@qst>2ToQsD*94f&I*OQ^uW1Z*2cxw+e zS;AA8pKP@Iea~ZYGj;;F`(IPn@l{Tn=(JdX8n9rfV+(@Nw1#hrph zoatij=H|3Ek+I4%EoEVfYH?mORbhXpRnb3*X3NbI4d?=HP}6Gw=4E>;je{kI7@Iv_ z#W`2(4y3HfBBW)U>E63*_Qc+JGd^~q*rlQ*C&Tv*e)Vr9X3ByZefd(A;u-wEpF(qn z;bv5O9p7o?v1{&VxDTZ&$EAu^nhE`2#PF`592M2YCkB;Q@9Qe>YopB~P=eQH?^ZmFD?edhADwV0gLuUR)Y{I<>Qfv#5P^9% zyNF{S@?wQ8-H)iov^OQbOcGej69)V=cm+rYEc}s-h7of{zQoGpZ(nlxf@-8z_g!^L z-E0$2AF^L8DDrX&?y>FLW%4_4(G}iJ;pF%?z)^wW8^h8C! zkN)2aN*NdZcwuQ_@r`j%iQyu&=8eAiVWjrNQD49!4;ma=K~I|!ZeQ&r^poaT%WV&G ztH!Q6gT>uBhS%)M#N{uRPHuQzLd zC{H9x?7%wLMo8H-NBi>(d=N%Byv{j{HNI?NJZI1T++*+|sRv7j|1zShIOi zynfyMs6GK_U|vicBR;gA?UxqMgQSb@%T$!4q>xM(Zs?EHp5y`|+s zt6OmDs;rEeLoYjBll7lb+{qEQ5RUJ(zNC+7R@Z3MI;7JSz-Ur?z9|m`PKy(wVde#UyVqCFr)I@tm0iA z*w8^XAWmCU{o!$t8p~-;j_O{#P2D`ul>O(9xg|z<`EH>o?#>iYs{e#MU`^)oXt9aa z``&fNy2s?$D-W{$bZ3ilT~e@=5K&aBsbNMKa6rMdm@QO|W?Q`*?IIv>nLSmfPMMU113ARIgh`G)oyf zyon8&mW?#x9b0#TnNfOU1Z>C@<7G}QKgI>>%%O9=HZuUAxw*LnQw(6^yCT)%lhq*d zw-0M>puHqgfOfC_^=PF%aiB@?urM!RVGfpBh?`uF0|5~m%@S2oQSl!d8mgD}8u6JY zA~Cn3>-wziDI~##w@@KWHfg`?z8T+}4s;7X&d=k>k&Uylh%5+v7A;`}mK! z-YPa@eAUYWH7>U=S6g6n+RVL(>iwQ28f4fPO%W=lmpc&@MkOsQ>PECT_;cp|L700S zwvWM5;&Ie;1Kn2a&0J@&DkbW$I#qbj#Uo!DePEyhvjcu|Z3&&2TWUCym5H_1VQ<1D z*4*cf=(|PJDhej1SImhFI|A;&lmIo7ux`KRW`0ycwil|Zs(~A#0GRyMEue;b9fws; zDV7ons;jQftgk2N?(XjE>uUpci`DgH1!D|2a?8Bd;>FO)SDJK8zhAt=p+^&PM5xKK z7h^{bONq9ziLi&ckZFn$xX?0Tzh!-i=4`a~!L~#XyNUgBE*0H(eqD-9n(H=cZ@e(h zH=@|sT4T32L6uF52$-U=O_rq~D=TVgS%g7|U$bagLn$Og{{1ETkwBgB>&y$R7y3sY z`2QALses}=#9BSJ0bv;)9@c2}7MKF-0dpBqpI_@c!YdIqwFKr5_=LU7@qR^zWGXgL z;e?kIleogVN(^TRb+7Fd861`8qp>ErVjWx|l}2-icni>#vFq7}bEClrxwtjzwUAN$ zOhEy1;~x_sK2DhVDcPA?PTyKn`n88SstfsicCnq|A5f|JPs+;g^xHtFB;45X!QbFsW@l!C)QTsa3ZpKb(rnaz;F)DsbHi0K zc3Q%Zf8kSnS3-It`Z?y)OdzAvTZ~3m)Diq;#CJBg_V-ti?^gO<+EaJkoj(A2OUKCArOC!PO}r(C(TI<#`~!ahve%}8#Tt4^WQm`ct4JQlzXIxFU}hu%u(kw$cMTe zYck>iE5Abaiee1&CfDt9uA7he@Ew7Ii;HW|?c(WoMJ1kT`@eO^`fD)a0eY>Xs+v_) zgp3yk-_X!NM@J{Dt4qel#-?APu||10w&wIcU*DyIxG8MWV;allE{44Tn+J|B`qRv} zWXobb8f(_8g18p0>C`MHpyy@&aO&I{DKn=bkncWtY}5+O@x_%H~wK; zmuXxaK5oM{P%CS0q3E44Yt6|`61lW`^&rkO=Dz>M(Gd^_-z9acf1wlqgKcHfLqf=AE0R6+k!FkQWFDgOW$A(=xAYE$l8&TZz&Xt+OJk z=KZn~r7_xM*T4H@^=6G2Zd{v_}*Tr zGB&N!o9(db;dz*eAKXeU47k30=BGi}KP!z>HD?-T-q)*fj=Fcxk1o6cRdwltb{;Bzx3I^|7!@*9dtn$A_!w%xgN zmlS`M)E9il+}Qe+XJ{HL-{I4AQF!MVcg*WDb6)LO!|~2DncKku|6*>1phav_{a#~q zj;>cj&DO5dcWHS5*d^OKD6-ecyE|8z#;IyXJQOx|b|qjMyZMxd3=pCG8>Un|Sg&lU zs=`i7ODiZVJ7NyE=5*yJ0~Y;k@?D&9m;% zT_QCt9z%1ovS41ldew60Zp!p*w%px$$N&8Ko=jxuov>RfWjnfi){hX;UVF$syltpBe8)CSils|13c+%mGVtI)eR z!>wbf`4(N4W?j2uF)t9n%wrM~rtWq%zc=SkbB@6aBopHg1sm09c?fw02oh53*!&C+ z4_`8Xa;7Hj5j?N^uX(@11lGsuc4kcC*hfJJ+~-}U)1(MU3AJ+n!k^vqn-%EYcmHL> zif3)ub-MCa{`6_pp8I2>H*#a;1Awjc^z#zDlDWo*w9hY0_@!OdHeP`ytL4$zy=Qo zM~zF*MY_qTdsl{^?Lq_eFyJ_ux@`LiTdpx;Aw4B!j^V<>!lJ`O+e*vE#-^aS*vM4+ zAC2nw1Bl%9>H0JEtp@l?NeOCRUY@zF?b7zp#owN^6CWkf^- z9XQhz@{Pk4{Ym0;6}H!5ab*q~9C}K8&UQ5KDdS zDJZ_`LG3DfHnxCJK67PU%Asy=vSgPmqDlKK&S;}1b7i{Yjlmg_tCz3R*!l<_c5dQ3J*!cMP zf}$e7mTIh(SUb!U(Uj(Sh6A>4O=jG!Q&4nBb06Fcy0a;@`CIV|tR8n3X(IB({YUzT)!Vj97ghj>0W0RBd zKgNdxk!+_^5{6Ahot-(KcP&0X&wRGgRoY8k9sex@gOVmNcygJX^6X(V-S2Wd(Tk-vERJU_S_$!Zel(sj0y= zF)=9sgO5o$u*wqCpELP?sZwReA1dPA-CY89Q`DYtLOAKz(a}-gqN2AG6BC&g6*1Y; zgbD+P&P^@qTUAw6fc~K3JGSfst(dpB?$0!(qCzRAuL_uR_fIFk6ru-BS{|l-L`sPkL<_m@T|EA4=X@l;w|ENWsxv$wZ5Iy);pwsBRjos*t^ldh1P ze2A^N_gPEJJtGeydVJX@01uQ{|Ah*=D^`FoeyBXYboU54ot~b~$;%61`yGfRTFaLv z55O{DNS@6ez=GE*y-(c*f8=gL@031$`o6bkSzTARk98w8|3ChtxTl)8ZqMybyA9Vk zy<%%;CkZIj^P!H8g224*h}gfV$kvbe;%;M6eBQ*#Z<(pYt zT&#Y(zrR1Wu&}`ApUjrdKVPcGpv|m)JbGJQ{T{G|L8zs`?98X9tD}S7n1gcYJ&PIw zRDdB-IkBjyxCtJ0?iDdcPbE#w6R2e<4F(~>xUc8BJ1 zM!a_AM;okE6YSZpAfK;Q0#9$oQzc-Yku=a!8ZKaCjuElRVWX4rw*emFD6kk;*Wk`h z(f35p_^C6!32Q}pE8Z7;;zA|JT%&$3wZkZ>6Frlo%4Bq_Pu|sBEWBk_shDs8AtW8p}*2L$+x-QiSl0BIFdZ zj_i`ANumaWu{IctWf)_A_dDqPe4bB#l-~D!p7(z4`?{|CdS;?J%-`i<2&$o7g`+QV z3s&TahzM^BW8-T( zXC+G)!tH5frsYi3X|{Vxl1ZF-MxHX<45g0e};RNmibUg#RNS) zairc`>VVb%+Gsgv8}Oc`S4TI^=F&ce^5)X2{5w-y@@!%uTa5u;QXZLP>o}Ap<*S98lPOmxXc7huv~%%Cd_Vgj`xXg;Kw` zXmEGO;R4&*;o&`BzI^#QdQ&1HdLrjmj6Tt*w6m_ZcCB90m$>Zayk6~m2ji(7HCA|8 zIk~z+A|f8=zT;Vji;-zB>{Qav?sZZ_y`a#k3ky z>(zpKTQwhJW9rf`dL?7}$V0adh zJd0GDf4Uko4p+ZtZKxKGr8b}$xwK7itqToihT?4*;sn*l(O(_K*~UvRaDUXiRfL3< z)x+j14;wG_-6R*1;7afi!+toGpS7sLRZ+ALxdQdxaP=PDFQ=Hf*WjUwa|PfR>9eZl zG8enIh=@>=Bi#N@$5^@4{i{^ttp3mOT|7kip1tY7p_`=;;4q8?!qn@E7`MaDY2}hf zJufvx5kq%8$n0cQ7+SqyykA-GbM1LXpQAm`SZ#fMc0=Q()1Cs=r*QUpZI9&(j|i*P z>5K$z$tFa-(}fphIA|^~=k|4$71Ca)EM9-1hW`+6%5s`WBe_-j+oN##VzSs44<2EB zz0>(8c5l3R$FVG zMGhW3*#7h95hXEl!Urr#+seu*LE%J8*$mBiB=Q0ZH@V|>1ZAk6bXzQb-E-IPQG8dI}}k>e}E-)-D@x z_H%f{#J04Ida*q(Hix%wa{#O_!F_*JG z_a*ntkHxUS0n7y0Pybpi*WH zwwfG)`fZRBpV_7P`#$%MHFmiQorw)$4`U}_r;>LEITomDe$PDgIi?-MW9kl5x{o~9@WWtv;BPJgFdDZ7=d z^Zsy3nj%0KxCnA7g~xA|{x;L2kAU~Yy+V%r z_q?m)$ei@HA1F~h_heV7p)_7z8lTtd93nV3mtw0bR^cmUmU{`?S`|Rs%!I&5$dFAs zDWBLZCygiW`nEDLTVI*r@l{<|PDpT{yv##}Z=3opsQoZbT3B5E2ZfV$af}|{hFfK2 zB{9_gd|2>^aOEsJ)r6jw`6I7)*Kh{L~Vg9pZH4&1ZE}3-33L$K+nmRonmp0B{0uB;YFd1U2(B>$Xra z`%`R$*YMv(l2KQUa{%pqcjr!gzS7_8$}`*r@BwuOOSk!9C%i^iU%#fOM;2mZtgAdK zG?cfGF&DQ!89m~whN>*|mwP4?}DF|%DkLiF-k zX?XHYn>M{Az}F|K6ir3g_PGES29TQFTH;4QO^EBXXkBJTonhAwFAB~zk=KQ;s#5zW zot`|{m`E$xrYVE@HRze3080S(IMMR_?`WdtZ#v8b%Cii6$ZUc4*ixM=Q*-rnjx5M6?g)LW4(PFq(=aL8SY>Gw@0QOwrWjn zip+6o2zR=5`Mnu@aI`(MLY08oS{-;EuP(^jcxiLvB_9<5YBTFmK(XB>K0ZDO%LSvq zmhNrtOx8AFcJl_0f8>KeZSU-?{qkkghOnyR_gepTQ5LLDMK9{0qxm^QM{Wzxp+)m6WiQzBDY91{{bEq(g+ z_m?8`{Js3RJ3#R`g2d# zT3LxpQW@&n~{CO6-Uik>jJSMG#@`i?-Qi|2oI zU@0q)BRa$Q)kUB62AxRs;_I`=bB&WvW~S2gsuPGIy{wJZhxMh+l*B@vP=7~FpgqLX zr^1f*e467#2QIN9_1`wmLBea?3T$EcT+o+}PoH=0vwD8C`R#zOjp*8v-0FXoxEm%_ z@zbZ2)7ZzO`R4>7{z^+rjc8VCFd>9Iz7HEvkH7Woq5R2y@hJHE$M~IFGSAgJ=ck6% zpnfFIb%4?XQ_=RE_+S+^bxbDRT@v0UFyjZ!R|YrtArHgN{kLr#L$)GOhcIa=X=yqB zyK#{z?&7~`vn(TWdQ|%TaZN#&A<-+i>A+xuvsRauISL0~&;24ROX{_wsh_G(Db$S2 z79a02ck43O@5|`9dQaqC1ObkWbjRp)RRRS})<@rEFc`&k-~Oe3^0)tx8+GN~f%lzS z<_D@&k6O08&zENl(#Rpv%sCWpqN-&$e$K)E`;4*N9oScJB@|A*4V7}!(PsOC{BqF| zu2#E2?>f{`8X#2eAY^7{h9q9-DSv%6>`}w?@DL{%j?ror2_N=E%Gp?o%hx7K{wUfn z;{e`R|Ls{>4rqm1J+t|p`1|!#MX@N@Z$Q~K>zQR~6+qXROjLb+{esa-u6y*?&ha6V zynf&mKR}xC@l&`(g?g)hsJihd>G2#F)B$C?Hh=WhTYi3MQvB$J{?OHO-_uFbA@)^j zQrqb1Wb26#b^7RiO5T85eC5XY%sk$(Xf+}G>c z;*u`e!QDIfSGN~#Bp2qf14TF_ULI#ZTPgmhyVAVmyKDDMkPhQ9LcO$gpb_(0+) z>J9O38HFSYaZ~H;?RF2-WobBC0d+L_P!R5Ff!?%i5SD+$kp4F1ieZkm0IVV~L;mXVZ$Y=0!R#HVuwxVgo;8c$ z|K}+wRDjjHckiC`oAEc?k3CPWy|Y>59C^X^4UKgake$x~V3~mLo@N}FMWxhZpkigU z?nM@7^k#pdH{UH3a~T%hNXT#k#sNEp`OfU&zYxRht)F6W~jQG0H9dDKHDdUZj1SbF9qtxRVH?M%UnjSgWUMG zsECgT;2M&}e*Tn@jH!EhhA5E`tZY430BEaWkO9~XFyNWB^BQRtKd*y)VeIg0GDDWw zGVv#cVdMPtVqn-fpw~K~(psInMgXOm#r8n=fY{VajdDK~Mv8tgE57)ZQ{DK_xS6L3+5Q9}CpOPfuA$ z*gc_ee0JJnAr|rzXe;wAK7faYk(Q#G>8Ki9T(>@?7 zz-c=@#bZo)rvtagm{PyZHV!#(Mqw2m{CLB zp3PUbh>MGZ05h(SSFh;hQ!uW{JiP~fJVaj_o_C)FmGKMU|B(V)lN{?%I;%+R&zmGA zK*Pcz97v0P$h@dUih)VF1EBy#$YI0MK>meS0{OT2kf716}oH9?orfoh9V)%=J{Io6L|6P5P;x2g7DN>FgX%v~;9m}~+O zMORM`;ZYfz*!~jJHda=#Q?BoRrm~EgAc9Q62s{J>D8+m7=u7;3UdvA&xvnku*Fg4A zYLc9HL)D$eEM~u5l5as2TSYF6m;P^BIDkyIriMfiUC<#f9OPRlZ(WL0I(zEUdJH`c zfrC`gt7k)QCG&yap}T3GU7^3_@Ip~Uy7{(F9rG<2?we{$a%pPjTL5?8Ab1`r^@n;s zQeWs{XJ_}qI(uWeq4l1=T5azu0?UcJ*=Qw^${BH>fE3t{esk+8e6F{>^9onW)t4qr z=F2?-5{beAmjLbp5dY#~sh#5?G6<0g0SW~ZpPSC5IUKmFckF-H5)?uY2DbQz3=zy* z{vp?TOwo#V)eU|u^nlcc{>0vk3-(LoimsD~j;&C`uL1VruCA_2aOu9jNl2Va@OhR< zyj$Xxsd*EktDi_swzO~S$Mtng=h?0HmH8BU-t`7Ep~DAadC7&U%DraGS2~>Gj^KKL z-tc@NJs?+vKFZn5aSg3Za@)@M5OQCHLmYd}k=xeUVW#PKth09sRlJ#3ET=EY)I!_U zdY^+5z(pEwNu27uti1q15OCJ!h=m=(i##W!U#QvzPL-C97%y4{yid^mNN!qvcW-4& zfkX?6Btm05^DT*Qm$Bu#aCX#cIRJr=8@qd|!(lC9!xh+W1Z@T+IzjU~rY*q~q}?V3 z?ZoD|;&p8EqP0hMZau>%8K^Jctv&KBj#?F#z01UFWgmfuZ)SprAob&Ii#qr0y0L*!xf=U zh-Ff5rt!>J?qMZdOav!e(y0PJeTuJXB^S0QEOd!+B=67K15_^}$BC4A%F1*Oprx;q zpQKNL9_lM{XnE03eOHzLD=nWuAPDgwBA%b#67Gam(kWTk=JK3RDfgEJff7#Dg@5zu zn$rtk7_tkj_g1mLNmp1!Y?)kMtvi;4y*&j_dfg;y;7 z?vC3KhBr$!`pgCs^H_=?7#pQe>BLMQTm9dpHn997Bup$H9DfNM#C!0|b_jQf9oU?n zMIgZTr#bcJM-Pp*^H_WUV$JO zb9YMrk?3Q*80kC2hUM!;{=Qkt{3wLET|~ib>-Uj>jRSlpE|qW+5m-go0Q!b1pt(|# z8~vBJz}<^kq(%S8q03`tc3H~vzwqluw8Mg@_KWao*M*GAtzOJxL9zq(#c^9E`Ej7> z9)EfF_{-H+R+f9?5CQjKz*wbPbit;%V}*dY&nc~I(W=MTYcWG8X2NSG6;3|u!l6u-Eh(>iTWl9~2J)lH@rU0QATvh<@1~wGc=SfM8UnyL2 z{Uwf68Y^qq3_>wE*#!U^_priv^5z94WRl)p*u1^dRLXs99hA zL(;YqO;AyqJZY}0jOA}=)tRQVN<4JXDpcp7DV~r1EQtW&JMxq9Mfby`x7XhpMB6L| zdmS9FrQa&Hd%`y<<_%DDX_p&Anuk0CgI>W>0i7kXRt^+m2;;L>mpfca<~X25v}g^L z7!=*^D{QrusbZ9+hOHVDp)xiEo3}h54#G-UUDm7$Vk0@!m_AS&l40~{m{I-UPfV@YgZNus9gowB_w z1HjT*tyy;727pjfh{$;Om!0jam{pA)*-T%^tR=jM+D7*sK5x6?e}@BbZw*pHQ8~KY{&;+K%%Z^sONWBv;mSb7X208GUBnL45SZCDGGtZ&2br8-?3H2X3;#nwP) z%m@H0ff65|d7&fM{y|F6Xz2IP6* zXs<(49+q#HRWG0l}Y-uJ4Rhs7>HTE5)Cc#Q` z`;+@8z2KAEnDcFf*r3_5o|o|sl9H?JibR^#1d*EQS=7&OI3B!-fa=HBQ(^gsPG5|k zm=>H<6NH8iXx&s@K#LW0p+i@!x)R@woRNCJ+4c3Stdf6TxsEKe!P?fV`@dh^zrd_F ztXq~zfyIMdSMba?xKo=lOS-NNBYaH9{7mu})w`hX6$?wGhs?>!;AEj^L}%Tww@J2^ zS@W^Olxj1r{^WAwe$f3AuSHvw=8;-B%PPt~MgG;uK7#Ola=8ak2B>Sm!uM@G#`u-V zUmFz_^|zyVRPDv{VjyRpXt@VMtRQbf#*sFEoc=(NWH~AfP!2SxEt&oT%=5GE2^mtU zgyz;U$M>E8Dj|a81oi*1_g>vi(uSDmdb|v1D@a=kig2ADNyLj}qID!={+`_z zxw=|SII0%LMbtXDWrjQ45{e7C5Lc$_;m}c|e@k5)U@|D>q&-uKOoJ_ak7{^n1A~5;tn%*9(gy9 z#G>3{Z398K**5sBsL7X?vAJvCZh)2SIrWC`$PTj(llNv8Ndfi&BL+6pgQd}fprJ15 zl4!H}_pIYDPlJ4S*dWFA>+;(>W5fv+GN|N}3BG+!2?x~y=|S76INHWx5}il0JfL03 z#$dCsW>WmZJ*u8^g*6{}CY}x*y+&din|)0uoBS`zWGGF>q{gd-c$I`mBY>h^%!b58 zW?HTj`+cCEnHZ!D${bwA7g5k(fv>CRg&q$y#UK|VO)>Z#Xo^KvUg%?thhT?JhY8ZA z=P@8iT|U3mSJ23c56NOHnzx)%67z;Cc1!<@;q~RJa3%EP;oK6=yA-w{WKOPpA@b}5 z&im8buE@b$njVA$NYhG9w)bxlej9)U1ULfTH~1dlvpJUPx_0SaMF2|xySRO<40pK{ zV)`k?7w42zLe8c<{7esUz*~y01yyZHWgdCiE9QNytvfTf}+a*dUA0(AuKB~&fUEs#ca6(6L*ePy;c?veZh z!D|@8JA(l4;v-G}!Y1^ycRU_VbScfH!6rpsu*S3JEV|wj@d5rm{g*2Ww=8Lv!fS>O z5(w76dQ=mNh#+h7CF4KUJFB02vcJf3?y?sI5~6U6MQN%&s9(YKPt4dJbtizLLslm5 zH#dSO(RlVx&z18}1h?a+XOirB^ko{w82$eC@$_)3(w&`JWaXwRSeUVnV$JxbcJ&ot zKTz{>I4!@-Q$8Lf5^e(DFXPHju!byw0%+pd zVK{%RNxpnhynj#`F!xXx1Q8yyl5Vp}P z>uiDhW0yT=Lu7ztC!KV4E7dJP46i7U}j5{$yg0hHn8BHKbq29 zk_Vy?AP8umhGHC`KZL1~&{(lVKsH-e+rTyi{YHQcpk9{XqaFMmh1VEm&IqxEC=Gik z2^o#uHlml^L^P8Nv21M<%NOnm`WKE$9hMMW7cXa6B4lDIR>JJuTW0gPaVIpNV9}JC z5)RZ2Zk;|N{NnWM?x7+60^1nu&gJaqsnk?e9WFEhJ`;&~z?Mex^NFH1HXh3(aHlvkQgRh5o&d={Sv2|E>Ck zM_5GZ=(%WT{J|YNJe^RRJQB6LYu!Gy+e}@*N6IVyT^t=XlU?XVX7y(ALZ^B|t@Fe; zezsf{CIM_tA#mYt5GpI^j&lZbOW7gk0QAy^!kr=juplctHRU(LPf@T7yzW4*Mp%Bl zLneB}@%f~|ais^h9&3sg?k^E9wzskZ5Fs(uW?$m@%#uQz?t6B?3FUwuUjeOm&RW%u zjj6zf1sZuOyS49iSuFL9xu4h3+pz+Ebxfba#Ekn#Q*Q%ov{TX1GI4rnX*gT~Y7%H> zfGdG@s;*_bb!e`Tx3!>sLF4`P*(zEdAr_d_(NBN=!hs)Pj%M9I156ahXAxE83m=_EHb zEeYy1wkDLe=mHwULvEO|AR(kYO)6`d4dk3YW8W0%( ztX5YGetmWJ6EruS-Rs$=wejb5>*T9YT*ak9z_J0(Nw{`IxI?#ZWs^F$J2q*Vca#T- z2hh9Ljt7U0foj*%at5(9&CMf1fJWgq+?^8&q3}TP=Ov8;zE3FwZXqZfY%~a=hnZa# zoBxPLplUiplqt?^uRj9lG2e@xo-ZOX(jX zZQ_<4hMi@?j+)tulLbu>lurOznUOPz*ImGogy{4UPKGU*n&3fb!-fmVtiEM%6b=U= zU^%cn7Kk`C6o~j1OVHu~#y@UEjL%byzl(;W5 z9h1viD)IWxp^7Cp0ty?6j7AX6}lbH{JyB~jf5mjefUX(}1 z52BG(Ja`a*rNG8Ndaj$f{;hEyy!4@}mQ0Iq>o<;P-zsTfL{09vCn&}phoN!YuFVAS z_q(6}e5C7catUr0Isq{QVk79VODeFjocc{;OlP`*Ah<5Um9ig1w~pftjs-Ex#O^L+ zmu+5UcMu`zKasTGZMLL_=n@CyY;gKpkW_;vqjeqCMu>a=~h0xOvW(o_*7#I^M^*ud3XHyBe>KQaWKYOG= zX3h#Yp{iy>K(GcZ#??~xd9g8^Z`aQI*E!$^SWI`NTb}!~UguHA-@wZ3P9iVfN)Y@>18Ff$3y;T;Ag5bilCB1o| z8^tArU+J^`=29r(B6Qw3q2b*sc*#yEu4G!tZ4%E>^3R>{Z1dlqkYTRRhuPL;#?!4E zD>zRIo3K`f?^+7?AiwoU#EPx}<`XS7BGGS-a(eH7=zey@Jd4eaps$UHfq*6@c7jA)hnLWd?kTd_$CfEYgS~@NE zmzG8$VG0^0bh)7Y3~eAJHCd0=``d%}69U{%FDY)*+)yF#INJ0@a-sgUOIpAGh>Ks} z%82s-8ipamYOlXY)`t85&8Aa1);%l(qsQtMmK1QDf&Mo6C^C64Cm76;C5Wf7Uqa_k zr`Pg3MQaCa8qgCF%niZ=$hCRo5N~hwBDafgCxc3x*$_lr6X_k#qM%{Dahlq3dWj08 z|Cddp>f_7r$i&jnjOQ*at8~CxQj_g4d&5) zt*m3KF&%&WoiX#|4V&%TK$JPPH%<*D*o{Vv!7zYU!+?wx%?L`QVT>1Ua;>%Pg(1W2 zV)&Hame{YFD^rlQB)mbIz+8WXbR`1~`UV2YThfEdr99r!BuBgHT zv6_A!^X|OKnf=RDi?<5QDrjfW!I!CeYuN##xz%CWQUujjZf+H|+> zrn@`Z8y<+K1SRh_8?Mh;ul9H4pGjOXwK&#wZwRIVW{=lruMT~41b-}8ctGd9VLw|( zs)}%kVMi}2)w(SxB-vtHwbfr%7LMiMa`oKVczsvr;>{pz?W1|5IfDa0|m8@7)&Sk_5YAJdE4|Y>9zB&z~rD3`0tVuum zF;jK)hQ-U=#t338Z=20(@4!94V^mCs()kjes2?>%30_Q400j>S9+Pk7UY@|JY;&L* z_wF{FTMtcJr2ay~wMDDVpl^3)hI@h_hmxajYP@}%F{X3wKMUqHQ!KJTP@R})ZrbSJy3zuuSGeiWx;og5ogela{&Gdx+bSo;-Tf7D;3uH z&x4e0zuI?58Q|d_1`Ve%_Tk<{6Z_!|)wBgXqbB{lNyW z|6@1$1XvM4Xq9{TVbaxnnUe|wJb^(ca4Mch!QoU(C9%}3y+)p~WKol6^OZb~O|3r% zkQ^Y5^v8l+l2;l2-JP8k30Mj|xaX9#wU=#;#@lkiHUR|#Xc8EZfZHz7_r^<)yo-8h zn{SX?F1O_ zwa!Z51n10yn!)>@NhTK_dY@6H5Ar)Jx}J(O&#$~YWfiwVY(4Pv;wF%pAlGu;KE%Q1 zmHzrU~Uuw9D=x9W>J!~3^Qac4Ua`Yj)sA7 zK%NJn6EvwcJTb9KccXXgXkN%%2~E-s9-Q&gr*Y`olAqqfro--Xx44l_LYQD zjY-ZFj|!Uu=R#kDGwgjBdPz?hw!u*P(`iXce4(Q8Qyuc+H(?beq4;Kmy~jzG;gV zpjNO@Am)izgD-(&aJyf!mRRycZ;8s1_!}JsNC;jjkbp}X>sz!WCOfoAE^v?R_Nb@g z0^Q2;-L9Rkat|NS&UNhkHYl;M!Y%n(R<=DCg0F+|Upvt!5zcLlVW4dVxFma0vrl4@ z+trkqOmBNe80EEQ1V)1=Df)?be177f8#ocT{*#U1uF#F;ZTk0z#;ql%BH+SVh_R*S z^AbRVNPDkmXoO)L?V4r(EkBvg=(8EU-MSMG&FkdiM<*1-CI75Z&NJwesAA_3h2cX0 zT^ekd0@Z3i6mHddZ23sJTAtI)vNd57z4_Se zo{Y4JMBT&tk40-Tm+e+2OFlNtOtd)In_p=%G3OcZ#7bG{qP9Yv3 z5N`ia5T8{3!+kS(SKq^_hnC^mJ=1anArzUw4n67Ml13L0jO;EPUD%|e_icsD{Mu1L z=enimr}h94f)gl^#-P&S7A+R_zMQET!;lM|$c1)g79G$?Rcbn_Bxb}lni=&FEjh!v z5;C~d=m5yp=;UZ-qKaqHG5IOvu)xC;3gIfejVr=-^JjniU}jG^bG%vz^hCs)0$x#Y z%V|%w?J841pAcRHWe-N1isuy+E*dMs<_fQXy}wLTn+#+QaU-5Lf*H2>Yh7||Y; zH2Yu`vD^#wyt+kfeX!8_4%O%Q84};7_?Lb-3RZ{5uGTAtO|>Zym4E_6j)iOrDsQ36 z4+V5Is@9=zc3;MZh=+c-X&;$%#MJ$v>~5%TluKN?0sm@*ebA==rrWIG!wq+NfN#La z%7We5^c9r$3tMhwPj%GSwin{2|4k0hrD+V*(m#3_&%a^$*o!ZFK`yWPgXdQFejf+c z1|x;DFbJ9`1S$+@nYiR>65O52&522d**zY;kpjTAaZXA~-QxM>T<1mauYz663%X4j ze39Y;q(flafM`g9)a}7G<`=H335-o}EH1ibKX#0^ z2p!uEP5*LFvHbWPpLJp2&Jr380~DzntJzB1z3Hon2x;IHbF;8Wj#&|JLr0%LRIQxB z#C?m(f8T_eK|4RH-9q6p;yb*Q;?x|vI_g(uhCAE=EPU|iDJ%ben*uE9CY zT>Bk#6Y@OUpklV1o@$-lIYqY+IQ#^QTf(${QQkuODTWIJiC*wZDS@{;`qZKJ0E>Qh z;uGy&)Wfs5{RE1;G{jvrx>^m_6-wJor;Y)h0I(WkY83m8IGM0ta$_*<0_CXin#T)l zrxkPE$xW-IPRzf+V;889in>0lR}76tronaP;*K}AWWd=J+yCL6ng>-PEa#woTdmd? zx9thK@@f~kv{MCOEgUiv82m<=DhL5TU>Alp>b|%rwdzBYVf9i0Pw3?jUp< z_^`V6UTd`R8Suk`iLf1FbMhM4L9sXQq$&FJl)@eC+rq8$9s7B$TdqR~1)d+ALlt!5 z=(M!px?6)u0br<>Il-D8B=9hW7Bo*R^__l(Y^XFeX9bknsZh71Fz-F5Rb=hMT5MjB zn}J42E=?yAgPZ=1A#n4=0ZGD0E6s}*P{YtiDks?kpjPq)Y<9jy4m=*j#ky=E1F zAD|W`onuv4G0-uYJwjOBCW@qZrTD6a^_-Dqu%?b#OB}4M&>Gz&Z4eycI2Y$Akw7RL z_TKLqG^KSz{gyF z1oI~mgM*G0aZ+ahRf!eqKDgk!Jf+&byi5W86oG?r(BbK}!A{#iXZMFm+fF>m-@dCR zg1|(vOVk8aV=m@Z!{->^Hk~4*=SA_~6v+&Bv?~wM8X7G4#x@@VPc>p!P-~o~)z8fk z!rALNpO+>xbaRMjQHO`acz+PG6LZAXP{6#UPUSKvKg8W>Jj+UYjpoD{t2hae$C)XVBE&a6nntJoiHh)A_0cun- zb~@7nBv)pZppz;689`w^PgNBKr_86<`gQ%!&ED^%$XJVB`Y6X3?2oikRFun zn7)G_Xf(W8@XW~fHAM$nSxtayUK8m@1DuDWVBm<1r=56HwM(FOtE6BxD)0+pHmqQedJ!j!?l5S} zl>f?=C+lig}7bJP)>}l)O zRPOOpI5n$zSb{3h3FovB_Q`sLgE>B3Q$q?2Y2 zp3z(dcG*<8+iY;RL>wXKm3U1994d&fh^Ni4Ah&ks6m=!^2PFSl+|&ULA&w4~p%Z>m zKKW9gfgW+r#A&CBk(m9*4u-zz>=UWmJjtqo;xTB-Y@Xnp$=dnI2ZXJ}przXDP8lhqfQN~fJXF&PBMVkcs}f_ZoE)-;b9Tv$0b zaKHF-H7DJ0ULK_0?mFLbN59_{5g3t$$2Cv54uCG>@u+i`{8R%<`EO0A!$I!}o%02l zhj$Ey*`$Y1LsddjbT-bBSb!ud=3Dc8kPzmw0cYQ5;haVbjL*PZ32|$=$-HfygkzO! zkKEWZQ@f{2P8WQ|+n+{AKb2try#LfO?r=(2~j z($6cQ_REJL*v+YqW{exMeiCEt*+J6j^XaM5F?HxQ&vkdfIu~Qri!OAC+l$^vN(+)E zn|v_KJZ;R2@ly`75|}zRv&Xc5zxiWzLpo>?5RMpTwG1LPG%69Q>vG?!cA zMa9k4U@-SxKbJTr9~c>+#S|BLM8dlINgkP^ejt0)G5sGJ%>Obi!W&TB-mC5_6rV#` zS@pjvLp`&x@^_-Qk~lY|_#8R&Z3mFBL)e;@4YDZp*oFEJSk7Wj4e0|bEq$J3IcpWs zTD$Zj2v%L@)eLXG=*0|J`yXlBa%_e?SA z$;V1Rbo8pQox57K5BTVe7UMmmOv}#II9ARc@(*L&OS5lxxs;>UOuCE&e3ehfe)DaS0qSNBR;@3~;yrz!wa8qYCE#T^%=&i= zp8vgIIb<$;*l|AK$bSpX9h;Rsik+HTOmqlJx?{j{F)qX7%CfRF9Wi5t6g-@@xaP=` z1pBk=TvFek$2dEnEyRJ3A%P?OkGpgN?-vb4V5fL^c-g<}6(d)3S<0!(?5w?pgsUQz zRzeVGz;i1m)7X99^`ww!vX@q+87IWV5@*^_i;W_#YCwt4-j5JZ#kB63OuYH^%C~n1 zD_EWM-q9jTa(_VVe{aol-8hz)8RH&+oIqLVky(`0c=q`dZbnZ^u>0EE5+cc4YJpAR z90Fb8%A7q*>^5iBb4Tbg>-=wG*ABU~^;0|xjy%phn2?0+U==_>YE^b#MdZ=c9Yy$9 zl$lwmnIU0}CRQ$FP(R^V-_|`h2n{PNe0Au3u;HA?5Es`}a`;~~8Xhn<_B(@oLPnZ< z$QVWbQpQp+@!Zf#AbPV?sHOCZsJxKTYwy0~ag2C4&#(^5Or^+O1$N$SF|iTN6g*{6 z?;jKk=a~;u<~AW4>C0qk4YEl9h_g+@_?=Ic1xiR>RYj~$m!@P0u%W>gYy_HH)#*tl}GlE1rl>mz!I`Q zF%INT`7X>6v9VZYIQGBDu1kQ7NyIKPzWOJLG!1?@op%Hxc&3ODL&tJh?-pXYRhFBg zls{jVUKA%VO+Z;O)kvWpAE6X_1o!WA{0MILD|4OPi`w#kK~~D-DO|RwD^MV>Vjek7 zHyF-1pfY_R84kS=ISWL~?A9xDaI00aGl_=W!%KK}zaBL`0U~@0#u)2w?Fjlj4x`jB4?6?3ag*UkspiIPQv_ zapi{9wJ>B&-wPsZ3XExWx0{w@=v?RQmxJ_f#GdwNcNyzw6``3Wxth*%Qm(%cKs2C3 zxDSE#ETitfhuhZ-DFr!z^#4}$EDMeYFtZ8s`#^NN^kh~8nLp{Y_ZJ8N@+P`WKI_d(-$BJqDT`5V#`$pjTTxA*;^_YN%YT?4*e@BNW|Vn zypj{T$D)PrHXw}duJVa`K2N>lL7iX}=ZKeJe} zSp@m9Z(cK=!XtQ#>QU>-8KzKDjJZF4Mk&<{lUY<;nKEmDhnd3M2c~}b(Mob$H!e$k zRJ5yW)2fA{1*@IlKai74FkfHUCh@FHxo1W!Igj@U_jiqbFWzR1qf6Oy)9bL)umRy` z01tmdMIIe~(l%D2nPuF)1W!U**R3Di#oa6Li*CFqp2Ywj+3J6aBs_aS72^KGk0({} zV5rru%