From 2691bcdd9dce4cc298e97214c2fa4a7dff2594d3 Mon Sep 17 00:00:00 2001 From: Carson Date: Thu, 20 Aug 2020 15:55:46 -0500 Subject: [PATCH 1/3] Make sliderInput() accessible for keyboard and screen readers, closes #2845 --- .../ionrangeslider/js/ion.rangeSlider.js | 4 ++-- .../ionrangeslider/js/ion.rangeSlider.min.js | 2 +- inst/www/shared/shiny.js | 20 ++++++++++++++++++- inst/www/shared/shiny.js.map | 2 +- inst/www/shared/shiny.min.js | 2 +- inst/www/shared/shiny.min.js.map | 2 +- srcjs/input_binding_slider.js | 15 ++++++++++++++ ...propagation-of-click-and-down-events.patch | 4 ++-- 8 files changed, 42 insertions(+), 9 deletions(-) diff --git a/inst/www/shared/ionrangeslider/js/ion.rangeSlider.js b/inst/www/shared/ionrangeslider/js/ion.rangeSlider.js index ae467f103c..096c8c1a4e 100644 --- a/inst/www/shared/ionrangeslider/js/ion.rangeSlider.js +++ b/inst/www/shared/ionrangeslider/js/ion.rangeSlider.js @@ -816,7 +816,7 @@ */ pointerDown: function (target, e) { e.preventDefault(); - e.stopPropagation(); + if (e.stopPropagation) e.stopPropagation(); var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX; if (e.button === 2) { return; @@ -860,7 +860,7 @@ */ pointerClick: function (target, e) { e.preventDefault(); - e.stopPropagation(); + if (e.stopPropagation) e.stopPropagation(); var x = e.pageX || e.originalEvent.touches && e.originalEvent.touches[0].pageX; if (e.button === 2) { return; diff --git a/inst/www/shared/ionrangeslider/js/ion.rangeSlider.min.js b/inst/www/shared/ionrangeslider/js/ion.rangeSlider.min.js index 4597a35790..8a0907b2c8 100644 --- a/inst/www/shared/ionrangeslider/js/ion.rangeSlider.min.js +++ b/inst/www/shared/ionrangeslider/js/ion.rangeSlider.min.js @@ -1 +1 @@ -!function(i){"function"==typeof define&&define.amd?define(["jquery"],function(t){return i(t,document,window,navigator)}):"object"==typeof exports?i(require("jquery"),document,window,navigator):i(jQuery,document,window,navigator)}(function(a,c,l,t,_){"use strict";var i,s,o=0,e=(i=t.userAgent,s=/msie\s\d+/i,0>>0;if(0==e)return-1;var h=+i||0;if(Math.abs(h)===1/0&&(h=0),e<=h)return-1;for(s=Math.max(0<=h?h:e-Math.abs(h),0);s!",h[0]),(o={type:h.data("type"),min:h.data("min"),max:h.data("max"),from:h.data("from"),to:h.data("to"),step:h.data("step"),min_interval:h.data("minInterval"),max_interval:h.data("maxInterval"),drag_interval:h.data("dragInterval"),values:h.data("values"),from_fixed:h.data("fromFixed"),from_min:h.data("fromMin"),from_max:h.data("fromMax"),from_shadow:h.data("fromShadow"),to_fixed:h.data("toFixed"),to_min:h.data("toMin"),to_max:h.data("toMax"),to_shadow:h.data("toShadow"),prettify_enabled:h.data("prettifyEnabled"),prettify_separator:h.data("prettifySeparator"),force_edges:h.data("forceEdges"),keyboard:h.data("keyboard"),grid:h.data("grid"),grid_margin:h.data("gridMargin"),grid_num:h.data("gridNum"),grid_snap:h.data("gridSnap"),hide_min_max:h.data("hideMinMax"),hide_from_to:h.data("hideFromTo"),prefix:h.data("prefix"),postfix:h.data("postfix"),max_postfix:h.data("maxPostfix"),decorate_both:h.data("decorateBoth"),values_separator:h.data("valuesSeparator"),input_values_separator:h.data("inputValuesSeparator"),disable:h.data("disable"),block:h.data("block"),extra_classes:h.data("extraClasses")}).values=o.values&&o.values.split(","),o)o.hasOwnProperty(e)&&(o[e]!==_&&""!==o[e]||delete o[e]);r!==_&&""!==r&&((r=r.split(o.input_values_separator||i.input_values_separator||";"))[0]&&r[0]==+r[0]&&(r[0]=+r[0]),r[1]&&r[1]==+r[1]&&(r[1]=+r[1]),i&&i.values&&i.values.length?(n.from=r[0]&&i.values.indexOf(r[0]),n.to=r[1]&&i.values.indexOf(r[1])):(n.from=r[0]&&+r[0],n.to=r[1]&&+r[1])),a.extend(n,i),a.extend(n,o),this.options=n,this.update_check={},this.validate(),this.result={input:this.$cache.input,slider:null,min:this.options.min,max:this.options.max,from:this.options.from,from_percent:0,from_value:null,to:this.options.to,to_percent:0,to_value:null},this.init()}h.prototype={init:function(t){this.no_diapason=!1,this.coords.p_step=this.convertToPercent(this.options.step,!0),this.target="base",this.toggleInput(),this.append(),this.setMinMax(),t?(this.force_redraw=!0,this.calc(!0),this.callOnUpdate()):(this.force_redraw=!0,this.calc(!0),this.callOnStart()),this.updateScene()},append:function(){var t='';this.$cache.input.before(t),this.$cache.input.prop("readonly",!0),this.$cache.cont=this.$cache.input.prev(),this.result.slider=this.$cache.cont,this.$cache.cont.html('01000'),this.$cache.rs=this.$cache.cont.find(".irs"),this.$cache.min=this.$cache.cont.find(".irs-min"),this.$cache.max=this.$cache.cont.find(".irs-max"),this.$cache.from=this.$cache.cont.find(".irs-from"),this.$cache.to=this.$cache.cont.find(".irs-to"),this.$cache.single=this.$cache.cont.find(".irs-single"),this.$cache.bar=this.$cache.cont.find(".irs-bar"),this.$cache.line=this.$cache.cont.find(".irs-line"),this.$cache.grid=this.$cache.cont.find(".irs-grid"),"single"===this.options.type?(this.$cache.cont.append(''),this.$cache.edge=this.$cache.cont.find(".irs-bar-edge"),this.$cache.s_single=this.$cache.cont.find(".single"),this.$cache.from[0].style.visibility="hidden",this.$cache.to[0].style.visibility="hidden",this.$cache.shad_single=this.$cache.cont.find(".shadow-single")):(this.$cache.cont.append(''),this.$cache.s_from=this.$cache.cont.find(".from"),this.$cache.s_to=this.$cache.cont.find(".to"),this.$cache.shad_from=this.$cache.cont.find(".shadow-from"),this.$cache.shad_to=this.$cache.cont.find(".shadow-to"),this.setTopHandler()),this.options.hide_from_to&&(this.$cache.from[0].style.display="none",this.$cache.to[0].style.display="none",this.$cache.single[0].style.display="none"),this.appendGrid(),this.options.disable?(this.appendDisableMask(),this.$cache.input[0].disabled=!0):(this.$cache.input[0].disabled=!1,this.removeDisableMask(),this.bindEvents()),this.options.disable||(this.options.block?this.appendDisableMask():this.removeDisableMask()),this.options.drag_interval&&(this.$cache.bar[0].style.cursor="ew-resize")},setTopHandler:function(){var t=this.options.min,i=this.options.max,s=this.options.from,o=this.options.to;t'),this.$cache.cont.addClass("irs-disabled")},removeDisableMask:function(){this.$cache.cont.remove(".irs-disable-mask"),this.$cache.cont.removeClass("irs-disabled")},remove:function(){this.$cache.cont.remove(),this.$cache.cont=null,this.$cache.line.off("keydown.irs_"+this.plugin_count),this.$cache.body.off("touchmove.irs_"+this.plugin_count),this.$cache.body.off("mousemove.irs_"+this.plugin_count),this.$cache.win.off("touchend.irs_"+this.plugin_count),this.$cache.win.off("mouseup.irs_"+this.plugin_count),e&&(this.$cache.body.off("mouseup.irs_"+this.plugin_count),this.$cache.body.off("mouseleave.irs_"+this.plugin_count)),this.$cache.grid_labels=[],this.coords.big=[],this.coords.big_w=[],this.coords.big_p=[],this.coords.big_x=[],cancelAnimationFrame(this.raf_id)},bindEvents:function(){this.no_diapason||(this.$cache.body.on("touchmove.irs_"+this.plugin_count,this.pointerMove.bind(this)),this.$cache.body.on("mousemove.irs_"+this.plugin_count,this.pointerMove.bind(this)),this.$cache.win.on("touchend.irs_"+this.plugin_count,this.pointerUp.bind(this)),this.$cache.win.on("mouseup.irs_"+this.plugin_count,this.pointerUp.bind(this)),this.$cache.line.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.line.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.line.on("focus.irs_"+this.plugin_count,this.pointerFocus.bind(this)),this.options.drag_interval&&"double"===this.options.type?(this.$cache.bar.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"both")),this.$cache.bar.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"both"))):(this.$cache.bar.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.bar.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click"))),"single"===this.options.type?(this.$cache.single.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.s_single.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.shad_single.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.single.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.s_single.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.edge.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.shad_single.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click"))):(this.$cache.single.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,null)),this.$cache.single.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,null)),this.$cache.from.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.s_from.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.to.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.s_to.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.shad_from.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.shad_to.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.from.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.s_from.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.to.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.s_to.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.shad_from.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.shad_to.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click"))),this.options.keyboard&&this.$cache.line.on("keydown.irs_"+this.plugin_count,this.key.bind(this,"keyboard")),e&&(this.$cache.body.on("mouseup.irs_"+this.plugin_count,this.pointerUp.bind(this)),this.$cache.body.on("mouseleave.irs_"+this.plugin_count,this.pointerUp.bind(this))))},pointerFocus:function(t){var i,s;this.target||(s=(i="single"===this.options.type?this.$cache.single:this.$cache.from).offset().left,s+=i.width()/2-1,this.pointerClick("single",{preventDefault:function(){},pageX:s}))},pointerMove:function(t){var i;this.dragging&&(i=t.pageX||t.originalEvent.touches&&t.originalEvent.touches[0].pageX,this.coords.x_pointer=i-this.coords.x_gap,this.calc())},pointerUp:function(t){this.current_plugin===this.plugin_count&&this.is_active&&(this.is_active=!1,this.$cache.cont.find(".state_hover").removeClass("state_hover"),this.force_redraw=!0,e&&a("*").prop("unselectable",!1),this.updateScene(),this.restoreOriginalMinInterval(),(a.contains(this.$cache.cont[0],t.target)||this.dragging)&&this.callOnFinish(),this.dragging=!1)},pointerDown:function(t,i){i.preventDefault(),i.stopPropagation();var s=i.pageX||i.originalEvent.touches&&i.originalEvent.touches[0].pageX;2!==i.button&&("both"===t&&this.setTempMinInterval(),t=t||(this.target||"from"),this.current_plugin=this.plugin_count,this.target=t,this.is_active=!0,this.dragging=!0,this.coords.x_gap=this.$cache.rs.offset().left,this.coords.x_pointer=s-this.coords.x_gap,this.calcPointerPercent(),this.changeLevel(t),e&&a("*").prop("unselectable",!0),this.$cache.line.trigger("focus"),this.updateScene())},pointerClick:function(t,i){i.preventDefault(),i.stopPropagation();var s=i.pageX||i.originalEvent.touches&&i.originalEvent.touches[0].pageX;2!==i.button&&(this.current_plugin=this.plugin_count,this.target=t,this.is_click=!0,this.coords.x_gap=this.$cache.rs.offset().left,this.coords.x_pointer=+(s-this.coords.x_gap).toFixed(),this.force_redraw=!0,this.calc(),this.$cache.line.trigger("focus"))},key:function(t,i){if(!(this.current_plugin!==this.plugin_count||i.altKey||i.ctrlKey||i.shiftKey||i.metaKey)){switch(i.which){case 83:case 65:case 40:case 37:i.preventDefault(),this.moveByKey(!1);break;case 87:case 68:case 38:case 39:i.preventDefault(),this.moveByKey(!0)}return!0}},moveByKey:function(t){var i=this.coords.p_pointer,s=(this.options.max-this.options.min)/100,s=this.options.step/s;t?i+=s:i-=s,this.coords.x_pointer=this.toFixed(this.coords.w_rs/100*i),this.is_key=!0,this.calc()},setMinMax:function(){if(this.options){if(this.options.hide_min_max)return this.$cache.min[0].style.display="none",void(this.$cache.max[0].style.display="none");var t,i;this.options.values.length?(this.$cache.min.html(this.decorate(this.options.p_values[this.options.min])),this.$cache.max.html(this.decorate(this.options.p_values[this.options.max]))):(t=this._prettify(this.options.min),i=this._prettify(this.options.max),this.result.min_pretty=t,this.result.max_pretty=i,this.$cache.min.html(this.decorate(t,this.options.min)),this.$cache.max.html(this.decorate(i,this.options.max))),this.labels.w_min=this.$cache.min.outerWidth(!1),this.labels.w_max=this.$cache.max.outerWidth(!1)}},setTempMinInterval:function(){var t=this.result.to-this.result.from;null===this.old_min_interval&&(this.old_min_interval=this.options.min_interval),this.options.min_interval=t},restoreOriginalMinInterval:function(){null!==this.old_min_interval&&(this.options.min_interval=this.old_min_interval,this.old_min_interval=null)},calc:function(t){if(this.options&&(this.calc_count++,10!==this.calc_count&&!t||(this.calc_count=0,this.coords.w_rs=this.$cache.rs.outerWidth(!1),this.calcHandlePercent()),this.coords.w_rs)){this.calcPointerPercent();var i=this.getHandleX();switch("both"===this.target&&(this.coords.p_gap=0,i=this.getHandleX()),"click"===this.target&&(this.coords.p_gap=this.coords.p_handle/2,i=this.getHandleX(),this.options.drag_interval?this.target="both_one":this.target=this.chooseHandle(i)),this.target){case"base":var s=(this.options.max-this.options.min)/100,o=(this.result.from-this.options.min)/s,e=(this.result.to-this.options.min)/s;this.coords.p_single_real=this.toFixed(o),this.coords.p_from_real=this.toFixed(o),this.coords.p_to_real=this.toFixed(e),this.coords.p_single_real=this.checkDiapason(this.coords.p_single_real,this.options.from_min,this.options.from_max),this.coords.p_from_real=this.checkDiapason(this.coords.p_from_real,this.options.from_min,this.options.from_max),this.coords.p_to_real=this.checkDiapason(this.coords.p_to_real,this.options.to_min,this.options.to_max),this.coords.p_single_fake=this.convertToFakePercent(this.coords.p_single_real),this.coords.p_from_fake=this.convertToFakePercent(this.coords.p_from_real),this.coords.p_to_fake=this.convertToFakePercent(this.coords.p_to_real),this.target=null;break;case"single":if(this.options.from_fixed)break;this.coords.p_single_real=this.convertToRealPercent(i),this.coords.p_single_real=this.calcWithStep(this.coords.p_single_real),this.coords.p_single_real=this.checkDiapason(this.coords.p_single_real,this.options.from_min,this.options.from_max),this.coords.p_single_fake=this.convertToFakePercent(this.coords.p_single_real);break;case"from":if(this.options.from_fixed)break;this.coords.p_from_real=this.convertToRealPercent(i),this.coords.p_from_real=this.calcWithStep(this.coords.p_from_real),this.coords.p_from_real>this.coords.p_to_real&&(this.coords.p_from_real=this.coords.p_to_real),this.coords.p_from_real=this.checkDiapason(this.coords.p_from_real,this.options.from_min,this.options.from_max),this.coords.p_from_real=this.checkMinInterval(this.coords.p_from_real,this.coords.p_to_real,"from"),this.coords.p_from_real=this.checkMaxInterval(this.coords.p_from_real,this.coords.p_to_real,"from"),this.coords.p_from_fake=this.convertToFakePercent(this.coords.p_from_real);break;case"to":if(this.options.to_fixed)break;this.coords.p_to_real=this.convertToRealPercent(i),this.coords.p_to_real=this.calcWithStep(this.coords.p_to_real),this.coords.p_to_realthis.coords.w_rs&&(this.coords.x_pointer=this.coords.w_rs),this.coords.p_pointer=this.toFixed(this.coords.x_pointer/this.coords.w_rs*100)):this.coords.p_pointer=0},convertToRealPercent:function(t){return t/(100-this.coords.p_handle)*100},convertToFakePercent:function(t){return t/100*(100-this.coords.p_handle)},getHandleX:function(){var t=100-this.coords.p_handle,i=this.toFixed(this.coords.p_pointer-this.coords.p_gap);return i<0?i=0:t100-this.labels.p_max-1?this.$cache.max[0].style.visibility="hidden":this.$cache.max[0].style.visibility="visible"):(e=t?(this.options.decorate_both?(s=this.decorate(i[this.result.from]),s+=this.options.values_separator,s+=this.decorate(i[this.result.to])):s=this.decorate(i[this.result.from]+this.options.values_separator+i[this.result.to]),o=this.decorate(i[this.result.from]),this.decorate(i[this.result.to])):(h=this._prettify(this.result.from),r=this._prettify(this.result.to),this.options.decorate_both?(s=this.decorate(h,this.result.from),s+=this.options.values_separator,s+=this.decorate(r,this.result.to)):s=this.decorate(h+this.options.values_separator+r,this.result.to),o=this.decorate(h,this.result.from),this.decorate(r,this.result.to)),this.$cache.single.html(s),this.$cache.from.html(o),this.$cache.to.html(e),this.calcLabels(),n=Math.min(this.labels.p_single_left,this.labels.p_from_left),a=this.labels.p_single_left+this.labels.p_single_fake,c=this.labels.p_to_left+this.labels.p_to_fake,l=Math.max(a,c),this.labels.p_from_left+this.labels.p_from_fake>=this.labels.p_to_left?(this.$cache.from[0].style.visibility="hidden",this.$cache.to[0].style.visibility="hidden",this.$cache.single[0].style.visibility="visible",l=this.result.from===this.result.to?("from"===this.target?this.$cache.from[0].style.visibility="visible":"to"===this.target?this.$cache.to[0].style.visibility="visible":this.target||(this.$cache.from[0].style.visibility="visible"),this.$cache.single[0].style.visibility="hidden",c):(this.$cache.from[0].style.visibility="hidden",this.$cache.to[0].style.visibility="hidden",this.$cache.single[0].style.visibility="visible",Math.max(a,c))):(this.$cache.from[0].style.visibility="visible",this.$cache.to[0].style.visibility="visible",this.$cache.single[0].style.visibility="hidden"),n100-this.labels.p_max-1?this.$cache.max[0].style.visibility="hidden":this.$cache.max[0].style.visibility="visible")))},drawShadow:function(){var t,i,s,o,e=this.options,h=this.$cache,r="number"==typeof e.from_min&&!isNaN(e.from_min),n="number"==typeof e.from_max&&!isNaN(e.from_max),a="number"==typeof e.to_min&&!isNaN(e.to_min),c="number"==typeof e.to_max&&!isNaN(e.to_max);"single"===e.type?e.from_shadow&&(r||n)?(t=this.convertToPercent(r?e.from_min:e.min),i=this.convertToPercent(n?e.from_max:e.max)-t,t=this.toFixed(t-this.coords.p_handle/100*t),i=this.toFixed(i-this.coords.p_handle/100*i),t+=this.coords.p_handle/2,h.shad_single[0].style.display="block",h.shad_single[0].style.left=t+"%",h.shad_single[0].style.width=i+"%"):h.shad_single[0].style.display="none":(e.from_shadow&&(r||n)?(t=this.convertToPercent(r?e.from_min:e.min),i=this.convertToPercent(n?e.from_max:e.max)-t,t=this.toFixed(t-this.coords.p_handle/100*t),i=this.toFixed(i-this.coords.p_handle/100*i),t+=this.coords.p_handle/2,h.shad_from[0].style.display="block",h.shad_from[0].style.left=t+"%",h.shad_from[0].style.width=i+"%"):h.shad_from[0].style.display="none",e.to_shadow&&(a||c)?(s=this.convertToPercent(a?e.to_min:e.min),o=this.convertToPercent(c?e.to_max:e.max)-s,s=this.toFixed(s-this.coords.p_handle/100*s),o=this.toFixed(o-this.coords.p_handle/100*o),s+=this.coords.p_handle/2,h.shad_to[0].style.display="block",h.shad_to[0].style.left=s+"%",h.shad_to[0].style.width=o+"%"):h.shad_to[0].style.display="none")},writeToInput:function(){"single"===this.options.type?(this.options.values.length?this.$cache.input.prop("value",this.result.from_value):this.$cache.input.prop("value",this.result.from),this.$cache.input.data("from",this.result.from)):(this.options.values.length?this.$cache.input.prop("value",this.result.from_value+this.options.input_values_separator+this.result.to_value):this.$cache.input.prop("value",this.result.from+this.options.input_values_separator+this.result.to),this.$cache.input.data("from",this.result.from),this.$cache.input.data("to",this.result.to))},callOnStart:function(){this.writeToInput(),this.options.onStart&&"function"==typeof this.options.onStart&&(this.options.scope?this.options.onStart.call(this.options.scope,this.result):this.options.onStart(this.result))},callOnChange:function(){this.writeToInput(),this.options.onChange&&"function"==typeof this.options.onChange&&(this.options.scope?this.options.onChange.call(this.options.scope,this.result):this.options.onChange(this.result))},callOnFinish:function(){this.writeToInput(),this.options.onFinish&&"function"==typeof this.options.onFinish&&(this.options.scope?this.options.onFinish.call(this.options.scope,this.result):this.options.onFinish(this.result))},callOnUpdate:function(){this.writeToInput(),this.options.onUpdate&&"function"==typeof this.options.onUpdate&&(this.options.scope?this.options.onUpdate.call(this.options.scope,this.result):this.options.onUpdate(this.result))},toggleInput:function(){if(this.$cache.input.toggleClass("irs-hidden-input"),this.has_tab_index)this.$cache.input.prop("tabindex",-1);else try{this.$cache.input.removeProp("tabindex")}catch(t){}this.has_tab_index=!this.has_tab_index},convertToPercent:function(t,i){var s,o=this.options.max-this.options.min,e=o/100;return o?(s=(i?t:t-this.options.min)/e,this.toFixed(s)):(this.no_diapason=!0,0)},convertToValue:function(t){var i,s,o=this.options.min,e=this.options.max,h=o.toString().split(".")[1],r=e.toString().split(".")[1],n=0,a=0;if(0===t)return this.options.min;if(100===t)return this.options.max;h&&(n=i=h.length),r&&(n=s=r.length),i&&s&&(n=s<=i?i:s),o<0&&(o=+(o+(a=Math.abs(o))).toFixed(n),e=+(e+a).toFixed(n));var c,l=(e-o)/100*t+o,_=this.options.step.toString().split(".")[1],l=_?+l.toFixed(_.length):(l/=this.options.step,+(l*=this.options.step).toFixed(0));return a&&(l-=a),(c=_?+l.toFixed(_.length):this.toFixed(l))this.options.max&&(c=this.options.max),c},calcWithStep:function(t){var i=Math.round(t/this.coords.p_step)*this.coords.p_step;return 100h.max_interval&&(o=e-h.max_interval):o-e>h.max_interval&&(o=e+h.max_interval),this.convertToPercent(o)):t},checkDiapason:function(t,i,s){var o=this.convertToValue(t),e=this.options;return"number"!=typeof i&&(i=e.min),"number"!=typeof s&&(s=e.max),os.max&&(s.from=s.max)):(s.froms.max&&(s.from=s.max),s.tos.max&&(s.to=s.max),this.update_check.from&&(this.update_check.from!==s.from&&s.from>s.to&&(s.from=s.to),this.update_check.to!==s.to&&s.tos.to&&(s.from=s.to),s.tos.from_max&&(s.from=s.from_max),"number"==typeof s.to_min&&s.tos.to_max&&(s.to=s.to_max),o&&(o.min!==s.min&&(o.min=s.min),o.max!==s.max&&(o.max=s.max),(o.fromo.max)&&(o.from=s.from),(o.too.max)&&(o.to=s.to)),("number"!=typeof s.min_interval||isNaN(s.min_interval)||!s.min_interval||s.min_interval<0)&&(s.min_interval=0),("number"!=typeof s.max_interval||isNaN(s.max_interval)||!s.max_interval||s.max_interval<0)&&(s.max_interval=0),s.min_interval&&s.min_interval>s.max-s.min&&(s.min_interval=s.max-s.min),s.max_interval&&s.max_interval>s.max-s.min&&(s.max_interval=s.max-s.min)},decorate:function(t,i){var s="",o=this.options;return o.prefix&&(s+=o.prefix),s+=t,o.max_postfix&&(o.values.length&&t===o.p_values[o.max]||i===o.max)&&(s+=o.max_postfix,o.postfix&&(s+=" ")),o.postfix&&(s+=o.postfix),s},updateFrom:function(){this.result.from=this.options.from,this.result.from_percent=this.convertToPercent(this.result.from),this.result.from_pretty=this._prettify(this.result.from),this.options.values&&(this.result.from_value=this.options.values[this.result.from])},updateTo:function(){this.result.to=this.options.to,this.result.to_percent=this.convertToPercent(this.result.to),this.result.to_pretty=this._prettify(this.result.to),this.options.values&&(this.result.to_value=this.options.values[this.result.to])},updateResult:function(){this.result.min=this.options.min,this.result.max=this.options.max,this.updateFrom(),this.updateTo()},appendGrid:function(){if(this.options.grid){var t,i,s,o,e,h=this.options,r=h.max-h.min,n=h.grid_num,a=0,c=0,l=4,_="";for(this.calcGridMargin(),a=h.grid_snap?50';_+='',e=this.convertToValue(c),_+=''+(e=h.values.length?h.p_values[e]:this._prettify(e))+""}this.coords.big_num=Math.ceil(n+1),this.$cache.cont.addClass("irs-with-grid"),this.$cache.grid.html(_),this.cacheGridLabels()}},cacheGridLabels:function(){for(var t,i=this.coords.big_num,s=0;s100+this.coords.grid_gap&&(s[o-1]=100+this.coords.grid_gap,i[o-1]=this.toFixed(s[o-1]-this.coords.big_p[o-1]),this.coords.big_x[o-1]=this.toFixed(this.coords.big_p[o-1]-this.coords.grid_gap))),this.calcGridCollision(2,i,s),this.calcGridCollision(4,i,s),e=0;e>>0;if(0==e)return-1;var h=+i||0;if(Math.abs(h)===1/0&&(h=0),e<=h)return-1;for(s=Math.max(0<=h?h:e-Math.abs(h),0);s!",h[0]),(o={type:h.data("type"),min:h.data("min"),max:h.data("max"),from:h.data("from"),to:h.data("to"),step:h.data("step"),min_interval:h.data("minInterval"),max_interval:h.data("maxInterval"),drag_interval:h.data("dragInterval"),values:h.data("values"),from_fixed:h.data("fromFixed"),from_min:h.data("fromMin"),from_max:h.data("fromMax"),from_shadow:h.data("fromShadow"),to_fixed:h.data("toFixed"),to_min:h.data("toMin"),to_max:h.data("toMax"),to_shadow:h.data("toShadow"),prettify_enabled:h.data("prettifyEnabled"),prettify_separator:h.data("prettifySeparator"),force_edges:h.data("forceEdges"),keyboard:h.data("keyboard"),grid:h.data("grid"),grid_margin:h.data("gridMargin"),grid_num:h.data("gridNum"),grid_snap:h.data("gridSnap"),hide_min_max:h.data("hideMinMax"),hide_from_to:h.data("hideFromTo"),prefix:h.data("prefix"),postfix:h.data("postfix"),max_postfix:h.data("maxPostfix"),decorate_both:h.data("decorateBoth"),values_separator:h.data("valuesSeparator"),input_values_separator:h.data("inputValuesSeparator"),disable:h.data("disable"),block:h.data("block"),extra_classes:h.data("extraClasses")}).values=o.values&&o.values.split(","),o)o.hasOwnProperty(e)&&(o[e]!==_&&""!==o[e]||delete o[e]);r!==_&&""!==r&&((r=r.split(o.input_values_separator||i.input_values_separator||";"))[0]&&r[0]==+r[0]&&(r[0]=+r[0]),r[1]&&r[1]==+r[1]&&(r[1]=+r[1]),i&&i.values&&i.values.length?(n.from=r[0]&&i.values.indexOf(r[0]),n.to=r[1]&&i.values.indexOf(r[1])):(n.from=r[0]&&+r[0],n.to=r[1]&&+r[1])),a.extend(n,i),a.extend(n,o),this.options=n,this.update_check={},this.validate(),this.result={input:this.$cache.input,slider:null,min:this.options.min,max:this.options.max,from:this.options.from,from_percent:0,from_value:null,to:this.options.to,to_percent:0,to_value:null},this.init()}h.prototype={init:function(t){this.no_diapason=!1,this.coords.p_step=this.convertToPercent(this.options.step,!0),this.target="base",this.toggleInput(),this.append(),this.setMinMax(),t?(this.force_redraw=!0,this.calc(!0),this.callOnUpdate()):(this.force_redraw=!0,this.calc(!0),this.callOnStart()),this.updateScene()},append:function(){var t='';this.$cache.input.before(t),this.$cache.input.prop("readonly",!0),this.$cache.cont=this.$cache.input.prev(),this.result.slider=this.$cache.cont,this.$cache.cont.html('01000'),this.$cache.rs=this.$cache.cont.find(".irs"),this.$cache.min=this.$cache.cont.find(".irs-min"),this.$cache.max=this.$cache.cont.find(".irs-max"),this.$cache.from=this.$cache.cont.find(".irs-from"),this.$cache.to=this.$cache.cont.find(".irs-to"),this.$cache.single=this.$cache.cont.find(".irs-single"),this.$cache.bar=this.$cache.cont.find(".irs-bar"),this.$cache.line=this.$cache.cont.find(".irs-line"),this.$cache.grid=this.$cache.cont.find(".irs-grid"),"single"===this.options.type?(this.$cache.cont.append(''),this.$cache.edge=this.$cache.cont.find(".irs-bar-edge"),this.$cache.s_single=this.$cache.cont.find(".single"),this.$cache.from[0].style.visibility="hidden",this.$cache.to[0].style.visibility="hidden",this.$cache.shad_single=this.$cache.cont.find(".shadow-single")):(this.$cache.cont.append(''),this.$cache.s_from=this.$cache.cont.find(".from"),this.$cache.s_to=this.$cache.cont.find(".to"),this.$cache.shad_from=this.$cache.cont.find(".shadow-from"),this.$cache.shad_to=this.$cache.cont.find(".shadow-to"),this.setTopHandler()),this.options.hide_from_to&&(this.$cache.from[0].style.display="none",this.$cache.to[0].style.display="none",this.$cache.single[0].style.display="none"),this.appendGrid(),this.options.disable?(this.appendDisableMask(),this.$cache.input[0].disabled=!0):(this.$cache.input[0].disabled=!1,this.removeDisableMask(),this.bindEvents()),this.options.disable||(this.options.block?this.appendDisableMask():this.removeDisableMask()),this.options.drag_interval&&(this.$cache.bar[0].style.cursor="ew-resize")},setTopHandler:function(){var t=this.options.min,i=this.options.max,s=this.options.from,o=this.options.to;t'),this.$cache.cont.addClass("irs-disabled")},removeDisableMask:function(){this.$cache.cont.remove(".irs-disable-mask"),this.$cache.cont.removeClass("irs-disabled")},remove:function(){this.$cache.cont.remove(),this.$cache.cont=null,this.$cache.line.off("keydown.irs_"+this.plugin_count),this.$cache.body.off("touchmove.irs_"+this.plugin_count),this.$cache.body.off("mousemove.irs_"+this.plugin_count),this.$cache.win.off("touchend.irs_"+this.plugin_count),this.$cache.win.off("mouseup.irs_"+this.plugin_count),e&&(this.$cache.body.off("mouseup.irs_"+this.plugin_count),this.$cache.body.off("mouseleave.irs_"+this.plugin_count)),this.$cache.grid_labels=[],this.coords.big=[],this.coords.big_w=[],this.coords.big_p=[],this.coords.big_x=[],cancelAnimationFrame(this.raf_id)},bindEvents:function(){this.no_diapason||(this.$cache.body.on("touchmove.irs_"+this.plugin_count,this.pointerMove.bind(this)),this.$cache.body.on("mousemove.irs_"+this.plugin_count,this.pointerMove.bind(this)),this.$cache.win.on("touchend.irs_"+this.plugin_count,this.pointerUp.bind(this)),this.$cache.win.on("mouseup.irs_"+this.plugin_count,this.pointerUp.bind(this)),this.$cache.line.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.line.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.line.on("focus.irs_"+this.plugin_count,this.pointerFocus.bind(this)),this.options.drag_interval&&"double"===this.options.type?(this.$cache.bar.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"both")),this.$cache.bar.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"both"))):(this.$cache.bar.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.bar.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click"))),"single"===this.options.type?(this.$cache.single.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.s_single.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.shad_single.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.single.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.s_single.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"single")),this.$cache.edge.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.shad_single.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click"))):(this.$cache.single.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,null)),this.$cache.single.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,null)),this.$cache.from.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.s_from.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.to.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.s_to.on("touchstart.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.shad_from.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.shad_to.on("touchstart.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.from.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.s_from.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"from")),this.$cache.to.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.s_to.on("mousedown.irs_"+this.plugin_count,this.pointerDown.bind(this,"to")),this.$cache.shad_from.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click")),this.$cache.shad_to.on("mousedown.irs_"+this.plugin_count,this.pointerClick.bind(this,"click"))),this.options.keyboard&&this.$cache.line.on("keydown.irs_"+this.plugin_count,this.key.bind(this,"keyboard")),e&&(this.$cache.body.on("mouseup.irs_"+this.plugin_count,this.pointerUp.bind(this)),this.$cache.body.on("mouseleave.irs_"+this.plugin_count,this.pointerUp.bind(this))))},pointerFocus:function(t){var i,s;this.target||(s=(i="single"===this.options.type?this.$cache.single:this.$cache.from).offset().left,s+=i.width()/2-1,this.pointerClick("single",{preventDefault:function(){},pageX:s}))},pointerMove:function(t){var i;this.dragging&&(i=t.pageX||t.originalEvent.touches&&t.originalEvent.touches[0].pageX,this.coords.x_pointer=i-this.coords.x_gap,this.calc())},pointerUp:function(t){this.current_plugin===this.plugin_count&&this.is_active&&(this.is_active=!1,this.$cache.cont.find(".state_hover").removeClass("state_hover"),this.force_redraw=!0,e&&a("*").prop("unselectable",!1),this.updateScene(),this.restoreOriginalMinInterval(),(a.contains(this.$cache.cont[0],t.target)||this.dragging)&&this.callOnFinish(),this.dragging=!1)},pointerDown:function(t,i){i.preventDefault(),i.stopPropagation&&i.stopPropagation();var s=i.pageX||i.originalEvent.touches&&i.originalEvent.touches[0].pageX;2!==i.button&&("both"===t&&this.setTempMinInterval(),t=t||(this.target||"from"),this.current_plugin=this.plugin_count,this.target=t,this.is_active=!0,this.dragging=!0,this.coords.x_gap=this.$cache.rs.offset().left,this.coords.x_pointer=s-this.coords.x_gap,this.calcPointerPercent(),this.changeLevel(t),e&&a("*").prop("unselectable",!0),this.$cache.line.trigger("focus"),this.updateScene())},pointerClick:function(t,i){i.preventDefault(),i.stopPropagation&&i.stopPropagation();var s=i.pageX||i.originalEvent.touches&&i.originalEvent.touches[0].pageX;2!==i.button&&(this.current_plugin=this.plugin_count,this.target=t,this.is_click=!0,this.coords.x_gap=this.$cache.rs.offset().left,this.coords.x_pointer=+(s-this.coords.x_gap).toFixed(),this.force_redraw=!0,this.calc(),this.$cache.line.trigger("focus"))},key:function(t,i){if(!(this.current_plugin!==this.plugin_count||i.altKey||i.ctrlKey||i.shiftKey||i.metaKey)){switch(i.which){case 83:case 65:case 40:case 37:i.preventDefault(),this.moveByKey(!1);break;case 87:case 68:case 38:case 39:i.preventDefault(),this.moveByKey(!0)}return!0}},moveByKey:function(t){var i=this.coords.p_pointer,s=(this.options.max-this.options.min)/100,s=this.options.step/s;t?i+=s:i-=s,this.coords.x_pointer=this.toFixed(this.coords.w_rs/100*i),this.is_key=!0,this.calc()},setMinMax:function(){if(this.options){if(this.options.hide_min_max)return this.$cache.min[0].style.display="none",void(this.$cache.max[0].style.display="none");var t,i;this.options.values.length?(this.$cache.min.html(this.decorate(this.options.p_values[this.options.min])),this.$cache.max.html(this.decorate(this.options.p_values[this.options.max]))):(t=this._prettify(this.options.min),i=this._prettify(this.options.max),this.result.min_pretty=t,this.result.max_pretty=i,this.$cache.min.html(this.decorate(t,this.options.min)),this.$cache.max.html(this.decorate(i,this.options.max))),this.labels.w_min=this.$cache.min.outerWidth(!1),this.labels.w_max=this.$cache.max.outerWidth(!1)}},setTempMinInterval:function(){var t=this.result.to-this.result.from;null===this.old_min_interval&&(this.old_min_interval=this.options.min_interval),this.options.min_interval=t},restoreOriginalMinInterval:function(){null!==this.old_min_interval&&(this.options.min_interval=this.old_min_interval,this.old_min_interval=null)},calc:function(t){if(this.options&&(this.calc_count++,10!==this.calc_count&&!t||(this.calc_count=0,this.coords.w_rs=this.$cache.rs.outerWidth(!1),this.calcHandlePercent()),this.coords.w_rs)){this.calcPointerPercent();var i=this.getHandleX();switch("both"===this.target&&(this.coords.p_gap=0,i=this.getHandleX()),"click"===this.target&&(this.coords.p_gap=this.coords.p_handle/2,i=this.getHandleX(),this.options.drag_interval?this.target="both_one":this.target=this.chooseHandle(i)),this.target){case"base":var s=(this.options.max-this.options.min)/100,o=(this.result.from-this.options.min)/s,e=(this.result.to-this.options.min)/s;this.coords.p_single_real=this.toFixed(o),this.coords.p_from_real=this.toFixed(o),this.coords.p_to_real=this.toFixed(e),this.coords.p_single_real=this.checkDiapason(this.coords.p_single_real,this.options.from_min,this.options.from_max),this.coords.p_from_real=this.checkDiapason(this.coords.p_from_real,this.options.from_min,this.options.from_max),this.coords.p_to_real=this.checkDiapason(this.coords.p_to_real,this.options.to_min,this.options.to_max),this.coords.p_single_fake=this.convertToFakePercent(this.coords.p_single_real),this.coords.p_from_fake=this.convertToFakePercent(this.coords.p_from_real),this.coords.p_to_fake=this.convertToFakePercent(this.coords.p_to_real),this.target=null;break;case"single":if(this.options.from_fixed)break;this.coords.p_single_real=this.convertToRealPercent(i),this.coords.p_single_real=this.calcWithStep(this.coords.p_single_real),this.coords.p_single_real=this.checkDiapason(this.coords.p_single_real,this.options.from_min,this.options.from_max),this.coords.p_single_fake=this.convertToFakePercent(this.coords.p_single_real);break;case"from":if(this.options.from_fixed)break;this.coords.p_from_real=this.convertToRealPercent(i),this.coords.p_from_real=this.calcWithStep(this.coords.p_from_real),this.coords.p_from_real>this.coords.p_to_real&&(this.coords.p_from_real=this.coords.p_to_real),this.coords.p_from_real=this.checkDiapason(this.coords.p_from_real,this.options.from_min,this.options.from_max),this.coords.p_from_real=this.checkMinInterval(this.coords.p_from_real,this.coords.p_to_real,"from"),this.coords.p_from_real=this.checkMaxInterval(this.coords.p_from_real,this.coords.p_to_real,"from"),this.coords.p_from_fake=this.convertToFakePercent(this.coords.p_from_real);break;case"to":if(this.options.to_fixed)break;this.coords.p_to_real=this.convertToRealPercent(i),this.coords.p_to_real=this.calcWithStep(this.coords.p_to_real),this.coords.p_to_realthis.coords.w_rs&&(this.coords.x_pointer=this.coords.w_rs),this.coords.p_pointer=this.toFixed(this.coords.x_pointer/this.coords.w_rs*100)):this.coords.p_pointer=0},convertToRealPercent:function(t){return t/(100-this.coords.p_handle)*100},convertToFakePercent:function(t){return t/100*(100-this.coords.p_handle)},getHandleX:function(){var t=100-this.coords.p_handle,i=this.toFixed(this.coords.p_pointer-this.coords.p_gap);return i<0?i=0:t100-this.labels.p_max-1?this.$cache.max[0].style.visibility="hidden":this.$cache.max[0].style.visibility="visible"):(e=t?(this.options.decorate_both?(s=this.decorate(i[this.result.from]),s+=this.options.values_separator,s+=this.decorate(i[this.result.to])):s=this.decorate(i[this.result.from]+this.options.values_separator+i[this.result.to]),o=this.decorate(i[this.result.from]),this.decorate(i[this.result.to])):(h=this._prettify(this.result.from),r=this._prettify(this.result.to),this.options.decorate_both?(s=this.decorate(h,this.result.from),s+=this.options.values_separator,s+=this.decorate(r,this.result.to)):s=this.decorate(h+this.options.values_separator+r,this.result.to),o=this.decorate(h,this.result.from),this.decorate(r,this.result.to)),this.$cache.single.html(s),this.$cache.from.html(o),this.$cache.to.html(e),this.calcLabels(),n=Math.min(this.labels.p_single_left,this.labels.p_from_left),a=this.labels.p_single_left+this.labels.p_single_fake,c=this.labels.p_to_left+this.labels.p_to_fake,l=Math.max(a,c),this.labels.p_from_left+this.labels.p_from_fake>=this.labels.p_to_left?(this.$cache.from[0].style.visibility="hidden",this.$cache.to[0].style.visibility="hidden",this.$cache.single[0].style.visibility="visible",l=this.result.from===this.result.to?("from"===this.target?this.$cache.from[0].style.visibility="visible":"to"===this.target?this.$cache.to[0].style.visibility="visible":this.target||(this.$cache.from[0].style.visibility="visible"),this.$cache.single[0].style.visibility="hidden",c):(this.$cache.from[0].style.visibility="hidden",this.$cache.to[0].style.visibility="hidden",this.$cache.single[0].style.visibility="visible",Math.max(a,c))):(this.$cache.from[0].style.visibility="visible",this.$cache.to[0].style.visibility="visible",this.$cache.single[0].style.visibility="hidden"),n100-this.labels.p_max-1?this.$cache.max[0].style.visibility="hidden":this.$cache.max[0].style.visibility="visible")))},drawShadow:function(){var t,i,s,o,e=this.options,h=this.$cache,r="number"==typeof e.from_min&&!isNaN(e.from_min),n="number"==typeof e.from_max&&!isNaN(e.from_max),a="number"==typeof e.to_min&&!isNaN(e.to_min),c="number"==typeof e.to_max&&!isNaN(e.to_max);"single"===e.type?e.from_shadow&&(r||n)?(t=this.convertToPercent(r?e.from_min:e.min),i=this.convertToPercent(n?e.from_max:e.max)-t,t=this.toFixed(t-this.coords.p_handle/100*t),i=this.toFixed(i-this.coords.p_handle/100*i),t+=this.coords.p_handle/2,h.shad_single[0].style.display="block",h.shad_single[0].style.left=t+"%",h.shad_single[0].style.width=i+"%"):h.shad_single[0].style.display="none":(e.from_shadow&&(r||n)?(t=this.convertToPercent(r?e.from_min:e.min),i=this.convertToPercent(n?e.from_max:e.max)-t,t=this.toFixed(t-this.coords.p_handle/100*t),i=this.toFixed(i-this.coords.p_handle/100*i),t+=this.coords.p_handle/2,h.shad_from[0].style.display="block",h.shad_from[0].style.left=t+"%",h.shad_from[0].style.width=i+"%"):h.shad_from[0].style.display="none",e.to_shadow&&(a||c)?(s=this.convertToPercent(a?e.to_min:e.min),o=this.convertToPercent(c?e.to_max:e.max)-s,s=this.toFixed(s-this.coords.p_handle/100*s),o=this.toFixed(o-this.coords.p_handle/100*o),s+=this.coords.p_handle/2,h.shad_to[0].style.display="block",h.shad_to[0].style.left=s+"%",h.shad_to[0].style.width=o+"%"):h.shad_to[0].style.display="none")},writeToInput:function(){"single"===this.options.type?(this.options.values.length?this.$cache.input.prop("value",this.result.from_value):this.$cache.input.prop("value",this.result.from),this.$cache.input.data("from",this.result.from)):(this.options.values.length?this.$cache.input.prop("value",this.result.from_value+this.options.input_values_separator+this.result.to_value):this.$cache.input.prop("value",this.result.from+this.options.input_values_separator+this.result.to),this.$cache.input.data("from",this.result.from),this.$cache.input.data("to",this.result.to))},callOnStart:function(){this.writeToInput(),this.options.onStart&&"function"==typeof this.options.onStart&&(this.options.scope?this.options.onStart.call(this.options.scope,this.result):this.options.onStart(this.result))},callOnChange:function(){this.writeToInput(),this.options.onChange&&"function"==typeof this.options.onChange&&(this.options.scope?this.options.onChange.call(this.options.scope,this.result):this.options.onChange(this.result))},callOnFinish:function(){this.writeToInput(),this.options.onFinish&&"function"==typeof this.options.onFinish&&(this.options.scope?this.options.onFinish.call(this.options.scope,this.result):this.options.onFinish(this.result))},callOnUpdate:function(){this.writeToInput(),this.options.onUpdate&&"function"==typeof this.options.onUpdate&&(this.options.scope?this.options.onUpdate.call(this.options.scope,this.result):this.options.onUpdate(this.result))},toggleInput:function(){if(this.$cache.input.toggleClass("irs-hidden-input"),this.has_tab_index)this.$cache.input.prop("tabindex",-1);else try{this.$cache.input.removeProp("tabindex")}catch(t){}this.has_tab_index=!this.has_tab_index},convertToPercent:function(t,i){var s,o=this.options.max-this.options.min,e=o/100;return o?(s=(i?t:t-this.options.min)/e,this.toFixed(s)):(this.no_diapason=!0,0)},convertToValue:function(t){var i,s,o=this.options.min,e=this.options.max,h=o.toString().split(".")[1],r=e.toString().split(".")[1],n=0,a=0;if(0===t)return this.options.min;if(100===t)return this.options.max;h&&(n=i=h.length),r&&(n=s=r.length),i&&s&&(n=s<=i?i:s),o<0&&(o=+(o+(a=Math.abs(o))).toFixed(n),e=+(e+a).toFixed(n));var c,l=(e-o)/100*t+o,_=this.options.step.toString().split(".")[1],l=_?+l.toFixed(_.length):(l/=this.options.step,+(l*=this.options.step).toFixed(0));return a&&(l-=a),(c=_?+l.toFixed(_.length):this.toFixed(l))this.options.max&&(c=this.options.max),c},calcWithStep:function(t){var i=Math.round(t/this.coords.p_step)*this.coords.p_step;return 100h.max_interval&&(o=e-h.max_interval):o-e>h.max_interval&&(o=e+h.max_interval),this.convertToPercent(o)):t},checkDiapason:function(t,i,s){var o=this.convertToValue(t),e=this.options;return"number"!=typeof i&&(i=e.min),"number"!=typeof s&&(s=e.max),os.max&&(s.from=s.max)):(s.froms.max&&(s.from=s.max),s.tos.max&&(s.to=s.max),this.update_check.from&&(this.update_check.from!==s.from&&s.from>s.to&&(s.from=s.to),this.update_check.to!==s.to&&s.tos.to&&(s.from=s.to),s.tos.from_max&&(s.from=s.from_max),"number"==typeof s.to_min&&s.tos.to_max&&(s.to=s.to_max),o&&(o.min!==s.min&&(o.min=s.min),o.max!==s.max&&(o.max=s.max),(o.fromo.max)&&(o.from=s.from),(o.too.max)&&(o.to=s.to)),("number"!=typeof s.min_interval||isNaN(s.min_interval)||!s.min_interval||s.min_interval<0)&&(s.min_interval=0),("number"!=typeof s.max_interval||isNaN(s.max_interval)||!s.max_interval||s.max_interval<0)&&(s.max_interval=0),s.min_interval&&s.min_interval>s.max-s.min&&(s.min_interval=s.max-s.min),s.max_interval&&s.max_interval>s.max-s.min&&(s.max_interval=s.max-s.min)},decorate:function(t,i){var s="",o=this.options;return o.prefix&&(s+=o.prefix),s+=t,o.max_postfix&&(o.values.length&&t===o.p_values[o.max]||i===o.max)&&(s+=o.max_postfix,o.postfix&&(s+=" ")),o.postfix&&(s+=o.postfix),s},updateFrom:function(){this.result.from=this.options.from,this.result.from_percent=this.convertToPercent(this.result.from),this.result.from_pretty=this._prettify(this.result.from),this.options.values&&(this.result.from_value=this.options.values[this.result.from])},updateTo:function(){this.result.to=this.options.to,this.result.to_percent=this.convertToPercent(this.result.to),this.result.to_pretty=this._prettify(this.result.to),this.options.values&&(this.result.to_value=this.options.values[this.result.to])},updateResult:function(){this.result.min=this.options.min,this.result.max=this.options.max,this.updateFrom(),this.updateTo()},appendGrid:function(){if(this.options.grid){var t,i,s,o,e,h=this.options,r=h.max-h.min,n=h.grid_num,a=0,c=0,l=4,_="";for(this.calcGridMargin(),a=h.grid_snap?50';_+='',e=this.convertToValue(c),_+=''+(e=h.values.length?h.p_values[e]:this._prettify(e))+""}this.coords.big_num=Math.ceil(n+1),this.$cache.cont.addClass("irs-with-grid"),this.$cache.grid.html(_),this.cacheGridLabels()}},cacheGridLabels:function(){for(var t,i=this.coords.big_num,s=0;s100+this.coords.grid_gap&&(s[o-1]=100+this.coords.grid_gap,i[o-1]=this.toFixed(s[o-1]-this.coords.big_p[o-1]),this.coords.big_x[o-1]=this.toFixed(this.coords.big_p[o-1]-this.coords.grid_gap))),this.calcGridCollision(2,i,s),this.calcGridCollision(4,i,s),e=0;e\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n \"/\": \"/\"\n };\n\n return str.replace(/[&<>'\"\\/]/g, function(m) {\n return escaped[m];\n });\n}\n\nfunction randomId() {\n return Math.floor(0x100000000 + (Math.random() * 0xF00000000)).toString(16);\n}\n\nfunction strToBool(str) {\n if (!str || !str.toLowerCase)\n return undefined;\n\n switch(str.toLowerCase()) {\n case 'true':\n return true;\n case 'false':\n return false;\n default:\n return undefined;\n }\n}\n\n// A wrapper for getComputedStyle that is compatible with older browsers.\n// This is significantly faster than jQuery's .css() function.\nfunction getStyle(el, styleProp) {\n var x;\n if (el.currentStyle)\n x = el.currentStyle[styleProp];\n else if (window.getComputedStyle) {\n // getComputedStyle can return null when we're inside a hidden iframe on\n // Firefox; don't attempt to retrieve style props in this case.\n // https://bugzilla.mozilla.org/show_bug.cgi?id=548397\n var style = document.defaultView.getComputedStyle(el, null);\n if (style)\n x = style.getPropertyValue(styleProp);\n }\n return x;\n}\n\n// Convert a number to a string with leading zeros\nfunction padZeros(n, digits) {\n var str = n.toString();\n while (str.length < digits)\n str = \"0\" + str;\n return str;\n}\n\n// Round to a specified number of significant digits.\nfunction roundSignif(x, digits = 1) {\n if (digits < 1)\n throw \"Significant digits must be at least 1.\";\n\n // This converts to a string and back to a number, which is inelegant, but\n // is less prone to FP rounding error than an alternate method which used\n // Math.round().\n return parseFloat(x.toPrecision(digits));\n}\n\n// Take a string with format \"YYYY-MM-DD\" and return a Date object.\n// IE8 and QTWebKit don't support YYYY-MM-DD, but they support YYYY/MM/DD\nfunction parseDate(dateString) {\n var date = new Date(dateString);\n if (isNaN(date))\n date = new Date(dateString.replace(/-/g, \"/\"));\n return date;\n}\n\n// Given a Date object, return a string in yyyy-mm-dd format, using the\n// UTC date. This may be a day off from the date in the local time zone.\nfunction formatDateUTC(date) {\n if (date instanceof Date) {\n return date.getUTCFullYear() + '-' +\n padZeros(date.getUTCMonth()+1, 2) + '-' +\n padZeros(date.getUTCDate(), 2);\n\n } else {\n return null;\n }\n}\n\n\n// Given an element and a function(width, height), returns a function(). When\n// the output function is called, it calls the input function with the offset\n// width and height of the input element--but only if the size of the element\n// is non-zero and the size is different than the last time the output\n// function was called.\n//\n// Basically we are trying to filter out extraneous calls to func, so that\n// when the window size changes or whatever, we don't run resize logic for\n// elements that haven't actually changed size or aren't visible anyway.\nfunction makeResizeFilter(el, func) {\n var lastSize = {};\n return function() {\n var size = { w: el.offsetWidth, h: el.offsetHeight };\n if (size.w === 0 && size.h === 0)\n return;\n if (size.w === lastSize.w && size.h === lastSize.h)\n return;\n lastSize = size;\n func(size.w, size.h);\n };\n}\n\nvar _BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||\n window.MozBlobBuilder || window.MSBlobBuilder;\n\nfunction makeBlob(parts) {\n\n // Browser compatibility is a mess right now. The code as written works in\n // a variety of modern browsers, but sadly gives a deprecation warning\n // message on the console in current versions (as of this writing) of\n // Chrome.\n\n // Safari 6.0 (8536.25) on Mac OS X 10.8.1:\n // Has Blob constructor but it doesn't work with ArrayBufferView args\n\n // Google Chrome 21.0.1180.81 on Xubuntu 12.04:\n // Has Blob constructor, accepts ArrayBufferView args, accepts ArrayBuffer\n // but with a deprecation warning message\n\n // Firefox 15.0 on Xubuntu 12.04:\n // Has Blob constructor, accepts both ArrayBuffer and ArrayBufferView args\n\n // Chromium 18.0.1025.168 (Developer Build 134367 Linux) on Xubuntu 12.04:\n // No Blob constructor. Has WebKitBlobBuilder.\n\n try {\n return new Blob(parts);\n }\n catch (e) {\n var blobBuilder = new _BlobBuilder();\n $.each(parts, function(i, part) {\n blobBuilder.append(part);\n });\n return blobBuilder.getBlob();\n }\n}\n\nfunction pixelRatio() {\n if (window.devicePixelRatio) {\n return window.devicePixelRatio;\n } else {\n return 1;\n }\n}\n\n// Takes a string expression and returns a function that takes an argument.\n//\n// When the function is executed, it will evaluate that expression using\n// \"with\" on the argument value, and return the result.\nfunction scopeExprToFunc(expr) {\n /*jshint evil: true */\n var expr_escaped = expr\n .replace(/[\\\\\"']/g, '\\\\$&')\n .replace(/\\u0000/g, '\\\\0')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n // \\b has a special meaning; need [\\b] to match backspace char.\n .replace(/[\\b]/g, '\\\\b');\n\n try {\n var func = new Function(\n `with (this) {\n try {\n return (${expr});\n } catch (e) {\n console.error('Error evaluating expression: ${expr_escaped}');\n throw e;\n }\n }`\n );\n } catch (e) {\n console.error(\"Error parsing expression: \" + expr);\n throw e;\n }\n\n\n return function(scope) {\n return func.call(scope);\n };\n}\n\nfunction asArray(value) {\n if (value === null || value === undefined)\n return [];\n if ($.isArray(value))\n return value;\n return [value];\n}\n\n// We need a stable sorting algorithm for ordering\n// bindings by priority and insertion order.\nfunction mergeSort(list, sortfunc) {\n function merge(sortfunc, a, b) {\n var ia = 0;\n var ib = 0;\n var sorted = [];\n while (ia < a.length && ib < b.length) {\n if (sortfunc(a[ia], b[ib]) <= 0) {\n sorted.push(a[ia++]);\n }\n else {\n sorted.push(b[ib++]);\n }\n }\n while (ia < a.length)\n sorted.push(a[ia++]);\n while (ib < b.length)\n sorted.push(b[ib++]);\n return sorted;\n }\n\n // Don't mutate list argument\n list = list.slice(0);\n\n for (var chunkSize = 1; chunkSize < list.length; chunkSize *= 2) {\n for (var i = 0; i < list.length; i += chunkSize * 2) {\n var listA = list.slice(i, i + chunkSize);\n var listB = list.slice(i + chunkSize, i + chunkSize * 2);\n var merged = merge(sortfunc, listA, listB);\n var args = [i, merged.length];\n Array.prototype.push.apply(args, merged);\n Array.prototype.splice.apply(list, args);\n }\n }\n\n return list;\n}\n\n// Escape jQuery selector metacharacters: !\"#$%&'()*+,./:;<=>?@[\\]^`{|}~\nvar $escape = exports.$escape = function(val) {\n return val.replace(/([!\"#$%&'()*+,.\\/:;<=>?@\\[\\\\\\]^`{|}~])/g, '\\\\$1');\n};\n\n// Maps a function over an object, preserving keys. Like the mapValues\n// function from lodash.\nfunction mapValues(obj, f) {\n const newObj = {};\n for (let key in obj) {\n if (obj.hasOwnProperty(key))\n newObj[key] = f(obj[key], key, obj);\n }\n return newObj;\n}\n\n// This is does the same as Number.isNaN, but that function unfortunately does\n// not exist in any version of IE.\nfunction isnan(x) {\n return typeof(x) === 'number' && isNaN(x);\n}\n\n// Binary equality function used by the equal function.\nfunction _equal(x, y) {\n if ($.type(x) === \"object\" && $.type(y) === \"object\") {\n if (Object.keys(x).length !== Object.keys(y).length) return false;\n for (let prop in x)\n if (!y.hasOwnProperty(prop) || !_equal(x[prop], y[prop]))\n return false;\n return true;\n } else if ($.type(x) === \"array\" && $.type(y) === \"array\") {\n if (x.length !== y.length) return false;\n for (let i = 0; i < x.length; i++)\n if (!_equal(x[i], y[i])) return false;\n return true;\n } else {\n return (x === y);\n }\n}\n\n// Structural or \"deep\" equality predicate. Tests two or more arguments for\n// equality, traversing arrays and objects (as determined by $.type) as\n// necessary.\n//\n// Objects other than objects and arrays are tested for equality using ===.\nfunction equal(...args) {\n if (args.length < 2) throw new Error(\"equal requires at least two arguments.\");\n for (let i = 0; i < args.length-1; i++) {\n if (!_equal(args[i], args[i+1]))\n return false;\n }\n return true;\n};\n\n// Compare version strings like \"1.0.1\", \"1.4-2\". `op` must be a string like\n// \"==\" or \"<\".\nexports.compareVersion = function(a, op, b) {\n function versionParts(ver) {\n return (ver + \"\")\n .replace(/-/, \".\")\n .replace(/(\\.0)+[^\\.]*$/, \"\")\n .split(\".\");\n }\n\n function cmpVersion(a, b) {\n a = versionParts(a);\n b = versionParts(b);\n var len = Math.min(a.length, b.length);\n var cmp;\n\n for(var i=0; i=\") return (diff >= 0);\n else if (op === \">\") return (diff > 0);\n else if (op === \"<=\") return (diff <= 0);\n else if (op === \"<\") return (diff < 0);\n else throw `Unknown operator: ${op}`;\n};\n\n\nfunction updateLabel(labelTxt, labelNode) {\n // Only update if label was specified in the update method\n if (typeof labelTxt === \"undefined\") return;\n if (labelNode.length !== 1) {\n throw new Error(\"labelNode must be of length 1\");\n }\n\n // Should the label be empty?\n var emptyLabel = $.isArray(labelTxt) && labelTxt.length === 0;\n\n if (emptyLabel) {\n labelNode.addClass(\"shiny-label-null\");\n } else {\n labelNode.text(labelTxt);\n labelNode.removeClass(\"shiny-label-null\");\n }\n\n}\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/browser.js\n\nvar browser = (function() {\n\n var isQt = false;\n // For easy handling of Qt quirks using CSS\n if (/\\bQt\\//.test(window.navigator.userAgent)) {\n $(document.documentElement).addClass('qt');\n isQt = true;\n }\n\n // Enable special treatment for Qt 5 quirks on Linux\n if (/\\bQt\\/5/.test(window.navigator.userAgent) &&\n /Linux/.test(window.navigator.userAgent)) {\n $(document.documentElement).addClass('qt5');\n }\n\n // Detect IE information\n var isIE = (navigator.appName === 'Microsoft Internet Explorer');\n\n function getIEVersion() {\n var rv = -1;\n if (isIE) {\n var ua = navigator.userAgent;\n var re = new RegExp(\"MSIE ([0-9]{1,}[\\\\.0-9]{0,})\");\n if (re.exec(ua) !== null)\n rv = parseFloat(RegExp.$1);\n }\n return rv;\n }\n\n return {\n isQt: isQt,\n isIE: isIE,\n IEVersion: getIEVersion()\n };\n\n})();\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/input_rate.js\n\nvar Invoker = function(target, func) {\n this.target = target;\n this.func = func;\n};\n\n(function() {\n this.normalCall =\n this.immediateCall = function() {\n this.func.apply(this.target, arguments);\n };\n}).call(Invoker.prototype);\n\nvar Debouncer = function(target, func, delayMs) {\n this.target = target;\n this.func = func;\n this.delayMs = delayMs;\n\n this.timerId = null;\n this.args = null;\n};\n\n(function() {\n this.normalCall = function() {\n var self = this;\n\n this.$clearTimer();\n this.args = arguments;\n\n this.timerId = setTimeout(function() {\n // IE8 doesn't reliably clear timeout, so this additional\n // check is needed\n if (self.timerId === null)\n return;\n self.$clearTimer();\n self.$invoke();\n }, this.delayMs);\n };\n this.immediateCall = function() {\n this.$clearTimer();\n this.args = arguments;\n this.$invoke();\n };\n this.isPending = function() {\n return this.timerId !== null;\n };\n this.$clearTimer = function() {\n if (this.timerId !== null) {\n clearTimeout(this.timerId);\n this.timerId = null;\n }\n };\n this.$invoke = function() {\n this.func.apply(this.target, this.args);\n this.args = null;\n };\n}).call(Debouncer.prototype);\n\nvar Throttler = function(target, func, delayMs) {\n this.target = target;\n this.func = func;\n this.delayMs = delayMs;\n\n this.timerId = null;\n this.args = null;\n};\n\n(function() {\n this.normalCall = function() {\n var self = this;\n\n this.args = arguments;\n if (this.timerId === null) {\n this.$invoke();\n this.timerId = setTimeout(function() {\n // IE8 doesn't reliably clear timeout, so this additional\n // check is needed\n if (self.timerId === null)\n return;\n self.$clearTimer();\n if (self.args)\n self.normalCall.apply(self, self.args);\n }, this.delayMs);\n }\n };\n this.immediateCall = function() {\n this.$clearTimer();\n this.args = arguments;\n this.$invoke();\n };\n this.isPending = function() {\n return this.timerId !== null;\n };\n this.$clearTimer = function() {\n if (this.timerId !== null) {\n clearTimeout(this.timerId);\n this.timerId = null;\n }\n };\n this.$invoke = function() {\n this.func.apply(this.target, this.args);\n this.args = null;\n };\n}).call(Throttler.prototype);\n\n// Returns a debounced version of the given function.\n// Debouncing means that when the function is invoked,\n// there is a delay of `threshold` milliseconds before\n// it is actually executed, and if the function is\n// invoked again before that threshold has elapsed then\n// the clock starts over.\n//\n// For example, if a function is debounced with a\n// threshold of 1000ms, then calling it 17 times at\n// 900ms intervals will result in a single execution\n// of the underlying function, 1000ms after the 17th\n// call.\nfunction debounce(threshold, func) {\n var timerId = null;\n var self, args;\n return function() {\n self = this;\n args = arguments;\n if (timerId !== null) {\n clearTimeout(timerId);\n timerId = null;\n }\n timerId = setTimeout(function() {\n // IE8 doesn't reliably clear timeout, so this additional\n // check is needed\n if (timerId === null)\n return;\n timerId = null;\n func.apply(self, args);\n }, threshold);\n };\n}\n\n// Returns a throttled version of the given function.\n// Throttling means that the underlying function will\n// be executed no more than once every `threshold`\n// milliseconds.\n//\n// For example, if a function is throttled with a\n// threshold of 1000ms, then calling it 17 times at\n// 900ms intervals will result in something like 15\n// or 16 executions of the underlying function.\n// eslint-disable-next-line no-unused-vars\nfunction throttle(threshold, func) {\n var executionPending = false;\n var timerId = null;\n var self, args;\n\n function throttled() {\n self = null;\n args = null;\n if (timerId === null) {\n // Haven't seen a call recently. Execute now and\n // start a timer to buffer any subsequent calls.\n timerId = setTimeout(function() {\n // When time expires, clear the timer; and if\n // there has been a call in the meantime, repeat.\n timerId = null;\n if (executionPending) {\n executionPending = false;\n throttled.apply(self, args);\n }\n }, threshold);\n func.apply(this, arguments);\n }\n else {\n // Something executed recently. Don't do anything\n // except set up target/arguments to be called later\n executionPending = true;\n self = this;\n args = arguments;\n }\n }\n return throttled;\n}\n\n\n// Schedules data to be sent to shinyapp at the next setTimeout(0).\n// Batches multiple input calls into one websocket message.\nvar InputBatchSender = function(shinyapp) {\n this.shinyapp = shinyapp;\n this.timerId = null;\n this.pendingData = {};\n this.reentrant = false;\n this.lastChanceCallback = [];\n};\n(function() {\n this.setInput = function(nameType, value, opts) {\n this.pendingData[nameType] = value;\n\n if (!this.reentrant) {\n if (opts.priority === \"event\") {\n this.$sendNow();\n } else if (!this.timerId) {\n this.timerId = setTimeout(this.$sendNow.bind(this), 0);\n }\n }\n };\n\n this.$sendNow = function() {\n if (this.reentrant) {\n console.trace(\"Unexpected reentrancy in InputBatchSender!\");\n }\n\n this.reentrant = true;\n try {\n this.timerId = null;\n $.each(this.lastChanceCallback, (i, callback) => {\n callback();\n });\n var currentData = this.pendingData;\n this.pendingData = {};\n this.shinyapp.sendInput(currentData);\n } finally {\n this.reentrant = false;\n }\n };\n}).call(InputBatchSender.prototype);\n\n\nvar InputNoResendDecorator = function(target, initialValues) {\n this.target = target;\n this.lastSentValues = this.reset(initialValues);\n};\n(function() {\n this.setInput = function(nameType, value, opts) {\n const { name: inputName, inputType: inputType } = splitInputNameType(nameType);\n const jsonValue = JSON.stringify(value);\n\n if (opts.priority !== \"event\" &&\n this.lastSentValues[inputName] &&\n this.lastSentValues[inputName].jsonValue === jsonValue &&\n this.lastSentValues[inputName].inputType === inputType) {\n return;\n }\n this.lastSentValues[inputName] = { jsonValue, inputType };\n this.target.setInput(nameType, value, opts);\n };\n this.reset = function(values = {}) {\n // Given an object with flat name-value format:\n // { x: \"abc\", \"y.shiny.number\": 123 }\n // Create an object in cache format and save it:\n // { x: { jsonValue: '\"abc\"', inputType: \"\" },\n // y: { jsonValue: \"123\", inputType: \"shiny.number\" } }\n const cacheValues = {};\n\n for (let inputName in values) {\n if (values.hasOwnProperty(inputName)) {\n let { name, inputType } = splitInputNameType(inputName);\n cacheValues[name] = {\n jsonValue: JSON.stringify(values[inputName]),\n inputType: inputType\n };\n }\n }\n\n this.lastSentValues = cacheValues;\n };\n}).call(InputNoResendDecorator.prototype);\n\n\nvar InputEventDecorator = function(target) {\n this.target = target;\n};\n(function() {\n this.setInput = function(nameType, value, opts) {\n var evt = jQuery.Event(\"shiny:inputchanged\");\n\n const input = splitInputNameType(nameType);\n evt.name = input.name;\n evt.inputType = input.inputType;\n evt.value = value;\n evt.binding = opts.binding;\n evt.el = opts.el;\n evt.priority = opts.priority;\n\n $(opts.el).trigger(evt);\n\n if (!evt.isDefaultPrevented()) {\n let name = evt.name;\n if (evt.inputType !== '') name += ':' + evt.inputType;\n\n // Most opts aren't passed along to lower levels in the input decorator\n // stack.\n this.target.setInput(name, evt.value, { priority: opts.priority });\n }\n };\n}).call(InputEventDecorator.prototype);\n\n\nvar InputRateDecorator = function(target) {\n this.target = target;\n this.inputRatePolicies = {};\n};\n(function() {\n // Note that the first argument of setInput() and setRatePolicy()\n // are passed both the input name (i.e., inputId) and type.\n // https://github.com/rstudio/shiny/blob/67d3a/srcjs/init_shiny.js#L111-L126\n // However, $ensureInit() and $doSetInput() are meant to be passed just\n // the input name (i.e., inputId), which is why we distinguish between\n // nameType and name.\n this.setInput = function(nameType, value, opts) {\n const {name: inputName} = splitInputNameType(nameType);\n\n this.$ensureInit(inputName);\n\n if (opts.priority !== \"deferred\")\n this.inputRatePolicies[inputName].immediateCall(nameType, value, opts);\n else\n this.inputRatePolicies[inputName].normalCall(nameType, value, opts);\n };\n this.setRatePolicy = function(nameType, mode, millis) {\n const {name: inputName} = splitInputNameType(nameType);\n\n if (mode === 'direct') {\n this.inputRatePolicies[inputName] = new Invoker(this, this.$doSetInput);\n }\n else if (mode === 'debounce') {\n this.inputRatePolicies[inputName] = new Debouncer(this, this.$doSetInput, millis);\n }\n else if (mode === 'throttle') {\n this.inputRatePolicies[inputName] = new Throttler(this, this.$doSetInput, millis);\n }\n };\n this.$ensureInit = function(name) {\n if (!(name in this.inputRatePolicies))\n this.setRatePolicy(name, 'direct');\n };\n this.$doSetInput = function(nameType, value, opts) {\n this.target.setInput(nameType, value, opts);\n };\n}).call(InputRateDecorator.prototype);\n\n\nvar InputDeferDecorator = function(target) {\n this.target = target;\n this.pendingInput = {};\n};\n(function() {\n this.setInput = function(nameType, value, opts) {\n if (/^\\./.test(nameType))\n this.target.setInput(nameType, value, opts);\n else\n this.pendingInput[nameType] = { value, opts };\n };\n this.submit = function() {\n for (var nameType in this.pendingInput) {\n if (this.pendingInput.hasOwnProperty(nameType)) {\n let { value, opts } = this.pendingInput[nameType];\n this.target.setInput(nameType, value, opts);\n }\n }\n };\n}).call(InputDeferDecorator.prototype);\n\n\nconst InputValidateDecorator = function(target) {\n this.target = target;\n};\n(function() {\n this.setInput = function(nameType, value, opts) {\n if (!nameType)\n throw \"Can't set input with empty name.\";\n\n opts = addDefaultInputOpts(opts);\n\n this.target.setInput(nameType, value, opts);\n };\n}).call(InputValidateDecorator.prototype);\n\n\n// Merge opts with defaults, and return a new object.\nfunction addDefaultInputOpts(opts) {\n\n opts = $.extend({\n priority: \"immediate\",\n binding: null,\n el: null\n }, opts);\n\n if (opts && typeof(opts.priority) !== \"undefined\") {\n switch (opts.priority) {\n case \"deferred\":\n case \"immediate\":\n case \"event\":\n break;\n default:\n throw new Error(\"Unexpected input value mode: '\" + opts.priority + \"'\");\n }\n }\n\n return opts;\n}\n\n\nfunction splitInputNameType(nameType) {\n const name2 = nameType.split(':');\n return {\n name: name2[0],\n inputType: name2.length > 1 ? name2[1] : ''\n };\n}\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/shinyapp.js\n\nvar ShinyApp = function() {\n this.$socket = null;\n\n // Cached input values\n this.$inputValues = {};\n\n // Input values at initialization (and reconnect)\n this.$initialInput = {};\n\n // Output bindings\n this.$bindings = {};\n\n // Cached values/errors\n this.$values = {};\n this.$errors = {};\n\n // Conditional bindings (show/hide element based on expression)\n this.$conditionals = {};\n\n this.$pendingMessages = [];\n this.$activeRequests = {};\n this.$nextRequestId = 0;\n\n this.$allowReconnect = false;\n};\n\n(function() {\n\n this.connect = function(initialInput) {\n if (this.$socket)\n throw \"Connect was already called on this application object\";\n\n this.$socket = this.createSocket();\n this.$initialInput = initialInput;\n $.extend(this.$inputValues, initialInput);\n\n this.$updateConditionals();\n };\n\n this.isConnected = function() {\n return !!this.$socket;\n };\n\n var scheduledReconnect = null;\n this.reconnect = function() {\n // This function can be invoked directly even if there's a scheduled\n // reconnect, so be sure to clear any such scheduled reconnects.\n clearTimeout(scheduledReconnect);\n\n if (this.isConnected())\n throw \"Attempted to reconnect, but already connected.\";\n\n this.$socket = this.createSocket();\n this.$initialInput = $.extend({}, this.$inputValues);\n this.$updateConditionals();\n };\n\n this.createSocket = function () {\n var self = this;\n\n var createSocketFunc = exports.createSocket || function() {\n var protocol = 'ws:';\n if (window.location.protocol === 'https:')\n protocol = 'wss:';\n\n var defaultPath = window.location.pathname;\n // some older WebKit browsers return the pathname already decoded;\n // if we find invalid URL characters in the path, encode them\n if (!/^([$#!&-;=?-[\\]_a-z~]|%[0-9a-fA-F]{2})+$/.test(defaultPath)) {\n defaultPath = encodeURI(defaultPath);\n // Bizarrely, QtWebKit requires us to encode these characters *twice*\n if (browser.isQt) {\n defaultPath = encodeURI(defaultPath);\n }\n }\n if (!/\\/$/.test(defaultPath))\n defaultPath += '/';\n defaultPath += 'websocket/';\n\n var ws = new WebSocket(protocol + '//' + window.location.host + defaultPath);\n ws.binaryType = 'arraybuffer';\n\n return ws;\n };\n\n var socket = createSocketFunc();\n var hasOpened = false;\n socket.onopen = function() {\n hasOpened = true;\n\n $(document).trigger({\n type: 'shiny:connected',\n socket: socket\n });\n\n self.onConnected();\n\n socket.send(JSON.stringify({\n method: 'init',\n data: self.$initialInput\n }));\n\n while (self.$pendingMessages.length) {\n var msg = self.$pendingMessages.shift();\n socket.send(msg);\n }\n };\n socket.onmessage = function(e) {\n self.dispatchMessage(e.data);\n };\n // Called when a successfully-opened websocket is closed, or when an\n // attempt to open a connection fails.\n socket.onclose = function() {\n // These things are needed only if we've successfully opened the\n // websocket.\n if (hasOpened) {\n $(document).trigger({\n type: 'shiny:disconnected',\n socket: socket\n });\n\n self.$notifyDisconnected();\n }\n\n self.onDisconnected(); // Must be run before self.$removeSocket()\n self.$removeSocket();\n };\n return socket;\n };\n\n this.sendInput = function(values) {\n var msg = JSON.stringify({\n method: 'update',\n data: values\n });\n\n this.$sendMsg(msg);\n\n $.extend(this.$inputValues, values);\n this.$updateConditionals();\n };\n\n this.$notifyDisconnected = function() {\n if (window.parent) {\n window.parent.postMessage(\"disconnected\", \"*\");\n }\n };\n\n this.$removeSocket = function() {\n this.$socket = null;\n };\n\n this.$scheduleReconnect = function(delay) {\n var self = this;\n scheduledReconnect = setTimeout(function() { self.reconnect(); }, delay);\n };\n\n // How long should we wait before trying the next reconnection?\n // The delay will increase with subsequent attempts.\n // .next: Return the time to wait for next connection, and increment counter.\n // .reset: Reset the attempt counter.\n var reconnectDelay = (function() {\n var attempts = 0;\n // Time to wait before each reconnection attempt. If we go through all of\n // these values, repeated use the last one. Add 500ms to each one so that\n // in the last 0.5s, it shows \"...\"\n var delays = [1500, 1500, 2500, 2500, 5500, 5500, 10500];\n\n return {\n next: function() {\n var i = attempts;\n // Instead of going off the end, use the last one\n if (i >= delays.length) {\n i = delays.length - 1;\n }\n\n attempts++;\n return delays[i];\n },\n reset: function() {\n attempts = 0;\n }\n };\n })();\n\n this.onDisconnected = function() {\n // Add gray-out overlay, if not already present\n var $overlay = $('#shiny-disconnected-overlay');\n if ($overlay.length === 0) {\n $(document.body).append('
');\n }\n\n // To try a reconnect, both the app (this.$allowReconnect) and the\n // server (this.$socket.allowReconnect) must allow reconnections, or\n // session$allowReconnect(\"force\") was called. The \"force\" option should\n // only be used for testing.\n if ((this.$allowReconnect === true && this.$socket.allowReconnect === true) ||\n this.$allowReconnect === \"force\")\n {\n var delay = reconnectDelay.next();\n exports.showReconnectDialog(delay);\n this.$scheduleReconnect(delay);\n }\n };\n\n this.onConnected = function() {\n $('#shiny-disconnected-overlay').remove();\n exports.hideReconnectDialog();\n reconnectDelay.reset();\n };\n\n // NB: Including blobs will cause IE to break!\n // TODO: Make blobs work with Internet Explorer\n //\n // Websocket messages are normally one-way--i.e. the client passes a\n // message to the server but there is no way for the server to provide\n // a response to that specific message. makeRequest provides a way to\n // do asynchronous RPC over websocket. Each request has a method name\n // and arguments, plus optionally one or more binary blobs can be\n // included as well. The request is tagged with a unique number that\n // the server will use to label the corresponding response.\n //\n // @param method A string that tells the server what logic to run.\n // @param args An array of objects that should also be passed to the\n // server in JSON-ified form.\n // @param onSuccess A function that will be called back if the server\n // responds with success. If the server provides a value in the\n // response, the function will be called with it as the only argument.\n // @param onError A function that will be called back if the server\n // responds with error, or if the request fails for any other reason.\n // The parameter to onError will be a string describing the error.\n // @param blobs Optionally, an array of Blob, ArrayBuffer, or string\n // objects that will be made available to the server as part of the\n // request. Strings will be encoded using UTF-8.\n this.makeRequest = function(method, args, onSuccess, onError, blobs) {\n var requestId = this.$nextRequestId;\n while (this.$activeRequests[requestId]) {\n requestId = (requestId + 1) % 1000000000;\n }\n this.$nextRequestId = requestId + 1;\n\n this.$activeRequests[requestId] = {\n onSuccess: onSuccess,\n onError: onError\n };\n\n var msg = JSON.stringify({\n method: method,\n args: args,\n tag: requestId\n });\n\n if (blobs) {\n // We have binary data to transfer; form a different kind of packet.\n // Start with a 4-byte signature, then for each blob, emit 4 bytes for\n // the length followed by the blob. The json payload is UTF-8 encoded\n // and used as the first blob.\n\n var uint32_to_buf = function(val) {\n var buffer = new ArrayBuffer(4);\n var view = new DataView(buffer);\n view.setUint32(0, val, true); // little-endian\n return buffer;\n };\n\n var payload = [];\n payload.push(uint32_to_buf(0x01020202)); // signature\n\n var jsonBuf = makeBlob([msg]);\n payload.push(uint32_to_buf(jsonBuf.size));\n payload.push(jsonBuf);\n\n for (var i = 0; i < blobs.length; i++) {\n payload.push(uint32_to_buf(blobs[i].byteLength || blobs[i].size || 0));\n payload.push(blobs[i]);\n }\n\n msg = makeBlob(payload);\n }\n\n this.$sendMsg(msg);\n };\n\n this.$sendMsg = function(msg) {\n if (!this.$socket.readyState) {\n this.$pendingMessages.push(msg);\n }\n else {\n this.$socket.send(msg);\n }\n };\n\n this.receiveError = function(name, error) {\n if (this.$errors[name] === error)\n return;\n\n this.$errors[name] = error;\n delete this.$values[name];\n\n var binding = this.$bindings[name];\n var evt = jQuery.Event('shiny:error');\n evt.name = name;\n evt.error = error;\n evt.binding = binding;\n $(binding ? binding.el : document).trigger(evt);\n if (!evt.isDefaultPrevented() && binding && binding.onValueError) {\n binding.onValueError(evt.error);\n }\n };\n\n this.receiveOutput = function(name, value) {\n var binding = this.$bindings[name];\n var evt = jQuery.Event('shiny:value');\n evt.name = name;\n evt.value = value;\n evt.binding = binding;\n\n if (this.$values[name] === value) {\n $(binding ? binding.el : document).trigger(evt);\n return undefined;\n }\n\n this.$values[name] = value;\n delete this.$errors[name];\n\n $(binding ? binding.el : document).trigger(evt);\n\n if (!evt.isDefaultPrevented() && binding) {\n binding.onValueChange(evt.value);\n }\n\n return value;\n };\n\n this.bindOutput = function(id, binding) {\n if (!id)\n throw \"Can't bind an element with no ID\";\n if (this.$bindings[id])\n throw \"Duplicate binding for ID \" + id;\n this.$bindings[id] = binding;\n\n if (this.$values[id] !== undefined)\n binding.onValueChange(this.$values[id]);\n else if (this.$errors[id] !== undefined)\n binding.onValueError(this.$errors[id]);\n\n return binding;\n };\n\n this.unbindOutput = function(id, binding) {\n if (this.$bindings[id] === binding) {\n delete this.$bindings[id];\n return true;\n }\n else {\n return false;\n }\n };\n\n\n // Narrows a scopeComponent -- an input or output object -- to one constrained\n // by nsPrefix. Returns a new object with keys removed and renamed as\n // necessary.\n function narrowScopeComponent(scopeComponent, nsPrefix) {\n return Object.keys(scopeComponent)\n .filter(k => k.indexOf(nsPrefix) === 0)\n .map(k => ({[k.substring(nsPrefix.length)]: scopeComponent[k]}))\n .reduce((obj, pair) => $.extend(obj, pair),\n {});\n }\n\n // Narrows a scope -- an object with input and output \"subComponents\" -- to\n // one constrained by the nsPrefix string.\n //\n // If nsPrefix is null or empty, returns scope without modification.\n //\n // Otherwise, returns a new object with keys in subComponents removed and\n // renamed as necessary.\n function narrowScope(scope, nsPrefix) {\n return nsPrefix ? {\n input: narrowScopeComponent(scope.input, nsPrefix),\n output: narrowScopeComponent(scope.output, nsPrefix)\n } : scope;\n }\n\n this.$updateConditionals = function() {\n $(document).trigger({\n type: 'shiny:conditional'\n });\n\n var inputs = {};\n\n // Input keys use \"name:type\" format; we don't want the user to\n // have to know about the type suffix when referring to inputs.\n for (var name in this.$inputValues) {\n if (this.$inputValues.hasOwnProperty(name)) {\n var shortName = name.replace(/:.*/, '');\n inputs[shortName] = this.$inputValues[name];\n }\n }\n\n var scope = {input: inputs, output: this.$values};\n\n var conditionals = $(document).find('[data-display-if]');\n for (var i = 0; i < conditionals.length; i++) {\n var el = $(conditionals[i]);\n var condFunc = el.data('data-display-if-func');\n\n if (!condFunc) {\n var condExpr = el.attr('data-display-if');\n condFunc = scopeExprToFunc(condExpr);\n el.data('data-display-if-func', condFunc);\n }\n\n var nsPrefix = el.attr('data-ns-prefix');\n var nsScope = narrowScope(scope, nsPrefix);\n var show = condFunc(nsScope);\n var showing = el.css(\"display\") !== \"none\";\n if (show !== showing) {\n if (show) {\n el.trigger('show');\n el.show();\n el.trigger('shown');\n }\n else {\n el.trigger('hide');\n el.hide();\n el.trigger('hidden');\n }\n }\n }\n };\n\n // Message handler management functions =================================\n\n // Records insertion order of handlers. Maps number to name. This is so\n // we can dispatch messages to handlers in the order that handlers were\n // added.\n var messageHandlerOrder = [];\n // Keep track of handlers by name. Maps name to handler function.\n var messageHandlers = {};\n\n // Two categories of message handlers: those that are from Shiny, and those\n // that are added by the user. The Shiny ones handle messages in\n // msgObj.values, msgObj.errors, and so on. The user ones handle messages\n // in msgObj.custom.foo and msgObj.custom.bar.\n var customMessageHandlerOrder = [];\n var customMessageHandlers = {};\n\n // Adds Shiny (internal) message handler\n function addMessageHandler(type, handler) {\n if (messageHandlers[type]) {\n throw('handler for message of type \"' + type + '\" already added.');\n }\n if (typeof(handler) !== 'function') {\n throw('handler must be a function.');\n }\n if (handler.length !== 1) {\n throw('handler must be a function that takes one argument.');\n }\n messageHandlerOrder.push(type);\n messageHandlers[type] = handler;\n }\n\n // Adds custom message handler - this one is exposed to the user\n function addCustomMessageHandler(type, handler) {\n // Remove any previously defined handlers so that only the most recent one\n // will be called\n if (customMessageHandlers[type]) {\n var typeIdx = customMessageHandlerOrder.indexOf(type);\n if (typeIdx !== -1) {\n customMessageHandlerOrder.splice(typeIdx, 1);\n delete customMessageHandlers[type];\n }\n }\n if (typeof(handler) !== 'function') {\n throw('handler must be a function.');\n }\n if (handler.length !== 1) {\n throw('handler must be a function that takes one argument.');\n }\n\n customMessageHandlerOrder.push(type);\n customMessageHandlers[type] = handler;\n }\n\n exports.addCustomMessageHandler = addCustomMessageHandler;\n\n this.dispatchMessage = function(data) {\n var msgObj = {};\n if(typeof data === \"string\") {\n msgObj = JSON.parse(data);\n } else { // data is arraybuffer\n var len = new DataView(data,0,1).getUint8(0);\n var typedv = new DataView(data,1,len);\n var typebuf = [];\n for(var i=0; i 0) {\n var el = $obj[0];\n var evt = jQuery.Event('shiny:updateinput');\n evt.message = message[i].message;\n evt.binding = inputBinding;\n $(el).trigger(evt);\n if (!evt.isDefaultPrevented())\n inputBinding.receiveMessage(el, evt.message);\n }\n }\n });\n\n addMessageHandler('javascript', function(message) {\n /*jshint evil: true */\n eval(message);\n });\n\n addMessageHandler('console', function(message) {\n for (var i = 0; i < message.length; i++) {\n if (console.log)\n console.log(message[i]);\n }\n });\n\n addMessageHandler('progress', function(message) {\n if (message.type && message.message) {\n var handler = progressHandlers[message.type];\n if (handler)\n handler.call(this, message.message);\n }\n });\n\n addMessageHandler('notification', function(message) {\n if (message.type === 'show')\n exports.notifications.show(message.message);\n else if (message.type === 'remove')\n exports.notifications.remove(message.message);\n else\n throw('Unkown notification type: ' + message.type);\n });\n\n addMessageHandler('modal', function(message) {\n if (message.type === 'show')\n exports.modal.show(message.message);\n else if (message.type === 'remove')\n exports.modal.remove(); // For 'remove', message content isn't used\n else\n throw('Unkown modal type: ' + message.type);\n });\n\n addMessageHandler('response', function(message) {\n var requestId = message.tag;\n var request = this.$activeRequests[requestId];\n if (request) {\n delete this.$activeRequests[requestId];\n if ('value' in message)\n request.onSuccess(message.value);\n else\n request.onError(message.error);\n }\n });\n\n addMessageHandler('allowReconnect', function(message) {\n if (message === true || message === false || message === \"force\") {\n this.$allowReconnect = message;\n } else {\n throw \"Invalid value for allowReconnect: \" + message;\n }\n });\n\n addMessageHandler('custom', function(message) {\n // For old-style custom messages - should deprecate and migrate to new\n // method\n if (exports.oncustommessage) {\n exports.oncustommessage(message);\n }\n\n // Send messages.foo and messages.bar to appropriate handlers\n this._sendMessagesToHandlers(message, customMessageHandlers,\n customMessageHandlerOrder);\n });\n\n addMessageHandler('config', function(message) {\n this.config = {workerId: message.workerId, sessionId: message.sessionId};\n if (message.user) exports.user = message.user;\n $(document).trigger('shiny:sessioninitialized');\n });\n\n addMessageHandler('busy', function(message) {\n if (message === 'busy') {\n $(document.documentElement).addClass('shiny-busy');\n $(document).trigger('shiny:busy');\n } else if (message === 'idle') {\n $(document.documentElement).removeClass('shiny-busy');\n $(document).trigger('shiny:idle');\n }\n });\n\n addMessageHandler('recalculating', function(message) {\n if (message.hasOwnProperty('name') && message.hasOwnProperty('status')) {\n var binding = this.$bindings[message.name];\n $(binding ? binding.el : null).trigger({\n type: 'shiny:' + message.status\n });\n }\n });\n\n addMessageHandler('reload', function(message) {\n window.location.reload();\n });\n\n addMessageHandler('shiny-insert-ui', function(message) {\n var targets = $(message.selector);\n if (targets.length === 0) {\n // render the HTML and deps to a null target, so\n // the side-effect of rendering the deps, singletons,\n // and still occur\n console.warn('The selector you chose (\"' + message.selector +\n '\") could not be found in the DOM.');\n exports.renderHtml(message.content.html, $([]), message.content.deps);\n } else {\n targets.each(function (i, target) {\n exports.renderContent(target, message.content, message.where);\n return message.multiple;\n });\n }\n });\n\n addMessageHandler('shiny-remove-ui', function(message) {\n var els = $(message.selector);\n els.each(function (i, el) {\n exports.unbindAll(el, true);\n $(el).remove();\n // If `multiple` is false, returning false terminates the function\n // and no other elements are removed; if `multiple` is true,\n // returning true continues removing all remaining elements.\n return message.multiple;\n });\n });\n\n function getTabset(id) {\n var $tabset = $(\"#\" + $escape(id));\n if ($tabset.length === 0)\n throw \"There is no tabsetPanel (or navbarPage or navlistPanel) \" +\n \"with id equal to '\" + id + \"'\";\n return $tabset;\n }\n\n function getTabContent($tabset) {\n var tabsetId = $tabset.attr(\"data-tabsetid\");\n var $tabContent = $(\"div.tab-content[data-tabsetid='\" +\n $escape(tabsetId) + \"']\");\n return $tabContent;\n }\n\n function getTargetTabs($tabset, $tabContent, target) {\n var dataValue = \"[data-value='\" + $escape(target) + \"']\";\n var $aTag = $tabset.find(\"a\" + dataValue);\n var $liTag = $aTag.parent();\n if ($liTag.length === 0) {\n throw \"There is no tabPanel (or navbarMenu) with value\" +\n \" (or menuName) equal to '\" + target + \"'\";\n }\n var $liTags = [];\n var $divTags = [];\n\n if ($aTag.attr(\"data-toggle\") === \"dropdown\") {\n // dropdown\n var $dropdownTabset = $aTag.find(\"+ ul.dropdown-menu\");\n var dropdownId = $dropdownTabset.attr(\"data-tabsetid\");\n\n var $dropdownLiTags = $dropdownTabset.find(\"a[data-toggle='tab']\").parent(\"li\");\n $dropdownLiTags.each(function (i, el) {\n $liTags.push($(el));\n });\n var selector = \"div.tab-pane[id^='tab-\" + $escape(dropdownId) + \"']\";\n var $dropdownDivs = $tabContent.find(selector);\n $dropdownDivs.each(function (i, el) {\n $divTags.push($(el));\n });\n\n }\n else {\n // regular tab\n $divTags.push($tabContent.find(\"div\" + dataValue));\n }\n return { $liTag: $liTag, $liTags: $liTags, $divTags: $divTags };\n }\n\n addMessageHandler(\"shiny-insert-tab\", function(message) {\n var $parentTabset = getTabset(message.inputId);\n var $tabset = $parentTabset;\n var $tabContent = getTabContent($tabset);\n var tabsetId = $parentTabset.attr(\"data-tabsetid\");\n\n var $divTag = $(message.divTag.html);\n var $liTag = $(message.liTag.html);\n var $aTag = $liTag.find(\"> a\");\n\n // Unless the item is being prepended/appended, the target tab\n // must be provided\n var target = null;\n var $targetLiTag = null;\n if (message.target !== null) {\n target = getTargetTabs($tabset, $tabContent, message.target);\n $targetLiTag = target.$liTag;\n }\n\n // If the item is to be placed inside a navbarMenu (dropdown),\n // change the value of $tabset from the parent's ul tag to the\n // dropdown's ul tag\n var dropdown = getDropdown();\n if (dropdown !== null) {\n if ($aTag.attr(\"data-toggle\") === \"dropdown\")\n throw \"Cannot insert a navbarMenu inside another one\";\n $tabset = dropdown.$tabset;\n tabsetId = dropdown.id;\n }\n\n // For regular tab items, fix the href (of the li > a tag)\n // and the id (of the div tag). This does not apply to plain\n // text items (which function as dividers and headers inside\n // navbarMenus) and whole navbarMenus (since those get\n // constructed from scratch on the R side and therefore\n // there are no ids that need matching)\n if ($aTag.attr(\"data-toggle\") === \"tab\") {\n var index = getTabIndex($tabset, tabsetId);\n var tabId = \"tab-\" + tabsetId + \"-\" + index;\n $liTag.find(\"> a\").attr(\"href\", \"#\" + tabId);\n $divTag.attr(\"id\", tabId);\n }\n\n // actually insert the item into the right place\n if (message.position === \"before\") {\n if ($targetLiTag) {\n $targetLiTag.before($liTag);\n } else {\n $tabset.append($liTag);\n }\n } else if (message.position === \"after\") {\n if ($targetLiTag) {\n $targetLiTag.after($liTag);\n } else {\n $tabset.prepend($liTag);\n }\n }\n\n exports.renderContent($liTag[0], {html: $liTag.html(), deps: message.liTag.deps});\n // jcheng 2017-07-28: This next part might look a little insane versus the\n // more obvious `$tabContent.append($divTag);`, but there's a method to the\n // madness.\n //\n // 1) We need to load the dependencies, and this needs to happen before\n // any scripts in $divTag get a chance to run.\n // 2) The scripts in $divTag need to run only once.\n // 3) The contents of $divTag need to be sent through renderContent so that\n // singletons may be registered and/or obeyed, and so that inputs/outputs\n // may be bound.\n //\n // Add to these constraints these facts:\n //\n // A) The (non-jQuery) DOM manipulation functions don't cause scripts to\n // run, but the jQuery functions all do.\n // B) renderContent must be called on an element that's attached to the\n // document.\n // C) $divTag may be of length > 1 (e.g. navbarMenu). I also noticed text\n // elements consisting of just \"\\n\" being included in the nodeset of\n // $divTag.\n // D) renderContent has a bug where only position \"replace\" (the default)\n // uses the jQuery functions, so other positions like \"beforeend\" will\n // prevent child script tags from running.\n //\n // In theory the same problem exists for $liTag but since that content is\n // much less likely to include arbitrary scripts, we're skipping it.\n //\n // This code could be nicer if we didn't use renderContent, but rather the\n // lower-level functions that renderContent uses. Like if we pre-process\n // the value of message.divTag.html for singletons, we could do that, then\n // render dependencies, then do $tabContent.append($divTag).\n exports.renderContent($tabContent[0], {html: \"\", deps: message.divTag.deps}, \"beforeend\");\n $divTag.get().forEach(el => {\n // Must not use jQuery for appending el to the doc, we don't want any\n // scripts to run (since they will run when renderContent takes a crack).\n $tabContent[0].appendChild(el);\n // If `el` itself is a script tag, this approach won't work (the script\n // won't be run), since we're only sending innerHTML through renderContent\n // and not the whole tag. That's fine in this case because we control the\n // R code that generates this HTML, and we know that the element is not\n // a script tag.\n exports.renderContent(el, el.innerHTML || el.textContent);\n });\n\n if (message.select) {\n $liTag.find(\"a\").tab(\"show\");\n }\n\n /* Barbara -- August 2017\n Note: until now, the number of tabs in a tabsetPanel (or navbarPage\n or navlistPanel) was always fixed. So, an easy way to give an id to\n a tab was simply incrementing a counter. (Just like it was easy to\n give a random 4-digit number to identify the tabsetPanel). Now that\n we're introducing dynamic tabs, we must retrieve these numbers and\n fix the dummy id given to the tab in the R side -- there, we always\n set the tab id (counter dummy) to \"id\" and the tabset id to \"tsid\")\n */\n function getTabIndex($tabset, tabsetId) {\n // The 0 is to ensure this works for empty tabsetPanels as well\n var existingTabIds = [0];\n // loop through all existing tabs, find the one with highest id\n // (since this is based on a numeric counter), and increment\n $tabset.find(\"> li\").each(function() {\n var $tab = $(this).find(\"> a[data-toggle='tab']\");\n if ($tab.length > 0) {\n // remove leading url if it exists. (copy of bootstrap url stripper)\n var href = $tab.attr(\"href\").replace(/.*(?=#[^\\s]+$)/, '');\n // remove tab id to get the index\n var index = href.replace(\"#tab-\" + tabsetId + \"-\", \"\");\n existingTabIds.push(Number(index));\n }\n });\n return Math.max.apply(null, existingTabIds) + 1;\n }\n\n // Finds out if the item will be placed inside a navbarMenu\n // (dropdown). If so, returns the dropdown tabset (ul tag)\n // and the dropdown tabsetid (to be used to fix the tab ID)\n function getDropdown() {\n if (message.menuName !== null) {\n // menuName is only provided if the user wants to prepend\n // or append an item inside a navbarMenu (dropdown)\n var $dropdownATag = $(\"a.dropdown-toggle[data-value='\" +\n $escape(message.menuName) + \"']\");\n if ($dropdownATag.length === 0) {\n throw \"There is no navbarMenu with menuName equal to '\" +\n message.menuName + \"'\";\n }\n var $dropdownTabset = $dropdownATag.find(\"+ ul.dropdown-menu\");\n var dropdownId = $dropdownTabset.attr(\"data-tabsetid\");\n return { $tabset: $dropdownTabset, id: dropdownId };\n\n } else if (message.target !== null) {\n // if our item is to be placed next to a tab that is inside\n // a navbarMenu, our item will also be inside\n var $uncleTabset = $targetLiTag.parent(\"ul\");\n if ($uncleTabset.hasClass(\"dropdown-menu\")) {\n var uncleId = $uncleTabset.attr(\"data-tabsetid\");\n return { $tabset: $uncleTabset, id: uncleId };\n }\n }\n return null;\n }\n });\n\n // If the given tabset has no active tabs, select the first one\n function ensureTabsetHasVisibleTab($tabset) {\n if ($tabset.find(\"li.active\").not(\".dropdown\").length === 0) {\n // Note: destTabValue may be null. We still want to proceed\n // through the below logic and setValue so that the input\n // value for the tabset gets updated (i.e. input$tabsetId\n // should be null if there are no tabs).\n let destTabValue = getFirstTab($tabset);\n let inputBinding = $tabset.data('shiny-input-binding');\n let evt = jQuery.Event('shiny:updateinput');\n evt.binding = inputBinding;\n $tabset.trigger(evt);\n inputBinding.setValue($tabset[0], destTabValue);\n }\n }\n\n // Given a tabset ul jquery object, return the value of the first tab\n // (in document order) that's visible and able to be selected.\n function getFirstTab($ul) {\n return $ul.find(\"li:visible a[data-toggle='tab']\")\n .first()\n .attr(\"data-value\") || null;\n }\n\n function tabApplyFunction(target, func, liTags = false) {\n $.each(target, function(key, el) {\n if (key === \"$liTag\") {\n // $liTag is always just one jQuery element\n func(el);\n }\n else if (key === \"$divTags\") {\n // $divTags is always an array (even if length = 1)\n $.each(el, function(i, div) { func(div); });\n\n } else if (liTags && key === \"$liTags\") {\n // $liTags is always an array (even if length = 0)\n $.each(el, function(i, div) { func(div); });\n }\n });\n }\n\n addMessageHandler(\"shiny-remove-tab\", function(message) {\n var $tabset = getTabset(message.inputId);\n var $tabContent = getTabContent($tabset);\n var target = getTargetTabs($tabset, $tabContent, message.target);\n\n tabApplyFunction(target, removeEl);\n\n ensureTabsetHasVisibleTab($tabset);\n\n function removeEl($el) {\n exports.unbindAll($el, true);\n $el.remove();\n }\n });\n\n addMessageHandler(\"shiny-change-tab-visibility\", function(message) {\n var $tabset = getTabset(message.inputId);\n var $tabContent = getTabContent($tabset);\n var target = getTargetTabs($tabset, $tabContent, message.target);\n\n tabApplyFunction(target, changeVisibility, true);\n\n ensureTabsetHasVisibleTab($tabset);\n\n function changeVisibility($el) {\n if (message.type === \"show\") $el.css(\"display\", \"\");\n else if (message.type === \"hide\") {\n $el.hide();\n $el.removeClass(\"active\");\n }\n }\n });\n\n addMessageHandler('updateQueryString', function(message) {\n\n // leave the bookmarking code intact\n if (message.mode === \"replace\") {\n window.history.replaceState(null, null, message.queryString);\n return;\n }\n\n var what = null;\n if (message.queryString.charAt(0) === \"#\") what = \"hash\";\n else if (message.queryString.charAt(0) === \"?\") what = \"query\";\n else throw \"The 'query' string must start with either '?' \" +\n \"(to update the query string) or with '#' (to \" +\n \"update the hash).\";\n\n var path = window.location.pathname;\n var oldQS = window.location.search;\n var oldHash = window.location.hash;\n\n /* Barbara -- December 2016\n Note: we could check if the new QS and/or hash are different\n from the old one(s) and, if not, we could choose not to push\n a new state (whether or not we would replace it is moot/\n inconsequential). However, I think that it is better to\n interpret each call to `updateQueryString` as representing\n new state (even if the message.queryString is the same), so\n that check isn't even performed as of right now.\n */\n\n var relURL = path;\n if (what === \"query\") relURL += message.queryString;\n else relURL += oldQS + message.queryString; // leave old QS if it exists\n window.history.pushState(null, null, relURL);\n\n // for the case when message.queryString has both a query string\n // and a hash (`what = \"hash\"` allows us to trigger the\n // hashchange event)\n if (message.queryString.indexOf(\"#\") !== -1) what = \"hash\";\n\n // for the case when there was a hash before, but there isn't\n // any hash now (e.g. for when only the query string is updated)\n if (window.location.hash !== oldHash) what = \"hash\";\n\n // This event needs to be triggered manually because pushState() never\n // causes a hashchange event to be fired,\n if (what === \"hash\") $(document).trigger(\"hashchange\");\n });\n\n addMessageHandler(\"resetBrush\", function(message) {\n exports.resetBrush(message.brushId);\n });\n\n // Progress reporting ====================================================\n\n var progressHandlers = {\n // Progress for a particular object\n binding: function(message) {\n var key = message.id;\n var binding = this.$bindings[key];\n if (binding) {\n $(binding.el).trigger({\n type: 'shiny:outputinvalidated',\n binding: binding,\n name: key\n });\n if (binding.showProgress) binding.showProgress(true);\n }\n },\n\n // Open a page-level progress bar\n open: function(message) {\n if (message.style === \"notification\") {\n // For new-style (starting in Shiny 0.14) progress indicators that use\n // the notification API.\n\n // Progress bar starts hidden; will be made visible if a value is provided\n // during updates.\n exports.notifications.show({\n html:\n `
` +\n '
' +\n '
' +\n 'message ' +\n '' +\n '
' +\n '
',\n id: message.id,\n duration: null\n });\n\n } else if (message.style === \"old\") {\n // For old-style (Shiny <=0.13.2) progress indicators.\n\n // Add progress container (for all progress items) if not already present\n var $container = $('.shiny-progress-container');\n if ($container.length === 0) {\n $container = $('
');\n $(document.body).append($container);\n }\n\n // Add div for just this progress ID\n var depth = $('.shiny-progress.open').length;\n // The 'bar' class is needed for backward compatibility with Bootstrap 2.\n var $progress = $('
' +\n '
' +\n '
' +\n 'message' +\n '' +\n '
' +\n '
'\n );\n\n $progress.attr('id', message.id);\n $container.append($progress);\n\n // Stack bars\n var $progressBar = $progress.find('.progress');\n $progressBar.css('top', depth * $progressBar.height() + 'px');\n\n // Stack text objects\n var $progressText = $progress.find('.progress-text');\n $progressText.css('top', 3 * $progressBar.height() +\n depth * $progressText.outerHeight() + 'px');\n\n $progress.hide();\n }\n\n },\n\n // Update page-level progress bar\n update: function(message) {\n if (message.style === \"notification\") {\n // For new-style (starting in Shiny 0.14) progress indicators that use\n // the notification API.\n var $progress = $('#shiny-progress-' + message.id);\n\n if ($progress.length === 0)\n return;\n\n if (typeof(message.message) !== 'undefined') {\n $progress.find('.progress-message').text(message.message);\n }\n if (typeof(message.detail) !== 'undefined') {\n $progress.find('.progress-detail').text(message.detail);\n }\n if (typeof(message.value) !== 'undefined' && message.value !== null) {\n $progress.find('.progress').show();\n $progress.find('.progress-bar').width((message.value*100) + '%');\n }\n\n } else if (message.style === \"old\") {\n // For old-style (Shiny <=0.13.2) progress indicators.\n\n var $progress = $('#' + message.id + '.shiny-progress');\n if (typeof(message.message) !== 'undefined') {\n $progress.find('.progress-message').text(message.message);\n }\n if (typeof(message.detail) !== 'undefined') {\n $progress.find('.progress-detail').text(message.detail);\n }\n if (typeof(message.value) !== 'undefined' && message.value !== null) {\n $progress.find('.progress').show();\n $progress.find('.bar').width((message.value*100) + '%');\n }\n\n $progress.fadeIn();\n }\n\n },\n\n // Close page-level progress bar\n close: function(message) {\n if (message.style === \"notification\") {\n exports.notifications.remove(message.id);\n\n } else if (message.style === \"old\") {\n var $progress = $('#' + message.id + '.shiny-progress');\n $progress.removeClass('open');\n\n $progress.fadeOut({\n complete: function() {\n $progress.remove();\n\n // If this was the last shiny-progress, remove container\n if ($('.shiny-progress').length === 0)\n $('.shiny-progress-container').remove();\n }\n });\n }\n }\n };\n\n exports.progressHandlers = progressHandlers;\n\n // Returns a URL which can be queried to get values from inside the server\n // function. This is enabled with `options(shiny.testmode=TRUE)`.\n this.getTestSnapshotBaseUrl = function({ fullUrl = true } = {})\n {\n const loc = window.location;\n let url = \"\";\n\n if (fullUrl) {\n // Strip off everything after last slash in path, like dirname() in R\n url = loc.origin + loc.pathname.replace(/\\/[^/]*$/, \"\");\n }\n url += \"/session/\" +\n encodeURIComponent(this.config.sessionId) +\n \"/dataobj/shinytest?w=\" +\n encodeURIComponent(this.config.workerId) +\n \"&nonce=\" + randomId();\n\n return url;\n };\n\n}).call(ShinyApp.prototype);\n\n\n\nexports.showReconnectDialog = (function() {\n var reconnectTime = null;\n\n function updateTime() {\n var $time = $(\"#shiny-reconnect-time\");\n // If the time has been removed, exit and don't reschedule this function.\n if ($time.length === 0) return;\n\n var seconds = Math.floor((reconnectTime - new Date().getTime()) / 1000);\n if (seconds > 0) {\n $time.text(\" in \" + seconds + \"s\");\n } else {\n $time.text(\"...\");\n }\n\n // Reschedule this function after 1 second\n setTimeout(updateTime, 1000);\n }\n\n\n return function(delay) {\n reconnectTime = new Date().getTime() + delay;\n\n // If there's already a reconnect dialog, don't add another\n if ($('#shiny-reconnect-text').length > 0)\n return;\n\n var html = 'Attempting to reconnect' +\n '';\n var action = 'Try now';\n\n exports.notifications.show({\n id: \"reconnect\",\n html: html,\n action: action,\n duration: null,\n closeButton: false,\n type: 'warning'\n });\n\n updateTime();\n };\n})();\n\nexports.hideReconnectDialog = function() {\n exports.notifications.remove(\"reconnect\");\n};\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/notifications.js\n\nexports.notifications = (function() {\n\n // Milliseconds to fade in or out\n const fadeDuration = 250;\n\n function show({ html='', action='', deps=[], duration=5000,\n id=null, closeButton=true, type=null } = {})\n {\n if (!id)\n id = randomId();\n\n // Create panel if necessary\n _createPanel();\n\n // Get existing DOM element for this ID, or create if needed.\n let $notification = _get(id);\n if ($notification.length === 0)\n $notification = _create(id);\n\n // Render html and dependencies\n const newHtml = `
${html}
` +\n `
${action}
`;\n const $content = $notification.find('.shiny-notification-content');\n exports.renderContent($content, { html: newHtml, deps: deps });\n\n // Remove any existing classes of the form 'shiny-notification-xxxx'.\n // The xxxx would be strings like 'warning'.\n const classes = $notification.attr('class')\n .split(/\\s+/)\n .filter(cls => cls.match(/^shiny-notification-/))\n .join(' ');\n $notification.removeClass(classes);\n\n // Add class. 'default' means no additional CSS class.\n if (type && type !== 'default')\n $notification.addClass('shiny-notification-' + type);\n\n\n // Make sure that the presence/absence of close button matches with value\n // of `closeButton`.\n const $close = $notification.find('.shiny-notification-close');\n if (closeButton && $close.length === 0) {\n $notification.append('
×
');\n } else if (!closeButton && $close.length !== 0) {\n $close.remove();\n }\n\n // If duration was provided, schedule removal. If not, clear existing\n // removal callback (this happens if a message was first added with\n // a duration, and then updated with no duration).\n if (duration)\n _addRemovalCallback(id, duration);\n else\n _clearRemovalCallback(id);\n\n return id;\n }\n\n function remove(id) {\n _get(id).fadeOut(fadeDuration, function() {\n\n exports.unbindAll(this);\n $(this).remove();\n\n // If no more notifications, remove the panel from the DOM.\n if (_ids().length === 0) {\n _getPanel().remove();\n }\n });\n }\n\n // Returns an individual notification DOM object (wrapped in jQuery).\n function _get(id) {\n if (!id)\n return null;\n return _getPanel().find('#shiny-notification-' + $escape(id));\n }\n\n // Return array of all notification IDs\n function _ids() {\n return _getPanel()\n .find('.shiny-notification')\n .map(function() { return this.id.replace(/shiny-notification-/, ''); })\n .get();\n }\n\n // Returns the notification panel DOM object (wrapped in jQuery).\n function _getPanel() {\n return $('#shiny-notification-panel');\n }\n\n // Create notifications panel and return the jQuery object. If the DOM\n // element already exists, just return it.\n function _createPanel() {\n let $panel = _getPanel();\n\n if ($panel.length > 0)\n return $panel;\n\n $(document.body).append('
');\n\n return $panel;\n }\n\n // Create a notification DOM element and return the jQuery object. If the\n // DOM element already exists for the ID, just return it without creating.\n function _create(id) {\n let $notification = _get(id);\n\n if ($notification.length === 0) {\n $notification = $(\n `
` +\n '
×
' +\n '
' +\n '
'\n );\n\n $notification.find('.shiny-notification-close').on('click', e => {\n e.preventDefault();\n e.stopPropagation();\n remove(id);\n });\n\n _getPanel().append($notification);\n }\n\n return $notification;\n }\n\n // Add a callback to remove a notification after a delay in ms.\n function _addRemovalCallback(id, delay) {\n // If there's an existing removalCallback, clear it before adding the new\n // one.\n _clearRemovalCallback(id);\n\n // Attach new removal callback\n const removalCallback = setTimeout(function() { remove(id); }, delay);\n _get(id).data('removalCallback', removalCallback);\n }\n\n // Clear a removal callback from a notification, if present.\n function _clearRemovalCallback(id) {\n const $notification = _get(id);\n const oldRemovalCallback = $notification.data('removalCallback');\n if (oldRemovalCallback) {\n clearTimeout(oldRemovalCallback);\n }\n }\n\n return {\n show,\n remove\n };\n})();\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/modal.js\n\nexports.modal = {\n\n // Show a modal dialog. This is meant to handle two types of cases: one is\n // that the content is a Bootstrap modal dialog, and the other is that the\n // content is non-Bootstrap. Bootstrap modals require some special handling,\n // which is coded in here.\n show: function({ html='', deps=[] } = {}) {\n\n // If there was an existing Bootstrap modal, then there will be a modal-\n // backdrop div that was added outside of the modal wrapper, and it must be\n // removed; otherwise there can be multiple of these divs.\n $('.modal-backdrop').remove();\n\n // Get existing wrapper DOM element, or create if needed.\n let $modal = $('#shiny-modal-wrapper');\n if ($modal.length === 0) {\n $modal = $('
');\n $(document.body).append($modal);\n\n // If the wrapper's content is a Bootstrap modal, then when the inner\n // modal is hidden, remove the entire thing, including wrapper.\n $modal.on('hidden.bs.modal', function(e) {\n if (e.target === $(\"#shiny-modal\")[0]) {\n exports.unbindAll($modal);\n $modal.remove();\n }\n });\n }\n\n $modal.on('keydown.shinymodal', function(e) {\n // If we're listening for Esc, don't let the event propagate. See\n // https://github.com/rstudio/shiny/issues/1453. The value of\n // data(\"keyboard\") needs to be checked inside the handler, because at\n // the time that $modal.on() is called, the $(\"#shiny-modal\") div doesn't\n // yet exist.\n if ($(\"#shiny-modal\").data(\"keyboard\") === false)\n return;\n\n if (e.keyCode === 27) {\n e.stopPropagation();\n e.preventDefault();\n }\n });\n\n // Set/replace contents of wrapper with html.\n exports.renderContent($modal, { html: html, deps: deps });\n },\n\n remove: function() {\n const $modal = $('#shiny-modal-wrapper');\n\n $modal.off('keydown.shinymodal');\n\n // Look for a Bootstrap modal and if present, trigger hide event. This will\n // trigger the hidden.bs.modal callback that we set in show(), which unbinds\n // and removes the element.\n if ($modal.find('.modal').length > 0) {\n $modal.find('.modal').modal('hide');\n\n } else {\n // If not a Bootstrap modal dialog, simply unbind and remove it.\n exports.unbindAll($modal);\n $modal.remove();\n }\n }\n};\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/file_processor.js\n\n// Generic driver class for doing chunk-wise asynchronous processing of a\n// FileList object. Subclass/clone it and override the `on*` functions to\n// make it do something useful.\nvar FileProcessor = function(files) {\n this.files = files;\n this.fileIndex = -1;\n // Currently need to use small chunk size because R-Websockets can't\n // handle continuation frames\n this.aborted = false;\n this.completed = false;\n\n // TODO: Register error/abort callbacks\n\n this.$run();\n};\n(function() {\n // Begin callbacks. Subclassers/cloners may override any or all of these.\n this.onBegin = function(files, cont) {\n setTimeout(cont, 0);\n };\n this.onFile = function(file, cont) {\n setTimeout(cont, 0);\n };\n this.onComplete = function() {\n };\n this.onAbort = function() {\n };\n // End callbacks\n\n // Aborts processing, unless it's already completed\n this.abort = function() {\n if (this.completed || this.aborted)\n return;\n\n this.aborted = true;\n this.onAbort();\n };\n\n // Returns a bound function that will call this.$run one time.\n this.$getRun = function() {\n var self = this;\n var called = false;\n return function() {\n if (called)\n return;\n called = true;\n self.$run();\n };\n };\n\n // This function will be called multiple times to advance the process.\n // It relies on the state of the object's fields to know what to do next.\n this.$run = function() {\n\n if (this.aborted || this.completed)\n return;\n\n if (this.fileIndex < 0) {\n // Haven't started yet--begin\n this.fileIndex = 0;\n this.onBegin(this.files, this.$getRun());\n return;\n }\n\n if (this.fileIndex === this.files.length) {\n // Just ended\n this.completed = true;\n this.onComplete();\n return;\n }\n\n // If we got here, then we have a file to process, or we are\n // in the middle of processing a file, or have just finished\n // processing a file.\n\n var file = this.files[this.fileIndex++];\n this.onFile(file, this.$getRun());\n };\n}).call(FileProcessor.prototype);\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/binding_registry.js\n\nvar BindingRegistry = function() {\n this.bindings = [];\n this.bindingNames = {};\n};\n(function() {\n this.register = function(binding, bindingName, priority) {\n var bindingObj = {binding: binding, priority: priority || 0};\n this.bindings.unshift(bindingObj);\n if (bindingName) {\n this.bindingNames[bindingName] = bindingObj;\n binding.name = bindingName;\n }\n };\n this.setPriority = function(bindingName, priority) {\n var bindingObj = this.bindingNames[bindingName];\n if (!bindingObj)\n throw \"Tried to set priority on unknown binding \" + bindingName;\n bindingObj.priority = priority || 0;\n };\n this.getPriority = function(bindingName) {\n var bindingObj = this.bindingNames[bindingName];\n if (!bindingObj)\n return false;\n return bindingObj.priority;\n };\n this.getBindings = function() {\n // Sort the bindings. The ones with higher priority are consulted\n // first; ties are broken by most-recently-registered.\n return mergeSort(this.bindings, function(a, b) {\n return b.priority - a.priority;\n });\n };\n}).call(BindingRegistry.prototype);\n\n\nvar inputBindings = exports.inputBindings = new BindingRegistry();\nvar outputBindings = exports.outputBindings = new BindingRegistry();\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/output_binding.js\n\nvar OutputBinding = exports.OutputBinding = function() {};\n(function() {\n // Returns a jQuery object or element array that contains the\n // descendants of scope that match this binding\n this.find = function(scope) { throw \"Not implemented\"; };\n\n this.getId = function(el) {\n return el['data-input-id'] || el.id;\n };\n\n this.onValueChange = function(el, data) {\n this.clearError(el);\n this.renderValue(el, data);\n };\n this.onValueError = function(el, err) {\n this.renderError(el, err);\n };\n this.renderError = function(el, err) {\n this.clearError(el);\n if (err.message === '') {\n // not really error, but we just need to wait (e.g. action buttons)\n $(el).empty();\n return;\n }\n var errClass = 'shiny-output-error';\n if (err.type !== null) {\n // use the classes of the error condition as CSS class names\n errClass = errClass + ' ' + $.map(asArray(err.type), function(type) {\n return errClass + '-' + type;\n }).join(' ');\n }\n $(el).addClass(errClass).text(err.message);\n };\n this.clearError = function(el) {\n $(el).attr('class', function(i, c) {\n return c.replace(/(^|\\s)shiny-output-error\\S*/g, '');\n });\n };\n this.showProgress = function(el, show) {\n var RECALC_CLASS = 'recalculating';\n if (show)\n $(el).addClass(RECALC_CLASS);\n else\n $(el).removeClass(RECALC_CLASS);\n };\n}).call(OutputBinding.prototype);\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/output_binding_text.js\n\nvar textOutputBinding = new OutputBinding();\n$.extend(textOutputBinding, {\n find: function(scope) {\n return $(scope).find('.shiny-text-output');\n },\n renderValue: function(el, data) {\n $(el).text(data);\n }\n});\noutputBindings.register(textOutputBinding, 'shiny.textOutput');\n\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/output_binding_image.js\n\nvar imageOutputBinding = new OutputBinding();\n$.extend(imageOutputBinding, {\n find: function(scope) {\n return $(scope).find('.shiny-image-output, .shiny-plot-output');\n },\n renderValue: function(el, data) {\n // The overall strategy:\n // * Clear out existing image and event handlers.\n // * Create new image.\n // * Create various event handlers.\n // * Bind those event handlers to events.\n // * Insert the new image.\n\n var outputId = this.getId(el);\n\n var $el = $(el);\n var img;\n\n // Get existing img element if present.\n var $img = $el.find('img');\n\n if ($img.length === 0) {\n // If a img element is not already present, that means this is either\n // the first time renderValue() has been called, or this is after an\n // error.\n img = document.createElement('img');\n $el.append(img);\n $img = $(img);\n } else {\n // Trigger custom 'reset' event for any existing images in the div\n img = $img[0];\n $img.trigger('reset');\n }\n\n if (!data) {\n $el.empty();\n return;\n }\n\n // If value is undefined, return alternate. Sort of like ||, except it won't\n // return alternate for other falsy values (0, false, null).\n function OR(value, alternate) {\n if (value === undefined) return alternate;\n return value;\n }\n\n var opts = {\n clickId: $el.data('click-id'),\n clickClip: OR(strToBool($el.data('click-clip')), true),\n\n dblclickId: $el.data('dblclick-id'),\n dblclickClip: OR(strToBool($el.data('dblclick-clip')), true),\n dblclickDelay: OR($el.data('dblclick-delay'), 400),\n\n hoverId: $el.data('hover-id'),\n hoverClip: OR(strToBool($el.data('hover-clip')), true),\n hoverDelayType: OR($el.data('hover-delay-type'), 'debounce'),\n hoverDelay: OR($el.data('hover-delay'), 300),\n hoverNullOutside: OR(strToBool($el.data('hover-null-outside')), false),\n\n brushId: $el.data('brush-id'),\n brushClip: OR(strToBool($el.data('brush-clip')), true),\n brushDelayType: OR($el.data('brush-delay-type'), 'debounce'),\n brushDelay: OR($el.data('brush-delay'), 300),\n brushFill: OR($el.data('brush-fill'), '#666'),\n brushStroke: OR($el.data('brush-stroke'), '#000'),\n brushOpacity: OR($el.data('brush-opacity'), 0.3),\n brushDirection: OR($el.data('brush-direction'), 'xy'),\n brushResetOnNew: OR(strToBool($el.data('brush-reset-on-new')), false),\n\n coordmap: data.coordmap\n };\n\n // Copy items from data to img. Don't set the coordmap as an attribute.\n $.each(data, function(key, value) {\n if (value === null || key === 'coordmap') {\n return;\n }\n // this checks only against base64 encoded src values\n // images put here are only from renderImage and renderPlot\n if (key === \"src\" && value === img.getAttribute(\"src\")) {\n // Ensure the browser actually fires an onLoad event, which doesn't\n // happen on WebKit if the value we set on src is the same as the\n // value it already has\n // https://github.com/rstudio/shiny/issues/2197\n // https://stackoverflow.com/questions/5024111/javascript-image-onload-doesnt-fire-in-webkit-if-loading-same-image\n img.removeAttribute(\"src\");\n }\n img.setAttribute(key, value);\n });\n\n // Unset any attributes in the current img that were not provided in the\n // new data.\n for (var i=0; i max)\n newval = max;\n else if (newval < min)\n newval = min;\n }\n return newval;\n }\n\n // Create scale and inverse-scale functions for a single direction (x or y).\n function scaler1D(domainMin, domainMax, rangeMin, rangeMax, logbase) {\n return {\n scale: function(val, clip) {\n if (logbase)\n val = Math.log(val) / Math.log(logbase);\n return mapLinear(val, domainMin, domainMax, rangeMin, rangeMax, clip);\n },\n\n scaleInv: function(val, clip) {\n var res = mapLinear(val, rangeMin, rangeMax, domainMin, domainMax, clip);\n if (logbase)\n res = Math.pow(logbase, res);\n return res;\n }\n };\n }\n\n // Modify panel, adding scale and inverse-scale functions that take objects\n // like {x:1, y:3}, and also add clip function.\n function addScaleFuns(panel) {\n var d = panel.domain;\n var r = panel.range;\n var xlog = (panel.log && panel.log.x) ? panel.log.x : null;\n var ylog = (panel.log && panel.log.y) ? panel.log.y : null;\n var xscaler = scaler1D(d.left, d.right, r.left, r.right, xlog);\n var yscaler = scaler1D(d.bottom, d.top, r.bottom, r.top, ylog);\n\n // Given an object of form {x:1, y:2}, or {x:1, xmin:2:, ymax: 3}, convert\n // from data coordinates to img. Whether a value is converted as x or y\n // depends on the first character of the key.\n panel.scaleDataToImg = function(val, clip) {\n return mapValues(val, (value, key) => {\n const prefix = key.substring(0, 1);\n if (prefix === \"x\") {\n return xscaler.scale(value, clip);\n } else if (prefix === \"y\") {\n return yscaler.scale(value, clip);\n }\n return null;\n });\n };\n\n panel.scaleImgToData = function(val, clip) {\n return mapValues(val, (value, key) => {\n const prefix = key.substring(0, 1);\n if (prefix === \"x\") {\n return xscaler.scaleInv(value, clip);\n } else if (prefix === \"y\") {\n return yscaler.scaleInv(value, clip);\n }\n return null;\n });\n };\n\n // Given a scaled offset (in img pixels), clip it to the nearest panel region.\n panel.clipImg = function(offset_img) {\n var newOffset = {\n x: offset_img.x,\n y: offset_img.y\n };\n\n var bounds = panel.range;\n\n if (offset_img.x > bounds.right) newOffset.x = bounds.right;\n else if (offset_img.x < bounds.left) newOffset.x = bounds.left;\n\n if (offset_img.y > bounds.bottom) newOffset.y = bounds.bottom;\n else if (offset_img.y < bounds.top) newOffset.y = bounds.top;\n\n return newOffset;\n };\n }\n\n // Add the functions to each panel object.\n for (var i=0; i (not including padding\n// and border).\n// 2. img: The pixel coordinates of the image data. A common case is on a\n// HiDPI device, where the source PNG image could be 1000 pixels wide but\n// be displayed in 500 CSS pixels. Another case is when the image has\n// additional scaling due to CSS transforms or width.\n// 3. data: The coordinates in the data space. This is a bit more complicated\n// than the other two, because there can be multiple panels (as in facets).\nimageutils.initCoordmap = function($el, coordmap) {\n const $img = $el.find(\"img\");\n const img = $img[0];\n\n // If we didn't get any panels, create a dummy one where the domain and range\n // are simply the pixel dimensions.\n // that we modify.\n if (coordmap.panels.length === 0) {\n let bounds = {\n top: 0,\n left: 0,\n right: img.clientWidth - 1,\n bottom: img.clientHeight - 1\n };\n\n coordmap.panels[0] = {\n domain: bounds,\n range: bounds,\n mapping: {}\n };\n }\n\n // If no dim height and width values are found, set them to the raw image height and width\n // These values should be the same...\n // This is only done to initialize an image output, whose height and width are unknown until the image is retrieved\n coordmap.dims.height = coordmap.dims.height || img.naturalHeight;\n coordmap.dims.width = coordmap.dims.width || img.naturalWidth;\n\n // Add scaling functions to each panel\n imageutils.initPanelScales(coordmap.panels);\n\n\n // This returns the offset of the mouse in CSS pixels relative to the img,\n // but not including the padding or border, if present.\n coordmap.mouseOffsetCss = function(mouseEvent) {\n const img_origin = findOrigin($img);\n\n // The offset of the mouse from the upper-left corner of the img, in\n // pixels.\n return {\n x: mouseEvent.pageX - img_origin.x,\n y: mouseEvent.pageY - img_origin.y\n };\n };\n\n // Given an offset in an img in CSS pixels, return the corresponding offset\n // in source image pixels. The offset_css can have properties like \"x\",\n // \"xmin\", \"y\", and \"ymax\" -- anything that starts with \"x\" and \"y\". If the\n // img content is 1000 pixels wide, but is scaled to 400 pixels on screen,\n // and the input is x:400, then this will return x:1000.\n coordmap.scaleCssToImg = function(offset_css) {\n const pixel_scaling = coordmap.imgToCssScalingRatio();\n\n const result = mapValues(offset_css, (value, key) => {\n const prefix = key.substring(0, 1);\n\n if (prefix === \"x\") {\n return offset_css[key] / pixel_scaling.x;\n } else if (prefix === \"y\") {\n return offset_css[key] / pixel_scaling.y;\n }\n return null;\n });\n\n return result;\n };\n\n // Given an offset in an img, in source image pixels, return the\n // corresponding offset in CSS pixels. If the img content is 1000 pixels\n // wide, but is scaled to 400 pixels on screen, and the input is x:1000,\n // then this will return x:400.\n coordmap.scaleImgToCss = function(offset_img) {\n const pixel_scaling = coordmap.imgToCssScalingRatio();\n\n const result = mapValues(offset_img, (value, key) => {\n const prefix = key.substring(0, 1);\n\n if (prefix === \"x\") {\n return offset_img[key] * pixel_scaling.x;\n } else if (prefix === \"y\") {\n return offset_img[key] * pixel_scaling.y;\n }\n return null;\n });\n\n return result;\n };\n\n // Returns the x and y ratio the image content is scaled to on screen. If\n // the image data is 1000 pixels wide and is scaled to 300 pixels on screen,\n // then this returns 0.3. (Note the 300 pixels refers to CSS pixels.)\n coordmap.imgToCssScalingRatio = function() {\n const img_dims = findDims($img);\n return {\n x: img_dims.x / coordmap.dims.width,\n y: img_dims.y / coordmap.dims.height\n };\n };\n\n coordmap.cssToImgScalingRatio = function() {\n const res = coordmap.imgToCssScalingRatio();\n return {\n x: 1 / res.x,\n y: 1 / res.y\n };\n };\n\n // Given an offset in css pixels, return an object representing which panel\n // it's in. The `expand` argument tells it to expand the panel area by that\n // many pixels. It's possible for an offset to be within more than one\n // panel, because of the `expand` value. If that's the case, find the\n // nearest panel.\n coordmap.getPanelCss = function(offset_css, expand = 0) {\n const offset_img = coordmap.scaleCssToImg(offset_css);\n const x = offset_img.x;\n const y = offset_img.y;\n\n // Convert expand from css pixels to img pixels\n const cssToImgRatio = coordmap.cssToImgScalingRatio();\n const expand_img = {\n x: expand * cssToImgRatio.x,\n y: expand * cssToImgRatio.y\n };\n\n const matches = []; // Panels that match\n const dists = []; // Distance of offset to each matching panel\n let b;\n for (var i=0; i= b.left - expand_img.x &&\n y <= b.bottom + expand_img.y &&\n y >= b.top - expand_img.y)\n {\n matches.push(coordmap.panels[i]);\n\n // Find distance from edges for x and y\n var xdist = 0;\n var ydist = 0;\n if (x > b.right && x <= b.right + expand_img.x) {\n xdist = x - b.right;\n } else if (x < b.left && x >= b.left - expand_img.x) {\n xdist = x - b.left;\n }\n if (y > b.bottom && y <= b.bottom + expand_img.y) {\n ydist = y - b.bottom;\n } else if (y < b.top && y >= b.top - expand_img.y) {\n ydist = y - b.top;\n }\n\n // Cartesian distance\n dists.push(Math.sqrt( Math.pow(xdist, 2) + Math.pow(ydist, 2) ));\n }\n }\n\n if (matches.length) {\n // Find shortest distance\n var min_dist = Math.min.apply(null, dists);\n for (i=0; i max) {\n shiftAmount = max - maxval;\n } else if (minval < min) {\n shiftAmount = min - minval;\n }\n\n var newvals = [];\n for (var i=0; i 2 ||\n Math.abs(pending_e.pageY - e.pageY) > 2) {\n\n triggerPendingMousedown2();\n scheduleMousedown2(e);\n\n } else {\n // The second click was close to the first one. If it happened\n // within specified delay, trigger our custom 'dblclick2' event.\n pending_e = null;\n triggerEvent('dblclick2', e);\n }\n }\n }\n\n // IE8 needs a special hack because when you do a double-click it doesn't\n // trigger the click event twice - it directly triggers dblclick.\n function dblclickIE8(e) {\n e.which = 1; // In IE8, e.which is 0 instead of 1. ???\n triggerEvent('dblclick2', e);\n }\n\n return {\n mousedown: mousedown,\n dblclickIE8: dblclickIE8\n };\n};\n\n\n// ----------------------------------------------------------\n// Handler creators for click, hover, brush.\n// Each of these returns an object with a few public members. These public\n// members are callbacks that are meant to be bound to events on $el with\n// the same name (like 'mousedown').\n// ----------------------------------------------------------\n\nimageutils.createClickHandler = function(inputId, clip, coordmap) {\n var clickInfoSender = coordmap.mouseCoordinateSender(inputId, clip);\n\n return {\n mousedown: function(e) {\n // Listen for left mouse button only\n if (e.which !== 1) return;\n clickInfoSender(e);\n },\n onResetImg: function() { clickInfoSender(null); },\n onResize: null\n };\n};\n\n\nimageutils.createHoverHandler = function(inputId, delay, delayType, clip,\n nullOutside, coordmap)\n{\n var sendHoverInfo = coordmap.mouseCoordinateSender(inputId, clip, nullOutside);\n\n var hoverInfoSender;\n if (delayType === 'throttle')\n hoverInfoSender = new Throttler(null, sendHoverInfo, delay);\n else\n hoverInfoSender = new Debouncer(null, sendHoverInfo, delay);\n\n // What to do when mouse exits the image\n var mouseout;\n if (nullOutside)\n mouseout = function() { hoverInfoSender.normalCall(null); };\n else\n mouseout = function() {};\n\n return {\n mousemove: function(e) { hoverInfoSender.normalCall(e); },\n mouseout: mouseout,\n onResetImg: function() { hoverInfoSender.immediateCall(null); },\n onResize: null\n };\n};\n\n\n// Returns a brush handler object. This has three public functions:\n// mousedown, mousemove, and onResetImg.\nimageutils.createBrushHandler = function(inputId, $el, opts, coordmap, outputId) {\n // Parameter: expand the area in which a brush can be started, by this\n // many pixels in all directions. (This should probably be a brush option)\n var expandPixels = 20;\n\n // Represents the state of the brush\n var brush = imageutils.createBrush($el, opts, coordmap, expandPixels);\n\n // Brush IDs can span multiple image/plot outputs. When an output is brushed,\n // if a brush with the same ID is active on a different image/plot, it must\n // be dismissed (but without sending any data to the server). We implement\n // this by sending the shiny-internal:brushed event to all plots, and letting\n // each plot decide for itself what to do.\n //\n // The decision to have the event sent to each plot (as opposed to a single\n // event triggered on, say, the document) was made to make cleanup easier;\n // listening on an event on the document would prevent garbage collection\n // of plot outputs that are removed from the document.\n $el.on(\"shiny-internal:brushed.image_output\", function(e, coords) {\n // If the new brush shares our ID but not our output element ID, we\n // need to clear our brush (if any).\n if (coords.brushId === inputId && coords.outputId !== outputId) {\n $el.data(\"mostRecentBrush\", false);\n brush.reset();\n }\n });\n\n // Set cursor to one of 7 styles. We need to set the cursor on the whole\n // el instead of the brush div, because the brush div has\n // 'pointer-events:none' so that it won't intercept pointer events.\n // If `style` is null, don't add a cursor style.\n function setCursorStyle(style) {\n $el.removeClass('crosshair grabbable grabbing ns-resize ew-resize nesw-resize nwse-resize');\n\n if (style) $el.addClass(style);\n }\n\n function sendBrushInfo() {\n var coords = brush.boundsData();\n\n // We're in a new or reset state\n if (isNaN(coords.xmin)) {\n exports.setInputValue(inputId, null);\n // Must tell other brushes to clear.\n imageOutputBinding.find(document).trigger(\"shiny-internal:brushed\", {\n brushId: inputId, outputId: null\n });\n return;\n }\n\n var panel = brush.getPanel();\n\n // Add the panel (facet) variables, if present\n $.extend(coords, panel.panel_vars);\n\n coords.coords_css = brush.boundsCss();\n coords.coords_img = coordmap.scaleCssToImg(coords.coords_css);\n\n coords.img_css_ratio = coordmap.cssToImgScalingRatio();\n\n // Add variable name mappings\n coords.mapping = panel.mapping;\n\n // Add scaling information\n coords.domain = panel.domain;\n coords.range = panel.range;\n coords.log = panel.log;\n\n coords.direction = opts.brushDirection;\n\n coords.brushId = inputId;\n coords.outputId = outputId;\n\n // Send data to server\n exports.setInputValue(inputId, coords);\n\n $el.data(\"mostRecentBrush\", true);\n imageOutputBinding.find(document).trigger(\"shiny-internal:brushed\", coords);\n }\n\n var brushInfoSender;\n if (opts.brushDelayType === 'throttle') {\n brushInfoSender = new Throttler(null, sendBrushInfo, opts.brushDelay);\n } else {\n brushInfoSender = new Debouncer(null, sendBrushInfo, opts.brushDelay);\n }\n\n function mousedown(e) {\n // This can happen when mousedown inside the graphic, then mouseup\n // outside, then mousedown inside. Just ignore the second\n // mousedown.\n if (brush.isBrushing() || brush.isDragging() || brush.isResizing()) return;\n\n // Listen for left mouse button only\n if (e.which !== 1) return;\n\n // In general, brush uses css pixels, and coordmap uses img pixels.\n const offset_css = coordmap.mouseOffsetCss(e);\n\n // Ignore mousedown events outside of plotting region, expanded by\n // a number of pixels specified in expandPixels.\n if (opts.brushClip && !coordmap.isInPanelCss(offset_css, expandPixels))\n return;\n\n brush.up({ x: NaN, y: NaN });\n brush.down(offset_css);\n\n\n if (brush.isInResizeArea(offset_css)) {\n brush.startResizing(offset_css);\n\n // Attach the move and up handlers to the window so that they respond\n // even when the mouse is moved outside of the image.\n $(document)\n .on('mousemove.image_brush', mousemoveResizing)\n .on('mouseup.image_brush', mouseupResizing);\n\n } else if (brush.isInsideBrush(offset_css)) {\n brush.startDragging(offset_css);\n setCursorStyle('grabbing');\n\n // Attach the move and up handlers to the window so that they respond\n // even when the mouse is moved outside of the image.\n $(document)\n .on('mousemove.image_brush', mousemoveDragging)\n .on('mouseup.image_brush', mouseupDragging);\n\n } else {\n const panel = coordmap.getPanelCss(offset_css, expandPixels);\n brush.startBrushing(panel.clipImg(coordmap.scaleCssToImg(offset_css)));\n\n // Attach the move and up handlers to the window so that they respond\n // even when the mouse is moved outside of the image.\n $(document)\n .on('mousemove.image_brush', mousemoveBrushing)\n .on('mouseup.image_brush', mouseupBrushing);\n }\n }\n\n // This sets the cursor style when it's in the el\n function mousemove(e) {\n // In general, brush uses css pixels, and coordmap uses img pixels.\n const offset_css = coordmap.mouseOffsetCss(e);\n\n if (!(brush.isBrushing() || brush.isDragging() || brush.isResizing())) {\n // Set the cursor depending on where it is\n if (brush.isInResizeArea(offset_css)) {\n const r = brush.whichResizeSides(offset_css);\n\n if ((r.left && r.top) || (r.right && r.bottom)) {\n setCursorStyle('nwse-resize');\n } else if ((r.left && r.bottom) || (r.right && r.top)) {\n setCursorStyle('nesw-resize');\n } else if (r.left || r.right) {\n setCursorStyle('ew-resize');\n } else if (r.top || r.bottom) {\n setCursorStyle('ns-resize');\n }\n } else if (brush.isInsideBrush(offset_css)) {\n setCursorStyle('grabbable');\n } else if (coordmap.isInPanelCss(offset_css, expandPixels)) {\n setCursorStyle('crosshair');\n } else {\n setCursorStyle(null);\n }\n }\n }\n\n // mousemove handlers while brushing or dragging\n function mousemoveBrushing(e) {\n brush.brushTo(coordmap.mouseOffsetCss(e));\n brushInfoSender.normalCall();\n }\n\n function mousemoveDragging(e) {\n brush.dragTo(coordmap.mouseOffsetCss(e));\n brushInfoSender.normalCall();\n }\n\n function mousemoveResizing(e) {\n brush.resizeTo(coordmap.mouseOffsetCss(e));\n brushInfoSender.normalCall();\n }\n\n // mouseup handlers while brushing or dragging\n function mouseupBrushing(e) {\n // Listen for left mouse button only\n if (e.which !== 1) return;\n\n $(document)\n .off('mousemove.image_brush')\n .off('mouseup.image_brush');\n\n brush.up(coordmap.mouseOffsetCss(e));\n\n brush.stopBrushing();\n setCursorStyle('crosshair');\n\n // If the brush didn't go anywhere, hide the brush, clear value,\n // and return.\n if (brush.down().x === brush.up().x && brush.down().y === brush.up().y) {\n brush.reset();\n brushInfoSender.immediateCall();\n return;\n }\n\n // Send info immediately on mouseup, but only if needed. If we don't\n // do the pending check, we might send the same data twice (with\n // with difference nonce).\n if (brushInfoSender.isPending())\n brushInfoSender.immediateCall();\n }\n\n function mouseupDragging(e) {\n // Listen for left mouse button only\n if (e.which !== 1) return;\n\n $(document)\n .off('mousemove.image_brush')\n .off('mouseup.image_brush');\n\n brush.up(coordmap.mouseOffsetCss(e));\n\n brush.stopDragging();\n setCursorStyle('grabbable');\n\n if (brushInfoSender.isPending())\n brushInfoSender.immediateCall();\n }\n\n function mouseupResizing(e) {\n // Listen for left mouse button only\n if (e.which !== 1) return;\n\n $(document)\n .off('mousemove.image_brush')\n .off('mouseup.image_brush');\n\n brush.up(coordmap.mouseOffsetCss(e));\n brush.stopResizing();\n\n if (brushInfoSender.isPending())\n brushInfoSender.immediateCall();\n\n }\n\n // Brush maintenance: When an image is re-rendered, the brush must either\n // be removed (if brushResetOnNew) or imported (if !brushResetOnNew). The\n // \"mostRecentBrush\" bit is to ensure that when multiple outputs share the\n // same brush ID, inactive brushes don't send null values up to the server.\n\n // This should be called when the img (not the el) is reset\n function onResetImg() {\n if (opts.brushResetOnNew) {\n if ($el.data(\"mostRecentBrush\")) {\n brush.reset();\n brushInfoSender.immediateCall();\n }\n }\n }\n\n if (!opts.brushResetOnNew) {\n if ($el.data(\"mostRecentBrush\")) {\n // Importing an old brush must happen after the image data has loaded\n // and the DOM element has the updated size. If importOldBrush()\n // is called before this happens, then the css-img coordinate mappings\n // will give the wrong result, and the brush will have the wrong\n // position.\n //\n // jcheng 09/26/2018: This used to happen in img.onLoad, but recently\n // we moved to all brush initialization moving to img.onLoad so this\n // logic can be executed inline.\n brush.importOldBrush();\n brushInfoSender.immediateCall();\n }\n }\n\n function onResize() {\n brush.onResize();\n brushInfoSender.immediateCall();\n }\n\n return {\n mousedown: mousedown,\n mousemove: mousemove,\n onResetImg: onResetImg,\n onResize: onResize\n };\n};\n\n// Returns an object that represents the state of the brush. This gets wrapped\n// in a brushHandler, which provides various event listeners.\nimageutils.createBrush = function($el, opts, coordmap, expandPixels) {\n // Number of pixels outside of brush to allow start resizing\n var resizeExpand = 10;\n\n var el = $el[0];\n var $div = null; // The div representing the brush\n\n var state = {};\n\n // Aliases for conciseness\n const cssToImg = coordmap.scaleCssToImg;\n const imgToCss = coordmap.scaleImgToCss;\n\n reset();\n\n function reset() {\n // Current brushing/dragging/resizing state\n state.brushing = false;\n state.dragging = false;\n state.resizing = false;\n\n // Offset of last mouse down and up events (in CSS pixels)\n state.down = { x: NaN, y: NaN };\n state.up = { x: NaN, y: NaN };\n\n // Which side(s) we're currently resizing\n state.resizeSides = {\n left: false,\n right: false,\n top: false,\n bottom: false\n };\n\n // Bounding rectangle of the brush, in CSS pixel and data dimensions. We\n // need to record data dimensions along with pixel dimensions so that when\n // a new plot is sent, we can re-draw the brush div with the appropriate\n // coords.\n state.boundsCss = {\n xmin: NaN,\n xmax: NaN,\n ymin: NaN,\n ymax: NaN\n };\n state.boundsData = {\n xmin: NaN,\n xmax: NaN,\n ymin: NaN,\n ymax: NaN\n };\n\n // Panel object that the brush is in\n state.panel = null;\n\n // The bounds at the start of a drag/resize (in CSS pixels)\n state.changeStartBounds = {\n xmin: NaN,\n xmax: NaN,\n ymin: NaN,\n ymax: NaN\n };\n\n if ($div)\n $div.remove();\n }\n\n // If there's an existing brush div, use that div to set the new brush's\n // settings, provided that the x, y, and panel variables have the same names,\n // and there's a panel with matching panel variable values.\n function importOldBrush() {\n var oldDiv = $el.find('#' + el.id + '_brush');\n if (oldDiv.length === 0)\n return;\n\n var oldBoundsData = oldDiv.data('bounds-data');\n var oldPanel = oldDiv.data('panel');\n\n if (!oldBoundsData || !oldPanel)\n return;\n\n // Find a panel that has matching vars; if none found, we can't restore.\n // The oldPanel and new panel must match on their mapping vars, and the\n // values.\n for (var i=0; i= bounds.xmin &&\n offset_css.y <= bounds.ymax && offset_css.y >= bounds.ymin;\n }\n\n // Return true if offset is inside a region to start a resize\n function isInResizeArea(offset_css) {\n var sides = whichResizeSides(offset_css);\n return sides.left || sides.right || sides.top || sides.bottom;\n }\n\n // Return an object representing which resize region(s) the cursor is in.\n function whichResizeSides(offset_css) {\n const b = state.boundsCss;\n // Bounds with expansion\n const e = {\n xmin: b.xmin - resizeExpand,\n xmax: b.xmax + resizeExpand,\n ymin: b.ymin - resizeExpand,\n ymax: b.ymax + resizeExpand\n };\n const res = {\n left: false,\n right: false,\n top: false,\n bottom: false\n };\n\n if ((opts.brushDirection === 'xy' || opts.brushDirection === 'x') &&\n (offset_css.y <= e.ymax && offset_css.y >= e.ymin))\n {\n if (offset_css.x < b.xmin && offset_css.x >= e.xmin)\n res.left = true;\n else if (offset_css.x > b.xmax && offset_css.x <= e.xmax)\n res.right = true;\n }\n\n if ((opts.brushDirection === 'xy' || opts.brushDirection === 'y') &&\n (offset_css.x <= e.xmax && offset_css.x >= e.xmin))\n {\n if (offset_css.y < b.ymin && offset_css.y >= e.ymin)\n res.top = true;\n else if (offset_css.y > b.ymax && offset_css.y <= e.ymax)\n res.bottom = true;\n }\n\n return res;\n }\n\n\n // Sets the bounds of the brush (in CSS pixels), given a box and optional\n // panel. This will fit the box bounds into the panel, so we don't brush\n // outside of it. This knows whether we're brushing in the x, y, or xy\n // directions, and sets bounds accordingly. If no box is passed in, just\n // return current bounds.\n function boundsCss(box_css) {\n if (box_css === undefined) {\n return $.extend({}, state.boundsCss);\n }\n\n let min_css = { x: box_css.xmin, y: box_css.ymin };\n let max_css = { x: box_css.xmax, y: box_css.ymax };\n\n const panel = state.panel;\n const panelBounds_img = panel.range;\n\n if (opts.brushClip) {\n min_css = imgToCss(panel.clipImg(cssToImg(min_css)));\n max_css = imgToCss(panel.clipImg(cssToImg(max_css)));\n }\n\n if (opts.brushDirection === 'xy') {\n // No change\n\n } else if (opts.brushDirection === 'x') {\n // Extend top and bottom of plotting area\n min_css.y = imgToCss({y: panelBounds_img.top }).y;\n max_css.y = imgToCss({y: panelBounds_img.bottom}).y;\n\n } else if (opts.brushDirection === 'y') {\n min_css.x = imgToCss({x: panelBounds_img.left }).x;\n max_css.x = imgToCss({x: panelBounds_img.right}).x;\n }\n\n state.boundsCss = {\n xmin: min_css.x,\n xmax: max_css.x,\n ymin: min_css.y,\n ymax: max_css.y\n };\n\n // Positions in data space\n const min_data = state.panel.scaleImgToData(cssToImg(min_css));\n const max_data = state.panel.scaleImgToData(cssToImg(max_css));\n // For reversed scales, the min and max can be reversed, so use findBox\n // to ensure correct order.\n state.boundsData = imageutils.findBox(min_data, max_data);\n // Round to 14 significant digits to avoid spurious changes in FP values\n // (#1634).\n state.boundsData = mapValues(state.boundsData, val => roundSignif(val, 14));\n\n // We also need to attach the data bounds and panel as data attributes, so\n // that if the image is re-sent, we can grab the data bounds to create a new\n // brush. This should be fast because it doesn't actually modify the DOM.\n $div.data('bounds-data', state.boundsData);\n $div.data('panel', state.panel);\n return undefined;\n }\n\n // Get or set the bounds of the brush using coordinates in the data space.\n function boundsData(box_data) {\n if (box_data === undefined) {\n return $.extend({}, state.boundsData);\n }\n\n let box_css = imgToCss(state.panel.scaleDataToImg(box_data));\n // Round to 13 significant digits to avoid spurious changes in FP values\n // (#2197).\n box_css = mapValues(box_css, val => roundSignif(val, 13));\n\n // The scaling function can reverse the direction of the axes, so we need to\n // find the min and max again.\n boundsCss({\n xmin: Math.min(box_css.xmin, box_css.xmax),\n xmax: Math.max(box_css.xmin, box_css.xmax),\n ymin: Math.min(box_css.ymin, box_css.ymax),\n ymax: Math.max(box_css.ymin, box_css.ymax)\n });\n return undefined;\n }\n\n function getPanel() {\n return state.panel;\n }\n\n // Add a new div representing the brush.\n function addDiv() {\n if ($div) $div.remove();\n\n // Start hidden; we'll show it when movement occurs\n $div = $(document.createElement('div'))\n .attr('id', el.id + '_brush')\n .css({\n 'background-color': opts.brushFill,\n 'opacity': opts.brushOpacity,\n 'pointer-events': 'none',\n 'position': 'absolute'\n })\n .hide();\n\n var borderStyle = '1px solid ' + opts.brushStroke;\n if (opts.brushDirection === 'xy') {\n $div.css({\n 'border': borderStyle\n });\n } else if (opts.brushDirection === 'x') {\n $div.css({\n 'border-left': borderStyle,\n 'border-right': borderStyle\n });\n } else if (opts.brushDirection === 'y') {\n $div.css({\n 'border-top': borderStyle,\n 'border-bottom': borderStyle\n });\n }\n\n $el.append($div);\n $div.offset({x:0, y:0}).width(0).outerHeight(0);\n }\n\n // Update the brush div to reflect the current brush bounds.\n function updateDiv() {\n // Need parent offset relative to page to calculate mouse offset\n // relative to page.\n const img_offset_css = findOrigin($el.find(\"img\"));\n const b = state.boundsCss;\n\n $div.offset({\n top: img_offset_css.y + b.ymin,\n left: img_offset_css.x + b.xmin\n })\n .outerWidth(b.xmax - b.xmin + 1)\n .outerHeight(b.ymax - b.ymin + 1);\n }\n\n function down(offset_css) {\n if (offset_css === undefined)\n return state.down;\n\n state.down = offset_css;\n return undefined;\n }\n\n function up(offset_css) {\n if (offset_css === undefined)\n return state.up;\n\n state.up = offset_css;\n return undefined;\n }\n\n function isBrushing() {\n return state.brushing;\n }\n\n function startBrushing() {\n state.brushing = true;\n addDiv();\n state.panel = coordmap.getPanelCss(state.down, expandPixels);\n\n boundsCss(imageutils.findBox(state.down, state.down));\n updateDiv();\n }\n\n function brushTo(offset_css) {\n boundsCss(imageutils.findBox(state.down, offset_css));\n $div.show();\n updateDiv();\n }\n\n function stopBrushing() {\n state.brushing = false;\n // Save the final bounding box of the brush\n boundsCss(imageutils.findBox(state.down, state.up));\n }\n\n function isDragging() {\n return state.dragging;\n }\n\n function startDragging() {\n state.dragging = true;\n state.changeStartBounds = $.extend({}, state.boundsCss);\n }\n\n function dragTo(offset_css) {\n // How far the brush was dragged\n const dx = offset_css.x - state.down.x;\n const dy = offset_css.y - state.down.y;\n\n // Calculate what new positions would be, before clipping.\n const start = state.changeStartBounds;\n let newBounds_css = {\n xmin: start.xmin + dx,\n xmax: start.xmax + dx,\n ymin: start.ymin + dy,\n ymax: start.ymax + dy\n };\n\n // Clip to the plotting area\n if (opts.brushClip) {\n const panelBounds_img = state.panel.range;\n const newBounds_img = cssToImg(newBounds_css);\n\n // Convert to format for shiftToRange\n let xvals_img = [ newBounds_img.xmin, newBounds_img.xmax ];\n let yvals_img = [ newBounds_img.ymin, newBounds_img.ymax ];\n\n xvals_img = imageutils.shiftToRange(xvals_img, panelBounds_img.left, panelBounds_img.right);\n yvals_img = imageutils.shiftToRange(yvals_img, panelBounds_img.top, panelBounds_img.bottom);\n\n // Convert back to bounds format\n newBounds_css = imgToCss({\n xmin: xvals_img[0],\n xmax: xvals_img[1],\n ymin: yvals_img[0],\n ymax: yvals_img[1]\n });\n }\n\n boundsCss(newBounds_css);\n updateDiv();\n }\n\n function stopDragging() {\n state.dragging = false;\n }\n\n function isResizing() {\n return state.resizing;\n }\n\n function startResizing() {\n state.resizing = true;\n state.changeStartBounds = $.extend({}, state.boundsCss);\n state.resizeSides = whichResizeSides(state.down);\n }\n\n function resizeTo(offset_css) {\n // How far the brush was dragged\n const d_css = {\n x: offset_css.x - state.down.x,\n y: offset_css.y - state.down.y\n };\n\n const d_img = cssToImg(d_css);\n\n // Calculate what new positions would be, before clipping.\n const b_img = cssToImg(state.changeStartBounds);\n const panelBounds_img = state.panel.range;\n\n if (state.resizeSides.left) {\n const xmin_img = imageutils.shiftToRange(b_img.xmin + d_img.x, panelBounds_img.left, b_img.xmax)[0];\n b_img.xmin = xmin_img;\n } else if (state.resizeSides.right) {\n const xmax_img = imageutils.shiftToRange(b_img.xmax + d_img.x, b_img.xmin, panelBounds_img.right)[0];\n b_img.xmax = xmax_img;\n }\n\n if (state.resizeSides.top) {\n const ymin_img = imageutils.shiftToRange(b_img.ymin + d_img.y, panelBounds_img.top, b_img.ymax)[0];\n b_img.ymin = ymin_img;\n } else if (state.resizeSides.bottom) {\n const ymax_img = imageutils.shiftToRange(b_img.ymax + d_img.y, b_img.ymin, panelBounds_img.bottom)[0];\n b_img.ymax = ymax_img;\n }\n\n boundsCss(imgToCss(b_img));\n updateDiv();\n }\n\n function stopResizing() {\n state.resizing = false;\n }\n\n return {\n reset: reset,\n\n importOldBrush: importOldBrush,\n isInsideBrush: isInsideBrush,\n isInResizeArea: isInResizeArea,\n whichResizeSides: whichResizeSides,\n\n onResize: onResize, // A callback when the wrapper div or img is resized.\n\n boundsCss: boundsCss,\n boundsData: boundsData,\n getPanel: getPanel,\n\n down: down,\n up: up,\n\n isBrushing: isBrushing,\n startBrushing: startBrushing,\n brushTo: brushTo,\n stopBrushing: stopBrushing,\n\n isDragging: isDragging,\n startDragging: startDragging,\n dragTo: dragTo,\n stopDragging: stopDragging,\n\n isResizing: isResizing,\n startResizing: startResizing,\n resizeTo: resizeTo,\n stopResizing: stopResizing\n };\n};\n\nexports.resetBrush = function(brushId) {\n exports.setInputValue(brushId, null);\n imageOutputBinding.find(document).trigger(\"shiny-internal:brushed\", {\n brushId: brushId, outputId: null\n });\n};\n\n\n// -----------------------------------------------------------------------\n// Utility functions for finding dimensions and locations of DOM elements\n// -----------------------------------------------------------------------\n\n// Returns the ratio that an element has been scaled (for example, by CSS\n// transforms) in the x and y directions.\nfunction findScalingRatio($el) {\n const boundingRect = $el[0].getBoundingClientRect();\n return {\n x: boundingRect.width / $el.outerWidth(),\n y: boundingRect.height / $el.outerHeight()\n };\n}\n\nfunction findOrigin($el) {\n const offset = $el.offset();\n const scaling_ratio = findScalingRatio($el);\n\n // Find the size of the padding and border, for the top and left. This is\n // before any transforms.\n const paddingBorder = {\n left: parseInt($el.css(\"border-left-width\")) + parseInt($el.css(\"padding-left\")),\n top: parseInt($el.css(\"border-top-width\")) + parseInt($el.css(\"padding-top\"))\n };\n\n // offset() returns the upper left corner of the element relative to the\n // page, but it includes padding and border. Here we find the upper left\n // of the element, not including padding and border.\n return {\n x: offset.left + scaling_ratio.x * paddingBorder.left,\n y: offset.top + scaling_ratio.y * paddingBorder.top\n };\n}\n\n// Find the dimensions of a tag, after transforms, and without padding and\n// border.\nfunction findDims($el) {\n // If there's any padding/border, we need to find the ratio of the actual\n // element content compared to the element plus padding and border.\n const content_ratio = {\n x: $el.width() / $el.outerWidth(),\n y: $el.height() / $el.outerHeight()\n };\n\n // Get the dimensions of the element _after_ any CSS transforms. This\n // includes the padding and border.\n const bounding_rect = $el[0].getBoundingClientRect();\n\n // Dimensions of the element after any CSS transforms, and without\n // padding/border.\n return {\n x: content_ratio.x * bounding_rect.width,\n y: content_ratio.y * bounding_rect.height\n };\n}\n","//---------------------------------------------------------------------\n// Source file: ../srcjs/output_binding_html.js\n\nvar htmlOutputBinding = new OutputBinding();\n$.extend(htmlOutputBinding, {\n find: function(scope) {\n return $(scope).find('.shiny-html-output');\n },\n onValueError: function(el, err) {\n exports.unbindAll(el);\n this.renderError(el, err);\n },\n renderValue: function(el, data) {\n exports.renderContent(el, data);\n }\n});\noutputBindings.register(htmlOutputBinding, 'shiny.htmlOutput');\n\nvar renderDependencies = exports.renderDependencies = function(dependencies) {\n if (dependencies) {\n $.each(dependencies, function(i, dep) {\n renderDependency(dep);\n });\n }\n};\n\n// Render HTML in a DOM element, add dependencies, and bind Shiny\n// inputs/outputs. `content` can be null, a string, or an object with\n// properties 'html' and 'deps'.\nexports.renderContent = function(el, content, where=\"replace\") {\n if (where === \"replace\") {\n exports.unbindAll(el);\n }\n\n var html;\n var dependencies = [];\n if (content === null) {\n html = '';\n } else if (typeof(content) === 'string') {\n html = content;\n } else if (typeof(content) === 'object') {\n html = content.html;\n dependencies = content.deps || [];\n }\n\n exports.renderHtml(html, el, dependencies, where);\n\n var scope = el;\n if (where === \"replace\") {\n exports.initializeInputs(el);\n exports.bindAll(el);\n } else {\n var $parent = $(el).parent();\n if ($parent.length > 0) {\n scope = $parent;\n if (where === \"beforeBegin\" || where === \"afterEnd\") {\n var $grandparent = $parent.parent();\n if ($grandparent.length > 0) scope = $grandparent;\n }\n }\n exports.initializeInputs(scope);\n exports.bindAll(scope);\n }\n};\n\n// Render HTML in a DOM element, inserting singletons into head as needed\nexports.renderHtml = function(html, el, dependencies, where = 'replace') {\n renderDependencies(dependencies);\n return singletons.renderHtml(html, el, where);\n};\n\nvar htmlDependencies = {};\nfunction registerDependency(name, version) {\n htmlDependencies[name] = version;\n}\n\n// Client-side dependency resolution and rendering\nfunction renderDependency(dep) {\n if (htmlDependencies.hasOwnProperty(dep.name))\n return false;\n\n registerDependency(dep.name, dep.version);\n\n var href = dep.src.href;\n\n var $head = $(\"head\").first();\n\n if (dep.meta) {\n var metas = $.map(asArray(dep.meta), function(obj, idx) {\n // only one named pair is expected in obj as it's already been decomposed\n var name = Object.keys(obj)[0];\n return $(\"\").attr(\"name\", name).attr(\"content\", obj[name]);\n });\n $head.append(metas);\n }\n\n if (dep.stylesheet) {\n var stylesheets = $.map(asArray(dep.stylesheet), function(stylesheet) {\n return $(\"\")\n .attr(\"href\", href + \"/\" + encodeURI(stylesheet));\n });\n $head.append(stylesheets);\n }\n\n if (dep.script) {\n var scripts = $.map(asArray(dep.script), function(scriptName) {\n return $(\"