Plugin Directory

Changeset 3734102


Ignore:
Timestamp:
10/08/2026 09:17:45 AM (3 weeks ago)
Author:
dfactory
Message:

Tagging version 1.8.0

Location:
post-views-counter/tags/1.8.0
Files:
5 added
15 deleted
73 copied

Legend:

Unmodified
Added
Removed
  • post-views-counter/tags/1.8.0/assets/chartjs/chart.min.js

    r3378305 r3734102  
    11/*!
    2  * Chart.js v4.5.0
     2 * Chart.js v4.5.1
    33 * https://www.chartjs.org
    44 * (c) 2025 Chart.js Contributors
    … …  
    1111 * (c) 2023 Jukka Kurkela
    1212 * Released under the MIT License
    13  */function _t(t){return t+.5|0}const yt=(t,e,i)=>Math.max(Math.min(t,i),e);function vt(t){return yt(_t(2.55*t),0,255)}function Mt(t){return yt(_t(255*t),0,255)}function wt(t){return yt(_t(t/2.55)/100,0,1)}function kt(t){return yt(_t(100*t),0,100)}const St={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Pt=[..."0123456789ABCDEF"],Dt=t=>Pt[15&t],Ct=t=>Pt[(240&t)>>4]+Pt[15&t],Ot=t=>(240&t)>>4==(15&t);function At(t){var e=(t=>Ot(t.r)&&Ot(t.g)&&Ot(t.b)&&Ot(t.a))(t)?Dt:Ct;return t?"#"+e(t.r)+e(t.g)+e(t.b)+((t,e)=>t<255?e(t):"")(t.a,e):void 0}const Tt=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function Lt(t,e,i){const s=e*Math.min(i,1-i),n=(e,n=(e+t/30)%12)=>i-s*Math.max(Math.min(n-3,9-n,1),-1);return[n(0),n(8),n(4)]}function Et(t,e,i){const s=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return[s(5),s(3),s(1)]}function Rt(t,e,i){const s=Lt(t,1,.5);let n;for(e+i>1&&(n=1/(e+i),e*=n,i*=n),n=0;n<3;n++)s[n]*=1-e-i,s[n]+=e;return s}function It(t){const e=t.r/255,i=t.g/255,s=t.b/255,n=Math.max(e,i,s),o=Math.min(e,i,s),a=(n+o)/2;let r,l,h;return n!==o&&(h=n-o,l=a>.5?h/(2-n-o):h/(n+o),r=function(t,e,i,s,n){return t===n?(e-i)/s+(e<i?6:0):e===n?(i-t)/s+2:(t-e)/s+4}(e,i,s,h,n),r=60*r+.5),[0|r,l||0,a]}function zt(t,e,i,s){return(Array.isArray(e)?t(e[0],e[1],e[2]):t(e,i,s)).map(Mt)}function Ft(t,e,i){return zt(Lt,t,e,i)}function Vt(t){return(t%360+360)%360}function Bt(t){const e=Tt.exec(t);let i,s=255;if(!e)return;e[5]!==i&&(s=e[6]?vt(+e[5]):Mt(+e[5]));const n=Vt(+e[2]),o=+e[3]/100,a=+e[4]/100;return i="hwb"===e[1]?function(t,e,i){return zt(Rt,t,e,i)}(n,o,a):"hsv"===e[1]?function(t,e,i){return zt(Et,t,e,i)}(n,o,a):Ft(n,o,a),{r:i[0],g:i[1],b:i[2],a:s}}const Wt={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},Nt={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};let Ht;function jt(t){Ht||(Ht=function(){const t={},e=Object.keys(Nt),i=Object.keys(Wt);let s,n,o,a,r;for(s=0;s<e.length;s++){for(a=r=e[s],n=0;n<i.length;n++)o=i[n],r=r.replace(o,Wt[o]);o=parseInt(Nt[a],16),t[r]=[o>>16&255,o>>8&255,255&o]}return t}(),Ht.transparent=[0,0,0,0]);const e=Ht[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}const $t=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;const Yt=t=>t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055,Ut=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4);function Xt(t,e,i){if(t){let s=It(t);s[e]=Math.max(0,Math.min(s[e]+s[e]*i,0===e?360:1)),s=Ft(s),t.r=s[0],t.g=s[1],t.b=s[2]}}function qt(t,e){return t?Object.assign(e||{},t):t}function Kt(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=Mt(t[3]))):(e=qt(t,{r:0,g:0,b:0,a:1})).a=Mt(e.a),e}function Gt(t){return"r"===t.charAt(0)?function(t){const e=$t.exec(t);let i,s,n,o=255;if(e){if(e[7]!==i){const t=+e[7];o=e[8]?vt(t):yt(255*t,0,255)}return i=+e[1],s=+e[3],n=+e[5],i=255&(e[2]?vt(i):yt(i,0,255)),s=255&(e[4]?vt(s):yt(s,0,255)),n=255&(e[6]?vt(n):yt(n,0,255)),{r:i,g:s,b:n,a:o}}}(t):Bt(t)}class Jt{constructor(t){if(t instanceof Jt)return t;const e=typeof t;let i;var s,n,o;"object"===e?i=Kt(t):"string"===e&&(o=(s=t).length,"#"===s[0]&&(4===o||5===o?n={r:255&17*St[s[1]],g:255&17*St[s[2]],b:255&17*St[s[3]],a:5===o?17*St[s[4]]:255}:7!==o&&9!==o||(n={r:St[s[1]]<<4|St[s[2]],g:St[s[3]]<<4|St[s[4]],b:St[s[5]]<<4|St[s[6]],a:9===o?St[s[7]]<<4|St[s[8]]:255})),i=n||jt(t)||Gt(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=qt(this._rgb);return t&&(t.a=wt(t.a)),t}set rgb(t){this._rgb=Kt(t)}rgbString(){return this._valid?(t=this._rgb)&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${wt(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`):void 0;var t}hexString(){return this._valid?At(this._rgb):void 0}hslString(){return this._valid?function(t){if(!t)return;const e=It(t),i=e[0],s=kt(e[1]),n=kt(e[2]);return t.a<255?`hsla(${i}, ${s}%, ${n}%, ${wt(t.a)})`:`hsl(${i}, ${s}%, ${n}%)`}(this._rgb):void 0}mix(t,e){if(t){const i=this.rgb,s=t.rgb;let n;const o=e===n?.5:e,a=2*o-1,r=i.a-s.a,l=((a*r==-1?a:(a+r)/(1+a*r))+1)/2;n=1-l,i.r=255&l*i.r+n*s.r+.5,i.g=255&l*i.g+n*s.g+.5,i.b=255&l*i.b+n*s.b+.5,i.a=o*i.a+(1-o)*s.a,this.rgb=i}return this}interpolate(t,e){return t&&(this._rgb=function(t,e,i){const s=Ut(wt(t.r)),n=Ut(wt(t.g)),o=Ut(wt(t.b));return{r:Mt(Yt(s+i*(Ut(wt(e.r))-s))),g:Mt(Yt(n+i*(Ut(wt(e.g))-n))),b:Mt(Yt(o+i*(Ut(wt(e.b))-o))),a:t.a+i*(e.a-t.a)}}(this._rgb,t._rgb,e)),this}clone(){return new Jt(this.rgb)}alpha(t){return this._rgb.a=Mt(t),this}clearer(t){return this._rgb.a*=1-t,this}greyscale(){const t=this._rgb,e=_t(.3*t.r+.59*t.g+.11*t.b);return t.r=t.g=t.b=e,this}opaquer(t){return this._rgb.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return Xt(this._rgb,2,t),this}darken(t){return Xt(this._rgb,2,-t),this}saturate(t){return Xt(this._rgb,1,t),this}desaturate(t){return Xt(this._rgb,1,-t),this}rotate(t){return function(t,e){var i=It(t);i[0]=Vt(i[0]+e),i=Ft(i),t.r=i[0],t.g=i[1],t.b=i[2]}(this._rgb,t),this}}function Zt(t){if(t&&"object"==typeof t){const e=t.toString();return"[object CanvasPattern]"===e||"[object CanvasGradient]"===e}return!1}function Qt(t){return Zt(t)?t:new Jt(t)}function te(t){return Zt(t)?t:new Jt(t).saturate(.5).darken(.1).hexString()}const ee=["x","y","borderWidth","radius","tension"],ie=["color","borderColor","backgroundColor"];const se=new Map;function ne(t,e,i){return function(t,e){e=e||{};const i=t+JSON.stringify(e);let s=se.get(i);return s||(s=new Intl.NumberFormat(t,e),se.set(i,s)),s}(e,i).format(t)}const oe={values:t=>n(t)?t:""+t,numeric(t,e,i){if(0===t)return"0";const s=this.chart.options.locale;let n,o=t;if(i.length>1){const e=Math.max(Math.abs(i[0].value),Math.abs(i[i.length-1].value));(e<1e-4||e>1e15)&&(n="scientific"),o=function(t,e){let i=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;Math.abs(i)>=1&&t!==Math.floor(t)&&(i=t-Math.floor(t));return i}(t,i)}const a=z(Math.abs(o)),r=isNaN(a)?1:Math.max(Math.min(-1*Math.floor(a),20),0),l={notation:n,minimumFractionDigits:r,maximumFractionDigits:r};return Object.assign(l,this.options.ticks.format),ne(t,s,l)},logarithmic(t,e,i){if(0===t)return"0";const s=i[e].significand||t/Math.pow(10,Math.floor(z(t)));return[1,2,3,5,10,15].includes(s)||e>.8*i.length?oe.numeric.call(this,t,e,i):""}};var ae={formatters:oe};const re=Object.create(null),le=Object.create(null);function he(t,e){if(!e)return t;const i=e.split(".");for(let e=0,s=i.length;e<s;++e){const s=i[e];t=t[s]||(t[s]=Object.create(null))}return t}function ce(t,e,i){return"string"==typeof e?x(he(t,e),i):x(he(t,""),e)}class de{constructor(t,e){this.animation=void 0,this.backgroundColor="rgba(0,0,0,0.1)",this.borderColor="rgba(0,0,0,0.1)",this.color="#666",this.datasets={},this.devicePixelRatio=t=>t.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(t,e)=>te(e.backgroundColor),this.hoverBorderColor=(t,e)=>te(e.borderColor),this.hoverColor=(t,e)=>te(e.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ce(this,t,e)}get(t){return he(this,t)}describe(t,e){return ce(le,t,e)}override(t,e){return ce(re,t,e)}route(t,e,i,s){const n=he(this,t),a=he(this,i),r="_"+e;Object.defineProperties(n,{[r]:{value:n[e],writable:!0},[e]:{enumerable:!0,get(){const t=this[r],e=a[s];return o(t)?Object.assign({},e,t):l(t,e)},set(t){this[r]=t}}})}apply(t){t.forEach((t=>t(this)))}}var ue=new de({_scriptable:t=>!t.startsWith("on"),_indexable:t=>"events"!==t,hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[function(t){t.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),t.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>"onProgress"!==t&&"onComplete"!==t&&"fn"!==t}),t.set("animations",{colors:{type:"color",properties:ie},numbers:{type:"number",properties:ee}}),t.describe("animations",{_fallback:"animation"}),t.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>0|t}}}})},function(t){t.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})},function(t){t.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:ae.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),t.route("scale.ticks","color","","color"),t.route("scale.grid","color","","borderColor"),t.route("scale.border","color","","borderColor"),t.route("scale.title","color","","color"),t.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&"callback"!==t&&"parser"!==t,_indexable:t=>"borderDash"!==t&&"tickBorderDash"!==t&&"dash"!==t}),t.describe("scales",{_fallback:"scale"}),t.describe("scale.ticks",{_scriptable:t=>"backdropPadding"!==t&&"callback"!==t,_indexable:t=>"backdropPadding"!==t})}]);function fe(){return"undefined"!=typeof window&&"undefined"!=typeof document}function ge(t){let e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e}function pe(t,e,i){let s;return"string"==typeof t?(s=parseInt(t,10),-1!==t.indexOf("%")&&(s=s/100*e.parentNode[i])):s=t,s}const me=t=>t.ownerDocument.defaultView.getComputedStyle(t,null);function xe(t,e){return me(t).getPropertyValue(e)}const be=["top","right","bottom","left"];function _e(t,e,i){const s={};i=i?"-"+i:"";for(let n=0;n<4;n++){const o=be[n];s[o]=parseFloat(t[e+"-"+o+i])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const ye=(t,e,i)=>(t>0||e>0)&&(!i||!i.shadowRoot);function ve(t,e){if("native"in t)return t;const{canvas:i,currentDevicePixelRatio:s}=e,n=me(i),o="border-box"===n.boxSizing,a=_e(n,"padding"),r=_e(n,"border","width"),{x:l,y:h,box:c}=function(t,e){const i=t.touches,s=i&&i.length?i[0]:t,{offsetX:n,offsetY:o}=s;let a,r,l=!1;if(ye(n,o,t.target))a=n,r=o;else{const t=e.getBoundingClientRect();a=s.clientX-t.left,r=s.clientY-t.top,l=!0}return{x:a,y:r,box:l}}(t,i),d=a.left+(c&&r.left),u=a.top+(c&&r.top);let{width:f,height:g}=e;return o&&(f-=a.width+r.width,g-=a.height+r.height),{x:Math.round((l-d)/f*i.width/s),y:Math.round((h-u)/g*i.height/s)}}const Me=t=>Math.round(10*t)/10;function we(t,e,i,s){const n=me(t),o=_e(n,"margin"),a=pe(n.maxWidth,t,"clientWidth")||T,r=pe(n.maxHeight,t,"clientHeight")||T,l=function(t,e,i){let s,n;if(void 0===e||void 0===i){const o=t&&ge(t);if(o){const t=o.getBoundingClientRect(),a=me(o),r=_e(a,"border","width"),l=_e(a,"padding");e=t.width-l.width-r.width,i=t.height-l.height-r.height,s=pe(a.maxWidth,o,"clientWidth"),n=pe(a.maxHeight,o,"clientHeight")}else e=t.clientWidth,i=t.clientHeight}return{width:e,height:i,maxWidth:s||T,maxHeight:n||T}}(t,e,i);let{width:h,height:c}=l;if("content-box"===n.boxSizing){const t=_e(n,"border","width"),e=_e(n,"padding");h-=e.width+t.width,c-=e.height+t.height}h=Math.max(0,h-o.width),c=Math.max(0,s?h/s:c-o.height),h=Me(Math.min(h,a,l.maxWidth)),c=Me(Math.min(c,r,l.maxHeight)),h&&!c&&(c=Me(h/2));return(void 0!==e||void 0!==i)&&s&&l.height&&c>l.height&&(c=l.height,h=Me(Math.floor(c*s))),{width:h,height:c}}function ke(t,e,i){const s=e||1,n=Math.floor(t.height*s),o=Math.floor(t.width*s);t.height=Math.floor(t.height),t.width=Math.floor(t.width);const a=t.canvas;return a.style&&(i||!a.style.height&&!a.style.width)&&(a.style.height=`${t.height}px`,a.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==s||a.height!==n||a.width!==o)&&(t.currentDevicePixelRatio=s,a.height=n,a.width=o,t.ctx.setTransform(s,0,0,s,0,0),!0)}const Se=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};fe()&&(window.addEventListener("test",null,e),window.removeEventListener("test",null,e))}catch(t){}return t}();function Pe(t,e){const i=xe(t,e),s=i&&i.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function De(t){return!t||s(t.size)||s(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}function Ce(t,e,i,s,n){let o=e[n];return o||(o=e[n]=t.measureText(n).width,i.push(n)),o>s&&(s=o),s}function Oe(t,e,i,s){let o=(s=s||{}).data=s.data||{},a=s.garbageCollect=s.garbageCollect||[];s.font!==e&&(o=s.data={},a=s.garbageCollect=[],s.font=e),t.save(),t.font=e;let r=0;const l=i.length;let h,c,d,u,f;for(h=0;h<l;h++)if(u=i[h],null==u||n(u)){if(n(u))for(c=0,d=u.length;c<d;c++)f=u[c],null==f||n(f)||(r=Ce(t,o,a,r,f))}else r=Ce(t,o,a,r,u);t.restore();const g=a.length/2;if(g>i.length){for(h=0;h<g;h++)delete o[a[h]];a.splice(0,g)}return r}function Ae(t,e,i){const s=t.currentDevicePixelRatio,n=0!==i?Math.max(i/2,.5):0;return Math.round((e-n)*s)/s+n}function Te(t,e){(e||t)&&((e=e||t.getContext("2d")).save(),e.resetTransform(),e.clearRect(0,0,t.width,t.height),e.restore())}function Le(t,e,i,s){Ee(t,e,i,s,null)}function Ee(t,e,i,s,n){let o,a,r,l,h,c,d,u;const f=e.pointStyle,g=e.rotation,p=e.radius;let m=(g||0)*L;if(f&&"object"==typeof f&&(o=f.toString(),"[object HTMLImageElement]"===o||"[object HTMLCanvasElement]"===o))return t.save(),t.translate(i,s),t.rotate(m),t.drawImage(f,-f.width/2,-f.height/2,f.width,f.height),void t.restore();if(!(isNaN(p)||p<=0)){switch(t.beginPath(),f){default:n?t.ellipse(i,s,n/2,p,0,0,O):t.arc(i,s,p,0,O),t.closePath();break;case"triangle":c=n?n/2:p,t.moveTo(i+Math.sin(m)*c,s-Math.cos(m)*p),m+=I,t.lineTo(i+Math.sin(m)*c,s-Math.cos(m)*p),m+=I,t.lineTo(i+Math.sin(m)*c,s-Math.cos(m)*p),t.closePath();break;case"rectRounded":h=.516*p,l=p-h,a=Math.cos(m+R)*l,d=Math.cos(m+R)*(n?n/2-h:l),r=Math.sin(m+R)*l,u=Math.sin(m+R)*(n?n/2-h:l),t.arc(i-d,s-r,h,m-C,m-E),t.arc(i+u,s-a,h,m-E,m),t.arc(i+d,s+r,h,m,m+E),t.arc(i-u,s+a,h,m+E,m+C),t.closePath();break;case"rect":if(!g){l=Math.SQRT1_2*p,c=n?n/2:l,t.rect(i-c,s-l,2*c,2*l);break}m+=R;case"rectRot":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+u,s-a),t.lineTo(i+d,s+r),t.lineTo(i-u,s+a),t.closePath();break;case"crossRot":m+=R;case"cross":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a);break;case"star":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a),m+=R,d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a);break;case"line":a=n?n/2:Math.cos(m)*p,r=Math.sin(m)*p,t.moveTo(i-a,s-r),t.lineTo(i+a,s+r);break;case"dash":t.moveTo(i,s),t.lineTo(i+Math.cos(m)*(n?n/2:p),s+Math.sin(m)*p);break;case!1:t.closePath()}t.fill(),e.borderWidth>0&&t.stroke()}}function Re(t,e,i){return i=i||.5,!e||t&&t.x>e.left-i&&t.x<e.right+i&&t.y>e.top-i&&t.y<e.bottom+i}function Ie(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()}function ze(t){t.restore()}function Fe(t,e,i,s,n){if(!e)return t.lineTo(i.x,i.y);if("middle"===n){const s=(e.x+i.x)/2;t.lineTo(s,e.y),t.lineTo(s,i.y)}else"after"===n!=!!s?t.lineTo(e.x,i.y):t.lineTo(i.x,e.y);t.lineTo(i.x,i.y)}function Ve(t,e,i,s){if(!e)return t.lineTo(i.x,i.y);t.bezierCurveTo(s?e.cp1x:e.cp2x,s?e.cp1y:e.cp2y,s?i.cp2x:i.cp1x,s?i.cp2y:i.cp1y,i.x,i.y)}function Be(t,e,i,s,n){if(n.strikethrough||n.underline){const o=t.measureText(s),a=e-o.actualBoundingBoxLeft,r=e+o.actualBoundingBoxRight,l=i-o.actualBoundingBoxAscent,h=i+o.actualBoundingBoxDescent,c=n.strikethrough?(l+h)/2:h;t.strokeStyle=t.fillStyle,t.beginPath(),t.lineWidth=n.decorationWidth||2,t.moveTo(a,c),t.lineTo(r,c),t.stroke()}}function We(t,e){const i=t.fillStyle;t.fillStyle=e.color,t.fillRect(e.left,e.top,e.width,e.height),t.fillStyle=i}function Ne(t,e,i,o,a,r={}){const l=n(e)?e:[e],h=r.strokeWidth>0&&""!==r.strokeColor;let c,d;for(t.save(),t.font=a.string,function(t,e){e.translation&&t.translate(e.translation[0],e.translation[1]),s(e.rotation)||t.rotate(e.rotation),e.color&&(t.fillStyle=e.color),e.textAlign&&(t.textAlign=e.textAlign),e.textBaseline&&(t.textBaseline=e.textBaseline)}(t,r),c=0;c<l.length;++c)d=l[c],r.backdrop&&We(t,r.backdrop),h&&(r.strokeColor&&(t.strokeStyle=r.strokeColor),s(r.strokeWidth)||(t.lineWidth=r.strokeWidth),t.strokeText(d,i,o,r.maxWidth)),t.fillText(d,i,o,r.maxWidth),Be(t,i,o,d,r),o+=Number(a.lineHeight);t.restore()}function He(t,e){const{x:i,y:s,w:n,h:o,radius:a}=e;t.arc(i+a.topLeft,s+a.topLeft,a.topLeft,1.5*C,C,!0),t.lineTo(i,s+o-a.bottomLeft),t.arc(i+a.bottomLeft,s+o-a.bottomLeft,a.bottomLeft,C,E,!0),t.lineTo(i+n-a.bottomRight,s+o),t.arc(i+n-a.bottomRight,s+o-a.bottomRight,a.bottomRight,E,0,!0),t.lineTo(i+n,s+a.topRight),t.arc(i+n-a.topRight,s+a.topRight,a.topRight,0,-E,!0),t.lineTo(i+a.topLeft,s)}function je(t,e=[""],i,s,n=(()=>t[0])){const o=i||t;void 0===s&&(s=ti("_fallback",t));const a={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:t,_rootScopes:o,_fallback:s,_getTarget:n,override:i=>je([i,...t],e,o,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete e._keys,delete t[0][i],!0),get:(i,s)=>qe(i,s,(()=>function(t,e,i,s){let n;for(const o of e)if(n=ti(Ue(o,t),i),void 0!==n)return Xe(t,n)?Ze(i,s,t,n):n}(s,e,t,i))),getOwnPropertyDescriptor:(t,e)=>Reflect.getOwnPropertyDescriptor(t._scopes[0],e),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(t,e)=>ei(t).includes(e),ownKeys:t=>ei(t),set(t,e,i){const s=t._storage||(t._storage=n());return t[e]=s[e]=i,delete t._keys,!0}})}function $e(t,e,i,s){const a={_cacheable:!1,_proxy:t,_context:e,_subProxy:i,_stack:new Set,_descriptors:Ye(t,s),setContext:e=>$e(t,e,i,s),override:n=>$e(t.override(n),e,i,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete t[i],!0),get:(t,e,i)=>qe(t,e,(()=>function(t,e,i){const{_proxy:s,_context:a,_subProxy:r,_descriptors:l}=t;let h=s[e];S(h)&&l.isScriptable(e)&&(h=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=i;if(r.has(t))throw new Error("Recursion detected: "+Array.from(r).join("->")+"->"+t);r.add(t);let l=e(o,a||s);r.delete(t),Xe(t,l)&&(l=Ze(n._scopes,n,t,l));return l}(e,h,t,i));n(h)&&h.length&&(h=function(t,e,i,s){const{_proxy:n,_context:a,_subProxy:r,_descriptors:l}=i;if(void 0!==a.index&&s(t))return e[a.index%e.length];if(o(e[0])){const i=e,s=n._scopes.filter((t=>t!==i));e=[];for(const o of i){const i=Ze(s,n,t,o);e.push($e(i,a,r&&r[t],l))}}return e}(e,h,t,l.isIndexable));Xe(e,h)&&(h=$e(h,a,r&&r[e],l));return h}(t,e,i))),getOwnPropertyDescriptor:(e,i)=>e._descriptors.allKeys?Reflect.has(t,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,i),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(e,i)=>Reflect.has(t,i),ownKeys:()=>Reflect.ownKeys(t),set:(e,i,s)=>(t[i]=s,delete e[i],!0)})}function Ye(t,e={scriptable:!0,indexable:!0}){const{_scriptable:i=e.scriptable,_indexable:s=e.indexable,_allKeys:n=e.allKeys}=t;return{allKeys:n,scriptable:i,indexable:s,isScriptable:S(i)?i:()=>i,isIndexable:S(s)?s:()=>s}}const Ue=(t,e)=>t?t+w(e):e,Xe=(t,e)=>o(e)&&"adapters"!==t&&(null===Object.getPrototypeOf(e)||e.constructor===Object);function qe(t,e,i){if(Object.prototype.hasOwnProperty.call(t,e)||"constructor"===e)return t[e];const s=i();return t[e]=s,s}function Ke(t,e,i){return S(t)?t(e,i):t}const Ge=(t,e)=>!0===t?e:"string"==typeof t?M(e,t):void 0;function Je(t,e,i,s,n){for(const o of e){const e=Ge(i,o);if(e){t.add(e);const o=Ke(e._fallback,i,n);if(void 0!==o&&o!==i&&o!==s)return o}else if(!1===e&&void 0!==s&&i!==s)return null}return!1}function Ze(t,e,i,s){const a=e._rootScopes,r=Ke(e._fallback,i,s),l=[...t,...a],h=new Set;h.add(s);let c=Qe(h,l,i,r||i,s);return null!==c&&((void 0===r||r===i||(c=Qe(h,l,r,c,s),null!==c))&&je(Array.from(h),[""],a,r,(()=>function(t,e,i){const s=t._getTarget();e in s||(s[e]={});const a=s[e];if(n(a)&&o(i))return i;return a||{}}(e,i,s))))}function Qe(t,e,i,s,n){for(;i;)i=Je(t,e,i,s,n);return i}function ti(t,e){for(const i of e){if(!i)continue;const e=i[t];if(void 0!==e)return e}}function ei(t){let e=t._keys;return e||(e=t._keys=function(t){const e=new Set;for(const i of t)for(const t of Object.keys(i).filter((t=>!t.startsWith("_"))))e.add(t);return Array.from(e)}(t._scopes)),e}function ii(t,e,i,s){const{iScale:n}=t,{key:o="r"}=this._parsing,a=new Array(s);let r,l,h,c;for(r=0,l=s;r<l;++r)h=r+i,c=e[h],a[r]={r:n.parse(M(c,o),h)};return a}const si=Number.EPSILON||1e-14,ni=(t,e)=>e<t.length&&!t[e].skip&&t[e],oi=t=>"x"===t?"y":"x";function ai(t,e,i,s){const n=t.skip?e:t,o=e,a=i.skip?e:i,r=q(o,n),l=q(a,o);let h=r/(r+l),c=l/(r+l);h=isNaN(h)?0:h,c=isNaN(c)?0:c;const d=s*h,u=s*c;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+u*(a.x-n.x),y:o.y+u*(a.y-n.y)}}}function ri(t,e="x"){const i=oi(e),s=t.length,n=Array(s).fill(0),o=Array(s);let a,r,l,h=ni(t,0);for(a=0;a<s;++a)if(r=l,l=h,h=ni(t,a+1),l){if(h){const t=h[e]-l[e];n[a]=0!==t?(h[i]-l[i])/t:0}o[a]=r?h?F(n[a-1])!==F(n[a])?0:(n[a-1]+n[a])/2:n[a-1]:n[a]}!function(t,e,i){const s=t.length;let n,o,a,r,l,h=ni(t,0);for(let c=0;c<s-1;++c)l=h,h=ni(t,c+1),l&&h&&(V(e[c],0,si)?i[c]=i[c+1]=0:(n=i[c]/e[c],o=i[c+1]/e[c],r=Math.pow(n,2)+Math.pow(o,2),r<=9||(a=3/Math.sqrt(r),i[c]=n*a*e[c],i[c+1]=o*a*e[c])))}(t,n,o),function(t,e,i="x"){const s=oi(i),n=t.length;let o,a,r,l=ni(t,0);for(let h=0;h<n;++h){if(a=r,r=l,l=ni(t,h+1),!r)continue;const n=r[i],c=r[s];a&&(o=(n-a[i])/3,r[`cp1${i}`]=n-o,r[`cp1${s}`]=c-o*e[h]),l&&(o=(l[i]-n)/3,r[`cp2${i}`]=n+o,r[`cp2${s}`]=c+o*e[h])}}(t,o,e)}function li(t,e,i){return Math.max(Math.min(t,i),e)}function hi(t,e,i,s,n){let o,a,r,l;if(e.spanGaps&&(t=t.filter((t=>!t.skip))),"monotone"===e.cubicInterpolationMode)ri(t,n);else{let i=s?t[t.length-1]:t[0];for(o=0,a=t.length;o<a;++o)r=t[o],l=ai(i,r,t[Math.min(o+1,a-(s?0:1))%a],e.tension),r.cp1x=l.previous.x,r.cp1y=l.previous.y,r.cp2x=l.next.x,r.cp2y=l.next.y,i=r}e.capBezierPoints&&function(t,e){let i,s,n,o,a,r=Re(t[0],e);for(i=0,s=t.length;i<s;++i)a=o,o=r,r=i<s-1&&Re(t[i+1],e),o&&(n=t[i],a&&(n.cp1x=li(n.cp1x,e.left,e.right),n.cp1y=li(n.cp1y,e.top,e.bottom)),r&&(n.cp2x=li(n.cp2x,e.left,e.right),n.cp2y=li(n.cp2y,e.top,e.bottom)))}(t,i)}const ci=t=>0===t||1===t,di=(t,e,i)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*O/i),ui=(t,e,i)=>Math.pow(2,-10*t)*Math.sin((t-e)*O/i)+1,fi={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*E),easeOutSine:t=>Math.sin(t*E),easeInOutSine:t=>-.5*(Math.cos(C*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>ci(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>ci(t)?t:di(t,.075,.3),easeOutElastic:t=>ci(t)?t:ui(t,.075,.3),easeInOutElastic(t){const e=.1125;return ci(t)?t:t<.5?.5*di(2*t,e,.45):.5+.5*ui(2*t-1,e,.45)},easeInBack(t){const e=1.70158;return t*t*((e+1)*t-e)},easeOutBack(t){const e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-fi.easeOutBounce(1-t),easeOutBounce(t){const e=7.5625,i=2.75;return t<1/i?e*t*t:t<2/i?e*(t-=1.5/i)*t+.75:t<2.5/i?e*(t-=2.25/i)*t+.9375:e*(t-=2.625/i)*t+.984375},easeInOutBounce:t=>t<.5?.5*fi.easeInBounce(2*t):.5*fi.easeOutBounce(2*t-1)+.5};function gi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:t.y+i*(e.y-t.y)}}function pi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:"middle"===s?i<.5?t.y:e.y:"after"===s?i<1?t.y:e.y:i>0?e.y:t.y}}function mi(t,e,i,s){const n={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},a=gi(t,n,i),r=gi(n,o,i),l=gi(o,e,i),h=gi(a,r,i),c=gi(r,l,i);return gi(h,c,i)}const xi=/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/,bi=/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;function _i(t,e){const i=(""+t).match(xi);if(!i||"normal"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case"px":return t;case"%":t/=100}return e*t}const yi=t=>+t||0;function vi(t,e){const i={},s=o(e),n=s?Object.keys(e):e,a=o(t)?s?i=>l(t[i],t[e[i]]):e=>t[e]:()=>t;for(const t of n)i[t]=yi(a(t));return i}function Mi(t){return vi(t,{top:"y",right:"x",bottom:"y",left:"x"})}function wi(t){return vi(t,["topLeft","topRight","bottomLeft","bottomRight"])}function ki(t){const e=Mi(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function Si(t,e){t=t||{},e=e||ue.font;let i=l(t.size,e.size);"string"==typeof i&&(i=parseInt(i,10));let s=l(t.style,e.style);s&&!(""+s).match(bi)&&(console.warn('Invalid font style specified: "'+s+'"'),s=void 0);const n={family:l(t.family,e.family),lineHeight:_i(l(t.lineHeight,e.lineHeight),i),size:i,style:s,weight:l(t.weight,e.weight),string:""};return n.string=De(n),n}function Pi(t,e,i,s){let o,a,r,l=!0;for(o=0,a=t.length;o<a;++o)if(r=t[o],void 0!==r&&(void 0!==e&&"function"==typeof r&&(r=r(e),l=!1),void 0!==i&&n(r)&&(r=r[i%r.length],l=!1),void 0!==r))return s&&!l&&(s.cacheable=!1),r}function Di(t,e,i){const{min:s,max:n}=t,o=c(e,(n-s)/2),a=(t,e)=>i&&0===t?0:t+e;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function Ci(t,e){return Object.assign(Object.create(t),e)}function Oi(t,e,i){return t?function(t,e){return{x:i=>t+t+e-i,setWidth(t){e=t},textAlign:t=>"center"===t?t:"right"===t?"left":"right",xPlus:(t,e)=>t-e,leftForLtr:(t,e)=>t-e}}(e,i):{x:t=>t,setWidth(t){},textAlign:t=>t,xPlus:(t,e)=>t+e,leftForLtr:(t,e)=>t}}function Ai(t,e){let i,s;"ltr"!==e&&"rtl"!==e||(i=t.canvas.style,s=[i.getPropertyValue("direction"),i.getPropertyPriority("direction")],i.setProperty("direction",e,"important"),t.prevTextDirection=s)}function Ti(t,e){void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}function Li(t){return"angle"===t?{between:J,compare:K,normalize:G}:{between:tt,compare:(t,e)=>t-e,normalize:t=>t}}function Ei({start:t,end:e,count:i,loop:s,style:n}){return{start:t%i,end:e%i,loop:s&&(e-t+1)%i==0,style:n}}function Ri(t,e,i){if(!i)return[t];const{property:s,start:n,end:o}=i,a=e.length,{compare:r,between:l,normalize:h}=Li(s),{start:c,end:d,loop:u,style:f}=function(t,e,i){const{property:s,start:n,end:o}=i,{between:a,normalize:r}=Li(s),l=e.length;let h,c,{start:d,end:u,loop:f}=t;if(f){for(d+=l,u+=l,h=0,c=l;h<c&&a(r(e[d%l][s]),n,o);++h)d--,u--;d%=l,u%=l}return u<d&&(u+=l),{start:d,end:u,loop:f,style:t.style}}(t,e,i),g=[];let p,m,x,b=!1,_=null;const y=()=>b||l(n,x,p)&&0!==r(n,x),v=()=>!b||0===r(o,p)||l(o,x,p);for(let t=c,i=c;t<=d;++t)m=e[t%a],m.skip||(p=h(m[s]),p!==x&&(b=l(p,n,o),null===_&&y()&&(_=0===r(p,n)?t:i),null!==_&&v()&&(g.push(Ei({start:_,end:t,loop:u,count:a,style:f})),_=null),i=t,x=p));return null!==_&&g.push(Ei({start:_,end:d,loop:u,count:a,style:f})),g}function Ii(t,e){const i=[],s=t.segments;for(let n=0;n<s.length;n++){const o=Ri(s[n],t.points,e);o.length&&i.push(...o)}return i}function zi(t,e){const i=t.points,s=t.options.spanGaps,n=i.length;if(!n)return[];const o=!!t._loop,{start:a,end:r}=function(t,e,i,s){let n=0,o=e-1;if(i&&!s)for(;n<e&&!t[n].skip;)n++;for(;n<e&&t[n].skip;)n++;for(n%=e,i&&(o+=n);o>n&&t[o%e].skip;)o--;return o%=e,{start:n,end:o}}(i,n,o,s);if(!0===s)return Fi(t,[{start:a,end:r,loop:o}],i,e);return Fi(t,function(t,e,i,s){const n=t.length,o=[];let a,r=e,l=t[e];for(a=e+1;a<=i;++a){const i=t[a%n];i.skip||i.stop?l.skip||(s=!1,o.push({start:e%n,end:(a-1)%n,loop:s}),e=r=i.stop?a:null):(r=a,l.skip&&(e=a)),l=i}return null!==r&&o.push({start:e%n,end:r%n,loop:s}),o}(i,a,r<a?r+n:r,!!t._fullLoop&&0===a&&r===n-1),i,e)}function Fi(t,e,i,s){return s&&s.setContext&&i?function(t,e,i,s){const n=t._chart.getContext(),o=Vi(t.options),{_datasetIndex:a,options:{spanGaps:r}}=t,l=i.length,h=[];let c=o,d=e[0].start,u=d;function f(t,e,s,n){const o=r?-1:1;if(t!==e){for(t+=l;i[t%l].skip;)t-=o;for(;i[e%l].skip;)e+=o;t%l!=e%l&&(h.push({start:t%l,end:e%l,loop:s,style:n}),c=n,d=e%l)}}for(const t of e){d=r?d:t.start;let e,o=i[d%l];for(u=d+1;u<=t.end;u++){const r=i[u%l];e=Vi(s.setContext(Ci(n,{type:"segment",p0:o,p1:r,p0DataIndex:(u-1)%l,p1DataIndex:u%l,datasetIndex:a}))),Bi(e,c)&&f(d,u-1,t.loop,c),o=r,c=e}d<u-1&&f(d,u-1,t.loop,c)}return h}(t,e,i,s):e}function Vi(t){return{backgroundColor:t.backgroundColor,borderCapStyle:t.borderCapStyle,borderDash:t.borderDash,borderDashOffset:t.borderDashOffset,borderJoinStyle:t.borderJoinStyle,borderWidth:t.borderWidth,borderColor:t.borderColor}}function Bi(t,e){if(!e)return!1;const i=[],s=function(t,e){return Zt(e)?(i.includes(e)||i.push(e),i.indexOf(e)):e};return JSON.stringify(t,s)!==JSON.stringify(e,s)}function Wi(t,e,i){return t.options.clip?t[i]:e[i]}function Ni(t,e){const i=e._clip;if(i.disabled)return!1;const s=function(t,e){const{xScale:i,yScale:s}=t;return i&&s?{left:Wi(i,e,"left"),right:Wi(i,e,"right"),top:Wi(s,e,"top"),bottom:Wi(s,e,"bottom")}:e}(e,t.chartArea);return{left:!1===i.left?0:s.left-(!0===i.left?0:i.left),right:!1===i.right?t.width:s.right+(!0===i.right?0:i.right),top:!1===i.top?0:s.top-(!0===i.top?0:i.top),bottom:!1===i.bottom?t.height:s.bottom+(!0===i.bottom?0:i.bottom)}}var Hi=Object.freeze({__proto__:null,HALF_PI:E,INFINITY:T,PI:C,PITAU:A,QUARTER_PI:R,RAD_PER_DEG:L,TAU:O,TWO_THIRDS_PI:I,_addGrace:Di,_alignPixel:Ae,_alignStartEnd:ft,_angleBetween:J,_angleDiff:K,_arrayUnique:lt,_attachContext:$e,_bezierCurveTo:Ve,_bezierInterpolation:mi,_boundSegment:Ri,_boundSegments:Ii,_capitalize:w,_computeSegments:zi,_createResolver:je,_decimalPlaces:U,_deprecated:function(t,e,i,s){void 0!==e&&console.warn(t+': "'+i+'" is deprecated. Please use "'+s+'" instead')},_descriptors:Ye,_elementsEqual:f,_factorize:W,_filterBetween:nt,_getParentNode:ge,_getStartAndCountOfVisiblePoints:pt,_int16Range:Q,_isBetween:tt,_isClickEvent:D,_isDomSupported:fe,_isPointInArea:Re,_limitValue:Z,_longestText:Oe,_lookup:et,_lookupByKey:it,_measureText:Ce,_merger:m,_mergerIf:_,_normalizeAngle:G,_parseObjectDataRadialScale:ii,_pointInLine:gi,_readValueToProps:vi,_rlookupByKey:st,_scaleRangesChanged:mt,_setMinAndMaxByKey:j,_splitKey:v,_steppedInterpolation:pi,_steppedLineTo:Fe,_textX:gt,_toLeftRightCenter:ut,_updateBezierControlPoints:hi,addRoundedRectPath:He,almostEquals:V,almostWhole:H,callback:d,clearCanvas:Te,clipArea:Ie,clone:g,color:Qt,createContext:Ci,debounce:dt,defined:k,distanceBetweenPoints:q,drawPoint:Le,drawPointLegend:Ee,each:u,easingEffects:fi,finiteOrDefault:r,fontString:function(t,e,i){return e+" "+t+"px "+i},formatNumber:ne,getAngleFromPoint:X,getDatasetClipArea:Ni,getHoverColor:te,getMaximumSize:we,getRelativePosition:ve,getRtlAdapter:Oi,getStyle:xe,isArray:n,isFinite:a,isFunction:S,isNullOrUndef:s,isNumber:N,isObject:o,isPatternOrGradient:Zt,listenArrayEvents:at,log10:z,merge:x,mergeIf:b,niceNum:B,noop:e,overrideTextDirection:Ai,readUsedSize:Pe,renderText:Ne,requestAnimFrame:ht,resolve:Pi,resolveObjectKey:M,restoreTextDirection:Ti,retinaScale:ke,setsEqual:P,sign:F,splineCurve:ai,splineCurveMonotone:ri,supportsEventListenerOptions:Se,throttled:ct,toDegrees:Y,toDimension:c,toFont:Si,toFontString:De,toLineHeight:_i,toPadding:ki,toPercentage:h,toRadians:$,toTRBL:Mi,toTRBLCorners:wi,uid:i,unclipArea:ze,unlistenArrayEvents:rt,valueOrDefault:l});function ji(t,e,i,n){const{controller:o,data:a,_sorted:r}=t,l=o._cachedMeta.iScale,h=t.dataset&&t.dataset.options?t.dataset.options.spanGaps:null;if(l&&e===l.axis&&"r"!==e&&r&&a.length){const r=l._reversePixels?st:it;if(!n){const n=r(a,e,i);if(h){const{vScale:e}=o._cachedMeta,{_parsed:i}=t,a=i.slice(0,n.lo+1).reverse().findIndex((t=>!s(t[e.axis])));n.lo-=Math.max(0,a);const r=i.slice(n.hi).findIndex((t=>!s(t[e.axis])));n.hi+=Math.max(0,r)}return n}if(o._sharedOptions){const t=a[0],s="function"==typeof t.getRange&&t.getRange(e);if(s){const t=r(a,e,i-s),n=r(a,e,i+s);return{lo:t.lo,hi:n.hi}}}}return{lo:0,hi:a.length-1}}function $i(t,e,i,s,n){const o=t.getSortedVisibleDatasetMetas(),a=i[e];for(let t=0,i=o.length;t<i;++t){const{index:i,data:r}=o[t],{lo:l,hi:h}=ji(o[t],e,a,n);for(let t=l;t<=h;++t){const e=r[t];e.skip||s(e,i,t)}}}function Yi(t,e,i,s,n){const o=[];if(!n&&!t.isPointInArea(e))return o;return $i(t,i,e,(function(i,a,r){(n||Re(i,t.chartArea,0))&&i.inRange(e.x,e.y,s)&&o.push({element:i,datasetIndex:a,index:r})}),!0),o}function Ui(t,e,i,s,n,o){let a=[];const r=function(t){const e=-1!==t.indexOf("x"),i=-1!==t.indexOf("y");return function(t,s){const n=e?Math.abs(t.x-s.x):0,o=i?Math.abs(t.y-s.y):0;return Math.sqrt(Math.pow(n,2)+Math.pow(o,2))}}(i);let l=Number.POSITIVE_INFINITY;return $i(t,i,e,(function(i,h,c){const d=i.inRange(e.x,e.y,n);if(s&&!d)return;const u=i.getCenterPoint(n);if(!(!!o||t.isPointInArea(u))&&!d)return;const f=r(e,u);f<l?(a=[{element:i,datasetIndex:h,index:c}],l=f):f===l&&a.push({element:i,datasetIndex:h,index:c})})),a}function Xi(t,e,i,s,n,o){return o||t.isPointInArea(e)?"r"!==i||s?Ui(t,e,i,s,n,o):function(t,e,i,s){let n=[];return $i(t,i,e,(function(t,i,o){const{startAngle:a,endAngle:r}=t.getProps(["startAngle","endAngle"],s),{angle:l}=X(t,{x:e.x,y:e.y});J(l,a,r)&&n.push({element:t,datasetIndex:i,index:o})})),n}(t,e,i,n):[]}function qi(t,e,i,s,n){const o=[],a="x"===i?"inXRange":"inYRange";let r=!1;return $i(t,i,e,((t,s,l)=>{t[a]&&t[a](e[i],n)&&(o.push({element:t,datasetIndex:s,index:l}),r=r||t.inRange(e.x,e.y,n))})),s&&!r?[]:o}var Ki={evaluateInteractionItems:$i,modes:{index(t,e,i,s){const n=ve(e,t),o=i.axis||"x",a=i.includeInvisible||!1,r=i.intersect?Yi(t,n,o,s,a):Xi(t,n,o,!1,s,a),l=[];return r.length?(t.getSortedVisibleDatasetMetas().forEach((t=>{const e=r[0].index,i=t.data[e];i&&!i.skip&&l.push({element:i,datasetIndex:t.index,index:e})})),l):[]},dataset(t,e,i,s){const n=ve(e,t),o=i.axis||"xy",a=i.includeInvisible||!1;let r=i.intersect?Yi(t,n,o,s,a):Xi(t,n,o,!1,s,a);if(r.length>0){const e=r[0].datasetIndex,i=t.getDatasetMeta(e).data;r=[];for(let t=0;t<i.length;++t)r.push({element:i[t],datasetIndex:e,index:t})}return r},point:(t,e,i,s)=>Yi(t,ve(e,t),i.axis||"xy",s,i.includeInvisible||!1),nearest(t,e,i,s){const n=ve(e,t),o=i.axis||"xy",a=i.includeInvisible||!1;return Xi(t,n,o,i.intersect,s,a)},x:(t,e,i,s)=>qi(t,ve(e,t),"x",i.intersect,s),y:(t,e,i,s)=>qi(t,ve(e,t),"y",i.intersect,s)}};const Gi=["left","top","right","bottom"];function Ji(t,e){return t.filter((t=>t.pos===e))}function Zi(t,e){return t.filter((t=>-1===Gi.indexOf(t.pos)&&t.box.axis===e))}function Qi(t,e){return t.sort(((t,i)=>{const s=e?i:t,n=e?t:i;return s.weight===n.weight?s.index-n.index:s.weight-n.weight}))}function ts(t,e){const i=function(t){const e={};for(const i of t){const{stack:t,pos:s,stackWeight:n}=i;if(!t||!Gi.includes(s))continue;const o=e[t]||(e[t]={count:0,placed:0,weight:0,size:0});o.count++,o.weight+=n}return e}(t),{vBoxMaxWidth:s,hBoxMaxHeight:n}=e;let o,a,r;for(o=0,a=t.length;o<a;++o){r=t[o];const{fullSize:a}=r.box,l=i[r.stack],h=l&&r.stackWeight/l.weight;r.horizontal?(r.width=h?h*s:a&&e.availableWidth,r.height=n):(r.width=s,r.height=h?h*n:a&&e.availableHeight)}return i}function es(t,e,i,s){return Math.max(t[i],e[i])+Math.max(t[s],e[s])}function is(t,e){t.top=Math.max(t.top,e.top),t.left=Math.max(t.left,e.left),t.bottom=Math.max(t.bottom,e.bottom),t.right=Math.max(t.right,e.right)}function ss(t,e,i,s){const{pos:n,box:a}=i,r=t.maxPadding;if(!o(n)){i.size&&(t[n]-=i.size);const e=s[i.stack]||{size:0,count:1};e.size=Math.max(e.size,i.horizontal?a.height:a.width),i.size=e.size/e.count,t[n]+=i.size}a.getPadding&&is(r,a.getPadding());const l=Math.max(0,e.outerWidth-es(r,t,"left","right")),h=Math.max(0,e.outerHeight-es(r,t,"top","bottom")),c=l!==t.w,d=h!==t.h;return t.w=l,t.h=h,i.horizontal?{same:c,other:d}:{same:d,other:c}}function ns(t,e){const i=e.maxPadding;function s(t){const s={left:0,top:0,right:0,bottom:0};return t.forEach((t=>{s[t]=Math.max(e[t],i[t])})),s}return s(t?["left","right"]:["top","bottom"])}function os(t,e,i,s){const n=[];let o,a,r,l,h,c;for(o=0,a=t.length,h=0;o<a;++o){r=t[o],l=r.box,l.update(r.width||e.w,r.height||e.h,ns(r.horizontal,e));const{same:a,other:d}=ss(e,i,r,s);h|=a&&n.length,c=c||d,l.fullSize||n.push(r)}return h&&os(n,e,i,s)||c}function as(t,e,i,s,n){t.top=i,t.left=e,t.right=e+s,t.bottom=i+n,t.width=s,t.height=n}function rs(t,e,i,s){const n=i.padding;let{x:o,y:a}=e;for(const r of t){const t=r.box,l=s[r.stack]||{count:1,placed:0,weight:1},h=r.stackWeight/l.weight||1;if(r.horizontal){const s=e.w*h,o=l.size||t.height;k(l.start)&&(a=l.start),t.fullSize?as(t,n.left,a,i.outerWidth-n.right-n.left,o):as(t,e.left+l.placed,a,s,o),l.start=a,l.placed+=s,a=t.bottom}else{const s=e.h*h,a=l.size||t.width;k(l.start)&&(o=l.start),t.fullSize?as(t,o,n.top,a,i.outerHeight-n.bottom-n.top):as(t,o,e.top+l.placed,a,s),l.start=o,l.placed+=s,o=t.right}}e.x=o,e.y=a}var ls={addBox(t,e){t.boxes||(t.boxes=[]),e.fullSize=e.fullSize||!1,e.position=e.position||"top",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw(t){e.draw(t)}}]},t.boxes.push(e)},removeBox(t,e){const i=t.boxes?t.boxes.indexOf(e):-1;-1!==i&&t.boxes.splice(i,1)},configure(t,e,i){e.fullSize=i.fullSize,e.position=i.position,e.weight=i.weight},update(t,e,i,s){if(!t)return;const n=ki(t.options.layout.padding),o=Math.max(e-n.width,0),a=Math.max(i-n.height,0),r=function(t){const e=function(t){const e=[];let i,s,n,o,a,r;for(i=0,s=(t||[]).length;i<s;++i)n=t[i],({position:o,options:{stack:a,stackWeight:r=1}}=n),e.push({index:i,box:n,pos:o,horizontal:n.isHorizontal(),weight:n.weight,stack:a&&o+a,stackWeight:r});return e}(t),i=Qi(e.filter((t=>t.box.fullSize)),!0),s=Qi(Ji(e,"left"),!0),n=Qi(Ji(e,"right")),o=Qi(Ji(e,"top"),!0),a=Qi(Ji(e,"bottom")),r=Zi(e,"x"),l=Zi(e,"y");return{fullSize:i,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:Ji(e,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}(t.boxes),l=r.vertical,h=r.horizontal;u(t.boxes,(t=>{"function"==typeof t.beforeLayout&&t.beforeLayout()}));const c=l.reduce(((t,e)=>e.box.options&&!1===e.box.options.display?t:t+1),0)||1,d=Object.freeze({outerWidth:e,outerHeight:i,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/c,hBoxMaxHeight:a/2}),f=Object.assign({},n);is(f,ki(s));const g=Object.assign({maxPadding:f,w:o,h:a,x:n.left,y:n.top},n),p=ts(l.concat(h),d);os(r.fullSize,g,d,p),os(l,g,d,p),os(h,g,d,p)&&os(l,g,d,p),function(t){const e=t.maxPadding;function i(i){const s=Math.max(e[i]-t[i],0);return t[i]+=s,s}t.y+=i("top"),t.x+=i("left"),i("right"),i("bottom")}(g),rs(r.leftAndTop,g,d,p),g.x+=g.w,g.y+=g.h,rs(r.rightAndBottom,g,d,p),t.chartArea={left:g.left,top:g.top,right:g.left+g.w,bottom:g.top+g.h,height:g.h,width:g.w},u(r.chartArea,(e=>{const i=e.box;Object.assign(i,t.chartArea),i.update(g.w,g.h,{left:0,top:0,right:0,bottom:0})}))}};class hs{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,i){}removeEventListener(t,e,i){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,i,s){return e=Math.max(0,e||t.width),i=i||t.height,{width:e,height:Math.max(0,s?Math.floor(e/s):i)}}isAttached(t){return!0}updateConfig(t){}}class cs extends hs{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const ds="$chartjs",us={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},fs=t=>null===t||""===t;const gs=!!Se&&{passive:!0};function ps(t,e,i){t&&t.canvas&&t.canvas.removeEventListener(e,i,gs)}function ms(t,e){for(const i of t)if(i===e||i.contains(e))return!0}function xs(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||ms(i.addedNodes,s),e=e&&!ms(i.removedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}function bs(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||ms(i.removedNodes,s),e=e&&!ms(i.addedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}const _s=new Map;let ys=0;function vs(){const t=window.devicePixelRatio;t!==ys&&(ys=t,_s.forEach(((e,i)=>{i.currentDevicePixelRatio!==t&&e()})))}function Ms(t,e,i){const s=t.canvas,n=s&&ge(s);if(!n)return;const o=ct(((t,e)=>{const s=n.clientWidth;i(t,e),s<n.clientWidth&&i()}),window),a=new ResizeObserver((t=>{const e=t[0],i=e.contentRect.width,s=e.contentRect.height;0===i&&0===s||o(i,s)}));return a.observe(n),function(t,e){_s.size||window.addEventListener("resize",vs),_s.set(t,e)}(t,o),a}function ws(t,e,i){i&&i.disconnect(),"resize"===e&&function(t){_s.delete(t),_s.size||window.removeEventListener("resize",vs)}(t)}function ks(t,e,i){const s=t.canvas,n=ct((e=>{null!==t.ctx&&i(function(t,e){const i=us[t.type]||t.type,{x:s,y:n}=ve(t,e);return{type:i,chart:e,native:t,x:void 0!==s?s:null,y:void 0!==n?n:null}}(e,t))}),t);return function(t,e,i){t&&t.addEventListener(e,i,gs)}(s,e,n),n}class Ss extends hs{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(function(t,e){const i=t.style,s=t.getAttribute("height"),n=t.getAttribute("width");if(t[ds]={initial:{height:s,width:n,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||"block",i.boxSizing=i.boxSizing||"border-box",fs(n)){const e=Pe(t,"width");void 0!==e&&(t.width=e)}if(fs(s))if(""===t.style.height)t.height=t.width/(e||2);else{const e=Pe(t,"height");void 0!==e&&(t.height=e)}}(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e[ds])return!1;const i=e[ds].initial;["height","width"].forEach((t=>{const n=i[t];s(n)?e.removeAttribute(t):e.setAttribute(t,n)}));const n=i.style||{};return Object.keys(n).forEach((t=>{e.style[t]=n[t]})),e.width=e.width,delete e[ds],!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),n={attach:xs,detach:bs,resize:Ms}[e]||ks;s[e]=n(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:ws,detach:ws,resize:ws}[e]||ps)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return we(t,e,i,s)}isAttached(t){const e=t&&ge(t);return!(!e||!e.isConnected)}}function Ps(t){return!fe()||"undefined"!=typeof OffscreenCanvas&&t instanceof OffscreenCanvas?cs:Ss}var Ds=Object.freeze({__proto__:null,BasePlatform:hs,BasicPlatform:cs,DomPlatform:Ss,_detectPlatform:Ps});const Cs="transparent",Os={boolean:(t,e,i)=>i>.5?e:t,color(t,e,i){const s=Qt(t||Cs),n=s.valid&&Qt(e||Cs);return n&&n.valid?n.mix(s,i).hexString():e},number:(t,e,i)=>t+(e-t)*i};class As{constructor(t,e,i,s){const n=e[i];s=Pi([t.to,s,n,t.from]);const o=Pi([t.from,n,s]);this._active=!0,this._fn=t.fn||Os[t.type||typeof o],this._easing=fi[t.easing]||fi.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=o,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],n=i-this._start,o=this._duration-n;this._start=i,this._duration=Math.floor(Math.max(o,t.duration)),this._total+=n,this._loop=!!t.loop,this._to=Pi([t.to,e,s,t.from]),this._from=Pi([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,n=this._from,o=this._loop,a=this._to;let r;if(this._active=n!==a&&(o||e<i),!this._active)return this._target[s]=a,void this._notify(!0);e<0?this._target[s]=n:(r=e/i%2,r=o&&r>1?2-r:r,r=this._easing(Math.min(1,Math.max(0,r))),this._target[s]=this._fn(n,a,r))}wait(){const t=this._promises||(this._promises=[]);return new Promise(((e,i)=>{t.push({res:e,rej:i})}))}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let t=0;t<i.length;t++)i[t][e]()}}class Ts{constructor(t,e){this._chart=t,this._properties=new Map,this.configure(e)}configure(t){if(!o(t))return;const e=Object.keys(ue.animation),i=this._properties;Object.getOwnPropertyNames(t).forEach((s=>{const a=t[s];if(!o(a))return;const r={};for(const t of e)r[t]=a[t];(n(a.properties)&&a.properties||[s]).forEach((t=>{t!==s&&i.has(t)||i.set(t,r)}))}))}_animateOptions(t,e){const i=e.options,s=function(t,e){if(!e)return;let i=t.options;if(!i)return void(t.options=e);i.$shared&&(t.options=i=Object.assign({},i,{$shared:!1,$animations:{}}));return i}(t,i);if(!s)return[];const n=this._createAnimations(s,i);return i.$shared&&function(t,e){const i=[],s=Object.keys(e);for(let e=0;e<s.length;e++){const n=t[s[e]];n&&n.active()&&i.push(n.wait())}return Promise.all(i)}(t.options.$animations,i).then((()=>{t.options=i}),(()=>{})),n}_createAnimations(t,e){const i=this._properties,s=[],n=t.$animations||(t.$animations={}),o=Object.keys(e),a=Date.now();let r;for(r=o.length-1;r>=0;--r){const l=o[r];if("$"===l.charAt(0))continue;if("options"===l){s.push(...this._animateOptions(t,e));continue}const h=e[l];let c=n[l];const d=i.get(l);if(c){if(d&&c.active()){c.update(d,h,a);continue}c.cancel()}d&&d.duration?(n[l]=c=new As(d,t,l,h),s.push(c)):t[l]=h}return s}update(t,e){if(0===this._properties.size)return void Object.assign(t,e);const i=this._createAnimations(t,e);return i.length?(bt.add(this._chart,i),!0):void 0}}function Ls(t,e){const i=t&&t.options||{},s=i.reverse,n=void 0===i.min?e:0,o=void 0===i.max?e:0;return{start:s?o:n,end:s?n:o}}function Es(t,e){const i=[],s=t._getSortedDatasetMetas(e);let n,o;for(n=0,o=s.length;n<o;++n)i.push(s[n].index);return i}function Rs(t,e,i,s={}){const n=t.keys,o="single"===s.mode;let r,l,h,c;if(null===e)return;let d=!1;for(r=0,l=n.length;r<l;++r){if(h=+n[r],h===i){if(d=!0,s.all)continue;break}c=t.values[h],a(c)&&(o||0===e||F(e)===F(c))&&(e+=c)}return d||s.all?e:0}function Is(t,e){const i=t&&t.options.stacked;return i||void 0===i&&void 0!==e.stack}function zs(t,e,i){const s=t[e]||(t[e]={});return s[i]||(s[i]={})}function Fs(t,e,i,s){for(const n of e.getMatchingVisibleMetas(s).reverse()){const e=t[n.index];if(i&&e>0||!i&&e<0)return n.index}return null}function Vs(t,e){const{chart:i,_cachedMeta:s}=t,n=i._stacks||(i._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,h=a.axis,c=function(t,e,i){return`${t.id}.${e.id}.${i.stack||i.type}`}(o,a,s),d=e.length;let u;for(let t=0;t<d;++t){const i=e[t],{[l]:o,[h]:d}=i;u=(i._stacks||(i._stacks={}))[h]=zs(n,c,o),u[r]=d,u._top=Fs(u,a,!0,s.type),u._bottom=Fs(u,a,!1,s.type);(u._visualValues||(u._visualValues={}))[r]=d}}function Bs(t,e){const i=t.scales;return Object.keys(i).filter((t=>i[t].axis===e)).shift()}function Ws(t,e){const i=t.controller.index,s=t.vScale&&t.vScale.axis;if(s){e=e||t._parsed;for(const t of e){const e=t._stacks;if(!e||void 0===e[s]||void 0===e[s][i])return;delete e[s][i],void 0!==e[s]._visualValues&&void 0!==e[s]._visualValues[i]&&delete e[s]._visualValues[i]}}}const Ns=t=>"reset"===t||"none"===t,Hs=(t,e)=>e?t:Object.assign({},t);class js{static defaults={};static datasetElementType=null;static dataElementType=null;constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=Is(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Ws(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(t,e,i,s)=>"x"===t?e:"r"===t?s:i,n=e.xAxisID=l(i.xAxisID,Bs(t,"x")),o=e.yAxisID=l(i.yAxisID,Bs(t,"y")),a=e.rAxisID=l(i.rAxisID,Bs(t,"r")),r=e.indexAxis,h=e.iAxisID=s(r,n,o,a),c=e.vAxisID=s(r,o,n,a);e.xScale=this.getScaleForId(n),e.yScale=this.getScaleForId(o),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(h),e.vScale=this.getScaleForId(c)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&rt(this._data,this),t._stacked&&Ws(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(o(e)){const t=this._cachedMeta;this._data=function(t,e){const{iScale:i,vScale:s}=e,n="x"===i.axis?"x":"y",o="x"===s.axis?"x":"y",a=Object.keys(t),r=new Array(a.length);let l,h,c;for(l=0,h=a.length;l<h;++l)c=a[l],r[l]={[n]:c,[o]:t[c]};return r}(e,t)}else if(i!==e){if(i){rt(i,this);const t=this._cachedMeta;Ws(t),t._parsed=[]}e&&Object.isExtensible(e)&&at(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,i=this.getDataset();let s=!1;this._dataCheck();const n=e._stacked;e._stacked=Is(e.vScale,e),e.stack!==i.stack&&(s=!0,Ws(e),e.stack=i.stack),this._resyncElements(t),(s||n!==e._stacked)&&(Vs(this,e._parsed),e._stacked=Is(e.vScale,e))}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),i=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(i,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:i,_data:s}=this,{iScale:a,_stacked:r}=i,l=a.axis;let h,c,d,u=0===t&&e===s.length||i._sorted,f=t>0&&i._parsed[t-1];if(!1===this._parsing)i._parsed=s,i._sorted=!0,d=s;else{d=n(s[t])?this.parseArrayData(i,s,t,e):o(s[t])?this.parseObjectData(i,s,t,e):this.parsePrimitiveData(i,s,t,e);const a=()=>null===c[l]||f&&c[l]<f[l];for(h=0;h<e;++h)i._parsed[h+t]=c=d[h],u&&(a()&&(u=!1),f=c);i._sorted=u}r&&Vs(this,d)}parsePrimitiveData(t,e,i,s){const{iScale:n,vScale:o}=t,a=n.axis,r=o.axis,l=n.getLabels(),h=n===o,c=new Array(s);let d,u,f;for(d=0,u=s;d<u;++d)f=d+i,c[d]={[a]:h||n.parse(l[f],f),[r]:o.parse(e[f],f)};return c}parseArrayData(t,e,i,s){const{xScale:n,yScale:o}=t,a=new Array(s);let r,l,h,c;for(r=0,l=s;r<l;++r)h=r+i,c=e[h],a[r]={x:n.parse(c[0],h),y:o.parse(c[1],h)};return a}parseObjectData(t,e,i,s){const{xScale:n,yScale:o}=t,{xAxisKey:a="x",yAxisKey:r="y"}=this._parsing,l=new Array(s);let h,c,d,u;for(h=0,c=s;h<c;++h)d=h+i,u=e[d],l[h]={x:n.parse(M(u,a),d),y:o.parse(M(u,r),d)};return l}getParsed(t){return this._cachedMeta._parsed[t]}getDataElement(t){return this._cachedMeta.data[t]}applyStack(t,e,i){const s=this.chart,n=this._cachedMeta,o=e[t.axis];return Rs({keys:Es(s,!0),values:e._stacks[t.axis]._visualValues},o,n.index,{mode:i})}updateRangeFromParsed(t,e,i,s){const n=i[e.axis];let o=null===n?NaN:n;const a=s&&i._stacks[e.axis];s&&a&&(s.values=a,o=Rs(s,n,this._cachedMeta.index)),t.min=Math.min(t.min,o),t.max=Math.max(t.max,o)}getMinMax(t,e){const i=this._cachedMeta,s=i._parsed,n=i._sorted&&t===i.iScale,o=s.length,r=this._getOtherScale(t),l=((t,e,i)=>t&&!e.hidden&&e._stacked&&{keys:Es(i,!0),values:null})(e,i,this.chart),h={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:c,max:d}=function(t){const{min:e,max:i,minDefined:s,maxDefined:n}=t.getUserBounds();return{min:s?e:Number.NEGATIVE_INFINITY,max:n?i:Number.POSITIVE_INFINITY}}(r);let u,f;function g(){f=s[u];const e=f[r.axis];return!a(f[t.axis])||c>e||d<e}for(u=0;u<o&&(g()||(this.updateRangeFromParsed(h,t,f,l),!n));++u);if(n)for(u=o-1;u>=0;--u)if(!g()){this.updateRangeFromParsed(h,t,f,l);break}return h}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,n,o;for(s=0,n=e.length;s<n;++s)o=e[s][t.axis],a(o)&&i.push(o);return i}getMaxOverflow(){return!1}getLabelAndValue(t){const e=this._cachedMeta,i=e.iScale,s=e.vScale,n=this.getParsed(t);return{label:i?""+i.getLabelForValue(n[i.axis]):"",value:s?""+s.getLabelForValue(n[s.axis]):""}}_update(t){const e=this._cachedMeta;this.update(t||"default"),e._clip=function(t){let e,i,s,n;return o(t)?(e=t.top,i=t.right,s=t.bottom,n=t.left):e=i=s=n=t,{top:e,right:i,bottom:s,left:n,disabled:!1===t}}(l(this.options.clip,function(t,e,i){if(!1===i)return!1;const s=Ls(t,i),n=Ls(e,i);return{top:n.end,right:s.end,bottom:n.start,left:s.start}}(e.xScale,e.yScale,this.getMaxOverflow())))}update(t){}draw(){const t=this._ctx,e=this.chart,i=this._cachedMeta,s=i.data||[],n=e.chartArea,o=[],a=this._drawStart||0,r=this._drawCount||s.length-a,l=this.options.drawActiveElementsOnTop;let h;for(i.dataset&&i.dataset.draw(t,n,a,r),h=a;h<a+r;++h){const e=s[h];e.hidden||(e.active&&l?o.push(e):e.draw(t,n))}for(h=0;h<o.length;++h)o[h].draw(t,n)}getStyle(t,e){const i=e?"active":"default";return void 0===t&&this._cachedMeta.dataset?this.resolveDatasetElementOptions(i):this.resolveDataElementOptions(t||0,i)}getContext(t,e,i){const s=this.getDataset();let n;if(t>=0&&t<this._cachedMeta.data.length){const e=this._cachedMeta.data[t];n=e.$context||(e.$context=function(t,e,i){return Ci(t,{active:!1,dataIndex:e,parsed:void 0,raw:void 0,element:i,index:e,mode:"default",type:"data"})}(this.getContext(),t,e)),n.parsed=this.getParsed(t),n.raw=s.data[t],n.index=n.dataIndex=t}else n=this.$context||(this.$context=function(t,e){return Ci(t,{active:!1,dataset:void 0,datasetIndex:e,index:e,mode:"default",type:"dataset"})}(this.chart.getContext(),this.index)),n.dataset=s,n.index=n.datasetIndex=this.index;return n.active=!!e,n.mode=i,n}resolveDatasetElementOptions(t){return this._resolveElementOptions(this.datasetElementType.id,t)}resolveDataElementOptions(t,e){return this._resolveElementOptions(this.dataElementType.id,e,t)}_resolveElementOptions(t,e="default",i){const s="active"===e,n=this._cachedDataOpts,o=t+"-"+e,a=n[o],r=this.enableOptionSharing&&k(i);if(a)return Hs(a,r);const l=this.chart.config,h=l.datasetElementScopeKeys(this._type,t),c=s?[`${t}Hover`,"hover",t,""]:[t,""],d=l.getOptionScopes(this.getDataset(),h),u=Object.keys(ue.elements[t]),f=l.resolveNamedOptions(d,u,(()=>this.getContext(i,s,e)),c);return f.$shared&&(f.$shared=r,n[o]=Object.freeze(Hs(f,r))),f}_resolveAnimations(t,e,i){const s=this.chart,n=this._cachedDataOpts,o=`animation-${e}`,a=n[o];if(a)return a;let r;if(!1!==s.options.animation){const s=this.chart.config,n=s.datasetAnimationScopeKeys(this._type,e),o=s.getOptionScopes(this.getDataset(),n);r=s.createResolver(o,this.getContext(t,i,e))}const l=new Ts(s,r&&r.animations);return r&&r._cacheable&&(n[o]=Object.freeze(l)),l}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||Ns(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const i=this.resolveDataElementOptions(t,e),s=this._sharedOptions,n=this.getSharedOptions(i),o=this.includeOptions(e,n)||n!==s;return this.updateSharedOptions(n,e,i),{sharedOptions:n,includeOptions:o}}updateElement(t,e,i,s){Ns(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!Ns(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const n=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(n)||n})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[t,e,i]of this._syncList)this[t](e,i);this._syncList=[];const s=i.length,n=e.length,o=Math.min(n,s);o&&this.parse(0,o),n>s?this._insertElements(s,n-s,t):n<s&&this._removeElements(n,s-n)}_insertElements(t,e,i=!0){const s=this._cachedMeta,n=s.data,o=t+e;let a;const r=t=>{for(t.length+=e,a=t.length-1;a>=o;a--)t[a]=t[a-e]};for(r(n),a=t;a<o;++a)n[a]=new this.dataElementType;this._parsing&&r(s._parsed),this.parse(t,e),i&&this.updateElements(n,t,e,"reset")}updateElements(t,e,i,s){}_removeElements(t,e){const i=this._cachedMeta;if(this._parsing){const s=i._parsed.splice(t,e);i._stacked&&Ws(i,s)}i.data.splice(t,e)}_sync(t){if(this._parsing)this._syncList.push(t);else{const[e,i,s]=t;this[e](i,s)}this.chart._dataChanges.push([this.index,...t])}_onDataPush(){const t=arguments.length;this._sync(["_insertElements",this.getDataset().data.length-t,t])}_onDataPop(){this._sync(["_removeElements",this._cachedMeta.data.length-1,1])}_onDataShift(){this._sync(["_removeElements",0,1])}_onDataSplice(t,e){e&&this._sync(["_removeElements",t,e]);const i=arguments.length-2;i&&this._sync(["_insertElements",t,i])}_onDataUnshift(){this._sync(["_insertElements",0,arguments.length])}}class $s{static defaults={};static defaultRoutes=void 0;x;y;active=!1;options;$animations;tooltipPosition(t){const{x:e,y:i}=this.getProps(["x","y"],t);return{x:e,y:i}}hasValue(){return N(this.x)&&N(this.y)}getProps(t,e){const i=this.$animations;if(!e||!i)return this;const s={};return t.forEach((t=>{s[t]=i[t]&&i[t].active()?i[t]._to:this[t]})),s}}function Ys(t,e){const i=t.options.ticks,n=function(t){const e=t.options.offset,i=t._tickSize(),s=t._length/i+(e?0:1),n=t._maxLength/i;return Math.floor(Math.min(s,n))}(t),o=Math.min(i.maxTicksLimit||n,n),a=i.major.enabled?function(t){const e=[];let i,s;for(i=0,s=t.length;i<s;i++)t[i].major&&e.push(i);return e}(e):[],r=a.length,l=a[0],h=a[r-1],c=[];if(r>o)return function(t,e,i,s){let n,o=0,a=i[0];for(s=Math.ceil(s),n=0;n<t.length;n++)n===a&&(e.push(t[n]),o++,a=i[o*s])}(e,c,a,r/o),c;const d=function(t,e,i){const s=function(t){const e=t.length;let i,s;if(e<2)return!1;for(s=t[0],i=1;i<e;++i)if(t[i]-t[i-1]!==s)return!1;return s}(t),n=e.length/i;if(!s)return Math.max(n,1);const o=W(s);for(let t=0,e=o.length-1;t<e;t++){const e=o[t];if(e>n)return e}return Math.max(n,1)}(a,e,o);if(r>0){let t,i;const n=r>1?Math.round((h-l)/(r-1)):null;for(Us(e,c,d,s(n)?0:l-n,l),t=0,i=r-1;t<i;t++)Us(e,c,d,a[t],a[t+1]);return Us(e,c,d,h,s(n)?e.length:h+n),c}return Us(e,c,d),c}function Us(t,e,i,s,n){const o=l(s,0),a=Math.min(l(n,t.length),t.length);let r,h,c,d=0;for(i=Math.ceil(i),n&&(r=n-s,i=r/Math.floor(r/i)),c=o;c<0;)d++,c=Math.round(o+d*i);for(h=Math.max(o,0);h<a;h++)h===c&&(e.push(t[h]),d++,c=Math.round(o+d*i))}const Xs=(t,e,i)=>"top"===e||"left"===e?t[e]+i:t[e]-i,qs=(t,e)=>Math.min(e||t,t);function Ks(t,e){const i=[],s=t.length/e,n=t.length;let o=0;for(;o<n;o+=s)i.push(t[Math.floor(o)]);return i}function Gs(t,e,i){const s=t.ticks.length,n=Math.min(e,s-1),o=t._startPixel,a=t._endPixel,r=1e-6;let l,h=t.getPixelForTick(n);if(!(i&&(l=1===s?Math.max(h-o,a-h):0===e?(t.getPixelForTick(1)-h)/2:(h-t.getPixelForTick(n-1))/2,h+=n<e?l:-l,h<o-r||h>a+r)))return h}function Js(t){return t.drawTicks?t.tickLength:0}function Zs(t,e){if(!t.display)return 0;const i=Si(t.font,e),s=ki(t.padding);return(n(t.text)?t.text.length:1)*i.lineHeight+s.height}function Qs(t,e,i){let s=ut(t);return(i&&"right"!==e||!i&&"right"===e)&&(s=(t=>"left"===t?"right":"right"===t?"left":t)(s)),s}class tn extends $s{constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void 0,this.ctx=t.ctx,this.chart=t.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,this._userMin=this.parse(t.min),this._userMax=this.parse(t.max),this._suggestedMin=this.parse(t.suggestedMin),this._suggestedMax=this.parse(t.suggestedMax)}parse(t,e){return t}getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:i,_suggestedMax:s}=this;return t=r(t,Number.POSITIVE_INFINITY),e=r(e,Number.NEGATIVE_INFINITY),i=r(i,Number.POSITIVE_INFINITY),s=r(s,Number.NEGATIVE_INFINITY),{min:r(t,i),max:r(e,s),minDefined:a(t),maxDefined:a(e)}}getMinMax(t){let e,{min:i,max:s,minDefined:n,maxDefined:o}=this.getUserBounds();if(n&&o)return{min:i,max:s};const a=this.getMatchingVisibleMetas();for(let r=0,l=a.length;r<l;++r)e=a[r].controller.getMinMax(this,t),n||(i=Math.min(i,e.min)),o||(s=Math.max(s,e.max));return i=o&&i>s?s:i,s=n&&i>s?i:s,{min:r(i,r(s,i)),max:r(s,r(i,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){d(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:n,ticks:o}=this.options,a=o.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Di(this,n,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const r=a<this.ticks.length;this._convertTicksToLabels(r?Ks(this.ticks,a):this.ticks),this.configure(),this.beforeCalculateLabelRotation(),this.calculateLabelRotation(),this.afterCalculateLabelRotation(),o.display&&(o.autoSkip||"auto"===o.source)&&(this.ticks=Ys(this,this.ticks),this._labelSizes=null,this.afterAutoSkip()),r&&this._convertTicksToLabels(this.ticks),this.beforeFit(),this.fit(),this.afterFit(),this.afterUpdate()}configure(){let t,e,i=this.options.reverse;this.isHorizontal()?(t=this.left,e=this.right):(t=this.top,e=this.bottom,i=!i),this._startPixel=t,this._endPixel=e,this._reversePixels=i,this._length=e-t,this._alignToPixels=this.options.alignToPixels}afterUpdate(){d(this.options.afterUpdate,[this])}beforeSetDimensions(){d(this.options.beforeSetDimensions,[this])}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=0,this.right=this.width):(this.height=this.maxHeight,this.top=0,this.bottom=this.height),this.paddingLeft=0,this.paddingTop=0,this.paddingRight=0,this.paddingBottom=0}afterSetDimensions(){d(this.options.afterSetDimensions,[this])}_callHooks(t){this.chart.notifyPlugins(t,this.getContext()),d(this.options[t],[this])}beforeDataLimits(){this._callHooks("beforeDataLimits")}determineDataLimits(){}afterDataLimits(){this._callHooks("afterDataLimits")}beforeBuildTicks(){this._callHooks("beforeBuildTicks")}buildTicks(){return[]}afterBuildTicks(){this._callHooks("afterBuildTicks")}beforeTickToLabelConversion(){d(this.options.beforeTickToLabelConversion,[this])}generateTickLabels(t){const e=this.options.ticks;let i,s,n;for(i=0,s=t.length;i<s;i++)n=t[i],n.label=d(e.callback,[n.value,i,t],this)}afterTickToLabelConversion(){d(this.options.afterTickToLabelConversion,[this])}beforeCalculateLabelRotation(){d(this.options.beforeCalculateLabelRotation,[this])}calculateLabelRotation(){const t=this.options,e=t.ticks,i=qs(this.ticks.length,t.ticks.maxTicksLimit),s=e.minRotation||0,n=e.maxRotation;let o,a,r,l=s;if(!this._isVisible()||!e.display||s>=n||i<=1||!this.isHorizontal())return void(this.labelRotation=s);const h=this._getLabelSizes(),c=h.widest.width,d=h.highest.height,u=Z(this.chart.width-c,0,this.maxWidth);o=t.offset?this.maxWidth/i:u/(i-1),c+6>o&&(o=u/(i-(t.offset?.5:1)),a=this.maxHeight-Js(t.grid)-e.padding-Zs(t.title,this.chart.options.font),r=Math.sqrt(c*c+d*d),l=Y(Math.min(Math.asin(Z((h.highest.height+6)/o,-1,1)),Math.asin(Z(a/r,-1,1))-Math.asin(Z(d/r,-1,1)))),l=Math.max(s,Math.min(n,l))),this.labelRotation=l}afterCalculateLabelRotation(){d(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){d(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:n}}=this,o=this._isVisible(),a=this.isHorizontal();if(o){const o=Zs(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Js(n)+o):(t.height=this.maxHeight,t.width=Js(n)+o),i.display&&this.ticks.length){const{first:e,last:s,widest:n,highest:o}=this._getLabelSizes(),r=2*i.padding,l=$(this.labelRotation),h=Math.cos(l),c=Math.sin(l);if(a){const e=i.mirror?0:c*n.width+h*o.height;t.height=Math.min(this.maxHeight,t.height+e+r)}else{const e=i.mirror?0:h*n.width+c*o.height;t.width=Math.min(this.maxWidth,t.width+e+r)}this._calculatePadding(e,s,c,h)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:n,padding:o},position:a}=this.options,r=0!==this.labelRotation,l="top"!==a&&"x"===this.axis;if(this.isHorizontal()){const a=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let c=0,d=0;r?l?(c=s*t.width,d=i*e.height):(c=i*t.height,d=s*e.width):"start"===n?d=e.width:"end"===n?c=t.width:"inner"!==n&&(c=t.width/2,d=e.width/2),this.paddingLeft=Math.max((c-a+o)*this.width/(this.width-a),0),this.paddingRight=Math.max((d-h+o)*this.width/(this.width-h),0)}else{let i=e.height/2,s=t.height/2;"start"===n?(i=0,s=t.height):"end"===n&&(i=e.height,s=0),this.paddingTop=i+o,this.paddingBottom=s+o}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){d(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return"top"===e||"bottom"===e||"x"===t}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){let e,i;for(this.beforeTickToLabelConversion(),this.generateTickLabels(t),e=0,i=t.length;e<i;e++)s(t[e].label)&&(t.splice(e,1),i--,e--);this.afterTickToLabelConversion()}_getLabelSizes(){let t=this._labelSizes;if(!t){const e=this.options.ticks.sampleSize;let i=this.ticks;e<i.length&&(i=Ks(i,e)),this._labelSizes=t=this._computeLabelSizes(i,i.length,this.options.ticks.maxTicksLimit)}return t}_computeLabelSizes(t,e,i){const{ctx:o,_longestTextCache:a}=this,r=[],l=[],h=Math.floor(e/qs(e,i));let c,d,f,g,p,m,x,b,_,y,v,M=0,w=0;for(c=0;c<e;c+=h){if(g=t[c].label,p=this._resolveTickFontOptions(c),o.font=m=p.string,x=a[m]=a[m]||{data:{},gc:[]},b=p.lineHeight,_=y=0,s(g)||n(g)){if(n(g))for(d=0,f=g.length;d<f;++d)v=g[d],s(v)||n(v)||(_=Ce(o,x.data,x.gc,_,v),y+=b)}else _=Ce(o,x.data,x.gc,_,g),y=b;r.push(_),l.push(y),M=Math.max(_,M),w=Math.max(y,w)}!function(t,e){u(t,(t=>{const i=t.gc,s=i.length/2;let n;if(s>e){for(n=0;n<s;++n)delete t.data[i[n]];i.splice(0,s)}}))}(a,e);const k=r.indexOf(M),S=l.indexOf(w),P=t=>({width:r[t]||0,height:l[t]||0});return{first:P(0),last:P(e-1),widest:P(k),highest:P(S),widths:r,heights:l}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return Q(this._alignToPixels?Ae(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&t<e.length){const i=e[t];return i.$context||(i.$context=function(t,e,i){return Ci(t,{tick:i,index:e,type:"tick"})}(this.getContext(),t,i))}return this.$context||(this.$context=Ci(this.chart.getContext(),{scale:this,type:"scale"}))}_tickSize(){const t=this.options.ticks,e=$(this.labelRotation),i=Math.abs(Math.cos(e)),s=Math.abs(Math.sin(e)),n=this._getLabelSizes(),o=t.autoSkipPadding||0,a=n?n.widest.width+o:0,r=n?n.highest.height+o:0;return this.isHorizontal()?r*i>a*s?a/i:r/s:r*s<a*i?r/i:a/s}_isVisible(){const t=this.options.display;return"auto"!==t?!!t:this.getMatchingVisibleMetas().length>0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:n,position:a,border:r}=s,h=n.offset,c=this.isHorizontal(),d=this.ticks.length+(h?1:0),u=Js(n),f=[],g=r.setContext(this.getContext()),p=g.display?g.width:0,m=p/2,x=function(t){return Ae(i,t,p)};let b,_,y,v,M,w,k,S,P,D,C,O;if("top"===a)b=x(this.bottom),w=this.bottom-u,S=b-m,D=x(t.top)+m,O=t.bottom;else if("bottom"===a)b=x(this.top),D=t.top,O=x(t.bottom)-m,w=b+m,S=this.top+u;else if("left"===a)b=x(this.right),M=this.right-u,k=b-m,P=x(t.left)+m,C=t.right;else if("right"===a)b=x(this.left),P=t.left,C=x(t.right)-m,M=b+m,k=this.left+u;else if("x"===e){if("center"===a)b=x((t.top+t.bottom)/2+.5);else if(o(a)){const t=Object.keys(a)[0],e=a[t];b=x(this.chart.scales[t].getPixelForValue(e))}D=t.top,O=t.bottom,w=b+m,S=w+u}else if("y"===e){if("center"===a)b=x((t.left+t.right)/2);else if(o(a)){const t=Object.keys(a)[0],e=a[t];b=x(this.chart.scales[t].getPixelForValue(e))}M=b-m,k=M-u,P=t.left,C=t.right}const A=l(s.ticks.maxTicksLimit,d),T=Math.max(1,Math.ceil(d/A));for(_=0;_<d;_+=T){const t=this.getContext(_),e=n.setContext(t),s=r.setContext(t),o=e.lineWidth,a=e.color,l=s.dash||[],d=s.dashOffset,u=e.tickWidth,g=e.tickColor,p=e.tickBorderDash||[],m=e.tickBorderDashOffset;y=Gs(this,_,h),void 0!==y&&(v=Ae(i,y,o),c?M=k=P=C=v:w=S=D=O=v,f.push({tx1:M,ty1:w,tx2:k,ty2:S,x1:P,y1:D,x2:C,y2:O,width:o,color:a,borderDash:l,borderDashOffset:d,tickWidth:u,tickColor:g,tickBorderDash:p,tickBorderDashOffset:m}))}return this._ticksLength=d,this._borderValue=b,f}_computeLabelItems(t){const e=this.axis,i=this.options,{position:s,ticks:a}=i,r=this.isHorizontal(),l=this.ticks,{align:h,crossAlign:c,padding:d,mirror:u}=a,f=Js(i.grid),g=f+d,p=u?-d:g,m=-$(this.labelRotation),x=[];let b,_,y,v,M,w,k,S,P,D,C,O,A="middle";if("top"===s)w=this.bottom-p,k=this._getXAxisLabelAlignment();else if("bottom"===s)w=this.top+p,k=this._getXAxisLabelAlignment();else if("left"===s){const t=this._getYAxisLabelAlignment(f);k=t.textAlign,M=t.x}else if("right"===s){const t=this._getYAxisLabelAlignment(f);k=t.textAlign,M=t.x}else if("x"===e){if("center"===s)w=(t.top+t.bottom)/2+g;else if(o(s)){const t=Object.keys(s)[0],e=s[t];w=this.chart.scales[t].getPixelForValue(e)+g}k=this._getXAxisLabelAlignment()}else if("y"===e){if("center"===s)M=(t.left+t.right)/2-g;else if(o(s)){const t=Object.keys(s)[0],e=s[t];M=this.chart.scales[t].getPixelForValue(e)}k=this._getYAxisLabelAlignment(f).textAlign}"y"===e&&("start"===h?A="top":"end"===h&&(A="bottom"));const T=this._getLabelSizes();for(b=0,_=l.length;b<_;++b){y=l[b],v=y.label;const t=a.setContext(this.getContext(b));S=this.getPixelForTick(b)+a.labelOffset,P=this._resolveTickFontOptions(b),D=P.lineHeight,C=n(v)?v.length:1;const e=C/2,i=t.color,o=t.textStrokeColor,h=t.textStrokeWidth;let d,f=k;if(r?(M=S,"inner"===k&&(f=b===_-1?this.options.reverse?"left":"right":0===b?this.options.reverse?"right":"left":"center"),O="top"===s?"near"===c||0!==m?-C*D+D/2:"center"===c?-T.highest.height/2-e*D+D:-T.highest.height+D/2:"near"===c||0!==m?D/2:"center"===c?T.highest.height/2-e*D:T.highest.height-C*D,u&&(O*=-1),0===m||t.showLabelBackdrop||(M+=D/2*Math.sin(m))):(w=S,O=(1-C)*D/2),t.showLabelBackdrop){const e=ki(t.backdropPadding),i=T.heights[b],s=T.widths[b];let n=O-e.top,o=0-e.left;switch(A){case"middle":n-=i/2;break;case"bottom":n-=i}switch(k){case"center":o-=s/2;break;case"right":o-=s;break;case"inner":b===_-1?o-=s:b>0&&(o-=s/2)}d={left:o,top:n,width:s+e.width,height:i+e.height,color:t.backdropColor}}x.push({label:v,font:P,textOffset:O,options:{rotation:m,color:i,strokeColor:o,strokeWidth:h,textAlign:f,textBaseline:A,translation:[M,w],backdrop:d}})}return x}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-$(this.labelRotation))return"top"===t?"left":"right";let i="center";return"start"===e.align?i="left":"end"===e.align?i="right":"inner"===e.align&&(i="inner"),i}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:i,mirror:s,padding:n}}=this.options,o=t+n,a=this._getLabelSizes().widest.width;let r,l;return"left"===e?s?(l=this.right+n,"near"===i?r="left":"center"===i?(r="center",l+=a/2):(r="right",l+=a)):(l=this.right-o,"near"===i?r="right":"center"===i?(r="center",l-=a/2):(r="left",l=this.left)):"right"===e?s?(l=this.left+n,"near"===i?r="right":"center"===i?(r="center",l-=a/2):(r="left",l-=a)):(l=this.left+o,"near"===i?r="left":"center"===i?(r="center",l+=a/2):(r="right",l=this.right)):r="right",{textAlign:r,x:l}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;return"left"===e||"right"===e?{top:0,left:this.left,bottom:t.height,right:this.right}:"top"===e||"bottom"===e?{top:this.top,left:0,bottom:this.bottom,right:t.width}:void 0}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:i,top:s,width:n,height:o}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(i,s,n,o),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const i=this.ticks.findIndex((e=>e.value===t));if(i>=0){return e.setContext(this.getContext(i)).lineWidth}return 0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let n,o;const a=(t,e,s)=>{s.width&&s.color&&(i.save(),i.lineWidth=s.width,i.strokeStyle=s.color,i.setLineDash(s.borderDash||[]),i.lineDashOffset=s.borderDashOffset,i.beginPath(),i.moveTo(t.x,t.y),i.lineTo(e.x,e.y),i.stroke(),i.restore())};if(e.display)for(n=0,o=s.length;n<o;++n){const t=s[n];e.drawOnChartArea&&a({x:t.x1,y:t.y1},{x:t.x2,y:t.y2},t),e.drawTicks&&a({x:t.tx1,y:t.ty1},{x:t.tx2,y:t.ty2},{color:t.tickColor,width:t.tickWidth,borderDash:t.tickBorderDash,borderDashOffset:t.tickBorderDashOffset})}}drawBorder(){const{chart:t,ctx:e,options:{border:i,grid:s}}=this,n=i.setContext(this.getContext()),o=i.display?n.width:0;if(!o)return;const a=s.setContext(this.getContext(0)).lineWidth,r=this._borderValue;let l,h,c,d;this.isHorizontal()?(l=Ae(t,this.left,o)-o/2,h=Ae(t,this.right,a)+a/2,c=d=r):(c=Ae(t,this.top,o)-o/2,d=Ae(t,this.bottom,a)+a/2,l=h=r),e.save(),e.lineWidth=n.width,e.strokeStyle=n.color,e.beginPath(),e.moveTo(l,c),e.lineTo(h,d),e.stroke(),e.restore()}drawLabels(t){if(!this.options.ticks.display)return;const e=this.ctx,i=this._computeLabelArea();i&&Ie(e,i);const s=this.getLabelItems(t);for(const t of s){const i=t.options,s=t.font;Ne(e,t.label,0,t.textOffset,s,i)}i&&ze(e)}drawTitle(){const{ctx:t,options:{position:e,title:i,reverse:s}}=this;if(!i.display)return;const a=Si(i.font),r=ki(i.padding),l=i.align;let h=a.lineHeight/2;"bottom"===e||"center"===e||o(e)?(h+=r.bottom,n(i.text)&&(h+=a.lineHeight*(i.text.length-1))):h+=r.top;const{titleX:c,titleY:d,maxWidth:u,rotation:f}=function(t,e,i,s){const{top:n,left:a,bottom:r,right:l,chart:h}=t,{chartArea:c,scales:d}=h;let u,f,g,p=0;const m=r-n,x=l-a;if(t.isHorizontal()){if(f=ft(s,a,l),o(i)){const t=Object.keys(i)[0],s=i[t];g=d[t].getPixelForValue(s)+m-e}else g="center"===i?(c.bottom+c.top)/2+m-e:Xs(t,i,e);u=l-a}else{if(o(i)){const t=Object.keys(i)[0],s=i[t];f=d[t].getPixelForValue(s)-x+e}else f="center"===i?(c.left+c.right)/2-x+e:Xs(t,i,e);g=ft(s,r,n),p="left"===i?-E:E}return{titleX:f,titleY:g,maxWidth:u,rotation:p}}(this,h,e,l);Ne(t,i.text,0,0,a,{color:i.color,maxWidth:u,rotation:f,textAlign:Qs(l,e,s),textBaseline:"middle",translation:[c,d]})}draw(t){this._isVisible()&&(this.drawBackground(),this.drawGrid(t),this.drawBorder(),this.drawTitle(),this.drawLabels(t))}_layers(){const t=this.options,e=t.ticks&&t.ticks.z||0,i=l(t.grid&&t.grid.z,-1),s=l(t.border&&t.border.z,0);return this._isVisible()&&this.draw===tn.prototype.draw?[{z:i,draw:t=>{this.drawBackground(),this.drawGrid(t),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:e,draw:t=>{this.drawLabels(t)}}]:[{z:e,draw:t=>{this.draw(t)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let n,o;for(n=0,o=e.length;n<o;++n){const o=e[n];o[i]!==this.id||t&&o.type!==t||s.push(o)}return s}_resolveTickFontOptions(t){return Si(this.options.ticks.setContext(this.getContext(t)).font)}_maxDigits(){const t=this._resolveTickFontOptions(0).lineHeight;return(this.isHorizontal()?this.width:this.height)/t}}class en{constructor(t,e,i){this.type=t,this.scope=e,this.override=i,this.items=Object.create(null)}isForType(t){return Object.prototype.isPrototypeOf.call(this.type.prototype,t.prototype)}register(t){const e=Object.getPrototypeOf(t);let i;(function(t){return"id"in t&&"defaults"in t})(e)&&(i=this.register(e));const s=this.items,n=t.id,o=this.scope+"."+n;if(!n)throw new Error("class does not have id: "+t);return n in s||(s[n]=t,function(t,e,i){const s=x(Object.create(null),[i?ue.get(i):{},ue.get(e),t.defaults]);ue.set(e,s),t.defaultRoutes&&function(t,e){Object.keys(e).forEach((i=>{const s=i.split("."),n=s.pop(),o=[t].concat(s).join("."),a=e[i].split("."),r=a.pop(),l=a.join(".");ue.route(o,n,l,r)}))}(e,t.defaultRoutes);t.descriptors&&ue.describe(e,t.descriptors)}(t,o,i),this.override&&ue.override(t.id,t.overrides)),o}get(t){return this.items[t]}unregister(t){const e=this.items,i=t.id,s=this.scope;i in e&&delete e[i],s&&i in ue[s]&&(delete ue[s][i],this.override&&delete re[i])}}class sn{constructor(){this.controllers=new en(js,"datasets",!0),this.elements=new en($s,"elements"),this.plugins=new en(Object,"plugins"),this.scales=new en(tn,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,i){[...e].forEach((e=>{const s=i||this._getRegistryForType(e);i||s.isForType(e)||s===this.plugins&&e.id?this._exec(t,s,e):u(e,(e=>{const s=i||this._getRegistryForType(e);this._exec(t,s,e)}))}))}_exec(t,e,i){const s=w(t);d(i["before"+s],[],i),e[t](i),d(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;e<this._typedRegistries.length;e++){const i=this._typedRegistries[e];if(i.isForType(t))return i}return this.plugins}_get(t,e,i){const s=e.get(t);if(void 0===s)throw new Error('"'+t+'" is not a registered '+i+".");return s}}var nn=new sn;class on{constructor(){this._init=[]}notify(t,e,i,s){"beforeInit"===e&&(this._init=this._createDescriptors(t,!0),this._notify(this._init,t,"install"));const n=s?this._descriptors(t).filter(s):this._descriptors(t),o=this._notify(n,t,e,i);return"afterDestroy"===e&&(this._notify(n,t,"stop"),this._notify(this._init,t,"uninstall")),o}_notify(t,e,i,s){s=s||{};for(const n of t){const t=n.plugin;if(!1===d(t[i],[e,s,n.options],t)&&s.cancelable)return!1}return!0}invalidate(){s(this._cache)||(this._oldCache=this._cache,this._cache=void 0)}_descriptors(t){if(this._cache)return this._cache;const e=this._cache=this._createDescriptors(t);return this._notifyStateChanges(t),e}_createDescriptors(t,e){const i=t&&t.config,s=l(i.options&&i.options.plugins,{}),n=function(t){const e={},i=[],s=Object.keys(nn.plugins.items);for(let t=0;t<s.length;t++)i.push(nn.getPlugin(s[t]));const n=t.plugins||[];for(let t=0;t<n.length;t++){const s=n[t];-1===i.indexOf(s)&&(i.push(s),e[s.id]=!0)}return{plugins:i,localIds:e}}(i);return!1!==s||e?function(t,{plugins:e,localIds:i},s,n){const o=[],a=t.getContext();for(const r of e){const e=r.id,l=an(s[e],n);null!==l&&o.push({plugin:r,options:rn(t.config,{plugin:r,local:i[e]},l,a)})}return o}(t,n,s,e):[]}_notifyStateChanges(t){const e=this._oldCache||[],i=this._cache,s=(t,e)=>t.filter((t=>!e.some((e=>t.plugin.id===e.plugin.id))));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function an(t,e){return e||!1!==t?!0===t?{}:t:null}function rn(t,{plugin:e,local:i},s,n){const o=t.pluginScopeKeys(e),a=t.getOptionScopes(s,o);return i&&e.defaults&&a.push(e.defaults),t.createResolver(a,n,[""],{scriptable:!1,indexable:!1,allKeys:!0})}function ln(t,e){const i=ue.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||i.indexAxis||"x"}function hn(t){if("x"===t||"y"===t||"r"===t)return t}function cn(t,...e){if(hn(t))return t;for(const s of e){const e=s.axis||("top"===(i=s.position)||"bottom"===i?"x":"left"===i||"right"===i?"y":void 0)||t.length>1&&hn(t[0].toLowerCase());if(e)return e}var i;throw new Error(`Cannot determine type of '${t}' axis. Please provide 'axis' or 'position' option.`)}function dn(t,e,i){if(i[e+"AxisID"]===t)return{axis:e}}function un(t,e){const i=re[t.type]||{scales:{}},s=e.scales||{},n=ln(t.type,e),a=Object.create(null);return Object.keys(s).forEach((e=>{const r=s[e];if(!o(r))return console.error(`Invalid scale configuration for scale: ${e}`);if(r._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${e}`);const l=cn(e,r,function(t,e){if(e.data&&e.data.datasets){const i=e.data.datasets.filter((e=>e.xAxisID===t||e.yAxisID===t));if(i.length)return dn(t,"x",i[0])||dn(t,"y",i[0])}return{}}(e,t),ue.scales[r.type]),h=function(t,e){return t===e?"_index_":"_value_"}(l,n),c=i.scales||{};a[e]=b(Object.create(null),[{axis:l},r,c[l],c[h]])})),t.data.datasets.forEach((i=>{const n=i.type||t.type,o=i.indexAxis||ln(n,e),r=(re[n]||{}).scales||{};Object.keys(r).forEach((t=>{const e=function(t,e){let i=t;return"_index_"===t?i=e:"_value_"===t&&(i="x"===e?"y":"x"),i}(t,o),n=i[e+"AxisID"]||e;a[n]=a[n]||Object.create(null),b(a[n],[{axis:e},s[n],r[t]])}))})),Object.keys(a).forEach((t=>{const e=a[t];b(e,[ue.scales[e.type],ue.scale])})),a}function fn(t){const e=t.options||(t.options={});e.plugins=l(e.plugins,{}),e.scales=un(t,e)}function gn(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const pn=new Map,mn=new Set;function xn(t,e){let i=pn.get(t);return i||(i=e(),pn.set(t,i),mn.add(i)),i}const bn=(t,e,i)=>{const s=M(e,i);void 0!==s&&t.add(s)};class _n{constructor(t){this._config=function(t){return(t=t||{}).data=gn(t.data),fn(t),t}(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=gn(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),fn(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return xn(t,(()=>[[`datasets.${t}`,""]]))}datasetAnimationScopeKeys(t,e){return xn(`${t}.transition.${e}`,(()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]]))}datasetElementScopeKeys(t,e){return xn(`${t}-${e}`,(()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]]))}pluginScopeKeys(t){const e=t.id;return xn(`${this.type}-plugin-${e}`,(()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]]))}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return s&&!e||(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:n}=this,o=this._cachedScopes(t,i),a=o.get(e);if(a)return a;const r=new Set;e.forEach((e=>{t&&(r.add(t),e.forEach((e=>bn(r,t,e)))),e.forEach((t=>bn(r,s,t))),e.forEach((t=>bn(r,re[n]||{},t))),e.forEach((t=>bn(r,ue,t))),e.forEach((t=>bn(r,le,t)))}));const l=Array.from(r);return 0===l.length&&l.push(Object.create(null)),mn.has(e)&&o.set(e,l),l}chartOptionScopes(){const{options:t,type:e}=this;return[t,re[e]||{},ue.datasets[e]||{},{type:e},ue,le]}resolveNamedOptions(t,e,i,s=[""]){const o={$shared:!0},{resolver:a,subPrefixes:r}=yn(this._resolverCache,t,s);let l=a;if(function(t,e){const{isScriptable:i,isIndexable:s}=Ye(t);for(const o of e){const e=i(o),a=s(o),r=(a||e)&&t[o];if(e&&(S(r)||vn(r))||a&&n(r))return!0}return!1}(a,e)){o.$shared=!1;l=$e(a,i=S(i)?i():i,this.createResolver(t,i,r))}for(const t of e)o[t]=l[t];return o}createResolver(t,e,i=[""],s){const{resolver:n}=yn(this._resolverCache,t,i);return o(e)?$e(n,e,void 0,s):n}}function yn(t,e,i){let s=t.get(e);s||(s=new Map,t.set(e,s));const n=i.join();let o=s.get(n);if(!o){o={resolver:je(e,i),subPrefixes:i.filter((t=>!t.toLowerCase().includes("hover")))},s.set(n,o)}return o}const vn=t=>o(t)&&Object.getOwnPropertyNames(t).some((e=>S(t[e])));const Mn=["top","bottom","left","right","chartArea"];function wn(t,e){return"top"===t||"bottom"===t||-1===Mn.indexOf(t)&&"x"===e}function kn(t,e){return function(i,s){return i[t]===s[t]?i[e]-s[e]:i[t]-s[t]}}function Sn(t){const e=t.chart,i=e.options.animation;e.notifyPlugins("afterRender"),d(i&&i.onComplete,[t],e)}function Pn(t){const e=t.chart,i=e.options.animation;d(i&&i.onProgress,[t],e)}function Dn(t){return fe()&&"string"==typeof t?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const Cn={},On=t=>{const e=Dn(t);return Object.values(Cn).filter((t=>t.canvas===e)).pop()};function An(t,e,i){const s=Object.keys(t);for(const n of s){const s=+n;if(s>=e){const o=t[n];delete t[n],(i>0||s>e)&&(t[s+i]=o)}}}class Tn{static defaults=ue;static instances=Cn;static overrides=re;static registry=nn;static version="4.5.0";static getChart=On;static register(...t){nn.add(...t),Ln()}static unregister(...t){nn.remove(...t),Ln()}constructor(t,e){const s=this.config=new _n(e),n=Dn(t),o=On(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const a=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||Ps(n)),this.platform.updateConfig(s);const r=this.platform.acquireContext(n,a.aspectRatio),l=r&&r.canvas,h=l&&l.height,c=l&&l.width;this.id=i(),this.ctx=r,this.canvas=l,this.width=c,this.height=h,this._options=a,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new on,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=dt((t=>this.update(t)),a.resizeDelay||0),this._dataChanges=[],Cn[this.id]=this,r&&l?(bt.listen(this,"complete",Sn),bt.listen(this,"progress",Pn),this._initialize(),this.attached&&this.update()):console.error("Failed to create chart: can't acquire context from the given item")}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:n,_aspectRatio:o}=this;return s(t)?e&&o?o:n?i/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return nn}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():ke(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Te(this.canvas,this.ctx),this}stop(){return bt.stop(this),this}resize(t,e){bt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,n=i.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(s,t,e,n),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),r=this.width?"resize":"attach";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,ke(this,a,!0)&&(this.notifyPlugins("resize",{size:o}),d(i.onResize,[this,o],this),this.attached&&this._doResize(r)&&this.render())}ensureScalesHaveIDs(){u(this.options.scales||{},((t,e)=>{t.id=e}))}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce(((t,e)=>(t[e]=!1,t)),{});let n=[];e&&(n=n.concat(Object.keys(e).map((t=>{const i=e[t],s=cn(t,i),n="r"===s,o="x"===s;return{options:i,dposition:n?"chartArea":o?"bottom":"left",dtype:n?"radialLinear":o?"category":"linear"}})))),u(n,(e=>{const n=e.options,o=n.id,a=cn(o,n),r=l(n.type,e.dtype);void 0!==n.position&&wn(n.position,a)===wn(e.dposition)||(n.position=e.dposition),s[o]=!0;let h=null;if(o in i&&i[o].type===r)h=i[o];else{h=new(nn.getScale(r))({id:o,type:r,ctx:this.ctx,chart:this}),i[h.id]=h}h.init(n,t)})),u(s,((t,e)=>{t||delete i[e]})),u(i,(t=>{ls.configure(this,t,t.options),ls.addBox(this,t)}))}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort(((t,e)=>t.index-e.index)),i>e){for(let t=e;t<i;++t)this._destroyDatasetMeta(t);t.splice(e,i-e)}this._sortedMetasets=t.slice(0).sort(kn("order","index"))}_removeUnreferencedMetasets(){const{_metasets:t,data:{datasets:e}}=this;t.length>e.length&&delete this._stacks,t.forEach(((t,i)=>{0===e.filter((e=>e===t._dataset)).length&&this._destroyDatasetMeta(i)}))}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i<s;i++){const s=e[i];let n=this.getDatasetMeta(i);const o=s.type||this.config.type;if(n.type&&n.type!==o&&(this._destroyDatasetMeta(i),n=this.getDatasetMeta(i)),n.type=o,n.indexAxis=s.indexAxis||ln(o,this.options),n.order=s.order||0,n.index=i,n.label=""+s.label,n.visible=this.isDatasetVisible(i),n.controller)n.controller.updateIndex(i),n.controller.linkScales();else{const e=nn.getController(o),{datasetElementType:s,dataElementType:a}=ue.datasets[o];Object.assign(e,{dataElementType:nn.getElement(a),datasetElementType:s&&nn.getElement(s)}),n.controller=new e(this,i),t.push(n.controller)}}return this._updateMetasets(),t}_resetElements(){u(this.data.datasets,((t,e)=>{this.getDatasetMeta(e).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0}))return;const n=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let o=0;for(let t=0,e=this.data.datasets.length;t<e;t++){const{controller:e}=this.getDatasetMeta(t),i=!s&&-1===n.indexOf(e);e.buildOrUpdateElements(i),o=Math.max(+e.getMaxOverflow(),o)}o=this._minPadding=i.layout.autoPadding?o:0,this._updateLayout(o),s||u(n,(t=>{t.reset()})),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(kn("z","_idx"));const{_active:a,_lastEvent:r}=this;r?this._eventHandler(r,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){u(this.scales,(t=>{ls.removeBox(this,t)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);P(e,i)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:n}of e){An(t,s,"_removeElements"===i?-n:n)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=e=>new Set(t.filter((t=>t[0]===e)).map(((t,e)=>e+","+t.splice(1).join(",")))),s=i(0);for(let t=1;t<e;t++)if(!P(s,i(t)))return;return Array.from(s).map((t=>t.split(","))).map((t=>({method:t[1],start:+t[2],count:+t[3]})))}_updateLayout(t){if(!1===this.notifyPlugins("beforeLayout",{cancelable:!0}))return;ls.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],u(this.boxes,(t=>{i&&"chartArea"===t.position||(t.configure&&t.configure(),this._layers.push(...t._layers()))}),this),this._layers.forEach(((t,e)=>{t._idx=e})),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(!1!==this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})){for(let t=0,e=this.data.datasets.length;t<e;++t)this.getDatasetMeta(t).controller.configure();for(let e=0,i=this.data.datasets.length;e<i;++e)this._updateDataset(e,S(t)?t({datasetIndex:e}):t);this.notifyPlugins("afterDatasetsUpdate",{mode:t})}}_updateDataset(t,e){const i=this.getDatasetMeta(t),s={meta:i,index:t,mode:e,cancelable:!0};!1!==this.notifyPlugins("beforeDatasetUpdate",s)&&(i.controller._update(e),s.cancelable=!1,this.notifyPlugins("afterDatasetUpdate",s))}render(){!1!==this.notifyPlugins("beforeRender",{cancelable:!0})&&(bt.has(this)?this.attached&&!bt.running(this)&&bt.start(this):(this.draw(),Sn({chart:this})))}draw(){let t;if(this._resizeBeforeDraw){const{width:t,height:e}=this._resizeBeforeDraw;this._resizeBeforeDraw=null,this._resize(t,e)}if(this.clear(),this.width<=0||this.height<=0)return;if(!1===this.notifyPlugins("beforeDraw",{cancelable:!0}))return;const e=this._layers;for(t=0;t<e.length&&e[t].z<=0;++t)e[t].draw(this.chartArea);for(this._drawDatasets();t<e.length;++t)e[t].draw(this.chartArea);this.notifyPlugins("afterDraw")}_getSortedDatasetMetas(t){const e=this._sortedMetasets,i=[];let s,n;for(s=0,n=e.length;s<n;++s){const n=e[s];t&&!n.visible||i.push(n)}return i}getSortedVisibleDatasetMetas(){return this._getSortedDatasetMetas(!0)}_drawDatasets(){if(!1===this.notifyPlugins("beforeDatasetsDraw",{cancelable:!0}))return;const t=this.getSortedVisibleDatasetMetas();for(let e=t.length-1;e>=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i={meta:t,index:t.index,cancelable:!0},s=Ni(this,t);!1!==this.notifyPlugins("beforeDatasetDraw",i)&&(s&&Ie(e,s),t.controller.draw(),s&&ze(e),i.cancelable=!1,this.notifyPlugins("afterDatasetDraw",i))}isPointInArea(t){return Re(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,i,s){const n=Ki.modes[e];return"function"==typeof n?n(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter((t=>t&&t._dataset===e)).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=Ci(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return"boolean"==typeof i.hidden?!i.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",n=this.getDatasetMeta(t),o=n.controller._resolveAnimations(void 0,s);k(e)?(n.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),o.update(n,{visible:i}),this.update((e=>e.datasetIndex===t?s:void 0)))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),bt.remove(this),t=0,e=this.data.datasets.length;t<e;++t)this._destroyDatasetMeta(t)}destroy(){this.notifyPlugins("beforeDestroy");const{canvas:t,ctx:e}=this;this._stop(),this.config.clearCache(),t&&(this.unbindEvents(),Te(t,e),this.platform.releaseContext(e),this.canvas=null,this.ctx=null),delete Cn[this.id],this.notifyPlugins("afterDestroy")}toBase64Image(...t){return this.canvas.toDataURL(...t)}bindEvents(){this.bindUserEvents(),this.options.responsive?this.bindResponsiveEvents():this.attached=!0}bindUserEvents(){const t=this._listeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(t,e,i)=>{t.offsetX=e,t.offsetY=i,this._eventHandler(t)};u(this.options.events,(t=>i(t,s)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(i,s)=>{t[i]&&(e.removeEventListener(this,i,s),delete t[i])},n=(t,e)=>{this.canvas&&this.resize(t,e)};let o;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",n),i("detach",o)};o=()=>{this.attached=!1,s("resize",n),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():o()}unbindEvents(){u(this._listeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._listeners={},u(this._responsiveListeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let n,o,a,r;for("dataset"===e&&(n=this.getDatasetMeta(t[0].datasetIndex),n.controller["_"+s+"DatasetHoverStyle"]()),a=0,r=t.length;a<r;++a){o=t[a];const e=o&&this.getDatasetMeta(o.datasetIndex).controller;e&&e[s+"HoverStyle"](o.element,o.datasetIndex,o.index)}}getActiveElements(){return this._active||[]}setActiveElements(t){const e=this._active||[],i=t.map((({datasetIndex:t,index:e})=>{const i=this.getDatasetMeta(t);if(!i)throw new Error("No dataset found at index "+t);return{datasetIndex:t,element:i.data[e],index:e}}));!f(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}isPluginEnabled(t){return 1===this._plugins._cache.filter((e=>e.plugin.id===t)).length}_updateHoverStyles(t,e,i){const s=this.options.hover,n=(t,e)=>t.filter((t=>!e.some((e=>t.datasetIndex===e.datasetIndex&&t.index===e.index)))),o=n(e,t),a=i?t:n(t,e);o.length&&this.updateHoverStyle(o,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},s=e=>(e.options.events||this.options.events).includes(t.native.type);if(!1===this.notifyPlugins("beforeEvent",i,s))return;const n=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(n||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:n}=this,o=e,a=this._getActiveElements(t,s,i,o),r=D(t),l=function(t,e,i,s){return i&&"mouseout"!==t.type?s?e:t:null}(t,this._lastEvent,i,r);i&&(this._lastEvent=null,d(n.onHover,[t,a,this],this),r&&d(n.onClick,[t,a,this],this));const h=!f(a,s);return(h||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=l,h}_getActiveElements(t,e,i,s){if("mouseout"===t.type)return[];if(!i)return e;const n=this.options.hover;return this.getElementsAtEventForMode(t,n.mode,n,s)}}function Ln(){return u(Tn.instances,(t=>t._plugins.invalidate()))}function En(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class Rn{static override(t){Object.assign(Rn.prototype,t)}options;constructor(t){this.options=t||{}}init(){}formats(){return En()}parse(){return En()}format(){return En()}add(){return En()}diff(){return En()}startOf(){return En()}endOf(){return En()}}var In={_date:Rn};function zn(t){const e=t.iScale,i=function(t,e){if(!t._cache.$bar){const i=t.getMatchingVisibleMetas(e);let s=[];for(let e=0,n=i.length;e<n;e++)s=s.concat(i[e].controller.getAllParsedValues(t));t._cache.$bar=lt(s.sort(((t,e)=>t-e)))}return t._cache.$bar}(e,t.type);let s,n,o,a,r=e._length;const l=()=>{32767!==o&&-32768!==o&&(k(a)&&(r=Math.min(r,Math.abs(o-a)||r)),a=o)};for(s=0,n=i.length;s<n;++s)o=e.getPixelForValue(i[s]),l();for(a=void 0,s=0,n=e.ticks.length;s<n;++s)o=e.getPixelForTick(s),l();return r}function Fn(t,e,i,s){return n(t)?function(t,e,i,s){const n=i.parse(t[0],s),o=i.parse(t[1],s),a=Math.min(n,o),r=Math.max(n,o);let l=a,h=r;Math.abs(a)>Math.abs(r)&&(l=r,h=a),e[i.axis]=h,e._custom={barStart:l,barEnd:h,start:n,end:o,min:a,max:r}}(t,e,i,s):e[i.axis]=i.parse(t,s),e}function Vn(t,e,i,s){const n=t.iScale,o=t.vScale,a=n.getLabels(),r=n===o,l=[];let h,c,d,u;for(h=i,c=i+s;h<c;++h)u=e[h],d={},d[n.axis]=r||n.parse(a[h],h),l.push(Fn(u,d,o,h));return l}function Bn(t){return t&&void 0!==t.barStart&&void 0!==t.barEnd}function Wn(t,e,i,s){let n=e.borderSkipped;const o={};if(!n)return void(t.borderSkipped=o);if(!0===n)return void(t.borderSkipped={top:!0,right:!0,bottom:!0,left:!0});const{start:a,end:r,reverse:l,top:h,bottom:c}=function(t){let e,i,s,n,o;return t.horizontal?(e=t.base>t.x,i="left",s="right"):(e=t.base<t.y,i="bottom",s="top"),e?(n="end",o="start"):(n="start",o="end"),{start:i,end:s,reverse:e,top:n,bottom:o}}(t);"middle"===n&&i&&(t.enableBorderRadius=!0,(i._top||0)===s?n=h:(i._bottom||0)===s?n=c:(o[Nn(c,a,r,l)]=!0,n=h)),o[Nn(n,a,r,l)]=!0,t.borderSkipped=o}function Nn(t,e,i,s){var n,o,a;return s?(a=i,t=Hn(t=(n=t)===(o=e)?a:n===a?o:n,i,e)):t=Hn(t,e,i),t}function Hn(t,e,i){return"start"===t?e:"end"===t?i:t}function jn(t,{inflateAmount:e},i){t.inflateAmount="auto"===e?1===i?.33:0:e}class $n extends js{static id="doughnut";static defaults={datasetElementType:!1,dataElementType:"arc",animation:{animateRotate:!0,animateScale:!1},animations:{numbers:{type:"number",properties:["circumference","endAngle","innerRadius","outerRadius","startAngle","x","y","offset","borderWidth","spacing"]}},cutout:"50%",rotation:0,circumference:360,radius:"100%",spacing:0,indexAxis:"r"};static descriptors={_scriptable:t=>"spacing"!==t,_indexable:t=>"spacing"!==t&&!t.startsWith("borderDash")&&!t.startsWith("hoverBorderDash")};static overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map(((e,n)=>{const o=t.getDatasetMeta(0).controller.getStyle(n);return{text:e,fillStyle:o.backgroundColor,strokeStyle:o.borderColor,fontColor:s,lineWidth:o.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(n),index:n}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}}};constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(!1===this._parsing)s._parsed=i;else{let n,a,r=t=>+i[t];if(o(i[t])){const{key:t="value"}=this._parsing;r=e=>+M(i[e],t)}for(n=t,a=t+e;n<a;++n)s._parsed[n]=r(n)}}_getRotation(){return $(this.options.rotation-90)}_getCircumference(){return $(this.options.circumference)}_getRotationExtents(){let t=O,e=-O;for(let i=0;i<this.chart.data.datasets.length;++i)if(this.chart.isDatasetVisible(i)&&this.chart.getDatasetMeta(i).type===this._type){const s=this.chart.getDatasetMeta(i).controller,n=s._getRotation(),o=s._getCircumference();t=Math.min(t,n),e=Math.max(e,n+o)}return{rotation:t,circumference:e-t}}update(t){const e=this.chart,{chartArea:i}=e,s=this._cachedMeta,n=s.data,o=this.getMaxBorderWidth()+this.getMaxOffset(n)+this.options.spacing,a=Math.max((Math.min(i.width,i.height)-o)/2,0),r=Math.min(h(this.options.cutout,a),1),l=this._getRingWeight(this.index),{circumference:d,rotation:u}=this._getRotationExtents(),{ratioX:f,ratioY:g,offsetX:p,offsetY:m}=function(t,e,i){let s=1,n=1,o=0,a=0;if(e<O){const r=t,l=r+e,h=Math.cos(r),c=Math.sin(r),d=Math.cos(l),u=Math.sin(l),f=(t,e,s)=>J(t,r,l,!0)?1:Math.max(e,e*i,s,s*i),g=(t,e,s)=>J(t,r,l,!0)?-1:Math.min(e,e*i,s,s*i),p=f(0,h,d),m=f(E,c,u),x=g(C,h,d),b=g(C+E,c,u);s=(p-x)/2,n=(m-b)/2,o=-(p+x)/2,a=-(m+b)/2}return{ratioX:s,ratioY:n,offsetX:o,offsetY:a}}(u,d,r),x=(i.width-o)/f,b=(i.height-o)/g,_=Math.max(Math.min(x,b)/2,0),y=c(this.options.radius,_),v=(y-Math.max(y*r,0))/this._getVisibleDatasetWeightTotal();this.offsetX=p*y,this.offsetY=m*y,s.total=this.calculateTotal(),this.outerRadius=y-v*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-v*l,0),this.updateElements(n,0,n.length,t)}_circumference(t,e){const i=this.options,s=this._cachedMeta,n=this._getCircumference();return e&&i.animation.animateRotate||!this.chart.getDataVisibility(t)||null===s._parsed[t]||s.data[t].hidden?0:this.calculateCircumference(s._parsed[t]*n/O)}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.chartArea,r=o.options.animation,l=(a.left+a.right)/2,h=(a.top+a.bottom)/2,c=n&&r.animateScale,d=c?0:this.innerRadius,u=c?0:this.outerRadius,{sharedOptions:f,includeOptions:g}=this._getSharedOptions(e,s);let p,m=this._getRotation();for(p=0;p<e;++p)m+=this._circumference(p,n);for(p=e;p<e+i;++p){const e=this._circumference(p,n),i=t[p],o={x:l+this.offsetX,y:h+this.offsetY,startAngle:m,endAngle:m+e,circumference:e,outerRadius:u,innerRadius:d};g&&(o.options=f||this.resolveDataElementOptions(p,i.active?"active":s)),m+=e,this.updateElement(i,p,o,s)}}calculateTotal(){const t=this._cachedMeta,e=t.data;let i,s=0;for(i=0;i<e.length;i++){const n=t._parsed[i];null===n||isNaN(n)||!this.chart.getDataVisibility(i)||e[i].hidden||(s+=Math.abs(n))}return s}calculateCircumference(t){const e=this._cachedMeta.total;return e>0&&!isNaN(t)?O*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t],i.options.locale);return{label:s[t]||"",value:n}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,n,o,a,r;if(!t)for(s=0,n=i.data.datasets.length;s<n;++s)if(i.isDatasetVisible(s)){o=i.getDatasetMeta(s),t=o.data,a=o.controller;break}if(!t)return 0;for(s=0,n=t.length;s<n;++s)r=a.resolveDataElementOptions(s),"inner"!==r.borderAlign&&(e=Math.max(e,r.borderWidth||0,r.hoverBorderWidth||0));return e}getMaxOffset(t){let e=0;for(let i=0,s=t.length;i<s;++i){const t=this.resolveDataElementOptions(i);e=Math.max(e,t.offset||0,t.hoverOffset||0)}return e}_getRingWeightOffset(t){let e=0;for(let i=0;i<t;++i)this.chart.isDatasetVisible(i)&&(e+=this._getRingWeight(i));return e}_getRingWeight(t){return Math.max(l(this.chart.data.datasets[t].weight,1),0)}_getVisibleDatasetWeightTotal(){return this._getRingWeightOffset(this.chart.data.datasets.length)||1}}class Yn extends js{static id="polarArea";static defaults={dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0};static overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map(((e,n)=>{const o=t.getDatasetMeta(0).controller.getStyle(n);return{text:e,fillStyle:o.backgroundColor,strokeStyle:o.borderColor,fontColor:s,lineWidth:o.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(n),index:n}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:n}}parseObjectData(t,e,i,s){return ii.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach(((t,i)=>{const s=this.getParsed(i).r;!isNaN(s)&&this.chart.getDataVisibility(i)&&(s<e.min&&(e.min=s),s>e.max&&(e.max=s))})),e}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),n=Math.max(s/2,0),o=(n-Math.max(i.cutoutPercentage?n/100*i.cutoutPercentage:1,0))/t.getVisibleDatasetCount();this.outerRadius=n-o*this.index,this.innerRadius=this.outerRadius-o}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.options.animation,r=this._cachedMeta.rScale,l=r.xCenter,h=r.yCenter,c=r.getIndexAngle(0)-.5*C;let d,u=c;const f=360/this.countVisibleElements();for(d=0;d<e;++d)u+=this._computeAngle(d,s,f);for(d=e;d<e+i;d++){const e=t[d];let i=u,g=u+this._computeAngle(d,s,f),p=o.getDataVisibility(d)?r.getDistanceFromCenterForValue(this.getParsed(d).r):0;u=g,n&&(a.animateScale&&(p=0),a.animateRotate&&(i=g=c));const m={x:l,y:h,innerRadius:0,outerRadius:p,startAngle:i,endAngle:g,options:this.resolveDataElementOptions(d,e.active?"active":s)};this.updateElement(e,d,m,s)}}countVisibleElements(){const t=this._cachedMeta;let e=0;return t.data.forEach(((t,i)=>{!isNaN(this.getParsed(i).r)&&this.chart.getDataVisibility(i)&&e++})),e}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?$(this.resolveDataElementOptions(t,e).angle||i):0}}var Un=Object.freeze({__proto__:null,BarController:class extends js{static id="bar";static defaults={datasetElementType:!1,dataElementType:"bar",categoryPercentage:.8,barPercentage:.9,grouped:!0,animations:{numbers:{type:"number",properties:["x","y","base","width","height"]}}};static overrides={scales:{_index_:{type:"category",offset:!0,grid:{offset:!0}},_value_:{type:"linear",beginAtZero:!0}}};parsePrimitiveData(t,e,i,s){return Vn(t,e,i,s)}parseArrayData(t,e,i,s){return Vn(t,e,i,s)}parseObjectData(t,e,i,s){const{iScale:n,vScale:o}=t,{xAxisKey:a="x",yAxisKey:r="y"}=this._parsing,l="x"===n.axis?a:r,h="x"===o.axis?a:r,c=[];let d,u,f,g;for(d=i,u=i+s;d<u;++d)g=e[d],f={},f[n.axis]=n.parse(M(g,l),d),c.push(Fn(M(g,h),f,o,d));return c}updateRangeFromParsed(t,e,i,s){super.updateRangeFromParsed(t,e,i,s);const n=i._custom;n&&e===this._cachedMeta.vScale&&(t.min=Math.min(t.min,n.min),t.max=Math.max(t.max,n.max))}getMaxOverflow(){return 0}getLabelAndValue(t){const e=this._cachedMeta,{iScale:i,vScale:s}=e,n=this.getParsed(t),o=n._custom,a=Bn(o)?"["+o.start+", "+o.end+"]":""+s.getLabelForValue(n[s.axis]);return{label:""+i.getLabelForValue(n[i.axis]),value:a}}initialize(){this.enableOptionSharing=!0,super.initialize();this._cachedMeta.stack=this.getDataset().stack}update(t){const e=this._cachedMeta;this.updateElements(e.data,0,e.data.length,t)}updateElements(t,e,i,n){const o="reset"===n,{index:a,_cachedMeta:{vScale:r}}=this,l=r.getBasePixel(),h=r.isHorizontal(),c=this._getRuler(),{sharedOptions:d,includeOptions:u}=this._getSharedOptions(e,n);for(let f=e;f<e+i;f++){const e=this.getParsed(f),i=o||s(e[r.axis])?{base:l,head:l}:this._calculateBarValuePixels(f),g=this._calculateBarIndexPixels(f,c),p=(e._stacks||{})[r.axis],m={horizontal:h,base:i.base,enableBorderRadius:!p||Bn(e._custom)||a===p._top||a===p._bottom,x:h?i.head:g.center,y:h?g.center:i.head,height:h?g.size:Math.abs(i.size),width:h?Math.abs(i.size):g.size};u&&(m.options=d||this.resolveDataElementOptions(f,t[f].active?"active":n));const x=m.options||t[f].options;Wn(m,x,p,a),jn(m,x,c.ratio),this.updateElement(t[f],f,m,n)}}_getStacks(t,e){const{iScale:i}=this._cachedMeta,n=i.getMatchingVisibleMetas(this._type).filter((t=>t.controller.options.grouped)),o=i.options.stacked,a=[],r=this._cachedMeta.controller.getParsed(e),l=r&&r[i.axis],h=t=>{const e=t._parsed.find((t=>t[i.axis]===l)),n=e&&e[t.vScale.axis];if(s(n)||isNaN(n))return!0};for(const i of n)if((void 0===e||!h(i))&&((!1===o||-1===a.indexOf(i.stack)||void 0===o&&void 0===i.stack)&&a.push(i.stack),i.index===t))break;return a.length||a.push(void 0),a}_getStackCount(t){return this._getStacks(void 0,t).length}_getAxisCount(){return this._getAxis().length}getFirstScaleIdForIndexAxis(){const t=this.chart.scales,e=this.chart.options.indexAxis;return Object.keys(t).filter((i=>t[i].axis===e)).shift()}_getAxis(){const t={},e=this.getFirstScaleIdForIndexAxis();for(const i of this.chart.data.datasets)t[l("x"===this.chart.options.indexAxis?i.xAxisID:i.yAxisID,e)]=!0;return Object.keys(t)}_getStackIndex(t,e,i){const s=this._getStacks(t,i),n=void 0!==e?s.indexOf(e):-1;return-1===n?s.length-1:n}_getRuler(){const t=this.options,e=this._cachedMeta,i=e.iScale,s=[];let n,o;for(n=0,o=e.data.length;n<o;++n)s.push(i.getPixelForValue(this.getParsed(n)[i.axis],n));const a=t.barThickness;return{min:a||zn(e),pixels:s,start:i._startPixel,end:i._endPixel,stackCount:this._getStackCount(),scale:i,grouped:t.grouped,ratio:a?1:t.categoryPercentage*t.barPercentage}}_calculateBarValuePixels(t){const{_cachedMeta:{vScale:e,_stacked:i,index:n},options:{base:o,minBarLength:a}}=this,r=o||0,l=this.getParsed(t),h=l._custom,c=Bn(h);let d,u,f=l[e.axis],g=0,p=i?this.applyStack(e,l,i):f;p!==f&&(g=p-f,p=f),c&&(f=h.barStart,p=h.barEnd-h.barStart,0!==f&&F(f)!==F(h.barEnd)&&(g=0),g+=f);const m=s(o)||c?g:o;let x=e.getPixelForValue(m);if(d=this.chart.getDataVisibility(t)?e.getPixelForValue(g+p):x,u=d-x,Math.abs(u)<a){u=function(t,e,i){return 0!==t?F(t):(e.isHorizontal()?1:-1)*(e.min>=i?1:-1)}(u,e,r)*a,f===r&&(x-=u/2);const t=e.getPixelForDecimal(0),s=e.getPixelForDecimal(1),o=Math.min(t,s),h=Math.max(t,s);x=Math.max(Math.min(x,h),o),d=x+u,i&&!c&&(l._stacks[e.axis]._visualValues[n]=e.getValueForPixel(d)-e.getValueForPixel(x))}if(x===e.getPixelForValue(r)){const t=F(u)*e.getLineWidthForValue(r)/2;x+=t,u-=t}return{size:u,base:x,head:d,center:d+u/2}}_calculateBarIndexPixels(t,e){const i=e.scale,n=this.options,o=n.skipNull,a=l(n.maxBarThickness,1/0);let r,h;const c=this._getAxisCount();if(e.grouped){const i=o?this._getStackCount(t):e.stackCount,d="flex"===n.barThickness?function(t,e,i,s){const n=e.pixels,o=n[t];let a=t>0?n[t-1]:null,r=t<n.length-1?n[t+1]:null;const l=i.categoryPercentage;null===a&&(a=o-(null===r?e.end-e.start:r-o)),null===r&&(r=o+o-a);const h=o-(o-Math.min(a,r))/2*l;return{chunk:Math.abs(r-a)/2*l/s,ratio:i.barPercentage,start:h}}(t,e,n,i*c):function(t,e,i,n){const o=i.barThickness;let a,r;return s(o)?(a=e.min*i.categoryPercentage,r=i.barPercentage):(a=o*n,r=1),{chunk:a/n,ratio:r,start:e.pixels[t]-a/2}}(t,e,n,i*c),u="x"===this.chart.options.indexAxis?this.getDataset().xAxisID:this.getDataset().yAxisID,f=this._getAxis().indexOf(l(u,this.getFirstScaleIdForIndexAxis())),g=this._getStackIndex(this.index,this._cachedMeta.stack,o?t:void 0)+f;r=d.start+d.chunk*g+d.chunk/2,h=Math.min(a,d.chunk*d.ratio)}else r=i.getPixelForValue(this.getParsed(t)[i.axis],t),h=Math.min(a,e.min*e.ratio);return{base:r-h/2,head:r+h/2,center:r,size:h}}draw(){const t=this._cachedMeta,e=t.vScale,i=t.data,s=i.length;let n=0;for(;n<s;++n)null===this.getParsed(n)[e.axis]||i[n].hidden||i[n].draw(this._ctx)}},BubbleController:class extends js{static id="bubble";static defaults={datasetElementType:!1,dataElementType:"point",animations:{numbers:{type:"number",properties:["x","y","borderWidth","radius"]}}};static overrides={scales:{x:{type:"linear"},y:{type:"linear"}}};initialize(){this.enableOptionSharing=!0,super.initialize()}parsePrimitiveData(t,e,i,s){const n=super.parsePrimitiveData(t,e,i,s);for(let t=0;t<n.length;t++)n[t]._custom=this.resolveDataElementOptions(t+i).radius;return n}parseArrayData(t,e,i,s){const n=super.parseArrayData(t,e,i,s);for(let t=0;t<n.length;t++){const s=e[i+t];n[t]._custom=l(s[2],this.resolveDataElementOptions(t+i).radius)}return n}parseObjectData(t,e,i,s){const n=super.parseObjectData(t,e,i,s);for(let t=0;t<n.length;t++){const s=e[i+t];n[t]._custom=l(s&&s.r&&+s.r,this.resolveDataElementOptions(t+i).radius)}return n}getMaxOverflow(){const t=this._cachedMeta.data;let e=0;for(let i=t.length-1;i>=0;--i)e=Math.max(e,t[i].size(this.resolveDataElementOptions(i))/2);return e>0&&e}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart.data.labels||[],{xScale:s,yScale:n}=e,o=this.getParsed(t),a=s.getLabelForValue(o.x),r=n.getLabelForValue(o.y),l=o._custom;return{label:i[t]||"",value:"("+a+", "+r+(l?", "+l:"")+")"}}update(t){const e=this._cachedMeta.data;this.updateElements(e,0,e.length,t)}updateElements(t,e,i,s){const n="reset"===s,{iScale:o,vScale:a}=this._cachedMeta,{sharedOptions:r,includeOptions:l}=this._getSharedOptions(e,s),h=o.axis,c=a.axis;for(let d=e;d<e+i;d++){const e=t[d],i=!n&&this.getParsed(d),u={},f=u[h]=n?o.getPixelForDecimal(.5):o.getPixelForValue(i[h]),g=u[c]=n?a.getBasePixel():a.getPixelForValue(i[c]);u.skip=isNaN(f)||isNaN(g),l&&(u.options=r||this.resolveDataElementOptions(d,e.active?"active":s),n&&(u.options.radius=0)),this.updateElement(e,d,u,s)}}resolveDataElementOptions(t,e){const i=this.getParsed(t);let s=super.resolveDataElementOptions(t,e);s.$shared&&(s=Object.assign({},s,{$shared:!1}));const n=s.radius;return"active"!==e&&(s.radius=0),s.radius+=l(i&&i._custom,n),s}},DoughnutController:$n,LineController:class extends js{static id="line";static defaults={datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1};static overrides={scales:{_index_:{type:"category"},_value_:{type:"linear"}}};initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:n}=e,o=this.chart._animationsDisabled;let{start:a,count:r}=pt(e,s,o);this._drawStart=a,this._drawCount=r,mt(e)&&(a=0,r=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!n._decimated,i.points=s;const l=this.resolveDatasetElementOptions(t);this.options.showLine||(l.borderWidth=0),l.segment=this.options.segment,this.updateElement(i,void 0,{animated:!o,options:l},t),this.updateElements(s,a,r,t)}updateElements(t,e,i,n){const o="reset"===n,{iScale:a,vScale:r,_stacked:l,_dataset:h}=this._cachedMeta,{sharedOptions:c,includeOptions:d}=this._getSharedOptions(e,n),u=a.axis,f=r.axis,{spanGaps:g,segment:p}=this.options,m=N(g)?g:Number.POSITIVE_INFINITY,x=this.chart._animationsDisabled||o||"none"===n,b=e+i,_=t.length;let y=e>0&&this.getParsed(e-1);for(let i=0;i<_;++i){const g=t[i],_=x?g:{};if(i<e||i>=b){_.skip=!0;continue}const v=this.getParsed(i),M=s(v[f]),w=_[u]=a.getPixelForValue(v[u],i),k=_[f]=o||M?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,v,l):v[f],i);_.skip=isNaN(w)||isNaN(k)||M,_.stop=i>0&&Math.abs(v[u]-y[u])>m,p&&(_.parsed=v,_.raw=h.data[i]),d&&(_.options=c||this.resolveDataElementOptions(i,g.active?"active":n)),x||this.updateElement(g,i,_,n),y=v}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const n=s[0].size(this.resolveDataElementOptions(0)),o=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,n,o)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}},PieController:class extends $n{static id="pie";static defaults={cutout:0,rotation:0,circumference:360,radius:"100%"}},PolarAreaController:Yn,RadarController:class extends js{static id="radar";static defaults={datasetElementType:"line",dataElementType:"point",indexAxis:"r",showLine:!0,elements:{line:{fill:"start"}}};static overrides={aspectRatio:1,scales:{r:{type:"radialLinear"}}};getLabelAndValue(t){const e=this._cachedMeta.vScale,i=this.getParsed(t);return{label:e.getLabels()[t],value:""+e.getLabelForValue(i[e.axis])}}parseObjectData(t,e,i,s){return ii.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta,i=e.dataset,s=e.data||[],n=e.iScale.getLabels();if(i.points=s,"resize"!==t){const e=this.resolveDatasetElementOptions(t);this.options.showLine||(e.borderWidth=0);const o={_loop:!0,_fullLoop:n.length===s.length,options:e};this.updateElement(i,void 0,o,t)}this.updateElements(s,0,s.length,t)}updateElements(t,e,i,s){const n=this._cachedMeta.rScale,o="reset"===s;for(let a=e;a<e+i;a++){const e=t[a],i=this.resolveDataElementOptions(a,e.active?"active":s),r=n.getPointPositionForValue(a,this.getParsed(a).r),l=o?n.xCenter:r.x,h=o?n.yCenter:r.y,c={x:l,y:h,angle:r.angle,skip:isNaN(l)||isNaN(h),options:i};this.updateElement(e,a,c,s)}}},ScatterController:class extends js{static id="scatter";static defaults={datasetElementType:!1,dataElementType:"point",showLine:!1,fill:!1};static overrides={interaction:{mode:"point"},scales:{x:{type:"linear"},y:{type:"linear"}}};getLabelAndValue(t){const e=this._cachedMeta,i=this.chart.data.labels||[],{xScale:s,yScale:n}=e,o=this.getParsed(t),a=s.getLabelForValue(o.x),r=n.getLabelForValue(o.y);return{label:i[t]||"",value:"("+a+", "+r+")"}}update(t){const e=this._cachedMeta,{data:i=[]}=e,s=this.chart._animationsDisabled;let{start:n,count:o}=pt(e,i,s);if(this._drawStart=n,this._drawCount=o,mt(e)&&(n=0,o=i.length),this.options.showLine){this.datasetElementType||this.addElements();const{dataset:n,_dataset:o}=e;n._chart=this.chart,n._datasetIndex=this.index,n._decimated=!!o._decimated,n.points=i;const a=this.resolveDatasetElementOptions(t);a.segment=this.options.segment,this.updateElement(n,void 0,{animated:!s,options:a},t)}else this.datasetElementType&&(delete e.dataset,this.datasetElementType=!1);this.updateElements(i,n,o,t)}addElements(){const{showLine:t}=this.options;!this.datasetElementType&&t&&(this.datasetElementType=this.chart.registry.getElement("line")),super.addElements()}updateElements(t,e,i,n){const o="reset"===n,{iScale:a,vScale:r,_stacked:l,_dataset:h}=this._cachedMeta,c=this.resolveDataElementOptions(e,n),d=this.getSharedOptions(c),u=this.includeOptions(n,d),f=a.axis,g=r.axis,{spanGaps:p,segment:m}=this.options,x=N(p)?p:Number.POSITIVE_INFINITY,b=this.chart._animationsDisabled||o||"none"===n;let _=e>0&&this.getParsed(e-1);for(let c=e;c<e+i;++c){const e=t[c],i=this.getParsed(c),p=b?e:{},y=s(i[g]),v=p[f]=a.getPixelForValue(i[f],c),M=p[g]=o||y?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,i,l):i[g],c);p.skip=isNaN(v)||isNaN(M)||y,p.stop=c>0&&Math.abs(i[f]-_[f])>x,m&&(p.parsed=i,p.raw=h.data[c]),u&&(p.options=d||this.resolveDataElementOptions(c,e.active?"active":n)),b||this.updateElement(e,c,p,n),_=i}this.updateSharedOptions(d,n,c)}getMaxOverflow(){const t=this._cachedMeta,e=t.data||[];if(!this.options.showLine){let t=0;for(let i=e.length-1;i>=0;--i)t=Math.max(t,e[i].size(this.resolveDataElementOptions(i))/2);return t>0&&t}const i=t.dataset,s=i.options&&i.options.borderWidth||0;if(!e.length)return s;const n=e[0].size(this.resolveDataElementOptions(0)),o=e[e.length-1].size(this.resolveDataElementOptions(e.length-1));return Math.max(s,n,o)/2}}});function Xn(t,e,i,s){const n=vi(t.options.borderRadius,["outerStart","outerEnd","innerStart","innerEnd"]);const o=(i-e)/2,a=Math.min(o,s*e/2),r=t=>{const e=(i-Math.min(o,t))*s/2;return Z(t,0,Math.min(o,e))};return{outerStart:r(n.outerStart),outerEnd:r(n.outerEnd),innerStart:Z(n.innerStart,0,a),innerEnd:Z(n.innerEnd,0,a)}}function qn(t,e,i,s){return{x:i+t*Math.cos(e),y:s+t*Math.sin(e)}}function Kn(t,e,i,s,n,o){const{x:a,y:r,startAngle:l,pixelMargin:h,innerRadius:c}=e,d=Math.max(e.outerRadius+s+i-h,0),u=c>0?c+s+i+h:0;let f=0;const g=n-l;if(s){const t=((c>0?c-s:0)+(d>0?d-s:0))/2;f=(g-(0!==t?g*t/(t+s):g))/2}const p=(g-Math.max(.001,g*d-i/C)/d)/2,m=l+p+f,x=n-p-f,{outerStart:b,outerEnd:_,innerStart:y,innerEnd:v}=Xn(e,u,d,x-m),M=d-b,w=d-_,k=m+b/M,S=x-_/w,P=u+y,D=u+v,O=m+y/P,A=x-v/D;if(t.beginPath(),o){const e=(k+S)/2;if(t.arc(a,r,d,k,e),t.arc(a,r,d,e,S),_>0){const e=qn(w,S,a,r);t.arc(e.x,e.y,_,S,x+E)}const i=qn(D,x,a,r);if(t.lineTo(i.x,i.y),v>0){const e=qn(D,A,a,r);t.arc(e.x,e.y,v,x+E,A+Math.PI)}const s=(x-v/u+(m+y/u))/2;if(t.arc(a,r,u,x-v/u,s,!0),t.arc(a,r,u,s,m+y/u,!0),y>0){const e=qn(P,O,a,r);t.arc(e.x,e.y,y,O+Math.PI,m-E)}const n=qn(M,m,a,r);if(t.lineTo(n.x,n.y),b>0){const e=qn(M,k,a,r);t.arc(e.x,e.y,b,m-E,k)}}else{t.moveTo(a,r);const e=Math.cos(k)*d+a,i=Math.sin(k)*d+r;t.lineTo(e,i);const s=Math.cos(S)*d+a,n=Math.sin(S)*d+r;t.lineTo(s,n)}t.closePath()}function Gn(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r,options:l}=e,{borderWidth:h,borderJoinStyle:c,borderDash:d,borderDashOffset:u,borderRadius:f}=l,g="inner"===l.borderAlign;if(!h)return;t.setLineDash(d||[]),t.lineDashOffset=u,g?(t.lineWidth=2*h,t.lineJoin=c||"round"):(t.lineWidth=h,t.lineJoin=c||"bevel");let p=e.endAngle;if(o){Kn(t,e,i,s,p,n);for(let e=0;e<o;++e)t.stroke();isNaN(r)||(p=a+(r%O||O))}g&&function(t,e,i){const{startAngle:s,pixelMargin:n,x:o,y:a,outerRadius:r,innerRadius:l}=e;let h=n/r;t.beginPath(),t.arc(o,a,r,s-h,i+h),l>n?(h=n/l,t.arc(o,a,l,i+h,s-h,!0)):t.arc(o,a,n,i+E,s-E),t.closePath(),t.clip()}(t,e,p),l.selfJoin&&p-a>=C&&0===f&&"miter"!==c&&function(t,e,i){const{startAngle:s,x:n,y:o,outerRadius:a,innerRadius:r,options:l}=e,{borderWidth:h,borderJoinStyle:c}=l,d=Math.min(h/a,G(s-i));if(t.beginPath(),t.arc(n,o,a-h/2,s+d/2,i-d/2),r>0){const e=Math.min(h/r,G(s-i));t.arc(n,o,r+h/2,i-e/2,s+e/2,!0)}else{const e=Math.min(h/2,a*G(s-i));if("round"===c)t.arc(n,o,e,i-C/2,s+C/2,!0);else if("bevel"===c){const a=2*e*e,r=-a*Math.cos(i+C/2)+n,l=-a*Math.sin(i+C/2)+o,h=a*Math.cos(s+C/2)+n,c=a*Math.sin(s+C/2)+o;t.lineTo(r,l),t.lineTo(h,c)}}t.closePath(),t.moveTo(0,0),t.rect(0,0,t.canvas.width,t.canvas.height),t.clip("evenodd")}(t,e,p),o||(Kn(t,e,i,s,p,n),t.stroke())}function Jn(t,e,i=e){t.lineCap=l(i.borderCapStyle,e.borderCapStyle),t.setLineDash(l(i.borderDash,e.borderDash)),t.lineDashOffset=l(i.borderDashOffset,e.borderDashOffset),t.lineJoin=l(i.borderJoinStyle,e.borderJoinStyle),t.lineWidth=l(i.borderWidth,e.borderWidth),t.strokeStyle=l(i.borderColor,e.borderColor)}function Zn(t,e,i){t.lineTo(i.x,i.y)}function Qn(t,e,i={}){const s=t.length,{start:n=0,end:o=s-1}=i,{start:a,end:r}=e,l=Math.max(n,a),h=Math.min(o,r),c=n<a&&o<a||n>r&&o>r;return{count:s,start:l,loop:e.loop,ilen:h<l&&!c?s+h-l:h-l}}function to(t,e,i,s){const{points:n,options:o}=e,{count:a,start:r,loop:l,ilen:h}=Qn(n,i,s),c=function(t){return t.stepped?Fe:t.tension||"monotone"===t.cubicInterpolationMode?Ve:Zn}(o);let d,u,f,{move:g=!0,reverse:p}=s||{};for(d=0;d<=h;++d)u=n[(r+(p?h-d:d))%a],u.skip||(g?(t.moveTo(u.x,u.y),g=!1):c(t,f,u,p,o.stepped),f=u);return l&&(u=n[(r+(p?h:0))%a],c(t,f,u,p,o.stepped)),!!l}function eo(t,e,i,s){const n=e.points,{count:o,start:a,ilen:r}=Qn(n,i,s),{move:l=!0,reverse:h}=s||{};let c,d,u,f,g,p,m=0,x=0;const b=t=>(a+(h?r-t:t))%o,_=()=>{f!==g&&(t.lineTo(m,g),t.lineTo(m,f),t.lineTo(m,p))};for(l&&(d=n[b(0)],t.moveTo(d.x,d.y)),c=0;c<=r;++c){if(d=n[b(c)],d.skip)continue;const e=d.x,i=d.y,s=0|e;s===u?(i<f?f=i:i>g&&(g=i),m=(x*m+e)/++x):(_(),t.lineTo(e,i),u=s,x=0,f=g=i),p=i}_()}function io(t){const e=t.options,i=e.borderDash&&e.borderDash.length;return!(t._decimated||t._loop||e.tension||"monotone"===e.cubicInterpolationMode||e.stepped||i)?eo:to}const so="function"==typeof Path2D;function no(t,e,i,s){so&&!e.options.segment?function(t,e,i,s){let n=e._path;n||(n=e._path=new Path2D,e.path(n,i,s)&&n.closePath()),Jn(t,e.options),t.stroke(n)}(t,e,i,s):function(t,e,i,s){const{segments:n,options:o}=e,a=io(e);for(const r of n)Jn(t,o,r.style),t.beginPath(),a(t,e,r,{start:i,end:i+s-1})&&t.closePath(),t.stroke()}(t,e,i,s)}class oo extends $s{static id="line";static defaults={borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderWidth:3,capBezierPoints:!0,cubicInterpolationMode:"default",fill:!1,spanGaps:!1,stepped:!1,tension:0};static defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};static descriptors={_scriptable:!0,_indexable:t=>"borderDash"!==t&&"fill"!==t};constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||"monotone"===i.cubicInterpolationMode)&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;hi(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=zi(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],n=this.points,o=Ii(this,{property:e,start:s,end:s});if(!o.length)return;const a=[],r=function(t){return t.stepped?pi:t.tension||"monotone"===t.cubicInterpolationMode?mi:gi}(i);let l,h;for(l=0,h=o.length;l<h;++l){const{start:h,end:c}=o[l],d=n[h],u=n[c];if(d===u){a.push(d);continue}const f=r(d,u,Math.abs((s-d[e])/(u[e]-d[e])),i.stepped);f[e]=t[e],a.push(f)}return 1===a.length?a[0]:a}pathSegment(t,e,i){return io(this)(t,this,e,i)}path(t,e,i){const s=this.segments,n=io(this);let o=this._loop;e=e||0,i=i||this.points.length-e;for(const a of s)o&=n(t,this,a,{start:e,end:e+i-1});return!!o}draw(t,e,i,s){const n=this.options||{};(this.points||[]).length&&n.borderWidth&&(t.save(),no(t,this,i,s),t.restore()),this.animated&&(this._pointsUpdated=!1,this._path=void 0)}}function ao(t,e,i,s){const n=t.options,{[i]:o}=t.getProps([i],s);return Math.abs(e-o)<n.radius+n.hitRadius}function ro(t,e){const{x:i,y:s,base:n,width:o,height:a}=t.getProps(["x","y","base","width","height"],e);let r,l,h,c,d;return t.horizontal?(d=a/2,r=Math.min(i,n),l=Math.max(i,n),h=s-d,c=s+d):(d=o/2,r=i-d,l=i+d,h=Math.min(s,n),c=Math.max(s,n)),{left:r,top:h,right:l,bottom:c}}function lo(t,e,i,s){return t?0:Z(e,i,s)}function ho(t){const e=ro(t),i=e.right-e.left,s=e.bottom-e.top,n=function(t,e,i){const s=t.options.borderWidth,n=t.borderSkipped,o=Mi(s);return{t:lo(n.top,o.top,0,i),r:lo(n.right,o.right,0,e),b:lo(n.bottom,o.bottom,0,i),l:lo(n.left,o.left,0,e)}}(t,i/2,s/2),a=function(t,e,i){const{enableBorderRadius:s}=t.getProps(["enableBorderRadius"]),n=t.options.borderRadius,a=wi(n),r=Math.min(e,i),l=t.borderSkipped,h=s||o(n);return{topLeft:lo(!h||l.top||l.left,a.topLeft,0,r),topRight:lo(!h||l.top||l.right,a.topRight,0,r),bottomLeft:lo(!h||l.bottom||l.left,a.bottomLeft,0,r),bottomRight:lo(!h||l.bottom||l.right,a.bottomRight,0,r)}}(t,i/2,s/2);return{outer:{x:e.left,y:e.top,w:i,h:s,radius:a},inner:{x:e.left+n.l,y:e.top+n.t,w:i-n.l-n.r,h:s-n.t-n.b,radius:{topLeft:Math.max(0,a.topLeft-Math.max(n.t,n.l)),topRight:Math.max(0,a.topRight-Math.max(n.t,n.r)),bottomLeft:Math.max(0,a.bottomLeft-Math.max(n.b,n.l)),bottomRight:Math.max(0,a.bottomRight-Math.max(n.b,n.r))}}}}function co(t,e,i,s){const n=null===e,o=null===i,a=t&&!(n&&o)&&ro(t,s);return a&&(n||tt(e,a.left,a.right))&&(o||tt(i,a.top,a.bottom))}function uo(t,e){t.rect(e.x,e.y,e.w,e.h)}function fo(t,e,i={}){const s=t.x!==i.x?-e:0,n=t.y!==i.y?-e:0,o=(t.x+t.w!==i.x+i.w?e:0)-s,a=(t.y+t.h!==i.y+i.h?e:0)-n;return{x:t.x+s,y:t.y+n,w:t.w+o,h:t.h+a,radius:t.radius}}var go=Object.freeze({__proto__:null,ArcElement:class extends $s{static id="arc";static defaults={borderAlign:"center",borderColor:"#fff",borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0,selfJoin:!1};static defaultRoutes={backgroundColor:"backgroundColor"};static descriptors={_scriptable:!0,_indexable:t=>"borderDash"!==t};circumference;endAngle;fullCircles;innerRadius;outerRadius;pixelMargin;startAngle;constructor(t){super(),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.getProps(["x","y"],i),{angle:n,distance:o}=X(s,{x:t,y:e}),{startAngle:a,endAngle:r,innerRadius:h,outerRadius:c,circumference:d}=this.getProps(["startAngle","endAngle","innerRadius","outerRadius","circumference"],i),u=(this.options.spacing+this.options.borderWidth)/2,f=l(d,r-a),g=J(n,a,r)&&a!==r,p=f>=O||g,m=tt(o,h+u,c+u);return p&&m}getCenterPoint(t){const{x:e,y:i,startAngle:s,endAngle:n,innerRadius:o,outerRadius:a}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius"],t),{offset:r,spacing:l}=this.options,h=(s+n)/2,c=(o+a+l+r)/2;return{x:e+Math.cos(h)*c,y:i+Math.sin(h)*c}}tooltipPosition(t){return this.getCenterPoint(t)}draw(t){const{options:e,circumference:i}=this,s=(e.offset||0)/4,n=(e.spacing||0)/2,o=e.circular;if(this.pixelMargin="inner"===e.borderAlign?.33:0,this.fullCircles=i>O?Math.floor(i/O):0,0===i||this.innerRadius<0||this.outerRadius<0)return;t.save();const a=(this.startAngle+this.endAngle)/2;t.translate(Math.cos(a)*s,Math.sin(a)*s);const r=s*(1-Math.sin(Math.min(C,i||0)));t.fillStyle=e.backgroundColor,t.strokeStyle=e.borderColor,function(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r}=e;let l=e.endAngle;if(o){Kn(t,e,i,s,l,n);for(let e=0;e<o;++e)t.fill();isNaN(r)||(l=a+(r%O||O))}Kn(t,e,i,s,l,n),t.fill()}(t,this,r,n,o),Gn(t,this,r,n,o),t.restore()}},BarElement:class extends $s{static id="bar";static defaults={borderSkipped:"start",borderWidth:0,borderRadius:0,inflateAmount:"auto",pointStyle:void 0};static defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};constructor(t){super(),this.options=void 0,this.horizontal=void 0,this.base=void 0,this.width=void 0,this.height=void 0,this.inflateAmount=void 0,t&&Object.assign(this,t)}draw(t){const{inflateAmount:e,options:{borderColor:i,backgroundColor:s}}=this,{inner:n,outer:o}=ho(this),a=(r=o.radius).topLeft||r.topRight||r.bottomLeft||r.bottomRight?He:uo;var r;t.save(),o.w===n.w&&o.h===n.h||(t.beginPath(),a(t,fo(o,e,n)),t.clip(),a(t,fo(n,-e,o)),t.fillStyle=i,t.fill("evenodd")),t.beginPath(),a(t,fo(n,e)),t.fillStyle=s,t.fill(),t.restore()}inRange(t,e,i){return co(this,t,e,i)}inXRange(t,e){return co(this,t,null,e)}inYRange(t,e){return co(this,null,t,e)}getCenterPoint(t){const{x:e,y:i,base:s,horizontal:n}=this.getProps(["x","y","base","horizontal"],t);return{x:n?(e+s)/2:e,y:n?i:(i+s)/2}}getRange(t){return"x"===t?this.width/2:this.height/2}},LineElement:oo,PointElement:class extends $s{static id="point";parsed;skip;stop;static defaults={borderWidth:1,hitRadius:1,hoverBorderWidth:1,hoverRadius:4,pointStyle:"circle",radius:3,rotation:0};static defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};constructor(t){super(),this.options=void 0,this.parsed=void 0,this.skip=void 0,this.stop=void 0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.options,{x:n,y:o}=this.getProps(["x","y"],i);return Math.pow(t-n,2)+Math.pow(e-o,2)<Math.pow(s.hitRadius+s.radius,2)}inXRange(t,e){return ao(this,t,"x",e)}inYRange(t,e){return ao(this,t,"y",e)}getCenterPoint(t){const{x:e,y:i}=this.getProps(["x","y"],t);return{x:e,y:i}}size(t){let e=(t=t||this.options||{}).radius||0;e=Math.max(e,e&&t.hoverRadius||0);return 2*(e+(e&&t.borderWidth||0))}draw(t,e){const i=this.options;this.skip||i.radius<.1||!Re(this,e,this.size(i)/2)||(t.strokeStyle=i.borderColor,t.lineWidth=i.borderWidth,t.fillStyle=i.backgroundColor,Le(t,i,this.x,this.y))}getRange(){const t=this.options||{};return t.radius+t.hitRadius}}});function po(t,e,i,s){const n=t.indexOf(e);if(-1===n)return((t,e,i,s)=>("string"==typeof e?(i=t.push(e)-1,s.unshift({index:i,label:e})):isNaN(e)&&(i=null),i))(t,e,i,s);return n!==t.lastIndexOf(e)?i:n}function mo(t){const e=this.getLabels();return t>=0&&t<e.length?e[t]:t}function xo(t,e,{horizontal:i,minRotation:s}){const n=$(s),o=(i?Math.sin(n):Math.cos(n))||.001,a=.75*e*(""+t).length;return Math.min(e/o,a)}class bo extends tn{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return s(t)||("number"==typeof t||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:n}=this;const o=t=>s=e?s:t,a=t=>n=i?n:t;if(t){const t=F(s),e=F(n);t<0&&e<0?a(0):t>0&&e>0&&o(0)}if(s===n){let e=0===n?1:Math.abs(.05*n);a(n+e),t||o(s-e)}this.min=s,this.max=n}getTickLimit(){const t=this.options.ticks;let e,{maxTicksLimit:i,stepSize:s}=t;return s?(e=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,e>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.`),e=1e3)):(e=this.computeTickLimit(),i=i||11),i&&(e=Math.min(i,e)),e}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const n=function(t,e){const i=[],{bounds:n,step:o,min:a,max:r,precision:l,count:h,maxTicks:c,maxDigits:d,includeBounds:u}=t,f=o||1,g=c-1,{min:p,max:m}=e,x=!s(a),b=!s(r),_=!s(h),y=(m-p)/(d+1);let v,M,w,k,S=B((m-p)/g/f)*f;if(S<1e-14&&!x&&!b)return[{value:p},{value:m}];k=Math.ceil(m/S)-Math.floor(p/S),k>g&&(S=B(k*S/g/f)*f),s(l)||(v=Math.pow(10,l),S=Math.ceil(S*v)/v),"ticks"===n?(M=Math.floor(p/S)*S,w=Math.ceil(m/S)*S):(M=p,w=m),x&&b&&o&&H((r-a)/o,S/1e3)?(k=Math.round(Math.min((r-a)/S,c)),S=(r-a)/k,M=a,w=r):_?(M=x?a:M,w=b?r:w,k=h-1,S=(w-M)/k):(k=(w-M)/S,k=V(k,Math.round(k),S/1e3)?Math.round(k):Math.ceil(k));const P=Math.max(U(S),U(M));v=Math.pow(10,s(l)?P:l),M=Math.round(M*v)/v,w=Math.round(w*v)/v;let D=0;for(x&&(u&&M!==a?(i.push({value:a}),M<a&&D++,V(Math.round((M+D*S)*v)/v,a,xo(a,y,t))&&D++):M<a&&D++);D<k;++D){const t=Math.round((M+D*S)*v)/v;if(b&&t>r)break;i.push({value:t})}return b&&u&&w!==r?i.length&&V(i[i.length-1].value,r,xo(r,y,t))?i[i.length-1].value=r:i.push({value:r}):b&&w!==r||i.push({value:w}),i}({maxTicks:i,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:!1!==e.includeBounds},this._range||this);return"ticks"===t.bounds&&j(n,this,"value"),t.reverse?(n.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),n}configure(){const t=this.ticks;let e=this.min,i=this.max;if(super.configure(),this.options.offset&&t.length){const s=(i-e)/Math.max(t.length-1,1)/2;e-=s,i+=s}this._startValue=e,this._endValue=i,this._valueRange=i-e}getLabelForValue(t){return ne(t,this.chart.options.locale,this.options.ticks.format)}}class _o extends bo{static id="linear";static defaults={ticks:{callback:ae.formatters.numeric}};determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?t:0,this.max=a(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,i=$(this.options.ticks.minRotation),s=(t?Math.sin(i):Math.cos(i))||.001,n=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,n.lineHeight/s))}getPixelForValue(t){return null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}const yo=t=>Math.floor(z(t)),vo=(t,e)=>Math.pow(10,yo(t)+e);function Mo(t){return 1===t/Math.pow(10,yo(t))}function wo(t,e,i){const s=Math.pow(10,i),n=Math.floor(t/s);return Math.ceil(e/s)-n}function ko(t,{min:e,max:i}){e=r(t.min,e);const s=[],n=yo(e);let o=function(t,e){let i=yo(e-t);for(;wo(t,e,i)>10;)i++;for(;wo(t,e,i)<10;)i--;return Math.min(i,yo(t))}(e,i),a=o<0?Math.pow(10,Math.abs(o)):1;const l=Math.pow(10,o),h=n>o?Math.pow(10,n):0,c=Math.round((e-h)*a)/a,d=Math.floor((e-h)/l/10)*l*10;let u=Math.floor((c-d)/Math.pow(10,o)),f=r(t.min,Math.round((h+d+u*Math.pow(10,o))*a)/a);for(;f<i;)s.push({value:f,major:Mo(f),significand:u}),u>=10?u=u<15?15:20:u++,u>=20&&(o++,u=2,a=o>=0?1:a),f=Math.round((h+d+u*Math.pow(10,o))*a)/a;const g=r(t.max,f);return s.push({value:g,major:Mo(g),significand:u}),s}class So extends tn{static id="logarithmic";static defaults={ticks:{callback:ae.formatters.logarithmic,major:{enabled:!0}}};constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const i=bo.prototype.parse.apply(this,[t,e]);if(0!==i)return a(i)&&i>0?i:null;this._zero=!0}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?Math.max(0,t):null,this.max=a(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!a(this._userMin)&&(this.min=t===vo(this.min,0)?vo(this.min,-1):vo(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const n=e=>i=t?i:e,o=t=>s=e?s:t;i===s&&(i<=0?(n(1),o(10)):(n(vo(i,-1)),o(vo(s,1)))),i<=0&&n(vo(s,-1)),s<=0&&o(vo(i,1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e=ko({min:this._userMin,max:this._userMax},this);return"ticks"===t.bounds&&j(e,this,"value"),t.reverse?(e.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),e}getLabelForValue(t){return void 0===t?"0":ne(t,this.chart.options.locale,this.options.ticks.format)}configure(){const t=this.min;super.configure(),this._startValue=z(t),this._valueRange=z(this.max)-z(t)}getPixelForValue(t){return void 0!==t&&0!==t||(t=this.min),null===t||isNaN(t)?NaN:this.getPixelForDecimal(t===this.min?0:(z(t)-this._startValue)/this._valueRange)}getValueForPixel(t){const e=this.getDecimalForPixel(t);return Math.pow(10,this._startValue+e*this._valueRange)}}function Po(t){const e=t.ticks;if(e.display&&t.display){const t=ki(e.backdropPadding);return l(e.font&&e.font.size,ue.font.size)+t.height}return 0}function Do(t,e,i,s,n){return t===s||t===n?{start:e-i/2,end:e+i/2}:t<s||t>n?{start:e-i,end:e}:{start:e,end:e+i}}function Co(t){const e={l:t.left+t._padding.left,r:t.right-t._padding.right,t:t.top+t._padding.top,b:t.bottom-t._padding.bottom},i=Object.assign({},e),s=[],o=[],a=t._pointLabels.length,r=t.options.pointLabels,l=r.centerPointLabels?C/a:0;for(let u=0;u<a;u++){const a=r.setContext(t.getPointLabelContext(u));o[u]=a.padding;const f=t.getPointPosition(u,t.drawingArea+o[u],l),g=Si(a.font),p=(h=t.ctx,c=g,d=n(d=t._pointLabels[u])?d:[d],{w:Oe(h,c.string,d),h:d.length*c.lineHeight});s[u]=p;const m=G(t.getIndexAngle(u)+l),x=Math.round(Y(m));Oo(i,e,m,Do(x,f.x,p.w,0,180),Do(x,f.y,p.h,90,270))}var h,c,d;t.setCenterPoint(e.l-i.l,i.r-e.r,e.t-i.t,i.b-e.b),t._pointLabelItems=function(t,e,i){const s=[],n=t._pointLabels.length,o=t.options,{centerPointLabels:a,display:r}=o.pointLabels,l={extra:Po(o)/2,additionalAngle:a?C/n:0};let h;for(let o=0;o<n;o++){l.padding=i[o],l.size=e[o];const n=Ao(t,o,l);s.push(n),"auto"===r&&(n.visible=To(n,h),n.visible&&(h=n))}return s}(t,s,o)}function Oo(t,e,i,s,n){const o=Math.abs(Math.sin(i)),a=Math.abs(Math.cos(i));let r=0,l=0;s.start<e.l?(r=(e.l-s.start)/o,t.l=Math.min(t.l,e.l-r)):s.end>e.r&&(r=(s.end-e.r)/o,t.r=Math.max(t.r,e.r+r)),n.start<e.t?(l=(e.t-n.start)/a,t.t=Math.min(t.t,e.t-l)):n.end>e.b&&(l=(n.end-e.b)/a,t.b=Math.max(t.b,e.b+l))}function Ao(t,e,i){const s=t.drawingArea,{extra:n,additionalAngle:o,padding:a,size:r}=i,l=t.getPointPosition(e,s+n+a,o),h=Math.round(Y(G(l.angle+E))),c=function(t,e,i){90===i||270===i?t-=e/2:(i>270||i<90)&&(t-=e);return t}(l.y,r.h,h),d=function(t){if(0===t||180===t)return"center";if(t<180)return"left";return"right"}(h),u=function(t,e,i){"right"===i?t-=e:"center"===i&&(t-=e/2);return t}(l.x,r.w,d);return{visible:!0,x:l.x,y:c,textAlign:d,left:u,top:c,right:u+r.w,bottom:c+r.h}}function To(t,e){if(!e)return!0;const{left:i,top:s,right:n,bottom:o}=t;return!(Re({x:i,y:s},e)||Re({x:i,y:o},e)||Re({x:n,y:s},e)||Re({x:n,y:o},e))}function Lo(t,e,i){const{left:n,top:o,right:a,bottom:r}=i,{backdropColor:l}=e;if(!s(l)){const i=wi(e.borderRadius),s=ki(e.backdropPadding);t.fillStyle=l;const h=n-s.left,c=o-s.top,d=a-n+s.width,u=r-o+s.height;Object.values(i).some((t=>0!==t))?(t.beginPath(),He(t,{x:h,y:c,w:d,h:u,radius:i}),t.fill()):t.fillRect(h,c,d,u)}}function Eo(t,e,i,s){const{ctx:n}=t;if(i)n.arc(t.xCenter,t.yCenter,e,0,O);else{let i=t.getPointPosition(0,e);n.moveTo(i.x,i.y);for(let o=1;o<s;o++)i=t.getPointPosition(o,e),n.lineTo(i.x,i.y)}}class Ro extends bo{static id="radialLinear";static defaults={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:ae.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback:t=>t,padding:5,centerPointLabels:!1}};static defaultRoutes={"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"};static descriptors={angleLines:{_fallback:"grid"}};constructor(t){super(t),this.xCenter=void 0,this.yCenter=void 0,this.drawingArea=void 0,this._pointLabels=[],this._pointLabelItems=[]}setDimensions(){const t=this._padding=ki(Po(this.options)/2),e=this.width=this.maxWidth-t.width,i=this.height=this.maxHeight-t.height;this.xCenter=Math.floor(this.left+e/2+t.left),this.yCenter=Math.floor(this.top+i/2+t.top),this.drawingArea=Math.floor(Math.min(e,i)/2)}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!1);this.min=a(t)&&!isNaN(t)?t:0,this.max=a(e)&&!isNaN(e)?e:0,this.handleTickRangeOptions()}computeTickLimit(){return Math.ceil(this.drawingArea/Po(this.options))}generateTickLabels(t){bo.prototype.generateTickLabels.call(this,t),this._pointLabels=this.getLabels().map(((t,e)=>{const i=d(this.options.pointLabels.callback,[t,e],this);return i||0===i?i:""})).filter(((t,e)=>this.chart.getDataVisibility(e)))}fit(){const t=this.options;t.display&&t.pointLabels.display?Co(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){return G(t*(O/(this._pointLabels.length||1))+$(this.options.startAngle||0))}getDistanceFromCenterForValue(t){if(s(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(s(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t<e.length){const i=e[t];return function(t,e,i){return Ci(t,{label:i,index:e,type:"pointLabel"})}(this.getContext(),t,i)}}getPointPosition(t,e,i=0){const s=this.getIndexAngle(t)-E+i;return{x:Math.cos(s)*e+this.xCenter,y:Math.sin(s)*e+this.yCenter,angle:s}}getPointPositionForValue(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))}getBasePosition(t){return this.getPointPositionForValue(t||0,this.getBaseValue())}getPointLabelPosition(t){const{left:e,top:i,right:s,bottom:n}=this._pointLabelItems[t];return{left:e,top:i,right:s,bottom:n}}drawBackground(){const{backgroundColor:t,grid:{circular:e}}=this.options;if(t){const i=this.ctx;i.save(),i.beginPath(),Eo(this,this.getDistanceFromCenterForValue(this._endValue),e,this._pointLabels.length),i.closePath(),i.fillStyle=t,i.fill(),i.restore()}}drawGrid(){const t=this.ctx,e=this.options,{angleLines:i,grid:s,border:n}=e,o=this._pointLabels.length;let a,r,l;if(e.pointLabels.display&&function(t,e){const{ctx:i,options:{pointLabels:s}}=t;for(let n=e-1;n>=0;n--){const e=t._pointLabelItems[n];if(!e.visible)continue;const o=s.setContext(t.getPointLabelContext(n));Lo(i,o,e);const a=Si(o.font),{x:r,y:l,textAlign:h}=e;Ne(i,t._pointLabels[n],r,l+a.lineHeight/2,a,{color:o.color,textAlign:h,textBaseline:"middle"})}}(this,o),s.display&&this.ticks.forEach(((t,e)=>{if(0!==e||0===e&&this.min<0){r=this.getDistanceFromCenterForValue(t.value);const i=this.getContext(e),a=s.setContext(i),l=n.setContext(i);!function(t,e,i,s,n){const o=t.ctx,a=e.circular,{color:r,lineWidth:l}=e;!a&&!s||!r||!l||i<0||(o.save(),o.strokeStyle=r,o.lineWidth=l,o.setLineDash(n.dash||[]),o.lineDashOffset=n.dashOffset,o.beginPath(),Eo(t,i,a,s),o.closePath(),o.stroke(),o.restore())}(this,a,r,o,l)}})),i.display){for(t.save(),a=o-1;a>=0;a--){const s=i.setContext(this.getPointLabelContext(a)),{color:n,lineWidth:o}=s;o&&n&&(t.lineWidth=o,t.strokeStyle=n,t.setLineDash(s.borderDash),t.lineDashOffset=s.borderDashOffset,r=this.getDistanceFromCenterForValue(e.reverse?this.min:this.max),l=this.getPointPosition(a,r),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(l.x,l.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let n,o;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach(((s,a)=>{if(0===a&&this.min>=0&&!e.reverse)return;const r=i.setContext(this.getContext(a)),l=Si(r.font);if(n=this.getDistanceFromCenterForValue(this.ticks[a].value),r.showLabelBackdrop){t.font=l.string,o=t.measureText(s.label).width,t.fillStyle=r.backdropColor;const e=ki(r.backdropPadding);t.fillRect(-o/2-e.left,-n-l.size/2-e.top,o+e.width,l.size+e.height)}Ne(t,s.label,0,-n,l,{color:r.color,strokeColor:r.textStrokeColor,strokeWidth:r.textStrokeWidth})})),t.restore()}drawTitle(){}}const Io={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},zo=Object.keys(Io);function Fo(t,e){return t-e}function Vo(t,e){if(s(e))return null;const i=t._adapter,{parser:n,round:o,isoWeekday:r}=t._parseOpts;let l=e;return"function"==typeof n&&(l=n(l)),a(l)||(l="string"==typeof n?i.parse(l,n):i.parse(l)),null===l?null:(o&&(l="week"!==o||!N(r)&&!0!==r?i.startOf(l,o):i.startOf(l,"isoWeek",r)),+l)}function Bo(t,e,i,s){const n=zo.length;for(let o=zo.indexOf(t);o<n-1;++o){const t=Io[zo[o]],n=t.steps?t.steps:Number.MAX_SAFE_INTEGER;if(t.common&&Math.ceil((i-e)/(n*t.size))<=s)return zo[o]}return zo[n-1]}function Wo(t,e,i){if(i){if(i.length){const{lo:s,hi:n}=et(i,e);t[i[s]>=e?i[s]:i[n]]=!0}}else t[e]=!0}function No(t,e,i){const s=[],n={},o=e.length;let a,r;for(a=0;a<o;++a)r=e[a],n[r]=a,s.push({value:r,major:!1});return 0!==o&&i?function(t,e,i,s){const n=t._adapter,o=+n.startOf(e[0].value,s),a=e[e.length-1].value;let r,l;for(r=o;r<=a;r=+n.add(r,1,s))l=i[r],l>=0&&(e[l].major=!0);return e}(t,s,n,i):s}class Ho extends tn{static id="time";static defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{source:"auto",callback:!1,major:{enabled:!1}}};constructor(t){super(t),this._cache={data:[],labels:[],all:[]},this._unit="day",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(t,e={}){const i=t.time||(t.time={}),s=this._adapter=new In._date(t.adapters.date);s.init(e),b(i.displayFormats,s.formats()),this._parseOpts={parser:i.parser,round:i.round,isoWeekday:i.isoWeekday},super.init(t),this._normalized=e.normalized}parse(t,e){return void 0===t?null:Vo(this,t)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const t=this.options,e=this._adapter,i=t.time.unit||"day";let{min:s,max:n,minDefined:o,maxDefined:r}=this.getUserBounds();function l(t){o||isNaN(t.min)||(s=Math.min(s,t.min)),r||isNaN(t.max)||(n=Math.max(n,t.max))}o&&r||(l(this._getLabelBounds()),"ticks"===t.bounds&&"labels"===t.ticks.source||l(this.getMinMax(!1))),s=a(s)&&!isNaN(s)?s:+e.startOf(Date.now(),i),n=a(n)&&!isNaN(n)?n:+e.endOf(Date.now(),i)+1,this.min=Math.min(s,n-1),this.max=Math.max(s+1,n)}_getLabelBounds(){const t=this.getLabelTimestamps();let e=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;return t.length&&(e=t[0],i=t[t.length-1]),{min:e,max:i}}buildTicks(){const t=this.options,e=t.time,i=t.ticks,s="labels"===i.source?this.getLabelTimestamps():this._generate();"ticks"===t.bounds&&s.length&&(this.min=this._userMin||s[0],this.max=this._userMax||s[s.length-1]);const n=this.min,o=nt(s,n,this.max);return this._unit=e.unit||(i.autoSkip?Bo(e.minUnit,this.min,this.max,this._getLabelCapacity(n)):function(t,e,i,s,n){for(let o=zo.length-1;o>=zo.indexOf(i);o--){const i=zo[o];if(Io[i].common&&t._adapter.diff(n,s,i)>=e-1)return i}return zo[i?zo.indexOf(i):0]}(this,o.length,e.minUnit,this.min,this.max)),this._majorUnit=i.major.enabled&&"year"!==this._unit?function(t){for(let e=zo.indexOf(t)+1,i=zo.length;e<i;++e)if(Io[zo[e]].common)return zo[e]}(this._unit):void 0,this.initOffsets(s),t.reverse&&o.reverse(),No(this,o,this._majorUnit)}afterAutoSkip(){this.options.offsetAfterAutoskip&&this.initOffsets(this.ticks.map((t=>+t.value)))}initOffsets(t=[]){let e,i,s=0,n=0;this.options.offset&&t.length&&(e=this.getDecimalForValue(t[0]),s=1===t.length?1-e:(this.getDecimalForValue(t[1])-e)/2,i=this.getDecimalForValue(t[t.length-1]),n=1===t.length?i:(i-this.getDecimalForValue(t[t.length-2]))/2);const o=t.length<3?.5:.25;s=Z(s,0,o),n=Z(n,0,o),this._offsets={start:s,end:n,factor:1/(s+1+n)}}_generate(){const t=this._adapter,e=this.min,i=this.max,s=this.options,n=s.time,o=n.unit||Bo(n.minUnit,e,i,this._getLabelCapacity(e)),a=l(s.ticks.stepSize,1),r="week"===o&&n.isoWeekday,h=N(r)||!0===r,c={};let d,u,f=e;if(h&&(f=+t.startOf(f,"isoWeek",r)),f=+t.startOf(f,h?"day":o),t.diff(i,e,o)>1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+o);const g="data"===s.ticks.source&&this.getDataTimestamps();for(d=f,u=0;d<i;d=+t.add(d,a,o),u++)Wo(c,d,g);return d!==i&&"ticks"!==s.bounds&&1!==u||Wo(c,d,g),Object.keys(c).sort(Fo).map((t=>+t))}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}format(t,e){const i=this.options.time.displayFormats,s=this._unit,n=e||i[s];return this._adapter.format(t,n)}_tickFormatFunction(t,e,i,s){const n=this.options,o=n.ticks.callback;if(o)return d(o,[t,e,i],this);const a=n.time.displayFormats,r=this._unit,l=this._majorUnit,h=r&&a[r],c=l&&a[l],u=i[e],f=l&&c&&u&&u.major;return this._adapter.format(t,s||(f?c:h))}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e<i;++e)s=t[e],s.label=this._tickFormatFunction(s.value,e,t)}getDecimalForValue(t){return null===t?NaN:(t-this.min)/(this.max-this.min)}getPixelForValue(t){const e=this._offsets,i=this.getDecimalForValue(t);return this.getPixelForDecimal((e.start+i)*e.factor)}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return this.min+i*(this.max-this.min)}_getLabelSize(t){const e=this.options.ticks,i=this.ctx.measureText(t).width,s=$(this.isHorizontal()?e.maxRotation:e.minRotation),n=Math.cos(s),o=Math.sin(s),a=this._resolveTickFontOptions(0).size;return{w:i*n+a*o,h:i*o+a*n}}_getLabelCapacity(t){const e=this.options.time,i=e.displayFormats,s=i[e.unit]||i.millisecond,n=this._tickFormatFunction(t,0,No(this,[t],this._majorUnit),s),o=this._getLabelSize(n),a=Math.floor(this.isHorizontal()?this.width/o.w:this.height/o.h)-1;return a>0?a:1}getDataTimestamps(){let t,e,i=this._cache.data||[];if(i.length)return i;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(t=0,e=s.length;t<e;++t)i=i.concat(s[t].controller.getAllParsedValues(this));return this._cache.data=this.normalize(i)}getLabelTimestamps(){const t=this._cache.labels||[];let e,i;if(t.length)return t;const s=this.getLabels();for(e=0,i=s.length;e<i;++e)t.push(Vo(this,s[e]));return this._cache.labels=this._normalized?t:this.normalize(t)}normalize(t){return lt(t.sort(Fo))}}function jo(t,e,i){let s,n,o,a,r=0,l=t.length-1;i?(e>=t[r].pos&&e<=t[l].pos&&({lo:r,hi:l}=it(t,"pos",e)),({pos:s,time:o}=t[r]),({pos:n,time:a}=t[l])):(e>=t[r].time&&e<=t[l].time&&({lo:r,hi:l}=it(t,"time",e)),({time:s,pos:o}=t[r]),({time:n,pos:a}=t[l]));const h=n-s;return h?o+(a-o)*(e-s)/h:o}var $o=Object.freeze({__proto__:null,CategoryScale:class extends tn{static id="category";static defaults={ticks:{callback:mo}};constructor(t){super(t),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(t){const e=this._addedLabels;if(e.length){const t=this.getLabels();for(const{index:i,label:s}of e)t[i]===s&&t.splice(i,1);this._addedLabels=[]}super.init(t)}parse(t,e){if(s(t))return null;const i=this.getLabels();return((t,e)=>null===t?null:Z(Math.round(t),0,e))(e=isFinite(e)&&i[e]===t?e:po(i,t,l(e,t),this._addedLabels),i.length-1)}determineDataLimits(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let{min:i,max:s}=this.getMinMax(!0);"ticks"===this.options.bounds&&(t||(i=0),e||(s=this.getLabels().length-1)),this.min=i,this.max=s}buildTicks(){const t=this.min,e=this.max,i=this.options.offset,s=[];let n=this.getLabels();n=0===t&&e===n.length-1?n:n.slice(t,e+1),this._valueRange=Math.max(n.length-(i?0:1),1),this._startValue=this.min-(i?.5:0);for(let i=t;i<=e;i++)s.push({value:i});return s}getLabelForValue(t){return mo.call(this,t)}configure(){super.configure(),this.isHorizontal()||(this._reversePixels=!this._reversePixels)}getPixelForValue(t){return"number"!=typeof t&&(t=this.parse(t)),null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}},LinearScale:_o,LogarithmicScale:So,RadialLinearScale:Ro,TimeScale:Ho,TimeSeriesScale:class extends Ho{static id="timeseries";static defaults=Ho.defaults;constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=jo(e,this.min),this._tableRange=jo(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],n=[];let o,a,r,l,h;for(o=0,a=t.length;o<a;++o)l=t[o],l>=e&&l<=i&&s.push(l);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(o=0,a=s.length;o<a;++o)h=s[o+1],r=s[o-1],l=s[o],Math.round((h+r)/2)!==l&&n.push({time:l,pos:o/(a-1)});return n}_generate(){const t=this.min,e=this.max;let i=super.getDataTimestamps();return i.includes(t)&&i.length||i.splice(0,0,t),i.includes(e)&&1!==i.length||i.push(e),i.sort(((t,e)=>t-e))}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),i=this.getLabelTimestamps();return t=e.length&&i.length?this.normalize(e.concat(i)):e.length?e:i,t=this._cache.all=t,t}getDecimalForValue(t){return(jo(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return jo(this._table,i*this._tableRange+this._minPos,!0)}}});const Yo=["rgb(54, 162, 235)","rgb(255, 99, 132)","rgb(255, 159, 64)","rgb(255, 205, 86)","rgb(75, 192, 192)","rgb(153, 102, 255)","rgb(201, 203, 207)"],Uo=Yo.map((t=>t.replace("rgb(","rgba(").replace(")",", 0.5)")));function Xo(t){return Yo[t%Yo.length]}function qo(t){return Uo[t%Uo.length]}function Ko(t){let e=0;return(i,s)=>{const n=t.getDatasetMeta(s).controller;n instanceof $n?e=function(t,e){return t.backgroundColor=t.data.map((()=>Xo(e++))),e}(i,e):n instanceof Yn?e=function(t,e){return t.backgroundColor=t.data.map((()=>qo(e++))),e}(i,e):n&&(e=function(t,e){return t.borderColor=Xo(e),t.backgroundColor=qo(e),++e}(i,e))}}function Go(t){let e;for(e in t)if(t[e].borderColor||t[e].backgroundColor)return!0;return!1}var Jo={id:"colors",defaults:{enabled:!0,forceOverride:!1},beforeLayout(t,e,i){if(!i.enabled)return;const{data:{datasets:s},options:n}=t.config,{elements:o}=n,a=Go(s)||(r=n)&&(r.borderColor||r.backgroundColor)||o&&Go(o)||"rgba(0,0,0,0.1)"!==ue.borderColor||"rgba(0,0,0,0.1)"!==ue.backgroundColor;var r;if(!i.forceOverride&&a)return;const l=Ko(t);s.forEach(l)}};function Zo(t){if(t._decimated){const e=t._data;delete t._decimated,delete t._data,Object.defineProperty(t,"data",{configurable:!0,enumerable:!0,writable:!0,value:e})}}function Qo(t){t.data.datasets.forEach((t=>{Zo(t)}))}var ta={id:"decimation",defaults:{algorithm:"min-max",enabled:!1},beforeElementsUpdate:(t,e,i)=>{if(!i.enabled)return void Qo(t);const n=t.width;t.data.datasets.forEach(((e,o)=>{const{_data:a,indexAxis:r}=e,l=t.getDatasetMeta(o),h=a||e.data;if("y"===Pi([r,t.options.indexAxis]))return;if(!l.controller.supportsDecimation)return;const c=t.scales[l.xAxisID];if("linear"!==c.type&&"time"!==c.type)return;if(t.options.parsing)return;let{start:d,count:u}=function(t,e){const i=e.length;let s,n=0;const{iScale:o}=t,{min:a,max:r,minDefined:l,maxDefined:h}=o.getUserBounds();return l&&(n=Z(it(e,o.axis,a).lo,0,i-1)),s=h?Z(it(e,o.axis,r).hi+1,n,i)-n:i-n,{start:n,count:s}}(l,h);if(u<=(i.threshold||4*n))return void Zo(e);let f;switch(s(a)&&(e._data=h,delete e.data,Object.defineProperty(e,"data",{configurable:!0,enumerable:!0,get:function(){return this._decimated},set:function(t){this._data=t}})),i.algorithm){case"lttb":f=function(t,e,i,s,n){const o=n.samples||s;if(o>=i)return t.slice(e,e+i);const a=[],r=(i-2)/(o-2);let l=0;const h=e+i-1;let c,d,u,f,g,p=e;for(a[l++]=t[p],c=0;c<o-2;c++){let s,n=0,o=0;const h=Math.floor((c+1)*r)+1+e,m=Math.min(Math.floor((c+2)*r)+1,i)+e,x=m-h;for(s=h;s<m;s++)n+=t[s].x,o+=t[s].y;n/=x,o/=x;const b=Math.floor(c*r)+1+e,_=Math.min(Math.floor((c+1)*r)+1,i)+e,{x:y,y:v}=t[p];for(u=f=-1,s=b;s<_;s++)f=.5*Math.abs((y-n)*(t[s].y-v)-(y-t[s].x)*(o-v)),f>u&&(u=f,d=t[s],g=s);a[l++]=d,p=g}return a[l++]=t[h],a}(h,d,u,n,i);break;case"min-max":f=function(t,e,i,n){let o,a,r,l,h,c,d,u,f,g,p=0,m=0;const x=[],b=e+i-1,_=t[e].x,y=t[b].x-_;for(o=e;o<e+i;++o){a=t[o],r=(a.x-_)/y*n,l=a.y;const e=0|r;if(e===h)l<f?(f=l,c=o):l>g&&(g=l,d=o),p=(m*p+a.x)/++m;else{const i=o-1;if(!s(c)&&!s(d)){const e=Math.min(c,d),s=Math.max(c,d);e!==u&&e!==i&&x.push({...t[e],x:p}),s!==u&&s!==i&&x.push({...t[s],x:p})}o>0&&i!==u&&x.push(t[i]),x.push(a),h=e,m=0,f=g=l,c=d=u=o}}return x}(h,d,u,n);break;default:throw new Error(`Unsupported decimation algorithm '${i.algorithm}'`)}e._decimated=f}))},destroy(t){Qo(t)}};function ea(t,e,i,s){if(s)return;let n=e[t],o=i[t];return"angle"===t&&(n=G(n),o=G(o)),{property:t,start:n,end:o}}function ia(t,e,i){for(;e>t;e--){const t=i[e];if(!isNaN(t.x)&&!isNaN(t.y))break}return e}function sa(t,e,i,s){return t&&e?s(t[i],e[i]):t?t[i]:e?e[i]:0}function na(t,e){let i=[],s=!1;return n(t)?(s=!0,i=t):i=function(t,e){const{x:i=null,y:s=null}=t||{},n=e.points,o=[];return e.segments.forEach((({start:t,end:e})=>{e=ia(t,e,n);const a=n[t],r=n[e];null!==s?(o.push({x:a.x,y:s}),o.push({x:r.x,y:s})):null!==i&&(o.push({x:i,y:a.y}),o.push({x:i,y:r.y}))})),o}(t,e),i.length?new oo({points:i,options:{tension:0},_loop:s,_fullLoop:s}):null}function oa(t){return t&&!1!==t.fill}function aa(t,e,i){let s=t[e].fill;const n=[e];let o;if(!i)return s;for(;!1!==s&&-1===n.indexOf(s);){if(!a(s))return s;if(o=t[s],!o)return!1;if(o.visible)return s;n.push(s),s=o.fill}return!1}function ra(t,e,i){const s=function(t){const e=t.options,i=e.fill;let s=l(i&&i.target,i);void 0===s&&(s=!!e.backgroundColor);if(!1===s||null===s)return!1;if(!0===s)return"origin";return s}(t);if(o(s))return!isNaN(s.value)&&s;let n=parseFloat(s);return a(n)&&Math.floor(n)===n?function(t,e,i,s){"-"!==t&&"+"!==t||(i=e+i);if(i===e||i<0||i>=s)return!1;return i}(s[0],e,n,i):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}function la(t,e,i){const s=[];for(let n=0;n<i.length;n++){const o=i[n],{first:a,last:r,point:l}=ha(o,e,"x");if(!(!l||a&&r))if(a)s.unshift(l);else if(t.push(l),!r)break}t.push(...s)}function ha(t,e,i){const s=t.interpolate(e,i);if(!s)return{};const n=s[i],o=t.segments,a=t.points;let r=!1,l=!1;for(let t=0;t<o.length;t++){const e=o[t],s=a[e.start][i],h=a[e.end][i];if(tt(n,s,h)){r=n===s,l=n===h;break}}return{first:r,last:l,point:s}}class ca{constructor(t){this.x=t.x,this.y=t.y,this.radius=t.radius}pathSegment(t,e,i){const{x:s,y:n,radius:o}=this;return e=e||{start:0,end:O},t.arc(s,n,o,e.end,e.start,!0),!i.bounds}interpolate(t){const{x:e,y:i,radius:s}=this,n=t.angle;return{x:e+Math.cos(n)*s,y:i+Math.sin(n)*s,angle:n}}}function da(t){const{chart:e,fill:i,line:s}=t;if(a(i))return function(t,e){const i=t.getDatasetMeta(e),s=i&&t.isDatasetVisible(e);return s?i.dataset:null}(e,i);if("stack"===i)return function(t){const{scale:e,index:i,line:s}=t,n=[],o=s.segments,a=s.points,r=function(t,e){const i=[],s=t.getMatchingVisibleMetas("line");for(let t=0;t<s.length;t++){const n=s[t];if(n.index===e)break;n.hidden||i.unshift(n.dataset)}return i}(e,i);r.push(na({x:null,y:e.bottom},s));for(let t=0;t<o.length;t++){const e=o[t];for(let t=e.start;t<=e.end;t++)la(n,a[t],r)}return new oo({points:n,options:{}})}(t);if("shape"===i)return!0;const n=function(t){const e=t.scale||{};if(e.getPointPositionForValue)return function(t){const{scale:e,fill:i}=t,s=e.options,n=e.getLabels().length,a=s.reverse?e.max:e.min,r=function(t,e,i){let s;return s="start"===t?i:"end"===t?e.options.reverse?e.min:e.max:o(t)?t.value:e.getBaseValue(),s}(i,e,a),l=[];if(s.grid.circular){const t=e.getPointPositionForValue(0,a);return new ca({x:t.x,y:t.y,radius:e.getDistanceFromCenterForValue(r)})}for(let t=0;t<n;++t)l.push(e.getPointPositionForValue(t,r));return l}(t);return function(t){const{scale:e={},fill:i}=t,s=function(t,e){let i=null;return"start"===t?i=e.bottom:"end"===t?i=e.top:o(t)?i=e.getPixelForValue(t.value):e.getBasePixel&&(i=e.getBasePixel()),i}(i,e);if(a(s)){const t=e.isHorizontal();return{x:t?s:null,y:t?null:s}}return null}(t)}(t);return n instanceof ca?n:na(n,s)}function ua(t,e,i){const s=da(e),{chart:n,index:o,line:a,scale:r,axis:l}=e,h=a.options,c=h.fill,d=h.backgroundColor,{above:u=d,below:f=d}=c||{},g=n.getDatasetMeta(o),p=Ni(n,g);s&&a.points.length&&(Ie(t,i),function(t,e){const{line:i,target:s,above:n,below:o,area:a,scale:r,clip:l}=e,h=i._loop?"angle":e.axis;t.save();let c=o;o!==n&&("x"===h?(fa(t,s,a.top),pa(t,{line:i,target:s,color:n,scale:r,property:h,clip:l}),t.restore(),t.save(),fa(t,s,a.bottom)):"y"===h&&(ga(t,s,a.left),pa(t,{line:i,target:s,color:o,scale:r,property:h,clip:l}),t.restore(),t.save(),ga(t,s,a.right),c=n));pa(t,{line:i,target:s,color:c,scale:r,property:h,clip:l}),t.restore()}(t,{line:a,target:s,above:u,below:f,area:i,scale:r,axis:l,clip:p}),ze(t))}function fa(t,e,i){const{segments:s,points:n}=e;let o=!0,a=!1;t.beginPath();for(const r of s){const{start:s,end:l}=r,h=n[s],c=n[ia(s,l,n)];o?(t.moveTo(h.x,h.y),o=!1):(t.lineTo(h.x,i),t.lineTo(h.x,h.y)),a=!!e.pathSegment(t,r,{move:a}),a?t.closePath():t.lineTo(c.x,i)}t.lineTo(e.first().x,i),t.closePath(),t.clip()}function ga(t,e,i){const{segments:s,points:n}=e;let o=!0,a=!1;t.beginPath();for(const r of s){const{start:s,end:l}=r,h=n[s],c=n[ia(s,l,n)];o?(t.moveTo(h.x,h.y),o=!1):(t.lineTo(i,h.y),t.lineTo(h.x,h.y)),a=!!e.pathSegment(t,r,{move:a}),a?t.closePath():t.lineTo(i,c.y)}t.lineTo(i,e.first().y),t.closePath(),t.clip()}function pa(t,e){const{line:i,target:s,property:n,color:o,scale:a,clip:r}=e,l=function(t,e,i){const s=t.segments,n=t.points,o=e.points,a=[];for(const t of s){let{start:s,end:r}=t;r=ia(s,r,n);const l=ea(i,n[s],n[r],t.loop);if(!e.segments){a.push({source:t,target:l,start:n[s],end:n[r]});continue}const h=Ii(e,l);for(const e of h){const s=ea(i,o[e.start],o[e.end],e.loop),r=Ri(t,n,s);for(const t of r)a.push({source:t,target:e,start:{[i]:sa(l,s,"start",Math.max)},end:{[i]:sa(l,s,"end",Math.min)}})}}return a}(i,s,n);for(const{source:e,target:h,start:c,end:d}of l){const{style:{backgroundColor:l=o}={}}=e,u=!0!==s;t.save(),t.fillStyle=l,ma(t,a,r,u&&ea(n,c,d)),t.beginPath();const f=!!i.pathSegment(t,e);let g;if(u){f?t.closePath():xa(t,s,d,n);const e=!!s.pathSegment(t,h,{move:f,reverse:!0});g=f&&e,g||xa(t,s,c,n)}t.closePath(),t.fill(g?"evenodd":"nonzero"),t.restore()}}function ma(t,e,i,s){const n=e.chart.chartArea,{property:o,start:a,end:r}=s||{};if("x"===o||"y"===o){let e,s,l,h;"x"===o?(e=a,s=n.top,l=r,h=n.bottom):(e=n.left,s=a,l=n.right,h=r),t.beginPath(),i&&(e=Math.max(e,i.left),l=Math.min(l,i.right),s=Math.max(s,i.top),h=Math.min(h,i.bottom)),t.rect(e,s,l-e,h-s),t.clip()}}function xa(t,e,i,s){const n=e.interpolate(i,s);n&&t.lineTo(n.x,n.y)}var ba={id:"filler",afterDatasetsUpdate(t,e,i){const s=(t.data.datasets||[]).length,n=[];let o,a,r,l;for(a=0;a<s;++a)o=t.getDatasetMeta(a),r=o.dataset,l=null,r&&r.options&&r instanceof oo&&(l={visible:t.isDatasetVisible(a),index:a,fill:ra(r,a,s),chart:t,axis:o.controller.options.indexAxis,scale:o.vScale,line:r}),o.$filler=l,n.push(l);for(a=0;a<s;++a)l=n[a],l&&!1!==l.fill&&(l.fill=aa(n,a,i.propagate))},beforeDraw(t,e,i){const s="beforeDraw"===i.drawTime,n=t.getSortedVisibleDatasetMetas(),o=t.chartArea;for(let e=n.length-1;e>=0;--e){const i=n[e].$filler;i&&(i.line.updateControlPoints(o,i.axis),s&&i.fill&&ua(t.ctx,i,o))}},beforeDatasetsDraw(t,e,i){if("beforeDatasetsDraw"!==i.drawTime)return;const s=t.getSortedVisibleDatasetMetas();for(let e=s.length-1;e>=0;--e){const i=s[e].$filler;oa(i)&&ua(t.ctx,i,t.chartArea)}},beforeDatasetDraw(t,e,i){const s=e.meta.$filler;oa(s)&&"beforeDatasetDraw"===i.drawTime&&ua(t.ctx,s,t.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const _a=(t,e)=>{let{boxHeight:i=e,boxWidth:s=e}=t;return t.usePointStyle&&(i=Math.min(i,e),s=t.pointStyleWidth||Math.min(s,e)),{boxWidth:s,boxHeight:i,itemHeight:Math.max(e,i)}};class ya extends $s{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=d(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter((e=>t.filter(e,this.chart.data)))),t.sort&&(e=e.sort(((e,i)=>t.sort(e,i,this.chart.data)))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display)return void(this.width=this.height=0);const i=t.labels,s=Si(i.font),n=s.size,o=this._computeTitleHeight(),{boxWidth:a,itemHeight:r}=_a(i,n);let l,h;e.font=s.string,this.isHorizontal()?(l=this.maxWidth,h=this._fitRows(o,n,a,r)+10):(h=this.maxHeight,l=this._fitCols(o,s,a,r)+10),this.width=Math.min(l,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:n,maxWidth:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.lineWidths=[0],h=s+a;let c=t;n.textAlign="left",n.textBaseline="middle";let d=-1,u=-h;return this.legendItems.forEach(((t,f)=>{const g=i+e/2+n.measureText(t.text).width;(0===f||l[l.length-1]+g+2*a>o)&&(c+=h,l[l.length-(f>0?0:1)]=0,u+=h,d++),r[f]={left:0,top:u,row:d,width:g,height:s},l[l.length-1]+=g+a})),c}_fitCols(t,e,i,s){const{ctx:n,maxHeight:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.columnSizes=[],h=o-t;let c=a,d=0,u=0,f=0,g=0;return this.legendItems.forEach(((t,o)=>{const{itemWidth:p,itemHeight:m}=function(t,e,i,s,n){const o=function(t,e,i,s){let n=t.text;n&&"string"!=typeof n&&(n=n.reduce(((t,e)=>t.length>e.length?t:e)));return e+i.size/2+s.measureText(n).width}(s,t,e,i),a=function(t,e,i){let s=t;"string"!=typeof e.text&&(s=va(e,i));return s}(n,s,e.lineHeight);return{itemWidth:o,itemHeight:a}}(i,e,n,t,s);o>0&&u+m+2*a>h&&(c+=d+a,l.push({width:d,height:u}),f+=d+a,g++,d=u=0),r[o]={left:f,top:u,col:g,width:p,height:m},d=Math.max(d,p),u+=m+a})),c+=d,l.push({width:d,height:u}),c}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:n}}=this,o=Oi(n,this.left,this.width);if(this.isHorizontal()){let n=0,a=ft(i,this.left+s,this.right-this.lineWidths[n]);for(const r of e)n!==r.row&&(n=r.row,a=ft(i,this.left+s,this.right-this.lineWidths[n])),r.top+=this.top+t+s,r.left=o.leftForLtr(o.x(a),r.width),a+=r.width+s}else{let n=0,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height);for(const r of e)r.col!==n&&(n=r.col,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height)),r.top=a,r.left+=this.left+s,r.left=o.leftForLtr(o.x(r.left),r.width),a+=r.height+s}}isHorizontal(){return"top"===this.options.position||"bottom"===this.options.position}draw(){if(this.options.display){const t=this.ctx;Ie(t,this),this._draw(),ze(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:n,labels:o}=t,a=ue.color,r=Oi(t.rtl,this.left,this.width),h=Si(o.font),{padding:c}=o,d=h.size,u=d/2;let f;this.drawTitle(),s.textAlign=r.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=h.string;const{boxWidth:g,boxHeight:p,itemHeight:m}=_a(o,d),x=this.isHorizontal(),b=this._computeTitleHeight();f=x?{x:ft(n,this.left+c,this.right-i[0]),y:this.top+c+b,line:0}:{x:this.left+c,y:ft(n,this.top+b+c,this.bottom-e[0].height),line:0},Ai(this.ctx,t.textDirection);const _=m+c;this.legendItems.forEach(((y,v)=>{s.strokeStyle=y.fontColor,s.fillStyle=y.fontColor;const M=s.measureText(y.text).width,w=r.textAlign(y.textAlign||(y.textAlign=o.textAlign)),k=g+u+M;let S=f.x,P=f.y;r.setWidth(this.width),x?v>0&&S+k+c>this.right&&(P=f.y+=_,f.line++,S=f.x=ft(n,this.left+c,this.right-i[f.line])):v>0&&P+_>this.bottom&&(S=f.x=S+e[f.line].width+c,f.line++,P=f.y=ft(n,this.top+b+c,this.bottom-e[f.line].height));if(function(t,e,i){if(isNaN(g)||g<=0||isNaN(p)||p<0)return;s.save();const n=l(i.lineWidth,1);if(s.fillStyle=l(i.fillStyle,a),s.lineCap=l(i.lineCap,"butt"),s.lineDashOffset=l(i.lineDashOffset,0),s.lineJoin=l(i.lineJoin,"miter"),s.lineWidth=n,s.strokeStyle=l(i.strokeStyle,a),s.setLineDash(l(i.lineDash,[])),o.usePointStyle){const a={radius:p*Math.SQRT2/2,pointStyle:i.pointStyle,rotation:i.rotation,borderWidth:n},l=r.xPlus(t,g/2);Ee(s,a,l,e+u,o.pointStyleWidth&&g)}else{const o=e+Math.max((d-p)/2,0),a=r.leftForLtr(t,g),l=wi(i.borderRadius);s.beginPath(),Object.values(l).some((t=>0!==t))?He(s,{x:a,y:o,w:g,h:p,radius:l}):s.rect(a,o,g,p),s.fill(),0!==n&&s.stroke()}s.restore()}(r.x(S),P,y),S=gt(w,S+g+u,x?S+k:this.right,t.rtl),function(t,e,i){Ne(s,i.text,t,e+m/2,h,{strikethrough:i.hidden,textAlign:r.textAlign(i.textAlign)})}(r.x(S),P,y),x)f.x+=k+c;else if("string"!=typeof y.text){const t=h.lineHeight;f.y+=va(y,t)+c}else f.y+=_})),Ti(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=Si(e.font),s=ki(e.padding);if(!e.display)return;const n=Oi(t.rtl,this.left,this.width),o=this.ctx,a=e.position,r=i.size/2,l=s.top+r;let h,c=this.left,d=this.width;if(this.isHorizontal())d=Math.max(...this.lineWidths),h=this.top+l,c=ft(t.align,c,this.right-d);else{const e=this.columnSizes.reduce(((t,e)=>Math.max(t,e.height)),0);h=l+ft(t.align,this.top,this.bottom-e-t.labels.padding-this._computeTitleHeight())}const u=ft(a,c,c+d);o.textAlign=n.textAlign(ut(a)),o.textBaseline="middle",o.strokeStyle=e.color,o.fillStyle=e.color,o.font=i.string,Ne(o,e.text,u,h,i)}_computeTitleHeight(){const t=this.options.title,e=Si(t.font),i=ki(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,n;if(tt(t,this.left,this.right)&&tt(e,this.top,this.bottom))for(n=this.legendHitBoxes,i=0;i<n.length;++i)if(s=n[i],tt(t,s.left,s.left+s.width)&&tt(e,s.top,s.top+s.height))return this.legendItems[i];return null}handleEvent(t){const e=this.options;if(!function(t,e){if(("mousemove"===t||"mouseout"===t)&&(e.onHover||e.onLeave))return!0;if(e.onClick&&("click"===t||"mouseup"===t))return!0;return!1}(t.type,e))return;const i=this._getLegendItemAt(t.x,t.y);if("mousemove"===t.type||"mouseout"===t.type){const o=this._hoveredItem,a=(n=i,null!==(s=o)&&null!==n&&s.datasetIndex===n.datasetIndex&&s.index===n.index);o&&!a&&d(e.onLeave,[t,o,this],this),this._hoveredItem=i,i&&!a&&d(e.onHover,[t,i,this],this)}else i&&d(e.onClick,[t,i,this],this);var s,n}}function va(t,e){return e*(t.text?t.text.length:0)}var Ma={id:"legend",_element:ya,start(t,e,i){const s=t.legend=new ya({ctx:t.ctx,options:i,chart:t});ls.configure(t,s,i),ls.addBox(t,s)},stop(t){ls.removeBox(t,t.legend),delete t.legend},beforeUpdate(t,e,i){const s=t.legend;ls.configure(t,s,i),s.options=i},afterUpdate(t){const e=t.legend;e.buildLabels(),e.adjustHitBoxes()},afterEvent(t,e){e.replay||t.legend.handleEvent(e.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(t,e,i){const s=e.datasetIndex,n=i.chart;n.isDatasetVisible(s)?(n.hide(s),e.hidden=!0):(n.show(s),e.hidden=!1)},onHover:null,onLeave:null,labels:{color:t=>t.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:i,pointStyle:s,textAlign:n,color:o,useBorderRadius:a,borderRadius:r}}=t.legend.options;return t._getSortedDatasetMetas().map((t=>{const l=t.controller.getStyle(i?0:void 0),h=ki(l.borderWidth);return{text:e[t.index].label,fillStyle:l.backgroundColor,fontColor:o,hidden:!t.visible,lineCap:l.borderCapStyle,lineDash:l.borderDash,lineDashOffset:l.borderDashOffset,lineJoin:l.borderJoinStyle,lineWidth:(h.width+h.height)/4,strokeStyle:l.borderColor,pointStyle:s||l.pointStyle,rotation:l.rotation,textAlign:n||l.textAlign,borderRadius:a&&(r||l.borderRadius),datasetIndex:t.index}}),this)}},title:{color:t=>t.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:t=>!t.startsWith("on"),labels:{_scriptable:t=>!["generateLabels","filter","sort"].includes(t)}}};class wa extends $s{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e){const i=this.options;if(this.left=0,this.top=0,!i.display)return void(this.width=this.height=this.right=this.bottom=0);this.width=this.right=t,this.height=this.bottom=e;const s=n(i.text)?i.text.length:1;this._padding=ki(i.padding);const o=s*Si(i.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=o:this.width=o}isHorizontal(){const t=this.options.position;return"top"===t||"bottom"===t}_drawArgs(t){const{top:e,left:i,bottom:s,right:n,options:o}=this,a=o.align;let r,l,h,c=0;return this.isHorizontal()?(l=ft(a,i,n),h=e+t,r=n-i):("left"===o.position?(l=i+t,h=ft(a,s,e),c=-.5*C):(l=n-t,h=ft(a,e,s),c=.5*C),r=s-e),{titleX:l,titleY:h,maxWidth:r,rotation:c}}draw(){const t=this.ctx,e=this.options;if(!e.display)return;const i=Si(e.font),s=i.lineHeight/2+this._padding.top,{titleX:n,titleY:o,maxWidth:a,rotation:r}=this._drawArgs(s);Ne(t,e.text,0,0,i,{color:e.color,maxWidth:a,rotation:r,textAlign:ut(e.align),textBaseline:"middle",translation:[n,o]})}}var ka={id:"title",_element:wa,start(t,e,i){!function(t,e){const i=new wa({ctx:t.ctx,options:e,chart:t});ls.configure(t,i,e),ls.addBox(t,i),t.titleBlock=i}(t,i)},stop(t){const e=t.titleBlock;ls.removeBox(t,e),delete t.titleBlock},beforeUpdate(t,e,i){const s=t.titleBlock;ls.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Sa=new WeakMap;var Pa={id:"subtitle",start(t,e,i){const s=new wa({ctx:t.ctx,options:i,chart:t});ls.configure(t,s,i),ls.addBox(t,s),Sa.set(t,s)},stop(t){ls.removeBox(t,Sa.get(t)),Sa.delete(t)},beforeUpdate(t,e,i){const s=Sa.get(t);ls.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"normal"},fullSize:!0,padding:0,position:"top",text:"",weight:1500},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Da={average(t){if(!t.length)return!1;let e,i,s=new Set,n=0,o=0;for(e=0,i=t.length;e<i;++e){const i=t[e].element;if(i&&i.hasValue()){const t=i.tooltipPosition();s.add(t.x),n+=t.y,++o}}if(0===o||0===s.size)return!1;return{x:[...s].reduce(((t,e)=>t+e))/s.size,y:n/o}},nearest(t,e){if(!t.length)return!1;let i,s,n,o=e.x,a=e.y,r=Number.POSITIVE_INFINITY;for(i=0,s=t.length;i<s;++i){const s=t[i].element;if(s&&s.hasValue()){const t=q(e,s.getCenterPoint());t<r&&(r=t,n=s)}}if(n){const t=n.tooltipPosition();o=t.x,a=t.y}return{x:o,y:a}}};function Ca(t,e){return e&&(n(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function Oa(t){return("string"==typeof t||t instanceof String)&&t.indexOf("\n")>-1?t.split("\n"):t}function Aa(t,e){const{element:i,datasetIndex:s,index:n}=e,o=t.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:t,label:a,parsed:o.getParsed(n),raw:t.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:i}}function Ta(t,e){const i=t.chart.ctx,{body:s,footer:n,title:o}=t,{boxWidth:a,boxHeight:r}=e,l=Si(e.bodyFont),h=Si(e.titleFont),c=Si(e.footerFont),d=o.length,f=n.length,g=s.length,p=ki(e.padding);let m=p.height,x=0,b=s.reduce(((t,e)=>t+e.before.length+e.lines.length+e.after.length),0);if(b+=t.beforeBody.length+t.afterBody.length,d&&(m+=d*h.lineHeight+(d-1)*e.titleSpacing+e.titleMarginBottom),b){m+=g*(e.displayColors?Math.max(r,l.lineHeight):l.lineHeight)+(b-g)*l.lineHeight+(b-1)*e.bodySpacing}f&&(m+=e.footerMarginTop+f*c.lineHeight+(f-1)*e.footerSpacing);let _=0;const y=function(t){x=Math.max(x,i.measureText(t).width+_)};return i.save(),i.font=h.string,u(t.title,y),i.font=l.string,u(t.beforeBody.concat(t.afterBody),y),_=e.displayColors?a+2+e.boxPadding:0,u(s,(t=>{u(t.before,y),u(t.lines,y),u(t.after,y)})),_=0,i.font=c.string,u(t.footer,y),i.restore(),x+=p.width,{width:x,height:m}}function La(t,e,i,s){const{x:n,width:o}=i,{width:a,chartArea:{left:r,right:l}}=t;let h="center";return"center"===s?h=n<=(r+l)/2?"left":"right":n<=o/2?h="left":n>=a-o/2&&(h="right"),function(t,e,i,s){const{x:n,width:o}=s,a=i.caretSize+i.caretPadding;return"left"===t&&n+o+a>e.width||"right"===t&&n-o-a<0||void 0}(h,t,e,i)&&(h="center"),h}function Ea(t,e,i){const s=i.yAlign||e.yAlign||function(t,e){const{y:i,height:s}=e;return i<s/2?"top":i>t.height-s/2?"bottom":"center"}(t,i);return{xAlign:i.xAlign||e.xAlign||La(t,e,i,s),yAlign:s}}function Ra(t,e,i,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=t,{xAlign:r,yAlign:l}=i,h=n+o,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=wi(a);let g=function(t,e){let{x:i,width:s}=t;return"right"===e?i-=s:"center"===e&&(i-=s/2),i}(e,r);const p=function(t,e,i){let{y:s,height:n}=t;return"top"===e?s+=i:s-="bottom"===e?n+i:n/2,s}(e,l,h);return"center"===l?"left"===r?g+=h:"right"===r&&(g-=h):"left"===r?g-=Math.max(c,u)+n:"right"===r&&(g+=Math.max(d,f)+n),{x:Z(g,0,s.width-e.width),y:Z(p,0,s.height-e.height)}}function Ia(t,e,i){const s=ki(i.padding);return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-s.right:t.x+s.left}function za(t){return Ca([],Oa(t))}function Fa(t,e){const i=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return i?t.override(i):t}const Va={beforeTitle:e,title(t){if(t.length>0){const e=t[0],i=e.chart.data.labels,s=i?i.length:0;if(this&&this.options&&"dataset"===this.options.mode)return e.dataset.label||"";if(e.label)return e.label;if(s>0&&e.dataIndex<s)return i[e.dataIndex]}return""},afterTitle:e,beforeBody:e,beforeLabel:e,label(t){if(this&&this.options&&"dataset"===this.options.mode)return t.label+": "+t.formattedValue||t.formattedValue;let e=t.dataset.label||"";e&&(e+=": ");const i=t.formattedValue;return s(i)||(e+=i),e},labelColor(t){const e=t.chart.getDatasetMeta(t.datasetIndex).controller.getStyle(t.dataIndex);return{borderColor:e.borderColor,backgroundColor:e.backgroundColor,borderWidth:e.borderWidth,borderDash:e.borderDash,borderDashOffset:e.borderDashOffset,borderRadius:0}},labelTextColor(){return this.options.bodyColor},labelPointStyle(t){const e=t.chart.getDatasetMeta(t.datasetIndex).controller.getStyle(t.dataIndex);return{pointStyle:e.pointStyle,rotation:e.rotation}},afterLabel:e,afterBody:e,beforeFooter:e,footer:e,afterFooter:e};function Ba(t,e,i,s){const n=t[e].call(i,s);return void 0===n?Va[e].call(i,s):n}class Wa extends $s{static positioners=Da;constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,n=new Ts(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(n)),n}getContext(){return this.$context||(this.$context=(t=this.chart.getContext(),e=this,i=this._tooltipItems,Ci(t,{tooltip:e,tooltipItems:i,type:"tooltip"})));var t,e,i}getTitle(t,e){const{callbacks:i}=e,s=Ba(i,"beforeTitle",this,t),n=Ba(i,"title",this,t),o=Ba(i,"afterTitle",this,t);let a=[];return a=Ca(a,Oa(s)),a=Ca(a,Oa(n)),a=Ca(a,Oa(o)),a}getBeforeBody(t,e){return za(Ba(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:i}=e,s=[];return u(t,(t=>{const e={before:[],lines:[],after:[]},n=Fa(i,t);Ca(e.before,Oa(Ba(n,"beforeLabel",this,t))),Ca(e.lines,Ba(n,"label",this,t)),Ca(e.after,Oa(Ba(n,"afterLabel",this,t))),s.push(e)})),s}getAfterBody(t,e){return za(Ba(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:i}=e,s=Ba(i,"beforeFooter",this,t),n=Ba(i,"footer",this,t),o=Ba(i,"afterFooter",this,t);let a=[];return a=Ca(a,Oa(s)),a=Ca(a,Oa(n)),a=Ca(a,Oa(o)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],n=[],o=[];let a,r,l=[];for(a=0,r=e.length;a<r;++a)l.push(Aa(this.chart,e[a]));return t.filter&&(l=l.filter(((e,s,n)=>t.filter(e,s,n,i)))),t.itemSort&&(l=l.sort(((e,s)=>t.itemSort(e,s,i)))),u(l,(e=>{const i=Fa(t.callbacks,e);s.push(Ba(i,"labelColor",this,e)),n.push(Ba(i,"labelPointStyle",this,e)),o.push(Ba(i,"labelTextColor",this,e))})),this.labelColors=s,this.labelPointStyles=n,this.labelTextColors=o,this.dataPoints=l,l}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let n,o=[];if(s.length){const t=Da[i.position].call(this,s,this._eventPosition);o=this._createItems(i),this.title=this.getTitle(o,i),this.beforeBody=this.getBeforeBody(o,i),this.body=this.getBody(o,i),this.afterBody=this.getAfterBody(o,i),this.footer=this.getFooter(o,i);const e=this._size=Ta(this,i),a=Object.assign({},t,e),r=Ea(this.chart,i,a),l=Ra(i,a,r,this.chart);this.xAlign=r.xAlign,this.yAlign=r.yAlign,n={opacity:1,x:l.x,y:l.y,width:e.width,height:e.height,caretX:t.x,caretY:t.y}}else 0!==this.opacity&&(n={opacity:0});this._tooltipItems=o,this.$context=void 0,n&&this._resolveAnimations().update(this,n),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const n=this.getCaretPosition(t,i,s);e.lineTo(n.x1,n.y1),e.lineTo(n.x2,n.y2),e.lineTo(n.x3,n.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:n}=this,{caretSize:o,cornerRadius:a}=i,{topLeft:r,topRight:l,bottomLeft:h,bottomRight:c}=wi(a),{x:d,y:u}=t,{width:f,height:g}=e;let p,m,x,b,_,y;return"center"===n?(_=u+g/2,"left"===s?(p=d,m=p-o,b=_+o,y=_-o):(p=d+f,m=p+o,b=_-o,y=_+o),x=p):(m="left"===s?d+Math.max(r,h)+o:"right"===s?d+f-Math.max(l,c)-o:this.caretX,"top"===n?(b=u,_=b-o,p=m-o,x=m+o):(b=u+g,_=b+o,p=m+o,x=m-o),y=b),{x1:p,x2:m,x3:x,y1:b,y2:_,y3:y}}drawTitle(t,e,i){const s=this.title,n=s.length;let o,a,r;if(n){const l=Oi(i.rtl,this.x,this.width);for(t.x=Ia(this,i.titleAlign,i),e.textAlign=l.textAlign(i.titleAlign),e.textBaseline="middle",o=Si(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=o.string,r=0;r<n;++r)e.fillText(s[r],l.x(t.x),t.y+o.lineHeight/2),t.y+=o.lineHeight+a,r+1===n&&(t.y+=i.titleMarginBottom-a)}}_drawColorBox(t,e,i,s,n){const a=this.labelColors[i],r=this.labelPointStyles[i],{boxHeight:l,boxWidth:h}=n,c=Si(n.bodyFont),d=Ia(this,"left",n),u=s.x(d),f=l<c.lineHeight?(c.lineHeight-l)/2:0,g=e.y+f;if(n.usePointStyle){const e={radius:Math.min(h,l)/2,pointStyle:r.pointStyle,rotation:r.rotation,borderWidth:1},i=s.leftForLtr(u,h)+h/2,o=g+l/2;t.strokeStyle=n.multiKeyBackground,t.fillStyle=n.multiKeyBackground,Le(t,e,i,o),t.strokeStyle=a.borderColor,t.fillStyle=a.backgroundColor,Le(t,e,i,o)}else{t.lineWidth=o(a.borderWidth)?Math.max(...Object.values(a.borderWidth)):a.borderWidth||1,t.strokeStyle=a.borderColor,t.setLineDash(a.borderDash||[]),t.lineDashOffset=a.borderDashOffset||0;const e=s.leftForLtr(u,h),i=s.leftForLtr(s.xPlus(u,1),h-2),r=wi(a.borderRadius);Object.values(r).some((t=>0!==t))?(t.beginPath(),t.fillStyle=n.multiKeyBackground,He(t,{x:e,y:g,w:h,h:l,radius:r}),t.fill(),t.stroke(),t.fillStyle=a.backgroundColor,t.beginPath(),He(t,{x:i,y:g+1,w:h-2,h:l-2,radius:r}),t.fill()):(t.fillStyle=n.multiKeyBackground,t.fillRect(e,g,h,l),t.strokeRect(e,g,h,l),t.fillStyle=a.backgroundColor,t.fillRect(i,g+1,h-2,l-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:n,bodyAlign:o,displayColors:a,boxHeight:r,boxWidth:l,boxPadding:h}=i,c=Si(i.bodyFont);let d=c.lineHeight,f=0;const g=Oi(i.rtl,this.x,this.width),p=function(i){e.fillText(i,g.x(t.x+f),t.y+d/2),t.y+=d+n},m=g.textAlign(o);let x,b,_,y,v,M,w;for(e.textAlign=o,e.textBaseline="middle",e.font=c.string,t.x=Ia(this,m,i),e.fillStyle=i.bodyColor,u(this.beforeBody,p),f=a&&"right"!==m?"center"===o?l/2+h:l+2+h:0,y=0,M=s.length;y<M;++y){for(x=s[y],b=this.labelTextColors[y],e.fillStyle=b,u(x.before,p),_=x.lines,a&&_.length&&(this._drawColorBox(e,t,y,g,i),d=Math.max(c.lineHeight,r)),v=0,w=_.length;v<w;++v)p(_[v]),d=c.lineHeight;u(x.after,p)}f=0,d=c.lineHeight,u(this.afterBody,p),t.y-=n}drawFooter(t,e,i){const s=this.footer,n=s.length;let o,a;if(n){const r=Oi(i.rtl,this.x,this.width);for(t.x=Ia(this,i.footerAlign,i),t.y+=i.footerMarginTop,e.textAlign=r.textAlign(i.footerAlign),e.textBaseline="middle",o=Si(i.footerFont),e.fillStyle=i.footerColor,e.font=o.string,a=0;a<n;++a)e.fillText(s[a],r.x(t.x),t.y+o.lineHeight/2),t.y+=o.lineHeight+i.footerSpacing}}drawBackground(t,e,i,s){const{xAlign:n,yAlign:o}=this,{x:a,y:r}=t,{width:l,height:h}=i,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=wi(s.cornerRadius);e.fillStyle=s.backgroundColor,e.strokeStyle=s.borderColor,e.lineWidth=s.borderWidth,e.beginPath(),e.moveTo(a+c,r),"top"===o&&this.drawCaret(t,e,i,s),e.lineTo(a+l-d,r),e.quadraticCurveTo(a+l,r,a+l,r+d),"center"===o&&"right"===n&&this.drawCaret(t,e,i,s),e.lineTo(a+l,r+h-f),e.quadraticCurveTo(a+l,r+h,a+l-f,r+h),"bottom"===o&&this.drawCaret(t,e,i,s),e.lineTo(a+u,r+h),e.quadraticCurveTo(a,r+h,a,r+h-u),"center"===o&&"left"===n&&this.drawCaret(t,e,i,s),e.lineTo(a,r+c),e.quadraticCurveTo(a,r,a+c,r),e.closePath(),e.fill(),s.borderWidth>0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,n=i&&i.y;if(s||n){const i=Da[t.position].call(this,this._active,this._eventPosition);if(!i)return;const o=this._size=Ta(this,t),a=Object.assign({},i,this._size),r=Ea(e,t,a),l=Ra(t,a,r,e);s._to===l.x&&n._to===l.y||(this.xAlign=r.xAlign,this.yAlign=r.yAlign,this.width=o.width,this.height=o.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,l))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},n={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const o=ki(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(n,t,s,e),Ai(t,e.textDirection),n.y+=o.top,this.drawTitle(n,t,e),this.drawBody(n,t,e),this.drawFooter(n,t,e),Ti(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map((({datasetIndex:t,index:e})=>{const i=this.chart.getDatasetMeta(t);if(!i)throw new Error("Cannot find a dataset at index "+t);return{datasetIndex:t,element:i.data[e],index:e}})),n=!f(i,s),o=this._positionChanged(s,e);(n||o)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,n=this._active||[],o=this._getActiveElements(t,n,e,i),a=this._positionChanged(o,t),r=e||!f(o,n)||a;return r&&(this._active=o,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),r}_getActiveElements(t,e,i,s){const n=this.options;if("mouseout"===t.type)return[];if(!s)return e.filter((t=>this.chart.data.datasets[t.datasetIndex]&&void 0!==this.chart.getDatasetMeta(t.datasetIndex).controller.getParsed(t.index)));const o=this.chart.getElementsAtEventForMode(t,n.mode,n,i);return n.reverse&&o.reverse(),o}_positionChanged(t,e){const{caretX:i,caretY:s,options:n}=this,o=Da[n.position].call(this,t,e);return!1!==o&&(i!==o.x||s!==o.y)}}var Na={id:"tooltip",_element:Wa,positioners:Da,afterInit(t,e,i){i&&(t.tooltip=new Wa({chart:t,options:i}))},beforeUpdate(t,e,i){t.tooltip&&t.tooltip.initialize(i)},reset(t,e,i){t.tooltip&&t.tooltip.initialize(i)},afterDraw(t){const e=t.tooltip;if(e&&e._willRender()){const i={tooltip:e};if(!1===t.notifyPlugins("beforeTooltipDraw",{...i,cancelable:!0}))return;e.draw(t.ctx),t.notifyPlugins("afterTooltipDraw",i)}},afterEvent(t,e){if(t.tooltip){const i=e.replay;t.tooltip.handleEvent(e.event,i,e.inChartArea)&&(e.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(t,e)=>e.bodyFont.size,boxWidth:(t,e)=>e.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:Va},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:t=>"filter"!==t&&"itemSort"!==t&&"external"!==t,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};return Tn.register(Un,$o,go,t),Tn.helpers={...Hi},Tn._adapters=In,Tn.Animation=As,Tn.Animations=Ts,Tn.animator=bt,Tn.controllers=nn.controllers.items,Tn.DatasetController=js,Tn.Element=$s,Tn.elements=go,Tn.Interaction=Ki,Tn.layouts=ls,Tn.platforms=Ds,Tn.Scale=tn,Tn.Ticks=ae,Object.assign(Tn,Un,$o,go,t,Ds),Tn.Chart=Tn,"undefined"!=typeof window&&(window.Chart=Tn),Tn}));
    14 //# sourceMappingURL=chart.umd.js.map
     13 */function _t(t){return t+.5|0}const yt=(t,e,i)=>Math.max(Math.min(t,i),e);function vt(t){return yt(_t(2.55*t),0,255)}function Mt(t){return yt(_t(255*t),0,255)}function wt(t){return yt(_t(t/2.55)/100,0,1)}function kt(t){return yt(_t(100*t),0,100)}const St={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Pt=[..."0123456789ABCDEF"],Dt=t=>Pt[15&t],Ct=t=>Pt[(240&t)>>4]+Pt[15&t],Ot=t=>(240&t)>>4==(15&t);function At(t){var e=(t=>Ot(t.r)&&Ot(t.g)&&Ot(t.b)&&Ot(t.a))(t)?Dt:Ct;return t?"#"+e(t.r)+e(t.g)+e(t.b)+((t,e)=>t<255?e(t):"")(t.a,e):void 0}const Tt=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function Lt(t,e,i){const s=e*Math.min(i,1-i),n=(e,n=(e+t/30)%12)=>i-s*Math.max(Math.min(n-3,9-n,1),-1);return[n(0),n(8),n(4)]}function Et(t,e,i){const s=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return[s(5),s(3),s(1)]}function Rt(t,e,i){const s=Lt(t,1,.5);let n;for(e+i>1&&(n=1/(e+i),e*=n,i*=n),n=0;n<3;n++)s[n]*=1-e-i,s[n]+=e;return s}function It(t){const e=t.r/255,i=t.g/255,s=t.b/255,n=Math.max(e,i,s),o=Math.min(e,i,s),a=(n+o)/2;let r,l,h;return n!==o&&(h=n-o,l=a>.5?h/(2-n-o):h/(n+o),r=function(t,e,i,s,n){return t===n?(e-i)/s+(e<i?6:0):e===n?(i-t)/s+2:(t-e)/s+4}(e,i,s,h,n),r=60*r+.5),[0|r,l||0,a]}function zt(t,e,i,s){return(Array.isArray(e)?t(e[0],e[1],e[2]):t(e,i,s)).map(Mt)}function Ft(t,e,i){return zt(Lt,t,e,i)}function Vt(t){return(t%360+360)%360}function Bt(t){const e=Tt.exec(t);let i,s=255;if(!e)return;e[5]!==i&&(s=e[6]?vt(+e[5]):Mt(+e[5]));const n=Vt(+e[2]),o=+e[3]/100,a=+e[4]/100;return i="hwb"===e[1]?function(t,e,i){return zt(Rt,t,e,i)}(n,o,a):"hsv"===e[1]?function(t,e,i){return zt(Et,t,e,i)}(n,o,a):Ft(n,o,a),{r:i[0],g:i[1],b:i[2],a:s}}const Wt={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},Nt={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};let Ht;function jt(t){Ht||(Ht=function(){const t={},e=Object.keys(Nt),i=Object.keys(Wt);let s,n,o,a,r;for(s=0;s<e.length;s++){for(a=r=e[s],n=0;n<i.length;n++)o=i[n],r=r.replace(o,Wt[o]);o=parseInt(Nt[a],16),t[r]=[o>>16&255,o>>8&255,255&o]}return t}(),Ht.transparent=[0,0,0,0]);const e=Ht[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}const $t=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;const Yt=t=>t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055,Ut=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4);function Xt(t,e,i){if(t){let s=It(t);s[e]=Math.max(0,Math.min(s[e]+s[e]*i,0===e?360:1)),s=Ft(s),t.r=s[0],t.g=s[1],t.b=s[2]}}function qt(t,e){return t?Object.assign(e||{},t):t}function Kt(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=Mt(t[3]))):(e=qt(t,{r:0,g:0,b:0,a:1})).a=Mt(e.a),e}function Gt(t){return"r"===t.charAt(0)?function(t){const e=$t.exec(t);let i,s,n,o=255;if(e){if(e[7]!==i){const t=+e[7];o=e[8]?vt(t):yt(255*t,0,255)}return i=+e[1],s=+e[3],n=+e[5],i=255&(e[2]?vt(i):yt(i,0,255)),s=255&(e[4]?vt(s):yt(s,0,255)),n=255&(e[6]?vt(n):yt(n,0,255)),{r:i,g:s,b:n,a:o}}}(t):Bt(t)}class Jt{constructor(t){if(t instanceof Jt)return t;const e=typeof t;let i;var s,n,o;"object"===e?i=Kt(t):"string"===e&&(o=(s=t).length,"#"===s[0]&&(4===o||5===o?n={r:255&17*St[s[1]],g:255&17*St[s[2]],b:255&17*St[s[3]],a:5===o?17*St[s[4]]:255}:7!==o&&9!==o||(n={r:St[s[1]]<<4|St[s[2]],g:St[s[3]]<<4|St[s[4]],b:St[s[5]]<<4|St[s[6]],a:9===o?St[s[7]]<<4|St[s[8]]:255})),i=n||jt(t)||Gt(t)),this._rgb=i,this._valid=!!i}get valid(){return this._valid}get rgb(){var t=qt(this._rgb);return t&&(t.a=wt(t.a)),t}set rgb(t){this._rgb=Kt(t)}rgbString(){return this._valid?(t=this._rgb)&&(t.a<255?`rgba(${t.r}, ${t.g}, ${t.b}, ${wt(t.a)})`:`rgb(${t.r}, ${t.g}, ${t.b})`):void 0;var t}hexString(){return this._valid?At(this._rgb):void 0}hslString(){return this._valid?function(t){if(!t)return;const e=It(t),i=e[0],s=kt(e[1]),n=kt(e[2]);return t.a<255?`hsla(${i}, ${s}%, ${n}%, ${wt(t.a)})`:`hsl(${i}, ${s}%, ${n}%)`}(this._rgb):void 0}mix(t,e){if(t){const i=this.rgb,s=t.rgb;let n;const o=e===n?.5:e,a=2*o-1,r=i.a-s.a,l=((a*r==-1?a:(a+r)/(1+a*r))+1)/2;n=1-l,i.r=255&l*i.r+n*s.r+.5,i.g=255&l*i.g+n*s.g+.5,i.b=255&l*i.b+n*s.b+.5,i.a=o*i.a+(1-o)*s.a,this.rgb=i}return this}interpolate(t,e){return t&&(this._rgb=function(t,e,i){const s=Ut(wt(t.r)),n=Ut(wt(t.g)),o=Ut(wt(t.b));return{r:Mt(Yt(s+i*(Ut(wt(e.r))-s))),g:Mt(Yt(n+i*(Ut(wt(e.g))-n))),b:Mt(Yt(o+i*(Ut(wt(e.b))-o))),a:t.a+i*(e.a-t.a)}}(this._rgb,t._rgb,e)),this}clone(){return new Jt(this.rgb)}alpha(t){return this._rgb.a=Mt(t),this}clearer(t){return this._rgb.a*=1-t,this}greyscale(){const t=this._rgb,e=_t(.3*t.r+.59*t.g+.11*t.b);return t.r=t.g=t.b=e,this}opaquer(t){return this._rgb.a*=1+t,this}negate(){const t=this._rgb;return t.r=255-t.r,t.g=255-t.g,t.b=255-t.b,this}lighten(t){return Xt(this._rgb,2,t),this}darken(t){return Xt(this._rgb,2,-t),this}saturate(t){return Xt(this._rgb,1,t),this}desaturate(t){return Xt(this._rgb,1,-t),this}rotate(t){return function(t,e){var i=It(t);i[0]=Vt(i[0]+e),i=Ft(i),t.r=i[0],t.g=i[1],t.b=i[2]}(this._rgb,t),this}}function Zt(t){if(t&&"object"==typeof t){const e=t.toString();return"[object CanvasPattern]"===e||"[object CanvasGradient]"===e}return!1}function Qt(t){return Zt(t)?t:new Jt(t)}function te(t){return Zt(t)?t:new Jt(t).saturate(.5).darken(.1).hexString()}const ee=["x","y","borderWidth","radius","tension"],ie=["color","borderColor","backgroundColor"];const se=new Map;function ne(t,e,i){return function(t,e){e=e||{};const i=t+JSON.stringify(e);let s=se.get(i);return s||(s=new Intl.NumberFormat(t,e),se.set(i,s)),s}(e,i).format(t)}const oe={values:t=>n(t)?t:""+t,numeric(t,e,i){if(0===t)return"0";const s=this.chart.options.locale;let n,o=t;if(i.length>1){const e=Math.max(Math.abs(i[0].value),Math.abs(i[i.length-1].value));(e<1e-4||e>1e15)&&(n="scientific"),o=function(t,e){let i=e.length>3?e[2].value-e[1].value:e[1].value-e[0].value;Math.abs(i)>=1&&t!==Math.floor(t)&&(i=t-Math.floor(t));return i}(t,i)}const a=z(Math.abs(o)),r=isNaN(a)?1:Math.max(Math.min(-1*Math.floor(a),20),0),l={notation:n,minimumFractionDigits:r,maximumFractionDigits:r};return Object.assign(l,this.options.ticks.format),ne(t,s,l)},logarithmic(t,e,i){if(0===t)return"0";const s=i[e].significand||t/Math.pow(10,Math.floor(z(t)));return[1,2,3,5,10,15].includes(s)||e>.8*i.length?oe.numeric.call(this,t,e,i):""}};var ae={formatters:oe};const re=Object.create(null),le=Object.create(null);function he(t,e){if(!e)return t;const i=e.split(".");for(let e=0,s=i.length;e<s;++e){const s=i[e];t=t[s]||(t[s]=Object.create(null))}return t}function ce(t,e,i){return"string"==typeof e?x(he(t,e),i):x(he(t,""),e)}class de{constructor(t,e){this.animation=void 0,this.backgroundColor="rgba(0,0,0,0.1)",this.borderColor="rgba(0,0,0,0.1)",this.color="#666",this.datasets={},this.devicePixelRatio=t=>t.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(t,e)=>te(e.backgroundColor),this.hoverBorderColor=(t,e)=>te(e.borderColor),this.hoverColor=(t,e)=>te(e.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(t),this.apply(e)}set(t,e){return ce(this,t,e)}get(t){return he(this,t)}describe(t,e){return ce(le,t,e)}override(t,e){return ce(re,t,e)}route(t,e,i,s){const n=he(this,t),a=he(this,i),r="_"+e;Object.defineProperties(n,{[r]:{value:n[e],writable:!0},[e]:{enumerable:!0,get(){const t=this[r],e=a[s];return o(t)?Object.assign({},e,t):l(t,e)},set(t){this[r]=t}}})}apply(t){t.forEach((t=>t(this)))}}var ue=new de({_scriptable:t=>!t.startsWith("on"),_indexable:t=>"events"!==t,hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[function(t){t.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),t.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:t=>"onProgress"!==t&&"onComplete"!==t&&"fn"!==t}),t.set("animations",{colors:{type:"color",properties:ie},numbers:{type:"number",properties:ee}}),t.describe("animations",{_fallback:"animation"}),t.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:t=>0|t}}}})},function(t){t.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})},function(t){t.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(t,e)=>e.lineWidth,tickColor:(t,e)=>e.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:ae.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),t.route("scale.ticks","color","","color"),t.route("scale.grid","color","","borderColor"),t.route("scale.border","color","","borderColor"),t.route("scale.title","color","","color"),t.describe("scale",{_fallback:!1,_scriptable:t=>!t.startsWith("before")&&!t.startsWith("after")&&"callback"!==t&&"parser"!==t,_indexable:t=>"borderDash"!==t&&"tickBorderDash"!==t&&"dash"!==t}),t.describe("scales",{_fallback:"scale"}),t.describe("scale.ticks",{_scriptable:t=>"backdropPadding"!==t&&"callback"!==t,_indexable:t=>"backdropPadding"!==t})}]);function fe(){return"undefined"!=typeof window&&"undefined"!=typeof document}function ge(t){let e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e}function pe(t,e,i){let s;return"string"==typeof t?(s=parseInt(t,10),-1!==t.indexOf("%")&&(s=s/100*e.parentNode[i])):s=t,s}const me=t=>t.ownerDocument.defaultView.getComputedStyle(t,null);function xe(t,e){return me(t).getPropertyValue(e)}const be=["top","right","bottom","left"];function _e(t,e,i){const s={};i=i?"-"+i:"";for(let n=0;n<4;n++){const o=be[n];s[o]=parseFloat(t[e+"-"+o+i])||0}return s.width=s.left+s.right,s.height=s.top+s.bottom,s}const ye=(t,e,i)=>(t>0||e>0)&&(!i||!i.shadowRoot);function ve(t,e){if("native"in t)return t;const{canvas:i,currentDevicePixelRatio:s}=e,n=me(i),o="border-box"===n.boxSizing,a=_e(n,"padding"),r=_e(n,"border","width"),{x:l,y:h,box:c}=function(t,e){const i=t.touches,s=i&&i.length?i[0]:t,{offsetX:n,offsetY:o}=s;let a,r,l=!1;if(ye(n,o,t.target))a=n,r=o;else{const t=e.getBoundingClientRect();a=s.clientX-t.left,r=s.clientY-t.top,l=!0}return{x:a,y:r,box:l}}(t,i),d=a.left+(c&&r.left),u=a.top+(c&&r.top);let{width:f,height:g}=e;return o&&(f-=a.width+r.width,g-=a.height+r.height),{x:Math.round((l-d)/f*i.width/s),y:Math.round((h-u)/g*i.height/s)}}const Me=t=>Math.round(10*t)/10;function we(t,e,i,s){const n=me(t),o=_e(n,"margin"),a=pe(n.maxWidth,t,"clientWidth")||T,r=pe(n.maxHeight,t,"clientHeight")||T,l=function(t,e,i){let s,n;if(void 0===e||void 0===i){const o=t&&ge(t);if(o){const t=o.getBoundingClientRect(),a=me(o),r=_e(a,"border","width"),l=_e(a,"padding");e=t.width-l.width-r.width,i=t.height-l.height-r.height,s=pe(a.maxWidth,o,"clientWidth"),n=pe(a.maxHeight,o,"clientHeight")}else e=t.clientWidth,i=t.clientHeight}return{width:e,height:i,maxWidth:s||T,maxHeight:n||T}}(t,e,i);let{width:h,height:c}=l;if("content-box"===n.boxSizing){const t=_e(n,"border","width"),e=_e(n,"padding");h-=e.width+t.width,c-=e.height+t.height}h=Math.max(0,h-o.width),c=Math.max(0,s?h/s:c-o.height),h=Me(Math.min(h,a,l.maxWidth)),c=Me(Math.min(c,r,l.maxHeight)),h&&!c&&(c=Me(h/2));return(void 0!==e||void 0!==i)&&s&&l.height&&c>l.height&&(c=l.height,h=Me(Math.floor(c*s))),{width:h,height:c}}function ke(t,e,i){const s=e||1,n=Me(t.height*s),o=Me(t.width*s);t.height=Me(t.height),t.width=Me(t.width);const a=t.canvas;return a.style&&(i||!a.style.height&&!a.style.width)&&(a.style.height=`${t.height}px`,a.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==s||a.height!==n||a.width!==o)&&(t.currentDevicePixelRatio=s,a.height=n,a.width=o,t.ctx.setTransform(s,0,0,s,0,0),!0)}const Se=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};fe()&&(window.addEventListener("test",null,e),window.removeEventListener("test",null,e))}catch(t){}return t}();function Pe(t,e){const i=xe(t,e),s=i&&i.match(/^(\d+)(\.\d+)?px$/);return s?+s[1]:void 0}function De(t){return!t||s(t.size)||s(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}function Ce(t,e,i,s,n){let o=e[n];return o||(o=e[n]=t.measureText(n).width,i.push(n)),o>s&&(s=o),s}function Oe(t,e,i,s){let o=(s=s||{}).data=s.data||{},a=s.garbageCollect=s.garbageCollect||[];s.font!==e&&(o=s.data={},a=s.garbageCollect=[],s.font=e),t.save(),t.font=e;let r=0;const l=i.length;let h,c,d,u,f;for(h=0;h<l;h++)if(u=i[h],null==u||n(u)){if(n(u))for(c=0,d=u.length;c<d;c++)f=u[c],null==f||n(f)||(r=Ce(t,o,a,r,f))}else r=Ce(t,o,a,r,u);t.restore();const g=a.length/2;if(g>i.length){for(h=0;h<g;h++)delete o[a[h]];a.splice(0,g)}return r}function Ae(t,e,i){const s=t.currentDevicePixelRatio,n=0!==i?Math.max(i/2,.5):0;return Math.round((e-n)*s)/s+n}function Te(t,e){(e||t)&&((e=e||t.getContext("2d")).save(),e.resetTransform(),e.clearRect(0,0,t.width,t.height),e.restore())}function Le(t,e,i,s){Ee(t,e,i,s,null)}function Ee(t,e,i,s,n){let o,a,r,l,h,c,d,u;const f=e.pointStyle,g=e.rotation,p=e.radius;let m=(g||0)*L;if(f&&"object"==typeof f&&(o=f.toString(),"[object HTMLImageElement]"===o||"[object HTMLCanvasElement]"===o))return t.save(),t.translate(i,s),t.rotate(m),t.drawImage(f,-f.width/2,-f.height/2,f.width,f.height),void t.restore();if(!(isNaN(p)||p<=0)){switch(t.beginPath(),f){default:n?t.ellipse(i,s,n/2,p,0,0,O):t.arc(i,s,p,0,O),t.closePath();break;case"triangle":c=n?n/2:p,t.moveTo(i+Math.sin(m)*c,s-Math.cos(m)*p),m+=I,t.lineTo(i+Math.sin(m)*c,s-Math.cos(m)*p),m+=I,t.lineTo(i+Math.sin(m)*c,s-Math.cos(m)*p),t.closePath();break;case"rectRounded":h=.516*p,l=p-h,a=Math.cos(m+R)*l,d=Math.cos(m+R)*(n?n/2-h:l),r=Math.sin(m+R)*l,u=Math.sin(m+R)*(n?n/2-h:l),t.arc(i-d,s-r,h,m-C,m-E),t.arc(i+u,s-a,h,m-E,m),t.arc(i+d,s+r,h,m,m+E),t.arc(i-u,s+a,h,m+E,m+C),t.closePath();break;case"rect":if(!g){l=Math.SQRT1_2*p,c=n?n/2:l,t.rect(i-c,s-l,2*c,2*l);break}m+=R;case"rectRot":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+u,s-a),t.lineTo(i+d,s+r),t.lineTo(i-u,s+a),t.closePath();break;case"crossRot":m+=R;case"cross":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a);break;case"star":d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a),m+=R,d=Math.cos(m)*(n?n/2:p),a=Math.cos(m)*p,r=Math.sin(m)*p,u=Math.sin(m)*(n?n/2:p),t.moveTo(i-d,s-r),t.lineTo(i+d,s+r),t.moveTo(i+u,s-a),t.lineTo(i-u,s+a);break;case"line":a=n?n/2:Math.cos(m)*p,r=Math.sin(m)*p,t.moveTo(i-a,s-r),t.lineTo(i+a,s+r);break;case"dash":t.moveTo(i,s),t.lineTo(i+Math.cos(m)*(n?n/2:p),s+Math.sin(m)*p);break;case!1:t.closePath()}t.fill(),e.borderWidth>0&&t.stroke()}}function Re(t,e,i){return i=i||.5,!e||t&&t.x>e.left-i&&t.x<e.right+i&&t.y>e.top-i&&t.y<e.bottom+i}function Ie(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()}function ze(t){t.restore()}function Fe(t,e,i,s,n){if(!e)return t.lineTo(i.x,i.y);if("middle"===n){const s=(e.x+i.x)/2;t.lineTo(s,e.y),t.lineTo(s,i.y)}else"after"===n!=!!s?t.lineTo(e.x,i.y):t.lineTo(i.x,e.y);t.lineTo(i.x,i.y)}function Ve(t,e,i,s){if(!e)return t.lineTo(i.x,i.y);t.bezierCurveTo(s?e.cp1x:e.cp2x,s?e.cp1y:e.cp2y,s?i.cp2x:i.cp1x,s?i.cp2y:i.cp1y,i.x,i.y)}function Be(t,e,i,s,n){if(n.strikethrough||n.underline){const o=t.measureText(s),a=e-o.actualBoundingBoxLeft,r=e+o.actualBoundingBoxRight,l=i-o.actualBoundingBoxAscent,h=i+o.actualBoundingBoxDescent,c=n.strikethrough?(l+h)/2:h;t.strokeStyle=t.fillStyle,t.beginPath(),t.lineWidth=n.decorationWidth||2,t.moveTo(a,c),t.lineTo(r,c),t.stroke()}}function We(t,e){const i=t.fillStyle;t.fillStyle=e.color,t.fillRect(e.left,e.top,e.width,e.height),t.fillStyle=i}function Ne(t,e,i,o,a,r={}){const l=n(e)?e:[e],h=r.strokeWidth>0&&""!==r.strokeColor;let c,d;for(t.save(),t.font=a.string,function(t,e){e.translation&&t.translate(e.translation[0],e.translation[1]),s(e.rotation)||t.rotate(e.rotation),e.color&&(t.fillStyle=e.color),e.textAlign&&(t.textAlign=e.textAlign),e.textBaseline&&(t.textBaseline=e.textBaseline)}(t,r),c=0;c<l.length;++c)d=l[c],r.backdrop&&We(t,r.backdrop),h&&(r.strokeColor&&(t.strokeStyle=r.strokeColor),s(r.strokeWidth)||(t.lineWidth=r.strokeWidth),t.strokeText(d,i,o,r.maxWidth)),t.fillText(d,i,o,r.maxWidth),Be(t,i,o,d,r),o+=Number(a.lineHeight);t.restore()}function He(t,e){const{x:i,y:s,w:n,h:o,radius:a}=e;t.arc(i+a.topLeft,s+a.topLeft,a.topLeft,1.5*C,C,!0),t.lineTo(i,s+o-a.bottomLeft),t.arc(i+a.bottomLeft,s+o-a.bottomLeft,a.bottomLeft,C,E,!0),t.lineTo(i+n-a.bottomRight,s+o),t.arc(i+n-a.bottomRight,s+o-a.bottomRight,a.bottomRight,E,0,!0),t.lineTo(i+n,s+a.topRight),t.arc(i+n-a.topRight,s+a.topRight,a.topRight,0,-E,!0),t.lineTo(i+a.topLeft,s)}function je(t,e=[""],i,s,n=(()=>t[0])){const o=i||t;void 0===s&&(s=ti("_fallback",t));const a={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:t,_rootScopes:o,_fallback:s,_getTarget:n,override:i=>je([i,...t],e,o,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete e._keys,delete t[0][i],!0),get:(i,s)=>qe(i,s,(()=>function(t,e,i,s){let n;for(const o of e)if(n=ti(Ue(o,t),i),void 0!==n)return Xe(t,n)?Ze(i,s,t,n):n}(s,e,t,i))),getOwnPropertyDescriptor:(t,e)=>Reflect.getOwnPropertyDescriptor(t._scopes[0],e),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(t,e)=>ei(t).includes(e),ownKeys:t=>ei(t),set(t,e,i){const s=t._storage||(t._storage=n());return t[e]=s[e]=i,delete t._keys,!0}})}function $e(t,e,i,s){const a={_cacheable:!1,_proxy:t,_context:e,_subProxy:i,_stack:new Set,_descriptors:Ye(t,s),setContext:e=>$e(t,e,i,s),override:n=>$e(t.override(n),e,i,s)};return new Proxy(a,{deleteProperty:(e,i)=>(delete e[i],delete t[i],!0),get:(t,e,i)=>qe(t,e,(()=>function(t,e,i){const{_proxy:s,_context:a,_subProxy:r,_descriptors:l}=t;let h=s[e];S(h)&&l.isScriptable(e)&&(h=function(t,e,i,s){const{_proxy:n,_context:o,_subProxy:a,_stack:r}=i;if(r.has(t))throw new Error("Recursion detected: "+Array.from(r).join("->")+"->"+t);r.add(t);let l=e(o,a||s);r.delete(t),Xe(t,l)&&(l=Ze(n._scopes,n,t,l));return l}(e,h,t,i));n(h)&&h.length&&(h=function(t,e,i,s){const{_proxy:n,_context:a,_subProxy:r,_descriptors:l}=i;if(void 0!==a.index&&s(t))return e[a.index%e.length];if(o(e[0])){const i=e,s=n._scopes.filter((t=>t!==i));e=[];for(const o of i){const i=Ze(s,n,t,o);e.push($e(i,a,r&&r[t],l))}}return e}(e,h,t,l.isIndexable));Xe(e,h)&&(h=$e(h,a,r&&r[e],l));return h}(t,e,i))),getOwnPropertyDescriptor:(e,i)=>e._descriptors.allKeys?Reflect.has(t,i)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,i),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(e,i)=>Reflect.has(t,i),ownKeys:()=>Reflect.ownKeys(t),set:(e,i,s)=>(t[i]=s,delete e[i],!0)})}function Ye(t,e={scriptable:!0,indexable:!0}){const{_scriptable:i=e.scriptable,_indexable:s=e.indexable,_allKeys:n=e.allKeys}=t;return{allKeys:n,scriptable:i,indexable:s,isScriptable:S(i)?i:()=>i,isIndexable:S(s)?s:()=>s}}const Ue=(t,e)=>t?t+w(e):e,Xe=(t,e)=>o(e)&&"adapters"!==t&&(null===Object.getPrototypeOf(e)||e.constructor===Object);function qe(t,e,i){if(Object.prototype.hasOwnProperty.call(t,e)||"constructor"===e)return t[e];const s=i();return t[e]=s,s}function Ke(t,e,i){return S(t)?t(e,i):t}const Ge=(t,e)=>!0===t?e:"string"==typeof t?M(e,t):void 0;function Je(t,e,i,s,n){for(const o of e){const e=Ge(i,o);if(e){t.add(e);const o=Ke(e._fallback,i,n);if(void 0!==o&&o!==i&&o!==s)return o}else if(!1===e&&void 0!==s&&i!==s)return null}return!1}function Ze(t,e,i,s){const a=e._rootScopes,r=Ke(e._fallback,i,s),l=[...t,...a],h=new Set;h.add(s);let c=Qe(h,l,i,r||i,s);return null!==c&&((void 0===r||r===i||(c=Qe(h,l,r,c,s),null!==c))&&je(Array.from(h),[""],a,r,(()=>function(t,e,i){const s=t._getTarget();e in s||(s[e]={});const a=s[e];if(n(a)&&o(i))return i;return a||{}}(e,i,s))))}function Qe(t,e,i,s,n){for(;i;)i=Je(t,e,i,s,n);return i}function ti(t,e){for(const i of e){if(!i)continue;const e=i[t];if(void 0!==e)return e}}function ei(t){let e=t._keys;return e||(e=t._keys=function(t){const e=new Set;for(const i of t)for(const t of Object.keys(i).filter((t=>!t.startsWith("_"))))e.add(t);return Array.from(e)}(t._scopes)),e}function ii(t,e,i,s){const{iScale:n}=t,{key:o="r"}=this._parsing,a=new Array(s);let r,l,h,c;for(r=0,l=s;r<l;++r)h=r+i,c=e[h],a[r]={r:n.parse(M(c,o),h)};return a}const si=Number.EPSILON||1e-14,ni=(t,e)=>e<t.length&&!t[e].skip&&t[e],oi=t=>"x"===t?"y":"x";function ai(t,e,i,s){const n=t.skip?e:t,o=e,a=i.skip?e:i,r=q(o,n),l=q(a,o);let h=r/(r+l),c=l/(r+l);h=isNaN(h)?0:h,c=isNaN(c)?0:c;const d=s*h,u=s*c;return{previous:{x:o.x-d*(a.x-n.x),y:o.y-d*(a.y-n.y)},next:{x:o.x+u*(a.x-n.x),y:o.y+u*(a.y-n.y)}}}function ri(t,e="x"){const i=oi(e),s=t.length,n=Array(s).fill(0),o=Array(s);let a,r,l,h=ni(t,0);for(a=0;a<s;++a)if(r=l,l=h,h=ni(t,a+1),l){if(h){const t=h[e]-l[e];n[a]=0!==t?(h[i]-l[i])/t:0}o[a]=r?h?F(n[a-1])!==F(n[a])?0:(n[a-1]+n[a])/2:n[a-1]:n[a]}!function(t,e,i){const s=t.length;let n,o,a,r,l,h=ni(t,0);for(let c=0;c<s-1;++c)l=h,h=ni(t,c+1),l&&h&&(V(e[c],0,si)?i[c]=i[c+1]=0:(n=i[c]/e[c],o=i[c+1]/e[c],r=Math.pow(n,2)+Math.pow(o,2),r<=9||(a=3/Math.sqrt(r),i[c]=n*a*e[c],i[c+1]=o*a*e[c])))}(t,n,o),function(t,e,i="x"){const s=oi(i),n=t.length;let o,a,r,l=ni(t,0);for(let h=0;h<n;++h){if(a=r,r=l,l=ni(t,h+1),!r)continue;const n=r[i],c=r[s];a&&(o=(n-a[i])/3,r[`cp1${i}`]=n-o,r[`cp1${s}`]=c-o*e[h]),l&&(o=(l[i]-n)/3,r[`cp2${i}`]=n+o,r[`cp2${s}`]=c+o*e[h])}}(t,o,e)}function li(t,e,i){return Math.max(Math.min(t,i),e)}function hi(t,e,i,s,n){let o,a,r,l;if(e.spanGaps&&(t=t.filter((t=>!t.skip))),"monotone"===e.cubicInterpolationMode)ri(t,n);else{let i=s?t[t.length-1]:t[0];for(o=0,a=t.length;o<a;++o)r=t[o],l=ai(i,r,t[Math.min(o+1,a-(s?0:1))%a],e.tension),r.cp1x=l.previous.x,r.cp1y=l.previous.y,r.cp2x=l.next.x,r.cp2y=l.next.y,i=r}e.capBezierPoints&&function(t,e){let i,s,n,o,a,r=Re(t[0],e);for(i=0,s=t.length;i<s;++i)a=o,o=r,r=i<s-1&&Re(t[i+1],e),o&&(n=t[i],a&&(n.cp1x=li(n.cp1x,e.left,e.right),n.cp1y=li(n.cp1y,e.top,e.bottom)),r&&(n.cp2x=li(n.cp2x,e.left,e.right),n.cp2y=li(n.cp2y,e.top,e.bottom)))}(t,i)}const ci=t=>0===t||1===t,di=(t,e,i)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*O/i),ui=(t,e,i)=>Math.pow(2,-10*t)*Math.sin((t-e)*O/i)+1,fi={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*E),easeOutSine:t=>Math.sin(t*E),easeInOutSine:t=>-.5*(Math.cos(C*t)-1),easeInExpo:t=>0===t?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>1===t?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>ci(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>ci(t)?t:di(t,.075,.3),easeOutElastic:t=>ci(t)?t:ui(t,.075,.3),easeInOutElastic(t){const e=.1125;return ci(t)?t:t<.5?.5*di(2*t,e,.45):.5+.5*ui(2*t-1,e,.45)},easeInBack(t){const e=1.70158;return t*t*((e+1)*t-e)},easeOutBack(t){const e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-fi.easeOutBounce(1-t),easeOutBounce(t){const e=7.5625,i=2.75;return t<1/i?e*t*t:t<2/i?e*(t-=1.5/i)*t+.75:t<2.5/i?e*(t-=2.25/i)*t+.9375:e*(t-=2.625/i)*t+.984375},easeInOutBounce:t=>t<.5?.5*fi.easeInBounce(2*t):.5*fi.easeOutBounce(2*t-1)+.5};function gi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:t.y+i*(e.y-t.y)}}function pi(t,e,i,s){return{x:t.x+i*(e.x-t.x),y:"middle"===s?i<.5?t.y:e.y:"after"===s?i<1?t.y:e.y:i>0?e.y:t.y}}function mi(t,e,i,s){const n={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},a=gi(t,n,i),r=gi(n,o,i),l=gi(o,e,i),h=gi(a,r,i),c=gi(r,l,i);return gi(h,c,i)}const xi=/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/,bi=/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/;function _i(t,e){const i=(""+t).match(xi);if(!i||"normal"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case"px":return t;case"%":t/=100}return e*t}const yi=t=>+t||0;function vi(t,e){const i={},s=o(e),n=s?Object.keys(e):e,a=o(t)?s?i=>l(t[i],t[e[i]]):e=>t[e]:()=>t;for(const t of n)i[t]=yi(a(t));return i}function Mi(t){return vi(t,{top:"y",right:"x",bottom:"y",left:"x"})}function wi(t){return vi(t,["topLeft","topRight","bottomLeft","bottomRight"])}function ki(t){const e=Mi(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function Si(t,e){t=t||{},e=e||ue.font;let i=l(t.size,e.size);"string"==typeof i&&(i=parseInt(i,10));let s=l(t.style,e.style);s&&!(""+s).match(bi)&&(console.warn('Invalid font style specified: "'+s+'"'),s=void 0);const n={family:l(t.family,e.family),lineHeight:_i(l(t.lineHeight,e.lineHeight),i),size:i,style:s,weight:l(t.weight,e.weight),string:""};return n.string=De(n),n}function Pi(t,e,i,s){let o,a,r,l=!0;for(o=0,a=t.length;o<a;++o)if(r=t[o],void 0!==r&&(void 0!==e&&"function"==typeof r&&(r=r(e),l=!1),void 0!==i&&n(r)&&(r=r[i%r.length],l=!1),void 0!==r))return s&&!l&&(s.cacheable=!1),r}function Di(t,e,i){const{min:s,max:n}=t,o=c(e,(n-s)/2),a=(t,e)=>i&&0===t?0:t+e;return{min:a(s,-Math.abs(o)),max:a(n,o)}}function Ci(t,e){return Object.assign(Object.create(t),e)}function Oi(t,e,i){return t?function(t,e){return{x:i=>t+t+e-i,setWidth(t){e=t},textAlign:t=>"center"===t?t:"right"===t?"left":"right",xPlus:(t,e)=>t-e,leftForLtr:(t,e)=>t-e}}(e,i):{x:t=>t,setWidth(t){},textAlign:t=>t,xPlus:(t,e)=>t+e,leftForLtr:(t,e)=>t}}function Ai(t,e){let i,s;"ltr"!==e&&"rtl"!==e||(i=t.canvas.style,s=[i.getPropertyValue("direction"),i.getPropertyPriority("direction")],i.setProperty("direction",e,"important"),t.prevTextDirection=s)}function Ti(t,e){void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}function Li(t){return"angle"===t?{between:J,compare:K,normalize:G}:{between:tt,compare:(t,e)=>t-e,normalize:t=>t}}function Ei({start:t,end:e,count:i,loop:s,style:n}){return{start:t%i,end:e%i,loop:s&&(e-t+1)%i==0,style:n}}function Ri(t,e,i){if(!i)return[t];const{property:s,start:n,end:o}=i,a=e.length,{compare:r,between:l,normalize:h}=Li(s),{start:c,end:d,loop:u,style:f}=function(t,e,i){const{property:s,start:n,end:o}=i,{between:a,normalize:r}=Li(s),l=e.length;let h,c,{start:d,end:u,loop:f}=t;if(f){for(d+=l,u+=l,h=0,c=l;h<c&&a(r(e[d%l][s]),n,o);++h)d--,u--;d%=l,u%=l}return u<d&&(u+=l),{start:d,end:u,loop:f,style:t.style}}(t,e,i),g=[];let p,m,x,b=!1,_=null;const y=()=>b||l(n,x,p)&&0!==r(n,x),v=()=>!b||0===r(o,p)||l(o,x,p);for(let t=c,i=c;t<=d;++t)m=e[t%a],m.skip||(p=h(m[s]),p!==x&&(b=l(p,n,o),null===_&&y()&&(_=0===r(p,n)?t:i),null!==_&&v()&&(g.push(Ei({start:_,end:t,loop:u,count:a,style:f})),_=null),i=t,x=p));return null!==_&&g.push(Ei({start:_,end:d,loop:u,count:a,style:f})),g}function Ii(t,e){const i=[],s=t.segments;for(let n=0;n<s.length;n++){const o=Ri(s[n],t.points,e);o.length&&i.push(...o)}return i}function zi(t,e){const i=t.points,s=t.options.spanGaps,n=i.length;if(!n)return[];const o=!!t._loop,{start:a,end:r}=function(t,e,i,s){let n=0,o=e-1;if(i&&!s)for(;n<e&&!t[n].skip;)n++;for(;n<e&&t[n].skip;)n++;for(n%=e,i&&(o+=n);o>n&&t[o%e].skip;)o--;return o%=e,{start:n,end:o}}(i,n,o,s);if(!0===s)return Fi(t,[{start:a,end:r,loop:o}],i,e);return Fi(t,function(t,e,i,s){const n=t.length,o=[];let a,r=e,l=t[e];for(a=e+1;a<=i;++a){const i=t[a%n];i.skip||i.stop?l.skip||(s=!1,o.push({start:e%n,end:(a-1)%n,loop:s}),e=r=i.stop?a:null):(r=a,l.skip&&(e=a)),l=i}return null!==r&&o.push({start:e%n,end:r%n,loop:s}),o}(i,a,r<a?r+n:r,!!t._fullLoop&&0===a&&r===n-1),i,e)}function Fi(t,e,i,s){return s&&s.setContext&&i?function(t,e,i,s){const n=t._chart.getContext(),o=Vi(t.options),{_datasetIndex:a,options:{spanGaps:r}}=t,l=i.length,h=[];let c=o,d=e[0].start,u=d;function f(t,e,s,n){const o=r?-1:1;if(t!==e){for(t+=l;i[t%l].skip;)t-=o;for(;i[e%l].skip;)e+=o;t%l!=e%l&&(h.push({start:t%l,end:e%l,loop:s,style:n}),c=n,d=e%l)}}for(const t of e){d=r?d:t.start;let e,o=i[d%l];for(u=d+1;u<=t.end;u++){const r=i[u%l];e=Vi(s.setContext(Ci(n,{type:"segment",p0:o,p1:r,p0DataIndex:(u-1)%l,p1DataIndex:u%l,datasetIndex:a}))),Bi(e,c)&&f(d,u-1,t.loop,c),o=r,c=e}d<u-1&&f(d,u-1,t.loop,c)}return h}(t,e,i,s):e}function Vi(t){return{backgroundColor:t.backgroundColor,borderCapStyle:t.borderCapStyle,borderDash:t.borderDash,borderDashOffset:t.borderDashOffset,borderJoinStyle:t.borderJoinStyle,borderWidth:t.borderWidth,borderColor:t.borderColor}}function Bi(t,e){if(!e)return!1;const i=[],s=function(t,e){return Zt(e)?(i.includes(e)||i.push(e),i.indexOf(e)):e};return JSON.stringify(t,s)!==JSON.stringify(e,s)}function Wi(t,e,i){return t.options.clip?t[i]:e[i]}function Ni(t,e){const i=e._clip;if(i.disabled)return!1;const s=function(t,e){const{xScale:i,yScale:s}=t;return i&&s?{left:Wi(i,e,"left"),right:Wi(i,e,"right"),top:Wi(s,e,"top"),bottom:Wi(s,e,"bottom")}:e}(e,t.chartArea);return{left:!1===i.left?0:s.left-(!0===i.left?0:i.left),right:!1===i.right?t.width:s.right+(!0===i.right?0:i.right),top:!1===i.top?0:s.top-(!0===i.top?0:i.top),bottom:!1===i.bottom?t.height:s.bottom+(!0===i.bottom?0:i.bottom)}}var Hi=Object.freeze({__proto__:null,HALF_PI:E,INFINITY:T,PI:C,PITAU:A,QUARTER_PI:R,RAD_PER_DEG:L,TAU:O,TWO_THIRDS_PI:I,_addGrace:Di,_alignPixel:Ae,_alignStartEnd:ft,_angleBetween:J,_angleDiff:K,_arrayUnique:lt,_attachContext:$e,_bezierCurveTo:Ve,_bezierInterpolation:mi,_boundSegment:Ri,_boundSegments:Ii,_capitalize:w,_computeSegments:zi,_createResolver:je,_decimalPlaces:U,_deprecated:function(t,e,i,s){void 0!==e&&console.warn(t+': "'+i+'" is deprecated. Please use "'+s+'" instead')},_descriptors:Ye,_elementsEqual:f,_factorize:W,_filterBetween:nt,_getParentNode:ge,_getStartAndCountOfVisiblePoints:pt,_int16Range:Q,_isBetween:tt,_isClickEvent:D,_isDomSupported:fe,_isPointInArea:Re,_limitValue:Z,_longestText:Oe,_lookup:et,_lookupByKey:it,_measureText:Ce,_merger:m,_mergerIf:_,_normalizeAngle:G,_parseObjectDataRadialScale:ii,_pointInLine:gi,_readValueToProps:vi,_rlookupByKey:st,_scaleRangesChanged:mt,_setMinAndMaxByKey:j,_splitKey:v,_steppedInterpolation:pi,_steppedLineTo:Fe,_textX:gt,_toLeftRightCenter:ut,_updateBezierControlPoints:hi,addRoundedRectPath:He,almostEquals:V,almostWhole:H,callback:d,clearCanvas:Te,clipArea:Ie,clone:g,color:Qt,createContext:Ci,debounce:dt,defined:k,distanceBetweenPoints:q,drawPoint:Le,drawPointLegend:Ee,each:u,easingEffects:fi,finiteOrDefault:r,fontString:function(t,e,i){return e+" "+t+"px "+i},formatNumber:ne,getAngleFromPoint:X,getDatasetClipArea:Ni,getHoverColor:te,getMaximumSize:we,getRelativePosition:ve,getRtlAdapter:Oi,getStyle:xe,isArray:n,isFinite:a,isFunction:S,isNullOrUndef:s,isNumber:N,isObject:o,isPatternOrGradient:Zt,listenArrayEvents:at,log10:z,merge:x,mergeIf:b,niceNum:B,noop:e,overrideTextDirection:Ai,readUsedSize:Pe,renderText:Ne,requestAnimFrame:ht,resolve:Pi,resolveObjectKey:M,restoreTextDirection:Ti,retinaScale:ke,setsEqual:P,sign:F,splineCurve:ai,splineCurveMonotone:ri,supportsEventListenerOptions:Se,throttled:ct,toDegrees:Y,toDimension:c,toFont:Si,toFontString:De,toLineHeight:_i,toPadding:ki,toPercentage:h,toRadians:$,toTRBL:Mi,toTRBLCorners:wi,uid:i,unclipArea:ze,unlistenArrayEvents:rt,valueOrDefault:l});function ji(t,e,i,n){const{controller:o,data:a,_sorted:r}=t,l=o._cachedMeta.iScale,h=t.dataset&&t.dataset.options?t.dataset.options.spanGaps:null;if(l&&e===l.axis&&"r"!==e&&r&&a.length){const r=l._reversePixels?st:it;if(!n){const n=r(a,e,i);if(h){const{vScale:e}=o._cachedMeta,{_parsed:i}=t,a=i.slice(0,n.lo+1).reverse().findIndex((t=>!s(t[e.axis])));n.lo-=Math.max(0,a);const r=i.slice(n.hi).findIndex((t=>!s(t[e.axis])));n.hi+=Math.max(0,r)}return n}if(o._sharedOptions){const t=a[0],s="function"==typeof t.getRange&&t.getRange(e);if(s){const t=r(a,e,i-s),n=r(a,e,i+s);return{lo:t.lo,hi:n.hi}}}}return{lo:0,hi:a.length-1}}function $i(t,e,i,s,n){const o=t.getSortedVisibleDatasetMetas(),a=i[e];for(let t=0,i=o.length;t<i;++t){const{index:i,data:r}=o[t],{lo:l,hi:h}=ji(o[t],e,a,n);for(let t=l;t<=h;++t){const e=r[t];e.skip||s(e,i,t)}}}function Yi(t,e,i,s,n){const o=[];if(!n&&!t.isPointInArea(e))return o;return $i(t,i,e,(function(i,a,r){(n||Re(i,t.chartArea,0))&&i.inRange(e.x,e.y,s)&&o.push({element:i,datasetIndex:a,index:r})}),!0),o}function Ui(t,e,i,s,n,o){let a=[];const r=function(t){const e=-1!==t.indexOf("x"),i=-1!==t.indexOf("y");return function(t,s){const n=e?Math.abs(t.x-s.x):0,o=i?Math.abs(t.y-s.y):0;return Math.sqrt(Math.pow(n,2)+Math.pow(o,2))}}(i);let l=Number.POSITIVE_INFINITY;return $i(t,i,e,(function(i,h,c){const d=i.inRange(e.x,e.y,n);if(s&&!d)return;const u=i.getCenterPoint(n);if(!(!!o||t.isPointInArea(u))&&!d)return;const f=r(e,u);f<l?(a=[{element:i,datasetIndex:h,index:c}],l=f):f===l&&a.push({element:i,datasetIndex:h,index:c})})),a}function Xi(t,e,i,s,n,o){return o||t.isPointInArea(e)?"r"!==i||s?Ui(t,e,i,s,n,o):function(t,e,i,s){let n=[];return $i(t,i,e,(function(t,i,o){const{startAngle:a,endAngle:r}=t.getProps(["startAngle","endAngle"],s),{angle:l}=X(t,{x:e.x,y:e.y});J(l,a,r)&&n.push({element:t,datasetIndex:i,index:o})})),n}(t,e,i,n):[]}function qi(t,e,i,s,n){const o=[],a="x"===i?"inXRange":"inYRange";let r=!1;return $i(t,i,e,((t,s,l)=>{t[a]&&t[a](e[i],n)&&(o.push({element:t,datasetIndex:s,index:l}),r=r||t.inRange(e.x,e.y,n))})),s&&!r?[]:o}var Ki={evaluateInteractionItems:$i,modes:{index(t,e,i,s){const n=ve(e,t),o=i.axis||"x",a=i.includeInvisible||!1,r=i.intersect?Yi(t,n,o,s,a):Xi(t,n,o,!1,s,a),l=[];return r.length?(t.getSortedVisibleDatasetMetas().forEach((t=>{const e=r[0].index,i=t.data[e];i&&!i.skip&&l.push({element:i,datasetIndex:t.index,index:e})})),l):[]},dataset(t,e,i,s){const n=ve(e,t),o=i.axis||"xy",a=i.includeInvisible||!1;let r=i.intersect?Yi(t,n,o,s,a):Xi(t,n,o,!1,s,a);if(r.length>0){const e=r[0].datasetIndex,i=t.getDatasetMeta(e).data;r=[];for(let t=0;t<i.length;++t)r.push({element:i[t],datasetIndex:e,index:t})}return r},point:(t,e,i,s)=>Yi(t,ve(e,t),i.axis||"xy",s,i.includeInvisible||!1),nearest(t,e,i,s){const n=ve(e,t),o=i.axis||"xy",a=i.includeInvisible||!1;return Xi(t,n,o,i.intersect,s,a)},x:(t,e,i,s)=>qi(t,ve(e,t),"x",i.intersect,s),y:(t,e,i,s)=>qi(t,ve(e,t),"y",i.intersect,s)}};const Gi=["left","top","right","bottom"];function Ji(t,e){return t.filter((t=>t.pos===e))}function Zi(t,e){return t.filter((t=>-1===Gi.indexOf(t.pos)&&t.box.axis===e))}function Qi(t,e){return t.sort(((t,i)=>{const s=e?i:t,n=e?t:i;return s.weight===n.weight?s.index-n.index:s.weight-n.weight}))}function ts(t,e){const i=function(t){const e={};for(const i of t){const{stack:t,pos:s,stackWeight:n}=i;if(!t||!Gi.includes(s))continue;const o=e[t]||(e[t]={count:0,placed:0,weight:0,size:0});o.count++,o.weight+=n}return e}(t),{vBoxMaxWidth:s,hBoxMaxHeight:n}=e;let o,a,r;for(o=0,a=t.length;o<a;++o){r=t[o];const{fullSize:a}=r.box,l=i[r.stack],h=l&&r.stackWeight/l.weight;r.horizontal?(r.width=h?h*s:a&&e.availableWidth,r.height=n):(r.width=s,r.height=h?h*n:a&&e.availableHeight)}return i}function es(t,e,i,s){return Math.max(t[i],e[i])+Math.max(t[s],e[s])}function is(t,e){t.top=Math.max(t.top,e.top),t.left=Math.max(t.left,e.left),t.bottom=Math.max(t.bottom,e.bottom),t.right=Math.max(t.right,e.right)}function ss(t,e,i,s){const{pos:n,box:a}=i,r=t.maxPadding;if(!o(n)){i.size&&(t[n]-=i.size);const e=s[i.stack]||{size:0,count:1};e.size=Math.max(e.size,i.horizontal?a.height:a.width),i.size=e.size/e.count,t[n]+=i.size}a.getPadding&&is(r,a.getPadding());const l=Math.max(0,e.outerWidth-es(r,t,"left","right")),h=Math.max(0,e.outerHeight-es(r,t,"top","bottom")),c=l!==t.w,d=h!==t.h;return t.w=l,t.h=h,i.horizontal?{same:c,other:d}:{same:d,other:c}}function ns(t,e){const i=e.maxPadding;function s(t){const s={left:0,top:0,right:0,bottom:0};return t.forEach((t=>{s[t]=Math.max(e[t],i[t])})),s}return s(t?["left","right"]:["top","bottom"])}function os(t,e,i,s){const n=[];let o,a,r,l,h,c;for(o=0,a=t.length,h=0;o<a;++o){r=t[o],l=r.box,l.update(r.width||e.w,r.height||e.h,ns(r.horizontal,e));const{same:a,other:d}=ss(e,i,r,s);h|=a&&n.length,c=c||d,l.fullSize||n.push(r)}return h&&os(n,e,i,s)||c}function as(t,e,i,s,n){t.top=i,t.left=e,t.right=e+s,t.bottom=i+n,t.width=s,t.height=n}function rs(t,e,i,s){const n=i.padding;let{x:o,y:a}=e;for(const r of t){const t=r.box,l=s[r.stack]||{count:1,placed:0,weight:1},h=r.stackWeight/l.weight||1;if(r.horizontal){const s=e.w*h,o=l.size||t.height;k(l.start)&&(a=l.start),t.fullSize?as(t,n.left,a,i.outerWidth-n.right-n.left,o):as(t,e.left+l.placed,a,s,o),l.start=a,l.placed+=s,a=t.bottom}else{const s=e.h*h,a=l.size||t.width;k(l.start)&&(o=l.start),t.fullSize?as(t,o,n.top,a,i.outerHeight-n.bottom-n.top):as(t,o,e.top+l.placed,a,s),l.start=o,l.placed+=s,o=t.right}}e.x=o,e.y=a}var ls={addBox(t,e){t.boxes||(t.boxes=[]),e.fullSize=e.fullSize||!1,e.position=e.position||"top",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw(t){e.draw(t)}}]},t.boxes.push(e)},removeBox(t,e){const i=t.boxes?t.boxes.indexOf(e):-1;-1!==i&&t.boxes.splice(i,1)},configure(t,e,i){e.fullSize=i.fullSize,e.position=i.position,e.weight=i.weight},update(t,e,i,s){if(!t)return;const n=ki(t.options.layout.padding),o=Math.max(e-n.width,0),a=Math.max(i-n.height,0),r=function(t){const e=function(t){const e=[];let i,s,n,o,a,r;for(i=0,s=(t||[]).length;i<s;++i)n=t[i],({position:o,options:{stack:a,stackWeight:r=1}}=n),e.push({index:i,box:n,pos:o,horizontal:n.isHorizontal(),weight:n.weight,stack:a&&o+a,stackWeight:r});return e}(t),i=Qi(e.filter((t=>t.box.fullSize)),!0),s=Qi(Ji(e,"left"),!0),n=Qi(Ji(e,"right")),o=Qi(Ji(e,"top"),!0),a=Qi(Ji(e,"bottom")),r=Zi(e,"x"),l=Zi(e,"y");return{fullSize:i,leftAndTop:s.concat(o),rightAndBottom:n.concat(l).concat(a).concat(r),chartArea:Ji(e,"chartArea"),vertical:s.concat(n).concat(l),horizontal:o.concat(a).concat(r)}}(t.boxes),l=r.vertical,h=r.horizontal;u(t.boxes,(t=>{"function"==typeof t.beforeLayout&&t.beforeLayout()}));const c=l.reduce(((t,e)=>e.box.options&&!1===e.box.options.display?t:t+1),0)||1,d=Object.freeze({outerWidth:e,outerHeight:i,padding:n,availableWidth:o,availableHeight:a,vBoxMaxWidth:o/2/c,hBoxMaxHeight:a/2}),f=Object.assign({},n);is(f,ki(s));const g=Object.assign({maxPadding:f,w:o,h:a,x:n.left,y:n.top},n),p=ts(l.concat(h),d);os(r.fullSize,g,d,p),os(l,g,d,p),os(h,g,d,p)&&os(l,g,d,p),function(t){const e=t.maxPadding;function i(i){const s=Math.max(e[i]-t[i],0);return t[i]+=s,s}t.y+=i("top"),t.x+=i("left"),i("right"),i("bottom")}(g),rs(r.leftAndTop,g,d,p),g.x+=g.w,g.y+=g.h,rs(r.rightAndBottom,g,d,p),t.chartArea={left:g.left,top:g.top,right:g.left+g.w,bottom:g.top+g.h,height:g.h,width:g.w},u(r.chartArea,(e=>{const i=e.box;Object.assign(i,t.chartArea),i.update(g.w,g.h,{left:0,top:0,right:0,bottom:0})}))}};class hs{acquireContext(t,e){}releaseContext(t){return!1}addEventListener(t,e,i){}removeEventListener(t,e,i){}getDevicePixelRatio(){return 1}getMaximumSize(t,e,i,s){return e=Math.max(0,e||t.width),i=i||t.height,{width:e,height:Math.max(0,s?Math.floor(e/s):i)}}isAttached(t){return!0}updateConfig(t){}}class cs extends hs{acquireContext(t){return t&&t.getContext&&t.getContext("2d")||null}updateConfig(t){t.options.animation=!1}}const ds="$chartjs",us={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},fs=t=>null===t||""===t;const gs=!!Se&&{passive:!0};function ps(t,e,i){t&&t.canvas&&t.canvas.removeEventListener(e,i,gs)}function ms(t,e){for(const i of t)if(i===e||i.contains(e))return!0}function xs(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||ms(i.addedNodes,s),e=e&&!ms(i.removedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}function bs(t,e,i){const s=t.canvas,n=new MutationObserver((t=>{let e=!1;for(const i of t)e=e||ms(i.removedNodes,s),e=e&&!ms(i.addedNodes,s);e&&i()}));return n.observe(document,{childList:!0,subtree:!0}),n}const _s=new Map;let ys=0;function vs(){const t=window.devicePixelRatio;t!==ys&&(ys=t,_s.forEach(((e,i)=>{i.currentDevicePixelRatio!==t&&e()})))}function Ms(t,e,i){const s=t.canvas,n=s&&ge(s);if(!n)return;const o=ct(((t,e)=>{const s=n.clientWidth;i(t,e),s<n.clientWidth&&i()}),window),a=new ResizeObserver((t=>{const e=t[0],i=e.contentRect.width,s=e.contentRect.height;0===i&&0===s||o(i,s)}));return a.observe(n),function(t,e){_s.size||window.addEventListener("resize",vs),_s.set(t,e)}(t,o),a}function ws(t,e,i){i&&i.disconnect(),"resize"===e&&function(t){_s.delete(t),_s.size||window.removeEventListener("resize",vs)}(t)}function ks(t,e,i){const s=t.canvas,n=ct((e=>{null!==t.ctx&&i(function(t,e){const i=us[t.type]||t.type,{x:s,y:n}=ve(t,e);return{type:i,chart:e,native:t,x:void 0!==s?s:null,y:void 0!==n?n:null}}(e,t))}),t);return function(t,e,i){t&&t.addEventListener(e,i,gs)}(s,e,n),n}class Ss extends hs{acquireContext(t,e){const i=t&&t.getContext&&t.getContext("2d");return i&&i.canvas===t?(function(t,e){const i=t.style,s=t.getAttribute("height"),n=t.getAttribute("width");if(t[ds]={initial:{height:s,width:n,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||"block",i.boxSizing=i.boxSizing||"border-box",fs(n)){const e=Pe(t,"width");void 0!==e&&(t.width=e)}if(fs(s))if(""===t.style.height)t.height=t.width/(e||2);else{const e=Pe(t,"height");void 0!==e&&(t.height=e)}}(t,e),i):null}releaseContext(t){const e=t.canvas;if(!e[ds])return!1;const i=e[ds].initial;["height","width"].forEach((t=>{const n=i[t];s(n)?e.removeAttribute(t):e.setAttribute(t,n)}));const n=i.style||{};return Object.keys(n).forEach((t=>{e.style[t]=n[t]})),e.width=e.width,delete e[ds],!0}addEventListener(t,e,i){this.removeEventListener(t,e);const s=t.$proxies||(t.$proxies={}),n={attach:xs,detach:bs,resize:Ms}[e]||ks;s[e]=n(t,e,i)}removeEventListener(t,e){const i=t.$proxies||(t.$proxies={}),s=i[e];if(!s)return;({attach:ws,detach:ws,resize:ws}[e]||ps)(t,e,s),i[e]=void 0}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(t,e,i,s){return we(t,e,i,s)}isAttached(t){const e=t&&ge(t);return!(!e||!e.isConnected)}}function Ps(t){return!fe()||"undefined"!=typeof OffscreenCanvas&&t instanceof OffscreenCanvas?cs:Ss}var Ds=Object.freeze({__proto__:null,BasePlatform:hs,BasicPlatform:cs,DomPlatform:Ss,_detectPlatform:Ps});const Cs="transparent",Os={boolean:(t,e,i)=>i>.5?e:t,color(t,e,i){const s=Qt(t||Cs),n=s.valid&&Qt(e||Cs);return n&&n.valid?n.mix(s,i).hexString():e},number:(t,e,i)=>t+(e-t)*i};class As{constructor(t,e,i,s){const n=e[i];s=Pi([t.to,s,n,t.from]);const o=Pi([t.from,n,s]);this._active=!0,this._fn=t.fn||Os[t.type||typeof o],this._easing=fi[t.easing]||fi.linear,this._start=Math.floor(Date.now()+(t.delay||0)),this._duration=this._total=Math.floor(t.duration),this._loop=!!t.loop,this._target=e,this._prop=i,this._from=o,this._to=s,this._promises=void 0}active(){return this._active}update(t,e,i){if(this._active){this._notify(!1);const s=this._target[this._prop],n=i-this._start,o=this._duration-n;this._start=i,this._duration=Math.floor(Math.max(o,t.duration)),this._total+=n,this._loop=!!t.loop,this._to=Pi([t.to,e,s,t.from]),this._from=Pi([t.from,s,e])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(t){const e=t-this._start,i=this._duration,s=this._prop,n=this._from,o=this._loop,a=this._to;let r;if(this._active=n!==a&&(o||e<i),!this._active)return this._target[s]=a,void this._notify(!0);e<0?this._target[s]=n:(r=e/i%2,r=o&&r>1?2-r:r,r=this._easing(Math.min(1,Math.max(0,r))),this._target[s]=this._fn(n,a,r))}wait(){const t=this._promises||(this._promises=[]);return new Promise(((e,i)=>{t.push({res:e,rej:i})}))}_notify(t){const e=t?"res":"rej",i=this._promises||[];for(let t=0;t<i.length;t++)i[t][e]()}}class Ts{constructor(t,e){this._chart=t,this._properties=new Map,this.configure(e)}configure(t){if(!o(t))return;const e=Object.keys(ue.animation),i=this._properties;Object.getOwnPropertyNames(t).forEach((s=>{const a=t[s];if(!o(a))return;const r={};for(const t of e)r[t]=a[t];(n(a.properties)&&a.properties||[s]).forEach((t=>{t!==s&&i.has(t)||i.set(t,r)}))}))}_animateOptions(t,e){const i=e.options,s=function(t,e){if(!e)return;let i=t.options;if(!i)return void(t.options=e);i.$shared&&(t.options=i=Object.assign({},i,{$shared:!1,$animations:{}}));return i}(t,i);if(!s)return[];const n=this._createAnimations(s,i);return i.$shared&&function(t,e){const i=[],s=Object.keys(e);for(let e=0;e<s.length;e++){const n=t[s[e]];n&&n.active()&&i.push(n.wait())}return Promise.all(i)}(t.options.$animations,i).then((()=>{t.options=i}),(()=>{})),n}_createAnimations(t,e){const i=this._properties,s=[],n=t.$animations||(t.$animations={}),o=Object.keys(e),a=Date.now();let r;for(r=o.length-1;r>=0;--r){const l=o[r];if("$"===l.charAt(0))continue;if("options"===l){s.push(...this._animateOptions(t,e));continue}const h=e[l];let c=n[l];const d=i.get(l);if(c){if(d&&c.active()){c.update(d,h,a);continue}c.cancel()}d&&d.duration?(n[l]=c=new As(d,t,l,h),s.push(c)):t[l]=h}return s}update(t,e){if(0===this._properties.size)return void Object.assign(t,e);const i=this._createAnimations(t,e);return i.length?(bt.add(this._chart,i),!0):void 0}}function Ls(t,e){const i=t&&t.options||{},s=i.reverse,n=void 0===i.min?e:0,o=void 0===i.max?e:0;return{start:s?o:n,end:s?n:o}}function Es(t,e){const i=[],s=t._getSortedDatasetMetas(e);let n,o;for(n=0,o=s.length;n<o;++n)i.push(s[n].index);return i}function Rs(t,e,i,s={}){const n=t.keys,o="single"===s.mode;let r,l,h,c;if(null===e)return;let d=!1;for(r=0,l=n.length;r<l;++r){if(h=+n[r],h===i){if(d=!0,s.all)continue;break}c=t.values[h],a(c)&&(o||0===e||F(e)===F(c))&&(e+=c)}return d||s.all?e:0}function Is(t,e){const i=t&&t.options.stacked;return i||void 0===i&&void 0!==e.stack}function zs(t,e,i){const s=t[e]||(t[e]={});return s[i]||(s[i]={})}function Fs(t,e,i,s){for(const n of e.getMatchingVisibleMetas(s).reverse()){const e=t[n.index];if(i&&e>0||!i&&e<0)return n.index}return null}function Vs(t,e){const{chart:i,_cachedMeta:s}=t,n=i._stacks||(i._stacks={}),{iScale:o,vScale:a,index:r}=s,l=o.axis,h=a.axis,c=function(t,e,i){return`${t.id}.${e.id}.${i.stack||i.type}`}(o,a,s),d=e.length;let u;for(let t=0;t<d;++t){const i=e[t],{[l]:o,[h]:d}=i;u=(i._stacks||(i._stacks={}))[h]=zs(n,c,o),u[r]=d,u._top=Fs(u,a,!0,s.type),u._bottom=Fs(u,a,!1,s.type);(u._visualValues||(u._visualValues={}))[r]=d}}function Bs(t,e){const i=t.scales;return Object.keys(i).filter((t=>i[t].axis===e)).shift()}function Ws(t,e){const i=t.controller.index,s=t.vScale&&t.vScale.axis;if(s){e=e||t._parsed;for(const t of e){const e=t._stacks;if(!e||void 0===e[s]||void 0===e[s][i])return;delete e[s][i],void 0!==e[s]._visualValues&&void 0!==e[s]._visualValues[i]&&delete e[s]._visualValues[i]}}}const Ns=t=>"reset"===t||"none"===t,Hs=(t,e)=>e?t:Object.assign({},t);class js{static defaults={};static datasetElementType=null;static dataElementType=null;constructor(t,e){this.chart=t,this._ctx=t.ctx,this.index=e,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const t=this._cachedMeta;this.configure(),this.linkScales(),t._stacked=Is(t.vScale,t),this.addElements(),this.options.fill&&!this.chart.isPluginEnabled("filler")&&console.warn("Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options")}updateIndex(t){this.index!==t&&Ws(this._cachedMeta),this.index=t}linkScales(){const t=this.chart,e=this._cachedMeta,i=this.getDataset(),s=(t,e,i,s)=>"x"===t?e:"r"===t?s:i,n=e.xAxisID=l(i.xAxisID,Bs(t,"x")),o=e.yAxisID=l(i.yAxisID,Bs(t,"y")),a=e.rAxisID=l(i.rAxisID,Bs(t,"r")),r=e.indexAxis,h=e.iAxisID=s(r,n,o,a),c=e.vAxisID=s(r,o,n,a);e.xScale=this.getScaleForId(n),e.yScale=this.getScaleForId(o),e.rScale=this.getScaleForId(a),e.iScale=this.getScaleForId(h),e.vScale=this.getScaleForId(c)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(t){return this.chart.scales[t]}_getOtherScale(t){const e=this._cachedMeta;return t===e.iScale?e.vScale:e.iScale}reset(){this._update("reset")}_destroy(){const t=this._cachedMeta;this._data&&rt(this._data,this),t._stacked&&Ws(t)}_dataCheck(){const t=this.getDataset(),e=t.data||(t.data=[]),i=this._data;if(o(e)){const t=this._cachedMeta;this._data=function(t,e){const{iScale:i,vScale:s}=e,n="x"===i.axis?"x":"y",o="x"===s.axis?"x":"y",a=Object.keys(t),r=new Array(a.length);let l,h,c;for(l=0,h=a.length;l<h;++l)c=a[l],r[l]={[n]:c,[o]:t[c]};return r}(e,t)}else if(i!==e){if(i){rt(i,this);const t=this._cachedMeta;Ws(t),t._parsed=[]}e&&Object.isExtensible(e)&&at(e,this),this._syncList=[],this._data=e}}addElements(){const t=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(t.dataset=new this.datasetElementType)}buildOrUpdateElements(t){const e=this._cachedMeta,i=this.getDataset();let s=!1;this._dataCheck();const n=e._stacked;e._stacked=Is(e.vScale,e),e.stack!==i.stack&&(s=!0,Ws(e),e.stack=i.stack),this._resyncElements(t),(s||n!==e._stacked)&&(Vs(this,e._parsed),e._stacked=Is(e.vScale,e))}configure(){const t=this.chart.config,e=t.datasetScopeKeys(this._type),i=t.getOptionScopes(this.getDataset(),e,!0);this.options=t.createResolver(i,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(t,e){const{_cachedMeta:i,_data:s}=this,{iScale:a,_stacked:r}=i,l=a.axis;let h,c,d,u=0===t&&e===s.length||i._sorted,f=t>0&&i._parsed[t-1];if(!1===this._parsing)i._parsed=s,i._sorted=!0,d=s;else{d=n(s[t])?this.parseArrayData(i,s,t,e):o(s[t])?this.parseObjectData(i,s,t,e):this.parsePrimitiveData(i,s,t,e);const a=()=>null===c[l]||f&&c[l]<f[l];for(h=0;h<e;++h)i._parsed[h+t]=c=d[h],u&&(a()&&(u=!1),f=c);i._sorted=u}r&&Vs(this,d)}parsePrimitiveData(t,e,i,s){const{iScale:n,vScale:o}=t,a=n.axis,r=o.axis,l=n.getLabels(),h=n===o,c=new Array(s);let d,u,f;for(d=0,u=s;d<u;++d)f=d+i,c[d]={[a]:h||n.parse(l[f],f),[r]:o.parse(e[f],f)};return c}parseArrayData(t,e,i,s){const{xScale:n,yScale:o}=t,a=new Array(s);let r,l,h,c;for(r=0,l=s;r<l;++r)h=r+i,c=e[h],a[r]={x:n.parse(c[0],h),y:o.parse(c[1],h)};return a}parseObjectData(t,e,i,s){const{xScale:n,yScale:o}=t,{xAxisKey:a="x",yAxisKey:r="y"}=this._parsing,l=new Array(s);let h,c,d,u;for(h=0,c=s;h<c;++h)d=h+i,u=e[d],l[h]={x:n.parse(M(u,a),d),y:o.parse(M(u,r),d)};return l}getParsed(t){return this._cachedMeta._parsed[t]}getDataElement(t){return this._cachedMeta.data[t]}applyStack(t,e,i){const s=this.chart,n=this._cachedMeta,o=e[t.axis];return Rs({keys:Es(s,!0),values:e._stacks[t.axis]._visualValues},o,n.index,{mode:i})}updateRangeFromParsed(t,e,i,s){const n=i[e.axis];let o=null===n?NaN:n;const a=s&&i._stacks[e.axis];s&&a&&(s.values=a,o=Rs(s,n,this._cachedMeta.index)),t.min=Math.min(t.min,o),t.max=Math.max(t.max,o)}getMinMax(t,e){const i=this._cachedMeta,s=i._parsed,n=i._sorted&&t===i.iScale,o=s.length,r=this._getOtherScale(t),l=((t,e,i)=>t&&!e.hidden&&e._stacked&&{keys:Es(i,!0),values:null})(e,i,this.chart),h={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:c,max:d}=function(t){const{min:e,max:i,minDefined:s,maxDefined:n}=t.getUserBounds();return{min:s?e:Number.NEGATIVE_INFINITY,max:n?i:Number.POSITIVE_INFINITY}}(r);let u,f;function g(){f=s[u];const e=f[r.axis];return!a(f[t.axis])||c>e||d<e}for(u=0;u<o&&(g()||(this.updateRangeFromParsed(h,t,f,l),!n));++u);if(n)for(u=o-1;u>=0;--u)if(!g()){this.updateRangeFromParsed(h,t,f,l);break}return h}getAllParsedValues(t){const e=this._cachedMeta._parsed,i=[];let s,n,o;for(s=0,n=e.length;s<n;++s)o=e[s][t.axis],a(o)&&i.push(o);return i}getMaxOverflow(){return!1}getLabelAndValue(t){const e=this._cachedMeta,i=e.iScale,s=e.vScale,n=this.getParsed(t);return{label:i?""+i.getLabelForValue(n[i.axis]):"",value:s?""+s.getLabelForValue(n[s.axis]):""}}_update(t){const e=this._cachedMeta;this.update(t||"default"),e._clip=function(t){let e,i,s,n;return o(t)?(e=t.top,i=t.right,s=t.bottom,n=t.left):e=i=s=n=t,{top:e,right:i,bottom:s,left:n,disabled:!1===t}}(l(this.options.clip,function(t,e,i){if(!1===i)return!1;const s=Ls(t,i),n=Ls(e,i);return{top:n.end,right:s.end,bottom:n.start,left:s.start}}(e.xScale,e.yScale,this.getMaxOverflow())))}update(t){}draw(){const t=this._ctx,e=this.chart,i=this._cachedMeta,s=i.data||[],n=e.chartArea,o=[],a=this._drawStart||0,r=this._drawCount||s.length-a,l=this.options.drawActiveElementsOnTop;let h;for(i.dataset&&i.dataset.draw(t,n,a,r),h=a;h<a+r;++h){const e=s[h];e.hidden||(e.active&&l?o.push(e):e.draw(t,n))}for(h=0;h<o.length;++h)o[h].draw(t,n)}getStyle(t,e){const i=e?"active":"default";return void 0===t&&this._cachedMeta.dataset?this.resolveDatasetElementOptions(i):this.resolveDataElementOptions(t||0,i)}getContext(t,e,i){const s=this.getDataset();let n;if(t>=0&&t<this._cachedMeta.data.length){const e=this._cachedMeta.data[t];n=e.$context||(e.$context=function(t,e,i){return Ci(t,{active:!1,dataIndex:e,parsed:void 0,raw:void 0,element:i,index:e,mode:"default",type:"data"})}(this.getContext(),t,e)),n.parsed=this.getParsed(t),n.raw=s.data[t],n.index=n.dataIndex=t}else n=this.$context||(this.$context=function(t,e){return Ci(t,{active:!1,dataset:void 0,datasetIndex:e,index:e,mode:"default",type:"dataset"})}(this.chart.getContext(),this.index)),n.dataset=s,n.index=n.datasetIndex=this.index;return n.active=!!e,n.mode=i,n}resolveDatasetElementOptions(t){return this._resolveElementOptions(this.datasetElementType.id,t)}resolveDataElementOptions(t,e){return this._resolveElementOptions(this.dataElementType.id,e,t)}_resolveElementOptions(t,e="default",i){const s="active"===e,n=this._cachedDataOpts,o=t+"-"+e,a=n[o],r=this.enableOptionSharing&&k(i);if(a)return Hs(a,r);const l=this.chart.config,h=l.datasetElementScopeKeys(this._type,t),c=s?[`${t}Hover`,"hover",t,""]:[t,""],d=l.getOptionScopes(this.getDataset(),h),u=Object.keys(ue.elements[t]),f=l.resolveNamedOptions(d,u,(()=>this.getContext(i,s,e)),c);return f.$shared&&(f.$shared=r,n[o]=Object.freeze(Hs(f,r))),f}_resolveAnimations(t,e,i){const s=this.chart,n=this._cachedDataOpts,o=`animation-${e}`,a=n[o];if(a)return a;let r;if(!1!==s.options.animation){const s=this.chart.config,n=s.datasetAnimationScopeKeys(this._type,e),o=s.getOptionScopes(this.getDataset(),n);r=s.createResolver(o,this.getContext(t,i,e))}const l=new Ts(s,r&&r.animations);return r&&r._cacheable&&(n[o]=Object.freeze(l)),l}getSharedOptions(t){if(t.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},t))}includeOptions(t,e){return!e||Ns(t)||this.chart._animationsDisabled}_getSharedOptions(t,e){const i=this.resolveDataElementOptions(t,e),s=this._sharedOptions,n=this.getSharedOptions(i),o=this.includeOptions(e,n)||n!==s;return this.updateSharedOptions(n,e,i),{sharedOptions:n,includeOptions:o}}updateElement(t,e,i,s){Ns(s)?Object.assign(t,i):this._resolveAnimations(e,s).update(t,i)}updateSharedOptions(t,e,i){t&&!Ns(e)&&this._resolveAnimations(void 0,e).update(t,i)}_setStyle(t,e,i,s){t.active=s;const n=this.getStyle(e,s);this._resolveAnimations(e,i,s).update(t,{options:!s&&this.getSharedOptions(n)||n})}removeHoverStyle(t,e,i){this._setStyle(t,i,"active",!1)}setHoverStyle(t,e,i){this._setStyle(t,i,"active",!0)}_removeDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!1)}_setDatasetHoverStyle(){const t=this._cachedMeta.dataset;t&&this._setStyle(t,void 0,"active",!0)}_resyncElements(t){const e=this._data,i=this._cachedMeta.data;for(const[t,e,i]of this._syncList)this[t](e,i);this._syncList=[];const s=i.length,n=e.length,o=Math.min(n,s);o&&this.parse(0,o),n>s?this._insertElements(s,n-s,t):n<s&&this._removeElements(n,s-n)}_insertElements(t,e,i=!0){const s=this._cachedMeta,n=s.data,o=t+e;let a;const r=t=>{for(t.length+=e,a=t.length-1;a>=o;a--)t[a]=t[a-e]};for(r(n),a=t;a<o;++a)n[a]=new this.dataElementType;this._parsing&&r(s._parsed),this.parse(t,e),i&&this.updateElements(n,t,e,"reset")}updateElements(t,e,i,s){}_removeElements(t,e){const i=this._cachedMeta;if(this._parsing){const s=i._parsed.splice(t,e);i._stacked&&Ws(i,s)}i.data.splice(t,e)}_sync(t){if(this._parsing)this._syncList.push(t);else{const[e,i,s]=t;this[e](i,s)}this.chart._dataChanges.push([this.index,...t])}_onDataPush(){const t=arguments.length;this._sync(["_insertElements",this.getDataset().data.length-t,t])}_onDataPop(){this._sync(["_removeElements",this._cachedMeta.data.length-1,1])}_onDataShift(){this._sync(["_removeElements",0,1])}_onDataSplice(t,e){e&&this._sync(["_removeElements",t,e]);const i=arguments.length-2;i&&this._sync(["_insertElements",t,i])}_onDataUnshift(){this._sync(["_insertElements",0,arguments.length])}}class $s{static defaults={};static defaultRoutes=void 0;x;y;active=!1;options;$animations;tooltipPosition(t){const{x:e,y:i}=this.getProps(["x","y"],t);return{x:e,y:i}}hasValue(){return N(this.x)&&N(this.y)}getProps(t,e){const i=this.$animations;if(!e||!i)return this;const s={};return t.forEach((t=>{s[t]=i[t]&&i[t].active()?i[t]._to:this[t]})),s}}function Ys(t,e){const i=t.options.ticks,n=function(t){const e=t.options.offset,i=t._tickSize(),s=t._length/i+(e?0:1),n=t._maxLength/i;return Math.floor(Math.min(s,n))}(t),o=Math.min(i.maxTicksLimit||n,n),a=i.major.enabled?function(t){const e=[];let i,s;for(i=0,s=t.length;i<s;i++)t[i].major&&e.push(i);return e}(e):[],r=a.length,l=a[0],h=a[r-1],c=[];if(r>o)return function(t,e,i,s){let n,o=0,a=i[0];for(s=Math.ceil(s),n=0;n<t.length;n++)n===a&&(e.push(t[n]),o++,a=i[o*s])}(e,c,a,r/o),c;const d=function(t,e,i){const s=function(t){const e=t.length;let i,s;if(e<2)return!1;for(s=t[0],i=1;i<e;++i)if(t[i]-t[i-1]!==s)return!1;return s}(t),n=e.length/i;if(!s)return Math.max(n,1);const o=W(s);for(let t=0,e=o.length-1;t<e;t++){const e=o[t];if(e>n)return e}return Math.max(n,1)}(a,e,o);if(r>0){let t,i;const n=r>1?Math.round((h-l)/(r-1)):null;for(Us(e,c,d,s(n)?0:l-n,l),t=0,i=r-1;t<i;t++)Us(e,c,d,a[t],a[t+1]);return Us(e,c,d,h,s(n)?e.length:h+n),c}return Us(e,c,d),c}function Us(t,e,i,s,n){const o=l(s,0),a=Math.min(l(n,t.length),t.length);let r,h,c,d=0;for(i=Math.ceil(i),n&&(r=n-s,i=r/Math.floor(r/i)),c=o;c<0;)d++,c=Math.round(o+d*i);for(h=Math.max(o,0);h<a;h++)h===c&&(e.push(t[h]),d++,c=Math.round(o+d*i))}const Xs=(t,e,i)=>"top"===e||"left"===e?t[e]+i:t[e]-i,qs=(t,e)=>Math.min(e||t,t);function Ks(t,e){const i=[],s=t.length/e,n=t.length;let o=0;for(;o<n;o+=s)i.push(t[Math.floor(o)]);return i}function Gs(t,e,i){const s=t.ticks.length,n=Math.min(e,s-1),o=t._startPixel,a=t._endPixel,r=1e-6;let l,h=t.getPixelForTick(n);if(!(i&&(l=1===s?Math.max(h-o,a-h):0===e?(t.getPixelForTick(1)-h)/2:(h-t.getPixelForTick(n-1))/2,h+=n<e?l:-l,h<o-r||h>a+r)))return h}function Js(t){return t.drawTicks?t.tickLength:0}function Zs(t,e){if(!t.display)return 0;const i=Si(t.font,e),s=ki(t.padding);return(n(t.text)?t.text.length:1)*i.lineHeight+s.height}function Qs(t,e,i){let s=ut(t);return(i&&"right"!==e||!i&&"right"===e)&&(s=(t=>"left"===t?"right":"right"===t?"left":t)(s)),s}class tn extends $s{constructor(t){super(),this.id=t.id,this.type=t.type,this.options=void 0,this.ctx=t.ctx,this.chart=t.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(t){this.options=t.setContext(this.getContext()),this.axis=t.axis,this._userMin=this.parse(t.min),this._userMax=this.parse(t.max),this._suggestedMin=this.parse(t.suggestedMin),this._suggestedMax=this.parse(t.suggestedMax)}parse(t,e){return t}getUserBounds(){let{_userMin:t,_userMax:e,_suggestedMin:i,_suggestedMax:s}=this;return t=r(t,Number.POSITIVE_INFINITY),e=r(e,Number.NEGATIVE_INFINITY),i=r(i,Number.POSITIVE_INFINITY),s=r(s,Number.NEGATIVE_INFINITY),{min:r(t,i),max:r(e,s),minDefined:a(t),maxDefined:a(e)}}getMinMax(t){let e,{min:i,max:s,minDefined:n,maxDefined:o}=this.getUserBounds();if(n&&o)return{min:i,max:s};const a=this.getMatchingVisibleMetas();for(let r=0,l=a.length;r<l;++r)e=a[r].controller.getMinMax(this,t),n||(i=Math.min(i,e.min)),o||(s=Math.max(s,e.max));return i=o&&i>s?s:i,s=n&&i>s?i:s,{min:r(i,r(s,i)),max:r(s,r(i,s))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]}getLabelItems(t=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(t))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){d(this.options.beforeUpdate,[this])}update(t,e,i){const{beginAtZero:s,grace:n,ticks:o}=this.options,a=o.sampleSize;this.beforeUpdate(),this.maxWidth=t,this.maxHeight=e,this._margins=i=Object.assign({left:0,right:0,top:0,bottom:0},i),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+i.left+i.right:this.height+i.top+i.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=Di(this,n,s),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const r=a<this.ticks.length;this._convertTicksToLabels(r?Ks(this.ticks,a):this.ticks),this.configure(),this.beforeCalculateLabelRotation(),this.calculateLabelRotation(),this.afterCalculateLabelRotation(),o.display&&(o.autoSkip||"auto"===o.source)&&(this.ticks=Ys(this,this.ticks),this._labelSizes=null,this.afterAutoSkip()),r&&this._convertTicksToLabels(this.ticks),this.beforeFit(),this.fit(),this.afterFit(),this.afterUpdate()}configure(){let t,e,i=this.options.reverse;this.isHorizontal()?(t=this.left,e=this.right):(t=this.top,e=this.bottom,i=!i),this._startPixel=t,this._endPixel=e,this._reversePixels=i,this._length=e-t,this._alignToPixels=this.options.alignToPixels}afterUpdate(){d(this.options.afterUpdate,[this])}beforeSetDimensions(){d(this.options.beforeSetDimensions,[this])}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=0,this.right=this.width):(this.height=this.maxHeight,this.top=0,this.bottom=this.height),this.paddingLeft=0,this.paddingTop=0,this.paddingRight=0,this.paddingBottom=0}afterSetDimensions(){d(this.options.afterSetDimensions,[this])}_callHooks(t){this.chart.notifyPlugins(t,this.getContext()),d(this.options[t],[this])}beforeDataLimits(){this._callHooks("beforeDataLimits")}determineDataLimits(){}afterDataLimits(){this._callHooks("afterDataLimits")}beforeBuildTicks(){this._callHooks("beforeBuildTicks")}buildTicks(){return[]}afterBuildTicks(){this._callHooks("afterBuildTicks")}beforeTickToLabelConversion(){d(this.options.beforeTickToLabelConversion,[this])}generateTickLabels(t){const e=this.options.ticks;let i,s,n;for(i=0,s=t.length;i<s;i++)n=t[i],n.label=d(e.callback,[n.value,i,t],this)}afterTickToLabelConversion(){d(this.options.afterTickToLabelConversion,[this])}beforeCalculateLabelRotation(){d(this.options.beforeCalculateLabelRotation,[this])}calculateLabelRotation(){const t=this.options,e=t.ticks,i=qs(this.ticks.length,t.ticks.maxTicksLimit),s=e.minRotation||0,n=e.maxRotation;let o,a,r,l=s;if(!this._isVisible()||!e.display||s>=n||i<=1||!this.isHorizontal())return void(this.labelRotation=s);const h=this._getLabelSizes(),c=h.widest.width,d=h.highest.height,u=Z(this.chart.width-c,0,this.maxWidth);o=t.offset?this.maxWidth/i:u/(i-1),c+6>o&&(o=u/(i-(t.offset?.5:1)),a=this.maxHeight-Js(t.grid)-e.padding-Zs(t.title,this.chart.options.font),r=Math.sqrt(c*c+d*d),l=Y(Math.min(Math.asin(Z((h.highest.height+6)/o,-1,1)),Math.asin(Z(a/r,-1,1))-Math.asin(Z(d/r,-1,1)))),l=Math.max(s,Math.min(n,l))),this.labelRotation=l}afterCalculateLabelRotation(){d(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){d(this.options.beforeFit,[this])}fit(){const t={width:0,height:0},{chart:e,options:{ticks:i,title:s,grid:n}}=this,o=this._isVisible(),a=this.isHorizontal();if(o){const o=Zs(s,e.options.font);if(a?(t.width=this.maxWidth,t.height=Js(n)+o):(t.height=this.maxHeight,t.width=Js(n)+o),i.display&&this.ticks.length){const{first:e,last:s,widest:n,highest:o}=this._getLabelSizes(),r=2*i.padding,l=$(this.labelRotation),h=Math.cos(l),c=Math.sin(l);if(a){const e=i.mirror?0:c*n.width+h*o.height;t.height=Math.min(this.maxHeight,t.height+e+r)}else{const e=i.mirror?0:h*n.width+c*o.height;t.width=Math.min(this.maxWidth,t.width+e+r)}this._calculatePadding(e,s,c,h)}}this._handleMargins(),a?(this.width=this._length=e.width-this._margins.left-this._margins.right,this.height=t.height):(this.width=t.width,this.height=this._length=e.height-this._margins.top-this._margins.bottom)}_calculatePadding(t,e,i,s){const{ticks:{align:n,padding:o},position:a}=this.options,r=0!==this.labelRotation,l="top"!==a&&"x"===this.axis;if(this.isHorizontal()){const a=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let c=0,d=0;r?l?(c=s*t.width,d=i*e.height):(c=i*t.height,d=s*e.width):"start"===n?d=e.width:"end"===n?c=t.width:"inner"!==n&&(c=t.width/2,d=e.width/2),this.paddingLeft=Math.max((c-a+o)*this.width/(this.width-a),0),this.paddingRight=Math.max((d-h+o)*this.width/(this.width-h),0)}else{let i=e.height/2,s=t.height/2;"start"===n?(i=0,s=t.height):"end"===n&&(i=e.height,s=0),this.paddingTop=i+o,this.paddingBottom=s+o}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){d(this.options.afterFit,[this])}isHorizontal(){const{axis:t,position:e}=this.options;return"top"===e||"bottom"===e||"x"===t}isFullSize(){return this.options.fullSize}_convertTicksToLabels(t){let e,i;for(this.beforeTickToLabelConversion(),this.generateTickLabels(t),e=0,i=t.length;e<i;e++)s(t[e].label)&&(t.splice(e,1),i--,e--);this.afterTickToLabelConversion()}_getLabelSizes(){let t=this._labelSizes;if(!t){const e=this.options.ticks.sampleSize;let i=this.ticks;e<i.length&&(i=Ks(i,e)),this._labelSizes=t=this._computeLabelSizes(i,i.length,this.options.ticks.maxTicksLimit)}return t}_computeLabelSizes(t,e,i){const{ctx:o,_longestTextCache:a}=this,r=[],l=[],h=Math.floor(e/qs(e,i));let c,d,f,g,p,m,x,b,_,y,v,M=0,w=0;for(c=0;c<e;c+=h){if(g=t[c].label,p=this._resolveTickFontOptions(c),o.font=m=p.string,x=a[m]=a[m]||{data:{},gc:[]},b=p.lineHeight,_=y=0,s(g)||n(g)){if(n(g))for(d=0,f=g.length;d<f;++d)v=g[d],s(v)||n(v)||(_=Ce(o,x.data,x.gc,_,v),y+=b)}else _=Ce(o,x.data,x.gc,_,g),y=b;r.push(_),l.push(y),M=Math.max(_,M),w=Math.max(y,w)}!function(t,e){u(t,(t=>{const i=t.gc,s=i.length/2;let n;if(s>e){for(n=0;n<s;++n)delete t.data[i[n]];i.splice(0,s)}}))}(a,e);const k=r.indexOf(M),S=l.indexOf(w),P=t=>({width:r[t]||0,height:l[t]||0});return{first:P(0),last:P(e-1),widest:P(k),highest:P(S),widths:r,heights:l}}getLabelForValue(t){return t}getPixelForValue(t,e){return NaN}getValueForPixel(t){}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getPixelForDecimal(t){this._reversePixels&&(t=1-t);const e=this._startPixel+t*this._length;return Q(this._alignToPixels?Ae(this.chart,e,0):e)}getDecimalForPixel(t){const e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:t,max:e}=this;return t<0&&e<0?e:t>0&&e>0?t:0}getContext(t){const e=this.ticks||[];if(t>=0&&t<e.length){const i=e[t];return i.$context||(i.$context=function(t,e,i){return Ci(t,{tick:i,index:e,type:"tick"})}(this.getContext(),t,i))}return this.$context||(this.$context=Ci(this.chart.getContext(),{scale:this,type:"scale"}))}_tickSize(){const t=this.options.ticks,e=$(this.labelRotation),i=Math.abs(Math.cos(e)),s=Math.abs(Math.sin(e)),n=this._getLabelSizes(),o=t.autoSkipPadding||0,a=n?n.widest.width+o:0,r=n?n.highest.height+o:0;return this.isHorizontal()?r*i>a*s?a/i:r/s:r*s<a*i?r/i:a/s}_isVisible(){const t=this.options.display;return"auto"!==t?!!t:this.getMatchingVisibleMetas().length>0}_computeGridLineItems(t){const e=this.axis,i=this.chart,s=this.options,{grid:n,position:a,border:r}=s,h=n.offset,c=this.isHorizontal(),d=this.ticks.length+(h?1:0),u=Js(n),f=[],g=r.setContext(this.getContext()),p=g.display?g.width:0,m=p/2,x=function(t){return Ae(i,t,p)};let b,_,y,v,M,w,k,S,P,D,C,O;if("top"===a)b=x(this.bottom),w=this.bottom-u,S=b-m,D=x(t.top)+m,O=t.bottom;else if("bottom"===a)b=x(this.top),D=t.top,O=x(t.bottom)-m,w=b+m,S=this.top+u;else if("left"===a)b=x(this.right),M=this.right-u,k=b-m,P=x(t.left)+m,C=t.right;else if("right"===a)b=x(this.left),P=t.left,C=x(t.right)-m,M=b+m,k=this.left+u;else if("x"===e){if("center"===a)b=x((t.top+t.bottom)/2+.5);else if(o(a)){const t=Object.keys(a)[0],e=a[t];b=x(this.chart.scales[t].getPixelForValue(e))}D=t.top,O=t.bottom,w=b+m,S=w+u}else if("y"===e){if("center"===a)b=x((t.left+t.right)/2);else if(o(a)){const t=Object.keys(a)[0],e=a[t];b=x(this.chart.scales[t].getPixelForValue(e))}M=b-m,k=M-u,P=t.left,C=t.right}const A=l(s.ticks.maxTicksLimit,d),T=Math.max(1,Math.ceil(d/A));for(_=0;_<d;_+=T){const t=this.getContext(_),e=n.setContext(t),s=r.setContext(t),o=e.lineWidth,a=e.color,l=s.dash||[],d=s.dashOffset,u=e.tickWidth,g=e.tickColor,p=e.tickBorderDash||[],m=e.tickBorderDashOffset;y=Gs(this,_,h),void 0!==y&&(v=Ae(i,y,o),c?M=k=P=C=v:w=S=D=O=v,f.push({tx1:M,ty1:w,tx2:k,ty2:S,x1:P,y1:D,x2:C,y2:O,width:o,color:a,borderDash:l,borderDashOffset:d,tickWidth:u,tickColor:g,tickBorderDash:p,tickBorderDashOffset:m}))}return this._ticksLength=d,this._borderValue=b,f}_computeLabelItems(t){const e=this.axis,i=this.options,{position:s,ticks:a}=i,r=this.isHorizontal(),l=this.ticks,{align:h,crossAlign:c,padding:d,mirror:u}=a,f=Js(i.grid),g=f+d,p=u?-d:g,m=-$(this.labelRotation),x=[];let b,_,y,v,M,w,k,S,P,D,C,O,A="middle";if("top"===s)w=this.bottom-p,k=this._getXAxisLabelAlignment();else if("bottom"===s)w=this.top+p,k=this._getXAxisLabelAlignment();else if("left"===s){const t=this._getYAxisLabelAlignment(f);k=t.textAlign,M=t.x}else if("right"===s){const t=this._getYAxisLabelAlignment(f);k=t.textAlign,M=t.x}else if("x"===e){if("center"===s)w=(t.top+t.bottom)/2+g;else if(o(s)){const t=Object.keys(s)[0],e=s[t];w=this.chart.scales[t].getPixelForValue(e)+g}k=this._getXAxisLabelAlignment()}else if("y"===e){if("center"===s)M=(t.left+t.right)/2-g;else if(o(s)){const t=Object.keys(s)[0],e=s[t];M=this.chart.scales[t].getPixelForValue(e)}k=this._getYAxisLabelAlignment(f).textAlign}"y"===e&&("start"===h?A="top":"end"===h&&(A="bottom"));const T=this._getLabelSizes();for(b=0,_=l.length;b<_;++b){y=l[b],v=y.label;const t=a.setContext(this.getContext(b));S=this.getPixelForTick(b)+a.labelOffset,P=this._resolveTickFontOptions(b),D=P.lineHeight,C=n(v)?v.length:1;const e=C/2,i=t.color,o=t.textStrokeColor,h=t.textStrokeWidth;let d,f=k;if(r?(M=S,"inner"===k&&(f=b===_-1?this.options.reverse?"left":"right":0===b?this.options.reverse?"right":"left":"center"),O="top"===s?"near"===c||0!==m?-C*D+D/2:"center"===c?-T.highest.height/2-e*D+D:-T.highest.height+D/2:"near"===c||0!==m?D/2:"center"===c?T.highest.height/2-e*D:T.highest.height-C*D,u&&(O*=-1),0===m||t.showLabelBackdrop||(M+=D/2*Math.sin(m))):(w=S,O=(1-C)*D/2),t.showLabelBackdrop){const e=ki(t.backdropPadding),i=T.heights[b],s=T.widths[b];let n=O-e.top,o=0-e.left;switch(A){case"middle":n-=i/2;break;case"bottom":n-=i}switch(k){case"center":o-=s/2;break;case"right":o-=s;break;case"inner":b===_-1?o-=s:b>0&&(o-=s/2)}d={left:o,top:n,width:s+e.width,height:i+e.height,color:t.backdropColor}}x.push({label:v,font:P,textOffset:O,options:{rotation:m,color:i,strokeColor:o,strokeWidth:h,textAlign:f,textBaseline:A,translation:[M,w],backdrop:d}})}return x}_getXAxisLabelAlignment(){const{position:t,ticks:e}=this.options;if(-$(this.labelRotation))return"top"===t?"left":"right";let i="center";return"start"===e.align?i="left":"end"===e.align?i="right":"inner"===e.align&&(i="inner"),i}_getYAxisLabelAlignment(t){const{position:e,ticks:{crossAlign:i,mirror:s,padding:n}}=this.options,o=t+n,a=this._getLabelSizes().widest.width;let r,l;return"left"===e?s?(l=this.right+n,"near"===i?r="left":"center"===i?(r="center",l+=a/2):(r="right",l+=a)):(l=this.right-o,"near"===i?r="right":"center"===i?(r="center",l-=a/2):(r="left",l=this.left)):"right"===e?s?(l=this.left+n,"near"===i?r="right":"center"===i?(r="center",l-=a/2):(r="left",l-=a)):(l=this.left+o,"near"===i?r="left":"center"===i?(r="center",l+=a/2):(r="right",l=this.right)):r="right",{textAlign:r,x:l}}_computeLabelArea(){if(this.options.ticks.mirror)return;const t=this.chart,e=this.options.position;return"left"===e||"right"===e?{top:0,left:this.left,bottom:t.height,right:this.right}:"top"===e||"bottom"===e?{top:this.top,left:0,bottom:this.bottom,right:t.width}:void 0}drawBackground(){const{ctx:t,options:{backgroundColor:e},left:i,top:s,width:n,height:o}=this;e&&(t.save(),t.fillStyle=e,t.fillRect(i,s,n,o),t.restore())}getLineWidthForValue(t){const e=this.options.grid;if(!this._isVisible()||!e.display)return 0;const i=this.ticks.findIndex((e=>e.value===t));if(i>=0){return e.setContext(this.getContext(i)).lineWidth}return 0}drawGrid(t){const e=this.options.grid,i=this.ctx,s=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(t));let n,o;const a=(t,e,s)=>{s.width&&s.color&&(i.save(),i.lineWidth=s.width,i.strokeStyle=s.color,i.setLineDash(s.borderDash||[]),i.lineDashOffset=s.borderDashOffset,i.beginPath(),i.moveTo(t.x,t.y),i.lineTo(e.x,e.y),i.stroke(),i.restore())};if(e.display)for(n=0,o=s.length;n<o;++n){const t=s[n];e.drawOnChartArea&&a({x:t.x1,y:t.y1},{x:t.x2,y:t.y2},t),e.drawTicks&&a({x:t.tx1,y:t.ty1},{x:t.tx2,y:t.ty2},{color:t.tickColor,width:t.tickWidth,borderDash:t.tickBorderDash,borderDashOffset:t.tickBorderDashOffset})}}drawBorder(){const{chart:t,ctx:e,options:{border:i,grid:s}}=this,n=i.setContext(this.getContext()),o=i.display?n.width:0;if(!o)return;const a=s.setContext(this.getContext(0)).lineWidth,r=this._borderValue;let l,h,c,d;this.isHorizontal()?(l=Ae(t,this.left,o)-o/2,h=Ae(t,this.right,a)+a/2,c=d=r):(c=Ae(t,this.top,o)-o/2,d=Ae(t,this.bottom,a)+a/2,l=h=r),e.save(),e.lineWidth=n.width,e.strokeStyle=n.color,e.beginPath(),e.moveTo(l,c),e.lineTo(h,d),e.stroke(),e.restore()}drawLabels(t){if(!this.options.ticks.display)return;const e=this.ctx,i=this._computeLabelArea();i&&Ie(e,i);const s=this.getLabelItems(t);for(const t of s){const i=t.options,s=t.font;Ne(e,t.label,0,t.textOffset,s,i)}i&&ze(e)}drawTitle(){const{ctx:t,options:{position:e,title:i,reverse:s}}=this;if(!i.display)return;const a=Si(i.font),r=ki(i.padding),l=i.align;let h=a.lineHeight/2;"bottom"===e||"center"===e||o(e)?(h+=r.bottom,n(i.text)&&(h+=a.lineHeight*(i.text.length-1))):h+=r.top;const{titleX:c,titleY:d,maxWidth:u,rotation:f}=function(t,e,i,s){const{top:n,left:a,bottom:r,right:l,chart:h}=t,{chartArea:c,scales:d}=h;let u,f,g,p=0;const m=r-n,x=l-a;if(t.isHorizontal()){if(f=ft(s,a,l),o(i)){const t=Object.keys(i)[0],s=i[t];g=d[t].getPixelForValue(s)+m-e}else g="center"===i?(c.bottom+c.top)/2+m-e:Xs(t,i,e);u=l-a}else{if(o(i)){const t=Object.keys(i)[0],s=i[t];f=d[t].getPixelForValue(s)-x+e}else f="center"===i?(c.left+c.right)/2-x+e:Xs(t,i,e);g=ft(s,r,n),p="left"===i?-E:E}return{titleX:f,titleY:g,maxWidth:u,rotation:p}}(this,h,e,l);Ne(t,i.text,0,0,a,{color:i.color,maxWidth:u,rotation:f,textAlign:Qs(l,e,s),textBaseline:"middle",translation:[c,d]})}draw(t){this._isVisible()&&(this.drawBackground(),this.drawGrid(t),this.drawBorder(),this.drawTitle(),this.drawLabels(t))}_layers(){const t=this.options,e=t.ticks&&t.ticks.z||0,i=l(t.grid&&t.grid.z,-1),s=l(t.border&&t.border.z,0);return this._isVisible()&&this.draw===tn.prototype.draw?[{z:i,draw:t=>{this.drawBackground(),this.drawGrid(t),this.drawTitle()}},{z:s,draw:()=>{this.drawBorder()}},{z:e,draw:t=>{this.drawLabels(t)}}]:[{z:e,draw:t=>{this.draw(t)}}]}getMatchingVisibleMetas(t){const e=this.chart.getSortedVisibleDatasetMetas(),i=this.axis+"AxisID",s=[];let n,o;for(n=0,o=e.length;n<o;++n){const o=e[n];o[i]!==this.id||t&&o.type!==t||s.push(o)}return s}_resolveTickFontOptions(t){return Si(this.options.ticks.setContext(this.getContext(t)).font)}_maxDigits(){const t=this._resolveTickFontOptions(0).lineHeight;return(this.isHorizontal()?this.width:this.height)/t}}class en{constructor(t,e,i){this.type=t,this.scope=e,this.override=i,this.items=Object.create(null)}isForType(t){return Object.prototype.isPrototypeOf.call(this.type.prototype,t.prototype)}register(t){const e=Object.getPrototypeOf(t);let i;(function(t){return"id"in t&&"defaults"in t})(e)&&(i=this.register(e));const s=this.items,n=t.id,o=this.scope+"."+n;if(!n)throw new Error("class does not have id: "+t);return n in s||(s[n]=t,function(t,e,i){const s=x(Object.create(null),[i?ue.get(i):{},ue.get(e),t.defaults]);ue.set(e,s),t.defaultRoutes&&function(t,e){Object.keys(e).forEach((i=>{const s=i.split("."),n=s.pop(),o=[t].concat(s).join("."),a=e[i].split("."),r=a.pop(),l=a.join(".");ue.route(o,n,l,r)}))}(e,t.defaultRoutes);t.descriptors&&ue.describe(e,t.descriptors)}(t,o,i),this.override&&ue.override(t.id,t.overrides)),o}get(t){return this.items[t]}unregister(t){const e=this.items,i=t.id,s=this.scope;i in e&&delete e[i],s&&i in ue[s]&&(delete ue[s][i],this.override&&delete re[i])}}class sn{constructor(){this.controllers=new en(js,"datasets",!0),this.elements=new en($s,"elements"),this.plugins=new en(Object,"plugins"),this.scales=new en(tn,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...t){this._each("register",t)}remove(...t){this._each("unregister",t)}addControllers(...t){this._each("register",t,this.controllers)}addElements(...t){this._each("register",t,this.elements)}addPlugins(...t){this._each("register",t,this.plugins)}addScales(...t){this._each("register",t,this.scales)}getController(t){return this._get(t,this.controllers,"controller")}getElement(t){return this._get(t,this.elements,"element")}getPlugin(t){return this._get(t,this.plugins,"plugin")}getScale(t){return this._get(t,this.scales,"scale")}removeControllers(...t){this._each("unregister",t,this.controllers)}removeElements(...t){this._each("unregister",t,this.elements)}removePlugins(...t){this._each("unregister",t,this.plugins)}removeScales(...t){this._each("unregister",t,this.scales)}_each(t,e,i){[...e].forEach((e=>{const s=i||this._getRegistryForType(e);i||s.isForType(e)||s===this.plugins&&e.id?this._exec(t,s,e):u(e,(e=>{const s=i||this._getRegistryForType(e);this._exec(t,s,e)}))}))}_exec(t,e,i){const s=w(t);d(i["before"+s],[],i),e[t](i),d(i["after"+s],[],i)}_getRegistryForType(t){for(let e=0;e<this._typedRegistries.length;e++){const i=this._typedRegistries[e];if(i.isForType(t))return i}return this.plugins}_get(t,e,i){const s=e.get(t);if(void 0===s)throw new Error('"'+t+'" is not a registered '+i+".");return s}}var nn=new sn;class on{constructor(){this._init=void 0}notify(t,e,i,s){if("beforeInit"===e&&(this._init=this._createDescriptors(t,!0),this._notify(this._init,t,"install")),void 0===this._init)return;const n=s?this._descriptors(t).filter(s):this._descriptors(t),o=this._notify(n,t,e,i);return"afterDestroy"===e&&(this._notify(n,t,"stop"),this._notify(this._init,t,"uninstall"),this._init=void 0),o}_notify(t,e,i,s){s=s||{};for(const n of t){const t=n.plugin;if(!1===d(t[i],[e,s,n.options],t)&&s.cancelable)return!1}return!0}invalidate(){s(this._cache)||(this._oldCache=this._cache,this._cache=void 0)}_descriptors(t){if(this._cache)return this._cache;const e=this._cache=this._createDescriptors(t);return this._notifyStateChanges(t),e}_createDescriptors(t,e){const i=t&&t.config,s=l(i.options&&i.options.plugins,{}),n=function(t){const e={},i=[],s=Object.keys(nn.plugins.items);for(let t=0;t<s.length;t++)i.push(nn.getPlugin(s[t]));const n=t.plugins||[];for(let t=0;t<n.length;t++){const s=n[t];-1===i.indexOf(s)&&(i.push(s),e[s.id]=!0)}return{plugins:i,localIds:e}}(i);return!1!==s||e?function(t,{plugins:e,localIds:i},s,n){const o=[],a=t.getContext();for(const r of e){const e=r.id,l=an(s[e],n);null!==l&&o.push({plugin:r,options:rn(t.config,{plugin:r,local:i[e]},l,a)})}return o}(t,n,s,e):[]}_notifyStateChanges(t){const e=this._oldCache||[],i=this._cache,s=(t,e)=>t.filter((t=>!e.some((e=>t.plugin.id===e.plugin.id))));this._notify(s(e,i),t,"stop"),this._notify(s(i,e),t,"start")}}function an(t,e){return e||!1!==t?!0===t?{}:t:null}function rn(t,{plugin:e,local:i},s,n){const o=t.pluginScopeKeys(e),a=t.getOptionScopes(s,o);return i&&e.defaults&&a.push(e.defaults),t.createResolver(a,n,[""],{scriptable:!1,indexable:!1,allKeys:!0})}function ln(t,e){const i=ue.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||i.indexAxis||"x"}function hn(t){if("x"===t||"y"===t||"r"===t)return t}function cn(t,...e){if(hn(t))return t;for(const s of e){const e=s.axis||("top"===(i=s.position)||"bottom"===i?"x":"left"===i||"right"===i?"y":void 0)||t.length>1&&hn(t[0].toLowerCase());if(e)return e}var i;throw new Error(`Cannot determine type of '${t}' axis. Please provide 'axis' or 'position' option.`)}function dn(t,e,i){if(i[e+"AxisID"]===t)return{axis:e}}function un(t,e){const i=re[t.type]||{scales:{}},s=e.scales||{},n=ln(t.type,e),a=Object.create(null);return Object.keys(s).forEach((e=>{const r=s[e];if(!o(r))return console.error(`Invalid scale configuration for scale: ${e}`);if(r._proxy)return console.warn(`Ignoring resolver passed as options for scale: ${e}`);const l=cn(e,r,function(t,e){if(e.data&&e.data.datasets){const i=e.data.datasets.filter((e=>e.xAxisID===t||e.yAxisID===t));if(i.length)return dn(t,"x",i[0])||dn(t,"y",i[0])}return{}}(e,t),ue.scales[r.type]),h=function(t,e){return t===e?"_index_":"_value_"}(l,n),c=i.scales||{};a[e]=b(Object.create(null),[{axis:l},r,c[l],c[h]])})),t.data.datasets.forEach((i=>{const n=i.type||t.type,o=i.indexAxis||ln(n,e),r=(re[n]||{}).scales||{};Object.keys(r).forEach((t=>{const e=function(t,e){let i=t;return"_index_"===t?i=e:"_value_"===t&&(i="x"===e?"y":"x"),i}(t,o),n=i[e+"AxisID"]||e;a[n]=a[n]||Object.create(null),b(a[n],[{axis:e},s[n],r[t]])}))})),Object.keys(a).forEach((t=>{const e=a[t];b(e,[ue.scales[e.type],ue.scale])})),a}function fn(t){const e=t.options||(t.options={});e.plugins=l(e.plugins,{}),e.scales=un(t,e)}function gn(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const pn=new Map,mn=new Set;function xn(t,e){let i=pn.get(t);return i||(i=e(),pn.set(t,i),mn.add(i)),i}const bn=(t,e,i)=>{const s=M(e,i);void 0!==s&&t.add(s)};class _n{constructor(t){this._config=function(t){return(t=t||{}).data=gn(t.data),fn(t),t}(t),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(t){this._config.type=t}get data(){return this._config.data}set data(t){this._config.data=gn(t)}get options(){return this._config.options}set options(t){this._config.options=t}get plugins(){return this._config.plugins}update(){const t=this._config;this.clearCache(),fn(t)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(t){return xn(t,(()=>[[`datasets.${t}`,""]]))}datasetAnimationScopeKeys(t,e){return xn(`${t}.transition.${e}`,(()=>[[`datasets.${t}.transitions.${e}`,`transitions.${e}`],[`datasets.${t}`,""]]))}datasetElementScopeKeys(t,e){return xn(`${t}-${e}`,(()=>[[`datasets.${t}.elements.${e}`,`datasets.${t}`,`elements.${e}`,""]]))}pluginScopeKeys(t){const e=t.id;return xn(`${this.type}-plugin-${e}`,(()=>[[`plugins.${e}`,...t.additionalOptionScopes||[]]]))}_cachedScopes(t,e){const i=this._scopeCache;let s=i.get(t);return s&&!e||(s=new Map,i.set(t,s)),s}getOptionScopes(t,e,i){const{options:s,type:n}=this,o=this._cachedScopes(t,i),a=o.get(e);if(a)return a;const r=new Set;e.forEach((e=>{t&&(r.add(t),e.forEach((e=>bn(r,t,e)))),e.forEach((t=>bn(r,s,t))),e.forEach((t=>bn(r,re[n]||{},t))),e.forEach((t=>bn(r,ue,t))),e.forEach((t=>bn(r,le,t)))}));const l=Array.from(r);return 0===l.length&&l.push(Object.create(null)),mn.has(e)&&o.set(e,l),l}chartOptionScopes(){const{options:t,type:e}=this;return[t,re[e]||{},ue.datasets[e]||{},{type:e},ue,le]}resolveNamedOptions(t,e,i,s=[""]){const o={$shared:!0},{resolver:a,subPrefixes:r}=yn(this._resolverCache,t,s);let l=a;if(function(t,e){const{isScriptable:i,isIndexable:s}=Ye(t);for(const o of e){const e=i(o),a=s(o),r=(a||e)&&t[o];if(e&&(S(r)||vn(r))||a&&n(r))return!0}return!1}(a,e)){o.$shared=!1;l=$e(a,i=S(i)?i():i,this.createResolver(t,i,r))}for(const t of e)o[t]=l[t];return o}createResolver(t,e,i=[""],s){const{resolver:n}=yn(this._resolverCache,t,i);return o(e)?$e(n,e,void 0,s):n}}function yn(t,e,i){let s=t.get(e);s||(s=new Map,t.set(e,s));const n=i.join();let o=s.get(n);if(!o){o={resolver:je(e,i),subPrefixes:i.filter((t=>!t.toLowerCase().includes("hover")))},s.set(n,o)}return o}const vn=t=>o(t)&&Object.getOwnPropertyNames(t).some((e=>S(t[e])));const Mn=["top","bottom","left","right","chartArea"];function wn(t,e){return"top"===t||"bottom"===t||-1===Mn.indexOf(t)&&"x"===e}function kn(t,e){return function(i,s){return i[t]===s[t]?i[e]-s[e]:i[t]-s[t]}}function Sn(t){const e=t.chart,i=e.options.animation;e.notifyPlugins("afterRender"),d(i&&i.onComplete,[t],e)}function Pn(t){const e=t.chart,i=e.options.animation;d(i&&i.onProgress,[t],e)}function Dn(t){return fe()&&"string"==typeof t?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const Cn={},On=t=>{const e=Dn(t);return Object.values(Cn).filter((t=>t.canvas===e)).pop()};function An(t,e,i){const s=Object.keys(t);for(const n of s){const s=+n;if(s>=e){const o=t[n];delete t[n],(i>0||s>e)&&(t[s+i]=o)}}}class Tn{static defaults=ue;static instances=Cn;static overrides=re;static registry=nn;static version="4.5.1";static getChart=On;static register(...t){nn.add(...t),Ln()}static unregister(...t){nn.remove(...t),Ln()}constructor(t,e){const s=this.config=new _n(e),n=Dn(t),o=On(n);if(o)throw new Error("Canvas is already in use. Chart with ID '"+o.id+"' must be destroyed before the canvas with ID '"+o.canvas.id+"' can be reused.");const a=s.createResolver(s.chartOptionScopes(),this.getContext());this.platform=new(s.platform||Ps(n)),this.platform.updateConfig(s);const r=this.platform.acquireContext(n,a.aspectRatio),l=r&&r.canvas,h=l&&l.height,c=l&&l.width;this.id=i(),this.ctx=r,this.canvas=l,this.width=c,this.height=h,this._options=a,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new on,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=dt((t=>this.update(t)),a.resizeDelay||0),this._dataChanges=[],Cn[this.id]=this,r&&l?(bt.listen(this,"complete",Sn),bt.listen(this,"progress",Pn),this._initialize(),this.attached&&this.update()):console.error("Failed to create chart: can't acquire context from the given item")}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:i,height:n,_aspectRatio:o}=this;return s(t)?e&&o?o:n?i/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return nn}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():ke(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Te(this.canvas,this.ctx),this}stop(){return bt.stop(this),this}resize(t,e){bt.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const i=this.options,s=this.canvas,n=i.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(s,t,e,n),a=i.devicePixelRatio||this.platform.getDevicePixelRatio(),r=this.width?"resize":"attach";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,ke(this,a,!0)&&(this.notifyPlugins("resize",{size:o}),d(i.onResize,[this,o],this),this.attached&&this._doResize(r)&&this.render())}ensureScalesHaveIDs(){u(this.options.scales||{},((t,e)=>{t.id=e}))}buildOrUpdateScales(){const t=this.options,e=t.scales,i=this.scales,s=Object.keys(i).reduce(((t,e)=>(t[e]=!1,t)),{});let n=[];e&&(n=n.concat(Object.keys(e).map((t=>{const i=e[t],s=cn(t,i),n="r"===s,o="x"===s;return{options:i,dposition:n?"chartArea":o?"bottom":"left",dtype:n?"radialLinear":o?"category":"linear"}})))),u(n,(e=>{const n=e.options,o=n.id,a=cn(o,n),r=l(n.type,e.dtype);void 0!==n.position&&wn(n.position,a)===wn(e.dposition)||(n.position=e.dposition),s[o]=!0;let h=null;if(o in i&&i[o].type===r)h=i[o];else{h=new(nn.getScale(r))({id:o,type:r,ctx:this.ctx,chart:this}),i[h.id]=h}h.init(n,t)})),u(s,((t,e)=>{t||delete i[e]})),u(i,(t=>{ls.configure(this,t,t.options),ls.addBox(this,t)}))}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,i=t.length;if(t.sort(((t,e)=>t.index-e.index)),i>e){for(let t=e;t<i;++t)this._destroyDatasetMeta(t);t.splice(e,i-e)}this._sortedMetasets=t.slice(0).sort(kn("order","index"))}_removeUnreferencedMetasets(){const{_metasets:t,data:{datasets:e}}=this;t.length>e.length&&delete this._stacks,t.forEach(((t,i)=>{0===e.filter((e=>e===t._dataset)).length&&this._destroyDatasetMeta(i)}))}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let i,s;for(this._removeUnreferencedMetasets(),i=0,s=e.length;i<s;i++){const s=e[i];let n=this.getDatasetMeta(i);const o=s.type||this.config.type;if(n.type&&n.type!==o&&(this._destroyDatasetMeta(i),n=this.getDatasetMeta(i)),n.type=o,n.indexAxis=s.indexAxis||ln(o,this.options),n.order=s.order||0,n.index=i,n.label=""+s.label,n.visible=this.isDatasetVisible(i),n.controller)n.controller.updateIndex(i),n.controller.linkScales();else{const e=nn.getController(o),{datasetElementType:s,dataElementType:a}=ue.datasets[o];Object.assign(e,{dataElementType:nn.getElement(a),datasetElementType:s&&nn.getElement(s)}),n.controller=new e(this,i),t.push(n.controller)}}return this._updateMetasets(),t}_resetElements(){u(this.data.datasets,((t,e)=>{this.getDatasetMeta(e).controller.reset()}),this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const i=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),s=this._animationsDisabled=!i.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),!1===this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0}))return;const n=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let o=0;for(let t=0,e=this.data.datasets.length;t<e;t++){const{controller:e}=this.getDatasetMeta(t),i=!s&&-1===n.indexOf(e);e.buildOrUpdateElements(i),o=Math.max(+e.getMaxOverflow(),o)}o=this._minPadding=i.layout.autoPadding?o:0,this._updateLayout(o),s||u(n,(t=>{t.reset()})),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(kn("z","_idx"));const{_active:a,_lastEvent:r}=this;r?this._eventHandler(r,!0):a.length&&this._updateHoverStyles(a,a,!0),this.render()}_updateScales(){u(this.scales,(t=>{ls.removeBox(this,t)})),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),i=new Set(t.events);P(e,i)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:i,start:s,count:n}of e){An(t,s,"_removeElements"===i?-n:n)}}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,i=e=>new Set(t.filter((t=>t[0]===e)).map(((t,e)=>e+","+t.splice(1).join(",")))),s=i(0);for(let t=1;t<e;t++)if(!P(s,i(t)))return;return Array.from(s).map((t=>t.split(","))).map((t=>({method:t[1],start:+t[2],count:+t[3]})))}_updateLayout(t){if(!1===this.notifyPlugins("beforeLayout",{cancelable:!0}))return;ls.update(this,this.width,this.height,t);const e=this.chartArea,i=e.width<=0||e.height<=0;this._layers=[],u(this.boxes,(t=>{i&&"chartArea"===t.position||(t.configure&&t.configure(),this._layers.push(...t._layers()))}),this),this._layers.forEach(((t,e)=>{t._idx=e})),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(!1!==this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})){for(let t=0,e=this.data.datasets.length;t<e;++t)this.getDatasetMeta(t).controller.configure();for(let e=0,i=this.data.datasets.length;e<i;++e)this._updateDataset(e,S(t)?t({datasetIndex:e}):t);this.notifyPlugins("afterDatasetsUpdate",{mode:t})}}_updateDataset(t,e){const i=this.getDatasetMeta(t),s={meta:i,index:t,mode:e,cancelable:!0};!1!==this.notifyPlugins("beforeDatasetUpdate",s)&&(i.controller._update(e),s.cancelable=!1,this.notifyPlugins("afterDatasetUpdate",s))}render(){!1!==this.notifyPlugins("beforeRender",{cancelable:!0})&&(bt.has(this)?this.attached&&!bt.running(this)&&bt.start(this):(this.draw(),Sn({chart:this})))}draw(){let t;if(this._resizeBeforeDraw){const{width:t,height:e}=this._resizeBeforeDraw;this._resizeBeforeDraw=null,this._resize(t,e)}if(this.clear(),this.width<=0||this.height<=0)return;if(!1===this.notifyPlugins("beforeDraw",{cancelable:!0}))return;const e=this._layers;for(t=0;t<e.length&&e[t].z<=0;++t)e[t].draw(this.chartArea);for(this._drawDatasets();t<e.length;++t)e[t].draw(this.chartArea);this.notifyPlugins("afterDraw")}_getSortedDatasetMetas(t){const e=this._sortedMetasets,i=[];let s,n;for(s=0,n=e.length;s<n;++s){const n=e[s];t&&!n.visible||i.push(n)}return i}getSortedVisibleDatasetMetas(){return this._getSortedDatasetMetas(!0)}_drawDatasets(){if(!1===this.notifyPlugins("beforeDatasetsDraw",{cancelable:!0}))return;const t=this.getSortedVisibleDatasetMetas();for(let e=t.length-1;e>=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,i={meta:t,index:t.index,cancelable:!0},s=Ni(this,t);!1!==this.notifyPlugins("beforeDatasetDraw",i)&&(s&&Ie(e,s),t.controller.draw(),s&&ze(e),i.cancelable=!1,this.notifyPlugins("afterDatasetDraw",i))}isPointInArea(t){return Re(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,i,s){const n=Ki.modes[e];return"function"==typeof n?n(this,t,i,s):[]}getDatasetMeta(t){const e=this.data.datasets[t],i=this._metasets;let s=i.filter((t=>t&&t._dataset===e)).pop();return s||(s={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},i.push(s)),s}getContext(){return this.$context||(this.$context=Ci(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const i=this.getDatasetMeta(t);return"boolean"==typeof i.hidden?!i.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,i){const s=i?"show":"hide",n=this.getDatasetMeta(t),o=n.controller._resolveAnimations(void 0,s);k(e)?(n.data[e].hidden=!i,this.update()):(this.setDatasetVisibility(t,i),o.update(n,{visible:i}),this.update((e=>e.datasetIndex===t?s:void 0)))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),bt.remove(this),t=0,e=this.data.datasets.length;t<e;++t)this._destroyDatasetMeta(t)}destroy(){this.notifyPlugins("beforeDestroy");const{canvas:t,ctx:e}=this;this._stop(),this.config.clearCache(),t&&(this.unbindEvents(),Te(t,e),this.platform.releaseContext(e),this.canvas=null,this.ctx=null),delete Cn[this.id],this.notifyPlugins("afterDestroy")}toBase64Image(...t){return this.canvas.toDataURL(...t)}bindEvents(){this.bindUserEvents(),this.options.responsive?this.bindResponsiveEvents():this.attached=!0}bindUserEvents(){const t=this._listeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(t,e,i)=>{t.offsetX=e,t.offsetY=i,this._eventHandler(t)};u(this.options.events,(t=>i(t,s)))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,i=(i,s)=>{e.addEventListener(this,i,s),t[i]=s},s=(i,s)=>{t[i]&&(e.removeEventListener(this,i,s),delete t[i])},n=(t,e)=>{this.canvas&&this.resize(t,e)};let o;const a=()=>{s("attach",a),this.attached=!0,this.resize(),i("resize",n),i("detach",o)};o=()=>{this.attached=!1,s("resize",n),this._stop(),this._resize(0,0),i("attach",a)},e.isAttached(this.canvas)?a():o()}unbindEvents(){u(this._listeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._listeners={},u(this._responsiveListeners,((t,e)=>{this.platform.removeEventListener(this,e,t)})),this._responsiveListeners=void 0}updateHoverStyle(t,e,i){const s=i?"set":"remove";let n,o,a,r;for("dataset"===e&&(n=this.getDatasetMeta(t[0].datasetIndex),n.controller["_"+s+"DatasetHoverStyle"]()),a=0,r=t.length;a<r;++a){o=t[a];const e=o&&this.getDatasetMeta(o.datasetIndex).controller;e&&e[s+"HoverStyle"](o.element,o.datasetIndex,o.index)}}getActiveElements(){return this._active||[]}setActiveElements(t){const e=this._active||[],i=t.map((({datasetIndex:t,index:e})=>{const i=this.getDatasetMeta(t);if(!i)throw new Error("No dataset found at index "+t);return{datasetIndex:t,element:i.data[e],index:e}}));!f(i,e)&&(this._active=i,this._lastEvent=null,this._updateHoverStyles(i,e))}notifyPlugins(t,e,i){return this._plugins.notify(this,t,e,i)}isPluginEnabled(t){return 1===this._plugins._cache.filter((e=>e.plugin.id===t)).length}_updateHoverStyles(t,e,i){const s=this.options.hover,n=(t,e)=>t.filter((t=>!e.some((e=>t.datasetIndex===e.datasetIndex&&t.index===e.index)))),o=n(e,t),a=i?t:n(t,e);o.length&&this.updateHoverStyle(o,s.mode,!1),a.length&&s.mode&&this.updateHoverStyle(a,s.mode,!0)}_eventHandler(t,e){const i={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},s=e=>(e.options.events||this.options.events).includes(t.native.type);if(!1===this.notifyPlugins("beforeEvent",i,s))return;const n=this._handleEvent(t,e,i.inChartArea);return i.cancelable=!1,this.notifyPlugins("afterEvent",i,s),(n||i.changed)&&this.render(),this}_handleEvent(t,e,i){const{_active:s=[],options:n}=this,o=e,a=this._getActiveElements(t,s,i,o),r=D(t),l=function(t,e,i,s){return i&&"mouseout"!==t.type?s?e:t:null}(t,this._lastEvent,i,r);i&&(this._lastEvent=null,d(n.onHover,[t,a,this],this),r&&d(n.onClick,[t,a,this],this));const h=!f(a,s);return(h||e)&&(this._active=a,this._updateHoverStyles(a,s,e)),this._lastEvent=l,h}_getActiveElements(t,e,i,s){if("mouseout"===t.type)return[];if(!i)return e;const n=this.options.hover;return this.getElementsAtEventForMode(t,n.mode,n,s)}}function Ln(){return u(Tn.instances,(t=>t._plugins.invalidate()))}function En(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class Rn{static override(t){Object.assign(Rn.prototype,t)}options;constructor(t){this.options=t||{}}init(){}formats(){return En()}parse(){return En()}format(){return En()}add(){return En()}diff(){return En()}startOf(){return En()}endOf(){return En()}}var In={_date:Rn};function zn(t){const e=t.iScale,i=function(t,e){if(!t._cache.$bar){const i=t.getMatchingVisibleMetas(e);let s=[];for(let e=0,n=i.length;e<n;e++)s=s.concat(i[e].controller.getAllParsedValues(t));t._cache.$bar=lt(s.sort(((t,e)=>t-e)))}return t._cache.$bar}(e,t.type);let s,n,o,a,r=e._length;const l=()=>{32767!==o&&-32768!==o&&(k(a)&&(r=Math.min(r,Math.abs(o-a)||r)),a=o)};for(s=0,n=i.length;s<n;++s)o=e.getPixelForValue(i[s]),l();for(a=void 0,s=0,n=e.ticks.length;s<n;++s)o=e.getPixelForTick(s),l();return r}function Fn(t,e,i,s){return n(t)?function(t,e,i,s){const n=i.parse(t[0],s),o=i.parse(t[1],s),a=Math.min(n,o),r=Math.max(n,o);let l=a,h=r;Math.abs(a)>Math.abs(r)&&(l=r,h=a),e[i.axis]=h,e._custom={barStart:l,barEnd:h,start:n,end:o,min:a,max:r}}(t,e,i,s):e[i.axis]=i.parse(t,s),e}function Vn(t,e,i,s){const n=t.iScale,o=t.vScale,a=n.getLabels(),r=n===o,l=[];let h,c,d,u;for(h=i,c=i+s;h<c;++h)u=e[h],d={},d[n.axis]=r||n.parse(a[h],h),l.push(Fn(u,d,o,h));return l}function Bn(t){return t&&void 0!==t.barStart&&void 0!==t.barEnd}function Wn(t,e,i,s){let n=e.borderSkipped;const o={};if(!n)return void(t.borderSkipped=o);if(!0===n)return void(t.borderSkipped={top:!0,right:!0,bottom:!0,left:!0});const{start:a,end:r,reverse:l,top:h,bottom:c}=function(t){let e,i,s,n,o;return t.horizontal?(e=t.base>t.x,i="left",s="right"):(e=t.base<t.y,i="bottom",s="top"),e?(n="end",o="start"):(n="start",o="end"),{start:i,end:s,reverse:e,top:n,bottom:o}}(t);"middle"===n&&i&&(t.enableBorderRadius=!0,(i._top||0)===s?n=h:(i._bottom||0)===s?n=c:(o[Nn(c,a,r,l)]=!0,n=h)),o[Nn(n,a,r,l)]=!0,t.borderSkipped=o}function Nn(t,e,i,s){var n,o,a;return s?(a=i,t=Hn(t=(n=t)===(o=e)?a:n===a?o:n,i,e)):t=Hn(t,e,i),t}function Hn(t,e,i){return"start"===t?e:"end"===t?i:t}function jn(t,{inflateAmount:e},i){t.inflateAmount="auto"===e?1===i?.33:0:e}class $n extends js{static id="doughnut";static defaults={datasetElementType:!1,dataElementType:"arc",animation:{animateRotate:!0,animateScale:!1},animations:{numbers:{type:"number",properties:["circumference","endAngle","innerRadius","outerRadius","startAngle","x","y","offset","borderWidth","spacing"]}},cutout:"50%",rotation:0,circumference:360,radius:"100%",spacing:0,indexAxis:"r"};static descriptors={_scriptable:t=>"spacing"!==t,_indexable:t=>"spacing"!==t&&!t.startsWith("borderDash")&&!t.startsWith("hoverBorderDash")};static overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data,{labels:{pointStyle:i,textAlign:s,color:n,useBorderRadius:o,borderRadius:a}}=t.legend.options;return e.labels.length&&e.datasets.length?e.labels.map(((e,r)=>{const l=t.getDatasetMeta(0).controller.getStyle(r);return{text:e,fillStyle:l.backgroundColor,fontColor:n,hidden:!t.getDataVisibility(r),lineDash:l.borderDash,lineDashOffset:l.borderDashOffset,lineJoin:l.borderJoinStyle,lineWidth:l.borderWidth,strokeStyle:l.borderColor,textAlign:s,pointStyle:i,borderRadius:o&&(a||l.borderRadius),index:r}})):[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}}};constructor(t,e){super(t,e),this.enableOptionSharing=!0,this.innerRadius=void 0,this.outerRadius=void 0,this.offsetX=void 0,this.offsetY=void 0}linkScales(){}parse(t,e){const i=this.getDataset().data,s=this._cachedMeta;if(!1===this._parsing)s._parsed=i;else{let n,a,r=t=>+i[t];if(o(i[t])){const{key:t="value"}=this._parsing;r=e=>+M(i[e],t)}for(n=t,a=t+e;n<a;++n)s._parsed[n]=r(n)}}_getRotation(){return $(this.options.rotation-90)}_getCircumference(){return $(this.options.circumference)}_getRotationExtents(){let t=O,e=-O;for(let i=0;i<this.chart.data.datasets.length;++i)if(this.chart.isDatasetVisible(i)&&this.chart.getDatasetMeta(i).type===this._type){const s=this.chart.getDatasetMeta(i).controller,n=s._getRotation(),o=s._getCircumference();t=Math.min(t,n),e=Math.max(e,n+o)}return{rotation:t,circumference:e-t}}update(t){const e=this.chart,{chartArea:i}=e,s=this._cachedMeta,n=s.data,o=this.getMaxBorderWidth()+this.getMaxOffset(n)+this.options.spacing,a=Math.max((Math.min(i.width,i.height)-o)/2,0),r=Math.min(h(this.options.cutout,a),1),l=this._getRingWeight(this.index),{circumference:d,rotation:u}=this._getRotationExtents(),{ratioX:f,ratioY:g,offsetX:p,offsetY:m}=function(t,e,i){let s=1,n=1,o=0,a=0;if(e<O){const r=t,l=r+e,h=Math.cos(r),c=Math.sin(r),d=Math.cos(l),u=Math.sin(l),f=(t,e,s)=>J(t,r,l,!0)?1:Math.max(e,e*i,s,s*i),g=(t,e,s)=>J(t,r,l,!0)?-1:Math.min(e,e*i,s,s*i),p=f(0,h,d),m=f(E,c,u),x=g(C,h,d),b=g(C+E,c,u);s=(p-x)/2,n=(m-b)/2,o=-(p+x)/2,a=-(m+b)/2}return{ratioX:s,ratioY:n,offsetX:o,offsetY:a}}(u,d,r),x=(i.width-o)/f,b=(i.height-o)/g,_=Math.max(Math.min(x,b)/2,0),y=c(this.options.radius,_),v=(y-Math.max(y*r,0))/this._getVisibleDatasetWeightTotal();this.offsetX=p*y,this.offsetY=m*y,s.total=this.calculateTotal(),this.outerRadius=y-v*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-v*l,0),this.updateElements(n,0,n.length,t)}_circumference(t,e){const i=this.options,s=this._cachedMeta,n=this._getCircumference();return e&&i.animation.animateRotate||!this.chart.getDataVisibility(t)||null===s._parsed[t]||s.data[t].hidden?0:this.calculateCircumference(s._parsed[t]*n/O)}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.chartArea,r=o.options.animation,l=(a.left+a.right)/2,h=(a.top+a.bottom)/2,c=n&&r.animateScale,d=c?0:this.innerRadius,u=c?0:this.outerRadius,{sharedOptions:f,includeOptions:g}=this._getSharedOptions(e,s);let p,m=this._getRotation();for(p=0;p<e;++p)m+=this._circumference(p,n);for(p=e;p<e+i;++p){const e=this._circumference(p,n),i=t[p],o={x:l+this.offsetX,y:h+this.offsetY,startAngle:m,endAngle:m+e,circumference:e,outerRadius:u,innerRadius:d};g&&(o.options=f||this.resolveDataElementOptions(p,i.active?"active":s)),m+=e,this.updateElement(i,p,o,s)}}calculateTotal(){const t=this._cachedMeta,e=t.data;let i,s=0;for(i=0;i<e.length;i++){const n=t._parsed[i];null===n||isNaN(n)||!this.chart.getDataVisibility(i)||e[i].hidden||(s+=Math.abs(n))}return s}calculateCircumference(t){const e=this._cachedMeta.total;return e>0&&!isNaN(t)?O*(Math.abs(t)/e):0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t],i.options.locale);return{label:s[t]||"",value:n}}getMaxBorderWidth(t){let e=0;const i=this.chart;let s,n,o,a,r;if(!t)for(s=0,n=i.data.datasets.length;s<n;++s)if(i.isDatasetVisible(s)){o=i.getDatasetMeta(s),t=o.data,a=o.controller;break}if(!t)return 0;for(s=0,n=t.length;s<n;++s)r=a.resolveDataElementOptions(s),"inner"!==r.borderAlign&&(e=Math.max(e,r.borderWidth||0,r.hoverBorderWidth||0));return e}getMaxOffset(t){let e=0;for(let i=0,s=t.length;i<s;++i){const t=this.resolveDataElementOptions(i);e=Math.max(e,t.offset||0,t.hoverOffset||0)}return e}_getRingWeightOffset(t){let e=0;for(let i=0;i<t;++i)this.chart.isDatasetVisible(i)&&(e+=this._getRingWeight(i));return e}_getRingWeight(t){return Math.max(l(this.chart.data.datasets[t].weight,1),0)}_getVisibleDatasetWeightTotal(){return this._getRingWeightOffset(this.chart.data.datasets.length)||1}}class Yn extends js{static id="polarArea";static defaults={dataElementType:"arc",animation:{animateRotate:!0,animateScale:!0},animations:{numbers:{type:"number",properties:["x","y","startAngle","endAngle","innerRadius","outerRadius"]}},indexAxis:"r",startAngle:0};static overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(t){const e=t.data;if(e.labels.length&&e.datasets.length){const{labels:{pointStyle:i,color:s}}=t.legend.options;return e.labels.map(((e,n)=>{const o=t.getDatasetMeta(0).controller.getStyle(n);return{text:e,fillStyle:o.backgroundColor,strokeStyle:o.borderColor,fontColor:s,lineWidth:o.borderWidth,pointStyle:i,hidden:!t.getDataVisibility(n),index:n}}))}return[]}},onClick(t,e,i){i.chart.toggleDataVisibility(e.index),i.chart.update()}}},scales:{r:{type:"radialLinear",angleLines:{display:!1},beginAtZero:!0,grid:{circular:!0},pointLabels:{display:!1},startAngle:0}}};constructor(t,e){super(t,e),this.innerRadius=void 0,this.outerRadius=void 0}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart,s=i.data.labels||[],n=ne(e._parsed[t].r,i.options.locale);return{label:s[t]||"",value:n}}parseObjectData(t,e,i,s){return ii.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta.data;this._updateRadius(),this.updateElements(e,0,e.length,t)}getMinMax(){const t=this._cachedMeta,e={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY};return t.data.forEach(((t,i)=>{const s=this.getParsed(i).r;!isNaN(s)&&this.chart.getDataVisibility(i)&&(s<e.min&&(e.min=s),s>e.max&&(e.max=s))})),e}_updateRadius(){const t=this.chart,e=t.chartArea,i=t.options,s=Math.min(e.right-e.left,e.bottom-e.top),n=Math.max(s/2,0),o=(n-Math.max(i.cutoutPercentage?n/100*i.cutoutPercentage:1,0))/t.getVisibleDatasetCount();this.outerRadius=n-o*this.index,this.innerRadius=this.outerRadius-o}updateElements(t,e,i,s){const n="reset"===s,o=this.chart,a=o.options.animation,r=this._cachedMeta.rScale,l=r.xCenter,h=r.yCenter,c=r.getIndexAngle(0)-.5*C;let d,u=c;const f=360/this.countVisibleElements();for(d=0;d<e;++d)u+=this._computeAngle(d,s,f);for(d=e;d<e+i;d++){const e=t[d];let i=u,g=u+this._computeAngle(d,s,f),p=o.getDataVisibility(d)?r.getDistanceFromCenterForValue(this.getParsed(d).r):0;u=g,n&&(a.animateScale&&(p=0),a.animateRotate&&(i=g=c));const m={x:l,y:h,innerRadius:0,outerRadius:p,startAngle:i,endAngle:g,options:this.resolveDataElementOptions(d,e.active?"active":s)};this.updateElement(e,d,m,s)}}countVisibleElements(){const t=this._cachedMeta;let e=0;return t.data.forEach(((t,i)=>{!isNaN(this.getParsed(i).r)&&this.chart.getDataVisibility(i)&&e++})),e}_computeAngle(t,e,i){return this.chart.getDataVisibility(t)?$(this.resolveDataElementOptions(t,e).angle||i):0}}var Un=Object.freeze({__proto__:null,BarController:class extends js{static id="bar";static defaults={datasetElementType:!1,dataElementType:"bar",categoryPercentage:.8,barPercentage:.9,grouped:!0,animations:{numbers:{type:"number",properties:["x","y","base","width","height"]}}};static overrides={scales:{_index_:{type:"category",offset:!0,grid:{offset:!0}},_value_:{type:"linear",beginAtZero:!0}}};parsePrimitiveData(t,e,i,s){return Vn(t,e,i,s)}parseArrayData(t,e,i,s){return Vn(t,e,i,s)}parseObjectData(t,e,i,s){const{iScale:n,vScale:o}=t,{xAxisKey:a="x",yAxisKey:r="y"}=this._parsing,l="x"===n.axis?a:r,h="x"===o.axis?a:r,c=[];let d,u,f,g;for(d=i,u=i+s;d<u;++d)g=e[d],f={},f[n.axis]=n.parse(M(g,l),d),c.push(Fn(M(g,h),f,o,d));return c}updateRangeFromParsed(t,e,i,s){super.updateRangeFromParsed(t,e,i,s);const n=i._custom;n&&e===this._cachedMeta.vScale&&(t.min=Math.min(t.min,n.min),t.max=Math.max(t.max,n.max))}getMaxOverflow(){return 0}getLabelAndValue(t){const e=this._cachedMeta,{iScale:i,vScale:s}=e,n=this.getParsed(t),o=n._custom,a=Bn(o)?"["+o.start+", "+o.end+"]":""+s.getLabelForValue(n[s.axis]);return{label:""+i.getLabelForValue(n[i.axis]),value:a}}initialize(){this.enableOptionSharing=!0,super.initialize();this._cachedMeta.stack=this.getDataset().stack}update(t){const e=this._cachedMeta;this.updateElements(e.data,0,e.data.length,t)}updateElements(t,e,i,n){const o="reset"===n,{index:a,_cachedMeta:{vScale:r}}=this,l=r.getBasePixel(),h=r.isHorizontal(),c=this._getRuler(),{sharedOptions:d,includeOptions:u}=this._getSharedOptions(e,n);for(let f=e;f<e+i;f++){const e=this.getParsed(f),i=o||s(e[r.axis])?{base:l,head:l}:this._calculateBarValuePixels(f),g=this._calculateBarIndexPixels(f,c),p=(e._stacks||{})[r.axis],m={horizontal:h,base:i.base,enableBorderRadius:!p||Bn(e._custom)||a===p._top||a===p._bottom,x:h?i.head:g.center,y:h?g.center:i.head,height:h?g.size:Math.abs(i.size),width:h?Math.abs(i.size):g.size};u&&(m.options=d||this.resolveDataElementOptions(f,t[f].active?"active":n));const x=m.options||t[f].options;Wn(m,x,p,a),jn(m,x,c.ratio),this.updateElement(t[f],f,m,n)}}_getStacks(t,e){const{iScale:i}=this._cachedMeta,n=i.getMatchingVisibleMetas(this._type).filter((t=>t.controller.options.grouped)),o=i.options.stacked,a=[],r=this._cachedMeta.controller.getParsed(e),l=r&&r[i.axis],h=t=>{const e=t._parsed.find((t=>t[i.axis]===l)),n=e&&e[t.vScale.axis];if(s(n)||isNaN(n))return!0};for(const i of n)if((void 0===e||!h(i))&&((!1===o||-1===a.indexOf(i.stack)||void 0===o&&void 0===i.stack)&&a.push(i.stack),i.index===t))break;return a.length||a.push(void 0),a}_getStackCount(t){return this._getStacks(void 0,t).length}_getAxisCount(){return this._getAxis().length}getFirstScaleIdForIndexAxis(){const t=this.chart.scales,e=this.chart.options.indexAxis;return Object.keys(t).filter((i=>t[i].axis===e)).shift()}_getAxis(){const t={},e=this.getFirstScaleIdForIndexAxis();for(const i of this.chart.data.datasets)t[l("x"===this.chart.options.indexAxis?i.xAxisID:i.yAxisID,e)]=!0;return Object.keys(t)}_getStackIndex(t,e,i){const s=this._getStacks(t,i),n=void 0!==e?s.indexOf(e):-1;return-1===n?s.length-1:n}_getRuler(){const t=this.options,e=this._cachedMeta,i=e.iScale,s=[];let n,o;for(n=0,o=e.data.length;n<o;++n)s.push(i.getPixelForValue(this.getParsed(n)[i.axis],n));const a=t.barThickness;return{min:a||zn(e),pixels:s,start:i._startPixel,end:i._endPixel,stackCount:this._getStackCount(),scale:i,grouped:t.grouped,ratio:a?1:t.categoryPercentage*t.barPercentage}}_calculateBarValuePixels(t){const{_cachedMeta:{vScale:e,_stacked:i,index:n},options:{base:o,minBarLength:a}}=this,r=o||0,l=this.getParsed(t),h=l._custom,c=Bn(h);let d,u,f=l[e.axis],g=0,p=i?this.applyStack(e,l,i):f;p!==f&&(g=p-f,p=f),c&&(f=h.barStart,p=h.barEnd-h.barStart,0!==f&&F(f)!==F(h.barEnd)&&(g=0),g+=f);const m=s(o)||c?g:o;let x=e.getPixelForValue(m);if(d=this.chart.getDataVisibility(t)?e.getPixelForValue(g+p):x,u=d-x,Math.abs(u)<a){u=function(t,e,i){return 0!==t?F(t):(e.isHorizontal()?1:-1)*(e.min>=i?1:-1)}(u,e,r)*a,f===r&&(x-=u/2);const t=e.getPixelForDecimal(0),s=e.getPixelForDecimal(1),o=Math.min(t,s),h=Math.max(t,s);x=Math.max(Math.min(x,h),o),d=x+u,i&&!c&&(l._stacks[e.axis]._visualValues[n]=e.getValueForPixel(d)-e.getValueForPixel(x))}if(x===e.getPixelForValue(r)){const t=F(u)*e.getLineWidthForValue(r)/2;x+=t,u-=t}return{size:u,base:x,head:d,center:d+u/2}}_calculateBarIndexPixels(t,e){const i=e.scale,n=this.options,o=n.skipNull,a=l(n.maxBarThickness,1/0);let r,h;const c=this._getAxisCount();if(e.grouped){const i=o?this._getStackCount(t):e.stackCount,d="flex"===n.barThickness?function(t,e,i,s){const n=e.pixels,o=n[t];let a=t>0?n[t-1]:null,r=t<n.length-1?n[t+1]:null;const l=i.categoryPercentage;null===a&&(a=o-(null===r?e.end-e.start:r-o)),null===r&&(r=o+o-a);const h=o-(o-Math.min(a,r))/2*l;return{chunk:Math.abs(r-a)/2*l/s,ratio:i.barPercentage,start:h}}(t,e,n,i*c):function(t,e,i,n){const o=i.barThickness;let a,r;return s(o)?(a=e.min*i.categoryPercentage,r=i.barPercentage):(a=o*n,r=1),{chunk:a/n,ratio:r,start:e.pixels[t]-a/2}}(t,e,n,i*c),u="x"===this.chart.options.indexAxis?this.getDataset().xAxisID:this.getDataset().yAxisID,f=this._getAxis().indexOf(l(u,this.getFirstScaleIdForIndexAxis())),g=this._getStackIndex(this.index,this._cachedMeta.stack,o?t:void 0)+f;r=d.start+d.chunk*g+d.chunk/2,h=Math.min(a,d.chunk*d.ratio)}else r=i.getPixelForValue(this.getParsed(t)[i.axis],t),h=Math.min(a,e.min*e.ratio);return{base:r-h/2,head:r+h/2,center:r,size:h}}draw(){const t=this._cachedMeta,e=t.vScale,i=t.data,s=i.length;let n=0;for(;n<s;++n)null===this.getParsed(n)[e.axis]||i[n].hidden||i[n].draw(this._ctx)}},BubbleController:class extends js{static id="bubble";static defaults={datasetElementType:!1,dataElementType:"point",animations:{numbers:{type:"number",properties:["x","y","borderWidth","radius"]}}};static overrides={scales:{x:{type:"linear"},y:{type:"linear"}}};initialize(){this.enableOptionSharing=!0,super.initialize()}parsePrimitiveData(t,e,i,s){const n=super.parsePrimitiveData(t,e,i,s);for(let t=0;t<n.length;t++)n[t]._custom=this.resolveDataElementOptions(t+i).radius;return n}parseArrayData(t,e,i,s){const n=super.parseArrayData(t,e,i,s);for(let t=0;t<n.length;t++){const s=e[i+t];n[t]._custom=l(s[2],this.resolveDataElementOptions(t+i).radius)}return n}parseObjectData(t,e,i,s){const n=super.parseObjectData(t,e,i,s);for(let t=0;t<n.length;t++){const s=e[i+t];n[t]._custom=l(s&&s.r&&+s.r,this.resolveDataElementOptions(t+i).radius)}return n}getMaxOverflow(){const t=this._cachedMeta.data;let e=0;for(let i=t.length-1;i>=0;--i)e=Math.max(e,t[i].size(this.resolveDataElementOptions(i))/2);return e>0&&e}getLabelAndValue(t){const e=this._cachedMeta,i=this.chart.data.labels||[],{xScale:s,yScale:n}=e,o=this.getParsed(t),a=s.getLabelForValue(o.x),r=n.getLabelForValue(o.y),l=o._custom;return{label:i[t]||"",value:"("+a+", "+r+(l?", "+l:"")+")"}}update(t){const e=this._cachedMeta.data;this.updateElements(e,0,e.length,t)}updateElements(t,e,i,s){const n="reset"===s,{iScale:o,vScale:a}=this._cachedMeta,{sharedOptions:r,includeOptions:l}=this._getSharedOptions(e,s),h=o.axis,c=a.axis;for(let d=e;d<e+i;d++){const e=t[d],i=!n&&this.getParsed(d),u={},f=u[h]=n?o.getPixelForDecimal(.5):o.getPixelForValue(i[h]),g=u[c]=n?a.getBasePixel():a.getPixelForValue(i[c]);u.skip=isNaN(f)||isNaN(g),l&&(u.options=r||this.resolveDataElementOptions(d,e.active?"active":s),n&&(u.options.radius=0)),this.updateElement(e,d,u,s)}}resolveDataElementOptions(t,e){const i=this.getParsed(t);let s=super.resolveDataElementOptions(t,e);s.$shared&&(s=Object.assign({},s,{$shared:!1}));const n=s.radius;return"active"!==e&&(s.radius=0),s.radius+=l(i&&i._custom,n),s}},DoughnutController:$n,LineController:class extends js{static id="line";static defaults={datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1};static overrides={scales:{_index_:{type:"category"},_value_:{type:"linear"}}};initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(t){const e=this._cachedMeta,{dataset:i,data:s=[],_dataset:n}=e,o=this.chart._animationsDisabled;let{start:a,count:r}=pt(e,s,o);this._drawStart=a,this._drawCount=r,mt(e)&&(a=0,r=s.length),i._chart=this.chart,i._datasetIndex=this.index,i._decimated=!!n._decimated,i.points=s;const l=this.resolveDatasetElementOptions(t);this.options.showLine||(l.borderWidth=0),l.segment=this.options.segment,this.updateElement(i,void 0,{animated:!o,options:l},t),this.updateElements(s,a,r,t)}updateElements(t,e,i,n){const o="reset"===n,{iScale:a,vScale:r,_stacked:l,_dataset:h}=this._cachedMeta,{sharedOptions:c,includeOptions:d}=this._getSharedOptions(e,n),u=a.axis,f=r.axis,{spanGaps:g,segment:p}=this.options,m=N(g)?g:Number.POSITIVE_INFINITY,x=this.chart._animationsDisabled||o||"none"===n,b=e+i,_=t.length;let y=e>0&&this.getParsed(e-1);for(let i=0;i<_;++i){const g=t[i],_=x?g:{};if(i<e||i>=b){_.skip=!0;continue}const v=this.getParsed(i),M=s(v[f]),w=_[u]=a.getPixelForValue(v[u],i),k=_[f]=o||M?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,v,l):v[f],i);_.skip=isNaN(w)||isNaN(k)||M,_.stop=i>0&&Math.abs(v[u]-y[u])>m,p&&(_.parsed=v,_.raw=h.data[i]),d&&(_.options=c||this.resolveDataElementOptions(i,g.active?"active":n)),x||this.updateElement(g,i,_,n),y=v}}getMaxOverflow(){const t=this._cachedMeta,e=t.dataset,i=e.options&&e.options.borderWidth||0,s=t.data||[];if(!s.length)return i;const n=s[0].size(this.resolveDataElementOptions(0)),o=s[s.length-1].size(this.resolveDataElementOptions(s.length-1));return Math.max(i,n,o)/2}draw(){const t=this._cachedMeta;t.dataset.updateControlPoints(this.chart.chartArea,t.iScale.axis),super.draw()}},PieController:class extends $n{static id="pie";static defaults={cutout:0,rotation:0,circumference:360,radius:"100%"}},PolarAreaController:Yn,RadarController:class extends js{static id="radar";static defaults={datasetElementType:"line",dataElementType:"point",indexAxis:"r",showLine:!0,elements:{line:{fill:"start"}}};static overrides={aspectRatio:1,scales:{r:{type:"radialLinear"}}};getLabelAndValue(t){const e=this._cachedMeta.vScale,i=this.getParsed(t);return{label:e.getLabels()[t],value:""+e.getLabelForValue(i[e.axis])}}parseObjectData(t,e,i,s){return ii.bind(this)(t,e,i,s)}update(t){const e=this._cachedMeta,i=e.dataset,s=e.data||[],n=e.iScale.getLabels();if(i.points=s,"resize"!==t){const e=this.resolveDatasetElementOptions(t);this.options.showLine||(e.borderWidth=0);const o={_loop:!0,_fullLoop:n.length===s.length,options:e};this.updateElement(i,void 0,o,t)}this.updateElements(s,0,s.length,t)}updateElements(t,e,i,s){const n=this._cachedMeta.rScale,o="reset"===s;for(let a=e;a<e+i;a++){const e=t[a],i=this.resolveDataElementOptions(a,e.active?"active":s),r=n.getPointPositionForValue(a,this.getParsed(a).r),l=o?n.xCenter:r.x,h=o?n.yCenter:r.y,c={x:l,y:h,angle:r.angle,skip:isNaN(l)||isNaN(h),options:i};this.updateElement(e,a,c,s)}}},ScatterController:class extends js{static id="scatter";static defaults={datasetElementType:!1,dataElementType:"point",showLine:!1,fill:!1};static overrides={interaction:{mode:"point"},scales:{x:{type:"linear"},y:{type:"linear"}}};getLabelAndValue(t){const e=this._cachedMeta,i=this.chart.data.labels||[],{xScale:s,yScale:n}=e,o=this.getParsed(t),a=s.getLabelForValue(o.x),r=n.getLabelForValue(o.y);return{label:i[t]||"",value:"("+a+", "+r+")"}}update(t){const e=this._cachedMeta,{data:i=[]}=e,s=this.chart._animationsDisabled;let{start:n,count:o}=pt(e,i,s);if(this._drawStart=n,this._drawCount=o,mt(e)&&(n=0,o=i.length),this.options.showLine){this.datasetElementType||this.addElements();const{dataset:n,_dataset:o}=e;n._chart=this.chart,n._datasetIndex=this.index,n._decimated=!!o._decimated,n.points=i;const a=this.resolveDatasetElementOptions(t);a.segment=this.options.segment,this.updateElement(n,void 0,{animated:!s,options:a},t)}else this.datasetElementType&&(delete e.dataset,this.datasetElementType=!1);this.updateElements(i,n,o,t)}addElements(){const{showLine:t}=this.options;!this.datasetElementType&&t&&(this.datasetElementType=this.chart.registry.getElement("line")),super.addElements()}updateElements(t,e,i,n){const o="reset"===n,{iScale:a,vScale:r,_stacked:l,_dataset:h}=this._cachedMeta,c=this.resolveDataElementOptions(e,n),d=this.getSharedOptions(c),u=this.includeOptions(n,d),f=a.axis,g=r.axis,{spanGaps:p,segment:m}=this.options,x=N(p)?p:Number.POSITIVE_INFINITY,b=this.chart._animationsDisabled||o||"none"===n;let _=e>0&&this.getParsed(e-1);for(let c=e;c<e+i;++c){const e=t[c],i=this.getParsed(c),p=b?e:{},y=s(i[g]),v=p[f]=a.getPixelForValue(i[f],c),M=p[g]=o||y?r.getBasePixel():r.getPixelForValue(l?this.applyStack(r,i,l):i[g],c);p.skip=isNaN(v)||isNaN(M)||y,p.stop=c>0&&Math.abs(i[f]-_[f])>x,m&&(p.parsed=i,p.raw=h.data[c]),u&&(p.options=d||this.resolveDataElementOptions(c,e.active?"active":n)),b||this.updateElement(e,c,p,n),_=i}this.updateSharedOptions(d,n,c)}getMaxOverflow(){const t=this._cachedMeta,e=t.data||[];if(!this.options.showLine){let t=0;for(let i=e.length-1;i>=0;--i)t=Math.max(t,e[i].size(this.resolveDataElementOptions(i))/2);return t>0&&t}const i=t.dataset,s=i.options&&i.options.borderWidth||0;if(!e.length)return s;const n=e[0].size(this.resolveDataElementOptions(0)),o=e[e.length-1].size(this.resolveDataElementOptions(e.length-1));return Math.max(s,n,o)/2}}});function Xn(t,e,i,s){const n=vi(t.options.borderRadius,["outerStart","outerEnd","innerStart","innerEnd"]);const o=(i-e)/2,a=Math.min(o,s*e/2),r=t=>{const e=(i-Math.min(o,t))*s/2;return Z(t,0,Math.min(o,e))};return{outerStart:r(n.outerStart),outerEnd:r(n.outerEnd),innerStart:Z(n.innerStart,0,a),innerEnd:Z(n.innerEnd,0,a)}}function qn(t,e,i,s){return{x:i+t*Math.cos(e),y:s+t*Math.sin(e)}}function Kn(t,e,i,s,n,o){const{x:a,y:r,startAngle:l,pixelMargin:h,innerRadius:c}=e,d=Math.max(e.outerRadius+s+i-h,0),u=c>0?c+s+i+h:0;let f=0;const g=n-l;if(s){const t=((c>0?c-s:0)+(d>0?d-s:0))/2;f=(g-(0!==t?g*t/(t+s):g))/2}const p=(g-Math.max(.001,g*d-i/C)/d)/2,m=l+p+f,x=n-p-f,{outerStart:b,outerEnd:_,innerStart:y,innerEnd:v}=Xn(e,u,d,x-m),M=d-b,w=d-_,k=m+b/M,S=x-_/w,P=u+y,D=u+v,O=m+y/P,A=x-v/D;if(t.beginPath(),o){const e=(k+S)/2;if(t.arc(a,r,d,k,e),t.arc(a,r,d,e,S),_>0){const e=qn(w,S,a,r);t.arc(e.x,e.y,_,S,x+E)}const i=qn(D,x,a,r);if(t.lineTo(i.x,i.y),v>0){const e=qn(D,A,a,r);t.arc(e.x,e.y,v,x+E,A+Math.PI)}const s=(x-v/u+(m+y/u))/2;if(t.arc(a,r,u,x-v/u,s,!0),t.arc(a,r,u,s,m+y/u,!0),y>0){const e=qn(P,O,a,r);t.arc(e.x,e.y,y,O+Math.PI,m-E)}const n=qn(M,m,a,r);if(t.lineTo(n.x,n.y),b>0){const e=qn(M,k,a,r);t.arc(e.x,e.y,b,m-E,k)}}else{t.moveTo(a,r);const e=Math.cos(k)*d+a,i=Math.sin(k)*d+r;t.lineTo(e,i);const s=Math.cos(S)*d+a,n=Math.sin(S)*d+r;t.lineTo(s,n)}t.closePath()}function Gn(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r,options:l}=e,{borderWidth:h,borderJoinStyle:c,borderDash:d,borderDashOffset:u,borderRadius:f}=l,g="inner"===l.borderAlign;if(!h)return;t.setLineDash(d||[]),t.lineDashOffset=u,g?(t.lineWidth=2*h,t.lineJoin=c||"round"):(t.lineWidth=h,t.lineJoin=c||"bevel");let p=e.endAngle;if(o){Kn(t,e,i,s,p,n);for(let e=0;e<o;++e)t.stroke();isNaN(r)||(p=a+(r%O||O))}g&&function(t,e,i){const{startAngle:s,pixelMargin:n,x:o,y:a,outerRadius:r,innerRadius:l}=e;let h=n/r;t.beginPath(),t.arc(o,a,r,s-h,i+h),l>n?(h=n/l,t.arc(o,a,l,i+h,s-h,!0)):t.arc(o,a,n,i+E,s-E),t.closePath(),t.clip()}(t,e,p),l.selfJoin&&p-a>=C&&0===f&&"miter"!==c&&function(t,e,i){const{startAngle:s,x:n,y:o,outerRadius:a,innerRadius:r,options:l}=e,{borderWidth:h,borderJoinStyle:c}=l,d=Math.min(h/a,G(s-i));if(t.beginPath(),t.arc(n,o,a-h/2,s+d/2,i-d/2),r>0){const e=Math.min(h/r,G(s-i));t.arc(n,o,r+h/2,i-e/2,s+e/2,!0)}else{const e=Math.min(h/2,a*G(s-i));if("round"===c)t.arc(n,o,e,i-C/2,s+C/2,!0);else if("bevel"===c){const a=2*e*e,r=-a*Math.cos(i+C/2)+n,l=-a*Math.sin(i+C/2)+o,h=a*Math.cos(s+C/2)+n,c=a*Math.sin(s+C/2)+o;t.lineTo(r,l),t.lineTo(h,c)}}t.closePath(),t.moveTo(0,0),t.rect(0,0,t.canvas.width,t.canvas.height),t.clip("evenodd")}(t,e,p),o||(Kn(t,e,i,s,p,n),t.stroke())}function Jn(t,e,i=e){t.lineCap=l(i.borderCapStyle,e.borderCapStyle),t.setLineDash(l(i.borderDash,e.borderDash)),t.lineDashOffset=l(i.borderDashOffset,e.borderDashOffset),t.lineJoin=l(i.borderJoinStyle,e.borderJoinStyle),t.lineWidth=l(i.borderWidth,e.borderWidth),t.strokeStyle=l(i.borderColor,e.borderColor)}function Zn(t,e,i){t.lineTo(i.x,i.y)}function Qn(t,e,i={}){const s=t.length,{start:n=0,end:o=s-1}=i,{start:a,end:r}=e,l=Math.max(n,a),h=Math.min(o,r),c=n<a&&o<a||n>r&&o>r;return{count:s,start:l,loop:e.loop,ilen:h<l&&!c?s+h-l:h-l}}function to(t,e,i,s){const{points:n,options:o}=e,{count:a,start:r,loop:l,ilen:h}=Qn(n,i,s),c=function(t){return t.stepped?Fe:t.tension||"monotone"===t.cubicInterpolationMode?Ve:Zn}(o);let d,u,f,{move:g=!0,reverse:p}=s||{};for(d=0;d<=h;++d)u=n[(r+(p?h-d:d))%a],u.skip||(g?(t.moveTo(u.x,u.y),g=!1):c(t,f,u,p,o.stepped),f=u);return l&&(u=n[(r+(p?h:0))%a],c(t,f,u,p,o.stepped)),!!l}function eo(t,e,i,s){const n=e.points,{count:o,start:a,ilen:r}=Qn(n,i,s),{move:l=!0,reverse:h}=s||{};let c,d,u,f,g,p,m=0,x=0;const b=t=>(a+(h?r-t:t))%o,_=()=>{f!==g&&(t.lineTo(m,g),t.lineTo(m,f),t.lineTo(m,p))};for(l&&(d=n[b(0)],t.moveTo(d.x,d.y)),c=0;c<=r;++c){if(d=n[b(c)],d.skip)continue;const e=d.x,i=d.y,s=0|e;s===u?(i<f?f=i:i>g&&(g=i),m=(x*m+e)/++x):(_(),t.lineTo(e,i),u=s,x=0,f=g=i),p=i}_()}function io(t){const e=t.options,i=e.borderDash&&e.borderDash.length;return!(t._decimated||t._loop||e.tension||"monotone"===e.cubicInterpolationMode||e.stepped||i)?eo:to}const so="function"==typeof Path2D;function no(t,e,i,s){so&&!e.options.segment?function(t,e,i,s){let n=e._path;n||(n=e._path=new Path2D,e.path(n,i,s)&&n.closePath()),Jn(t,e.options),t.stroke(n)}(t,e,i,s):function(t,e,i,s){const{segments:n,options:o}=e,a=io(e);for(const r of n)Jn(t,o,r.style),t.beginPath(),a(t,e,r,{start:i,end:i+s-1})&&t.closePath(),t.stroke()}(t,e,i,s)}class oo extends $s{static id="line";static defaults={borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderWidth:3,capBezierPoints:!0,cubicInterpolationMode:"default",fill:!1,spanGaps:!1,stepped:!1,tension:0};static defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};static descriptors={_scriptable:!0,_indexable:t=>"borderDash"!==t&&"fill"!==t};constructor(t){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,t&&Object.assign(this,t)}updateControlPoints(t,e){const i=this.options;if((i.tension||"monotone"===i.cubicInterpolationMode)&&!i.stepped&&!this._pointsUpdated){const s=i.spanGaps?this._loop:this._fullLoop;hi(this._points,i,t,s,e),this._pointsUpdated=!0}}set points(t){this._points=t,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=zi(this,this.options.segment))}first(){const t=this.segments,e=this.points;return t.length&&e[t[0].start]}last(){const t=this.segments,e=this.points,i=t.length;return i&&e[t[i-1].end]}interpolate(t,e){const i=this.options,s=t[e],n=this.points,o=Ii(this,{property:e,start:s,end:s});if(!o.length)return;const a=[],r=function(t){return t.stepped?pi:t.tension||"monotone"===t.cubicInterpolationMode?mi:gi}(i);let l,h;for(l=0,h=o.length;l<h;++l){const{start:h,end:c}=o[l],d=n[h],u=n[c];if(d===u){a.push(d);continue}const f=r(d,u,Math.abs((s-d[e])/(u[e]-d[e])),i.stepped);f[e]=t[e],a.push(f)}return 1===a.length?a[0]:a}pathSegment(t,e,i){return io(this)(t,this,e,i)}path(t,e,i){const s=this.segments,n=io(this);let o=this._loop;e=e||0,i=i||this.points.length-e;for(const a of s)o&=n(t,this,a,{start:e,end:e+i-1});return!!o}draw(t,e,i,s){const n=this.options||{};(this.points||[]).length&&n.borderWidth&&(t.save(),no(t,this,i,s),t.restore()),this.animated&&(this._pointsUpdated=!1,this._path=void 0)}}function ao(t,e,i,s){const n=t.options,{[i]:o}=t.getProps([i],s);return Math.abs(e-o)<n.radius+n.hitRadius}function ro(t,e){const{x:i,y:s,base:n,width:o,height:a}=t.getProps(["x","y","base","width","height"],e);let r,l,h,c,d;return t.horizontal?(d=a/2,r=Math.min(i,n),l=Math.max(i,n),h=s-d,c=s+d):(d=o/2,r=i-d,l=i+d,h=Math.min(s,n),c=Math.max(s,n)),{left:r,top:h,right:l,bottom:c}}function lo(t,e,i,s){return t?0:Z(e,i,s)}function ho(t){const e=ro(t),i=e.right-e.left,s=e.bottom-e.top,n=function(t,e,i){const s=t.options.borderWidth,n=t.borderSkipped,o=Mi(s);return{t:lo(n.top,o.top,0,i),r:lo(n.right,o.right,0,e),b:lo(n.bottom,o.bottom,0,i),l:lo(n.left,o.left,0,e)}}(t,i/2,s/2),a=function(t,e,i){const{enableBorderRadius:s}=t.getProps(["enableBorderRadius"]),n=t.options.borderRadius,a=wi(n),r=Math.min(e,i),l=t.borderSkipped,h=s||o(n);return{topLeft:lo(!h||l.top||l.left,a.topLeft,0,r),topRight:lo(!h||l.top||l.right,a.topRight,0,r),bottomLeft:lo(!h||l.bottom||l.left,a.bottomLeft,0,r),bottomRight:lo(!h||l.bottom||l.right,a.bottomRight,0,r)}}(t,i/2,s/2);return{outer:{x:e.left,y:e.top,w:i,h:s,radius:a},inner:{x:e.left+n.l,y:e.top+n.t,w:i-n.l-n.r,h:s-n.t-n.b,radius:{topLeft:Math.max(0,a.topLeft-Math.max(n.t,n.l)),topRight:Math.max(0,a.topRight-Math.max(n.t,n.r)),bottomLeft:Math.max(0,a.bottomLeft-Math.max(n.b,n.l)),bottomRight:Math.max(0,a.bottomRight-Math.max(n.b,n.r))}}}}function co(t,e,i,s){const n=null===e,o=null===i,a=t&&!(n&&o)&&ro(t,s);return a&&(n||tt(e,a.left,a.right))&&(o||tt(i,a.top,a.bottom))}function uo(t,e){t.rect(e.x,e.y,e.w,e.h)}function fo(t,e,i={}){const s=t.x!==i.x?-e:0,n=t.y!==i.y?-e:0,o=(t.x+t.w!==i.x+i.w?e:0)-s,a=(t.y+t.h!==i.y+i.h?e:0)-n;return{x:t.x+s,y:t.y+n,w:t.w+o,h:t.h+a,radius:t.radius}}var go=Object.freeze({__proto__:null,ArcElement:class extends $s{static id="arc";static defaults={borderAlign:"center",borderColor:"#fff",borderDash:[],borderDashOffset:0,borderJoinStyle:void 0,borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:void 0,circular:!0,selfJoin:!1};static defaultRoutes={backgroundColor:"backgroundColor"};static descriptors={_scriptable:!0,_indexable:t=>"borderDash"!==t};circumference;endAngle;fullCircles;innerRadius;outerRadius;pixelMargin;startAngle;constructor(t){super(),this.options=void 0,this.circumference=void 0,this.startAngle=void 0,this.endAngle=void 0,this.innerRadius=void 0,this.outerRadius=void 0,this.pixelMargin=0,this.fullCircles=0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.getProps(["x","y"],i),{angle:n,distance:o}=X(s,{x:t,y:e}),{startAngle:a,endAngle:r,innerRadius:h,outerRadius:c,circumference:d}=this.getProps(["startAngle","endAngle","innerRadius","outerRadius","circumference"],i),u=(this.options.spacing+this.options.borderWidth)/2,f=l(d,r-a),g=J(n,a,r)&&a!==r,p=f>=O||g,m=tt(o,h+u,c+u);return p&&m}getCenterPoint(t){const{x:e,y:i,startAngle:s,endAngle:n,innerRadius:o,outerRadius:a}=this.getProps(["x","y","startAngle","endAngle","innerRadius","outerRadius"],t),{offset:r,spacing:l}=this.options,h=(s+n)/2,c=(o+a+l+r)/2;return{x:e+Math.cos(h)*c,y:i+Math.sin(h)*c}}tooltipPosition(t){return this.getCenterPoint(t)}draw(t){const{options:e,circumference:i}=this,s=(e.offset||0)/4,n=(e.spacing||0)/2,o=e.circular;if(this.pixelMargin="inner"===e.borderAlign?.33:0,this.fullCircles=i>O?Math.floor(i/O):0,0===i||this.innerRadius<0||this.outerRadius<0)return;t.save();const a=(this.startAngle+this.endAngle)/2;t.translate(Math.cos(a)*s,Math.sin(a)*s);const r=s*(1-Math.sin(Math.min(C,i||0)));t.fillStyle=e.backgroundColor,t.strokeStyle=e.borderColor,function(t,e,i,s,n){const{fullCircles:o,startAngle:a,circumference:r}=e;let l=e.endAngle;if(o){Kn(t,e,i,s,l,n);for(let e=0;e<o;++e)t.fill();isNaN(r)||(l=a+(r%O||O))}Kn(t,e,i,s,l,n),t.fill()}(t,this,r,n,o),Gn(t,this,r,n,o),t.restore()}},BarElement:class extends $s{static id="bar";static defaults={borderSkipped:"start",borderWidth:0,borderRadius:0,inflateAmount:"auto",pointStyle:void 0};static defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};constructor(t){super(),this.options=void 0,this.horizontal=void 0,this.base=void 0,this.width=void 0,this.height=void 0,this.inflateAmount=void 0,t&&Object.assign(this,t)}draw(t){const{inflateAmount:e,options:{borderColor:i,backgroundColor:s}}=this,{inner:n,outer:o}=ho(this),a=(r=o.radius).topLeft||r.topRight||r.bottomLeft||r.bottomRight?He:uo;var r;t.save(),o.w===n.w&&o.h===n.h||(t.beginPath(),a(t,fo(o,e,n)),t.clip(),a(t,fo(n,-e,o)),t.fillStyle=i,t.fill("evenodd")),t.beginPath(),a(t,fo(n,e)),t.fillStyle=s,t.fill(),t.restore()}inRange(t,e,i){return co(this,t,e,i)}inXRange(t,e){return co(this,t,null,e)}inYRange(t,e){return co(this,null,t,e)}getCenterPoint(t){const{x:e,y:i,base:s,horizontal:n}=this.getProps(["x","y","base","horizontal"],t);return{x:n?(e+s)/2:e,y:n?i:(i+s)/2}}getRange(t){return"x"===t?this.width/2:this.height/2}},LineElement:oo,PointElement:class extends $s{static id="point";parsed;skip;stop;static defaults={borderWidth:1,hitRadius:1,hoverBorderWidth:1,hoverRadius:4,pointStyle:"circle",radius:3,rotation:0};static defaultRoutes={backgroundColor:"backgroundColor",borderColor:"borderColor"};constructor(t){super(),this.options=void 0,this.parsed=void 0,this.skip=void 0,this.stop=void 0,t&&Object.assign(this,t)}inRange(t,e,i){const s=this.options,{x:n,y:o}=this.getProps(["x","y"],i);return Math.pow(t-n,2)+Math.pow(e-o,2)<Math.pow(s.hitRadius+s.radius,2)}inXRange(t,e){return ao(this,t,"x",e)}inYRange(t,e){return ao(this,t,"y",e)}getCenterPoint(t){const{x:e,y:i}=this.getProps(["x","y"],t);return{x:e,y:i}}size(t){let e=(t=t||this.options||{}).radius||0;e=Math.max(e,e&&t.hoverRadius||0);return 2*(e+(e&&t.borderWidth||0))}draw(t,e){const i=this.options;this.skip||i.radius<.1||!Re(this,e,this.size(i)/2)||(t.strokeStyle=i.borderColor,t.lineWidth=i.borderWidth,t.fillStyle=i.backgroundColor,Le(t,i,this.x,this.y))}getRange(){const t=this.options||{};return t.radius+t.hitRadius}}});function po(t,e,i,s){const n=t.indexOf(e);if(-1===n)return((t,e,i,s)=>("string"==typeof e?(i=t.push(e)-1,s.unshift({index:i,label:e})):isNaN(e)&&(i=null),i))(t,e,i,s);return n!==t.lastIndexOf(e)?i:n}function mo(t){const e=this.getLabels();return t>=0&&t<e.length?e[t]:t}function xo(t,e,{horizontal:i,minRotation:s}){const n=$(s),o=(i?Math.sin(n):Math.cos(n))||.001,a=.75*e*(""+t).length;return Math.min(e/o,a)}class bo extends tn{constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(t,e){return s(t)||("number"==typeof t||t instanceof Number)&&!isFinite(+t)?null:+t}handleTickRangeOptions(){const{beginAtZero:t}=this.options,{minDefined:e,maxDefined:i}=this.getUserBounds();let{min:s,max:n}=this;const o=t=>s=e?s:t,a=t=>n=i?n:t;if(t){const t=F(s),e=F(n);t<0&&e<0?a(0):t>0&&e>0&&o(0)}if(s===n){let e=0===n?1:Math.abs(.05*n);a(n+e),t||o(s-e)}this.min=s,this.max=n}getTickLimit(){const t=this.options.ticks;let e,{maxTicksLimit:i,stepSize:s}=t;return s?(e=Math.ceil(this.max/s)-Math.floor(this.min/s)+1,e>1e3&&(console.warn(`scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.`),e=1e3)):(e=this.computeTickLimit(),i=i||11),i&&(e=Math.min(i,e)),e}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const t=this.options,e=t.ticks;let i=this.getTickLimit();i=Math.max(2,i);const n=function(t,e){const i=[],{bounds:n,step:o,min:a,max:r,precision:l,count:h,maxTicks:c,maxDigits:d,includeBounds:u}=t,f=o||1,g=c-1,{min:p,max:m}=e,x=!s(a),b=!s(r),_=!s(h),y=(m-p)/(d+1);let v,M,w,k,S=B((m-p)/g/f)*f;if(S<1e-14&&!x&&!b)return[{value:p},{value:m}];k=Math.ceil(m/S)-Math.floor(p/S),k>g&&(S=B(k*S/g/f)*f),s(l)||(v=Math.pow(10,l),S=Math.ceil(S*v)/v),"ticks"===n?(M=Math.floor(p/S)*S,w=Math.ceil(m/S)*S):(M=p,w=m),x&&b&&o&&H((r-a)/o,S/1e3)?(k=Math.round(Math.min((r-a)/S,c)),S=(r-a)/k,M=a,w=r):_?(M=x?a:M,w=b?r:w,k=h-1,S=(w-M)/k):(k=(w-M)/S,k=V(k,Math.round(k),S/1e3)?Math.round(k):Math.ceil(k));const P=Math.max(U(S),U(M));v=Math.pow(10,s(l)?P:l),M=Math.round(M*v)/v,w=Math.round(w*v)/v;let D=0;for(x&&(u&&M!==a?(i.push({value:a}),M<a&&D++,V(Math.round((M+D*S)*v)/v,a,xo(a,y,t))&&D++):M<a&&D++);D<k;++D){const t=Math.round((M+D*S)*v)/v;if(b&&t>r)break;i.push({value:t})}return b&&u&&w!==r?i.length&&V(i[i.length-1].value,r,xo(r,y,t))?i[i.length-1].value=r:i.push({value:r}):b&&w!==r||i.push({value:w}),i}({maxTicks:i,bounds:t.bounds,min:t.min,max:t.max,precision:e.precision,step:e.stepSize,count:e.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:e.minRotation||0,includeBounds:!1!==e.includeBounds},this._range||this);return"ticks"===t.bounds&&j(n,this,"value"),t.reverse?(n.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),n}configure(){const t=this.ticks;let e=this.min,i=this.max;if(super.configure(),this.options.offset&&t.length){const s=(i-e)/Math.max(t.length-1,1)/2;e-=s,i+=s}this._startValue=e,this._endValue=i,this._valueRange=i-e}getLabelForValue(t){return ne(t,this.chart.options.locale,this.options.ticks.format)}}class _o extends bo{static id="linear";static defaults={ticks:{callback:ae.formatters.numeric}};determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?t:0,this.max=a(e)?e:1,this.handleTickRangeOptions()}computeTickLimit(){const t=this.isHorizontal(),e=t?this.width:this.height,i=$(this.options.ticks.minRotation),s=(t?Math.sin(i):Math.cos(i))||.001,n=this._resolveTickFontOptions(0);return Math.ceil(e/Math.min(40,n.lineHeight/s))}getPixelForValue(t){return null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getValueForPixel(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange}}const yo=t=>Math.floor(z(t)),vo=(t,e)=>Math.pow(10,yo(t)+e);function Mo(t){return 1===t/Math.pow(10,yo(t))}function wo(t,e,i){const s=Math.pow(10,i),n=Math.floor(t/s);return Math.ceil(e/s)-n}function ko(t,{min:e,max:i}){e=r(t.min,e);const s=[],n=yo(e);let o=function(t,e){let i=yo(e-t);for(;wo(t,e,i)>10;)i++;for(;wo(t,e,i)<10;)i--;return Math.min(i,yo(t))}(e,i),a=o<0?Math.pow(10,Math.abs(o)):1;const l=Math.pow(10,o),h=n>o?Math.pow(10,n):0,c=Math.round((e-h)*a)/a,d=Math.floor((e-h)/l/10)*l*10;let u=Math.floor((c-d)/Math.pow(10,o)),f=r(t.min,Math.round((h+d+u*Math.pow(10,o))*a)/a);for(;f<i;)s.push({value:f,major:Mo(f),significand:u}),u>=10?u=u<15?15:20:u++,u>=20&&(o++,u=2,a=o>=0?1:a),f=Math.round((h+d+u*Math.pow(10,o))*a)/a;const g=r(t.max,f);return s.push({value:g,major:Mo(g),significand:u}),s}class So extends tn{static id="logarithmic";static defaults={ticks:{callback:ae.formatters.logarithmic,major:{enabled:!0}}};constructor(t){super(t),this.start=void 0,this.end=void 0,this._startValue=void 0,this._valueRange=0}parse(t,e){const i=bo.prototype.parse.apply(this,[t,e]);if(0!==i)return a(i)&&i>0?i:null;this._zero=!0}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!0);this.min=a(t)?Math.max(0,t):null,this.max=a(e)?Math.max(0,e):null,this.options.beginAtZero&&(this._zero=!0),this._zero&&this.min!==this._suggestedMin&&!a(this._userMin)&&(this.min=t===vo(this.min,0)?vo(this.min,-1):vo(this.min,0)),this.handleTickRangeOptions()}handleTickRangeOptions(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let i=this.min,s=this.max;const n=e=>i=t?i:e,o=t=>s=e?s:t;i===s&&(i<=0?(n(1),o(10)):(n(vo(i,-1)),o(vo(s,1)))),i<=0&&n(vo(s,-1)),s<=0&&o(vo(i,1)),this.min=i,this.max=s}buildTicks(){const t=this.options,e=ko({min:this._userMin,max:this._userMax},this);return"ticks"===t.bounds&&j(e,this,"value"),t.reverse?(e.reverse(),this.start=this.max,this.end=this.min):(this.start=this.min,this.end=this.max),e}getLabelForValue(t){return void 0===t?"0":ne(t,this.chart.options.locale,this.options.ticks.format)}configure(){const t=this.min;super.configure(),this._startValue=z(t),this._valueRange=z(this.max)-z(t)}getPixelForValue(t){return void 0!==t&&0!==t||(t=this.min),null===t||isNaN(t)?NaN:this.getPixelForDecimal(t===this.min?0:(z(t)-this._startValue)/this._valueRange)}getValueForPixel(t){const e=this.getDecimalForPixel(t);return Math.pow(10,this._startValue+e*this._valueRange)}}function Po(t){const e=t.ticks;if(e.display&&t.display){const t=ki(e.backdropPadding);return l(e.font&&e.font.size,ue.font.size)+t.height}return 0}function Do(t,e,i,s,n){return t===s||t===n?{start:e-i/2,end:e+i/2}:t<s||t>n?{start:e-i,end:e}:{start:e,end:e+i}}function Co(t){const e={l:t.left+t._padding.left,r:t.right-t._padding.right,t:t.top+t._padding.top,b:t.bottom-t._padding.bottom},i=Object.assign({},e),s=[],o=[],a=t._pointLabels.length,r=t.options.pointLabels,l=r.centerPointLabels?C/a:0;for(let u=0;u<a;u++){const a=r.setContext(t.getPointLabelContext(u));o[u]=a.padding;const f=t.getPointPosition(u,t.drawingArea+o[u],l),g=Si(a.font),p=(h=t.ctx,c=g,d=n(d=t._pointLabels[u])?d:[d],{w:Oe(h,c.string,d),h:d.length*c.lineHeight});s[u]=p;const m=G(t.getIndexAngle(u)+l),x=Math.round(Y(m));Oo(i,e,m,Do(x,f.x,p.w,0,180),Do(x,f.y,p.h,90,270))}var h,c,d;t.setCenterPoint(e.l-i.l,i.r-e.r,e.t-i.t,i.b-e.b),t._pointLabelItems=function(t,e,i){const s=[],n=t._pointLabels.length,o=t.options,{centerPointLabels:a,display:r}=o.pointLabels,l={extra:Po(o)/2,additionalAngle:a?C/n:0};let h;for(let o=0;o<n;o++){l.padding=i[o],l.size=e[o];const n=Ao(t,o,l);s.push(n),"auto"===r&&(n.visible=To(n,h),n.visible&&(h=n))}return s}(t,s,o)}function Oo(t,e,i,s,n){const o=Math.abs(Math.sin(i)),a=Math.abs(Math.cos(i));let r=0,l=0;s.start<e.l?(r=(e.l-s.start)/o,t.l=Math.min(t.l,e.l-r)):s.end>e.r&&(r=(s.end-e.r)/o,t.r=Math.max(t.r,e.r+r)),n.start<e.t?(l=(e.t-n.start)/a,t.t=Math.min(t.t,e.t-l)):n.end>e.b&&(l=(n.end-e.b)/a,t.b=Math.max(t.b,e.b+l))}function Ao(t,e,i){const s=t.drawingArea,{extra:n,additionalAngle:o,padding:a,size:r}=i,l=t.getPointPosition(e,s+n+a,o),h=Math.round(Y(G(l.angle+E))),c=function(t,e,i){90===i||270===i?t-=e/2:(i>270||i<90)&&(t-=e);return t}(l.y,r.h,h),d=function(t){if(0===t||180===t)return"center";if(t<180)return"left";return"right"}(h),u=function(t,e,i){"right"===i?t-=e:"center"===i&&(t-=e/2);return t}(l.x,r.w,d);return{visible:!0,x:l.x,y:c,textAlign:d,left:u,top:c,right:u+r.w,bottom:c+r.h}}function To(t,e){if(!e)return!0;const{left:i,top:s,right:n,bottom:o}=t;return!(Re({x:i,y:s},e)||Re({x:i,y:o},e)||Re({x:n,y:s},e)||Re({x:n,y:o},e))}function Lo(t,e,i){const{left:n,top:o,right:a,bottom:r}=i,{backdropColor:l}=e;if(!s(l)){const i=wi(e.borderRadius),s=ki(e.backdropPadding);t.fillStyle=l;const h=n-s.left,c=o-s.top,d=a-n+s.width,u=r-o+s.height;Object.values(i).some((t=>0!==t))?(t.beginPath(),He(t,{x:h,y:c,w:d,h:u,radius:i}),t.fill()):t.fillRect(h,c,d,u)}}function Eo(t,e,i,s){const{ctx:n}=t;if(i)n.arc(t.xCenter,t.yCenter,e,0,O);else{let i=t.getPointPosition(0,e);n.moveTo(i.x,i.y);for(let o=1;o<s;o++)i=t.getPointPosition(o,e),n.lineTo(i.x,i.y)}}class Ro extends bo{static id="radialLinear";static defaults={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,lineWidth:1,borderDash:[],borderDashOffset:0},grid:{circular:!1},startAngle:0,ticks:{showLabelBackdrop:!0,callback:ae.formatters.numeric},pointLabels:{backdropColor:void 0,backdropPadding:2,display:!0,font:{size:10},callback:t=>t,padding:5,centerPointLabels:!1}};static defaultRoutes={"angleLines.color":"borderColor","pointLabels.color":"color","ticks.color":"color"};static descriptors={angleLines:{_fallback:"grid"}};constructor(t){super(t),this.xCenter=void 0,this.yCenter=void 0,this.drawingArea=void 0,this._pointLabels=[],this._pointLabelItems=[]}setDimensions(){const t=this._padding=ki(Po(this.options)/2),e=this.width=this.maxWidth-t.width,i=this.height=this.maxHeight-t.height;this.xCenter=Math.floor(this.left+e/2+t.left),this.yCenter=Math.floor(this.top+i/2+t.top),this.drawingArea=Math.floor(Math.min(e,i)/2)}determineDataLimits(){const{min:t,max:e}=this.getMinMax(!1);this.min=a(t)&&!isNaN(t)?t:0,this.max=a(e)&&!isNaN(e)?e:0,this.handleTickRangeOptions()}computeTickLimit(){return Math.ceil(this.drawingArea/Po(this.options))}generateTickLabels(t){bo.prototype.generateTickLabels.call(this,t),this._pointLabels=this.getLabels().map(((t,e)=>{const i=d(this.options.pointLabels.callback,[t,e],this);return i||0===i?i:""})).filter(((t,e)=>this.chart.getDataVisibility(e)))}fit(){const t=this.options;t.display&&t.pointLabels.display?Co(this):this.setCenterPoint(0,0,0,0)}setCenterPoint(t,e,i,s){this.xCenter+=Math.floor((t-e)/2),this.yCenter+=Math.floor((i-s)/2),this.drawingArea-=Math.min(this.drawingArea/2,Math.max(t,e,i,s))}getIndexAngle(t){return G(t*(O/(this._pointLabels.length||1))+$(this.options.startAngle||0))}getDistanceFromCenterForValue(t){if(s(t))return NaN;const e=this.drawingArea/(this.max-this.min);return this.options.reverse?(this.max-t)*e:(t-this.min)*e}getValueForDistanceFromCenter(t){if(s(t))return NaN;const e=t/(this.drawingArea/(this.max-this.min));return this.options.reverse?this.max-e:this.min+e}getPointLabelContext(t){const e=this._pointLabels||[];if(t>=0&&t<e.length){const i=e[t];return function(t,e,i){return Ci(t,{label:i,index:e,type:"pointLabel"})}(this.getContext(),t,i)}}getPointPosition(t,e,i=0){const s=this.getIndexAngle(t)-E+i;return{x:Math.cos(s)*e+this.xCenter,y:Math.sin(s)*e+this.yCenter,angle:s}}getPointPositionForValue(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))}getBasePosition(t){return this.getPointPositionForValue(t||0,this.getBaseValue())}getPointLabelPosition(t){const{left:e,top:i,right:s,bottom:n}=this._pointLabelItems[t];return{left:e,top:i,right:s,bottom:n}}drawBackground(){const{backgroundColor:t,grid:{circular:e}}=this.options;if(t){const i=this.ctx;i.save(),i.beginPath(),Eo(this,this.getDistanceFromCenterForValue(this._endValue),e,this._pointLabels.length),i.closePath(),i.fillStyle=t,i.fill(),i.restore()}}drawGrid(){const t=this.ctx,e=this.options,{angleLines:i,grid:s,border:n}=e,o=this._pointLabels.length;let a,r,l;if(e.pointLabels.display&&function(t,e){const{ctx:i,options:{pointLabels:s}}=t;for(let n=e-1;n>=0;n--){const e=t._pointLabelItems[n];if(!e.visible)continue;const o=s.setContext(t.getPointLabelContext(n));Lo(i,o,e);const a=Si(o.font),{x:r,y:l,textAlign:h}=e;Ne(i,t._pointLabels[n],r,l+a.lineHeight/2,a,{color:o.color,textAlign:h,textBaseline:"middle"})}}(this,o),s.display&&this.ticks.forEach(((t,e)=>{if(0!==e||0===e&&this.min<0){r=this.getDistanceFromCenterForValue(t.value);const i=this.getContext(e),a=s.setContext(i),l=n.setContext(i);!function(t,e,i,s,n){const o=t.ctx,a=e.circular,{color:r,lineWidth:l}=e;!a&&!s||!r||!l||i<0||(o.save(),o.strokeStyle=r,o.lineWidth=l,o.setLineDash(n.dash||[]),o.lineDashOffset=n.dashOffset,o.beginPath(),Eo(t,i,a,s),o.closePath(),o.stroke(),o.restore())}(this,a,r,o,l)}})),i.display){for(t.save(),a=o-1;a>=0;a--){const s=i.setContext(this.getPointLabelContext(a)),{color:n,lineWidth:o}=s;o&&n&&(t.lineWidth=o,t.strokeStyle=n,t.setLineDash(s.borderDash),t.lineDashOffset=s.borderDashOffset,r=this.getDistanceFromCenterForValue(e.reverse?this.min:this.max),l=this.getPointPosition(a,r),t.beginPath(),t.moveTo(this.xCenter,this.yCenter),t.lineTo(l.x,l.y),t.stroke())}t.restore()}}drawBorder(){}drawLabels(){const t=this.ctx,e=this.options,i=e.ticks;if(!i.display)return;const s=this.getIndexAngle(0);let n,o;t.save(),t.translate(this.xCenter,this.yCenter),t.rotate(s),t.textAlign="center",t.textBaseline="middle",this.ticks.forEach(((s,a)=>{if(0===a&&this.min>=0&&!e.reverse)return;const r=i.setContext(this.getContext(a)),l=Si(r.font);if(n=this.getDistanceFromCenterForValue(this.ticks[a].value),r.showLabelBackdrop){t.font=l.string,o=t.measureText(s.label).width,t.fillStyle=r.backdropColor;const e=ki(r.backdropPadding);t.fillRect(-o/2-e.left,-n-l.size/2-e.top,o+e.width,l.size+e.height)}Ne(t,s.label,0,-n,l,{color:r.color,strokeColor:r.textStrokeColor,strokeWidth:r.textStrokeWidth})})),t.restore()}drawTitle(){}}const Io={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},zo=Object.keys(Io);function Fo(t,e){return t-e}function Vo(t,e){if(s(e))return null;const i=t._adapter,{parser:n,round:o,isoWeekday:r}=t._parseOpts;let l=e;return"function"==typeof n&&(l=n(l)),a(l)||(l="string"==typeof n?i.parse(l,n):i.parse(l)),null===l?null:(o&&(l="week"!==o||!N(r)&&!0!==r?i.startOf(l,o):i.startOf(l,"isoWeek",r)),+l)}function Bo(t,e,i,s){const n=zo.length;for(let o=zo.indexOf(t);o<n-1;++o){const t=Io[zo[o]],n=t.steps?t.steps:Number.MAX_SAFE_INTEGER;if(t.common&&Math.ceil((i-e)/(n*t.size))<=s)return zo[o]}return zo[n-1]}function Wo(t,e,i){if(i){if(i.length){const{lo:s,hi:n}=et(i,e);t[i[s]>=e?i[s]:i[n]]=!0}}else t[e]=!0}function No(t,e,i){const s=[],n={},o=e.length;let a,r;for(a=0;a<o;++a)r=e[a],n[r]=a,s.push({value:r,major:!1});return 0!==o&&i?function(t,e,i,s){const n=t._adapter,o=+n.startOf(e[0].value,s),a=e[e.length-1].value;let r,l;for(r=o;r<=a;r=+n.add(r,1,s))l=i[r],l>=0&&(e[l].major=!0);return e}(t,s,n,i):s}class Ho extends tn{static id="time";static defaults={bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{source:"auto",callback:!1,major:{enabled:!1}}};constructor(t){super(t),this._cache={data:[],labels:[],all:[]},this._unit="day",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(t,e={}){const i=t.time||(t.time={}),s=this._adapter=new In._date(t.adapters.date);s.init(e),b(i.displayFormats,s.formats()),this._parseOpts={parser:i.parser,round:i.round,isoWeekday:i.isoWeekday},super.init(t),this._normalized=e.normalized}parse(t,e){return void 0===t?null:Vo(this,t)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const t=this.options,e=this._adapter,i=t.time.unit||"day";let{min:s,max:n,minDefined:o,maxDefined:r}=this.getUserBounds();function l(t){o||isNaN(t.min)||(s=Math.min(s,t.min)),r||isNaN(t.max)||(n=Math.max(n,t.max))}o&&r||(l(this._getLabelBounds()),"ticks"===t.bounds&&"labels"===t.ticks.source||l(this.getMinMax(!1))),s=a(s)&&!isNaN(s)?s:+e.startOf(Date.now(),i),n=a(n)&&!isNaN(n)?n:+e.endOf(Date.now(),i)+1,this.min=Math.min(s,n-1),this.max=Math.max(s+1,n)}_getLabelBounds(){const t=this.getLabelTimestamps();let e=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;return t.length&&(e=t[0],i=t[t.length-1]),{min:e,max:i}}buildTicks(){const t=this.options,e=t.time,i=t.ticks,s="labels"===i.source?this.getLabelTimestamps():this._generate();"ticks"===t.bounds&&s.length&&(this.min=this._userMin||s[0],this.max=this._userMax||s[s.length-1]);const n=this.min,o=nt(s,n,this.max);return this._unit=e.unit||(i.autoSkip?Bo(e.minUnit,this.min,this.max,this._getLabelCapacity(n)):function(t,e,i,s,n){for(let o=zo.length-1;o>=zo.indexOf(i);o--){const i=zo[o];if(Io[i].common&&t._adapter.diff(n,s,i)>=e-1)return i}return zo[i?zo.indexOf(i):0]}(this,o.length,e.minUnit,this.min,this.max)),this._majorUnit=i.major.enabled&&"year"!==this._unit?function(t){for(let e=zo.indexOf(t)+1,i=zo.length;e<i;++e)if(Io[zo[e]].common)return zo[e]}(this._unit):void 0,this.initOffsets(s),t.reverse&&o.reverse(),No(this,o,this._majorUnit)}afterAutoSkip(){this.options.offsetAfterAutoskip&&this.initOffsets(this.ticks.map((t=>+t.value)))}initOffsets(t=[]){let e,i,s=0,n=0;this.options.offset&&t.length&&(e=this.getDecimalForValue(t[0]),s=1===t.length?1-e:(this.getDecimalForValue(t[1])-e)/2,i=this.getDecimalForValue(t[t.length-1]),n=1===t.length?i:(i-this.getDecimalForValue(t[t.length-2]))/2);const o=t.length<3?.5:.25;s=Z(s,0,o),n=Z(n,0,o),this._offsets={start:s,end:n,factor:1/(s+1+n)}}_generate(){const t=this._adapter,e=this.min,i=this.max,s=this.options,n=s.time,o=n.unit||Bo(n.minUnit,e,i,this._getLabelCapacity(e)),a=l(s.ticks.stepSize,1),r="week"===o&&n.isoWeekday,h=N(r)||!0===r,c={};let d,u,f=e;if(h&&(f=+t.startOf(f,"isoWeek",r)),f=+t.startOf(f,h?"day":o),t.diff(i,e,o)>1e5*a)throw new Error(e+" and "+i+" are too far apart with stepSize of "+a+" "+o);const g="data"===s.ticks.source&&this.getDataTimestamps();for(d=f,u=0;d<i;d=+t.add(d,a,o),u++)Wo(c,d,g);return d!==i&&"ticks"!==s.bounds&&1!==u||Wo(c,d,g),Object.keys(c).sort(Fo).map((t=>+t))}getLabelForValue(t){const e=this._adapter,i=this.options.time;return i.tooltipFormat?e.format(t,i.tooltipFormat):e.format(t,i.displayFormats.datetime)}format(t,e){const i=this.options.time.displayFormats,s=this._unit,n=e||i[s];return this._adapter.format(t,n)}_tickFormatFunction(t,e,i,s){const n=this.options,o=n.ticks.callback;if(o)return d(o,[t,e,i],this);const a=n.time.displayFormats,r=this._unit,l=this._majorUnit,h=r&&a[r],c=l&&a[l],u=i[e],f=l&&c&&u&&u.major;return this._adapter.format(t,s||(f?c:h))}generateTickLabels(t){let e,i,s;for(e=0,i=t.length;e<i;++e)s=t[e],s.label=this._tickFormatFunction(s.value,e,t)}getDecimalForValue(t){return null===t?NaN:(t-this.min)/(this.max-this.min)}getPixelForValue(t){const e=this._offsets,i=this.getDecimalForValue(t);return this.getPixelForDecimal((e.start+i)*e.factor)}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return this.min+i*(this.max-this.min)}_getLabelSize(t){const e=this.options.ticks,i=this.ctx.measureText(t).width,s=$(this.isHorizontal()?e.maxRotation:e.minRotation),n=Math.cos(s),o=Math.sin(s),a=this._resolveTickFontOptions(0).size;return{w:i*n+a*o,h:i*o+a*n}}_getLabelCapacity(t){const e=this.options.time,i=e.displayFormats,s=i[e.unit]||i.millisecond,n=this._tickFormatFunction(t,0,No(this,[t],this._majorUnit),s),o=this._getLabelSize(n),a=Math.floor(this.isHorizontal()?this.width/o.w:this.height/o.h)-1;return a>0?a:1}getDataTimestamps(){let t,e,i=this._cache.data||[];if(i.length)return i;const s=this.getMatchingVisibleMetas();if(this._normalized&&s.length)return this._cache.data=s[0].controller.getAllParsedValues(this);for(t=0,e=s.length;t<e;++t)i=i.concat(s[t].controller.getAllParsedValues(this));return this._cache.data=this.normalize(i)}getLabelTimestamps(){const t=this._cache.labels||[];let e,i;if(t.length)return t;const s=this.getLabels();for(e=0,i=s.length;e<i;++e)t.push(Vo(this,s[e]));return this._cache.labels=this._normalized?t:this.normalize(t)}normalize(t){return lt(t.sort(Fo))}}function jo(t,e,i){let s,n,o,a,r=0,l=t.length-1;i?(e>=t[r].pos&&e<=t[l].pos&&({lo:r,hi:l}=it(t,"pos",e)),({pos:s,time:o}=t[r]),({pos:n,time:a}=t[l])):(e>=t[r].time&&e<=t[l].time&&({lo:r,hi:l}=it(t,"time",e)),({time:s,pos:o}=t[r]),({time:n,pos:a}=t[l]));const h=n-s;return h?o+(a-o)*(e-s)/h:o}var $o=Object.freeze({__proto__:null,CategoryScale:class extends tn{static id="category";static defaults={ticks:{callback:mo}};constructor(t){super(t),this._startValue=void 0,this._valueRange=0,this._addedLabels=[]}init(t){const e=this._addedLabels;if(e.length){const t=this.getLabels();for(const{index:i,label:s}of e)t[i]===s&&t.splice(i,1);this._addedLabels=[]}super.init(t)}parse(t,e){if(s(t))return null;const i=this.getLabels();return((t,e)=>null===t?null:Z(Math.round(t),0,e))(e=isFinite(e)&&i[e]===t?e:po(i,t,l(e,t),this._addedLabels),i.length-1)}determineDataLimits(){const{minDefined:t,maxDefined:e}=this.getUserBounds();let{min:i,max:s}=this.getMinMax(!0);"ticks"===this.options.bounds&&(t||(i=0),e||(s=this.getLabels().length-1)),this.min=i,this.max=s}buildTicks(){const t=this.min,e=this.max,i=this.options.offset,s=[];let n=this.getLabels();n=0===t&&e===n.length-1?n:n.slice(t,e+1),this._valueRange=Math.max(n.length-(i?0:1),1),this._startValue=this.min-(i?.5:0);for(let i=t;i<=e;i++)s.push({value:i});return s}getLabelForValue(t){return mo.call(this,t)}configure(){super.configure(),this.isHorizontal()||(this._reversePixels=!this._reversePixels)}getPixelForValue(t){return"number"!=typeof t&&(t=this.parse(t)),null===t?NaN:this.getPixelForDecimal((t-this._startValue)/this._valueRange)}getPixelForTick(t){const e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t].value)}getValueForPixel(t){return Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange)}getBasePixel(){return this.bottom}},LinearScale:_o,LogarithmicScale:So,RadialLinearScale:Ro,TimeScale:Ho,TimeSeriesScale:class extends Ho{static id="timeseries";static defaults=Ho.defaults;constructor(t){super(t),this._table=[],this._minPos=void 0,this._tableRange=void 0}initOffsets(){const t=this._getTimestampsForTable(),e=this._table=this.buildLookupTable(t);this._minPos=jo(e,this.min),this._tableRange=jo(e,this.max)-this._minPos,super.initOffsets(t)}buildLookupTable(t){const{min:e,max:i}=this,s=[],n=[];let o,a,r,l,h;for(o=0,a=t.length;o<a;++o)l=t[o],l>=e&&l<=i&&s.push(l);if(s.length<2)return[{time:e,pos:0},{time:i,pos:1}];for(o=0,a=s.length;o<a;++o)h=s[o+1],r=s[o-1],l=s[o],Math.round((h+r)/2)!==l&&n.push({time:l,pos:o/(a-1)});return n}_generate(){const t=this.min,e=this.max;let i=super.getDataTimestamps();return i.includes(t)&&i.length||i.splice(0,0,t),i.includes(e)&&1!==i.length||i.push(e),i.sort(((t,e)=>t-e))}_getTimestampsForTable(){let t=this._cache.all||[];if(t.length)return t;const e=this.getDataTimestamps(),i=this.getLabelTimestamps();return t=e.length&&i.length?this.normalize(e.concat(i)):e.length?e:i,t=this._cache.all=t,t}getDecimalForValue(t){return(jo(this._table,t)-this._minPos)/this._tableRange}getValueForPixel(t){const e=this._offsets,i=this.getDecimalForPixel(t)/e.factor-e.end;return jo(this._table,i*this._tableRange+this._minPos,!0)}}});const Yo=["rgb(54, 162, 235)","rgb(255, 99, 132)","rgb(255, 159, 64)","rgb(255, 205, 86)","rgb(75, 192, 192)","rgb(153, 102, 255)","rgb(201, 203, 207)"],Uo=Yo.map((t=>t.replace("rgb(","rgba(").replace(")",", 0.5)")));function Xo(t){return Yo[t%Yo.length]}function qo(t){return Uo[t%Uo.length]}function Ko(t){let e=0;return(i,s)=>{const n=t.getDatasetMeta(s).controller;n instanceof $n?e=function(t,e){return t.backgroundColor=t.data.map((()=>Xo(e++))),e}(i,e):n instanceof Yn?e=function(t,e){return t.backgroundColor=t.data.map((()=>qo(e++))),e}(i,e):n&&(e=function(t,e){return t.borderColor=Xo(e),t.backgroundColor=qo(e),++e}(i,e))}}function Go(t){let e;for(e in t)if(t[e].borderColor||t[e].backgroundColor)return!0;return!1}var Jo={id:"colors",defaults:{enabled:!0,forceOverride:!1},beforeLayout(t,e,i){if(!i.enabled)return;const{data:{datasets:s},options:n}=t.config,{elements:o}=n,a=Go(s)||(r=n)&&(r.borderColor||r.backgroundColor)||o&&Go(o)||"rgba(0,0,0,0.1)"!==ue.borderColor||"rgba(0,0,0,0.1)"!==ue.backgroundColor;var r;if(!i.forceOverride&&a)return;const l=Ko(t);s.forEach(l)}};function Zo(t){if(t._decimated){const e=t._data;delete t._decimated,delete t._data,Object.defineProperty(t,"data",{configurable:!0,enumerable:!0,writable:!0,value:e})}}function Qo(t){t.data.datasets.forEach((t=>{Zo(t)}))}var ta={id:"decimation",defaults:{algorithm:"min-max",enabled:!1},beforeElementsUpdate:(t,e,i)=>{if(!i.enabled)return void Qo(t);const n=t.width;t.data.datasets.forEach(((e,o)=>{const{_data:a,indexAxis:r}=e,l=t.getDatasetMeta(o),h=a||e.data;if("y"===Pi([r,t.options.indexAxis]))return;if(!l.controller.supportsDecimation)return;const c=t.scales[l.xAxisID];if("linear"!==c.type&&"time"!==c.type)return;if(t.options.parsing)return;let{start:d,count:u}=function(t,e){const i=e.length;let s,n=0;const{iScale:o}=t,{min:a,max:r,minDefined:l,maxDefined:h}=o.getUserBounds();return l&&(n=Z(it(e,o.axis,a).lo,0,i-1)),s=h?Z(it(e,o.axis,r).hi+1,n,i)-n:i-n,{start:n,count:s}}(l,h);if(u<=(i.threshold||4*n))return void Zo(e);let f;switch(s(a)&&(e._data=h,delete e.data,Object.defineProperty(e,"data",{configurable:!0,enumerable:!0,get:function(){return this._decimated},set:function(t){this._data=t}})),i.algorithm){case"lttb":f=function(t,e,i,s,n){const o=n.samples||s;if(o>=i)return t.slice(e,e+i);const a=[],r=(i-2)/(o-2);let l=0;const h=e+i-1;let c,d,u,f,g,p=e;for(a[l++]=t[p],c=0;c<o-2;c++){let s,n=0,o=0;const h=Math.floor((c+1)*r)+1+e,m=Math.min(Math.floor((c+2)*r)+1,i)+e,x=m-h;for(s=h;s<m;s++)n+=t[s].x,o+=t[s].y;n/=x,o/=x;const b=Math.floor(c*r)+1+e,_=Math.min(Math.floor((c+1)*r)+1,i)+e,{x:y,y:v}=t[p];for(u=f=-1,s=b;s<_;s++)f=.5*Math.abs((y-n)*(t[s].y-v)-(y-t[s].x)*(o-v)),f>u&&(u=f,d=t[s],g=s);a[l++]=d,p=g}return a[l++]=t[h],a}(h,d,u,n,i);break;case"min-max":f=function(t,e,i,n){let o,a,r,l,h,c,d,u,f,g,p=0,m=0;const x=[],b=e+i-1,_=t[e].x,y=t[b].x-_;for(o=e;o<e+i;++o){a=t[o],r=(a.x-_)/y*n,l=a.y;const e=0|r;if(e===h)l<f?(f=l,c=o):l>g&&(g=l,d=o),p=(m*p+a.x)/++m;else{const i=o-1;if(!s(c)&&!s(d)){const e=Math.min(c,d),s=Math.max(c,d);e!==u&&e!==i&&x.push({...t[e],x:p}),s!==u&&s!==i&&x.push({...t[s],x:p})}o>0&&i!==u&&x.push(t[i]),x.push(a),h=e,m=0,f=g=l,c=d=u=o}}return x}(h,d,u,n);break;default:throw new Error(`Unsupported decimation algorithm '${i.algorithm}'`)}e._decimated=f}))},destroy(t){Qo(t)}};function ea(t,e,i,s){if(s)return;let n=e[t],o=i[t];return"angle"===t&&(n=G(n),o=G(o)),{property:t,start:n,end:o}}function ia(t,e,i){for(;e>t;e--){const t=i[e];if(!isNaN(t.x)&&!isNaN(t.y))break}return e}function sa(t,e,i,s){return t&&e?s(t[i],e[i]):t?t[i]:e?e[i]:0}function na(t,e){let i=[],s=!1;return n(t)?(s=!0,i=t):i=function(t,e){const{x:i=null,y:s=null}=t||{},n=e.points,o=[];return e.segments.forEach((({start:t,end:e})=>{e=ia(t,e,n);const a=n[t],r=n[e];null!==s?(o.push({x:a.x,y:s}),o.push({x:r.x,y:s})):null!==i&&(o.push({x:i,y:a.y}),o.push({x:i,y:r.y}))})),o}(t,e),i.length?new oo({points:i,options:{tension:0},_loop:s,_fullLoop:s}):null}function oa(t){return t&&!1!==t.fill}function aa(t,e,i){let s=t[e].fill;const n=[e];let o;if(!i)return s;for(;!1!==s&&-1===n.indexOf(s);){if(!a(s))return s;if(o=t[s],!o)return!1;if(o.visible)return s;n.push(s),s=o.fill}return!1}function ra(t,e,i){const s=function(t){const e=t.options,i=e.fill;let s=l(i&&i.target,i);void 0===s&&(s=!!e.backgroundColor);if(!1===s||null===s)return!1;if(!0===s)return"origin";return s}(t);if(o(s))return!isNaN(s.value)&&s;let n=parseFloat(s);return a(n)&&Math.floor(n)===n?function(t,e,i,s){"-"!==t&&"+"!==t||(i=e+i);if(i===e||i<0||i>=s)return!1;return i}(s[0],e,n,i):["origin","start","end","stack","shape"].indexOf(s)>=0&&s}function la(t,e,i){const s=[];for(let n=0;n<i.length;n++){const o=i[n],{first:a,last:r,point:l}=ha(o,e,"x");if(!(!l||a&&r))if(a)s.unshift(l);else if(t.push(l),!r)break}t.push(...s)}function ha(t,e,i){const s=t.interpolate(e,i);if(!s)return{};const n=s[i],o=t.segments,a=t.points;let r=!1,l=!1;for(let t=0;t<o.length;t++){const e=o[t],s=a[e.start][i],h=a[e.end][i];if(tt(n,s,h)){r=n===s,l=n===h;break}}return{first:r,last:l,point:s}}class ca{constructor(t){this.x=t.x,this.y=t.y,this.radius=t.radius}pathSegment(t,e,i){const{x:s,y:n,radius:o}=this;return e=e||{start:0,end:O},t.arc(s,n,o,e.end,e.start,!0),!i.bounds}interpolate(t){const{x:e,y:i,radius:s}=this,n=t.angle;return{x:e+Math.cos(n)*s,y:i+Math.sin(n)*s,angle:n}}}function da(t){const{chart:e,fill:i,line:s}=t;if(a(i))return function(t,e){const i=t.getDatasetMeta(e),s=i&&t.isDatasetVisible(e);return s?i.dataset:null}(e,i);if("stack"===i)return function(t){const{scale:e,index:i,line:s}=t,n=[],o=s.segments,a=s.points,r=function(t,e){const i=[],s=t.getMatchingVisibleMetas("line");for(let t=0;t<s.length;t++){const n=s[t];if(n.index===e)break;n.hidden||i.unshift(n.dataset)}return i}(e,i);r.push(na({x:null,y:e.bottom},s));for(let t=0;t<o.length;t++){const e=o[t];for(let t=e.start;t<=e.end;t++)la(n,a[t],r)}return new oo({points:n,options:{}})}(t);if("shape"===i)return!0;const n=function(t){const e=t.scale||{};if(e.getPointPositionForValue)return function(t){const{scale:e,fill:i}=t,s=e.options,n=e.getLabels().length,a=s.reverse?e.max:e.min,r=function(t,e,i){let s;return s="start"===t?i:"end"===t?e.options.reverse?e.min:e.max:o(t)?t.value:e.getBaseValue(),s}(i,e,a),l=[];if(s.grid.circular){const t=e.getPointPositionForValue(0,a);return new ca({x:t.x,y:t.y,radius:e.getDistanceFromCenterForValue(r)})}for(let t=0;t<n;++t)l.push(e.getPointPositionForValue(t,r));return l}(t);return function(t){const{scale:e={},fill:i}=t,s=function(t,e){let i=null;return"start"===t?i=e.bottom:"end"===t?i=e.top:o(t)?i=e.getPixelForValue(t.value):e.getBasePixel&&(i=e.getBasePixel()),i}(i,e);if(a(s)){const t=e.isHorizontal();return{x:t?s:null,y:t?null:s}}return null}(t)}(t);return n instanceof ca?n:na(n,s)}function ua(t,e,i){const s=da(e),{chart:n,index:o,line:a,scale:r,axis:l}=e,h=a.options,c=h.fill,d=h.backgroundColor,{above:u=d,below:f=d}=c||{},g=n.getDatasetMeta(o),p=Ni(n,g);s&&a.points.length&&(Ie(t,i),function(t,e){const{line:i,target:s,above:n,below:o,area:a,scale:r,clip:l}=e,h=i._loop?"angle":e.axis;t.save();let c=o;o!==n&&("x"===h?(fa(t,s,a.top),pa(t,{line:i,target:s,color:n,scale:r,property:h,clip:l}),t.restore(),t.save(),fa(t,s,a.bottom)):"y"===h&&(ga(t,s,a.left),pa(t,{line:i,target:s,color:o,scale:r,property:h,clip:l}),t.restore(),t.save(),ga(t,s,a.right),c=n));pa(t,{line:i,target:s,color:c,scale:r,property:h,clip:l}),t.restore()}(t,{line:a,target:s,above:u,below:f,area:i,scale:r,axis:l,clip:p}),ze(t))}function fa(t,e,i){const{segments:s,points:n}=e;let o=!0,a=!1;t.beginPath();for(const r of s){const{start:s,end:l}=r,h=n[s],c=n[ia(s,l,n)];o?(t.moveTo(h.x,h.y),o=!1):(t.lineTo(h.x,i),t.lineTo(h.x,h.y)),a=!!e.pathSegment(t,r,{move:a}),a?t.closePath():t.lineTo(c.x,i)}t.lineTo(e.first().x,i),t.closePath(),t.clip()}function ga(t,e,i){const{segments:s,points:n}=e;let o=!0,a=!1;t.beginPath();for(const r of s){const{start:s,end:l}=r,h=n[s],c=n[ia(s,l,n)];o?(t.moveTo(h.x,h.y),o=!1):(t.lineTo(i,h.y),t.lineTo(h.x,h.y)),a=!!e.pathSegment(t,r,{move:a}),a?t.closePath():t.lineTo(i,c.y)}t.lineTo(i,e.first().y),t.closePath(),t.clip()}function pa(t,e){const{line:i,target:s,property:n,color:o,scale:a,clip:r}=e,l=function(t,e,i){const s=t.segments,n=t.points,o=e.points,a=[];for(const t of s){let{start:s,end:r}=t;r=ia(s,r,n);const l=ea(i,n[s],n[r],t.loop);if(!e.segments){a.push({source:t,target:l,start:n[s],end:n[r]});continue}const h=Ii(e,l);for(const e of h){const s=ea(i,o[e.start],o[e.end],e.loop),r=Ri(t,n,s);for(const t of r)a.push({source:t,target:e,start:{[i]:sa(l,s,"start",Math.max)},end:{[i]:sa(l,s,"end",Math.min)}})}}return a}(i,s,n);for(const{source:e,target:h,start:c,end:d}of l){const{style:{backgroundColor:l=o}={}}=e,u=!0!==s;t.save(),t.fillStyle=l,ma(t,a,r,u&&ea(n,c,d)),t.beginPath();const f=!!i.pathSegment(t,e);let g;if(u){f?t.closePath():xa(t,s,d,n);const e=!!s.pathSegment(t,h,{move:f,reverse:!0});g=f&&e,g||xa(t,s,c,n)}t.closePath(),t.fill(g?"evenodd":"nonzero"),t.restore()}}function ma(t,e,i,s){const n=e.chart.chartArea,{property:o,start:a,end:r}=s||{};if("x"===o||"y"===o){let e,s,l,h;"x"===o?(e=a,s=n.top,l=r,h=n.bottom):(e=n.left,s=a,l=n.right,h=r),t.beginPath(),i&&(e=Math.max(e,i.left),l=Math.min(l,i.right),s=Math.max(s,i.top),h=Math.min(h,i.bottom)),t.rect(e,s,l-e,h-s),t.clip()}}function xa(t,e,i,s){const n=e.interpolate(i,s);n&&t.lineTo(n.x,n.y)}var ba={id:"filler",afterDatasetsUpdate(t,e,i){const s=(t.data.datasets||[]).length,n=[];let o,a,r,l;for(a=0;a<s;++a)o=t.getDatasetMeta(a),r=o.dataset,l=null,r&&r.options&&r instanceof oo&&(l={visible:t.isDatasetVisible(a),index:a,fill:ra(r,a,s),chart:t,axis:o.controller.options.indexAxis,scale:o.vScale,line:r}),o.$filler=l,n.push(l);for(a=0;a<s;++a)l=n[a],l&&!1!==l.fill&&(l.fill=aa(n,a,i.propagate))},beforeDraw(t,e,i){const s="beforeDraw"===i.drawTime,n=t.getSortedVisibleDatasetMetas(),o=t.chartArea;for(let e=n.length-1;e>=0;--e){const i=n[e].$filler;i&&(i.line.updateControlPoints(o,i.axis),s&&i.fill&&ua(t.ctx,i,o))}},beforeDatasetsDraw(t,e,i){if("beforeDatasetsDraw"!==i.drawTime)return;const s=t.getSortedVisibleDatasetMetas();for(let e=s.length-1;e>=0;--e){const i=s[e].$filler;oa(i)&&ua(t.ctx,i,t.chartArea)}},beforeDatasetDraw(t,e,i){const s=e.meta.$filler;oa(s)&&"beforeDatasetDraw"===i.drawTime&&ua(t.ctx,s,t.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const _a=(t,e)=>{let{boxHeight:i=e,boxWidth:s=e}=t;return t.usePointStyle&&(i=Math.min(i,e),s=t.pointStyleWidth||Math.min(s,e)),{boxWidth:s,boxHeight:i,itemHeight:Math.max(e,i)}};class ya extends $s{constructor(t){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e,i){this.maxWidth=t,this.maxHeight=e,this._margins=i,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const t=this.options.labels||{};let e=d(t.generateLabels,[this.chart],this)||[];t.filter&&(e=e.filter((e=>t.filter(e,this.chart.data)))),t.sort&&(e=e.sort(((e,i)=>t.sort(e,i,this.chart.data)))),this.options.reverse&&e.reverse(),this.legendItems=e}fit(){const{options:t,ctx:e}=this;if(!t.display)return void(this.width=this.height=0);const i=t.labels,s=Si(i.font),n=s.size,o=this._computeTitleHeight(),{boxWidth:a,itemHeight:r}=_a(i,n);let l,h;e.font=s.string,this.isHorizontal()?(l=this.maxWidth,h=this._fitRows(o,n,a,r)+10):(h=this.maxHeight,l=this._fitCols(o,s,a,r)+10),this.width=Math.min(l,t.maxWidth||this.maxWidth),this.height=Math.min(h,t.maxHeight||this.maxHeight)}_fitRows(t,e,i,s){const{ctx:n,maxWidth:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.lineWidths=[0],h=s+a;let c=t;n.textAlign="left",n.textBaseline="middle";let d=-1,u=-h;return this.legendItems.forEach(((t,f)=>{const g=i+e/2+n.measureText(t.text).width;(0===f||l[l.length-1]+g+2*a>o)&&(c+=h,l[l.length-(f>0?0:1)]=0,u+=h,d++),r[f]={left:0,top:u,row:d,width:g,height:s},l[l.length-1]+=g+a})),c}_fitCols(t,e,i,s){const{ctx:n,maxHeight:o,options:{labels:{padding:a}}}=this,r=this.legendHitBoxes=[],l=this.columnSizes=[],h=o-t;let c=a,d=0,u=0,f=0,g=0;return this.legendItems.forEach(((t,o)=>{const{itemWidth:p,itemHeight:m}=function(t,e,i,s,n){const o=function(t,e,i,s){let n=t.text;n&&"string"!=typeof n&&(n=n.reduce(((t,e)=>t.length>e.length?t:e)));return e+i.size/2+s.measureText(n).width}(s,t,e,i),a=function(t,e,i){let s=t;"string"!=typeof e.text&&(s=va(e,i));return s}(n,s,e.lineHeight);return{itemWidth:o,itemHeight:a}}(i,e,n,t,s);o>0&&u+m+2*a>h&&(c+=d+a,l.push({width:d,height:u}),f+=d+a,g++,d=u=0),r[o]={left:f,top:u,col:g,width:p,height:m},d=Math.max(d,p),u+=m+a})),c+=d,l.push({width:d,height:u}),c}adjustHitBoxes(){if(!this.options.display)return;const t=this._computeTitleHeight(),{legendHitBoxes:e,options:{align:i,labels:{padding:s},rtl:n}}=this,o=Oi(n,this.left,this.width);if(this.isHorizontal()){let n=0,a=ft(i,this.left+s,this.right-this.lineWidths[n]);for(const r of e)n!==r.row&&(n=r.row,a=ft(i,this.left+s,this.right-this.lineWidths[n])),r.top+=this.top+t+s,r.left=o.leftForLtr(o.x(a),r.width),a+=r.width+s}else{let n=0,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height);for(const r of e)r.col!==n&&(n=r.col,a=ft(i,this.top+t+s,this.bottom-this.columnSizes[n].height)),r.top=a,r.left+=this.left+s,r.left=o.leftForLtr(o.x(r.left),r.width),a+=r.height+s}}isHorizontal(){return"top"===this.options.position||"bottom"===this.options.position}draw(){if(this.options.display){const t=this.ctx;Ie(t,this),this._draw(),ze(t)}}_draw(){const{options:t,columnSizes:e,lineWidths:i,ctx:s}=this,{align:n,labels:o}=t,a=ue.color,r=Oi(t.rtl,this.left,this.width),h=Si(o.font),{padding:c}=o,d=h.size,u=d/2;let f;this.drawTitle(),s.textAlign=r.textAlign("left"),s.textBaseline="middle",s.lineWidth=.5,s.font=h.string;const{boxWidth:g,boxHeight:p,itemHeight:m}=_a(o,d),x=this.isHorizontal(),b=this._computeTitleHeight();f=x?{x:ft(n,this.left+c,this.right-i[0]),y:this.top+c+b,line:0}:{x:this.left+c,y:ft(n,this.top+b+c,this.bottom-e[0].height),line:0},Ai(this.ctx,t.textDirection);const _=m+c;this.legendItems.forEach(((y,v)=>{s.strokeStyle=y.fontColor,s.fillStyle=y.fontColor;const M=s.measureText(y.text).width,w=r.textAlign(y.textAlign||(y.textAlign=o.textAlign)),k=g+u+M;let S=f.x,P=f.y;r.setWidth(this.width),x?v>0&&S+k+c>this.right&&(P=f.y+=_,f.line++,S=f.x=ft(n,this.left+c,this.right-i[f.line])):v>0&&P+_>this.bottom&&(S=f.x=S+e[f.line].width+c,f.line++,P=f.y=ft(n,this.top+b+c,this.bottom-e[f.line].height));if(function(t,e,i){if(isNaN(g)||g<=0||isNaN(p)||p<0)return;s.save();const n=l(i.lineWidth,1);if(s.fillStyle=l(i.fillStyle,a),s.lineCap=l(i.lineCap,"butt"),s.lineDashOffset=l(i.lineDashOffset,0),s.lineJoin=l(i.lineJoin,"miter"),s.lineWidth=n,s.strokeStyle=l(i.strokeStyle,a),s.setLineDash(l(i.lineDash,[])),o.usePointStyle){const a={radius:p*Math.SQRT2/2,pointStyle:i.pointStyle,rotation:i.rotation,borderWidth:n},l=r.xPlus(t,g/2);Ee(s,a,l,e+u,o.pointStyleWidth&&g)}else{const o=e+Math.max((d-p)/2,0),a=r.leftForLtr(t,g),l=wi(i.borderRadius);s.beginPath(),Object.values(l).some((t=>0!==t))?He(s,{x:a,y:o,w:g,h:p,radius:l}):s.rect(a,o,g,p),s.fill(),0!==n&&s.stroke()}s.restore()}(r.x(S),P,y),S=gt(w,S+g+u,x?S+k:this.right,t.rtl),function(t,e,i){Ne(s,i.text,t,e+m/2,h,{strikethrough:i.hidden,textAlign:r.textAlign(i.textAlign)})}(r.x(S),P,y),x)f.x+=k+c;else if("string"!=typeof y.text){const t=h.lineHeight;f.y+=va(y,t)+c}else f.y+=_})),Ti(this.ctx,t.textDirection)}drawTitle(){const t=this.options,e=t.title,i=Si(e.font),s=ki(e.padding);if(!e.display)return;const n=Oi(t.rtl,this.left,this.width),o=this.ctx,a=e.position,r=i.size/2,l=s.top+r;let h,c=this.left,d=this.width;if(this.isHorizontal())d=Math.max(...this.lineWidths),h=this.top+l,c=ft(t.align,c,this.right-d);else{const e=this.columnSizes.reduce(((t,e)=>Math.max(t,e.height)),0);h=l+ft(t.align,this.top,this.bottom-e-t.labels.padding-this._computeTitleHeight())}const u=ft(a,c,c+d);o.textAlign=n.textAlign(ut(a)),o.textBaseline="middle",o.strokeStyle=e.color,o.fillStyle=e.color,o.font=i.string,Ne(o,e.text,u,h,i)}_computeTitleHeight(){const t=this.options.title,e=Si(t.font),i=ki(t.padding);return t.display?e.lineHeight+i.height:0}_getLegendItemAt(t,e){let i,s,n;if(tt(t,this.left,this.right)&&tt(e,this.top,this.bottom))for(n=this.legendHitBoxes,i=0;i<n.length;++i)if(s=n[i],tt(t,s.left,s.left+s.width)&&tt(e,s.top,s.top+s.height))return this.legendItems[i];return null}handleEvent(t){const e=this.options;if(!function(t,e){if(("mousemove"===t||"mouseout"===t)&&(e.onHover||e.onLeave))return!0;if(e.onClick&&("click"===t||"mouseup"===t))return!0;return!1}(t.type,e))return;const i=this._getLegendItemAt(t.x,t.y);if("mousemove"===t.type||"mouseout"===t.type){const o=this._hoveredItem,a=(n=i,null!==(s=o)&&null!==n&&s.datasetIndex===n.datasetIndex&&s.index===n.index);o&&!a&&d(e.onLeave,[t,o,this],this),this._hoveredItem=i,i&&!a&&d(e.onHover,[t,i,this],this)}else i&&d(e.onClick,[t,i,this],this);var s,n}}function va(t,e){return e*(t.text?t.text.length:0)}var Ma={id:"legend",_element:ya,start(t,e,i){const s=t.legend=new ya({ctx:t.ctx,options:i,chart:t});ls.configure(t,s,i),ls.addBox(t,s)},stop(t){ls.removeBox(t,t.legend),delete t.legend},beforeUpdate(t,e,i){const s=t.legend;ls.configure(t,s,i),s.options=i},afterUpdate(t){const e=t.legend;e.buildLabels(),e.adjustHitBoxes()},afterEvent(t,e){e.replay||t.legend.handleEvent(e.event)},defaults:{display:!0,position:"top",align:"center",fullSize:!0,reverse:!1,weight:1e3,onClick(t,e,i){const s=e.datasetIndex,n=i.chart;n.isDatasetVisible(s)?(n.hide(s),e.hidden=!0):(n.show(s),e.hidden=!1)},onHover:null,onLeave:null,labels:{color:t=>t.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:i,pointStyle:s,textAlign:n,color:o,useBorderRadius:a,borderRadius:r}}=t.legend.options;return t._getSortedDatasetMetas().map((t=>{const l=t.controller.getStyle(i?0:void 0),h=ki(l.borderWidth);return{text:e[t.index].label,fillStyle:l.backgroundColor,fontColor:o,hidden:!t.visible,lineCap:l.borderCapStyle,lineDash:l.borderDash,lineDashOffset:l.borderDashOffset,lineJoin:l.borderJoinStyle,lineWidth:(h.width+h.height)/4,strokeStyle:l.borderColor,pointStyle:s||l.pointStyle,rotation:l.rotation,textAlign:n||l.textAlign,borderRadius:a&&(r||l.borderRadius),datasetIndex:t.index}}),this)}},title:{color:t=>t.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:t=>!t.startsWith("on"),labels:{_scriptable:t=>!["generateLabels","filter","sort"].includes(t)}}};class wa extends $s{constructor(t){super(),this.chart=t.chart,this.options=t.options,this.ctx=t.ctx,this._padding=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(t,e){const i=this.options;if(this.left=0,this.top=0,!i.display)return void(this.width=this.height=this.right=this.bottom=0);this.width=this.right=t,this.height=this.bottom=e;const s=n(i.text)?i.text.length:1;this._padding=ki(i.padding);const o=s*Si(i.font).lineHeight+this._padding.height;this.isHorizontal()?this.height=o:this.width=o}isHorizontal(){const t=this.options.position;return"top"===t||"bottom"===t}_drawArgs(t){const{top:e,left:i,bottom:s,right:n,options:o}=this,a=o.align;let r,l,h,c=0;return this.isHorizontal()?(l=ft(a,i,n),h=e+t,r=n-i):("left"===o.position?(l=i+t,h=ft(a,s,e),c=-.5*C):(l=n-t,h=ft(a,e,s),c=.5*C),r=s-e),{titleX:l,titleY:h,maxWidth:r,rotation:c}}draw(){const t=this.ctx,e=this.options;if(!e.display)return;const i=Si(e.font),s=i.lineHeight/2+this._padding.top,{titleX:n,titleY:o,maxWidth:a,rotation:r}=this._drawArgs(s);Ne(t,e.text,0,0,i,{color:e.color,maxWidth:a,rotation:r,textAlign:ut(e.align),textBaseline:"middle",translation:[n,o]})}}var ka={id:"title",_element:wa,start(t,e,i){!function(t,e){const i=new wa({ctx:t.ctx,options:e,chart:t});ls.configure(t,i,e),ls.addBox(t,i),t.titleBlock=i}(t,i)},stop(t){const e=t.titleBlock;ls.removeBox(t,e),delete t.titleBlock},beforeUpdate(t,e,i){const s=t.titleBlock;ls.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"bold"},fullSize:!0,padding:10,position:"top",text:"",weight:2e3},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Sa=new WeakMap;var Pa={id:"subtitle",start(t,e,i){const s=new wa({ctx:t.ctx,options:i,chart:t});ls.configure(t,s,i),ls.addBox(t,s),Sa.set(t,s)},stop(t){ls.removeBox(t,Sa.get(t)),Sa.delete(t)},beforeUpdate(t,e,i){const s=Sa.get(t);ls.configure(t,s,i),s.options=i},defaults:{align:"center",display:!1,font:{weight:"normal"},fullSize:!0,padding:0,position:"top",text:"",weight:1500},defaultRoutes:{color:"color"},descriptors:{_scriptable:!0,_indexable:!1}};const Da={average(t){if(!t.length)return!1;let e,i,s=new Set,n=0,o=0;for(e=0,i=t.length;e<i;++e){const i=t[e].element;if(i&&i.hasValue()){const t=i.tooltipPosition();s.add(t.x),n+=t.y,++o}}if(0===o||0===s.size)return!1;return{x:[...s].reduce(((t,e)=>t+e))/s.size,y:n/o}},nearest(t,e){if(!t.length)return!1;let i,s,n,o=e.x,a=e.y,r=Number.POSITIVE_INFINITY;for(i=0,s=t.length;i<s;++i){const s=t[i].element;if(s&&s.hasValue()){const t=q(e,s.getCenterPoint());t<r&&(r=t,n=s)}}if(n){const t=n.tooltipPosition();o=t.x,a=t.y}return{x:o,y:a}}};function Ca(t,e){return e&&(n(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function Oa(t){return("string"==typeof t||t instanceof String)&&t.indexOf("\n")>-1?t.split("\n"):t}function Aa(t,e){const{element:i,datasetIndex:s,index:n}=e,o=t.getDatasetMeta(s).controller,{label:a,value:r}=o.getLabelAndValue(n);return{chart:t,label:a,parsed:o.getParsed(n),raw:t.data.datasets[s].data[n],formattedValue:r,dataset:o.getDataset(),dataIndex:n,datasetIndex:s,element:i}}function Ta(t,e){const i=t.chart.ctx,{body:s,footer:n,title:o}=t,{boxWidth:a,boxHeight:r}=e,l=Si(e.bodyFont),h=Si(e.titleFont),c=Si(e.footerFont),d=o.length,f=n.length,g=s.length,p=ki(e.padding);let m=p.height,x=0,b=s.reduce(((t,e)=>t+e.before.length+e.lines.length+e.after.length),0);if(b+=t.beforeBody.length+t.afterBody.length,d&&(m+=d*h.lineHeight+(d-1)*e.titleSpacing+e.titleMarginBottom),b){m+=g*(e.displayColors?Math.max(r,l.lineHeight):l.lineHeight)+(b-g)*l.lineHeight+(b-1)*e.bodySpacing}f&&(m+=e.footerMarginTop+f*c.lineHeight+(f-1)*e.footerSpacing);let _=0;const y=function(t){x=Math.max(x,i.measureText(t).width+_)};return i.save(),i.font=h.string,u(t.title,y),i.font=l.string,u(t.beforeBody.concat(t.afterBody),y),_=e.displayColors?a+2+e.boxPadding:0,u(s,(t=>{u(t.before,y),u(t.lines,y),u(t.after,y)})),_=0,i.font=c.string,u(t.footer,y),i.restore(),x+=p.width,{width:x,height:m}}function La(t,e,i,s){const{x:n,width:o}=i,{width:a,chartArea:{left:r,right:l}}=t;let h="center";return"center"===s?h=n<=(r+l)/2?"left":"right":n<=o/2?h="left":n>=a-o/2&&(h="right"),function(t,e,i,s){const{x:n,width:o}=s,a=i.caretSize+i.caretPadding;return"left"===t&&n+o+a>e.width||"right"===t&&n-o-a<0||void 0}(h,t,e,i)&&(h="center"),h}function Ea(t,e,i){const s=i.yAlign||e.yAlign||function(t,e){const{y:i,height:s}=e;return i<s/2?"top":i>t.height-s/2?"bottom":"center"}(t,i);return{xAlign:i.xAlign||e.xAlign||La(t,e,i,s),yAlign:s}}function Ra(t,e,i,s){const{caretSize:n,caretPadding:o,cornerRadius:a}=t,{xAlign:r,yAlign:l}=i,h=n+o,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=wi(a);let g=function(t,e){let{x:i,width:s}=t;return"right"===e?i-=s:"center"===e&&(i-=s/2),i}(e,r);const p=function(t,e,i){let{y:s,height:n}=t;return"top"===e?s+=i:s-="bottom"===e?n+i:n/2,s}(e,l,h);return"center"===l?"left"===r?g+=h:"right"===r&&(g-=h):"left"===r?g-=Math.max(c,u)+n:"right"===r&&(g+=Math.max(d,f)+n),{x:Z(g,0,s.width-e.width),y:Z(p,0,s.height-e.height)}}function Ia(t,e,i){const s=ki(i.padding);return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-s.right:t.x+s.left}function za(t){return Ca([],Oa(t))}function Fa(t,e){const i=e&&e.dataset&&e.dataset.tooltip&&e.dataset.tooltip.callbacks;return i?t.override(i):t}const Va={beforeTitle:e,title(t){if(t.length>0){const e=t[0],i=e.chart.data.labels,s=i?i.length:0;if(this&&this.options&&"dataset"===this.options.mode)return e.dataset.label||"";if(e.label)return e.label;if(s>0&&e.dataIndex<s)return i[e.dataIndex]}return""},afterTitle:e,beforeBody:e,beforeLabel:e,label(t){if(this&&this.options&&"dataset"===this.options.mode)return t.label+": "+t.formattedValue||t.formattedValue;let e=t.dataset.label||"";e&&(e+=": ");const i=t.formattedValue;return s(i)||(e+=i),e},labelColor(t){const e=t.chart.getDatasetMeta(t.datasetIndex).controller.getStyle(t.dataIndex);return{borderColor:e.borderColor,backgroundColor:e.backgroundColor,borderWidth:e.borderWidth,borderDash:e.borderDash,borderDashOffset:e.borderDashOffset,borderRadius:0}},labelTextColor(){return this.options.bodyColor},labelPointStyle(t){const e=t.chart.getDatasetMeta(t.datasetIndex).controller.getStyle(t.dataIndex);return{pointStyle:e.pointStyle,rotation:e.rotation}},afterLabel:e,afterBody:e,beforeFooter:e,footer:e,afterFooter:e};function Ba(t,e,i,s){const n=t[e].call(i,s);return void 0===n?Va[e].call(i,s):n}class Wa extends $s{static positioners=Da;constructor(t){super(),this.opacity=0,this._active=[],this._eventPosition=void 0,this._size=void 0,this._cachedAnimations=void 0,this._tooltipItems=[],this.$animations=void 0,this.$context=void 0,this.chart=t.chart,this.options=t.options,this.dataPoints=void 0,this.title=void 0,this.beforeBody=void 0,this.body=void 0,this.afterBody=void 0,this.footer=void 0,this.xAlign=void 0,this.yAlign=void 0,this.x=void 0,this.y=void 0,this.height=void 0,this.width=void 0,this.caretX=void 0,this.caretY=void 0,this.labelColors=void 0,this.labelPointStyles=void 0,this.labelTextColors=void 0}initialize(t){this.options=t,this._cachedAnimations=void 0,this.$context=void 0}_resolveAnimations(){const t=this._cachedAnimations;if(t)return t;const e=this.chart,i=this.options.setContext(this.getContext()),s=i.enabled&&e.options.animation&&i.animations,n=new Ts(this.chart,s);return s._cacheable&&(this._cachedAnimations=Object.freeze(n)),n}getContext(){return this.$context||(this.$context=(t=this.chart.getContext(),e=this,i=this._tooltipItems,Ci(t,{tooltip:e,tooltipItems:i,type:"tooltip"})));var t,e,i}getTitle(t,e){const{callbacks:i}=e,s=Ba(i,"beforeTitle",this,t),n=Ba(i,"title",this,t),o=Ba(i,"afterTitle",this,t);let a=[];return a=Ca(a,Oa(s)),a=Ca(a,Oa(n)),a=Ca(a,Oa(o)),a}getBeforeBody(t,e){return za(Ba(e.callbacks,"beforeBody",this,t))}getBody(t,e){const{callbacks:i}=e,s=[];return u(t,(t=>{const e={before:[],lines:[],after:[]},n=Fa(i,t);Ca(e.before,Oa(Ba(n,"beforeLabel",this,t))),Ca(e.lines,Ba(n,"label",this,t)),Ca(e.after,Oa(Ba(n,"afterLabel",this,t))),s.push(e)})),s}getAfterBody(t,e){return za(Ba(e.callbacks,"afterBody",this,t))}getFooter(t,e){const{callbacks:i}=e,s=Ba(i,"beforeFooter",this,t),n=Ba(i,"footer",this,t),o=Ba(i,"afterFooter",this,t);let a=[];return a=Ca(a,Oa(s)),a=Ca(a,Oa(n)),a=Ca(a,Oa(o)),a}_createItems(t){const e=this._active,i=this.chart.data,s=[],n=[],o=[];let a,r,l=[];for(a=0,r=e.length;a<r;++a)l.push(Aa(this.chart,e[a]));return t.filter&&(l=l.filter(((e,s,n)=>t.filter(e,s,n,i)))),t.itemSort&&(l=l.sort(((e,s)=>t.itemSort(e,s,i)))),u(l,(e=>{const i=Fa(t.callbacks,e);s.push(Ba(i,"labelColor",this,e)),n.push(Ba(i,"labelPointStyle",this,e)),o.push(Ba(i,"labelTextColor",this,e))})),this.labelColors=s,this.labelPointStyles=n,this.labelTextColors=o,this.dataPoints=l,l}update(t,e){const i=this.options.setContext(this.getContext()),s=this._active;let n,o=[];if(s.length){const t=Da[i.position].call(this,s,this._eventPosition);o=this._createItems(i),this.title=this.getTitle(o,i),this.beforeBody=this.getBeforeBody(o,i),this.body=this.getBody(o,i),this.afterBody=this.getAfterBody(o,i),this.footer=this.getFooter(o,i);const e=this._size=Ta(this,i),a=Object.assign({},t,e),r=Ea(this.chart,i,a),l=Ra(i,a,r,this.chart);this.xAlign=r.xAlign,this.yAlign=r.yAlign,n={opacity:1,x:l.x,y:l.y,width:e.width,height:e.height,caretX:t.x,caretY:t.y}}else 0!==this.opacity&&(n={opacity:0});this._tooltipItems=o,this.$context=void 0,n&&this._resolveAnimations().update(this,n),t&&i.external&&i.external.call(this,{chart:this.chart,tooltip:this,replay:e})}drawCaret(t,e,i,s){const n=this.getCaretPosition(t,i,s);e.lineTo(n.x1,n.y1),e.lineTo(n.x2,n.y2),e.lineTo(n.x3,n.y3)}getCaretPosition(t,e,i){const{xAlign:s,yAlign:n}=this,{caretSize:o,cornerRadius:a}=i,{topLeft:r,topRight:l,bottomLeft:h,bottomRight:c}=wi(a),{x:d,y:u}=t,{width:f,height:g}=e;let p,m,x,b,_,y;return"center"===n?(_=u+g/2,"left"===s?(p=d,m=p-o,b=_+o,y=_-o):(p=d+f,m=p+o,b=_-o,y=_+o),x=p):(m="left"===s?d+Math.max(r,h)+o:"right"===s?d+f-Math.max(l,c)-o:this.caretX,"top"===n?(b=u,_=b-o,p=m-o,x=m+o):(b=u+g,_=b+o,p=m+o,x=m-o),y=b),{x1:p,x2:m,x3:x,y1:b,y2:_,y3:y}}drawTitle(t,e,i){const s=this.title,n=s.length;let o,a,r;if(n){const l=Oi(i.rtl,this.x,this.width);for(t.x=Ia(this,i.titleAlign,i),e.textAlign=l.textAlign(i.titleAlign),e.textBaseline="middle",o=Si(i.titleFont),a=i.titleSpacing,e.fillStyle=i.titleColor,e.font=o.string,r=0;r<n;++r)e.fillText(s[r],l.x(t.x),t.y+o.lineHeight/2),t.y+=o.lineHeight+a,r+1===n&&(t.y+=i.titleMarginBottom-a)}}_drawColorBox(t,e,i,s,n){const a=this.labelColors[i],r=this.labelPointStyles[i],{boxHeight:l,boxWidth:h}=n,c=Si(n.bodyFont),d=Ia(this,"left",n),u=s.x(d),f=l<c.lineHeight?(c.lineHeight-l)/2:0,g=e.y+f;if(n.usePointStyle){const e={radius:Math.min(h,l)/2,pointStyle:r.pointStyle,rotation:r.rotation,borderWidth:1},i=s.leftForLtr(u,h)+h/2,o=g+l/2;t.strokeStyle=n.multiKeyBackground,t.fillStyle=n.multiKeyBackground,Le(t,e,i,o),t.strokeStyle=a.borderColor,t.fillStyle=a.backgroundColor,Le(t,e,i,o)}else{t.lineWidth=o(a.borderWidth)?Math.max(...Object.values(a.borderWidth)):a.borderWidth||1,t.strokeStyle=a.borderColor,t.setLineDash(a.borderDash||[]),t.lineDashOffset=a.borderDashOffset||0;const e=s.leftForLtr(u,h),i=s.leftForLtr(s.xPlus(u,1),h-2),r=wi(a.borderRadius);Object.values(r).some((t=>0!==t))?(t.beginPath(),t.fillStyle=n.multiKeyBackground,He(t,{x:e,y:g,w:h,h:l,radius:r}),t.fill(),t.stroke(),t.fillStyle=a.backgroundColor,t.beginPath(),He(t,{x:i,y:g+1,w:h-2,h:l-2,radius:r}),t.fill()):(t.fillStyle=n.multiKeyBackground,t.fillRect(e,g,h,l),t.strokeRect(e,g,h,l),t.fillStyle=a.backgroundColor,t.fillRect(i,g+1,h-2,l-2))}t.fillStyle=this.labelTextColors[i]}drawBody(t,e,i){const{body:s}=this,{bodySpacing:n,bodyAlign:o,displayColors:a,boxHeight:r,boxWidth:l,boxPadding:h}=i,c=Si(i.bodyFont);let d=c.lineHeight,f=0;const g=Oi(i.rtl,this.x,this.width),p=function(i){e.fillText(i,g.x(t.x+f),t.y+d/2),t.y+=d+n},m=g.textAlign(o);let x,b,_,y,v,M,w;for(e.textAlign=o,e.textBaseline="middle",e.font=c.string,t.x=Ia(this,m,i),e.fillStyle=i.bodyColor,u(this.beforeBody,p),f=a&&"right"!==m?"center"===o?l/2+h:l+2+h:0,y=0,M=s.length;y<M;++y){for(x=s[y],b=this.labelTextColors[y],e.fillStyle=b,u(x.before,p),_=x.lines,a&&_.length&&(this._drawColorBox(e,t,y,g,i),d=Math.max(c.lineHeight,r)),v=0,w=_.length;v<w;++v)p(_[v]),d=c.lineHeight;u(x.after,p)}f=0,d=c.lineHeight,u(this.afterBody,p),t.y-=n}drawFooter(t,e,i){const s=this.footer,n=s.length;let o,a;if(n){const r=Oi(i.rtl,this.x,this.width);for(t.x=Ia(this,i.footerAlign,i),t.y+=i.footerMarginTop,e.textAlign=r.textAlign(i.footerAlign),e.textBaseline="middle",o=Si(i.footerFont),e.fillStyle=i.footerColor,e.font=o.string,a=0;a<n;++a)e.fillText(s[a],r.x(t.x),t.y+o.lineHeight/2),t.y+=o.lineHeight+i.footerSpacing}}drawBackground(t,e,i,s){const{xAlign:n,yAlign:o}=this,{x:a,y:r}=t,{width:l,height:h}=i,{topLeft:c,topRight:d,bottomLeft:u,bottomRight:f}=wi(s.cornerRadius);e.fillStyle=s.backgroundColor,e.strokeStyle=s.borderColor,e.lineWidth=s.borderWidth,e.beginPath(),e.moveTo(a+c,r),"top"===o&&this.drawCaret(t,e,i,s),e.lineTo(a+l-d,r),e.quadraticCurveTo(a+l,r,a+l,r+d),"center"===o&&"right"===n&&this.drawCaret(t,e,i,s),e.lineTo(a+l,r+h-f),e.quadraticCurveTo(a+l,r+h,a+l-f,r+h),"bottom"===o&&this.drawCaret(t,e,i,s),e.lineTo(a+u,r+h),e.quadraticCurveTo(a,r+h,a,r+h-u),"center"===o&&"left"===n&&this.drawCaret(t,e,i,s),e.lineTo(a,r+c),e.quadraticCurveTo(a,r,a+c,r),e.closePath(),e.fill(),s.borderWidth>0&&e.stroke()}_updateAnimationTarget(t){const e=this.chart,i=this.$animations,s=i&&i.x,n=i&&i.y;if(s||n){const i=Da[t.position].call(this,this._active,this._eventPosition);if(!i)return;const o=this._size=Ta(this,t),a=Object.assign({},i,this._size),r=Ea(e,t,a),l=Ra(t,a,r,e);s._to===l.x&&n._to===l.y||(this.xAlign=r.xAlign,this.yAlign=r.yAlign,this.width=o.width,this.height=o.height,this.caretX=i.x,this.caretY=i.y,this._resolveAnimations().update(this,l))}}_willRender(){return!!this.opacity}draw(t){const e=this.options.setContext(this.getContext());let i=this.opacity;if(!i)return;this._updateAnimationTarget(e);const s={width:this.width,height:this.height},n={x:this.x,y:this.y};i=Math.abs(i)<.001?0:i;const o=ki(e.padding),a=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;e.enabled&&a&&(t.save(),t.globalAlpha=i,this.drawBackground(n,t,s,e),Ai(t,e.textDirection),n.y+=o.top,this.drawTitle(n,t,e),this.drawBody(n,t,e),this.drawFooter(n,t,e),Ti(t,e.textDirection),t.restore())}getActiveElements(){return this._active||[]}setActiveElements(t,e){const i=this._active,s=t.map((({datasetIndex:t,index:e})=>{const i=this.chart.getDatasetMeta(t);if(!i)throw new Error("Cannot find a dataset at index "+t);return{datasetIndex:t,element:i.data[e],index:e}})),n=!f(i,s),o=this._positionChanged(s,e);(n||o)&&(this._active=s,this._eventPosition=e,this._ignoreReplayEvents=!0,this.update(!0))}handleEvent(t,e,i=!0){if(e&&this._ignoreReplayEvents)return!1;this._ignoreReplayEvents=!1;const s=this.options,n=this._active||[],o=this._getActiveElements(t,n,e,i),a=this._positionChanged(o,t),r=e||!f(o,n)||a;return r&&(this._active=o,(s.enabled||s.external)&&(this._eventPosition={x:t.x,y:t.y},this.update(!0,e))),r}_getActiveElements(t,e,i,s){const n=this.options;if("mouseout"===t.type)return[];if(!s)return e.filter((t=>this.chart.data.datasets[t.datasetIndex]&&void 0!==this.chart.getDatasetMeta(t.datasetIndex).controller.getParsed(t.index)));const o=this.chart.getElementsAtEventForMode(t,n.mode,n,i);return n.reverse&&o.reverse(),o}_positionChanged(t,e){const{caretX:i,caretY:s,options:n}=this,o=Da[n.position].call(this,t,e);return!1!==o&&(i!==o.x||s!==o.y)}}var Na={id:"tooltip",_element:Wa,positioners:Da,afterInit(t,e,i){i&&(t.tooltip=new Wa({chart:t,options:i}))},beforeUpdate(t,e,i){t.tooltip&&t.tooltip.initialize(i)},reset(t,e,i){t.tooltip&&t.tooltip.initialize(i)},afterDraw(t){const e=t.tooltip;if(e&&e._willRender()){const i={tooltip:e};if(!1===t.notifyPlugins("beforeTooltipDraw",{...i,cancelable:!0}))return;e.draw(t.ctx),t.notifyPlugins("afterTooltipDraw",i)}},afterEvent(t,e){if(t.tooltip){const i=e.replay;t.tooltip.handleEvent(e.event,i,e.inChartArea)&&(e.changed=!0)}},defaults:{enabled:!0,external:null,position:"average",backgroundColor:"rgba(0,0,0,0.8)",titleColor:"#fff",titleFont:{weight:"bold"},titleSpacing:2,titleMarginBottom:6,titleAlign:"left",bodyColor:"#fff",bodySpacing:2,bodyFont:{},bodyAlign:"left",footerColor:"#fff",footerSpacing:2,footerMarginTop:6,footerFont:{weight:"bold"},footerAlign:"left",padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(t,e)=>e.bodyFont.size,boxWidth:(t,e)=>e.bodyFont.size,multiKeyBackground:"#fff",displayColors:!0,boxPadding:0,borderColor:"rgba(0,0,0,0)",borderWidth:0,animation:{duration:400,easing:"easeOutQuart"},animations:{numbers:{type:"number",properties:["x","y","width","height","caretX","caretY"]},opacity:{easing:"linear",duration:200}},callbacks:Va},defaultRoutes:{bodyFont:"font",footerFont:"font",titleFont:"font"},descriptors:{_scriptable:t=>"filter"!==t&&"itemSort"!==t&&"external"!==t,_indexable:!1,callbacks:{_scriptable:!1,_indexable:!1},animation:{_fallback:!1},animations:{_fallback:"animation"}},additionalOptionScopes:["interaction"]};return Tn.register(Un,$o,go,t),Tn.helpers={...Hi},Tn._adapters=In,Tn.Animation=As,Tn.Animations=Ts,Tn.animator=bt,Tn.controllers=nn.controllers.items,Tn.DatasetController=js,Tn.Element=$s,Tn.elements=go,Tn.Interaction=Ki,Tn.layouts=ls,Tn.platforms=Ds,Tn.Scale=tn,Tn.Ticks=ae,Object.assign(Tn,Un,$o,go,t,Ds),Tn.Chart=Tn,"undefined"!=typeof window&&(window.Chart=Tn),Tn}));
     14//# sourceMappingURL=chart.umd.min.js.map
  • post-views-counter/tags/1.8.0/blocks/most-viewed-posts/index.asset.php

    r3470602 r3734102  
    1 <?php return array('dependencies' => array('wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => '68a082639217d4af5888');
     1<?php return array('dependencies' => array('wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-server-side-render'), 'version' => '57f13f02c4751ea3e260');
  • post-views-counter/tags/1.8.0/blocks/most-viewed-posts/index.js

    r3470602 r3734102  
    1 !function(e,t,n,o,r,l){"use strict";var i=function(){return t.createElement(n.Spinner)},a=function(){return t.createElement(n.Notice,{status:"error",isDismissible:!1},o.__("Something went wrong. Try again or refresh the page.","post-views-counter"))};function s(e){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function c(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,o)}return n}function u(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?c(Object(n),!0).forEach(function(t){p(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):c(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function p(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=s(e)||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var o=n.call(e,t);if("object"!=s(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==s(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function m(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var o,r,l,i,a=[],s=!0,c=!1;try{if(l=(n=n.call(e)).next,0===t);else for(;!(s=(o=l.call(n)).done)&&(a.push(o.value),a.length!==t);s=!0);}catch(u){c=!0,r=u}finally{try{if(!s&&null!=n.return&&(i=n.return(),Object(i)!==i))return}finally{if(c)throw r}}return a}}(e,t)||function(e,t){if(e){if("string"==typeof e)return b(e,t);var n={}.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?b(e,t):void 0}}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function b(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=Array(t);n<t;n++)o[n]=e[n];return o}var v=t.createElement(function(e){var n=e.viewBox,o=void 0===n?"0 0 24 24":n,r=e.children;return t.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:o,width:24,height:24,fill:"currentColor"},r)},{viewBox:"0 0 16 16"},t.createElement("path",{d:"M12 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h1a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1ZM6.5 6a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1V6ZM2 9a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V9Z"}));e.registerBlockType("post-views-counter/most-viewed-posts",{icon:v,edit:function(e){var s=e.attributes,c=e.setAttributes,b=s.title,v=s.postTypes,w=s.period,f=s.numberOfPosts,d=s.noPostsMessage,y=s.order,h=s.listType,_=s.displayPostViews,g=s.displayPostExcerpt,E=s.displayPostAuthor,C=s.displayPostThumbnail,P=s.thumbnailSize,x=m(t.useState(v||pvcBlockEditorData.postTypesKeys),2),O=x[0],B=x[1];return wp.element.createElement(wp.element.Fragment,null,wp.element.createElement(r.InspectorControls,null,wp.element.createElement(n.PanelBody,{title:o.__("Settings","post-views-counter")},wp.element.createElement(n.TextControl,{__nextHasNoMarginBottom:!0,label:o.__("Title","post-views-counter"),value:b,onChange:function(e){return c({title:e})},help:o.__("Enter empty text to hide title.","post-views-counter")}),wp.element.createElement(n.BaseControl,{__nextHasNoMarginBottom:!0,label:o.__("Post types","post-views-counter")},Object.keys(pvcBlockEditorData.postTypes).map(function(e){return wp.element.createElement(n.CheckboxControl,{key:e,label:pvcBlockEditorData.postTypes[e],checked:O[e],onChange:function(t){var n=u({},v);n[e]=t,B(function(t){return u(u({},t),{},p({},e,!t[e]))}),c({postTypes:n})}})})),wp.element.createElement(n.SelectControl,{__nextHasNoMarginBottom:!0,disabled:1===pvcBlockEditorData.periods.length,label:o.__("Views period","post-views-counter"),value:w,options:pvcBlockEditorData.periods,onChange:function(e){return c({period:e})}}),wp.element.createElement(n.TextControl,{__nextHasNoMarginBottom:!0,label:o.__("Number of posts to show","post-views-counter"),value:f,onChange:function(e){return c({numberOfPosts:Number(e)})}}),wp.element.createElement(n.TextControl,{__nextHasNoMarginBottom:!0,label:o.__("No posts message","post-views-counter"),value:d,onChange:function(e){return c({noPostsMessage:e})}}),wp.element.createElement(n.RadioControl,{label:o.__("Order","post-views-counter"),selected:y,options:[{label:o.__("Ascending","post-views-counter"),value:"asc"},{label:o.__("Descending","post-views-counter"),value:"desc"}],onChange:function(e){return c({order:e})}}),wp.element.createElement(n.RadioControl,{label:o.__("Display style","post-views-counter"),selected:h,options:[{label:o.__("Unordered list","post-views-counter"),value:"unordered"},{label:o.__("Ordered list","post-views-counter"),value:"ordered"}],onChange:function(e){return c({listType:e})}}),wp.element.createElement(n.BaseControl,{__nextHasNoMarginBottom:!0,label:o.__("Display Data","post-views-counter")},wp.element.createElement(n.CheckboxControl,{__nextHasNoMarginBottom:!0,label:o.__("Post views","post-views-counter"),checked:_,onChange:function(e){return c({displayPostViews:e})}}),wp.element.createElement(n.CheckboxControl,{__nextHasNoMarginBottom:!0,label:o.__("Post excerpt","post-views-counter"),checked:g,onChange:function(e){return c({displayPostExcerpt:e})}}),wp.element.createElement(n.CheckboxControl,{__nextHasNoMarginBottom:!0,label:o.__("Post author","post-views-counter"),checked:E,onChange:function(e){return c({displayPostAuthor:e})}}),wp.element.createElement(n.CheckboxControl,{__nextHasNoMarginBottom:!0,label:o.__("Post thumbnail","post-views-counter"),checked:C,onChange:function(e){return c({displayPostThumbnail:e})}}),C&&wp.element.createElement(n.SelectControl,{__nextHasNoMarginBottom:!0,label:o.__("Thumbnail size","post-views-counter"),value:P,options:pvcBlockEditorData.imageSizes,onChange:function(e){return c({thumbnailSize:e})}})))),wp.element.createElement("div",r.useBlockProps(),wp.element.createElement(l,{httpMethod:"POST",block:"post-views-counter/most-viewed-posts",attributes:s,LoadingResponsePlaceholder:i,ErrorResponsePlaceholder:a})))}})}(wp.blocks,wp.element,wp.components,wp.i18n,wp.blockEditor,wp.serverSideRender);
     1!function(e,t,n,o,r,l){"use strict";var i=function(){return t.createElement(n.Spinner)},a=function(){return t.createElement(n.Notice,{status:"error",isDismissible:!1},o.__("Something went wrong. Try again or refresh the page.","post-views-counter"))};function s(e){return(s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function c(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,o)}return n}function u(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?c(Object(n),!0).forEach(function(t){p(e,t,n[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):c(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}function p(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=s(e)||!e)return e;var n;if("undefined"!=typeof Symbol&&void 0!==(n=e[Symbol.toPrimitive])){var o=n.call(e,t);if("object"!=s(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==s(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function m(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var o,r,l,i,a=[],s=!0,c=!1;try{if(l=(n=n.call(e)).next,0===t);else for(;!(s=(o=l.call(n)).done)&&(a.push(o.value),a.length!==t);s=!0);}catch(u){c=!0,r=u}finally{try{if(!s&&null!=n.return&&(i=n.return(),Object(i)!==i))return}finally{if(c)throw r}}return a}}(e,t)||function(e,t){if(e){if("string"==typeof e)return b(e,t);var n={}.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?b(e,t):void 0}}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function b(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=Array(t);n<t;n++)o[n]=e[n];return o}var f=t.createElement(function(e){var n=e.viewBox,o=void 0===n?"0 0 24 24":n,r=e.children;return t.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:o,width:24,height:24,fill:"currentColor"},r)},{viewBox:"0 0 16 16"},t.createElement("path",{d:"M12 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h1a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1ZM6.5 6a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1V6ZM2 9a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V9Z"}));e.registerBlockType("post-views-counter/most-viewed-posts",{icon:f,edit:function(e){var s=e.attributes,c=e.setAttributes,b=s.title,f=s.postTypes,v=s.period,w=s.numberOfPosts,d=s.noPostsMessage,y=s.order,h=s.listType,_=s.displayPostViews,g=s.displayPostExcerpt,E=s.displayPostAuthor,C=s.displayPostThumbnail,P=s.thumbnailSize,x=m(t.useState(f||pvcBlockEditorData.postTypesKeys),2),O=x[0],S=x[1];return wp.element.createElement(wp.element.Fragment,null,wp.element.createElement(r.InspectorControls,null,wp.element.createElement(n.PanelBody,{title:o.__("Settings","post-views-counter")},wp.element.createElement(n.TextControl,{__nextHasNoMarginBottom:!0,label:o.__("Title","post-views-counter"),value:b,onChange:function(e){return c({title:e})},help:o.__("Enter empty text to hide title.","post-views-counter")}),wp.element.createElement(n.BaseControl,{__nextHasNoMarginBottom:!0,label:o.__("Post types","post-views-counter")},Object.keys(pvcBlockEditorData.postTypes).map(function(e){return wp.element.createElement(n.CheckboxControl,{key:e,label:pvcBlockEditorData.postTypes[e],checked:O[e],onChange:function(t){var n=u({},f);n[e]=t,S(function(t){return u(u({},t),{},p({},e,!t[e]))}),c({postTypes:n})}})})),wp.element.createElement(n.SelectControl,{__nextHasNoMarginBottom:!0,disabled:1===pvcBlockEditorData.periods.length,label:o.__("Views period","post-views-counter"),value:v,options:pvcBlockEditorData.periods,onChange:function(e){return c({period:e})}}),wp.element.createElement(n.TextControl,{__nextHasNoMarginBottom:!0,label:o.__("Number of posts to show","post-views-counter"),value:w,onChange:function(e){return c({numberOfPosts:Number(e)})}}),wp.element.createElement(n.TextControl,{__nextHasNoMarginBottom:!0,label:o.__("No posts message","post-views-counter"),value:d,onChange:function(e){return c({noPostsMessage:e})}}),wp.element.createElement(n.RadioControl,{label:o.__("Order","post-views-counter"),selected:y,options:[{label:o.__("Ascending","post-views-counter"),value:"asc"},{label:o.__("Descending","post-views-counter"),value:"desc"}],onChange:function(e){return c({order:e})}}),wp.element.createElement(n.RadioControl,{label:o.__("Display style","post-views-counter"),selected:h,options:[{label:o.__("Unordered list","post-views-counter"),value:"unordered"},{label:o.__("Ordered list","post-views-counter"),value:"ordered"}],onChange:function(e){return c({listType:e})}}),wp.element.createElement(n.BaseControl,{__nextHasNoMarginBottom:!0,label:o.__("Display Data","post-views-counter")},wp.element.createElement(n.CheckboxControl,{__nextHasNoMarginBottom:!0,label:o.__("Post views","post-views-counter"),checked:_,onChange:function(e){return c({displayPostViews:e})}}),wp.element.createElement(n.CheckboxControl,{__nextHasNoMarginBottom:!0,label:o.__("Post excerpt","post-views-counter"),checked:g,onChange:function(e){return c({displayPostExcerpt:e})}}),wp.element.createElement(n.CheckboxControl,{__nextHasNoMarginBottom:!0,label:o.__("Post author","post-views-counter"),checked:E,onChange:function(e){return c({displayPostAuthor:e})}}),wp.element.createElement(n.CheckboxControl,{__nextHasNoMarginBottom:!0,label:o.__("Post thumbnail","post-views-counter"),checked:C,onChange:function(e){return c({displayPostThumbnail:e})}}),C&&wp.element.createElement(n.SelectControl,{__nextHasNoMarginBottom:!0,label:o.__("Thumbnail size","post-views-counter"),value:P,options:pvcBlockEditorData.imageSizes,onChange:function(e){return c({thumbnailSize:e})}})))),wp.element.createElement("div",r.useBlockProps(),wp.element.createElement(l,{httpMethod:"POST",block:"post-views-counter/most-viewed-posts",attributes:s,LoadingResponsePlaceholder:i,ErrorResponsePlaceholder:a})))}})}(wp.blocks,wp.element,wp.components,wp.i18n,wp.blockEditor,wp.serverSideRender);
  • post-views-counter/tags/1.8.0/css/admin-columns.css

    r3452736 r3734102  
    1 @charset "UTF-8";body{--pvc-columns-primary-color: var(--wp-admin-theme-color, #2271b1);--pvc-columns-success-color: #28a745;--pvc-columns-danger-color: #dc3545;--pvc-columns-warning-color: #ffc107;--pvc-columns-neutral-color: #6c757d;--pvc-columns-muted-color: #a2a6b0;--pvc-columns-dark-color: #1e1e1e;--pvc-columns-background-color: #fff;--pvc-columns-border-color: #dcdcde;--pvc-columns-hover-color: #f8f9fa}.pvc-modal{display:none}.pvc-modal.is-open{display:block}.pvc-modal__overlay{position:fixed;inset:0;background:#000000b3;display:flex;justify-content:center;align-items:center;z-index:100000}.pvc-modal__container{background-color:var(--pvc-columns-background-color);padding:0;max-width:800px;width:90%;max-height:90vh;border-radius:4px;overflow-y:auto;box-shadow:0 5px 15px #0000004d;position:relative}.pvc-modal__header{display:flex;justify-content:space-between;align-items:center;padding:15px 20px;border-bottom:1px solid var(--pvc-columns-border-color)}.pvc-modal__title{margin:0}.pvc-modal__close{background:transparent;border:none;font-size:28px;font-weight:400;line-height:1;color:var(--pvc-columns-neutral-color);cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center;transition:background-color .2s ease}.pvc-modal__close:hover,.pvc-modal__close:focus{color:var(--pvc-columns-dark-color);outline:none}.pvc-modal__close:before{content:"\d7";margin-top:-4px}.pvc-modal__content{padding:20px}.pvc-modal__content p{margin:0 0 1em;line-height:1.6}.pvc-modal__content ul{margin:0 0 1em 1.25em}.pvc-modal__content ul li{margin-bottom:.5em}.pvc-modal-chart-container{position:relative;height:200px;margin-bottom:20px;background-color:var(--pvc-columns-background-color);border-radius:3px}.pvc-modal-chart-container.loading{opacity:.5;pointer-events:none}.pvc-modal-chart-container .spinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:none;margin:0;float:none}.pvc-modal-chart-container.loading .spinner{display:block;visibility:visible}.pvc-modal-chart-container canvas{max-width:100%}.pvc-modal-content{padding:15px 20px}.pvc-modal-content-top{margin-bottom:20px}.pvc-modal-content-bottom{padding-top:10px;border-top:1px solid var(--pvc-columns-border-color)}.pvc-modal-count{font-weight:100;font-size:32px;line-height:1}.pvc-modal-summary{display:flex;flex-direction:column;gap:5px}.pvc-modal-views-data{display:flex;align-items:center;gap:10px}.pvc-modal-views-label{font-size:14px;color:var(--pvc-columns-neutral-color);font-weight:500}.pvc-modal-rows-container{max-height:220px;overflow:auto}.pvc-modal-legend,.pvc-modal-data{text-align:left;display:inline-block;vertical-align:top}.pvc-modal-legend{width:25%;padding:0;margin:0 10px 0 0;list-style:none}.pvc-modal-legend li{padding-left:5px;padding-right:5px}.pvc-modal-legend li.inactive,.pvc-modal-legend li.inactive a{color:var(--pvc-columns-muted-color);text-decoration:none}.pvc-modal-data{width:73%;text-align:left;border:1px solid var(--pvc-columns-border-color);border-radius:4px;border-spacing:0;border-collapse:collapse;margin:0}.pvc-modal-data th{border-bottom:1px solid var(--pvc-columns-border-color);padding-left:10px;text-align:left}.pvc-modal-data th+th{text-align:right;padding-right:10px}.pvc-modal-data td{border-bottom:1px solid var(--pvc-columns-border-color);padding-left:10px;text-align:left}.pvc-modal-data td+td{text-align:right;padding-right:10px}.pvc-modal-nav{display:flex;justify-content:space-between;align-items:center}.pvc-modal-nav-prev,.pvc-modal-nav-next{text-decoration:none;font-size:14px;padding:6px 12px;border-radius:3px;transition:background-color .2s ease}.pvc-modal-nav-prev:hover,.pvc-modal-nav-next:hover{background-color:var(--pvc-columns-hover-color)}.pvc-modal-nav-prev.pvc-disabled,.pvc-modal-nav-next.pvc-disabled{opacity:.5;cursor:default;pointer-events:none}.pvc-modal-nav-current{font-weight:600;font-size:14px;color:var(--pvc-columns-dark-color)}.pvc-modal-error{padding:15px 20px;text-align:center;color:var(--pvc-columns-danger-color);font-weight:500;margin:0 0 15px;background-color:#fcf0f1;border:1px solid #f1a0a4;border-radius:3px}.pvc-modal-tabs{display:flex;gap:5px;justify-content:center;border-bottom:1px solid var(--pvc-columns-border-color);padding-bottom:10px;margin-top:12px}.pvc-modal-tab{background:transparent;border:none;border-bottom:2px solid transparent;color:var(--pvc-columns-neutral-color);cursor:default;font-size:14px;font-weight:600;padding:8px 16px;transition:all .2s ease}.pvc-modal-tab.active{color:var(--pvc-columns-primary-color);border-bottom-color:var(--pvc-columns-primary-color);font-weight:600}.pvc-modal-tab:focus{box-shadow:none}.pvc-modal-tab:disabled{cursor:default;pointer-events:none}.pvc-modal-tab:disabled span{opacity:.5}.pvc-modal-tab.pvc-tab-disabled{opacity:.5}.pvc-modal-tab.pvc-pro:after{content:"PRO";display:inline-block;margin-left:6px;padding:1px 4px;font-size:11px;border-radius:4px;background-color:#ffc107;color:#fff;font-weight:600}@keyframes mmfadeIn{0%{opacity:0}to{opacity:1}}@keyframes mmfadeOut{0%{opacity:1}to{opacity:0}}@keyframes mmslideIn{0%{transform:translateY(-10%)}to{transform:translateY(0)}}@keyframes mmslideOut{0%{transform:translateY(0)}to{transform:translateY(-10%)}}.micromodal-slide{display:none}.micromodal-slide.is-open{display:block}.micromodal-slide[aria-hidden=false] .pvc-modal__overlay{animation:mmfadeIn .3s cubic-bezier(0,0,.2,1)}.micromodal-slide[aria-hidden=false] .pvc-modal__container{animation:mmslideIn .3s cubic-bezier(0,0,.2,1)}.micromodal-slide[aria-hidden=true] .pvc-modal__overlay{animation:mmfadeOut .3s cubic-bezier(0,0,.2,1)}.micromodal-slide[aria-hidden=true] .pvc-modal__container{animation:mmslideOut .3s cubic-bezier(0,0,.2,1)}.micromodal-slide .pvc-modal__container,.micromodal-slide .pvc-modal__overlay{will-change:transform}@media screen and (max-width:768px){.pvc-modal__container{width:95%;max-height:95vh}.pvc-modal__header{padding:15px}.pvc-modal__title{font-size:18px;padding-right:10px}.pvc-modal__content{padding:15px}.pvc-modal-chart-container{height:180px}.pvc-modal-rows-container{max-height:200px;width:100%}.pvc-modal-legend{width:30%}.pvc-modal-data{width:68%}.pvc-modal-content-bottom{padding-top:10px;border-top:1px solid var(--pvc-columns-border-color)}.pvc-modal-nav{flex-direction:column;gap:10px}.pvc-modal-nav-prev,.pvc-modal-nav-next{width:100%;text-align:center}}.pvc-modal-content-middle{margin:16px 0;padding:12px 16px;background:var(--pvc-columns-hover-color);border-radius:3px;border-left:3px solid var(--pvc-columns-primary-color)}.pvc-modal-insights{display:flex;flex-direction:column;gap:8px}.pvc-insight{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;font-size:13px;line-height:1.5;position:relative;padding-left:24px}.pvc-insight:before{content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);font-size:16px;line-height:1;width:16px;height:16px;display:inline-block;mask-size:cover;background-color:var(--pvc-columns-primary-color)}.pvc-insight-lock:before{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0ic2l6ZS00Ij4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04IDFhMy41IDMuNSAwIDAgMC0zLjUgMy41VjdBMS41IDEuNSAwIDAgMCAzIDguNXY1QTEuNSAxLjUgMCAwIDAgNC41IDE1aDdhMS41IDEuNSAwIDAgMCAxLjUtMS41di01QTEuNSAxLjUgMCAwIDAgMTEuNSA3VjQuNUEzLjUgMy41IDAgMCAwIDggMVptMiA2VjQuNWEyIDIgMCAxIDAtNCAwVjdoNFoiIGNsaXAtcnVsZT0iZXZlbm9kZCIgLz4KPC9zdmc+Cg==)}.pvc-modal-insights-empty .pvc-insight{padding-left:24px}.pvc-modal-insights-empty .pvc-insight-text{color:var(--pvc-columns-neutral-color);font-style:italic}.pvc-views-header{display:inline-block;width:20px;height:20px}.pvc-views-header svg{vertical-align:middle;color:var(--pvc-columns-primary-color)}.column-post_views{width:74px}.column-traffic_signal{width:2rem;text-align:center!important}.pvc-signal-header,.pvc-views-header{display:inline-block;width:20px;height:20px}.pvc-signal-header svg,.pvc-views-header svg{vertical-align:middle;color:var(--pvc-columns-primary-color)}.pvc-signal{display:inline-block;width:20px;height:20px;position:relative;cursor:default;transition:opacity .2s ease;padding:0}.pvc-signal:hover .pvc-insight{transform:scale(1.2)}.pvc-signal:hover{opacity:.8}.pvc-signal .pvc-insight{display:block;width:100%;height:100%;padding:0;transition:transform .2s ease}.pvc-signal .pvc-insight:before{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:16px;line-height:1;width:16px;height:16px;display:inline-block;mask-size:cover}.pvc-insight-silence:before{background-color:var(--pvc-columns-border-color);mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLW1pbnVzLWljb24gbHVjaWRlLW1pbnVzIj48cGF0aCBkPSJNNSAxMmgxNCIvPjwvc3ZnPg==)}.pvc-insight-anomaly:before{background-color:var(--pvc-columns-warning-color);mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWFsZXJ0LXRyaWFuZ2xlLWljb24gbHVjaWRlLWFsZXJ0LXRyaWFuZ2xlIj48cGF0aCBkPSJtMjEuNzMgMTgtOC0xNGEyIDIgMCAwIDAtMy40OCAwbC04IDE0QTIgMiAwIDAgMCA0IDIxaDE2YTIgMiAwIDAgMCAxLjczLTMiLz48cGF0aCBkPSJNMTIgOXY0Ii8+PHBhdGggZD0iTTEyIDE3aC4wMSIvPjwvc3ZnPg==)}@media print{.pvc-modal{display:none!important}}
     1@charset "UTF-8";body{--pvc-columns-primary-color: var(--wp-admin-theme-color, #2271b1);--pvc-columns-success-color: #28a745;--pvc-columns-danger-color: #dc3545;--pvc-columns-warning-color: #ffc107;--pvc-columns-neutral-color: #646970;--pvc-columns-muted-color: #a2a6b0;--pvc-columns-dark-color: #1e1e1e;--pvc-columns-background-color: #fff;--pvc-columns-border-color: #dcdcde;--pvc-columns-hover-color: #f8f9fa}.pvc-modal{display:none}.pvc-modal.is-open{display:block}.pvc-modal__overlay{position:fixed;inset:0;background:#000000b3;display:flex;justify-content:center;align-items:center;z-index:100000}.pvc-modal__container{background-color:var(--pvc-columns-background-color);padding:0;max-width:800px;width:90%;max-height:90vh;border-radius:4px;overflow-y:auto;box-shadow:0 5px 15px #0000004d;position:relative}.pvc-modal__header{display:flex;justify-content:space-between;align-items:center;padding:15px 20px;border-bottom:1px solid var(--pvc-columns-border-color)}.pvc-modal__title{margin:0}.pvc-modal__close{background:transparent;border:none;font-size:28px;font-weight:400;line-height:1;color:var(--pvc-columns-neutral-color);cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center;transition:background-color .2s ease}.pvc-modal__close:hover,.pvc-modal__close:focus{color:var(--pvc-columns-dark-color);outline:none}.pvc-modal__close:before{content:"\d7";margin-top:-4px}.pvc-modal__content{padding:20px}.pvc-modal__content p{margin:0 0 1em;line-height:1.6}.pvc-modal__content ul{margin:0 0 1em 1.25em}.pvc-modal__content ul li{margin-bottom:.5em}.pvc-modal-chart-container{position:relative;height:200px;margin-bottom:20px;background-color:var(--pvc-columns-background-color);border-radius:3px}.pvc-modal-chart-container.loading{opacity:.5;pointer-events:none}.pvc-modal-chart-container .spinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:none;margin:0;float:none}.pvc-modal-chart-container.loading .spinner{display:block;visibility:visible}.pvc-modal-chart-container canvas{max-width:100%}.pvc-modal-status{min-height:1.5em;margin-bottom:10px;color:var(--pvc-columns-neutral-color);font-size:13px;line-height:1.5;text-align:center}.pvc-modal-content{padding:15px 20px}.pvc-modal-content-top{margin-bottom:20px}.pvc-modal-content-bottom{padding-top:10px;border-top:1px solid var(--pvc-columns-border-color)}.pvc-modal-count{font-weight:100;font-size:32px;line-height:1}.pvc-modal-summary{display:flex;flex-direction:column;gap:5px}.pvc-modal-views-data{display:flex;align-items:center;gap:10px}.pvc-modal-views-label{font-size:14px;color:var(--pvc-columns-neutral-color);font-weight:500}.pvc-modal-nav{display:flex;justify-content:space-between;align-items:center}.pvc-modal-nav-prev,.pvc-modal-nav-next{text-decoration:none;font-size:14px;padding:6px 12px;border-radius:3px;transition:background-color .2s ease}.pvc-modal-nav-prev:hover,.pvc-modal-nav-next:hover{background-color:var(--pvc-columns-hover-color)}.pvc-modal-nav-prev.pvc-disabled,.pvc-modal-nav-next.pvc-disabled{opacity:.5;cursor:default;pointer-events:none}.pvc-modal-nav-current{font-weight:600;font-size:14px;color:var(--pvc-columns-dark-color)}.pvc-modal-error{padding:15px 20px;text-align:center;color:var(--pvc-columns-danger-color);font-weight:500;margin:0 0 15px;background-color:#fcf0f1;border:1px solid #f1a0a4;border-radius:3px}.pvc-modal-retry{margin-left:.25em}.pvc-modal-tabs{display:flex;gap:5px;justify-content:center;border-bottom:1px solid var(--pvc-columns-border-color);padding-bottom:10px;margin-top:12px}.pvc-modal-tab{background:transparent;border:none;border-bottom:2px solid transparent;color:var(--pvc-columns-neutral-color);cursor:default;font-size:14px;font-weight:600;padding:8px 16px;transition:all .2s ease}.pvc-modal-tab.active{color:var(--pvc-columns-primary-color);border-bottom-color:var(--pvc-columns-primary-color);font-weight:600}.pvc-modal-tab:focus{box-shadow:none}.pvc-modal-tab:focus-visible{outline:2px solid var(--pvc-columns-primary-color);outline-offset:2px;box-shadow:0 0 0 2px #fff,0 0 0 4px var(--pvc-columns-primary-color)}.pvc-modal-tab:disabled{cursor:default;pointer-events:none}.pvc-modal-tab:disabled span{opacity:.5}.pvc-modal-tab.pvc-tab-disabled{opacity:.5}.pvc-modal-tab.pvc-pro:after{content:"PRO";display:inline-block;margin-left:6px;padding:1px 4px;font-size:11px;border-radius:4px;background-color:#ffc107;color:#fff;font-weight:600}@keyframes mmfadeIn{0%{opacity:0}to{opacity:1}}@keyframes mmfadeOut{0%{opacity:1}to{opacity:0}}@keyframes mmslideIn{0%{transform:translateY(-10%)}to{transform:translateY(0)}}@keyframes mmslideOut{0%{transform:translateY(0)}to{transform:translateY(-10%)}}.micromodal-slide{display:none}.micromodal-slide.is-open{display:block}.micromodal-slide[aria-hidden=false] .pvc-modal__overlay{animation:mmfadeIn .3s cubic-bezier(0,0,.2,1)}.micromodal-slide[aria-hidden=false] .pvc-modal__container{animation:mmslideIn .3s cubic-bezier(0,0,.2,1)}.micromodal-slide[aria-hidden=true] .pvc-modal__overlay{animation:mmfadeOut .3s cubic-bezier(0,0,.2,1)}.micromodal-slide[aria-hidden=true] .pvc-modal__container{animation:mmslideOut .3s cubic-bezier(0,0,.2,1)}.micromodal-slide .pvc-modal__container,.micromodal-slide .pvc-modal__overlay{will-change:transform}@media screen and (max-width:768px){.pvc-modal__container{width:95%;max-height:95vh}.pvc-modal__header{padding:15px}.pvc-modal__title{font-size:18px;padding-right:10px}.pvc-modal__content{padding:15px}.pvc-modal-chart-container{height:180px}.pvc-modal-content-bottom{padding-top:10px;border-top:1px solid var(--pvc-columns-border-color)}.pvc-modal-nav{flex-direction:column;gap:10px}.pvc-modal-nav-prev,.pvc-modal-nav-next{width:100%;text-align:center}}.pvc-modal-content-middle{margin:16px 0;padding:12px 16px;background:var(--pvc-columns-hover-color);border-radius:3px;border-left:3px solid var(--pvc-columns-primary-color)}.pvc-modal-insights{display:flex;flex-direction:column;gap:8px}.pvc-insight{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;font-size:13px;line-height:1.5;position:relative;padding-left:24px}.pvc-insight:before{content:"";position:absolute;left:0;top:50%;transform:translateY(-50%);font-size:16px;line-height:1;width:16px;height:16px;display:inline-block;mask-size:cover;background-color:var(--pvc-columns-primary-color)}.pvc-insight-lock:before{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0ic2l6ZS00Ij4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04IDFhMy41IDMuNSAwIDAgMC0zLjUgMy41VjdBMS41IDEuNSAwIDAgMCAzIDguNXY1QTEuNSAxLjUgMCAwIDAgNC41IDE1aDdhMS41IDEuNSAwIDAgMCAxLjUtMS41di01QTEuNSAxLjUgMCAwIDAgMTEuNSA3VjQuNUEzLjUgMy41IDAgMCAwIDggMVptMiA2VjQuNWEyIDIgMCAxIDAtNCAwVjdoNFoiIGNsaXAtcnVsZT0iZXZlbm9kZCIgLz4KPC9zdmc+Cg==);background-color:var(--pvc-columns-neutral-color)}.pvc-modal-insights-empty .pvc-insight{padding-left:24px}.pvc-modal-insights-empty .pvc-insight-text{color:var(--pvc-columns-neutral-color);font-style:italic}.pvc-views-header,.pvc-signal-header{display:inline-flex;align-items:center;width:auto;height:20px;vertical-align:top}.pvc-views-header svg,.pvc-signal-header svg{flex:0 0 16px;width:16px;height:16px;color:var(--pvc-columns-primary-color)}.pvc-signal-header svg{flex-basis:14px;width:14px;height:14px}@media screen and (max-width:960px){.wp-list-table.fixed .column-title{width:20%}.column-post_views{width:auto;min-width:5em}}[data-pvc-tooltip]{position:relative}[data-pvc-tooltip]:before,[data-pvc-tooltip]:after{position:absolute;bottom:100%;left:50%;z-index:10;box-sizing:border-box;opacity:0;pointer-events:none;transform:translate3d(-50%,0,0);transform-origin:top;backface-visibility:hidden;will-change:transform;transition:all var(--microtip-transition-duration, .18s) var(--microtip-transition-easing, ease-in-out) var(--microtip-transition-delay, 0s)}[data-pvc-tooltip]:before{content:"";width:18px;height:6px;margin-bottom:5px;background:url(data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2236px%22%20height%3D%2212px%22%3E%3Cpath%20fill%3D%22rgba%2817,%2017,%2017,%200.9%29%22%20transform%3D%22rotate%280%29%22%20d%3D%22M2.658,0.000%20C-13.615,0.000%2050.938,0.000%2034.662,0.000%20C28.662,0.000%2023.035,12.002%2018.660,12.002%20C14.285,12.002%208.594,0.000%202.658,0.000%20Z%22/%3E%3C/svg%3E) no-repeat;background-size:100% auto!important}[data-pvc-tooltip]:after{content:attr(data-pvc-tooltip);width:max-content;max-width:260px;margin-bottom:11px;padding:.5em 1em;border-radius:4px;background:#111111e6;box-sizing:content-box;color:#fff;font-size:var(--microtip-font-size, 13px);font-weight:var(--microtip-font-weight, normal);text-align:left;text-transform:var(--microtip-text-transform, none);white-space:pre-line}[data-pvc-tooltip]:hover:before,[data-pvc-tooltip]:hover:after{opacity:1;pointer-events:auto;transform:translate3d(-50%,-5px,0)}.column-post_views.sortable a,.column-post_views.sorted a{overflow:visible;display:flow-root}.column-post_views a:focus-visible .pvc-views-header:before,.column-post_views a:focus-visible .pvc-views-header:after{opacity:1;transform:translate3d(-50%,-5px,0)}.column-post_views{width:55px}.column-traffic_signal{width:2rem;text-align:center!important}.pvc-signal{display:inline-block;width:20px;height:20px;position:relative;cursor:default;transition:opacity .2s ease;padding:0}.pvc-signal[data-post-id]{cursor:pointer}.pvc-signal:hover .pvc-insight{transform:scale(1.2);opacity:.8}.pvc-signal .pvc-insight{display:block;width:100%;height:100%;padding:0;transition:transform .2s ease}.pvc-signal .pvc-insight:before{content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:16px;line-height:1;width:16px;height:16px;display:inline-block;mask-size:cover}.pvc-insight-silence:before{background-color:var(--pvc-columns-border-color);mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLW1pbnVzLWljb24gbHVjaWRlLW1pbnVzIj48cGF0aCBkPSJNNSAxMmgxNCIvPjwvc3ZnPg==)}.pvc-insight-anomaly:before{background-color:var(--pvc-columns-warning-color);mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWFsZXJ0LXRyaWFuZ2xlLWljb24gbHVjaWRlLWFsZXJ0LXRyaWFuZ2xlIj48cGF0aCBkPSJtMjEuNzMgMTgtOC0xNGEyIDIgMCAwIDAtMy40OCAwbC04IDE0QTIgMiAwIDAgMCA0IDIxaDE2YTIgMiAwIDAgMCAxLjczLTMiLz48cGF0aCBkPSJNMTIgOXY0Ii8+PHBhdGggZD0iTTEyIDE3aC4wMSIvPjwvc3ZnPg==)}@media print{.pvc-modal{display:none!important}}
  • post-views-counter/tags/1.8.0/css/admin-dashboard.css

    r3578455 r3734102  
    1 .pvc-accordion{--pvc-theme-color: var(--wp-admin-theme-color, #2271b1);--pvc-theme-color-rgb: var(--wp-admin-theme-color--rgb, 34, 113, 177);--pvc-primary-color: #6610f2;--pvc-secondary-color: #ffc107;--pvc-border-color: #eee;--pvc-bg-color: #fafafa;--pvc-dark-color: #212529;--pvc-disabled-color: #72777c}#pvc_dashboard .inside{margin:0;padding:0}.pvc-accordion .pvc-accordion-item.pvc-collapsed .pvc-accordion-toggle:before{transform:rotate(180deg)}.pvc-accordion .pvc-accordion-item.pvc-collapsed .pvc-accordion-content{display:none}.pvc-accordion .pvc-accordion-item .pvc-according-header{display:flex;align-items:center;justify-content:space-between}.pvc-accordion .pvc-accordion-item .pvc-accordion-content{padding:11px 12px;border-bottom:1px solid var(--pvc-border-color);height:100%}.pvc-accordion .pvc-accordion-header{display:flex;align-items:center;justify-content:space-between;background-color:var(--pvc-bg-color);border-bottom:1px solid var(--pvc-border-color);position:relative}.pvc-accordion .pvc-accordion-toggle{background-color:var(--pvc-bg-color);border-bottom:1px solid var(--pvc-border-color);cursor:pointer;position:relative;font-size:14px;font-weight:400;line-height:1;margin:0;padding:11px 12px;color:var(--pvc-dark-color);flex-grow:1}.pvc-accordion .pvc-accordion-title{display:inline-block;padding-right:10px}.pvc-accordion .pvc-accordion-actions{position:absolute;top:0;right:0;z-index:1;padding:11px 6px;height:14px;line-height:1;flex-shrink:0;text-decoration:none}.pvc-accordion .pvc-accordion-actions .pvc-accordion-action{width:14px;text-align:center}.pvc-accordion .pvc-accordion-actions .pvc-accordion-action,.pvc-accordion .pvc-accordion-actions .pvc-accordion-action:before{font-size:14px;height:14px;width:14px;color:var(--pvc-disabled-color);display:inline-block;text-decoration:none!important}.pvc-accordion .pvc-accordion-actions .pvc-toggle-indicator:before{color:var(--pvc-disabled-color);content:"\f142";display:inline-block;font-family:dashicons;line-height:1;transform:rotate(0);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-indent:-1px}.pvc-accordion .pvc-tooltip{position:relative}.pvc-accordion .pvc-tooltip-icon{display:inline-block;width:16px;cursor:help}.pvc-accordion .pvc-tooltip-icon:before{color:var(--pvc-disabled-color);content:"\f14c";display:inline-block;font: 16px/1 dashicons;position:absolute;text-decoration:none!important;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;left:0;top:2px}.pvc-dashboard-container{min-height:260px;margin:0 -4px;text-align:center;position:relative;display:flex;flex-direction:column}.pvc-dashboard-container *{box-sizing:border-box}.pvc-dashboard-container.loading{pointer-events:none}.pvc-dashboard-container.loading:after{position:absolute;content:"";display:block;height:100%;width:100%;top:0;left:0;background-color:#fffc;z-index:1;transition:all .2s}.pvc-dashboard-container .spinner{position:absolute;left:50%;top:40%;margin-left:-10px;z-index:10}.pvc-dashboard-container .pvc-date-nav{display:flex;justify-content:space-between;color:var(--pvc-disabled-color);width:100%;margin:6px 0 0}.pvc-dashboard-container .pvc-date-nav .current{color:var(--pvc-dark-color)}.pvc-dashboard-container .pvc-dashboard p.sub{color:var(--pvc-disabled-color);font-size:14px;text-align:left;padding-bottom:3px;border-bottom:1px solid var(--pvc-border-color)}.pvc-dashboard-container .pvc-data-container{min-height:230px}.pvc-dashboard-container .pvc-data-container a{text-decoration:none}.pvc-dashboard-container .pvc-dashboard-message{display:none;text-align:left}.pvc-dashboard-container .pvc-dashboard-message:not(:empty){display:block;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);padding:10px}.pvc-dashboard-container .pvc-dashboard-error{display:flex;flex-direction:column;align-items:center;gap:8px}.pvc-dashboard-container .pvc-dashboard-error .button{white-space:normal}.pvc-dashboard-container .pvc-table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}.pvc-dashboard-container .pvc-table{caption-side:bottom;border-collapse:collapse;width:100%;margin-bottom:1rem;color:inherit;vertical-align:top;border-color:var(--pvc-border-color);text-align:left}.pvc-dashboard-container .pvc-table>thead{vertical-align:bottom;color:var(--pvc-dark-color)}.pvc-dashboard-container .pvc-table>tbody{vertical-align:inherit}.pvc-dashboard-container .pvc-table tbody,.pvc-dashboard-container .pvc-table td,.pvc-dashboard-container .pvc-table tfoot,.pvc-dashboard-container .pvc-table th,.pvc-dashboard-container .pvc-table thead,.pvc-dashboard-container .pvc-table tr{border-color:inherit;border-style:solid;border-width:0}.pvc-dashboard-container .pvc-table th,.pvc-dashboard-container .pvc-table td{text-align:inherit;text-align:-webkit-match-parent}.pvc-dashboard-container .pvc-table th:first-child,.pvc-dashboard-container .pvc-table td:first-child{width:1px;white-space:nowrap}.pvc-dashboard-container .pvc-table th:last-child,.pvc-dashboard-container .pvc-table td:last-child{text-align:right}.pvc-dashboard-container .pvc-table .no-posts :last-child{text-align:left}.pvc-dashboard-container .pvc-table>:not(caption)>*>*{padding:.5rem;background-color:transparent;border-bottom-width:1px;box-shadow:inset 0 0 0 9999px transparent}.pvc-dashboard-container .pvc-table .cn-edit-link{display:none}.pvc-dashboard-container .pvc-table .cn-edit-link:before{content:"(";display:inline-block}.pvc-dashboard-container .pvc-table .cn-edit-link:after{content:")";display:inline-block}.pvc-dashboard-container .pvc-table tr:hover .cn-edit-link{display:inline-block}.pvc-dashboard-content-top,.pvc-dashboard-content-bottom{padding:0 6px}.pvc-dashboard-block{display:flex;justify-content:center;align-items:center;background:var(--pvc-bg-color);color:var(--pvc-disabled-color);font-size:13px;font-style:italic;padding:13px;margin-top:0}.pvc-dashboard-unlock{padding:13px;margin-top:0}.pvc-dashboard-unlock>div{background-color:#f6f7f7;border:1px solid var(--pvc-border-color);padding:13px;border-radius:3px}.pvc-dashboard-unlock .button{margin:3px 0 5px}.pvc-dashboard-unlock h3{font-weight:600!important}.pvc-dashboard-unlock h3:after{content:"PRO";display:inline-block;margin-left:6px;padding:1px 4px;font-size:11px;border-radius:4px;background-color:var(--pvc-secondary-color);color:#fff;font-weight:600}
     1.pvc-metric-toggle,.pvc-reports-legend{display:flex;width:100%;justify-content:center;align-items:center;flex-wrap:wrap;column-gap:12px;row-gap:8px;margin:0 0 15px;padding:0}.pvc-metric-toggle-item,.pvc-reports-legend-item{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--pvc-columns-border-color, #dcdcde);background:transparent;cursor:pointer;padding:5px 9px}.pvc-metric-toggle-item[aria-pressed=false],.pvc-reports-legend-item[aria-pressed=false]{opacity:.55;text-decoration:line-through}.pvc-metric-toggle-item:disabled,.pvc-reports-legend-item:disabled{cursor:not-allowed;opacity:.55;text-decoration:none}.pvc-metric-toggle-item:focus-visible,.pvc-reports-legend-item:focus-visible{outline:2px solid var(--pvc-columns-primary-color, var(--wp-admin-theme-color, #2271b1));outline-offset:2px}.pvc-metric-toggle-swatch,.pvc-reports-legend-swatch{display:inline-block;width:18px;height:0;border-top-width:2px;border-top-style:solid}.pvc-metric-toggle-swatch-views,.pvc-reports-legend-swatch-views{border-top-style:solid;border-top-color:var(--pvc-metric-views-color, var(--pvc-columns-primary-color, var(--wp-admin-theme-color, #2271b1)))}.pvc-metric-toggle-swatch-visits,.pvc-reports-legend-swatch-visits{border-top-style:dashed;border-top-color:var(--pvc-metric-visits-color, rgb(116, 72, 157))}.pvc-metric-toggle-swatch-circle{width:8px;height:8px;border:0;border-radius:50%}.pvc-accordion{--pvc-theme-color: var(--wp-admin-theme-color, #2271b1);--pvc-theme-color-rgb: var(--wp-admin-theme-color--rgb, 34, 113, 177);--pvc-primary-color: #6610f2;--pvc-secondary-color: #ffc107;--pvc-border-color: #eee;--pvc-bg-color: #fafafa;--pvc-dark-color: #212529;--pvc-disabled-color: #72777c}#pvc_dashboard .inside{margin:0;padding:0}.pvc-accordion .pvc-accordion-item.pvc-collapsed .pvc-accordion-toggle:before{transform:rotate(180deg)}.pvc-accordion .pvc-accordion-item.pvc-collapsed .pvc-accordion-content{display:none}.pvc-accordion .pvc-accordion-item .pvc-according-header{display:flex;align-items:center;justify-content:space-between}.pvc-accordion .pvc-accordion-item .pvc-accordion-content{padding:11px 12px;border-bottom:1px solid var(--pvc-border-color);height:100%}.pvc-accordion .pvc-accordion-header{display:flex;align-items:center;justify-content:space-between;background-color:var(--pvc-bg-color);border-bottom:1px solid var(--pvc-border-color);position:relative}.pvc-accordion .pvc-accordion-toggle{background-color:var(--pvc-bg-color);border-bottom:1px solid var(--pvc-border-color);cursor:pointer;position:relative;font-size:14px;font-weight:400;line-height:1;margin:0;padding:11px 12px;color:var(--pvc-dark-color);flex-grow:1}.pvc-accordion .pvc-accordion-title{display:inline-block;padding-right:10px}.pvc-accordion .pvc-accordion-actions{position:absolute;top:0;right:0;z-index:1;padding:11px 6px;height:14px;line-height:1;flex-shrink:0;text-decoration:none}.pvc-accordion .pvc-accordion-actions .pvc-accordion-action{width:14px;text-align:center}.pvc-accordion .pvc-accordion-actions .pvc-accordion-action,.pvc-accordion .pvc-accordion-actions .pvc-accordion-action:before{font-size:14px;height:14px;width:14px;color:var(--pvc-disabled-color);display:inline-block;text-decoration:none!important}.pvc-accordion .pvc-accordion-actions .pvc-toggle-indicator:before{color:var(--pvc-disabled-color);content:"\f142";display:inline-block;font-family:dashicons;line-height:1;transform:rotate(0);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-indent:-1px}.pvc-accordion .pvc-tooltip{position:relative}.pvc-accordion .pvc-tooltip-icon{display:inline-block;width:16px;cursor:help}.pvc-accordion .pvc-tooltip-icon:before{color:var(--pvc-disabled-color);content:"\f14c";display:inline-block;font: 16px/1 dashicons;position:absolute;text-decoration:none!important;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;left:0;top:2px}.pvc-dashboard-container{min-height:260px;margin:0 -4px;text-align:center;position:relative;display:flex;flex-direction:column}.pvc-dashboard-container *{box-sizing:border-box}.pvc-dashboard-container.loading{pointer-events:none}.pvc-dashboard-container.loading:after{position:absolute;content:"";display:block;height:100%;width:100%;top:0;left:0;background-color:#fffc;z-index:1;transition:all .2s}.pvc-dashboard-container .spinner{position:absolute;left:50%;top:40%;margin-left:-10px;z-index:10}.pvc-dashboard-container .pvc-date-nav{display:flex;justify-content:space-between;color:var(--pvc-disabled-color);width:100%;margin:6px 0 0}.pvc-dashboard-container .pvc-date-nav .current{color:var(--pvc-dark-color)}.pvc-dashboard-container .pvc-dashboard p.sub{color:var(--pvc-disabled-color);font-size:14px;text-align:left;padding-bottom:3px;border-bottom:1px solid var(--pvc-border-color)}.pvc-dashboard-container .pvc-data-container{min-height:230px}.pvc-dashboard-container .pvc-data-container a{text-decoration:none}.pvc-dashboard-container.pvc-dashboard-container-chart .pvc-data-container{min-height:0;margin:10px 0}.pvc-dashboard-container .pvc-chart-wrapper{position:relative;width:100%;height:170px}.pvc-dashboard-container .pvc-chart-wrapper canvas{display:block}.pvc-dashboard-container .pvc-dashboard-metric-controls{padding:0 6px}.pvc-dashboard-container .pvc-dashboard-metrics{margin:0}.pvc-dashboard-container .pvc-dashboard-legend{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:13px;font-weight:400;column-gap:10px;row-gap:4px}.pvc-dashboard-container .pvc-dashboard-legend .pvc-metric-toggle-item{border:0;padding:2px 0;color:inherit;font:inherit;line-height:1.4}.pvc-dashboard-container .pvc-dashboard-visits-unavailable{margin:8px 0 0;color:var(--pvc-disabled-color);font-size:12px}.pvc-dashboard-container .pvc-dashboard-visits-unavailable{margin:0 0 10px}.pvc-dashboard-container .pvc-dashboard-message{display:none;text-align:left}.pvc-dashboard-container .pvc-dashboard-message:not(:empty){display:block;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);padding:10px}.pvc-dashboard-container .pvc-dashboard-error{display:flex;flex-direction:column;align-items:center;gap:8px}.pvc-dashboard-container .pvc-dashboard-error .button{white-space:normal}.pvc-dashboard-container .pvc-table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}.pvc-dashboard-container .pvc-table{caption-side:bottom;border-collapse:collapse;width:100%;margin-bottom:1rem;color:inherit;vertical-align:top;border-color:var(--pvc-border-color);text-align:left}.pvc-dashboard-container .pvc-table>thead{vertical-align:bottom;color:var(--pvc-dark-color)}.pvc-dashboard-container .pvc-table>tbody{vertical-align:inherit}.pvc-dashboard-container .pvc-table tbody,.pvc-dashboard-container .pvc-table td,.pvc-dashboard-container .pvc-table tfoot,.pvc-dashboard-container .pvc-table th,.pvc-dashboard-container .pvc-table thead,.pvc-dashboard-container .pvc-table tr{border-color:inherit;border-style:solid;border-width:0}.pvc-dashboard-container .pvc-table th,.pvc-dashboard-container .pvc-table td{text-align:inherit;text-align:-webkit-match-parent}.pvc-dashboard-container .pvc-table th:first-child,.pvc-dashboard-container .pvc-table td:first-child{width:1px;white-space:nowrap}.pvc-dashboard-container .pvc-table th:last-child,.pvc-dashboard-container .pvc-table td:last-child{text-align:right}.pvc-dashboard-container .pvc-table .no-posts :last-child{text-align:left}.pvc-dashboard-container .pvc-table>:not(caption)>*>*{padding:.5rem;background-color:transparent;border-bottom-width:1px;box-shadow:inset 0 0 0 9999px transparent}.pvc-dashboard-container .pvc-table .cn-edit-link{display:none}.pvc-dashboard-container .pvc-table .cn-edit-link:before{content:"(";display:inline-block}.pvc-dashboard-container .pvc-table .cn-edit-link:after{content:")";display:inline-block}.pvc-dashboard-container .pvc-table tr:hover .cn-edit-link{display:inline-block}.pvc-dashboard-content-top{padding:0 6px}.pvc-dashboard-headline{--pvc-dashboard-dark-color: #1e1e1e;--pvc-dashboard-neutral-color: #646970;display:flex;justify-content:space-between;width:100%;margin-bottom:6px}.pvc-dashboard-headline .pvc-summary{display:flex;align-items:center}.pvc-dashboard-headline .pvc-summary>*{opacity:1;transition:opacity .2s}.pvc-dashboard-headline .pvc-data-count{font-size:18px;font-weight:600;display:inline-block;color:var(--pvc-dashboard-dark-color)}.pvc-dashboard-headline .pvc-date-select{margin-left:auto;opacity:.7;transition:opacity .2s}.pvc-dashboard-headline .pvc-date-select:hover,.pvc-dashboard-headline .pvc-date-select:focus{opacity:1}.pvc-dashboard-headline .pvc-date-select .button{transition:border .05s ease-in-out,background .05s ease-in-out,color .05s ease-in-out}.pvc-dashboard-headline .pvc-date-select .button:not(:focus){outline:0}.pvc-dashboard-headline .pvc-date-select .button:disabled{color:var(--pvc-dashboard-neutral-color)!important;cursor:default}.pvc-dashboard-headline .pvc-date-select .button:disabled span{opacity:.5}.pvc-dashboard-content-bottom{padding:0 6px}.pvc-dashboard-block{display:flex;justify-content:center;align-items:center;background:var(--pvc-bg-color);color:var(--pvc-disabled-color);font-size:13px;font-style:italic;padding:13px;margin-top:0}.pvc-dashboard-unlock{padding:13px;margin-top:0}.pvc-dashboard-unlock>div{background-color:#f6f7f7;border:1px solid var(--pvc-border-color);padding:13px;border-radius:3px}.pvc-dashboard-unlock .button{margin:3px 0 5px}.pvc-dashboard-unlock h3{font-weight:600!important}.pvc-dashboard-unlock h3:after{content:"PRO";display:inline-block;margin-left:6px;padding:1px 4px;font-size:11px;border-radius:4px;background-color:var(--pvc-secondary-color);color:#fff;font-weight:600}.pvc-widget-insights{margin:12px 6px 4px;padding:4px 12px;background:#f8f9fa;border-left:3px solid var(--pvc-theme-color);border-radius:3px;font-size:13px;line-height:1.5;text-align:left;color:#1e1e1e}.pvc-widget-insights[hidden]{display:none}.pvc-widget-insights .pvc-widget-insight{position:relative;margin:6px 0;padding-left:24px;color:#646970}.pvc-widget-insights .pvc-widget-insight:before{content:"";position:absolute;left:0;top:2px;width:16px;height:16px;mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWluZm8taWNvbiBsdWNpZGUtaW5mbyI+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMTAiLz48cGF0aCBkPSJNMTIgMTZ2LTQiLz48cGF0aCBkPSJNMTIgOGguMDEiLz48L3N2Zz4=);mask-size:cover;background-color:#6c757d}.pvc-widget-insights .pvc-widget-insight-up:before{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXRyZW5kaW5nLXVwLWljb24gbHVjaWRlLXRyZW5kaW5nLXVwIj48cGF0aCBkPSJNMTYgN2g2djYiLz48cGF0aCBkPSJtMjIgNy04LjUgOC41LTUtNUwyIDE3Ii8+PC9zdmc+);background-color:#28a745}.pvc-widget-insights .pvc-widget-insight-down:before{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXRyZW5kaW5nLWRvd24taWNvbiBsdWNpZGUtdHJlbmRpbmctZG93biI+PHBhdGggZD0iTTE2IDE3aDZ2LTYiLz48cGF0aCBkPSJtMjIgMTctOC41LTguNS01IDVMMiA3Ii8+PC9zdmc+);background-color:#dc3545}.pvc-widget-insights .pvc-widget-insight-locked:before{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0ic2l6ZS00Ij4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04IDFhMy41IDMuNSAwIDAgMC0zLjUgMy41VjdBMS41IDEuNSAwIDAgMCAzIDguNXY1QTEuNSAxLjUgMCAwIDAgNC41IDE1aDdhMS41IDEuNSAwIDAgMCAxLjUtMS41di01QTEuNSAxLjUgMCAwIDAgMTEuNSA3VjQuNUEzLjUgMy41IDAgMCAwIDggMVptMiA2VjQuNWEyIDIgMCAxIDAtNCAwVjdoNFoiIGNsaXAtcnVsZT0iZXZlbm9kZCIgLz4KPC9zdmc+Cg==)}
  • post-views-counter/tags/1.8.0/css/admin-post.css

    r3452736 r3734102  
    1 #misc-publishing-actions #post-views #post-views-display:before{display:inline-block;font:400 20px/1 dashicons;left:-1px;padding:0 2px 0 0;position:relative;text-decoration:none!important;vertical-align:top;color:#888;content:"\f185";top:-1px}.edit-php .widefat th#post_views{width:74px}.edit-php .widefat th.column-post_views .dashicons,.edit-php .widefat th.column-post_views .dashicons:before{font-size:1.1em;vertical-align:middle}.edit-php .widefat th .dash-title{display:none}.edit-php .widefat td .dashicons,.edit-php .widefat td .dashicons:before{font-size:1.1em}.edit-php .metabox-prefs .dash-icon{display:none}.edit-php #inline-edit-post_views input{width:auto}.upload-php .widefat .dash-title{display:none}
     1#misc-publishing-actions #post-views #post-views-display:before{display:inline-block;font:400 20px/1 dashicons;left:-1px;padding:0 2px 0 0;position:relative;text-decoration:none!important;vertical-align:top;color:#888;content:"\f185";top:-1px}#misc-publishing-actions #post-views .pvc-editor-counter-field{margin:8px 0}#misc-publishing-actions #post-views .pvc-editor-counter-field label{display:block;margin-bottom:4px}#misc-publishing-actions #post-views .pvc-editor-counter-field input[type=number]{width:100%;max-width:12em}.edit-php .widefat th#post_views{width:74px}.edit-php .widefat th.column-post_views .dashicons,.edit-php .widefat th.column-post_views .dashicons:before{font-size:1.1em;vertical-align:middle}.edit-php .widefat th .dash-title{display:none}.edit-php .widefat td .dashicons,.edit-php .widefat td .dashicons:before{font-size:1.1em}.edit-php .metabox-prefs .dash-icon{display:none}.edit-php #inline-edit-post_views input{width:auto}.upload-php .widefat .dash-title{display:none}
  • post-views-counter/tags/1.8.0/css/admin-settings.css

    r3578455 r3734102  
    22 * Pico CSS ✨ v2.1.1 (https://picocss.com)
    33 * Copyright 2019-2025 - Licensed under MIT
    4  */:root,:host{--pvc-font-family-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--pvc-font-family-sans-serif: system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, Helvetica, Arial, "Helvetica Neue", sans-serif, var(--pvc-font-family-emoji);--pvc-font-family-monospace: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace, var(--pvc-font-family-emoji);--pvc-font-family: var(--pvc-font-family-sans-serif);--pvc-line-height: 1.5;--pvc-font-weight: 400;--pvc-font-size: 100%;--pvc-text-underline-offset: .1rem;--pvc-border-radius: .25rem;--pvc-border-width: .0625rem;--pvc-outline-width: .125rem;--pvc-transition: .2s ease-in-out;--pvc-spacing: 1rem;--pvc-group-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--pvc-group-box-shadow-focus-with-button: 0 0 0 var(--pvc-outline-width) var(--pvc-primary-focus);--pvc-group-box-shadow-focus-with-input: 0 0 0 .0625rem var(--pvc-form-element-border-color);--pvc-icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pvc-icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pvc-icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--pvc-icon-loading: url("data:image/svg+xml,%3Csvg fill='none' height='24' width='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' %3E%3Cstyle%3E g %7B animation: rotate 2s linear infinite; transform-origin: center center; %7D circle %7B stroke-dasharray: 75,100; stroke-dashoffset: -5; animation: dash 1.5s ease-in-out infinite; stroke-linecap: round; %7D @keyframes rotate %7B 0%25 %7B transform: rotate(0deg); %7D 100%25 %7B transform: rotate(360deg); %7D %7D @keyframes dash %7B 0%25 %7B stroke-dasharray: 1,100; stroke-dashoffset: 0; %7D 50%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -17.5; %7D 100%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -62; %7D %7D %3C/style%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='10' fill='none' stroke='rgb(136, 145, 164)' stroke-width='4' /%3E%3C/g%3E%3C/svg%3E")}@media(min-width:576px){:root,:host{--pvc-font-size: 106.25%}}@media(min-width:768px){:root,:host{--pvc-font-size: 112.5%}}@media(min-width:1024px){:root,:host{--pvc-font-size: 118.75%}}@media(min-width:1280px){:root,:host{--pvc-font-size: 125%}}@media(min-width:1536px){:root,:host{--pvc-font-size: 131.25%}}a,a.secondary,a.contrast{--pvc-text-decoration: underline}pre,code,kbd,samp{--pvc-font-family: var(--pvc-font-family-monospace)}kbd{--pvc-font-weight: bolder}input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),:where(select,textarea){--pvc-outline-width: .0625rem}[type=search]{--pvc-border-radius: 5rem}[type=checkbox],[type=radio]{--pvc-border-width: .125rem}[type=checkbox][role=switch]{--pvc-border-width: .1875rem}[role=search]{--pvc-border-radius: 5rem}[role=search]:has(button.secondary:focus,[type=submit].secondary:focus,[type=button].secondary:focus,[role=button].secondary:focus),[role=group]:has(button.secondary:focus,[type=submit].secondary:focus,[type=button].secondary:focus,[role=button].secondary:focus){--pvc-group-box-shadow-focus-with-button: 0 0 0 var(--pvc-outline-width) var(--pvc-secondary-focus)}[role=search]:has(button.contrast:focus,[type=submit].contrast:focus,[type=button].contrast:focus,[role=button].contrast:focus),[role=group]:has(button.contrast:focus,[type=submit].contrast:focus,[type=button].contrast:focus,[role=button].contrast:focus){--pvc-group-box-shadow-focus-with-button: 0 0 0 var(--pvc-outline-width) var(--pvc-contrast-focus)}[role=search] button,[role=search] [type=submit],[role=search] [type=button],[role=search] [role=button],[role=group] button,[role=group] [type=submit],[role=group] [type=button],[role=group] [role=button]{--pvc-form-element-spacing-horizontal: 2rem}.pvc-settings-form [aria-busy=true]:not(input,select,textarea):is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline):before{filter:brightness(0) invert(1)}[data-theme=light],:root:not([data-theme=dark]),:host(:not([data-theme=dark])){color-scheme:light;--pvc-background-color: #fff;--pvc-color: #373c44;--pvc-text-selection-color: rgba(2, 154, 232, .25);--pvc-muted-color: #646b79;--pvc-muted-border-color: rgb(231, 234, 239.5);--pvc-primary: #0172ad;--pvc-primary-background: #0172ad;--pvc-primary-border: var(--pvc-primary-background);--pvc-primary-underline: rgba(1, 114, 173, .5);--pvc-primary-hover: #015887;--pvc-primary-hover-background: #02659a;--pvc-primary-hover-border: var(--pvc-primary-hover-background);--pvc-primary-hover-underline: var(--pvc-primary-hover);--pvc-primary-focus: rgba(2, 154, 232, .5);--pvc-primary-inverse: #fff;--pvc-secondary: #5d6b89;--pvc-secondary-background: #525f7a;--pvc-secondary-border: var(--pvc-secondary-background);--pvc-secondary-underline: rgba(93, 107, 137, .5);--pvc-secondary-hover: #48536b;--pvc-secondary-hover-background: #48536b;--pvc-secondary-hover-border: var(--pvc-secondary-hover-background);--pvc-secondary-hover-underline: var(--pvc-secondary-hover);--pvc-secondary-focus: rgba(93, 107, 137, .25);--pvc-secondary-inverse: #fff;--pvc-contrast: #181c25;--pvc-contrast-background: #181c25;--pvc-contrast-border: var(--pvc-contrast-background);--pvc-contrast-underline: rgba(24, 28, 37, .5);--pvc-contrast-hover: #000;--pvc-contrast-hover-background: #000;--pvc-contrast-hover-border: var(--pvc-contrast-hover-background);--pvc-contrast-hover-underline: var(--pvc-secondary-hover);--pvc-contrast-focus: rgba(93, 107, 137, .25);--pvc-contrast-inverse: #fff;--pvc-box-shadow: .0145rem .029rem .174rem rgba(129, 145, 181, .01698), .0335rem .067rem .402rem rgba(129, 145, 181, .024), .0625rem .125rem .75rem rgba(129, 145, 181, .03), .1125rem .225rem 1.35rem rgba(129, 145, 181, .036), .2085rem .417rem 2.502rem rgba(129, 145, 181, .04302), .5rem 1rem 6rem rgba(129, 145, 181, .06), 0 0 0 .0625rem rgba(129, 145, 181, .015);--pvc-code-background-color: rgb(243, 244.5, 246.75);--pvc-code-color: #646b79;--pvc-code-kbd-background-color: var(--pvc-color);--pvc-code-kbd-color: var(--pvc-background-color);--pvc-form-element-background-color: rgb(251, 251.5, 252.25);--pvc-form-element-selected-background-color: #dfe3eb;--pvc-form-element-border-color: #cfd5e2;--pvc-form-element-color: #23262c;--pvc-form-element-placeholder-color: var(--pvc-muted-color);--pvc-form-element-active-background-color: #fff;--pvc-form-element-active-border-color: var(--pvc-primary-border);--pvc-form-element-focus-color: var(--pvc-primary-border);--pvc-form-element-disabled-opacity: .5;--pvc-form-element-invalid-border-color: rgb(183.5, 105.5, 106.5);--pvc-form-element-invalid-active-border-color: rgb(200.25, 79.25, 72.25);--pvc-form-element-invalid-focus-color: var(--pvc-form-element-invalid-active-border-color);--pvc-form-element-valid-border-color: rgb(76, 154.5, 137.5);--pvc-form-element-valid-active-border-color: rgb(39, 152.75, 118.75);--pvc-form-element-valid-focus-color: var(--pvc-form-element-valid-active-border-color);--pvc-switch-background-color: #bfc7d9;--pvc-switch-checked-background-color: var(--pvc-primary-background);--pvc-switch-color: #fff;--pvc-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--pvc-range-border-color: #dfe3eb;--pvc-range-active-border-color: #bfc7d9;--pvc-range-thumb-border-color: var(--pvc-background-color);--pvc-range-thumb-color: var(--pvc-secondary-background);--pvc-range-thumb-active-color: var(--pvc-primary-background);--pvc-loading-spinner-opacity: .5;--pvc-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 154.5, 137.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200.25, 79.25, 72.25)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E")}[data-theme=light] input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),:root:not([data-theme=dark]) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),:host(:not([data-theme=dark])) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pvc-form-element-focus-color: var(--pvc-primary-focus)}@media only screen and (prefers-color-scheme:dark){:root:not([data-theme]),:host(:not([data-theme])){color-scheme:dark;--pvc-background-color: rgb(19, 22.5, 30.5);--pvc-color: #c2c7d0;--pvc-text-selection-color: rgba(1, 170, 255, .1875);--pvc-muted-color: #7b8495;--pvc-muted-border-color: #202632;--pvc-primary: #01aaff;--pvc-primary-background: #0172ad;--pvc-primary-border: var(--pvc-primary-background);--pvc-primary-underline: rgba(1, 170, 255, .5);--pvc-primary-hover: #79c0ff;--pvc-primary-hover-background: #017fc0;--pvc-primary-hover-border: var(--pvc-primary-hover-background);--pvc-primary-hover-underline: var(--pvc-primary-hover);--pvc-primary-focus: rgba(1, 170, 255, .375);--pvc-primary-inverse: #fff;--pvc-secondary: #969eaf;--pvc-secondary-background: #525f7a;--pvc-secondary-border: var(--pvc-secondary-background);--pvc-secondary-underline: rgba(150, 158, 175, .5);--pvc-secondary-hover: #b3b9c5;--pvc-secondary-hover-background: #5d6b89;--pvc-secondary-hover-border: var(--pvc-secondary-hover-background);--pvc-secondary-hover-underline: var(--pvc-secondary-hover);--pvc-secondary-focus: rgba(144, 158, 190, .25);--pvc-secondary-inverse: #fff;--pvc-contrast: #dfe3eb;--pvc-contrast-background: #eff1f4;--pvc-contrast-border: var(--pvc-contrast-background);--pvc-contrast-underline: rgba(223, 227, 235, .5);--pvc-contrast-hover: #fff;--pvc-contrast-hover-background: #fff;--pvc-contrast-hover-border: var(--pvc-contrast-hover-background);--pvc-contrast-hover-underline: var(--pvc-contrast-hover);--pvc-contrast-focus: rgba(207, 213, 226, .25);--pvc-contrast-inverse: #000;--pvc-box-shadow: .0145rem .029rem .174rem rgba(7, 8.5, 12, .01698), .0335rem .067rem .402rem rgba(7, 8.5, 12, .024), .0625rem .125rem .75rem rgba(7, 8.5, 12, .03), .1125rem .225rem 1.35rem rgba(7, 8.5, 12, .036), .2085rem .417rem 2.502rem rgba(7, 8.5, 12, .04302), .5rem 1rem 6rem rgba(7, 8.5, 12, .06), 0 0 0 .0625rem rgba(7, 8.5, 12, .015);--pvc-code-background-color: rgb(26, 30.5, 40.25);--pvc-code-color: #8891a4;--pvc-code-kbd-background-color: var(--pvc-color);--pvc-code-kbd-color: var(--pvc-background-color);--pvc-form-element-background-color: rgb(28, 33, 43.5);--pvc-form-element-selected-background-color: #2a3140;--pvc-form-element-border-color: #2a3140;--pvc-form-element-color: #e0e3e7;--pvc-form-element-placeholder-color: #8891a4;--pvc-form-element-active-background-color: rgb(26, 30.5, 40.25);--pvc-form-element-active-border-color: var(--pvc-primary-border);--pvc-form-element-focus-color: var(--pvc-primary-border);--pvc-form-element-disabled-opacity: .5;--pvc-form-element-invalid-border-color: rgb(149.5, 74, 80);--pvc-form-element-invalid-active-border-color: rgb(183.25, 63.5, 59);--pvc-form-element-invalid-focus-color: var(--pvc-form-element-invalid-active-border-color);--pvc-form-element-valid-border-color: #2a7b6f;--pvc-form-element-valid-active-border-color: rgb(22, 137, 105.5);--pvc-form-element-valid-focus-color: var(--pvc-form-element-valid-active-border-color);--pvc-switch-background-color: #333c4e;--pvc-switch-checked-background-color: var(--pvc-primary-background);--pvc-switch-color: #fff;--pvc-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--pvc-range-border-color: #202632;--pvc-range-active-border-color: #2a3140;--pvc-range-thumb-border-color: var(--pvc-background-color);--pvc-range-thumb-color: var(--pvc-secondary-background);--pvc-range-thumb-active-color: var(--pvc-primary-background);--pvc-loading-spinner-opacity: .5;--pvc-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E")}:root:not([data-theme]) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),:host(:not([data-theme])) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pvc-form-element-focus-color: var(--pvc-primary-focus)}:root:not([data-theme]) .pvc-settings-form [aria-busy=true]:not(input,select,textarea).contrast:is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline):before,:host(:not([data-theme])) .pvc-settings-form [aria-busy=true]:not(input,select,textarea).contrast:is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline):before{filter:brightness(0)}}[data-theme=dark]{color-scheme:dark;--pvc-background-color: rgb(19, 22.5, 30.5);--pvc-color: #c2c7d0;--pvc-text-selection-color: rgba(1, 170, 255, .1875);--pvc-muted-color: #7b8495;--pvc-muted-border-color: #202632;--pvc-primary: #01aaff;--pvc-primary-background: #0172ad;--pvc-primary-border: var(--pvc-primary-background);--pvc-primary-underline: rgba(1, 170, 255, .5);--pvc-primary-hover: #79c0ff;--pvc-primary-hover-background: #017fc0;--pvc-primary-hover-border: var(--pvc-primary-hover-background);--pvc-primary-hover-underline: var(--pvc-primary-hover);--pvc-primary-focus: rgba(1, 170, 255, .375);--pvc-primary-inverse: #fff;--pvc-secondary: #969eaf;--pvc-secondary-background: #525f7a;--pvc-secondary-border: var(--pvc-secondary-background);--pvc-secondary-underline: rgba(150, 158, 175, .5);--pvc-secondary-hover: #b3b9c5;--pvc-secondary-hover-background: #5d6b89;--pvc-secondary-hover-border: var(--pvc-secondary-hover-background);--pvc-secondary-hover-underline: var(--pvc-secondary-hover);--pvc-secondary-focus: rgba(144, 158, 190, .25);--pvc-secondary-inverse: #fff;--pvc-contrast: #dfe3eb;--pvc-contrast-background: #eff1f4;--pvc-contrast-border: var(--pvc-contrast-background);--pvc-contrast-underline: rgba(223, 227, 235, .5);--pvc-contrast-hover: #fff;--pvc-contrast-hover-background: #fff;--pvc-contrast-hover-border: var(--pvc-contrast-hover-background);--pvc-contrast-hover-underline: var(--pvc-contrast-hover);--pvc-contrast-focus: rgba(207, 213, 226, .25);--pvc-contrast-inverse: #000;--pvc-box-shadow: .0145rem .029rem .174rem rgba(7, 8.5, 12, .01698), .0335rem .067rem .402rem rgba(7, 8.5, 12, .024), .0625rem .125rem .75rem rgba(7, 8.5, 12, .03), .1125rem .225rem 1.35rem rgba(7, 8.5, 12, .036), .2085rem .417rem 2.502rem rgba(7, 8.5, 12, .04302), .5rem 1rem 6rem rgba(7, 8.5, 12, .06), 0 0 0 .0625rem rgba(7, 8.5, 12, .015);--pvc-code-background-color: rgb(26, 30.5, 40.25);--pvc-code-color: #8891a4;--pvc-code-kbd-background-color: var(--pvc-color);--pvc-code-kbd-color: var(--pvc-background-color);--pvc-form-element-background-color: rgb(28, 33, 43.5);--pvc-form-element-selected-background-color: #2a3140;--pvc-form-element-border-color: #2a3140;--pvc-form-element-color: #e0e3e7;--pvc-form-element-placeholder-color: #8891a4;--pvc-form-element-active-background-color: rgb(26, 30.5, 40.25);--pvc-form-element-active-border-color: var(--pvc-primary-border);--pvc-form-element-focus-color: var(--pvc-primary-border);--pvc-form-element-disabled-opacity: .5;--pvc-form-element-invalid-border-color: rgb(149.5, 74, 80);--pvc-form-element-invalid-active-border-color: rgb(183.25, 63.5, 59);--pvc-form-element-invalid-focus-color: var(--pvc-form-element-invalid-active-border-color);--pvc-form-element-valid-border-color: #2a7b6f;--pvc-form-element-valid-active-border-color: rgb(22, 137, 105.5);--pvc-form-element-valid-focus-color: var(--pvc-form-element-valid-active-border-color);--pvc-switch-background-color: #333c4e;--pvc-switch-checked-background-color: var(--pvc-primary-background);--pvc-switch-color: #fff;--pvc-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--pvc-range-border-color: #202632;--pvc-range-active-border-color: #2a3140;--pvc-range-thumb-border-color: var(--pvc-background-color);--pvc-range-thumb-color: var(--pvc-secondary-background);--pvc-range-thumb-active-color: var(--pvc-primary-background);--pvc-loading-spinner-opacity: .5;--pvc-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E")}[data-theme=dark] input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pvc-form-element-focus-color: var(--pvc-primary-focus)}[data-theme=dark] .pvc-settings-form [aria-busy=true]:not(input,select,textarea).contrast:is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline):before{filter:brightness(0)}.pvc-settings-form progress,.pvc-settings-form [type=checkbox],.pvc-settings-form [type=radio],.pvc-settings-form [type=range]{accent-color:var(--pvc-primary)}.pvc-settings-form :where(a:not([role=button])),.pvc-settings-form [role=link]{--pvc-color: var(--pvc-primary);--pvc-background-color: transparent;--pvc-underline: var(--pvc-primary-underline);outline:none;background-color:var(--pvc-background-color);color:var(--pvc-color);text-decoration:var(--pvc-text-decoration);text-decoration-color:var(--pvc-underline);text-underline-offset:.125em;transition:background-color var(--pvc-transition),color var(--pvc-transition),text-decoration var(--pvc-transition),box-shadow var(--pvc-transition)}.pvc-settings-form :where(a:not([role=button])):is([aria-current]:not([aria-current=false]),:hover,:active,:focus),.pvc-settings-form [role=link]:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pvc-color: var(--pvc-primary-hover);--pvc-underline: var(--pvc-primary-hover-underline);--pvc-text-decoration: underline}.pvc-settings-form :where(a:not([role=button])):focus-visible,.pvc-settings-form [role=link]:focus-visible{box-shadow:0 0 0 var(--pvc-outline-width) var(--pvc-primary-focus)}.pvc-settings-form :where(a:not([role=button])).secondary,.pvc-settings-form [role=link].secondary{--pvc-color: var(--pvc-secondary);--pvc-underline: var(--pvc-secondary-underline)}.pvc-settings-form :where(a:not([role=button])).secondary:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),.pvc-settings-form [role=link].secondary:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pvc-color: var(--pvc-secondary-hover);--pvc-underline: var(--pvc-secondary-hover-underline)}.pvc-settings-form :where(a:not([role=button])).contrast,.pvc-settings-form [role=link].contrast{--pvc-color: var(--pvc-contrast);--pvc-underline: var(--pvc-contrast-underline)}.pvc-settings-form :where(a:not([role=button])).contrast:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),.pvc-settings-form [role=link].contrast:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pvc-color: var(--pvc-contrast-hover);--pvc-underline: var(--pvc-contrast-hover-underline)}.pvc-settings-form a[role=button]{display:inline-block}.pvc-settings-form pre,.pvc-settings-form code,.pvc-settings-form kbd,.pvc-settings-form samp{font-size:.875em;font-family:var(--pvc-font-family)}.pvc-settings-form pre code,.pvc-settings-form pre samp{font-size:inherit;font-family:inherit}.pvc-settings-form pre{-ms-overflow-style:scrollbar;overflow:auto}.pvc-settings-form pre,.pvc-settings-form code,.pvc-settings-form kbd,.pvc-settings-form samp{border-radius:var(--pvc-border-radius);background:var(--pvc-code-background-color);color:var(--pvc-code-color);font-weight:var(--pvc-font-weight);line-height:initial}.pvc-settings-form code,.pvc-settings-form kbd,.pvc-settings-form samp{display:inline-block;padding:.375rem}.pvc-settings-form pre{display:block;margin-bottom:var(--pvc-spacing);overflow-x:auto}.pvc-settings-form pre>code,.pvc-settings-form pre>samp{display:block;padding:var(--pvc-spacing);background:none;line-height:var(--pvc-line-height)}.pvc-settings-form kbd{background-color:var(--pvc-code-kbd-background-color);color:var(--pvc-code-kbd-color);vertical-align:baseline}.pvc-settings-form hr{height:0;margin:var(--pvc-typography-spacing-vertical) 0;border:0;border-top:1px solid var(--pvc-muted-border-color);color:inherit}.pvc-settings-form [hidden],.pvc-settings-form template{display:none!important}.pvc-settings-form canvas{display:inline-block}.pvc-settings-form input,.pvc-settings-form optgroup,.pvc-settings-form select,.pvc-settings-form textarea{margin:0;font-size:1rem;line-height:var(--pvc-line-height);font-family:inherit;letter-spacing:inherit}.pvc-settings-form input{overflow:visible}.pvc-settings-form select{text-transform:none}.pvc-settings-form legend{max-width:100%;padding:0;color:inherit;white-space:normal}.pvc-settings-form textarea{overflow:auto}.pvc-settings-form [type=checkbox],.pvc-settings-form [type=radio]{padding:0}.pvc-settings-form ::-webkit-inner-spin-button,.pvc-settings-form ::-webkit-outer-spin-button{height:auto}.pvc-settings-form [type=search]{-webkit-appearance:textfield;outline-offset:-2px}.pvc-settings-form [type=search]::-webkit-search-decoration{-webkit-appearance:none}.pvc-settings-form ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.pvc-settings-form ::-moz-focus-inner{padding:0;border-style:none}.pvc-settings-form :-moz-focusring{outline:none}.pvc-settings-form :-moz-ui-invalid{box-shadow:none}.pvc-settings-form ::-ms-expand{display:none}.pvc-settings-form [type=file],.pvc-settings-form [type=range]{padding:0;border-width:0}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range]){height:calc(1rem * var(--pvc-line-height) + var(--pvc-form-element-spacing-vertical) * 2 + var(--pvc-border-width) * 2)}.pvc-settings-form fieldset{width:100%;margin:0;margin-bottom:var(--pvc-spacing);padding:0;border:0}.pvc-settings-form label,.pvc-settings-form fieldset legend{display:block;margin-bottom:calc(var(--pvc-spacing) * .375);color:var(--pvc-color);font-weight:var(--pvc-form-label-font-weight, var(--pvc-font-weight))}.pvc-settings-form fieldset legend{margin-bottom:calc(var(--pvc-spacing) * .5)}.pvc-settings-form input:not([type=checkbox],[type=radio]),.pvc-settings-form button[type=submit],.pvc-settings-form select,.pvc-settings-form textarea{width:100%}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file]),.pvc-settings-form select,.pvc-settings-form textarea{appearance:none;padding:var(--pvc-form-element-spacing-vertical) var(--pvc-form-element-spacing-horizontal)}.pvc-settings-form input,.pvc-settings-form select,.pvc-settings-form textarea{--pvc-background-color: var(--pvc-form-element-background-color);--pvc-border-color: var(--pvc-form-element-border-color);--pvc-color: var(--pvc-form-element-color);--pvc-box-shadow: none;border:var(--pvc-border-width) solid var(--pvc-border-color);border-radius:var(--pvc-border-radius);outline:none;background-color:var(--pvc-background-color);box-shadow:var(--pvc-box-shadow);color:var(--pvc-color);font-weight:var(--pvc-font-weight);transition:background-color var(--pvc-transition),border-color var(--pvc-transition),color var(--pvc-transition),box-shadow var(--pvc-transition)}.pvc-settings-form input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[readonly]):is(:active,:focus),.pvc-settings-form :where(select,textarea):not([readonly]):is(:active,:focus){--pvc-background-color: var(--pvc-form-element-active-background-color)}.pvc-settings-form input:not([type=submit],[type=button],[type=reset],[role=switch],[readonly]):is(:active,:focus),.pvc-settings-form :where(select,textarea):not([readonly]):is(:active,:focus){--pvc-border-color: var(--pvc-form-element-active-border-color)}.pvc-settings-form input:not([type=submit],[type=button],[type=reset],[type=range],[type=file],[readonly]):focus,.pvc-settings-form :where(select,textarea):not([readonly]):focus{--pvc-box-shadow: 0 0 0 var(--pvc-outline-width) var(--pvc-form-element-focus-color)}.pvc-settings-form input:not([type=submit],[type=button],[type=reset])[disabled],.pvc-settings-form select[disabled],.pvc-settings-form textarea[disabled],.pvc-settings-form label[aria-disabled=true],.pvc-settings-form :where(fieldset[disabled]) :is(input:not([type=submit],[type=button],[type=reset]),select,textarea){opacity:var(--pvc-form-element-disabled-opacity);pointer-events:none}.pvc-settings-form label[aria-disabled=true] input[disabled]{opacity:1}.pvc-settings-form :where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid]{padding-right:calc(var(--pvc-form-element-spacing-horizontal) + 1.5rem)!important;padding-left:var(--pvc-form-element-spacing-horizontal);padding-inline-start:var(--pvc-form-element-spacing-horizontal)!important;padding-inline-end:calc(var(--pvc-form-element-spacing-horizontal) + 1.5rem)!important;background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}.pvc-settings-form :where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid=false]:not(select){background-image:var(--pvc-icon-valid)}.pvc-settings-form :where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid=true]:not(select){background-image:var(--pvc-icon-invalid)}.pvc-settings-form :where(input,select,textarea)[aria-invalid=false]{--pvc-border-color: var(--pvc-form-element-valid-border-color)}.pvc-settings-form :where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--pvc-border-color: var(--pvc-form-element-valid-active-border-color) !important}.pvc-settings-form :where(input,select,textarea)[aria-invalid=false]:is(:active,:focus):not([type=checkbox],[type=radio]){--pvc-box-shadow: 0 0 0 var(--pvc-outline-width) var(--pvc-form-element-valid-focus-color) !important}.pvc-settings-form :where(input,select,textarea)[aria-invalid=true]{--pvc-border-color: var(--pvc-form-element-invalid-border-color)}.pvc-settings-form :where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--pvc-border-color: var(--pvc-form-element-invalid-active-border-color) !important}.pvc-settings-form :where(input,select,textarea)[aria-invalid=true]:is(:active,:focus):not([type=checkbox],[type=radio]){--pvc-box-shadow: 0 0 0 var(--pvc-outline-width) var(--pvc-form-element-invalid-focus-color) !important}[dir=rtl] .pvc-settings-form :where(input,select,textarea):not([type=checkbox],[type=radio]):is([aria-invalid],[aria-invalid=true],[aria-invalid=false]){background-position:center left .75rem}.pvc-settings-form input::placeholder,.pvc-settings-form input::-webkit-input-placeholder,.pvc-settings-form textarea::placeholder,.pvc-settings-form textarea::-webkit-input-placeholder,.pvc-settings-form select:invalid{color:var(--pvc-form-element-placeholder-color);opacity:1}.pvc-settings-form input:not([type=checkbox],[type=radio]),.pvc-settings-form select,.pvc-settings-form textarea{margin-bottom:var(--pvc-spacing)}.pvc-settings-form select::-ms-expand{border:0;background-color:transparent}.pvc-settings-form select:not([multiple],[size]){padding-right:calc(var(--pvc-form-element-spacing-horizontal) + 1.5rem);padding-left:var(--pvc-form-element-spacing-horizontal);padding-inline-start:var(--pvc-form-element-spacing-horizontal);padding-inline-end:calc(var(--pvc-form-element-spacing-horizontal) + 1.5rem);background-image:var(--pvc-icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}.pvc-settings-form select[multiple] option:checked{background:var(--pvc-form-element-selected-background-color);color:var(--pvc-form-element-color)}[dir=rtl] .pvc-settings-form select:not([multiple],[size]){background-position:center left .75rem}.pvc-settings-form textarea{display:block;resize:vertical}.pvc-settings-form textarea[aria-invalid]{--pvc-icon-height: calc(1rem * var(--pvc-line-height) + var(--pvc-form-element-spacing-vertical) * 2 + var(--pvc-border-width) * 2);background-position:top right .75rem!important;background-size:1rem var(--pvc-icon-height)!important}.pvc-settings-form :where(input,select,textarea,fieldset)+small{display:block;width:100%;margin-top:calc(var(--pvc-spacing) * -.75);margin-bottom:var(--pvc-spacing);color:var(--pvc-muted-color)}.pvc-settings-form :where(input,select,textarea,fieldset)[aria-invalid=false]+small{color:var(--pvc-ins-color)}.pvc-settings-form :where(input,select,textarea,fieldset)[aria-invalid=true]+small{color:var(--pvc-del-color)}.pvc-settings-form label>:where(input,select,textarea){margin-top:calc(var(--pvc-spacing) * .25)}.pvc-settings-form label:has([type=checkbox],[type=radio]){width:fit-content;cursor:pointer}.pvc-settings-form [type=checkbox],.pvc-settings-form [type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-.125em;margin-inline-end:.5em;border-width:var(--pvc-border-width);vertical-align:middle;cursor:pointer}.pvc-settings-form [type=checkbox]::-ms-check,.pvc-settings-form [type=radio]::-ms-check{display:none}.pvc-settings-form [type=checkbox]:checked,.pvc-settings-form [type=checkbox]:checked:active,.pvc-settings-form [type=checkbox]:checked:focus,.pvc-settings-form [type=radio]:checked,.pvc-settings-form [type=radio]:checked:active,.pvc-settings-form [type=radio]:checked:focus{--pvc-background-color: var(--pvc-primary-background);--pvc-border-color: var(--pvc-primary-border);background-image:var(--pvc-icon-checkbox);background-position:center;background-size:.75em auto;background-repeat:no-repeat}.pvc-settings-form [type=checkbox]~label,.pvc-settings-form [type=radio]~label{display:inline-block;margin-bottom:0;cursor:pointer}.pvc-settings-form [type=checkbox]~label:not(:last-of-type),.pvc-settings-form [type=radio]~label:not(:last-of-type){margin-inline-end:1em}.pvc-settings-form [type=checkbox]:indeterminate{--pvc-background-color: var(--pvc-primary-background);--pvc-border-color: var(--pvc-primary-border);background-image:var(--pvc-icon-minus);background-position:center;background-size:.75em auto;background-repeat:no-repeat}.pvc-settings-form [type=radio]{border-radius:50%}.pvc-settings-form [type=radio]:checked,.pvc-settings-form [type=radio]:checked:active,.pvc-settings-form [type=radio]:checked:focus{--pvc-background-color: var(--pvc-primary-inverse);border-width:.35em;background-image:none}.pvc-settings-form [type=checkbox][role=switch]{--pvc-background-color: var(--pvc-switch-background-color);--pvc-color: var(--pvc-switch-color);width:2.25em;height:1.25em;border:var(--pvc-border-width) solid var(--pvc-border-color);border-radius:1.25em;background-color:var(--pvc-background-color);line-height:1.25em}.pvc-settings-form [type=checkbox][role=switch]:not([aria-invalid]){--pvc-border-color: var(--pvc-switch-background-color)}.pvc-settings-form [type=checkbox][role=switch]:before{display:block;aspect-ratio:1;height:100%;border-radius:50%;background-color:var(--pvc-color);box-shadow:var(--pvc-switch-thumb-box-shadow);content:"";transition:margin .1s ease-in-out}.pvc-settings-form [type=checkbox][role=switch]:focus{--pvc-background-color: var(--pvc-switch-background-color);--pvc-border-color: var(--pvc-switch-background-color)}.pvc-settings-form [type=checkbox][role=switch]:checked{--pvc-background-color: var(--pvc-switch-checked-background-color);--pvc-border-color: var(--pvc-switch-checked-background-color);background-image:none}.pvc-settings-form [type=checkbox][role=switch]:checked:before{margin-inline-start:1em}.pvc-settings-form [type=checkbox][role=switch][disabled]{--pvc-background-color: var(--pvc-border-color)}.pvc-settings-form [type=checkbox][aria-invalid=false]:checked,.pvc-settings-form [type=checkbox][aria-invalid=false]:checked:active,.pvc-settings-form [type=checkbox][aria-invalid=false]:checked:focus,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked:active,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked:focus{--pvc-background-color: var(--pvc-form-element-valid-border-color)}.pvc-settings-form [type=checkbox]:checked[aria-invalid=true],.pvc-settings-form [type=checkbox]:checked:active[aria-invalid=true],.pvc-settings-form [type=checkbox]:checked:focus[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked:active[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked:focus[aria-invalid=true]{--pvc-background-color: var(--pvc-form-element-invalid-border-color)}.pvc-settings-form [type=checkbox][aria-invalid=false]:checked,.pvc-settings-form [type=checkbox][aria-invalid=false]:checked:active,.pvc-settings-form [type=checkbox][aria-invalid=false]:checked:focus,.pvc-settings-form [type=radio][aria-invalid=false]:checked,.pvc-settings-form [type=radio][aria-invalid=false]:checked:active,.pvc-settings-form [type=radio][aria-invalid=false]:checked:focus,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked:active,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked:focus{--pvc-border-color: var(--pvc-form-element-valid-border-color)}.pvc-settings-form [type=checkbox]:checked[aria-invalid=true],.pvc-settings-form [type=checkbox]:checked:active[aria-invalid=true],.pvc-settings-form [type=checkbox]:checked:focus[aria-invalid=true],.pvc-settings-form [type=radio]:checked[aria-invalid=true],.pvc-settings-form [type=radio]:checked:active[aria-invalid=true],.pvc-settings-form [type=radio]:checked:focus[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked:active[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked:focus[aria-invalid=true]{--pvc-border-color: var(--pvc-form-element-invalid-border-color)}.pvc-settings-form [type=color]::-webkit-color-swatch-wrapper{padding:0}.pvc-settings-form [type=color]::-moz-focus-inner{padding:0}.pvc-settings-form [type=color]::-webkit-color-swatch{border:0;border-radius:calc(var(--pvc-border-radius) * .5)}.pvc-settings-form [type=color]::-moz-color-swatch{border:0;border-radius:calc(var(--pvc-border-radius) * .5)}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file]):is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){--pvc-icon-position: .75rem;--pvc-icon-width: 1rem;padding-right:calc(var(--pvc-icon-width) + var(--pvc-icon-position));background-image:var(--pvc-icon-date);background-position:center right var(--pvc-icon-position);background-size:var(--pvc-icon-width) auto;background-repeat:no-repeat}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=time]{background-image:var(--pvc-icon-time)}.pvc-settings-form [type=date]::-webkit-calendar-picker-indicator,.pvc-settings-form [type=datetime-local]::-webkit-calendar-picker-indicator,.pvc-settings-form [type=month]::-webkit-calendar-picker-indicator,.pvc-settings-form [type=time]::-webkit-calendar-picker-indicator,.pvc-settings-form [type=week]::-webkit-calendar-picker-indicator{width:var(--pvc-icon-width);margin-right:calc(var(--pvc-icon-width) * -1);margin-left:var(--pvc-icon-position);opacity:0}@-moz-document url-prefix(){.pvc-settings-form [type=date],.pvc-settings-form [type=datetime-local],.pvc-settings-form [type=month],.pvc-settings-form [type=time],.pvc-settings-form [type=week]{padding-right:var(--pvc-form-element-spacing-horizontal)!important;background-image:none!important}}[dir=rtl] .pvc-settings-form :is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){text-align:right}.pvc-settings-form [type=file]{--pvc-color: var(--pvc-muted-color);margin-left:calc(var(--pvc-outline-width) * -1);padding:calc(var(--pvc-form-element-spacing-vertical) * .5) 0;padding-left:var(--pvc-outline-width);border:0;border-radius:0;background:none}.pvc-settings-form [type=file]::file-selector-button{margin-right:calc(var(--pvc-spacing) / 2);padding:calc(var(--pvc-form-element-spacing-vertical) * .5) var(--pvc-form-element-spacing-horizontal)}.pvc-settings-form [type=file]:is(:hover,:active,:focus)::file-selector-button{--pvc-background-color: var(--pvc-secondary-hover-background);--pvc-border-color: var(--pvc-secondary-hover-border)}.pvc-settings-form [type=file]:focus::file-selector-button{--pvc-box-shadow: var(--pvc-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--pvc-outline-width) var(--pvc-secondary-focus)}.pvc-settings-form [type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:none}.pvc-settings-form [type=range]::-webkit-slider-runnable-track{width:100%;height:.375rem;border-radius:var(--pvc-border-radius);background-color:var(--pvc-range-border-color);transition:background-color var(--pvc-transition),box-shadow var(--pvc-transition)}.pvc-settings-form [type=range]::-moz-range-track{width:100%;height:.375rem;border-radius:var(--pvc-border-radius);background-color:var(--pvc-range-border-color);transition:background-color var(--pvc-transition),box-shadow var(--pvc-transition)}.pvc-settings-form [type=range]::-ms-track{width:100%;height:.375rem;border-radius:var(--pvc-border-radius);background-color:var(--pvc-range-border-color);transition:background-color var(--pvc-transition),box-shadow var(--pvc-transition)}.pvc-settings-form [type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pvc-range-thumb-border-color);border-radius:50%;background-color:var(--pvc-range-thumb-color);cursor:pointer;transition:background-color var(--pvc-transition),transform var(--pvc-transition)}.pvc-settings-form [type=range]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pvc-range-thumb-border-color);border-radius:50%;background-color:var(--pvc-range-thumb-color);cursor:pointer;transition:background-color var(--pvc-transition),transform var(--pvc-transition)}.pvc-settings-form [type=range]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pvc-range-thumb-border-color);border-radius:50%;background-color:var(--pvc-range-thumb-color);cursor:pointer;transition:background-color var(--pvc-transition),transform var(--pvc-transition)}.pvc-settings-form [type=range]:active,.pvc-settings-form [type=range]:focus-within{--pvc-range-border-color: var(--pvc-range-active-border-color);--pvc-range-thumb-color: var(--pvc-range-thumb-active-color)}.pvc-settings-form [type=range]:active::-webkit-slider-thumb{transform:scale(1.25)}.pvc-settings-form [type=range]:active::-moz-range-thumb{transform:scale(1.25)}.pvc-settings-form [type=range]:active::-ms-thumb{transform:scale(1.25)}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{padding-inline-start:calc(var(--pvc-form-element-spacing-horizontal) + 1.75rem);background-image:var(--pvc-icon-search);background-position:center left calc(var(--pvc-form-element-spacing-horizontal) + .125rem);background-size:1rem auto;background-repeat:no-repeat}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{padding-inline-start:calc(var(--pvc-form-element-spacing-horizontal) + 1.75rem)!important;background-position:center left 1.125rem,center right .75rem}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=false]{background-image:var(--pvc-icon-search),var(--pvc-icon-valid)}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=true]{background-image:var(--pvc-icon-search),var(--pvc-icon-invalid)}[dir=rtl] .pvc-settings-form :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{background-position:center right 1.125rem}[dir=rtl] .pvc-settings-form :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{background-position:center right 1.125rem,center left .75rem}.pvc-settings-form [role=search],.pvc-settings-form [role=group]{display:inline-flex;position:relative;width:100%;margin-bottom:var(--pvc-spacing);border-radius:var(--pvc-border-radius);box-shadow:var(--pvc-group-box-shadow, 0 0 0 rgba(0, 0, 0, 0));vertical-align:middle;transition:box-shadow var(--pvc-transition)}.pvc-settings-form [role=search]>*,.pvc-settings-form [role=search] input:not([type=checkbox],[type=radio]),.pvc-settings-form [role=search] select,.pvc-settings-form [role=group]>*,.pvc-settings-form [role=group] input:not([type=checkbox],[type=radio]),.pvc-settings-form [role=group] select{position:relative;flex:1 1 auto;margin-bottom:0}.pvc-settings-form [role=search]>*:not(:first-child),.pvc-settings-form [role=search] input:not([type=checkbox],[type=radio]):not(:first-child),.pvc-settings-form [role=search] select:not(:first-child),.pvc-settings-form [role=group]>*:not(:first-child),.pvc-settings-form [role=group] input:not([type=checkbox],[type=radio]):not(:first-child),.pvc-settings-form [role=group] select:not(:first-child){margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.pvc-settings-form [role=search]>*:not(:last-child),.pvc-settings-form [role=search] input:not([type=checkbox],[type=radio]):not(:last-child),.pvc-settings-form [role=search] select:not(:last-child),.pvc-settings-form [role=group]>*:not(:last-child),.pvc-settings-form [role=group] input:not([type=checkbox],[type=radio]):not(:last-child),.pvc-settings-form [role=group] select:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.pvc-settings-form [role=search]>*:focus,.pvc-settings-form [role=search] input:not([type=checkbox],[type=radio]):focus,.pvc-settings-form [role=search] select:focus,.pvc-settings-form [role=group]>*:focus,.pvc-settings-form [role=group] input:not([type=checkbox],[type=radio]):focus,.pvc-settings-form [role=group] select:focus{z-index:2}.pvc-settings-form [role=search] button:not(:first-child),.pvc-settings-form [role=search] [type=submit]:not(:first-child),.pvc-settings-form [role=search] [type=reset]:not(:first-child),.pvc-settings-form [role=search] [type=button]:not(:first-child),.pvc-settings-form [role=search] [role=button]:not(:first-child),.pvc-settings-form [role=search] input:not([type=checkbox],[type=radio]):not(:first-child),.pvc-settings-form [role=search] select:not(:first-child),.pvc-settings-form [role=group] button:not(:first-child),.pvc-settings-form [role=group] [type=submit]:not(:first-child),.pvc-settings-form [role=group] [type=reset]:not(:first-child),.pvc-settings-form [role=group] [type=button]:not(:first-child),.pvc-settings-form [role=group] [role=button]:not(:first-child),.pvc-settings-form [role=group] input:not([type=checkbox],[type=radio]):not(:first-child),.pvc-settings-form [role=group] select:not(:first-child){margin-left:calc(var(--pvc-border-width) * -1)}.pvc-settings-form [role=search] button,.pvc-settings-form [role=search] [type=submit],.pvc-settings-form [role=search] [type=reset],.pvc-settings-form [role=search] [type=button],.pvc-settings-form [role=search] [role=button],.pvc-settings-form [role=group] button,.pvc-settings-form [role=group] [type=submit],.pvc-settings-form [role=group] [type=reset],.pvc-settings-form [role=group] [type=button],.pvc-settings-form [role=group] [role=button]{width:auto}@supports selector(:has(*)){.pvc-settings-form [role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus),.pvc-settings-form [role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus){--pvc-group-box-shadow: var(--pvc-group-box-shadow-focus-with-button)}.pvc-settings-form [role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) input:not([type=checkbox],[type=radio]),.pvc-settings-form [role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) select,.pvc-settings-form [role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) input:not([type=checkbox],[type=radio]),.pvc-settings-form [role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) select{border-color:transparent}.pvc-settings-form [role=search]:has(input:not([type=submit],[type=button]):focus,select:focus),.pvc-settings-form [role=group]:has(input:not([type=submit],[type=button]):focus,select:focus){--pvc-group-box-shadow: var(--pvc-group-box-shadow-focus-with-input)}.pvc-settings-form [role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) button,.pvc-settings-form [role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=submit],.pvc-settings-form [role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=button],.pvc-settings-form [role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [role=button],.pvc-settings-form [role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) button,.pvc-settings-form [role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=submit],.pvc-settings-form [role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=button],.pvc-settings-form [role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [role=button]{--pvc-button-box-shadow: 0 0 0 var(--pvc-border-width) var(--pvc-primary-border);--pvc-button-hover-box-shadow: 0 0 0 var(--pvc-border-width) var(--pvc-primary-hover-border)}.pvc-settings-form [role=search] button:focus,.pvc-settings-form [role=search] [type=submit]:focus,.pvc-settings-form [role=search] [type=reset]:focus,.pvc-settings-form [role=search] [type=button]:focus,.pvc-settings-form [role=search] [role=button]:focus,.pvc-settings-form [role=group] button:focus,.pvc-settings-form [role=group] [type=submit]:focus,.pvc-settings-form [role=group] [type=reset]:focus,.pvc-settings-form [role=group] [type=button]:focus,.pvc-settings-form [role=group] [role=button]:focus{box-shadow:none}}.pvc-settings-form [role=search]>*:first-child{border-top-left-radius:5rem;border-bottom-left-radius:5rem}.pvc-settings-form [role=search]>*:last-child{border-top-right-radius:5rem;border-bottom-right-radius:5rem}.pvc-settings-form [aria-busy=true]:not(input,select,textarea,html,form){white-space:nowrap}.pvc-settings-form [aria-busy=true]:not(input,select,textarea,html,form):before{display:inline-block;width:1em;height:1em;background-image:var(--pvc-icon-loading);background-size:1em auto;background-repeat:no-repeat;content:"";vertical-align:-.125em}.pvc-settings-form [aria-busy=true]:not(input,select,textarea,html,form):not(:empty):before{margin-inline-end:calc(var(--pvc-spacing) * .5)}.pvc-settings-form [aria-busy=true]:not(input,select,textarea,html,form):empty{text-align:center}.pvc-settings-form button[aria-busy=true],.pvc-settings-form [type=submit][aria-busy=true],.pvc-settings-form [type=button][aria-busy=true],.pvc-settings-form [type=reset][aria-busy=true],.pvc-settings-form [role=button][aria-busy=true],.pvc-settings-form a[aria-busy=true]{pointer-events:none}.pvc-settings-form [aria-controls]{cursor:pointer}.pvc-settings-form [aria-disabled=true],.pvc-settings-form [disabled]{cursor:not-allowed}.pvc-settings-form [aria-hidden=false][hidden]{display:initial}.pvc-settings-form [aria-hidden=false][hidden]:not(:focus){clip:rect(0,0,0,0);position:absolute}.pvc-settings-form a,.pvc-settings-form area,.pvc-settings-form button,.pvc-settings-form input,.pvc-settings-form label,.pvc-settings-form select,.pvc-settings-form summary,.pvc-settings-form textarea,.pvc-settings-form [tabindex]{-ms-touch-action:manipulation}.pvc-settings-form [dir=rtl]{direction:rtl}.pvc-settings-wrapper{--pvc-theme-color: var(--wp-admin-theme-color, #2271b1);--pvc-theme-color-rgb: var(--wp-admin-theme-color--rgb, 34, 113, 177);--pvc-theme-color-darker-10: var(--wp-admin-theme-color-darker-10, #135e96);--pvc-border-width-focus: var(--wp-admin-border-width-focus, 2px);--pvc-surface: #fff;--pvc-surface-muted: #ebebeb;--pvc-text: #3c434a;--pvc-text-strong: #070707;--pvc-text-input: #2c3338;--pvc-text-muted: #757575;--pvc-border: #c3c4c7;--pvc-border-input: #8c8f94;--pvc-button-bg: #f6f7f7;--pvc-button-bg-hover: #f0f0f1;--pvc-button-border: #8c8f94;--pvc-button-border-hover: #757575;--pvc-button-text: #2c3338;--pvc-button-text-hover: #070707;--pvc-button-focus: var(--pvc-theme-color);--pvc-font-family-emoji: inherit;--pvc-font-family-sans-serif: inherit;--pvc-font-family-monospace: inherit;--pvc-font-family: var(--pvc-font-family-sans-serif);--pvc-line-height: 1.5;--pvc-font-weight: 400;--pvc-font-size: 100%;--pvc-text-underline-offset: .1rem;--pvc-border-radius: .25rem;--pvc-border-width: .0625rem;--pvc-outline-width: .125rem;--pvc-transition: .2s ease-in-out;--pvc-spacing: 1rem;--pvc-typography-spacing-vertical: 1rem;--pvc-block-spacing-vertical: 20px;--pvc-block-spacing-horizontal: 20px;--pvc-grid-column-gap: 20px;--pvc-grid-row-gap: 20px;--pvc-form-element-spacing-vertical: .45rem;--pvc-form-element-spacing-horizontal: .8rem;--pvc-form-element-disabled-opacity: .6;--pvc-group-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--pvc-group-box-shadow-focus-with-button: 0 0 0 var(--pvc-outline-width) var(--pvc-primary-focus);--pvc-group-box-shadow-focus-with-input: 0 0 0 .0625rem var(--pvc-form-element-border-color);--pvc-modal-overlay-backdrop-filter: blur(.375rem);--pvc-nav-element-spacing-vertical: 1rem;--pvc-nav-element-spacing-horizontal: .5rem;--pvc-nav-link-spacing-vertical: .5rem;--pvc-nav-link-spacing-horizontal: .5rem;--pvc-nav-breadcrumb-divider: ">";--pvc-primary: var(--pvc-theme-color);--pvc-primary-background: var(--pvc-theme-color);--pvc-primary-border: var(--pvc-theme-color);--pvc-primary-underline: rgba(var(--pvc-theme-color-rgb), .5);--pvc-primary-hover: var(--pvc-theme-color-darker-10);--pvc-primary-hover-background: var(--pvc-theme-color-darker-10);--pvc-primary-hover-border: var(--pvc-theme-color-darker-10);--pvc-primary-hover-underline: var(--pvc-theme-color-darker-10);--pvc-primary-focus: rgba(var(--pvc-theme-color-rgb), .25);--pvc-primary-inverse: #fff;--pvc-secondary: var(--pvc-button-text);--pvc-secondary-background: var(--pvc-button-bg);--pvc-secondary-border: var(--pvc-button-border);--pvc-secondary-underline: rgba(var(--pvc-theme-color-rgb), .4);--pvc-secondary-hover: var(--pvc-button-text-hover);--pvc-secondary-hover-background: var(--pvc-button-bg-hover);--pvc-secondary-hover-border: var(--pvc-button-border-hover);--pvc-secondary-hover-underline: var(--pvc-button-border-hover);--pvc-secondary-focus: var(--pvc-button-focus);--pvc-secondary-inverse: var(--pvc-button-text);--pvc-contrast: var(--pvc-text-strong);--pvc-contrast-background: var(--pvc-text-strong);--pvc-contrast-border: var(--pvc-text-strong);--pvc-contrast-hover: #000;--pvc-contrast-hover-background: #000;--pvc-contrast-hover-border: #000;--pvc-contrast-focus: rgba(var(--pvc-theme-color-rgb), .25);--pvc-contrast-inverse: #fff;color-scheme:light;--pvc-background-color: var(--pvc-surface);--pvc-color: var(--pvc-text);--pvc-muted-color: var(--pvc-text-muted);--pvc-muted-border-color: var(--pvc-border);--pvc-box-shadow: 0 1px 1px rgba(0, 0, 0, .04);--pvc-text-selection-color: rgba(var(--pvc-theme-color-rgb), .25);--pvc-table-border-color: var(--pvc-border);--pvc-table-row-stripped-background-color: var(--pvc-surface-muted);--pvc-form-element-background-color: var(--pvc-surface);--pvc-form-element-active-background-color: var(--pvc-surface);--pvc-form-element-selected-background-color: var(--pvc-surface-muted);--pvc-form-element-border-color: var(--pvc-border-input);--pvc-form-element-color: var(--pvc-text-input);--pvc-form-element-placeholder-color: var(--pvc-text-muted);--pvc-form-element-active-border-color: var(--pvc-theme-color);--pvc-form-element-focus-color: var(--pvc-theme-color);--pvc-switch-background-color: var(--pvc-border-input);--pvc-switch-checked-background-color: var(--pvc-theme-color);--pvc-switch-color: #fff;--pvc-outline-width: var(--pvc-border-width-focus);--pvc-block-spacing: var(--pvc-block-spacing-vertical) var(--pvc-block-spacing-horizontal);--pvc-form-element-spacing: var(--pvc-form-element-spacing-vertical) var(--pvc-form-element-spacing-horizontal);--pvc-typography-spacing: var(--pvc-typography-spacing-vertical)}.pvc-settings-wrapper .button.button-secondary{--pvc-primary: var(--pvc-secondary);--pvc-primary-background: var(--pvc-secondary-background);--pvc-primary-border: var(--pvc-secondary-border);--pvc-primary-hover: var(--pvc-secondary-hover);--pvc-primary-hover-background: var(--pvc-secondary-hover-background);--pvc-primary-hover-border: var(--pvc-secondary-hover-border);--pvc-primary-hover-underline: var(--pvc-secondary-hover-underline);--pvc-primary-focus: var(--pvc-secondary-focus);--pvc-primary-inverse: var(--pvc-secondary-inverse)}.pvc-settings-wrapper code,.pvc-settings-wrapper kbd,.pvc-settings-wrapper samp{background:#f0f0f1;background:#00000012;color:var(--pvc-text)}.wp-admin #wpcontent{padding:0}.wp-admin #wpbody-content>.notice,.wp-admin #wpbody-content>.error,.wp-admin #wpbody-content>.updated,.wp-admin #wpbody-content>.info{display:none}.wp-admin .wrap{margin:0}.pvc-settings-wrapper .header-wrapper{padding:20px 40px;background-color:var(--pvc-surface);border-bottom:1px solid var(--pvc-surface-muted)}.pvc-settings-wrapper .content-wrapper{padding:0 40px}.pvc-settings-wrapper .header-title{font-weight:600;font-size:18px;line-height:1;color:var(--pvc-text-strong)}.pvc-settings-wrapper .nav-tab-wrapper{display:flex;flex-wrap:wrap;list-style:none;padding:10px 40px 0;gap:30px;margin:0 0 20px;background:var(--pvc-surface);border-bottom:1px solid var(--pvc-surface-muted)}.pvc-settings-wrapper .nav-tab-wrapper .nav-tab{margin:2px 0 0;border:0;background:none;font-weight:600;font-size:14px;line-height:20px;padding:0 0 10px;text-decoration:none;border-bottom:2px solid transparent}.pvc-settings-wrapper .nav-tab-wrapper .nav-tab:hover,.pvc-settings-wrapper .nav-tab-wrapper .nav-tab:focus{color:var(--wp-admin-theme-color);outline:none;box-shadow:none}.pvc-settings-wrapper .nav-tab-wrapper .nav-tab.nav-tab-active{border-bottom-color:var(--wp-admin-theme-color)}.pvc-settings form{min-width:463px;width:auto;flex:1 1 auto}.pvc-settings form input[type=radio]:checked{background-color:var(--pvc-form-element-background-color)}.pvc-settings input:not([type=checkbox],[type=radio],[type=submit],[type=reset],[type=button]){width:auto;min-width:80px;max-width:100%;font-size:var(--pvc-font-size);line-height:var(--pvc-line-height);border-radius:var(--pvc-border-radius);margin-bottom:0;padding:var(--pvc-form-element-spacing-vertical) var(--pvc-form-element-spacing-horizontal);min-height:calc(1rem * var(--pvc-line-height) + var(--pvc-form-element-spacing-vertical) * 2 + var(--pvc-border-width) * 2)}.pvc-settings input:not([type=checkbox],[type=radio],[type=submit],[type=reset],[type=button]).large-text{width:100%}.pvc-settings input:not([type=checkbox],[type=radio],[type=submit],[type=reset],[type=button]).regular-text{width:25rem}.pvc-settings input:not([type=checkbox],[type=radio],[type=submit],[type=reset],[type=button]).small-text{width:8rem}.pvc-settings [type=search]{--pvc-border-radius: .25rem}.pvc-settings select,.pvc-settings textarea{width:auto;max-width:100%;font-size:var(--pvc-font-size);line-height:var(--pvc-line-height);margin-bottom:0}.pvc-settings select{width:25rem;min-height:calc(1rem * var(--pvc-line-height) + var(--pvc-form-element-spacing-vertical) * 2 + var(--pvc-border-width) * 2)}.pvc-settings [type=submit].button,.pvc-settings [type=button].button{width:auto;font-size:var(--pvc-font-size);line-height:var(--pvc-line-height);border-radius:var(--pvc-border-radius);min-height:calc(1rem * var(--pvc-line-height) + var(--pvc-form-element-spacing-vertical) * 2 + var(--pvc-border-width) * 2)}.pvc-settings [type=submit].button.button-small,.pvc-settings [type=button].button.button-small{font-size:calc(var(--pvc-font-size) * .875);height:auto}.pvc-settings span[role=button]{background:none;border:none;padding:0;margin:0;font-size:inherit}.pvc-settings .submit{display:flex;gap:10px}.pvc-settings .pvc-sidebar{width:300px;flex:0 0 300px;margin-left:20px}.pvc-settings.has-sidebar{display:flex;justify-content:space-between}@media(max-width:960px){.pvc-settings.has-sidebar{flex-direction:column}.pvc-settings form{width:100%}.pvc-settings .pvc-sidebar{width:100%;flex:0 0 auto;margin-left:0;margin-top:20px}}.pvc-settings p.help,.pvc-settings p.description,.pvc-settings span.description{font-size:13px;font-style:italic;color:var(--pvc-text-muted)}.pvc-range-field{display:flex;gap:10px;align-items:center}.pvc-range-field input[type=range]{appearance:none;color-scheme:light;background:transparent;accent-color:var(--pvc-theme-color)}.pvc-range-field input[type=range]::-webkit-slider-runnable-track{height:4px;background:#dcdcde;border-radius:999px}.pvc-range-field input[type=range]::-webkit-slider-thumb{appearance:none;width:16px;height:16px;margin-top:-6px;border-radius:50%;background:var(--pvc-theme-color);border:1px solid var(--pvc-theme-color)}.pvc-range-field input[type=range]::-moz-range-track{height:4px;background:#dcdcde;border-radius:999px}.pvc-range-field input[type=range]::-moz-range-progress{height:4px;background:var(--pvc-theme-color);border-radius:999px}.pvc-range-field input[type=range]::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background:var(--pvc-theme-color);border:1px solid var(--pvc-theme-color)}.pvc-buttons-group{display:flex;gap:10px}.pvc-buttons-group [type=submit].button,.pvc-buttons-group [type=button].button{margin:0}.pvc-field-group label{margin-right:10px}.pvc-field-group.horizontal label{display:inline-block}.pvc-field-group.vertical label{display:block;margin-bottom:8px}.pvc-field input[type=checkbox]:checked:not([role=switch]):before,.pvc-field input[type=radio]:checked:before{display:none}.pvc-field input[type=checkbox][role=switch]:checked:before{width:auto;margin-top:0;margin-bottom:0}.pvc-disabled{opacity:var(--pvc-form-element-disabled-opacity);cursor:not-allowed}.pvc-disabled [type=radio]{border-color:var(--pvc-form-element-border-color)}.pvc-disabled select[disabled]{opacity:1}.pvc-field-type-color .pvc-color-control{display:flex;gap:10px;align-items:center;position:relative;width:fit-content}.pvc-field-type-color .pvc-color-control button{margin-bottom:0}.pvc-field-type-color .pvc-color-input{min-width:8rem}.pvc-field-type-color .pvc-color-swatch{width:32px;height:32px;padding:0;border-radius:var(--pvc-border-radius);border:1px solid var(--pvc-form-element-border-color);box-shadow:inset 0 0 0 2px var(--pvc-form-element-background-color);cursor:pointer;flex-shrink:0}.pvc-field-type-color .pvc-color-swatch:focus{outline:none;box-shadow:inset 0 0 0 2px var(--pvc-form-element-background-color),0 0 0 2px var(--pvc-form-element-focus-color)}.pvc-field-type-color .pvc-color-popover{position:absolute;top:calc(100% + 6px);left:0;padding:8px;border-radius:var(--pvc-border-radius);border:1px solid var(--pvc-form-element-border-color);background:var(--pvc-form-element-background-color);box-shadow:0 6px 18px #0000001f;z-index:1000;display:none;pointer-events:auto}.pvc-field-type-color.is-open .pvc-color-popover{display:block}.pvc-field-type-color hex-color-picker{width:200px;height:200px;pointer-events:auto}.pvc-hidden{display:none!important}@keyframes pvc-fade-in{0%{opacity:0}to{opacity:1}}@keyframes pvc-fade-out{0%{opacity:1}to{opacity:0}}@keyframes pvc-slide-in{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes pvc-slide-out{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-10px)}}.pvc-anim-in,.pvc-anim-out{animation-duration:var(--pvc-animation-duration, .25s);animation-fill-mode:both}.pvc-anim-fade.pvc-anim-in{animation-name:pvc-fade-in}.pvc-anim-fade.pvc-anim-out{animation-name:pvc-fade-out}.pvc-anim-slide.pvc-anim-in{animation-name:pvc-slide-in}.pvc-anim-slide.pvc-anim-out{animation-name:pvc-slide-out}.post-views-counter-settings.has-sidebar{display:flex;flex-direction:row;gap:30px;justify-content:space-between}.post-views-counter-settings form{min-width:463px;width:auto;position:relative}.post-views-counter-settings p.help,.post-views-counter-settings p.description{font-size:13px;font-style:italic;line-height:1.6}.post-views-counter-settings .pvc-template-tags-list{display:block;margin-top:5px;font-style:normal}.post-views-counter-settings select{vertical-align:top;width:auto}.post-views-counter-settings .available{color:#00a32a}.post-views-counter-settings .unavailable{color:#d63638}.post-views-counter-settings .pvc-ip-box-group{margin-bottom:10px}.post-views-counter-settings .pvc-ip-box{margin-bottom:5px}.post-views-counter-settings .pvc-button{color:#fff;background-color:#6610f2;border-color:#6610f2;border-radius:.25rem}.post-views-counter-settings .pvc-button:active,.post-views-counter-settings .pvc-button:focus,.post-views-counter-settings .pvc-button:hover{color:#fff;background-color:#570ece;border-color:#570ece}.post-views-counter-settings .pvc-button:focus{box-shadow:0 0 0 1px #fff,0 0 0 3px #6610f2}.post-views-counter-settings tr.pvc-pro th:after{content:"PRO";display:inline-block;margin-left:6px;padding:1px 4px;font-size:11px;border-radius:4px;background-color:#ffc107;color:#fff;font-weight:600}.post-views-counter-settings label.pvc-pro:after{content:"PRO";display:inline-block;margin-left:6px;padding:1px 4px;font-size:11px;border-radius:4px;background-color:#ffc107;color:#fff;font-weight:600;font-style:normal}.post-views-sidebar{margin:15px 0;position:relative;order:1}.post-views-sidebar>div:not(:last-child){margin-bottom:3em}.post-views-sidebar .inner{padding:1.5em}.post-views-sidebar .inner img{max-width:80%;height:auto;display:block;margin:20px auto}.pvc-status-table .pvc-status{display:inline-block}.pvc-status-table .pvc-status-active{color:#00a32a}.pvc-status-table .pvc-status-missing{color:#d63638}.pvc-subfield{margin-top:12px}.is-hidden{display:none!important;visibility:hidden!important}output{display:block;font-size:30px;font-weight:700;text-align:center;margin:30px 0;width:100%}.post-views-credits{background:#fff;box-shadow:0 0 0 1px #0000000d}.post-views-credits .inner{text-align:center;margin:0}.post-views-credits p:first-child{margin-top:0}.post-views-credits .pvc-sidebar-title{font-size:17px;font-weight:700;margin:.5rem 0 1rem}.post-views-credits .pvc-sidebar-title:after{content:"PRO";display:inline-block;margin-left:6px;padding:0 4px 1px;font-size:14px;border-radius:4px;background-color:#ffc107;color:#fff;font-weight:600}.post-views-credits .pvc-sidebar-subtitle{font-size:15px;font-weight:600;color:#1d2327;text-align:center;margin:1rem 0 1.5rem}.post-views-credits .pvc-sidebar-one-liner{text-align:center;margin-top:1.5rem}.post-views-credits .pvc-sidebar-body{font-size:14px;text-align:left;margin:1.5rem 0 2rem;padding:0}.post-views-credits .pvc-sidebar-body p{font-size:inherit;padding-left:20px;margin:.75rem 0;position:relative}.post-views-credits .pvc-sidebar-body b{color:#000}.post-views-credits .pvc-sidebar-body .pvc-icon{position:absolute;top:1px;left:0}.post-views-credits .pvc-sidebar-body .pvc-icon-check{box-sizing:border-box;display:block;transform:scale(1);width:16px;height:22px;border-radius:100px}.post-views-credits .pvc-sidebar-body .pvc-icon-check:after{content:"";display:block;box-sizing:border-box;position:absolute;left:0;top:0;width:6px;height:10px;border-width:0 2px 2px 0;border-style:solid;transform-origin:bottom left;transform:rotate(45deg)}#post_views_counter_other_license_setting .pvc-status-icon{vertical-align:middle;margin-left:8px;padding-bottom:3px}#post_views_counter_other_license_setting .pvc-status-icon:before{content:"\2717";color:#d63638}#post_views_counter_other_license_setting.license-status .pvc-status-icon:before{content:"\2717";color:#d63638}#post_views_counter_other_license_setting.license-status.valid .pvc-status-icon:before{content:"\2713";color:#00a32a}#pvc-reports-upgrade{position:absolute;left:0;top:0;height:100%;width:100%;overflow:hidden;box-sizing:border-box;min-height:400px}#pvc-reports-upgrade #pvc-reports-modal{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);padding:1.5em 3em;box-shadow:0 0 25px 10px #0000001a;border-radius:3px;background-color:#fff;text-align:center;width:26em}#pvc-reports-upgrade #pvc-reports-modal p{margin:0}#pvc-reports-upgrade #pvc-reports-modal h2{font-size:21px;font-weight:400;margin:0 0 10px;padding:9px 0 4px;line-height:1.3}#pvc-reports-upgrade #pvc-reports-modal .button{margin-top:25px;margin-bottom:10px}#pvc-reports-bg{width:100%;height:auto;opacity:.8;filter:blur(2px)}@media only screen and (max-width:960px){.post-views-counter-settings{flex-wrap:wrap}.post-views-counter-settings .post-views-sidebar{width:100%}}.pvc-provider-radio{display:inline-block;margin-right:20px;font-weight:400}.pvc-provider-radio input[type=radio]{margin-right:5px}.pvc-provider-content{margin:0}.pvc-provider-fields{padding:0;margin-top:15px}.pvc-provider-fields label{font-weight:600;margin-bottom:5px;display:block}.pvc-provider-fields input.regular-text{margin-top:5px}.pvc-provider-unavailable{color:#d63638;font-style:italic}.pvc-import-strategy{margin-bottom:25px}.pvc-field>label{margin-bottom:0}.pvc-field-group label{margin-right:8px}.pvc-radio-vertical label{display:block;margin:6px 0;font-weight:400}.pvc-radio-vertical input[type=radio]{margin-right:5px}.pvc-import-actions{padding-top:10px}.pvc-import-actions .button{margin-right:10px}.pvc-import-actions .button-primary{font-weight:600}.pvc-integration-content.unavailable{color:inherit;pointer-events:none}.pvc-integration-content.unavailable .pvc-integration-items{display:none}.pvc-integration-content input[type=checkbox]{margin-right:10px}.pvc-integration-content label{display:block;cursor:pointer}.pvc-integration-content .pvc-integration-items{margin:15px 0 0;padding-left:13px;font-size:13px;font-style:italic}.pvc-integration-content .pvc-integration-items li{margin-bottom:5px;list-style:disc}.pvc-integration-content .pvc-integration-items li:last-child{margin-bottom:0}.mce-container[role=group]{margin:0;display:block}
     4 */:root,:host{--pvc-font-family-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--pvc-font-family-sans-serif: system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, Helvetica, Arial, "Helvetica Neue", sans-serif, var(--pvc-font-family-emoji);--pvc-font-family-monospace: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace, var(--pvc-font-family-emoji);--pvc-font-family: var(--pvc-font-family-sans-serif);--pvc-line-height: 1.5;--pvc-font-weight: 400;--pvc-font-size: 100%;--pvc-text-underline-offset: .1rem;--pvc-border-radius: .25rem;--pvc-border-width: .0625rem;--pvc-outline-width: .125rem;--pvc-transition: .2s ease-in-out;--pvc-spacing: 1rem;--pvc-group-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--pvc-group-box-shadow-focus-with-button: 0 0 0 var(--pvc-outline-width) var(--pvc-primary-focus);--pvc-group-box-shadow-focus-with-input: 0 0 0 .0625rem var(--pvc-form-element-border-color);--pvc-icon-checkbox: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(255, 255, 255)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--pvc-icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-date: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--pvc-icon-time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(136, 145, 164)' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--pvc-icon-loading: url("data:image/svg+xml,%3Csvg fill='none' height='24' width='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' %3E%3Cstyle%3E g %7B animation: rotate 2s linear infinite; transform-origin: center center; %7D circle %7B stroke-dasharray: 75,100; stroke-dashoffset: -5; animation: dash 1.5s ease-in-out infinite; stroke-linecap: round; %7D @keyframes rotate %7B 0%25 %7B transform: rotate(0deg); %7D 100%25 %7B transform: rotate(360deg); %7D %7D @keyframes dash %7B 0%25 %7B stroke-dasharray: 1,100; stroke-dashoffset: 0; %7D 50%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -17.5; %7D 100%25 %7B stroke-dasharray: 44.5,100; stroke-dashoffset: -62; %7D %7D %3C/style%3E%3Cg%3E%3Ccircle cx='12' cy='12' r='10' fill='none' stroke='rgb(136, 145, 164)' stroke-width='4' /%3E%3C/g%3E%3C/svg%3E")}@media(min-width:576px){:root,:host{--pvc-font-size: 106.25%}}@media(min-width:768px){:root,:host{--pvc-font-size: 112.5%}}@media(min-width:1024px){:root,:host{--pvc-font-size: 118.75%}}@media(min-width:1280px){:root,:host{--pvc-font-size: 125%}}@media(min-width:1536px){:root,:host{--pvc-font-size: 131.25%}}a,a.secondary,a.contrast{--pvc-text-decoration: underline}pre,code,kbd,samp{--pvc-font-family: var(--pvc-font-family-monospace)}kbd{--pvc-font-weight: bolder}input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),:where(select,textarea){--pvc-outline-width: .0625rem}[type=search]{--pvc-border-radius: 5rem}[type=checkbox],[type=radio]{--pvc-border-width: .125rem}[type=checkbox][role=switch]{--pvc-border-width: .1875rem}[role=search]{--pvc-border-radius: 5rem}[role=search]:has(button.secondary:focus,[type=submit].secondary:focus,[type=button].secondary:focus,[role=button].secondary:focus),[role=group]:has(button.secondary:focus,[type=submit].secondary:focus,[type=button].secondary:focus,[role=button].secondary:focus){--pvc-group-box-shadow-focus-with-button: 0 0 0 var(--pvc-outline-width) var(--pvc-secondary-focus)}[role=search]:has(button.contrast:focus,[type=submit].contrast:focus,[type=button].contrast:focus,[role=button].contrast:focus),[role=group]:has(button.contrast:focus,[type=submit].contrast:focus,[type=button].contrast:focus,[role=button].contrast:focus){--pvc-group-box-shadow-focus-with-button: 0 0 0 var(--pvc-outline-width) var(--pvc-contrast-focus)}[role=search] button,[role=search] [type=submit],[role=search] [type=button],[role=search] [role=button],[role=group] button,[role=group] [type=submit],[role=group] [type=button],[role=group] [role=button]{--pvc-form-element-spacing-horizontal: 2rem}.pvc-settings-form [aria-busy=true]:not(input,select,textarea):is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline):before{filter:brightness(0) invert(1)}[data-theme=light],:root:not([data-theme=dark]),:host(:not([data-theme=dark])){color-scheme:light;--pvc-background-color: #fff;--pvc-color: #373c44;--pvc-text-selection-color: rgba(2, 154, 232, .25);--pvc-muted-color: #646b79;--pvc-muted-border-color: rgb(231, 234, 239.5);--pvc-primary: #0172ad;--pvc-primary-background: #0172ad;--pvc-primary-border: var(--pvc-primary-background);--pvc-primary-underline: rgba(1, 114, 173, .5);--pvc-primary-hover: #015887;--pvc-primary-hover-background: #02659a;--pvc-primary-hover-border: var(--pvc-primary-hover-background);--pvc-primary-hover-underline: var(--pvc-primary-hover);--pvc-primary-focus: rgba(2, 154, 232, .5);--pvc-primary-inverse: #fff;--pvc-secondary: #5d6b89;--pvc-secondary-background: #525f7a;--pvc-secondary-border: var(--pvc-secondary-background);--pvc-secondary-underline: rgba(93, 107, 137, .5);--pvc-secondary-hover: #48536b;--pvc-secondary-hover-background: #48536b;--pvc-secondary-hover-border: var(--pvc-secondary-hover-background);--pvc-secondary-hover-underline: var(--pvc-secondary-hover);--pvc-secondary-focus: rgba(93, 107, 137, .25);--pvc-secondary-inverse: #fff;--pvc-contrast: #181c25;--pvc-contrast-background: #181c25;--pvc-contrast-border: var(--pvc-contrast-background);--pvc-contrast-underline: rgba(24, 28, 37, .5);--pvc-contrast-hover: #000;--pvc-contrast-hover-background: #000;--pvc-contrast-hover-border: var(--pvc-contrast-hover-background);--pvc-contrast-hover-underline: var(--pvc-secondary-hover);--pvc-contrast-focus: rgba(93, 107, 137, .25);--pvc-contrast-inverse: #fff;--pvc-box-shadow: .0145rem .029rem .174rem rgba(129, 145, 181, .01698), .0335rem .067rem .402rem rgba(129, 145, 181, .024), .0625rem .125rem .75rem rgba(129, 145, 181, .03), .1125rem .225rem 1.35rem rgba(129, 145, 181, .036), .2085rem .417rem 2.502rem rgba(129, 145, 181, .04302), .5rem 1rem 6rem rgba(129, 145, 181, .06), 0 0 0 .0625rem rgba(129, 145, 181, .015);--pvc-code-background-color: rgb(243, 244.5, 246.75);--pvc-code-color: #646b79;--pvc-code-kbd-background-color: var(--pvc-color);--pvc-code-kbd-color: var(--pvc-background-color);--pvc-form-element-background-color: rgb(251, 251.5, 252.25);--pvc-form-element-selected-background-color: #dfe3eb;--pvc-form-element-border-color: #cfd5e2;--pvc-form-element-color: #23262c;--pvc-form-element-placeholder-color: var(--pvc-muted-color);--pvc-form-element-active-background-color: #fff;--pvc-form-element-active-border-color: var(--pvc-primary-border);--pvc-form-element-focus-color: var(--pvc-primary-border);--pvc-form-element-disabled-opacity: .5;--pvc-form-element-invalid-border-color: rgb(183.5, 105.5, 106.5);--pvc-form-element-invalid-active-border-color: rgb(200.25, 79.25, 72.25);--pvc-form-element-invalid-focus-color: var(--pvc-form-element-invalid-active-border-color);--pvc-form-element-valid-border-color: rgb(76, 154.5, 137.5);--pvc-form-element-valid-active-border-color: rgb(39, 152.75, 118.75);--pvc-form-element-valid-focus-color: var(--pvc-form-element-valid-active-border-color);--pvc-switch-background-color: #bfc7d9;--pvc-switch-checked-background-color: var(--pvc-primary-background);--pvc-switch-color: #fff;--pvc-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--pvc-range-border-color: #dfe3eb;--pvc-range-active-border-color: #bfc7d9;--pvc-range-thumb-border-color: var(--pvc-background-color);--pvc-range-thumb-color: var(--pvc-secondary-background);--pvc-range-thumb-active-color: var(--pvc-primary-background);--pvc-loading-spinner-opacity: .5;--pvc-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 154.5, 137.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200.25, 79.25, 72.25)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E")}[data-theme=light] input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),:root:not([data-theme=dark]) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),:host(:not([data-theme=dark])) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pvc-form-element-focus-color: var(--pvc-primary-focus)}@media only screen and (prefers-color-scheme:dark){:root:not([data-theme]),:host(:not([data-theme])){color-scheme:dark;--pvc-background-color: rgb(19, 22.5, 30.5);--pvc-color: #c2c7d0;--pvc-text-selection-color: rgba(1, 170, 255, .1875);--pvc-muted-color: #7b8495;--pvc-muted-border-color: #202632;--pvc-primary: #01aaff;--pvc-primary-background: #0172ad;--pvc-primary-border: var(--pvc-primary-background);--pvc-primary-underline: rgba(1, 170, 255, .5);--pvc-primary-hover: #79c0ff;--pvc-primary-hover-background: #017fc0;--pvc-primary-hover-border: var(--pvc-primary-hover-background);--pvc-primary-hover-underline: var(--pvc-primary-hover);--pvc-primary-focus: rgba(1, 170, 255, .375);--pvc-primary-inverse: #fff;--pvc-secondary: #969eaf;--pvc-secondary-background: #525f7a;--pvc-secondary-border: var(--pvc-secondary-background);--pvc-secondary-underline: rgba(150, 158, 175, .5);--pvc-secondary-hover: #b3b9c5;--pvc-secondary-hover-background: #5d6b89;--pvc-secondary-hover-border: var(--pvc-secondary-hover-background);--pvc-secondary-hover-underline: var(--pvc-secondary-hover);--pvc-secondary-focus: rgba(144, 158, 190, .25);--pvc-secondary-inverse: #fff;--pvc-contrast: #dfe3eb;--pvc-contrast-background: #eff1f4;--pvc-contrast-border: var(--pvc-contrast-background);--pvc-contrast-underline: rgba(223, 227, 235, .5);--pvc-contrast-hover: #fff;--pvc-contrast-hover-background: #fff;--pvc-contrast-hover-border: var(--pvc-contrast-hover-background);--pvc-contrast-hover-underline: var(--pvc-contrast-hover);--pvc-contrast-focus: rgba(207, 213, 226, .25);--pvc-contrast-inverse: #000;--pvc-box-shadow: .0145rem .029rem .174rem rgba(7, 8.5, 12, .01698), .0335rem .067rem .402rem rgba(7, 8.5, 12, .024), .0625rem .125rem .75rem rgba(7, 8.5, 12, .03), .1125rem .225rem 1.35rem rgba(7, 8.5, 12, .036), .2085rem .417rem 2.502rem rgba(7, 8.5, 12, .04302), .5rem 1rem 6rem rgba(7, 8.5, 12, .06), 0 0 0 .0625rem rgba(7, 8.5, 12, .015);--pvc-code-background-color: rgb(26, 30.5, 40.25);--pvc-code-color: #8891a4;--pvc-code-kbd-background-color: var(--pvc-color);--pvc-code-kbd-color: var(--pvc-background-color);--pvc-form-element-background-color: rgb(28, 33, 43.5);--pvc-form-element-selected-background-color: #2a3140;--pvc-form-element-border-color: #2a3140;--pvc-form-element-color: #e0e3e7;--pvc-form-element-placeholder-color: #8891a4;--pvc-form-element-active-background-color: rgb(26, 30.5, 40.25);--pvc-form-element-active-border-color: var(--pvc-primary-border);--pvc-form-element-focus-color: var(--pvc-primary-border);--pvc-form-element-disabled-opacity: .5;--pvc-form-element-invalid-border-color: rgb(149.5, 74, 80);--pvc-form-element-invalid-active-border-color: rgb(183.25, 63.5, 59);--pvc-form-element-invalid-focus-color: var(--pvc-form-element-invalid-active-border-color);--pvc-form-element-valid-border-color: #2a7b6f;--pvc-form-element-valid-active-border-color: rgb(22, 137, 105.5);--pvc-form-element-valid-focus-color: var(--pvc-form-element-valid-active-border-color);--pvc-switch-background-color: #333c4e;--pvc-switch-checked-background-color: var(--pvc-primary-background);--pvc-switch-color: #fff;--pvc-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--pvc-range-border-color: #202632;--pvc-range-active-border-color: #2a3140;--pvc-range-thumb-border-color: var(--pvc-background-color);--pvc-range-thumb-color: var(--pvc-secondary-background);--pvc-range-thumb-active-color: var(--pvc-primary-background);--pvc-loading-spinner-opacity: .5;--pvc-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E")}:root:not([data-theme]) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]),:host(:not([data-theme])) input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pvc-form-element-focus-color: var(--pvc-primary-focus)}:root:not([data-theme]) .pvc-settings-form [aria-busy=true]:not(input,select,textarea).contrast:is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline):before,:host(:not([data-theme])) .pvc-settings-form [aria-busy=true]:not(input,select,textarea).contrast:is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline):before{filter:brightness(0)}}[data-theme=dark]{color-scheme:dark;--pvc-background-color: rgb(19, 22.5, 30.5);--pvc-color: #c2c7d0;--pvc-text-selection-color: rgba(1, 170, 255, .1875);--pvc-muted-color: #7b8495;--pvc-muted-border-color: #202632;--pvc-primary: #01aaff;--pvc-primary-background: #0172ad;--pvc-primary-border: var(--pvc-primary-background);--pvc-primary-underline: rgba(1, 170, 255, .5);--pvc-primary-hover: #79c0ff;--pvc-primary-hover-background: #017fc0;--pvc-primary-hover-border: var(--pvc-primary-hover-background);--pvc-primary-hover-underline: var(--pvc-primary-hover);--pvc-primary-focus: rgba(1, 170, 255, .375);--pvc-primary-inverse: #fff;--pvc-secondary: #969eaf;--pvc-secondary-background: #525f7a;--pvc-secondary-border: var(--pvc-secondary-background);--pvc-secondary-underline: rgba(150, 158, 175, .5);--pvc-secondary-hover: #b3b9c5;--pvc-secondary-hover-background: #5d6b89;--pvc-secondary-hover-border: var(--pvc-secondary-hover-background);--pvc-secondary-hover-underline: var(--pvc-secondary-hover);--pvc-secondary-focus: rgba(144, 158, 190, .25);--pvc-secondary-inverse: #fff;--pvc-contrast: #dfe3eb;--pvc-contrast-background: #eff1f4;--pvc-contrast-border: var(--pvc-contrast-background);--pvc-contrast-underline: rgba(223, 227, 235, .5);--pvc-contrast-hover: #fff;--pvc-contrast-hover-background: #fff;--pvc-contrast-hover-border: var(--pvc-contrast-hover-background);--pvc-contrast-hover-underline: var(--pvc-contrast-hover);--pvc-contrast-focus: rgba(207, 213, 226, .25);--pvc-contrast-inverse: #000;--pvc-box-shadow: .0145rem .029rem .174rem rgba(7, 8.5, 12, .01698), .0335rem .067rem .402rem rgba(7, 8.5, 12, .024), .0625rem .125rem .75rem rgba(7, 8.5, 12, .03), .1125rem .225rem 1.35rem rgba(7, 8.5, 12, .036), .2085rem .417rem 2.502rem rgba(7, 8.5, 12, .04302), .5rem 1rem 6rem rgba(7, 8.5, 12, .06), 0 0 0 .0625rem rgba(7, 8.5, 12, .015);--pvc-code-background-color: rgb(26, 30.5, 40.25);--pvc-code-color: #8891a4;--pvc-code-kbd-background-color: var(--pvc-color);--pvc-code-kbd-color: var(--pvc-background-color);--pvc-form-element-background-color: rgb(28, 33, 43.5);--pvc-form-element-selected-background-color: #2a3140;--pvc-form-element-border-color: #2a3140;--pvc-form-element-color: #e0e3e7;--pvc-form-element-placeholder-color: #8891a4;--pvc-form-element-active-background-color: rgb(26, 30.5, 40.25);--pvc-form-element-active-border-color: var(--pvc-primary-border);--pvc-form-element-focus-color: var(--pvc-primary-border);--pvc-form-element-disabled-opacity: .5;--pvc-form-element-invalid-border-color: rgb(149.5, 74, 80);--pvc-form-element-invalid-active-border-color: rgb(183.25, 63.5, 59);--pvc-form-element-invalid-focus-color: var(--pvc-form-element-invalid-active-border-color);--pvc-form-element-valid-border-color: #2a7b6f;--pvc-form-element-valid-active-border-color: rgb(22, 137, 105.5);--pvc-form-element-valid-focus-color: var(--pvc-form-element-valid-active-border-color);--pvc-switch-background-color: #333c4e;--pvc-switch-checked-background-color: var(--pvc-primary-background);--pvc-switch-color: #fff;--pvc-switch-thumb-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--pvc-range-border-color: #202632;--pvc-range-active-border-color: #2a3140;--pvc-range-thumb-border-color: var(--pvc-background-color);--pvc-range-thumb-color: var(--pvc-secondary-background);--pvc-range-thumb-active-color: var(--pvc-primary-background);--pvc-loading-spinner-opacity: .5;--pvc-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--pvc-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E")}[data-theme=dark] input:is([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[type=file]){--pvc-form-element-focus-color: var(--pvc-primary-focus)}[data-theme=dark] .pvc-settings-form [aria-busy=true]:not(input,select,textarea).contrast:is(button,[type=submit],[type=button],[type=reset],[role=button]):not(.outline):before{filter:brightness(0)}.pvc-settings-form progress,.pvc-settings-form [type=checkbox],.pvc-settings-form [type=radio],.pvc-settings-form [type=range]{accent-color:var(--pvc-primary)}.pvc-settings-form :where(a:not([role=button])),.pvc-settings-form [role=link]{--pvc-color: var(--pvc-primary);--pvc-background-color: transparent;--pvc-underline: var(--pvc-primary-underline);outline:none;background-color:var(--pvc-background-color);color:var(--pvc-color);text-decoration:var(--pvc-text-decoration);text-decoration-color:var(--pvc-underline);text-underline-offset:.125em;transition:background-color var(--pvc-transition),color var(--pvc-transition),text-decoration var(--pvc-transition),box-shadow var(--pvc-transition)}.pvc-settings-form :where(a:not([role=button])):is([aria-current]:not([aria-current=false]),:hover,:active,:focus),.pvc-settings-form [role=link]:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pvc-color: var(--pvc-primary-hover);--pvc-underline: var(--pvc-primary-hover-underline);--pvc-text-decoration: underline}.pvc-settings-form :where(a:not([role=button])):focus-visible,.pvc-settings-form [role=link]:focus-visible{box-shadow:0 0 0 var(--pvc-outline-width) var(--pvc-primary-focus)}.pvc-settings-form :where(a:not([role=button])).secondary,.pvc-settings-form [role=link].secondary{--pvc-color: var(--pvc-secondary);--pvc-underline: var(--pvc-secondary-underline)}.pvc-settings-form :where(a:not([role=button])).secondary:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),.pvc-settings-form [role=link].secondary:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pvc-color: var(--pvc-secondary-hover);--pvc-underline: var(--pvc-secondary-hover-underline)}.pvc-settings-form :where(a:not([role=button])).contrast,.pvc-settings-form [role=link].contrast{--pvc-color: var(--pvc-contrast);--pvc-underline: var(--pvc-contrast-underline)}.pvc-settings-form :where(a:not([role=button])).contrast:is([aria-current]:not([aria-current=false]),:hover,:active,:focus),.pvc-settings-form [role=link].contrast:is([aria-current]:not([aria-current=false]),:hover,:active,:focus){--pvc-color: var(--pvc-contrast-hover);--pvc-underline: var(--pvc-contrast-hover-underline)}.pvc-settings-form a[role=button]{display:inline-block}.pvc-settings-form pre,.pvc-settings-form code,.pvc-settings-form kbd,.pvc-settings-form samp{font-size:.875em;font-family:var(--pvc-font-family)}.pvc-settings-form pre code,.pvc-settings-form pre samp{font-size:inherit;font-family:inherit}.pvc-settings-form pre{-ms-overflow-style:scrollbar;overflow:auto}.pvc-settings-form pre,.pvc-settings-form code,.pvc-settings-form kbd,.pvc-settings-form samp{border-radius:var(--pvc-border-radius);background:var(--pvc-code-background-color);color:var(--pvc-code-color);font-weight:var(--pvc-font-weight);line-height:initial}.pvc-settings-form code,.pvc-settings-form kbd,.pvc-settings-form samp{display:inline-block;padding:.375rem}.pvc-settings-form pre{display:block;margin-bottom:var(--pvc-spacing);overflow-x:auto}.pvc-settings-form pre>code,.pvc-settings-form pre>samp{display:block;padding:var(--pvc-spacing);background:none;line-height:var(--pvc-line-height)}.pvc-settings-form kbd{background-color:var(--pvc-code-kbd-background-color);color:var(--pvc-code-kbd-color);vertical-align:baseline}.pvc-settings-form hr{height:0;margin:var(--pvc-typography-spacing-vertical) 0;border:0;border-top:1px solid var(--pvc-muted-border-color);color:inherit}.pvc-settings-form [hidden],.pvc-settings-form template{display:none!important}.pvc-settings-form canvas{display:inline-block}.pvc-settings-form input,.pvc-settings-form optgroup,.pvc-settings-form select,.pvc-settings-form textarea{margin:0;font-size:1rem;line-height:var(--pvc-line-height);font-family:inherit;letter-spacing:inherit}.pvc-settings-form input{overflow:visible}.pvc-settings-form select{text-transform:none}.pvc-settings-form legend{max-width:100%;padding:0;color:inherit;white-space:normal}.pvc-settings-form textarea{overflow:auto}.pvc-settings-form [type=checkbox],.pvc-settings-form [type=radio]{padding:0}.pvc-settings-form ::-webkit-inner-spin-button,.pvc-settings-form ::-webkit-outer-spin-button{height:auto}.pvc-settings-form [type=search]{-webkit-appearance:textfield;outline-offset:-2px}.pvc-settings-form [type=search]::-webkit-search-decoration{-webkit-appearance:none}.pvc-settings-form ::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.pvc-settings-form ::-moz-focus-inner{padding:0;border-style:none}.pvc-settings-form :-moz-focusring{outline:none}.pvc-settings-form :-moz-ui-invalid{box-shadow:none}.pvc-settings-form ::-ms-expand{display:none}.pvc-settings-form [type=file],.pvc-settings-form [type=range]{padding:0;border-width:0}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range]){height:calc(1rem * var(--pvc-line-height) + var(--pvc-form-element-spacing-vertical) * 2 + var(--pvc-border-width) * 2)}.pvc-settings-form fieldset{width:100%;margin:0;margin-bottom:var(--pvc-spacing);padding:0;border:0}.pvc-settings-form label,.pvc-settings-form fieldset legend{display:block;margin-bottom:calc(var(--pvc-spacing) * .375);color:var(--pvc-color);font-weight:var(--pvc-form-label-font-weight, var(--pvc-font-weight))}.pvc-settings-form fieldset legend{margin-bottom:calc(var(--pvc-spacing) * .5)}.pvc-settings-form input:not([type=checkbox],[type=radio]),.pvc-settings-form button[type=submit],.pvc-settings-form select,.pvc-settings-form textarea{width:100%}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file]),.pvc-settings-form select,.pvc-settings-form textarea{appearance:none;padding:var(--pvc-form-element-spacing-vertical) var(--pvc-form-element-spacing-horizontal)}.pvc-settings-form input,.pvc-settings-form select,.pvc-settings-form textarea{--pvc-background-color: var(--pvc-form-element-background-color);--pvc-border-color: var(--pvc-form-element-border-color);--pvc-color: var(--pvc-form-element-color);--pvc-box-shadow: none;border:var(--pvc-border-width) solid var(--pvc-border-color);border-radius:var(--pvc-border-radius);outline:none;background-color:var(--pvc-background-color);box-shadow:var(--pvc-box-shadow);color:var(--pvc-color);font-weight:var(--pvc-font-weight);transition:background-color var(--pvc-transition),border-color var(--pvc-transition),color var(--pvc-transition),box-shadow var(--pvc-transition)}.pvc-settings-form input:not([type=submit],[type=button],[type=reset],[type=checkbox],[type=radio],[readonly]):is(:active,:focus),.pvc-settings-form :where(select,textarea):not([readonly]):is(:active,:focus){--pvc-background-color: var(--pvc-form-element-active-background-color)}.pvc-settings-form input:not([type=submit],[type=button],[type=reset],[role=switch],[readonly]):is(:active,:focus),.pvc-settings-form :where(select,textarea):not([readonly]):is(:active,:focus){--pvc-border-color: var(--pvc-form-element-active-border-color)}.pvc-settings-form input:not([type=submit],[type=button],[type=reset],[type=range],[type=file],[readonly]):focus,.pvc-settings-form :where(select,textarea):not([readonly]):focus{--pvc-box-shadow: 0 0 0 var(--pvc-outline-width) var(--pvc-form-element-focus-color)}.pvc-settings-form input:not([type=submit],[type=button],[type=reset])[disabled],.pvc-settings-form select[disabled],.pvc-settings-form textarea[disabled],.pvc-settings-form label[aria-disabled=true],.pvc-settings-form :where(fieldset[disabled]) :is(input:not([type=submit],[type=button],[type=reset]),select,textarea){opacity:var(--pvc-form-element-disabled-opacity);pointer-events:none}.pvc-settings-form label[aria-disabled=true] input[disabled]{opacity:1}.pvc-settings-form :where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid]{padding-right:calc(var(--pvc-form-element-spacing-horizontal) + 1.5rem)!important;padding-left:var(--pvc-form-element-spacing-horizontal);padding-inline-start:var(--pvc-form-element-spacing-horizontal)!important;padding-inline-end:calc(var(--pvc-form-element-spacing-horizontal) + 1.5rem)!important;background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}.pvc-settings-form :where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid=false]:not(select){background-image:var(--pvc-icon-valid)}.pvc-settings-form :where(input,select,textarea):not([type=checkbox],[type=radio],[type=date],[type=datetime-local],[type=month],[type=time],[type=week],[type=range])[aria-invalid=true]:not(select){background-image:var(--pvc-icon-invalid)}.pvc-settings-form :where(input,select,textarea)[aria-invalid=false]{--pvc-border-color: var(--pvc-form-element-valid-border-color)}.pvc-settings-form :where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--pvc-border-color: var(--pvc-form-element-valid-active-border-color) !important}.pvc-settings-form :where(input,select,textarea)[aria-invalid=false]:is(:active,:focus):not([type=checkbox],[type=radio]){--pvc-box-shadow: 0 0 0 var(--pvc-outline-width) var(--pvc-form-element-valid-focus-color) !important}.pvc-settings-form :where(input,select,textarea)[aria-invalid=true]{--pvc-border-color: var(--pvc-form-element-invalid-border-color)}.pvc-settings-form :where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--pvc-border-color: var(--pvc-form-element-invalid-active-border-color) !important}.pvc-settings-form :where(input,select,textarea)[aria-invalid=true]:is(:active,:focus):not([type=checkbox],[type=radio]){--pvc-box-shadow: 0 0 0 var(--pvc-outline-width) var(--pvc-form-element-invalid-focus-color) !important}[dir=rtl] .pvc-settings-form :where(input,select,textarea):not([type=checkbox],[type=radio]):is([aria-invalid],[aria-invalid=true],[aria-invalid=false]){background-position:center left .75rem}.pvc-settings-form input::placeholder,.pvc-settings-form input::-webkit-input-placeholder,.pvc-settings-form textarea::placeholder,.pvc-settings-form textarea::-webkit-input-placeholder,.pvc-settings-form select:invalid{color:var(--pvc-form-element-placeholder-color);opacity:1}.pvc-settings-form input:not([type=checkbox],[type=radio]),.pvc-settings-form select,.pvc-settings-form textarea{margin-bottom:var(--pvc-spacing)}.pvc-settings-form select::-ms-expand{border:0;background-color:transparent}.pvc-settings-form select:not([multiple],[size]){padding-right:calc(var(--pvc-form-element-spacing-horizontal) + 1.5rem);padding-left:var(--pvc-form-element-spacing-horizontal);padding-inline-start:var(--pvc-form-element-spacing-horizontal);padding-inline-end:calc(var(--pvc-form-element-spacing-horizontal) + 1.5rem);background-image:var(--pvc-icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}.pvc-settings-form select[multiple] option:checked{background:var(--pvc-form-element-selected-background-color);color:var(--pvc-form-element-color)}[dir=rtl] .pvc-settings-form select:not([multiple],[size]){background-position:center left .75rem}.pvc-settings-form textarea{display:block;resize:vertical}.pvc-settings-form textarea[aria-invalid]{--pvc-icon-height: calc(1rem * var(--pvc-line-height) + var(--pvc-form-element-spacing-vertical) * 2 + var(--pvc-border-width) * 2);background-position:top right .75rem!important;background-size:1rem var(--pvc-icon-height)!important}.pvc-settings-form :where(input,select,textarea,fieldset)+small{display:block;width:100%;margin-top:calc(var(--pvc-spacing) * -.75);margin-bottom:var(--pvc-spacing);color:var(--pvc-muted-color)}.pvc-settings-form :where(input,select,textarea,fieldset)[aria-invalid=false]+small{color:var(--pvc-ins-color)}.pvc-settings-form :where(input,select,textarea,fieldset)[aria-invalid=true]+small{color:var(--pvc-del-color)}.pvc-settings-form label>:where(input,select,textarea){margin-top:calc(var(--pvc-spacing) * .25)}.pvc-settings-form label:has([type=checkbox],[type=radio]){width:fit-content;cursor:pointer}.pvc-settings-form [type=checkbox],.pvc-settings-form [type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-.125em;margin-inline-end:.5em;border-width:var(--pvc-border-width);vertical-align:middle;cursor:pointer}.pvc-settings-form [type=checkbox]::-ms-check,.pvc-settings-form [type=radio]::-ms-check{display:none}.pvc-settings-form [type=checkbox]:checked,.pvc-settings-form [type=checkbox]:checked:active,.pvc-settings-form [type=checkbox]:checked:focus,.pvc-settings-form [type=radio]:checked,.pvc-settings-form [type=radio]:checked:active,.pvc-settings-form [type=radio]:checked:focus{--pvc-background-color: var(--pvc-primary-background);--pvc-border-color: var(--pvc-primary-border);background-image:var(--pvc-icon-checkbox);background-position:center;background-size:.75em auto;background-repeat:no-repeat}.pvc-settings-form [type=checkbox]~label,.pvc-settings-form [type=radio]~label{display:inline-block;margin-bottom:0;cursor:pointer}.pvc-settings-form [type=checkbox]~label:not(:last-of-type),.pvc-settings-form [type=radio]~label:not(:last-of-type){margin-inline-end:1em}.pvc-settings-form [type=checkbox]:indeterminate{--pvc-background-color: var(--pvc-primary-background);--pvc-border-color: var(--pvc-primary-border);background-image:var(--pvc-icon-minus);background-position:center;background-size:.75em auto;background-repeat:no-repeat}.pvc-settings-form [type=radio]{border-radius:50%}.pvc-settings-form [type=radio]:checked,.pvc-settings-form [type=radio]:checked:active,.pvc-settings-form [type=radio]:checked:focus{--pvc-background-color: var(--pvc-primary-inverse);border-width:.35em;background-image:none}.pvc-settings-form [type=checkbox][role=switch]{--pvc-background-color: var(--pvc-switch-background-color);--pvc-color: var(--pvc-switch-color);width:2.25em;height:1.25em;border:var(--pvc-border-width) solid var(--pvc-border-color);border-radius:1.25em;background-color:var(--pvc-background-color);line-height:1.25em}.pvc-settings-form [type=checkbox][role=switch]:not([aria-invalid]){--pvc-border-color: var(--pvc-switch-background-color)}.pvc-settings-form [type=checkbox][role=switch]:before{display:block;aspect-ratio:1;height:100%;border-radius:50%;background-color:var(--pvc-color);box-shadow:var(--pvc-switch-thumb-box-shadow);content:"";transition:margin .1s ease-in-out}.pvc-settings-form [type=checkbox][role=switch]:focus{--pvc-background-color: var(--pvc-switch-background-color);--pvc-border-color: var(--pvc-switch-background-color)}.pvc-settings-form [type=checkbox][role=switch]:checked{--pvc-background-color: var(--pvc-switch-checked-background-color);--pvc-border-color: var(--pvc-switch-checked-background-color);background-image:none}.pvc-settings-form [type=checkbox][role=switch]:checked:before{margin-inline-start:1em}.pvc-settings-form [type=checkbox][role=switch][disabled]{--pvc-background-color: var(--pvc-border-color)}.pvc-settings-form [type=checkbox][aria-invalid=false]:checked,.pvc-settings-form [type=checkbox][aria-invalid=false]:checked:active,.pvc-settings-form [type=checkbox][aria-invalid=false]:checked:focus,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked:active,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked:focus{--pvc-background-color: var(--pvc-form-element-valid-border-color)}.pvc-settings-form [type=checkbox]:checked[aria-invalid=true],.pvc-settings-form [type=checkbox]:checked:active[aria-invalid=true],.pvc-settings-form [type=checkbox]:checked:focus[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked:active[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked:focus[aria-invalid=true]{--pvc-background-color: var(--pvc-form-element-invalid-border-color)}.pvc-settings-form [type=checkbox][aria-invalid=false]:checked,.pvc-settings-form [type=checkbox][aria-invalid=false]:checked:active,.pvc-settings-form [type=checkbox][aria-invalid=false]:checked:focus,.pvc-settings-form [type=radio][aria-invalid=false]:checked,.pvc-settings-form [type=radio][aria-invalid=false]:checked:active,.pvc-settings-form [type=radio][aria-invalid=false]:checked:focus,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked:active,.pvc-settings-form [type=checkbox][role=switch][aria-invalid=false]:checked:focus{--pvc-border-color: var(--pvc-form-element-valid-border-color)}.pvc-settings-form [type=checkbox]:checked[aria-invalid=true],.pvc-settings-form [type=checkbox]:checked:active[aria-invalid=true],.pvc-settings-form [type=checkbox]:checked:focus[aria-invalid=true],.pvc-settings-form [type=radio]:checked[aria-invalid=true],.pvc-settings-form [type=radio]:checked:active[aria-invalid=true],.pvc-settings-form [type=radio]:checked:focus[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked:active[aria-invalid=true],.pvc-settings-form [type=checkbox][role=switch]:checked:focus[aria-invalid=true]{--pvc-border-color: var(--pvc-form-element-invalid-border-color)}.pvc-settings-form [type=color]::-webkit-color-swatch-wrapper{padding:0}.pvc-settings-form [type=color]::-moz-focus-inner{padding:0}.pvc-settings-form [type=color]::-webkit-color-swatch{border:0;border-radius:calc(var(--pvc-border-radius) * .5)}.pvc-settings-form [type=color]::-moz-color-swatch{border:0;border-radius:calc(var(--pvc-border-radius) * .5)}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file]):is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){--pvc-icon-position: .75rem;--pvc-icon-width: 1rem;padding-right:calc(var(--pvc-icon-width) + var(--pvc-icon-position));background-image:var(--pvc-icon-date);background-position:center right var(--pvc-icon-position);background-size:var(--pvc-icon-width) auto;background-repeat:no-repeat}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=time]{background-image:var(--pvc-icon-time)}.pvc-settings-form [type=date]::-webkit-calendar-picker-indicator,.pvc-settings-form [type=datetime-local]::-webkit-calendar-picker-indicator,.pvc-settings-form [type=month]::-webkit-calendar-picker-indicator,.pvc-settings-form [type=time]::-webkit-calendar-picker-indicator,.pvc-settings-form [type=week]::-webkit-calendar-picker-indicator{width:var(--pvc-icon-width);margin-right:calc(var(--pvc-icon-width) * -1);margin-left:var(--pvc-icon-position);opacity:0}@-moz-document url-prefix(){.pvc-settings-form [type=date],.pvc-settings-form [type=datetime-local],.pvc-settings-form [type=month],.pvc-settings-form [type=time],.pvc-settings-form [type=week]{padding-right:var(--pvc-form-element-spacing-horizontal)!important;background-image:none!important}}[dir=rtl] .pvc-settings-form :is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){text-align:right}.pvc-settings-form [type=file]{--pvc-color: var(--pvc-muted-color);margin-left:calc(var(--pvc-outline-width) * -1);padding:calc(var(--pvc-form-element-spacing-vertical) * .5) 0;padding-left:var(--pvc-outline-width);border:0;border-radius:0;background:none}.pvc-settings-form [type=file]::file-selector-button{margin-right:calc(var(--pvc-spacing) / 2);padding:calc(var(--pvc-form-element-spacing-vertical) * .5) var(--pvc-form-element-spacing-horizontal)}.pvc-settings-form [type=file]:is(:hover,:active,:focus)::file-selector-button{--pvc-background-color: var(--pvc-secondary-hover-background);--pvc-border-color: var(--pvc-secondary-hover-border)}.pvc-settings-form [type=file]:focus::file-selector-button{--pvc-box-shadow: var(--pvc-button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)), 0 0 0 var(--pvc-outline-width) var(--pvc-secondary-focus)}.pvc-settings-form [type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:none}.pvc-settings-form [type=range]::-webkit-slider-runnable-track{width:100%;height:.375rem;border-radius:var(--pvc-border-radius);background-color:var(--pvc-range-border-color);transition:background-color var(--pvc-transition),box-shadow var(--pvc-transition)}.pvc-settings-form [type=range]::-moz-range-track{width:100%;height:.375rem;border-radius:var(--pvc-border-radius);background-color:var(--pvc-range-border-color);transition:background-color var(--pvc-transition),box-shadow var(--pvc-transition)}.pvc-settings-form [type=range]::-ms-track{width:100%;height:.375rem;border-radius:var(--pvc-border-radius);background-color:var(--pvc-range-border-color);transition:background-color var(--pvc-transition),box-shadow var(--pvc-transition)}.pvc-settings-form [type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pvc-range-thumb-border-color);border-radius:50%;background-color:var(--pvc-range-thumb-color);cursor:pointer;transition:background-color var(--pvc-transition),transform var(--pvc-transition)}.pvc-settings-form [type=range]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pvc-range-thumb-border-color);border-radius:50%;background-color:var(--pvc-range-thumb-color);cursor:pointer;transition:background-color var(--pvc-transition),transform var(--pvc-transition)}.pvc-settings-form [type=range]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.4375rem;border:2px solid var(--pvc-range-thumb-border-color);border-radius:50%;background-color:var(--pvc-range-thumb-color);cursor:pointer;transition:background-color var(--pvc-transition),transform var(--pvc-transition)}.pvc-settings-form [type=range]:active,.pvc-settings-form [type=range]:focus-within{--pvc-range-border-color: var(--pvc-range-active-border-color);--pvc-range-thumb-color: var(--pvc-range-thumb-active-color)}.pvc-settings-form [type=range]:active::-webkit-slider-thumb{transform:scale(1.25)}.pvc-settings-form [type=range]:active::-moz-range-thumb{transform:scale(1.25)}.pvc-settings-form [type=range]:active::-ms-thumb{transform:scale(1.25)}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{padding-inline-start:calc(var(--pvc-form-element-spacing-horizontal) + 1.75rem);background-image:var(--pvc-icon-search);background-position:center left calc(var(--pvc-form-element-spacing-horizontal) + .125rem);background-size:1rem auto;background-repeat:no-repeat}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{padding-inline-start:calc(var(--pvc-form-element-spacing-horizontal) + 1.75rem)!important;background-position:center left 1.125rem,center right .75rem}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=false]{background-image:var(--pvc-icon-search),var(--pvc-icon-valid)}.pvc-settings-form input:not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid=true]{background-image:var(--pvc-icon-search),var(--pvc-icon-invalid)}[dir=rtl] .pvc-settings-form :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search]{background-position:center right 1.125rem}[dir=rtl] .pvc-settings-form :where(input):not([type=checkbox],[type=radio],[type=range],[type=file])[type=search][aria-invalid]{background-position:center right 1.125rem,center left .75rem}.pvc-settings-form [role=search],.pvc-settings-form [role=group]{display:inline-flex;position:relative;width:100%;margin-bottom:var(--pvc-spacing);border-radius:var(--pvc-border-radius);box-shadow:var(--pvc-group-box-shadow, 0 0 0 rgba(0, 0, 0, 0));vertical-align:middle;transition:box-shadow var(--pvc-transition)}.pvc-settings-form [role=search]>*,.pvc-settings-form [role=search] input:not([type=checkbox],[type=radio]),.pvc-settings-form [role=search] select,.pvc-settings-form [role=group]>*,.pvc-settings-form [role=group] input:not([type=checkbox],[type=radio]),.pvc-settings-form [role=group] select{position:relative;flex:1 1 auto;margin-bottom:0}.pvc-settings-form [role=search]>*:not(:first-child),.pvc-settings-form [role=search] input:not([type=checkbox],[type=radio]):not(:first-child),.pvc-settings-form [role=search] select:not(:first-child),.pvc-settings-form [role=group]>*:not(:first-child),.pvc-settings-form [role=group] input:not([type=checkbox],[type=radio]):not(:first-child),.pvc-settings-form [role=group] select:not(:first-child){margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.pvc-settings-form [role=search]>*:not(:last-child),.pvc-settings-form [role=search] input:not([type=checkbox],[type=radio]):not(:last-child),.pvc-settings-form [role=search] select:not(:last-child),.pvc-settings-form [role=group]>*:not(:last-child),.pvc-settings-form [role=group] input:not([type=checkbox],[type=radio]):not(:last-child),.pvc-settings-form [role=group] select:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.pvc-settings-form [role=search]>*:focus,.pvc-settings-form [role=search] input:not([type=checkbox],[type=radio]):focus,.pvc-settings-form [role=search] select:focus,.pvc-settings-form [role=group]>*:focus,.pvc-settings-form [role=group] input:not([type=checkbox],[type=radio]):focus,.pvc-settings-form [role=group] select:focus{z-index:2}.pvc-settings-form [role=search] button:not(:first-child),.pvc-settings-form [role=search] [type=submit]:not(:first-child),.pvc-settings-form [role=search] [type=reset]:not(:first-child),.pvc-settings-form [role=search] [type=button]:not(:first-child),.pvc-settings-form [role=search] [role=button]:not(:first-child),.pvc-settings-form [role=search] input:not([type=checkbox],[type=radio]):not(:first-child),.pvc-settings-form [role=search] select:not(:first-child),.pvc-settings-form [role=group] button:not(:first-child),.pvc-settings-form [role=group] [type=submit]:not(:first-child),.pvc-settings-form [role=group] [type=reset]:not(:first-child),.pvc-settings-form [role=group] [type=button]:not(:first-child),.pvc-settings-form [role=group] [role=button]:not(:first-child),.pvc-settings-form [role=group] input:not([type=checkbox],[type=radio]):not(:first-child),.pvc-settings-form [role=group] select:not(:first-child){margin-left:calc(var(--pvc-border-width) * -1)}.pvc-settings-form [role=search] button,.pvc-settings-form [role=search] [type=submit],.pvc-settings-form [role=search] [type=reset],.pvc-settings-form [role=search] [type=button],.pvc-settings-form [role=search] [role=button],.pvc-settings-form [role=group] button,.pvc-settings-form [role=group] [type=submit],.pvc-settings-form [role=group] [type=reset],.pvc-settings-form [role=group] [type=button],.pvc-settings-form [role=group] [role=button]{width:auto}@supports selector(:has(*)){.pvc-settings-form [role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus),.pvc-settings-form [role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus){--pvc-group-box-shadow: var(--pvc-group-box-shadow-focus-with-button)}.pvc-settings-form [role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) input:not([type=checkbox],[type=radio]),.pvc-settings-form [role=search]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) select,.pvc-settings-form [role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) input:not([type=checkbox],[type=radio]),.pvc-settings-form [role=group]:has(button:focus,[type=submit]:focus,[type=button]:focus,[role=button]:focus) select{border-color:transparent}.pvc-settings-form [role=search]:has(input:not([type=submit],[type=button]):focus,select:focus),.pvc-settings-form [role=group]:has(input:not([type=submit],[type=button]):focus,select:focus){--pvc-group-box-shadow: var(--pvc-group-box-shadow-focus-with-input)}.pvc-settings-form [role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) button,.pvc-settings-form [role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=submit],.pvc-settings-form [role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=button],.pvc-settings-form [role=search]:has(input:not([type=submit],[type=button]):focus,select:focus) [role=button],.pvc-settings-form [role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) button,.pvc-settings-form [role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=submit],.pvc-settings-form [role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [type=button],.pvc-settings-form [role=group]:has(input:not([type=submit],[type=button]):focus,select:focus) [role=button]{--pvc-button-box-shadow: 0 0 0 var(--pvc-border-width) var(--pvc-primary-border);--pvc-button-hover-box-shadow: 0 0 0 var(--pvc-border-width) var(--pvc-primary-hover-border)}.pvc-settings-form [role=search] button:focus,.pvc-settings-form [role=search] [type=submit]:focus,.pvc-settings-form [role=search] [type=reset]:focus,.pvc-settings-form [role=search] [type=button]:focus,.pvc-settings-form [role=search] [role=button]:focus,.pvc-settings-form [role=group] button:focus,.pvc-settings-form [role=group] [type=submit]:focus,.pvc-settings-form [role=group] [type=reset]:focus,.pvc-settings-form [role=group] [type=button]:focus,.pvc-settings-form [role=group] [role=button]:focus{box-shadow:none}}.pvc-settings-form [role=search]>*:first-child{border-top-left-radius:5rem;border-bottom-left-radius:5rem}.pvc-settings-form [role=search]>*:last-child{border-top-right-radius:5rem;border-bottom-right-radius:5rem}.pvc-settings-form [aria-busy=true]:not(input,select,textarea,html,form){white-space:nowrap}.pvc-settings-form [aria-busy=true]:not(input,select,textarea,html,form):before{display:inline-block;width:1em;height:1em;background-image:var(--pvc-icon-loading);background-size:1em auto;background-repeat:no-repeat;content:"";vertical-align:-.125em}.pvc-settings-form [aria-busy=true]:not(input,select,textarea,html,form):not(:empty):before{margin-inline-end:calc(var(--pvc-spacing) * .5)}.pvc-settings-form [aria-busy=true]:not(input,select,textarea,html,form):empty{text-align:center}.pvc-settings-form button[aria-busy=true],.pvc-settings-form [type=submit][aria-busy=true],.pvc-settings-form [type=button][aria-busy=true],.pvc-settings-form [type=reset][aria-busy=true],.pvc-settings-form [role=button][aria-busy=true],.pvc-settings-form a[aria-busy=true]{pointer-events:none}.pvc-settings-form [aria-controls]{cursor:pointer}.pvc-settings-form [aria-disabled=true],.pvc-settings-form [disabled]{cursor:not-allowed}.pvc-settings-form [aria-hidden=false][hidden]{display:initial}.pvc-settings-form [aria-hidden=false][hidden]:not(:focus){clip:rect(0,0,0,0);position:absolute}.pvc-settings-form a,.pvc-settings-form area,.pvc-settings-form button,.pvc-settings-form input,.pvc-settings-form label,.pvc-settings-form select,.pvc-settings-form summary,.pvc-settings-form textarea,.pvc-settings-form [tabindex]{-ms-touch-action:manipulation}.pvc-settings-form [dir=rtl]{direction:rtl}.pvc-settings-wrapper{--pvc-theme-color: var(--wp-admin-theme-color, #2271b1);--pvc-theme-color-rgb: var(--wp-admin-theme-color--rgb, 34, 113, 177);--pvc-theme-color-darker-10: var(--wp-admin-theme-color-darker-10, #135e96);--pvc-border-width-focus: var(--wp-admin-border-width-focus, 2px);--pvc-surface: #fff;--pvc-surface-muted: #ebebeb;--pvc-text: #3c434a;--pvc-text-strong: #070707;--pvc-text-input: #2c3338;--pvc-text-muted: #757575;--pvc-border: #c3c4c7;--pvc-border-input: #8c8f94;--pvc-button-bg: #f6f7f7;--pvc-button-bg-hover: #f0f0f1;--pvc-button-border: #8c8f94;--pvc-button-border-hover: #757575;--pvc-button-text: #2c3338;--pvc-button-text-hover: #070707;--pvc-button-focus: var(--pvc-theme-color);--pvc-font-family-emoji: inherit;--pvc-font-family-sans-serif: inherit;--pvc-font-family-monospace: inherit;--pvc-font-family: var(--pvc-font-family-sans-serif);--pvc-line-height: 1.5;--pvc-font-weight: 400;--pvc-font-size: 100%;--pvc-text-underline-offset: .1rem;--pvc-border-radius: .25rem;--pvc-border-width: .0625rem;--pvc-outline-width: .125rem;--pvc-transition: .2s ease-in-out;--pvc-spacing: 1rem;--pvc-typography-spacing-vertical: 1rem;--pvc-block-spacing-vertical: 20px;--pvc-block-spacing-horizontal: 20px;--pvc-grid-column-gap: 20px;--pvc-grid-row-gap: 20px;--pvc-form-element-spacing-vertical: .45rem;--pvc-form-element-spacing-horizontal: .8rem;--pvc-form-element-disabled-opacity: .6;--pvc-group-box-shadow: 0 0 0 rgba(0, 0, 0, 0);--pvc-group-box-shadow-focus-with-button: 0 0 0 var(--pvc-outline-width) var(--pvc-primary-focus);--pvc-group-box-shadow-focus-with-input: 0 0 0 .0625rem var(--pvc-form-element-border-color);--pvc-modal-overlay-backdrop-filter: blur(.375rem);--pvc-nav-element-spacing-vertical: 1rem;--pvc-nav-element-spacing-horizontal: .5rem;--pvc-nav-link-spacing-vertical: .5rem;--pvc-nav-link-spacing-horizontal: .5rem;--pvc-nav-breadcrumb-divider: ">";--pvc-primary: var(--pvc-theme-color);--pvc-primary-background: var(--pvc-theme-color);--pvc-primary-border: var(--pvc-theme-color);--pvc-primary-underline: rgba(var(--pvc-theme-color-rgb), .5);--pvc-primary-hover: var(--pvc-theme-color-darker-10);--pvc-primary-hover-background: var(--pvc-theme-color-darker-10);--pvc-primary-hover-border: var(--pvc-theme-color-darker-10);--pvc-primary-hover-underline: var(--pvc-theme-color-darker-10);--pvc-primary-focus: rgba(var(--pvc-theme-color-rgb), .25);--pvc-primary-inverse: #fff;--pvc-secondary: var(--pvc-button-text);--pvc-secondary-background: var(--pvc-button-bg);--pvc-secondary-border: var(--pvc-button-border);--pvc-secondary-underline: rgba(var(--pvc-theme-color-rgb), .4);--pvc-secondary-hover: var(--pvc-button-text-hover);--pvc-secondary-hover-background: var(--pvc-button-bg-hover);--pvc-secondary-hover-border: var(--pvc-button-border-hover);--pvc-secondary-hover-underline: var(--pvc-button-border-hover);--pvc-secondary-focus: var(--pvc-button-focus);--pvc-secondary-inverse: var(--pvc-button-text);--pvc-contrast: var(--pvc-text-strong);--pvc-contrast-background: var(--pvc-text-strong);--pvc-contrast-border: var(--pvc-text-strong);--pvc-contrast-hover: #000;--pvc-contrast-hover-background: #000;--pvc-contrast-hover-border: #000;--pvc-contrast-focus: rgba(var(--pvc-theme-color-rgb), .25);--pvc-contrast-inverse: #fff;color-scheme:light;--pvc-background-color: var(--pvc-surface);--pvc-color: var(--pvc-text);--pvc-muted-color: var(--pvc-text-muted);--pvc-muted-border-color: var(--pvc-border);--pvc-box-shadow: 0 1px 1px rgba(0, 0, 0, .04);--pvc-text-selection-color: rgba(var(--pvc-theme-color-rgb), .25);--pvc-table-border-color: var(--pvc-border);--pvc-table-row-stripped-background-color: var(--pvc-surface-muted);--pvc-form-element-background-color: var(--pvc-surface);--pvc-form-element-active-background-color: var(--pvc-surface);--pvc-form-element-selected-background-color: var(--pvc-surface-muted);--pvc-form-element-border-color: var(--pvc-border-input);--pvc-form-element-color: var(--pvc-text-input);--pvc-form-element-placeholder-color: var(--pvc-text-muted);--pvc-form-element-active-border-color: var(--pvc-theme-color);--pvc-form-element-focus-color: var(--pvc-theme-color);--pvc-switch-background-color: var(--pvc-border-input);--pvc-switch-checked-background-color: var(--pvc-theme-color);--pvc-switch-color: #fff;--pvc-outline-width: var(--pvc-border-width-focus);--pvc-block-spacing: var(--pvc-block-spacing-vertical) var(--pvc-block-spacing-horizontal);--pvc-form-element-spacing: var(--pvc-form-element-spacing-vertical) var(--pvc-form-element-spacing-horizontal);--pvc-typography-spacing: var(--pvc-typography-spacing-vertical)}.pvc-settings-wrapper .button.button-secondary{--pvc-primary: var(--pvc-secondary);--pvc-primary-background: var(--pvc-secondary-background);--pvc-primary-border: var(--pvc-secondary-border);--pvc-primary-hover: var(--pvc-secondary-hover);--pvc-primary-hover-background: var(--pvc-secondary-hover-background);--pvc-primary-hover-border: var(--pvc-secondary-hover-border);--pvc-primary-hover-underline: var(--pvc-secondary-hover-underline);--pvc-primary-focus: var(--pvc-secondary-focus);--pvc-primary-inverse: var(--pvc-secondary-inverse)}.pvc-settings-wrapper code,.pvc-settings-wrapper kbd,.pvc-settings-wrapper samp{background:#f0f0f1;background:#00000012;color:var(--pvc-text)}.wp-admin #wpcontent{padding:0}.wp-admin #wpbody-content>.notice,.wp-admin #wpbody-content>.error,.wp-admin #wpbody-content>.updated,.wp-admin #wpbody-content>.info{display:none}.wp-admin .wrap{margin:0}.pvc-settings-wrapper .header-wrapper{padding:20px 40px;background-color:var(--pvc-surface);border-bottom:1px solid var(--pvc-surface-muted)}.pvc-settings-wrapper .content-wrapper{padding:0 40px}.pvc-settings-wrapper .header-title{font-weight:600;font-size:18px;line-height:1;color:var(--pvc-text-strong)}.pvc-settings-wrapper .nav-tab-wrapper{display:flex;flex-wrap:wrap;list-style:none;padding:10px 40px 0;gap:30px;margin:0 0 20px;background:var(--pvc-surface);border-bottom:1px solid var(--pvc-surface-muted)}.pvc-settings-wrapper .nav-tab-wrapper .nav-tab{margin:2px 0 0;border:0;background:none;font-weight:600;font-size:14px;line-height:20px;padding:0 0 10px;text-decoration:none;border-bottom:2px solid transparent}.pvc-settings-wrapper .nav-tab-wrapper .nav-tab:hover,.pvc-settings-wrapper .nav-tab-wrapper .nav-tab:focus{color:var(--wp-admin-theme-color);outline:none;box-shadow:none}.pvc-settings-wrapper .nav-tab-wrapper .nav-tab.nav-tab-active{border-bottom-color:var(--wp-admin-theme-color)}.pvc-settings form{min-width:463px;width:auto;flex:1 1 auto}.pvc-settings form input[type=radio]:checked{background-color:var(--pvc-form-element-background-color)}.pvc-settings input:not([type=checkbox],[type=radio],[type=submit],[type=reset],[type=button]){width:auto;min-width:80px;max-width:100%;font-size:var(--pvc-font-size);line-height:var(--pvc-line-height);border-radius:var(--pvc-border-radius);margin-bottom:0;padding:var(--pvc-form-element-spacing-vertical) var(--pvc-form-element-spacing-horizontal);min-height:calc(1rem * var(--pvc-line-height) + var(--pvc-form-element-spacing-vertical) * 2 + var(--pvc-border-width) * 2)}.pvc-settings input:not([type=checkbox],[type=radio],[type=submit],[type=reset],[type=button]).large-text{width:100%}.pvc-settings input:not([type=checkbox],[type=radio],[type=submit],[type=reset],[type=button]).regular-text{width:25rem}.pvc-settings input:not([type=checkbox],[type=radio],[type=submit],[type=reset],[type=button]).small-text{width:8rem}.pvc-settings [type=search]{--pvc-border-radius: .25rem}.pvc-settings select,.pvc-settings textarea{width:auto;max-width:100%;font-size:var(--pvc-font-size);line-height:var(--pvc-line-height);margin-bottom:0}.pvc-settings select{width:25rem;min-height:calc(1rem * var(--pvc-line-height) + var(--pvc-form-element-spacing-vertical) * 2 + var(--pvc-border-width) * 2)}.pvc-settings [type=submit].button,.pvc-settings [type=button].button{width:auto;font-size:var(--pvc-font-size);line-height:var(--pvc-line-height);border-radius:var(--pvc-border-radius);min-height:calc(1rem * var(--pvc-line-height) + var(--pvc-form-element-spacing-vertical) * 2 + var(--pvc-border-width) * 2)}.pvc-settings [type=submit].button.button-small,.pvc-settings [type=button].button.button-small{font-size:calc(var(--pvc-font-size) * .875);height:auto}.pvc-settings span[role=button]{background:none;border:none;padding:0;margin:0;font-size:inherit}.pvc-settings .submit{display:flex;gap:10px}.pvc-settings .pvc-sidebar{width:300px;flex:0 0 300px;margin-left:20px}.pvc-settings.has-sidebar{display:flex;justify-content:space-between}@media(max-width:960px){.pvc-settings.has-sidebar{flex-direction:column}.pvc-settings form{width:100%}.pvc-settings .pvc-sidebar{width:100%;flex:0 0 auto;margin-left:0;margin-top:20px}}.pvc-settings p.help,.pvc-settings p.description,.pvc-settings span.description{font-size:13px;font-style:italic;color:var(--pvc-text-muted)}.pvc-range-field{display:flex;gap:10px;align-items:center}.pvc-range-field input[type=range]{appearance:none;color-scheme:light;background:transparent;accent-color:var(--pvc-theme-color)}.pvc-range-field input[type=range]::-webkit-slider-runnable-track{height:4px;background:#dcdcde;border-radius:999px}.pvc-range-field input[type=range]::-webkit-slider-thumb{appearance:none;width:16px;height:16px;margin-top:-6px;border-radius:50%;background:var(--pvc-theme-color);border:1px solid var(--pvc-theme-color)}.pvc-range-field input[type=range]::-moz-range-track{height:4px;background:#dcdcde;border-radius:999px}.pvc-range-field input[type=range]::-moz-range-progress{height:4px;background:var(--pvc-theme-color);border-radius:999px}.pvc-range-field input[type=range]::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background:var(--pvc-theme-color);border:1px solid var(--pvc-theme-color)}.pvc-buttons-group{display:flex;gap:10px}.pvc-buttons-group [type=submit].button,.pvc-buttons-group [type=button].button{margin:0}.pvc-field-group label{margin-right:10px}.pvc-field-group.horizontal label{display:inline-block}.pvc-field-group.vertical label{display:block;margin-bottom:8px}.pvc-field input[type=checkbox]:checked:not([role=switch]):before,.pvc-field input[type=radio]:checked:before{display:none}.pvc-field input[type=checkbox][role=switch]:checked:before{width:auto;margin-top:0;margin-bottom:0}.pvc-disabled{opacity:var(--pvc-form-element-disabled-opacity);cursor:not-allowed}.pvc-disabled [type=radio]{border-color:var(--pvc-form-element-border-color)}.pvc-disabled select[disabled]{opacity:1}.pvc-field-type-color .pvc-color-control{display:flex;gap:10px;align-items:center;position:relative;width:fit-content}.pvc-field-type-color .pvc-color-control button{margin-bottom:0}.pvc-field-type-color .pvc-color-input{min-width:8rem}.pvc-field-type-color .pvc-color-swatch{width:32px;height:32px;padding:0;border-radius:var(--pvc-border-radius);border:1px solid var(--pvc-form-element-border-color);box-shadow:inset 0 0 0 2px var(--pvc-form-element-background-color);cursor:pointer;flex-shrink:0}.pvc-field-type-color .pvc-color-swatch:focus{outline:none;box-shadow:inset 0 0 0 2px var(--pvc-form-element-background-color),0 0 0 2px var(--pvc-form-element-focus-color)}.pvc-field-type-color .pvc-color-popover{position:absolute;top:calc(100% + 6px);left:0;padding:8px;border-radius:var(--pvc-border-radius);border:1px solid var(--pvc-form-element-border-color);background:var(--pvc-form-element-background-color);box-shadow:0 6px 18px #0000001f;z-index:1000;display:none;pointer-events:auto}.pvc-field-type-color.is-open .pvc-color-popover{display:block}.pvc-field-type-color hex-color-picker{width:200px;height:200px;pointer-events:auto}.pvc-checkbox-group label:has(input:disabled),.pvc-radio-group label:has(input:disabled){opacity:var(--pvc-form-element-disabled-opacity);cursor:not-allowed}.pvc-hidden{display:none!important}@keyframes pvc-fade-in{0%{opacity:0}to{opacity:1}}@keyframes pvc-fade-out{0%{opacity:1}to{opacity:0}}@keyframes pvc-slide-in{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes pvc-slide-out{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-10px)}}.pvc-anim-in,.pvc-anim-out{animation-duration:var(--pvc-animation-duration, .25s);animation-fill-mode:both}.pvc-anim-fade.pvc-anim-in{animation-name:pvc-fade-in}.pvc-anim-fade.pvc-anim-out{animation-name:pvc-fade-out}.pvc-anim-slide.pvc-anim-in{animation-name:pvc-slide-in}.pvc-anim-slide.pvc-anim-out{animation-name:pvc-slide-out}.post-views-counter-settings.has-sidebar{display:flex;flex-direction:row;gap:30px;justify-content:space-between}.post-views-counter-settings form{min-width:463px;width:auto;position:relative}.post-views-counter-settings p.help,.post-views-counter-settings p.description{font-size:13px;font-style:italic;line-height:1.6}.post-views-counter-settings .pvc-template-tags-list{display:block;margin-top:5px;font-style:normal}.post-views-counter-settings select{vertical-align:top;width:auto}.post-views-counter-settings .available{color:#00a32a}.post-views-counter-settings .unavailable{color:#d63638}.post-views-counter-settings .pvc-ip-box-group{margin-bottom:10px}.post-views-counter-settings .pvc-ip-box{margin-bottom:5px}.post-views-counter-settings .pvc-button{color:#fff;background-color:#6610f2;border-color:#6610f2;border-radius:.25rem}.post-views-counter-settings .pvc-button:active,.post-views-counter-settings .pvc-button:focus,.post-views-counter-settings .pvc-button:hover{color:#fff;background-color:#570ece;border-color:#570ece}.post-views-counter-settings .pvc-button:focus{box-shadow:0 0 0 1px #fff,0 0 0 3px #6610f2}.post-views-counter-settings tr.pvc-pro th:after{content:"PRO";display:inline-block;margin-left:6px;padding:1px 4px;font-size:11px;border-radius:4px;background-color:#ffc107;color:#fff;font-weight:600}.post-views-counter-settings label.pvc-pro:after{content:"PRO";display:inline-block;margin-left:6px;padding:1px 4px;font-size:11px;border-radius:4px;background-color:#ffc107;color:#fff;font-weight:600;font-style:normal}.post-views-sidebar{margin:15px 0;position:relative;order:1}.post-views-sidebar>div:not(:last-child){margin-bottom:3em}.post-views-sidebar .inner{padding:1.5em}.post-views-sidebar .inner img{max-width:80%;height:auto;display:block;margin:20px auto}.pvc-status-table .pvc-status{display:inline-block}.pvc-status-table .pvc-status-active{color:#00a32a}.pvc-status-table .pvc-status-missing{color:#d63638}.pvc-subfield{margin-top:12px}.is-hidden{display:none!important;visibility:hidden!important}output{display:block;font-size:30px;font-weight:700;text-align:center;margin:30px 0;width:100%}.post-views-credits{background:#fff;box-shadow:0 0 0 1px #0000000d}.post-views-credits .inner{text-align:center;margin:0}.post-views-credits p:first-child{margin-top:0}.post-views-credits .pvc-sidebar-title{font-size:17px;font-weight:700;margin:.5rem 0 1rem}.post-views-credits .pvc-sidebar-title:after{content:"PRO";display:inline-block;margin-left:6px;padding:0 4px 1px;font-size:14px;border-radius:4px;background-color:#ffc107;color:#fff;font-weight:600}.post-views-credits .pvc-sidebar-subtitle{font-size:15px;font-weight:600;color:#1d2327;text-align:center;margin:1rem 0 1.5rem}.post-views-credits .pvc-sidebar-one-liner{text-align:center;margin-top:1.5rem}.post-views-credits .pvc-sidebar-body{font-size:14px;text-align:left;margin:1.5rem 0 2rem;padding:0}.post-views-credits .pvc-sidebar-body p{font-size:inherit;padding-left:20px;margin:.75rem 0;position:relative}.post-views-credits .pvc-sidebar-body b{color:#000}.post-views-credits .pvc-sidebar-body .pvc-icon{position:absolute;top:1px;left:0}.post-views-credits .pvc-sidebar-body .pvc-icon-check{box-sizing:border-box;display:block;transform:scale(1);width:16px;height:22px;border-radius:100px}.post-views-credits .pvc-sidebar-body .pvc-icon-check:after{content:"";display:block;box-sizing:border-box;position:absolute;left:0;top:0;width:6px;height:10px;border-width:0 2px 2px 0;border-style:solid;transform-origin:bottom left;transform:rotate(45deg)}#post_views_counter_other_license_setting .pvc-status-icon{vertical-align:middle;margin-left:8px;padding-bottom:3px}#post_views_counter_other_license_setting .pvc-status-icon:before{content:"\2717";color:#d63638}#post_views_counter_other_license_setting.license-status .pvc-status-icon:before{content:"\2717";color:#d63638}#post_views_counter_other_license_setting.license-status.valid .pvc-status-icon:before{content:"\2713";color:#00a32a}#pvc-reports-upgrade{position:absolute;left:0;top:0;height:100%;width:100%;overflow:hidden;box-sizing:border-box;min-height:400px}#pvc-reports-upgrade #pvc-reports-modal{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);padding:1.5em 3em;box-shadow:0 0 25px 10px #0000001a;border-radius:3px;background-color:#fff;text-align:center;width:26em}#pvc-reports-upgrade #pvc-reports-modal p{margin:0}#pvc-reports-upgrade #pvc-reports-modal h2{font-size:21px;font-weight:400;margin:0 0 10px;padding:9px 0 4px;line-height:1.3}#pvc-reports-upgrade #pvc-reports-modal .button{margin-top:25px;margin-bottom:10px}#pvc-reports-bg{width:100%;height:auto;opacity:.8;filter:blur(2px)}@media only screen and (max-width:960px){.post-views-counter-settings{flex-wrap:wrap}.post-views-counter-settings .post-views-sidebar{width:100%}}@media only screen and (max-width:782px){.post-views-counter-settings .pvc-settings-form{min-width:0}.post-views-counter-settings .form-table{table-layout:fixed}.post-views-counter-settings .form-table td input.regular-text,.post-views-counter-settings .form-table td select,.post-views-counter-settings .form-table td textarea{width:100%;max-width:100%}}.pvc-provider-radio{display:inline-block;margin-right:20px;font-weight:400}.pvc-provider-radio input[type=radio]{margin-right:5px}.pvc-provider-content{margin:0}.pvc-provider-fields{padding:0;margin-top:15px}.pvc-provider-fields label{font-weight:600;margin-bottom:5px;display:block}.pvc-provider-fields input.regular-text{margin-top:5px}.pvc-provider-unavailable{color:#d63638;font-style:italic}.pvc-import-strategy{margin-bottom:25px}.pvc-field>label{margin-bottom:0}.pvc-field-group label{margin-right:8px}.pvc-radio-vertical label{display:block;margin:6px 0;font-weight:400}.pvc-radio-vertical input[type=radio]{margin-right:5px}.pvc-import-actions{padding-top:10px}.pvc-import-actions .button{margin-right:10px}.pvc-import-actions .button-primary{font-weight:600}.pvc-integration-content.unavailable{color:inherit;pointer-events:none}.pvc-integration-content.unavailable .pvc-integration-items{display:none}.pvc-integration-content input[type=checkbox]{margin-right:10px}.pvc-integration-content label{display:block;cursor:pointer}.pvc-integration-content .pvc-integration-items{margin:15px 0 0;padding-left:13px;font-size:13px;font-style:italic}.pvc-integration-content .pvc-integration-items li{margin-bottom:5px;list-style:disc}.pvc-integration-content .pvc-integration-items li:last-child{margin-bottom:0}.mce-container[role=group]{margin:0;display:block}
  • post-views-counter/tags/1.8.0/css/block-editor.css

    r3647380 r3734102  
    1 .edit-post-post-views{gap:8px;margin-top:0}.edit-post-post-views-popover .components-popover__content{padding:10px;min-width:260px}.edit-post-post-views-popover .components-popover__content legend{font-weight:600;margin-bottom:1em;margin-top:.5em;padding:0}.components-button:has(.pvc-icon){pointer-events:none;cursor:default;position:relative}.components-button:has(.pvc-icon) span.components-truncate{opacity:.5}.components-button:has(.pvc-icon) .pvc-icon svg{opacity:.5}.pvc-icon{position:relative;display:inline-flex}.pvc-icon__badge{position:absolute;bottom:-2px;right:-4px;font-size:9px;font-weight:600;line-height:1;color:#fff;background-color:#ffc107;border-radius:2px;padding:1px 2px 2px}
     1.edit-post-post-views{gap:8px;margin-top:0}.edit-post-post-views-value{display:inline-block;padding:0 8px;line-height:32px}.edit-post-post-views-popover .components-popover__content{padding:10px;min-width:260px}.edit-post-post-views-popover .components-popover__content .edit-post-post-views-popover-content{display:flex;flex-direction:column;align-items:flex-start;gap:8px}.edit-post-post-views-popover .components-popover__content .edit-post-post-views-popover-content .components-base-control{width:100%}.edit-post-post-views-popover .components-popover__content .edit-post-post-views-popover-content .description{margin:0}.components-button:has(.pvc-icon){pointer-events:none;cursor:default;position:relative}.components-button:has(.pvc-icon) span.components-truncate{opacity:.5}.components-button:has(.pvc-icon) .pvc-icon svg{opacity:.5}.pvc-icon{position:relative;display:inline-flex}.pvc-icon__badge{position:absolute;bottom:-2px;right:-4px;font-size:9px;font-weight:600;line-height:1;color:#fff;background-color:#ffc107;border-radius:2px;padding:1px 2px 2px}
  • post-views-counter/tags/1.8.0/css/frontend.css

    r3640258 r3734102  
    1 .post-views.entry-meta>span{margin-right:0!important;font:16px/1}.post-views.entry-meta>span.post-views-icon.dashicons{display:inline-block;font-size:16px;line-height:1;text-decoration:inherit;vertical-align:middle}.post-views.load-dynamic .post-views-count{color:#0000;transition:color .3s ease-in-out;position:relative}.post-views.load-dynamic .post-views-count:after{opacity:0;transition:opacity .3s ease-in-out;position:relative;color:#6610f2}.post-views.load-dynamic.loaded .post-views-count,.post-views.load-dynamic.load-error .post-views-count{color:inherit}.post-views.load-dynamic.load-error .post-views-count:after{content:none;opacity:0;animation:none}.post-views.load-dynamic.loading .post-views-count,.post-views.load-dynamic.loading .post-views-count:after{box-sizing:border-box}.post-views.load-dynamic.loading .post-views-count:after{content:"";display:block;width:16px;height:16px;border-radius:50%;border:2px solid currentColor;border-color:currentColor transparent currentColor transparent;animation:pvc-loading 1s linear infinite;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);opacity:1}@keyframes pvc-loading{0%{transform:translate(-50%,-50%) rotate(0)}to{transform:translate(-50%,-50%) rotate(360deg)}}
     1.pvc-container{display:inline}.pvc-container .pvc-icon,.pvc-custom-format .pvc-icon{display:inline-block;width:16px;height:16px;line-height:1;vertical-align:middle;vertical-align:calc(.5cap - 8px);margin-right:.25em;background-color:currentColor;-webkit-mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3.5 15v6a1.5 1.5 0 0 0 3 0v-6a1.5 1.5 0 0 0-3 0Zm7-6v12a1.5 1.5 0 0 0 3 0V9a1.5 1.5 0 0 0-3 0Zm7-6v18a1.5 1.5 0 0 0 3 0V3a1.5 1.5 0 0 0-3 0Z'/%3E%3C/svg%3E");mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3.5 15v6a1.5 1.5 0 0 0 3 0v-6a1.5 1.5 0 0 0-3 0Zm7-6v12a1.5 1.5 0 0 0 3 0V9a1.5 1.5 0 0 0-3 0Zm7-6v18a1.5 1.5 0 0 0 3 0V3a1.5 1.5 0 0 0-3 0Z'/%3E%3C/svg%3E");-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:center;mask-position:center;-webkit-mask-size:100% 100%;mask-size:100% 100%}.pvc-container .pvc-icon-custom,.pvc-custom-format .pvc-icon-custom{vertical-align:middle;background-color:transparent;-webkit-mask-image:none;mask-image:none}.post-views.entry-meta:not(.pvc-container)>span{margin-right:0!important;font:16px/1}.post-views.entry-meta:not(.pvc-container)>span.post-views-icon.dashicons{display:inline-block;font-size:16px;line-height:1;text-decoration:inherit;vertical-align:middle}[data-pvc-metric].load-dynamic>.post-views-count,.post-views.load-dynamic>.post-views-count{color:#0000;transition:color .3s ease-in-out;position:relative}[data-pvc-metric].load-dynamic>.post-views-count:after,.post-views.load-dynamic>.post-views-count:after{opacity:0;transition:opacity .3s ease-in-out;position:relative;color:#6610f2}[data-pvc-metric].load-dynamic.loaded>.post-views-count,[data-pvc-metric].load-dynamic.load-error>.post-views-count,.post-views.load-dynamic.loaded>.post-views-count,.post-views.load-dynamic.load-error>.post-views-count{color:inherit}[data-pvc-metric].load-dynamic.load-error>.post-views-count:after,.post-views.load-dynamic.load-error>.post-views-count:after{content:none;opacity:0;animation:none}[data-pvc-metric].load-dynamic.loading>.post-views-count,[data-pvc-metric].load-dynamic.loading>.post-views-count:after,.post-views.load-dynamic.loading>.post-views-count,.post-views.load-dynamic.loading>.post-views-count:after{box-sizing:border-box}[data-pvc-metric].load-dynamic.loading>.post-views-count:after,.post-views.load-dynamic.loading>.post-views-count:after{content:"";display:block;width:16px;height:16px;border-radius:50%;border:2px solid currentColor;border-color:currentColor transparent currentColor transparent;animation:pvc-loading 1s linear infinite;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);opacity:1}@keyframes pvc-loading{0%{transform:translate(-50%,-50%) rotate(0)}to{transform:translate(-50%,-50%) rotate(360deg)}}
  • post-views-counter/tags/1.8.0/includes/class-admin.php

    r3647380 r3734102  
    2323
    2424        /**
    25          * Register Chart.js.
     25         * Register Chart.js and the shared metric button component.
     26         *
     27         * Core owns the metric button runtime so dashboard charts, the column modal,
     28         * and extension charts draw the same control. Consumers load the registered
     29         * handle when available and keep a local fallback for older Core versions.
    2630         *
    2731         * @return void
    2832         */
    2933        public function register_chartjs() {
    30                 wp_register_script( 'pvc-chartjs', POST_VIEWS_COUNTER_URL . '/assets/chartjs/chart.min.js', [ 'jquery' ], '4.5.0', true );
     34                wp_register_script( 'pvc-chartjs', POST_VIEWS_COUNTER_URL . '/assets/chartjs/chart.min.js', [ 'jquery' ], '4.5.1', true );
     35                wp_register_script( 'pvc-metric-toggle', POST_VIEWS_COUNTER_URL . '/js/metric-toggle.js', [], Post_Views_Counter()->defaults['version'], true );
    3136        }
    3237
    … …  
    4752         */
    4853        public function block_editor_rest_api_init() {
    49                 // get views route
     54                // update counter totals route
    5055                register_rest_route(
    5156                        'post-views-counter',
    … …  
    5762                                'args'                                  => [
    5863                                        'id' => [
    59                                                 'sanitize_callback'     => 'absint',
     64                                                'required'                      => true,
     65                                                'validate_callback'     => [ $this, 'validate_post_id_param' ],
     66                                                'sanitize_callback'     => 'absint'
     67                                        ],
     68                                        'views' => [
     69                                                'validate_callback'     => [ $this, 'validate_counter_total_param' ]
     70                                        ],
     71                                        // released alias of views
     72                                        'post_views' => [
     73                                                'validate_callback'     => [ $this, 'validate_counter_total_param' ]
    6074                                        ]
    6175                                ]
    … …  
    6579
    6680        /**
    67          * Check whether user has permissions to perform post views update in block editor.
    68          *
    69          * @param object $request WP_REST_Request
     81         * Validate the post ID before absint() can turn a malformed value such
     82         * as -20, 20.7 or 20junk into another existing post ID.
     83         *
     84         * @param mixed           $value Submitted value.
     85         * @param WP_REST_Request $request Request.
     86         * @param string          $param Parameter name.
     87         * @return true|WP_Error
     88         */
     89        public function validate_post_id_param( $value, $request, $param ) {
     90                // a positive integer, as an int or a canonical decimal string
     91                if ( ( is_int( $value ) || is_string( $value ) ) && filter_var( $value, FILTER_VALIDATE_INT, [ 'options' => [ 'min_range' => 1 ] ] ) !== false && ( is_int( $value ) || ctype_digit( $value ) ) )
     92                        return true;
     93
     94                /* translators: %s: request parameter name */
     95                return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be a positive whole number.', 'post-views-counter' ), $param ), [ 'status' => 400 ] );
     96        }
     97
     98        /**
     99         * Validate one submitted counter total: a whole number, or blank.
     100         *
     101         * @param mixed           $value Submitted value.
     102         * @param WP_REST_Request $request Request.
     103         * @param string          $param Parameter name.
     104         * @return true|WP_Error
     105         */
     106        public function validate_counter_total_param( $value, $request, $param ) {
     107                if ( is_null( $value ) || is_int( $value ) )
     108                        return true;
     109
     110                if ( is_string( $value ) && ( trim( $value ) === '' || preg_match( '/^-?\d+$/', trim( $value ) ) ) )
     111                        return true;
     112
     113                /* translators: %s: request parameter name */
     114                return new WP_Error( 'rest_invalid_param', sprintf( __( '%s must be a whole number.', 'post-views-counter' ), $param ), [ 'status' => 400 ] );
     115        }
     116
     117        /**
     118         * Check whether the current user may update this post's counter totals
     119         * from the block editor.
     120         *
     121         * @param WP_REST_Request $request Request.
    70122         * @return bool|WP_Error
    71123         */
    72124        public function check_rest_route_permissions( $request ) {
    73                 // break if current user can't edit this post
    74                 if ( ! current_user_can( 'edit_post', (int) $request->get_param( 'id' ) ) )
    75                         return new WP_Error( 'pvc-user-not-allowed', __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
    76 
    77                 // break if views editing is restricted
    78                 if ( (bool) Post_Views_Counter()->options['display']['restrict_edit_views'] === true && ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) )
    79                         return new WP_Error( 'pvc-user-not-allowed', __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
     125                $post_id = (int) $request->get_param( 'id' );
     126                $columns = Post_Views_Counter()->columns;
     127
     128                // checked first so the route does not reveal which posts exist
     129                if ( ! current_user_can( 'edit_post', $post_id ) )
     130                        return new WP_Error( 'pvc-user-not-allowed', __( 'You are not allowed to edit this item.', 'post-views-counter' ), [ 'status' => rest_authorization_required_code() ] );
     131
     132                if ( ! $columns || ! $columns->is_post_counter_visible_in_editor( $post_id ) )
     133                        return new WP_Error( 'pvc-invalid-post', __( 'Invalid post ID.', 'post-views-counter' ), [ 'status' => 404 ] );
     134
     135                if ( ! $columns->can_edit_post_counter_in_editor( $post_id ) )
     136                        return new WP_Error( 'pvc-user-not-allowed', __( 'You are not allowed to edit this item.', 'post-views-counter' ), [ 'status' => rest_authorization_required_code() ] );
    80137
    81138                return true;
    … …  
    83140
    84141        /**
    85          * REST API callback for block editor endpoint.
    86          *
    87          * @param array $data
    88          * @return string|int
    89          */
    90         public function block_editor_update_callback( $data ) {
    91                 // get main instance
    92                 $pvc = Post_Views_Counter();
    93 
    94                 // cast post ID
    95                 $post_id = ! empty( $data['id'] ) ? (int) $data['id'] : 0;
    96 
    97                 // cast post views
    98                 $post_views = ! empty( $data['post_views'] ) ? (int) $data['post_views'] : 0;
    99 
    100                 // get countable post types
    101                 $post_types = (array) $pvc->options['general']['post_types_count'];
    102 
    103                 // check if post exists
    104                 $post = get_post( $post_id );
    105 
    106                 // whether to count this post type or not
    107                 if ( empty( $post_types ) || empty( $post ) || ! in_array( $post->post_type, $post_types, true ) )
    108                         return wp_send_json_error( __( 'Invalid post ID.', 'post-views-counter' ) );
    109 
    110                 // break if current user can't edit this post
    111                 if ( ! current_user_can( 'edit_post', $post_id ) )
    112                         return wp_send_json_error( __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
    113 
    114                 // break if views editing is restricted
    115                 if ( (bool) $pvc->options['display']['restrict_edit_views'] === true && ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) )
    116                         return wp_send_json_error( __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
    117 
    118                 // update post views
    119                 pvc_update_post_views( $post_id, $post_views );
    120 
    121                 do_action( 'pvc_after_update_post_views_count', $post_id );
    122 
    123                 return $post_id;
     142         * REST API callback for the block editor endpoint.
     143         *
     144         * Only an edited Views total is written. The shared writer re-checks
     145         * eligibility.
     146         *
     147         * @param WP_REST_Request $request Request.
     148         * @return WP_REST_Response|WP_Error
     149         */
     150        public function block_editor_update_callback( $request ) {
     151                $columns = Post_Views_Counter()->columns;
     152
     153                if ( ! $columns )
     154                        return new WP_Error( 'pvc-counter-write-failed', __( 'The counter totals could not be saved.', 'post-views-counter' ), [ 'status' => 500 ] );
     155
     156                $views = $request->get_param( 'views' );
     157
     158                // released clients send the Views total as post_views
     159                if ( is_null( $views ) )
     160                        $views = $request->get_param( 'post_views' );
     161
     162                // a blank value never means zero
     163                if ( is_null( $views ) || trim( (string) $views ) === '' )
     164                        return new WP_Error( 'pvc-no-counter-totals', __( 'No counter totals were submitted.', 'post-views-counter' ), [ 'status' => 400 ] );
     165
     166                $post_id = (int) $request->get_param( 'id' );
     167                $state = $columns->apply_editor_counter_totals( $post_id, [ 'post_views' => (string) $views ] );
     168
     169                if ( is_wp_error( $state ) )
     170                        return $state;
     171
     172                return rest_ensure_response(
     173                        [
     174                                'id'    => $post_id,
     175                                'views' => $state['views']
     176                        ]
     177                );
    124178        }
    125179
    … …  
    128182         *
    129183         * @global string $pagenow
    130          * @global string $wp_version
    131184         *
    132185         * @return void
    133186         */
    134187        public function block_editor_enqueue_scripts() {
    135                 global $pagenow, $wp_version;
     188                global $pagenow;
    136189
    137190                // get main instance
    … …  
    143196                        return;
    144197
     198                // enqueue frontend and editor block styles
     199                wp_enqueue_style( 'pvc-block-editor', POST_VIEWS_COUNTER_URL . '/css/block-editor.css', '', $pvc->defaults['version'] );
     200
     201                $id = (int) get_the_ID();
     202
     203                // untracked post types and posts hidden by the display filters get no counter rows
     204                if ( ! $id || ! $pvc->columns || ! $pvc->columns->is_post_counter_visible_in_editor( $id ) )
     205                        return;
     206
     207                $state = $pvc->columns->get_post_editor_counter_state( $id );
     208
    145209                // enqueue the bundled block JS file
    146                 // wp-editor: PluginPostStatusInfo, wp-api-request: wp.apiRequest used on save
    147                 wp_enqueue_script( 'pvc-block-editor', POST_VIEWS_COUNTER_URL . '/js/block-editor.js', [ 'wp-element', 'wp-components', 'wp-editor', 'wp-data', 'wp-plugins', 'wp-api-request' ], $pvc->defaults['version'], false );
    148 
    149                 // restrict editing
    150                 $can_edit = false;
    151                
    152                 $restrict = (bool) $pvc->options['display']['restrict_edit_views'];
    153                
    154                 if ( $restrict === false || ( $restrict === true && current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) ) )
    155                         $can_edit = true;
    156                
    157                 // get total post views
    158                 $id =  get_the_ID();
    159                 $count = pvc_get_post_views( $id );
    160 
    161                 // disable views display and editing?
    162                 if ( apply_filters( 'pvc_admin_display_views', true, $id ) === false ) {
    163                         $count = '—';
    164                         $can_edit = false;
    165                 }
     210                // wp-edit-post: PluginPostStatusInfo, wp-api-fetch: counter totals request after save
     211                wp_enqueue_script( 'pvc-block-editor', POST_VIEWS_COUNTER_URL . '/js/block-editor.js', [ 'wp-element', 'wp-components', 'wp-editor', 'wp-edit-post', 'wp-data', 'wp-plugins', 'wp-api-fetch' ], $pvc->defaults['version'], false );
    166212
    167213                // prepare script data
    168214                $script_data = [
    169                         'postID'                => $id,
    170                         'postViews'             => $count,
    171                         'canEdit'               => $can_edit,
    172                         'nonce'                 => wp_create_nonce( 'wp_rest' ),
    173                         'wpGreater53'   => version_compare( $wp_version, '5.3', '>=' ),
    174                         'textPostViews' => esc_html__( 'Post Views', 'post-views-counter' ),
    175                         'textHelp'              => esc_html__( 'Adjust the views count for this post.', 'post-views-counter' ),
    176                         'textCancel'    => esc_html__( 'Cancel', 'post-views-counter' )
     215                        'postID'                        => $id,
     216                        'views'                         => $state['views'],
     217                        'canEdit'                       => $state['editable'],
     218                        'locale'                        => str_replace( '_', '-', get_user_locale() ),
     219                        'i18n'                          => [
     220                                'views'                         => __( 'Views', 'post-views-counter' ),
     221                                /* translators: 1: metric name, 2: formatted count */
     222                                'valueLabel'            => __( '%1$s: %2$s.', 'post-views-counter' ),
     223                                'help'                          => __( 'Lifetime totals. Changing them does not alter daily, weekly, monthly, or yearly history.', 'post-views-counter' ),
     224                                'cancel'                        => __( 'Cancel', 'post-views-counter' ),
     225                                'saveFailed'            => __( 'Views could not be saved.', 'post-views-counter' )
     226                        ]
    177227                ];
    178228
    179229                wp_add_inline_script( 'pvc-block-editor', 'var pvcEditorArgs = ' . wp_json_encode( $script_data ) . ";\n", 'before' );
    180 
    181                 // enqueue frontend and editor block styles
    182                 wp_enqueue_style( 'pvc-block-editor', POST_VIEWS_COUNTER_URL . '/css/block-editor.css', '', $pvc->defaults['version'] );
    183230        }
    184231}
  • post-views-counter/tags/1.8.0/includes/class-columns-modal.php

    r3537950 r3734102  
    6363                wp_register_script( 'pvc-column-modal', POST_VIEWS_COUNTER_URL . '/js/admin-columns.js', [ 'jquery', 'pvc-chartjs', 'pvc-micromodal' ], $pvc->defaults['version'], true );
    6464
    65                 // localize script
    66                 wp_add_inline_script( 'pvc-admin-columns', 'var pvcColumnModal = ' . wp_json_encode( [
    67                         'ajaxURL'       => admin_url( 'admin-ajax.php' ),
    68                         'nonce'         => wp_create_nonce( 'pvc-column-modal' ),
    69                         'i18n'          => [
    70                                 'loading'               => __( 'Loading...', 'post-views-counter' ),
    71                                 'close'                 => __( 'Close', 'post-views-counter' ),
    72                                 'error'                 => __( 'An error occurred while loading data.', 'post-views-counter' ),
    73                                 'summary'               => __( 'Total views in this period:', 'post-views-counter' ),
    74                                 'view'                  => __( 'view', 'post-views-counter' ),
    75                                 'views'                 => __( 'views', 'post-views-counter' )
     65                $extension_owns_modal = $this->extension_modal_owner_is_loaded();
     66                $current_extension_owner = $extension_owns_modal && $this->extension_modal_owner_replaces_base_later();
     67
     68                // The configuration is created once by the base plugin. A compatible modal
     69                // owner transfers this script data to its controller. Legacy extensions supply
     70                // their own declaration, so the base must not add a second assignment.
     71                if ( ! $extension_owns_modal || $current_extension_owner )
     72                        wp_add_inline_script( 'pvc-admin-columns', 'var pvcColumnModal = ' . wp_json_encode( $this->get_modal_config( $extension_owns_modal ) ) . "\n", 'before' );
     73
     74                // A loaded extension modal owner renders its own compatible footer markup.
     75                // Do not rely on a released extension removing this callback after registration.
     76                if ( ! $extension_owns_modal )
     77                        add_action( 'admin_footer', [ $this, 'render_modal_html' ] );
     78        }
     79
     80        /**
     81         * Build the private configuration shared by the base and extension modal
     82         * controllers.
     83         *
     84         * @param bool $extension_owns_modal Whether a compatible extension footer
     85         *                                   owner is loaded.
     86         * @return array
     87         */
     88        private function get_modal_config( $extension_owns_modal ) {
     89                return [
     90                        'ajaxURL'                       => admin_url( 'admin-ajax.php' ),
     91                        'nonce'                         => wp_create_nonce( 'pvc-column-modal' ),
     92                        'handlerNamespace'      => 'pvcCoreModal',
     93                        'modalOwner'            => $extension_owns_modal ? 'pro' : 'core',
     94                        'i18n'                          => [
     95                                'loading'                               => __( 'Loading...', 'post-views-counter' ),
     96                                'close'                                 => __( 'Close', 'post-views-counter' ),
     97                                'error'                                 => __( 'An error occurred while loading data.', 'post-views-counter' ),
     98                                'summary'                               => __( 'Views in this period:', 'post-views-counter' ),
     99                                'retry'                                 => __( 'Retry', 'post-views-counter' ),
     100                                'view'                                  => __( 'view', 'post-views-counter' ),
     101                                'views'                                 => __( 'views', 'post-views-counter' )
    76102                        ]
    77                 ] ) . "\n", 'before' );
    78 
    79                 // add modal HTML to footer
    80                 add_action( 'admin_footer', [ $this, 'render_modal_html' ] );
     103                ];
     104        }
     105
     106        /**
     107         * Check whether an instantiated extension modal renderer owns this request.
     108         *
     109         * Class presence alone is insufficient: partial extension bootstraps must leave
     110         * the base modal active until an actual renderer callback is registered.
     111         *
     112         * @return bool
     113         */
     114        private function extension_modal_owner_is_loaded() {
     115                global $wp_filter;
     116
     117                if ( ! class_exists( 'Post_Views_Counter_Pro_Columns_Modal' ) || ! isset( $wp_filter['admin_footer'] ) )
     118                        return false;
     119
     120                foreach ( $wp_filter['admin_footer']->callbacks as $callbacks ) {
     121                        foreach ( $callbacks as $callback ) {
     122                                if ( ! is_array( $callback['function'] ) || ! isset( $callback['function'][0], $callback['function'][1] ) )
     123                                        continue;
     124
     125                                if ( $callback['function'][1] === 'render_modal_html' && is_a( $callback['function'][0], 'Post_Views_Counter_Pro_Columns_Modal' ) )
     126                                        return true;
     127                        }
     128                }
     129
     130                return false;
     131        }
     132
     133        /**
     134         * Check whether the loaded extension owner transfers base script data at
     135         * priority 30.
     136         *
     137         * @return bool
     138         */
     139        private function extension_modal_owner_replaces_base_later() {
     140                global $wp_filter;
     141
     142                if ( ! isset( $wp_filter['admin_enqueue_scripts']->callbacks[30] ) )
     143                        return false;
     144
     145                foreach ( $wp_filter['admin_enqueue_scripts']->callbacks[30] as $callback ) {
     146                        if ( is_array( $callback['function'] ) && isset( $callback['function'][0], $callback['function'][1] ) && $callback['function'][1] === 'remove_base_modal' && is_a( $callback['function'][0], 'Post_Views_Counter_Pro_Columns_Modal' ) )
     147                                return true;
     148                }
     149
     150                return false;
    81151        }
    82152
    … …  
    115185                        wp_send_json_error( [ 'message' => __( 'Post type is not tracked.', 'post-views-counter' ) ] );
    116186
     187                // the same rule as the chart link in the list column
     188                if ( ! $pvc->columns || ! $pvc->columns->current_user_can_view_post_chart( $post ) )
     189                        wp_send_json_error( [ 'message' => __( 'Access denied for this post.', 'post-views-counter' ) ] );
     190
    117191                // check display permission for this specific post
    118192                if ( apply_filters( 'pvc_admin_display_post_views', true, $post_id ) === false )
    … …  
    120194
    121195                // get period (format: YYYYMM or empty for current month)
    122                 $period_str = isset( $_POST['period'] ) && ! empty( $_POST['period'] ) ? preg_replace( '/[^0-9]/', '', $_POST['period'] ) : '';
     196                if ( isset( $_POST['period'] ) && ! is_scalar( $_POST['period'] ) )
     197                        wp_send_json_error( [ 'message' => __( 'Invalid period.', 'post-views-counter' ) ] );
     198
     199                $period_str = isset( $_POST['period'] ) && ! empty( $_POST['period'] ) ? preg_replace( '/[^0-9]/', '', wp_unslash( (string) $_POST['period'] ) ) : '';
    123200
    124201                // parse period or use current
    125                 if ( $period_str && strlen( $period_str ) === 6 ) {
    126                         $year = substr( $period_str, 0, 4 );
    127                         $month = substr( $period_str, 4, 2 );
    128                         $date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
    129                        
    130                         if ( ! $date )
    131                                 $date = new DateTime( 'now', wp_timezone() );
    132                 } else {
    133                         $date = new DateTime( 'now', wp_timezone() );
    134                 }
     202                $date = $this->get_requested_month( $period_str, $this->get_counting_now() );
    135203
    136204                $year = $date->format( 'Y' );
    … …  
    138206                $last_day = $date->format( 't' );
    139207
    140                 // fetch views data
     208                wp_send_json_success( $this->get_paired_column_chart_data( $post, (int) $year, (int) $month, (int) $last_day ) );
     209        }
     210
     211        /**
     212         * Build the Views modal response with the shared metric dataset style.
     213         *
     214         * The released total_views and period_has_data keys are kept for any
     215         * consumer of the legacy payload.
     216         *
     217         * @param WP_Post $post Post object.
     218         * @param int     $year Requested year.
     219         * @param int     $month Requested month.
     220         * @param int     $last_day Days in the requested month.
     221         * @return array
     222         */
     223        private function get_paired_column_chart_data( $post, $year, $month, $last_day ) {
     224                $pvc = Post_Views_Counter();
    141225                $views = pvc_get_views( [
    142                         'post_id'               => $post_id,
    143                         'post_type'             => $post->post_type,
    144                         'fields'                => 'date=>views',
    145                         'views_query'   => [
    146                                 'year'  => (int) $year,
    147                                 'month' => (int) $month
    148                         ]
     226                        'post_id' => $post->ID,
     227                        'post_type' => $post->post_type,
     228                        'fields' => 'date=>views',
     229                        'views_query' => [ 'year' => $year, 'month' => $month ]
    149230                ] );
    150 
    151                 // get colors
    152                 $colors = $pvc->functions->get_colors();
    153 
    154                 // prepare response data
    155231                $data = [
    156                         'post_id'       => $post_id,
    157                         'post_title'=> get_the_title( $post_id ),
    158                         'period'        => $year . $month,
    159                         'design'                => [
    160                                 'fill'                                  => true,
    161                                 'backgroundColor'               => 'rgba(' . $colors['r'] . ',' . $colors['g'] . ',' . $colors['b'] . ', 0.2)',
    162                                 'borderColor'                   => 'rgba(' . $colors['r'] . ',' . $colors['g'] . ',' . $colors['b'] . ', 1)',
    163                                 'borderWidth'                   => 1.2,
    164                                 'borderDash'                    => [],
    165                                 'pointBorderColor'              => 'rgba(' . $colors['r'] . ',' . $colors['g'] . ',' . $colors['b'] . ', 1)',
    166                                 'pointBackgroundColor'  => 'rgba(255, 255, 255, 1)',
    167                                 'pointBorderWidth'              => 1.2
     232                        'post_id' => (int) $post->ID,
     233                        'post_title' => get_the_title( $post->ID ),
     234                        'period' => sprintf( '%04d%02d', $year, $month ),
     235                        'dates_html' => $this->generate_modal_dates( $year, $month ),
     236                        'data' => [
     237                                'labels' => [],
     238                                'dates' => [],
     239                                'datasets' => [
     240                                        array_merge( [
     241                                                'metric' => 'views',
     242                                                'label' => __( 'Views', 'post-views-counter' ),
     243                                                'data' => []
     244                                        ], $pvc->functions->get_metric_dataset_style( 'views', 'line', [ 'fill' => true ] ) )
     245                                ]
    168246                        ],
    169                         'data'          => [
    170                                 'labels'        => [],
    171                                 'dates'         => [],
    172                                 'datasets'      => [
    173                                         [
    174                                                 'label' => get_the_title( $post_id ),
    175                                                 'data'  => []
    176                                         ]
    177                                 ]
    178                         ]
     247                        'totals' => [ 'views' => 0 ]
    179248                ];
    180249
    181                 // generate dates and data
    182                 for ( $i = 1; $i <= $last_day; $i++ ) {
    183                         $date_key = $year . $month . str_pad( $i, 2, '0', STR_PAD_LEFT );
    184                        
    185                         // labels: show only odd days
    186                         $data['data']['labels'][] = ( $i % 2 === 0 ? '' : $i );
    187                        
    188                         // formatted dates for tooltips
    189                         $data['data']['dates'][] = date_i18n( get_option( 'date_format' ), strtotime( $year . '-' . $month . '-' . str_pad( $i, 2, '0', STR_PAD_LEFT ) ) );
    190                        
    191                         // view count
     250                $timezone = wp_timezone();
     251
     252                for ( $day = 1; $day <= $last_day; $day++ ) {
     253                        $date_key = sprintf( '%04d%02d%02d', $year, $month, $day );
     254                        $timestamp = ( new DateTimeImmutable( sprintf( '%04d-%02d-%02d 00:00:00', $year, $month, $day ), $timezone ) )->getTimestamp();
     255
     256                        $data['data']['labels'][] = $day % 2 === 0 ? '' : $day;
     257                        $data['data']['dates'][] = wp_date( get_option( 'date_format' ), $timestamp, $timezone );
    192258                        $data['data']['datasets'][0]['data'][] = isset( $views[$date_key] ) ? (int) $views[$date_key] : 0;
    193259                }
    194260
    195                 // calculate total views for the period
    196                 $data['total_views'] = array_sum( $data['data']['datasets'][0]['data'] );
    197 
    198                 // check if there is any period-specific data
    199                 $period_has_data = false;
    200                 foreach ( $data['data']['datasets'][0]['data'] as $val ) {
    201                         if ( (int) $val > 0 ) {
    202                                 $period_has_data = true;
    203                                 break;
    204                         }
    205                 }
    206 
    207                 $data['period_has_data'] = $period_has_data;
    208 
    209                 // generate date navigation HTML
    210                 $data['dates_html'] = $this->generate_modal_dates( (int) $year, (int) $month );
    211 
    212                 wp_send_json_success( $data );
     261                $data['totals']['views'] = array_sum( $data['data']['datasets'][0]['data'] );
     262                $data['total_views'] = $data['totals']['views'];
     263                $data['period_has_data'] = $data['totals']['views'] > 0;
     264                $data['data'] = Post_Views_Counter()->functions->format_chart_datasets( $data['data'] );
     265
     266                return $data;
    213267        }
    214268
    … …  
    220274         * @return string
    221275         */
    222         private function generate_modal_dates( $year, $month ) {
    223                 // previous month
    224                 $prev_date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
    225                 $prev_date->modify( '-1 month' );
    226                
    227                 // next month
    228                 $next_date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
    229                 $next_date->modify( '+1 month' );
    230                
    231                 // current
    232                 $current_date = DateTime::createFromFormat( 'Y-m', $year . '-' . $month, wp_timezone() );
    233                
    234                 // check if next is in the future
    235                 $now = new DateTime( 'now', wp_timezone() );
    236                 $can_go_next = $next_date <= $now;
    237                
     276        private function generate_modal_dates( $year, $month, $now = null ) {
     277                $timezone = wp_timezone();
     278
     279                if ( ! ( $now instanceof DateTimeInterface ) )
     280                        $now = $this->get_counting_now();
     281
     282                $current_date = $this->parse_month( sprintf( '%04d-%02d', $year, $month ) );
     283
     284                if ( ! $current_date )
     285                        $current_date = $this->parse_month( $now->format( 'Y-m' ) );
     286
     287                $prev_date = $current_date->modify( '-1 month' );
     288                $next_date = $current_date->modify( '+1 month' );
     289
     290                // next is available once its month has started in the counting clock
     291                $can_go_next = (int) $next_date->format( 'Ym' ) <= (int) $now->format( 'Ym' );
     292
    238293                $html = '<div class="pvc-modal-nav">';
    239                 $html .= '<a href="#" class="pvc-modal-nav-prev" data-period="' . $prev_date->format( 'Ym' ) . '">‹ ' . date_i18n( 'F Y', $prev_date->getTimestamp() ) . '</a>';
    240                 $html .= '<span class="pvc-modal-nav-current">' . date_i18n( 'F Y', $current_date->getTimestamp() ) . '</span>';
     294                $html .= '<a href="#" class="pvc-modal-nav-prev" data-period="' . $prev_date->format( 'Ym' ) . '">‹ ' . wp_date( 'F Y', $prev_date->getTimestamp(), $timezone ) . '</a>';
     295                $html .= '<span class="pvc-modal-nav-current">' . wp_date( 'F Y', $current_date->getTimestamp(), $timezone ) . '</span>';
    241296               
    242297                if ( $can_go_next )
    243                         $html .= '<a href="#" class="pvc-modal-nav-next" data-period="' . $next_date->format( 'Ym' ) . '">' . date_i18n( 'F Y', $next_date->getTimestamp() ) . ' ›</a>';
     298                        $html .= '<a href="#" class="pvc-modal-nav-next" data-period="' . $next_date->format( 'Ym' ) . '">' . wp_date( 'F Y', $next_date->getTimestamp(), $timezone ) . ' ›</a>';
    244299                else
    245                         $html .= '<span class="pvc-modal-nav-next pvc-disabled">' . date_i18n( 'F Y', $next_date->getTimestamp() ) . ' ›</span>';
     300                        $html .= '<span class="pvc-modal-nav-next pvc-disabled">' . wp_date( 'F Y', $next_date->getTimestamp(), $timezone ) . ' ›</span>';
    246301               
    247302                $html .= '</div>';
    248303               
    249304                return $html;
     305        }
     306
     307        /**
     308         * Get the current time in the clock that buckets the stored periods.
     309         *
     310         * @return DateTimeImmutable
     311         */
     312        private function get_counting_now() {
     313                return new DateTimeImmutable( 'now', Post_Views_Counter_Visits_Query::get_timezone() );
     314        }
     315
     316        /**
     317         * Get the first day of the requested month, or of the current month in the
     318         * counting clock when none (or an invalid one) is requested.
     319         *
     320         * @param string            $period_str Requested period in YYYYMM format, or empty.
     321         * @param DateTimeInterface $now Current time in the counting clock.
     322         * @return DateTimeImmutable
     323         */
     324        private function get_requested_month( $period_str, $now ) {
     325                $date = false;
     326
     327                if ( $period_str && strlen( $period_str ) === 6 )
     328                        $date = $this->parse_month( substr( $period_str, 0, 4 ) . '-' . substr( $period_str, 4, 2 ) );
     329
     330                return $date ? $date : $this->parse_month( $now->format( 'Y-m' ) );
     331        }
     332
     333        /**
     334         * Parse one exact calendar month without inheriting the current day.
     335         *
     336         * @param string $value Month in Y-m format.
     337         * @return DateTimeImmutable|false
     338         */
     339        private function parse_month( $value ) {
     340                $date = DateTimeImmutable::createFromFormat( '!Y-m', $value, wp_timezone() );
     341                $errors = DateTimeImmutable::getLastErrors();
     342
     343                if ( ! $date || ( is_array( $errors ) && ( ! empty( $errors['warning_count'] ) || ! empty( $errors['error_count'] ) ) ) || $date->format( 'Y-m' ) !== $value )
     344                        return false;
     345
     346                return $date;
    250347        }
    251348
    … …  
    267364                                                <div class="pvc-modal-content-top">
    268365                                                        <div class="pvc-modal-summary">
    269                                                                 <span class="pvc-modal-views-label"></span>
     366                                                                <span class="pvc-modal-views-label"><?php esc_html_e( 'Views in this period:', 'post-views-counter' ); ?></span>
    270367                                                                <span class="pvc-modal-views-data">
    271                                                                         <span class="pvc-modal-count"></span>
     368                                                                        <span class="pvc-modal-count pvc-modal-count-views"></span>
    272369                                                                </span>
    273370                                                        </div>
    274                             <div class="pvc-modal-tabs" role="tablist">
    275                                                                 <button type="button" class="pvc-modal-tab pvc-pro" disabled><span><?php _e( 'Year', 'post-views-counter' ); ?></span></button>
    276                                                                 <button type="button" class="pvc-modal-tab active"><span><?php _e( 'Month', 'post-views-counter' ); ?></span></button>
    277                                                                 <button type="button" class="pvc-modal-tab pvc-pro" disabled><span><?php _e( 'Week', 'post-views-counter' ); ?></span></button>
     371                                    <div class="pvc-modal-tabs" role="group" aria-label="<?php esc_attr_e( 'Chart period', 'post-views-counter' ); ?>">
     372                                                                <button type="button" class="pvc-modal-tab pvc-pro" disabled aria-pressed="false"><span><?php _e( 'Year', 'post-views-counter' ); ?></span></button>
     373                                                                <button type="button" class="pvc-modal-tab active" aria-pressed="true"><span><?php _e( 'Month', 'post-views-counter' ); ?></span></button>
     374                                                                <button type="button" class="pvc-modal-tab pvc-pro" disabled aria-pressed="false"><span><?php _e( 'Week', 'post-views-counter' ); ?></span></button>
    278375                                                        </div>
    279376                                                </div>
    280                                                 <div class="pvc-modal-chart-container">
    281                                                     <canvas id="pvc-modal-chart" height="200"></canvas>
    282                                                         <span class="spinner"></span>
    283                                                 </div>
     377                                                        <div id="pvc-modal-chart-panel" class="pvc-modal-chart-container">
     378                                                            <canvas id="pvc-modal-chart" height="200"></canvas>
     379                                                                <span class="spinner"></span>
     380                                                        </div>
     381                                                        <div class="pvc-modal-status" role="status" aria-live="polite"></div>
    284382                        <div class="pvc-modal-content-middle" style="display: none;">
    285383                                                        <div class="pvc-modal-insights">
  • post-views-counter/tags/1.8.0/includes/class-columns.php

    r3578455 r3734102  
    1010 */
    1111class Post_Views_Counter_Columns {
     12
     13        private $bulk_counter_result = null;
    1214
    1315        /**
    … …  
    2527                add_action( 'bulk_edit_custom_box', [ $this, 'quick_edit_custom_box' ], 10, 2 );
    2628                add_action( 'quick_edit_custom_box', [ $this, 'quick_edit_custom_box' ], 10, 2 );
     29                // Retained for compatibility with any external consumer. The shipped UI
     30                // no longer calls it; native Bulk Edit owns current counter writes.
    2731                add_action( 'wp_ajax_save_bulk_post_views', [ $this, 'save_bulk_post_views' ] );
    28         }
    29 
    30         /**
    31          * Sanitize manual post views input.
     32                add_action( 'bulk_edit_posts', [ $this, 'bulk_edit_post_counters' ], 10, 2 );
     33                add_action( 'admin_notices', [ $this, 'bulk_counter_admin_notice' ] );
     34                add_filter( 'removable_query_args', [ $this, 'add_removable_query_args' ] );
     35                add_action( 'pre_get_posts', [ $this, 'prepare_admin_list_sort' ], 0 );
     36        }
     37
     38        /**
     39         * Sanitize one absolute counter total.
    3240         *
    3341         * @param mixed $value
    3442         * @return int|null
    3543         */
    36         private function sanitize_post_views_input( $value ) {
     44        private function sanitize_counter_total_input( $value ) {
    3745                if ( ! is_scalar( $value ) )
    3846                        return null;
    … …  
    4048                $value = trim( wp_unslash( (string) $value ) );
    4149
    42                 if ( $value === '' || ! is_numeric( $value ) )
     50                if ( $value === '' || ! preg_match( '/^-?\d+$/', $value ) )
    4351                        return null;
    4452
    … …  
    4654
    4755                return $count < 0 ? 0 : $count;
     56        }
     57
     58        /**
     59         * Collect the nonblank Views total submitted by an editor.
     60         *
     61         * @param array|null $source Submitted fields. Defaults to the POST body.
     62         * @return array|null Null indicates malformed input.
     63         */
     64        private function get_submitted_counter_totals( $source = null ) {
     65                if ( is_null( $source ) )
     66                        $source = $_POST;
     67
     68                if ( ! is_array( $source ) )
     69                        return null;
     70
     71                if ( ! array_key_exists( 'post_views', $source ) )
     72                        return [];
     73
     74                if ( ! is_scalar( $source['post_views'] ) )
     75                        return null;
     76
     77                if ( trim( wp_unslash( (string) $source['post_views'] ) ) === '' )
     78                        return [];
     79
     80                $value = $this->sanitize_counter_total_input( $source['post_views'] );
     81
     82                if ( is_null( $value ) )
     83                        return null;
     84
     85                // A value equal to the one the form was loaded with was not edited.
     86                // Writing it back would roll back Views counted since then.
     87                if ( array_key_exists( 'current_post_views', $source ) && $this->sanitize_counter_total_input( $source['current_post_views'] ) === $value )
     88                        return [];
     89
     90                return [ 'views' => $value ];
     91        }
     92
     93        /**
     94         * Check whether a post's counter totals appear on its edit screen.
     95         *
     96         * The edit screen is a separate feature from the list column, so the
     97         * column toggle does not apply here.
     98         *
     99         * @internal Shared by the Classic Editor and the block editor.
     100         *
     101         * @param int $post_id Post ID.
     102         * @return bool
     103         */
     104        public function is_post_counter_visible_in_editor( $post_id ) {
     105                $post = get_post( (int) $post_id );
     106
     107                if ( ! $post )
     108                        return false;
     109
     110                return in_array( $post->post_type, (array) Post_Views_Counter()->options['general']['post_types_count'], true )
     111                        && apply_filters( 'pvc_admin_display_post_views', true, $post->ID ) !== false
     112                        // The block editor has always honoured this filter name.
     113                        && apply_filters( 'pvc_admin_display_views', true, $post->ID ) !== false;
     114        }
     115
     116        /**
     117         * Check whether the current user may edit a post's counter totals on its
     118         * edit screen.
     119         *
     120         * @internal Shared by the Classic Editor and the block editor.
     121         *
     122         * @param int $post_id Post ID.
     123         * @return bool
     124         */
     125        public function can_edit_post_counter_in_editor( $post_id ) {
     126                $pvc = Post_Views_Counter();
     127
     128                return $this->is_post_counter_visible_in_editor( $post_id )
     129                        && current_user_can( 'edit_post', (int) $post_id )
     130                        && ! empty( $pvc->options['display']['restrict_edit_views'] )
     131                        && current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) );
     132        }
     133
     134        /**
     135         * Get the lifetime Views total shown on a post's edit screen.
     136         *
     137         * @internal Shared by the Classic Editor and the block editor.
     138         *
     139         * @param int $post_id Post ID.
     140         * @return array
     141         */
     142        public function get_post_editor_counter_state( $post_id ) {
     143                $post_id = (int) $post_id;
     144
     145                return [
     146                        'views'         => (int) pvc_get_post_views( $post_id ),
     147                        'editable'      => $this->can_edit_post_counter_in_editor( $post_id )
     148                ];
     149        }
     150
     151        /**
     152         * Apply counter totals submitted by the block editor.
     153         *
     154         * @internal Used only by the block editor REST route.
     155         *
     156         * @param int   $post_id Post ID.
     157         * @param array $source Submitted fields keyed post_views.
     158         * @return array|WP_Error Committed totals, the reason nothing was written, or
     159         *                        pvc-counter-read-failed when the committed total
     160         *                        cannot be read back.
     161         */
     162        public function apply_editor_counter_totals( $post_id, $source ) {
     163                $post_id = (int) $post_id;
     164
     165                if ( ! $this->is_post_counter_visible_in_editor( $post_id ) )
     166                        return new WP_Error( 'pvc-invalid-post', __( 'Invalid post ID.', 'post-views-counter' ), [ 'status' => 404 ] );
     167
     168                if ( ! $this->can_edit_post_counter_in_editor( $post_id ) )
     169                        return new WP_Error( 'pvc-user-not-allowed', __( 'You are not allowed to edit this item.', 'post-views-counter' ), [ 'status' => rest_authorization_required_code() ] );
     170
     171                $updates = $this->get_submitted_counter_totals( $source );
     172
     173                if ( is_null( $updates ) )
     174                        return new WP_Error( 'pvc-invalid-counter-totals', __( 'Invalid counter totals.', 'post-views-counter' ), [ 'status' => 400 ] );
     175
     176                if ( ! $this->update_post_counter_totals( $post_id, $updates ) )
     177                        return new WP_Error( 'pvc-counter-write-failed', __( 'The counter totals could not be saved.', 'post-views-counter' ), [ 'status' => 500 ] );
     178
     179                $state = $this->get_post_editor_counter_state( $post_id );
     180
     181                // pvc_get_post_views() may still hold the total it cached before the write
     182                $views = $this->get_stored_post_views_total( $post_id );
     183
     184                if ( $views === null )
     185                        return new WP_Error( 'pvc-counter-read-failed', __( 'The saved counter totals could not be read back.', 'post-views-counter' ), [ 'status' => 500 ] );
     186
     187                $state['views'] = $views;
     188
     189                return $state;
     190        }
     191
     192        /**
     193         * Read one post's committed lifetime Views total, bypassing read caches.
     194         *
     195         * @param int $post_id Post ID.
     196         * @return int|null The total (0 when no row exists), or null when the read failed.
     197         */
     198        private function get_stored_post_views_total( $post_id ) {
     199                global $wpdb;
     200
     201                $where = ' WHERE `id` = %d AND `type` = %d AND `period` = %s';
     202                $args = [ (int) $post_id, 4, 'total' ];
     203
     204                if ( pvc_post_views_has_content_column() ) {
     205                        $where .= ' AND `content` = %d';
     206                        $args[] = 0;
     207                }
     208
     209                $wpdb->last_error = '';
     210                $count = $wpdb->get_var( $wpdb->prepare( 'SELECT `count` FROM `' . $wpdb->prefix . 'post_views`' . $where, $args ) );
     211
     212                // a failed read must never pass for a stored zero
     213                return $wpdb->last_error === '' ? (int) $count : null;
     214        }
     215
     216        /**
     217         * Check whether the current request is one exact WordPress AJAX action.
     218         *
     219         * @param string $action AJAX action.
     220         * @return bool
     221         */
     222        private function is_ajax_action( $action ) {
     223                $is_ajax = function_exists( 'wp_doing_ajax' ) ? wp_doing_ajax() : defined( 'DOING_AJAX' ) && DOING_AJAX;
     224
     225                return $is_ajax
     226                        && isset( $_REQUEST['action'] )
     227                        && is_scalar( $_REQUEST['action'] )
     228                        && sanitize_key( wp_unslash( (string) $_REQUEST['action'] ) ) === $action;
     229        }
     230
     231        /**
     232         * Check whether WordPress is saving one Quick Edit row.
     233         *
     234         * @return bool
     235         */
     236        private function is_quick_edit_request() {
     237                return $this->is_ajax_action( 'inline-save' ) && isset( $_POST['_inline_edit'] );
     238        }
     239
     240        /**
     241         * Check whether the request submitted this post's edit form. The Classic
     242         * Editor, the attachment editor and Quick Edit all send the edited post as
     243         * post_ID.
     244         *
     245         * @param int $post_id Post ID being saved.
     246         * @return bool
     247         */
     248        private function is_submitted_post( $post_id ) {
     249                if ( ! isset( $_POST['post_ID'] ) || ! is_scalar( $_POST['post_ID'] ) )
     250                        return false;
     251
     252                $submitted = trim( wp_unslash( (string) $_POST['post_ID'] ) );
     253
     254                return preg_match( '/^[1-9]\d*$/', $submitted ) === 1 && (int) $submitted === (int) $post_id;
     255        }
     256
     257        /**
     258         * Check whether the ordinary save_post callback is running inside native
     259         * WordPress Bulk Edit. The bulk_edit_posts completion hook owns current
     260         * native Bulk Edit counter writes there.
     261         *
     262         * @return bool
     263         */
     264        private function is_native_bulk_edit_request() {
     265                return isset( $_REQUEST['bulk_edit'] );
     266        }
     267
     268        /**
     269         * Check all server-side conditions for one post total adjustment.
     270         *
     271         * @param int $post_id Post ID.
     272         * @return bool
     273         */
     274        private function can_update_post_counter_totals( $post_id ) {
     275                $pvc = Post_Views_Counter();
     276                $post = get_post( $post_id );
     277
     278                return $post
     279                        && ! empty( $pvc->options['display']['post_views_column'] )
     280                        && in_array( $post->post_type, (array) $pvc->options['general']['post_types_count'], true )
     281                        && apply_filters( 'pvc_admin_display_post_views', true, $post_id ) !== false
     282                        && current_user_can( 'edit_post', $post_id )
     283                        && ! empty( $pvc->options['display']['restrict_edit_views'] )
     284                        && current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) );
     285        }
     286
     287        /**
     288         * Retire the cached lifetime Views total of one post after a manual write.
     289         *
     290         * pvc_get_post_views() and shared cache priming use the query's MD5 key, so
     291         * only the single-post total query needs to be rebuilt. The content condition
     292         * is included when the shared table has a content dimension.
     293         *
     294         * @param int $post_id Post ID.
     295         * @return void
     296         */
     297        private function retire_post_views_cache( $post_id ) {
     298                global $wpdb;
     299
     300                $query = "SELECT SUM(count) AS views FROM " . $wpdb->prefix . "post_views WHERE id IN (%d) AND type = %d AND period = %s";
     301
     302                wp_cache_delete( md5( $wpdb->prepare( $query, (int) $post_id, 4, 'total' ) ), 'pvc-get_post_views' );
     303                wp_cache_delete( md5( $wpdb->prepare( $query . ' AND content = %d', (int) $post_id, 4, 'total', 0 ) ), 'pvc-get_post_views' );
     304        }
     305
     306        /**
     307         * Write one post's lifetime Views total without altering historical buckets.
     308         *
     309         * @param int   $post_id Post ID.
     310         * @param array $updates Absolute totals; only views is written.
     311         * @return bool
     312         */
     313        private function update_post_counter_totals( $post_id, $updates ) {
     314                if ( ! isset( $updates['views'] ) )
     315                        return true;
     316
     317                // Compare with the committed total, never a cached read: a stale cached
     318                // total equal to the submitted value would skip a real edit.
     319                $current = $this->get_stored_post_views_total( $post_id );
     320
     321                if ( $current === null )
     322                        return false;
     323
     324                // The submitted Views value is already a validated, normalized
     325                // non-negative integer. Pass it directly so pvc_update_post_views()
     326                // remains the single owner of the pvc_update_post_views_count filter
     327                // on this path.
     328                $views = (int) $updates['views'];
     329
     330                if ( $current === $views )
     331                        return true;
     332
     333                global $wpdb;
     334                $wpdb->last_error = '';
     335
     336                if ( pvc_update_post_views( $post_id, $views ) === false || $wpdb->last_error !== '' )
     337                        return false;
     338
     339                $this->retire_post_views_cache( $post_id );
     340                do_action( 'pvc_after_update_post_views_count', $post_id );
     341
     342                return true;
    48343        }
    49344
    … …  
    58353                global $post;
    59354
    60                 // get main instance
    61                 $pvc = Post_Views_Counter();
    62                 $post_types = (array) $pvc->options['general']['post_types_count'];
    63 
    64                 // break if display is not allowed
    65                 if ( ! $pvc->options['display']['post_views_column'] || ! in_array( $post->post_type, $post_types, true ) )
    66                         return;
    67 
    68                 // check if user can see post stats
    69                 if ( apply_filters( 'pvc_admin_display_post_views', true, $post->ID ) === false )
    70                         return;
    71 
    72                 // get total post views
    73                 $count = (int) pvc_get_post_views( $post->ID ); ?>
     355                if ( ! $post || ! $this->is_post_counter_visible_in_editor( $post->ID ) )
     356                        return;
     357
     358                $state = $this->get_post_editor_counter_state( $post->ID ); ?>
    74359
    75360                <div class="misc-pub-section" id="post-views">
    76361
    77                         <?php wp_nonce_field( 'post_views_count', 'pvc_nonce' ); ?>
    78 
    79                         <span id="post-views-display">
    80                                 <?php echo __( 'Post Views', 'post-views-counter' ) . ': <b>' . number_format_i18n( $count ) . '</b>'; ?>
     362                        <span id="post-views-display" class="pvc-editor-counter-line">
     363                                <?php echo esc_html__( 'Views', 'post-views-counter' ) . ': <b>' . esc_html( number_format_i18n( $state['views'] ) ) . '</b>'; ?>
    81364                        </span>
    82365
    83366                        <?php
    84                         // allow editing
    85                         $allow_edit = (bool) $pvc->options['display']['restrict_edit_views'];
    86 
    87                         // allow editing condition
    88                         $allow_edit_condition = (bool) current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) );
    89 
    90                         if ( $allow_edit === true && $allow_edit_condition === true ) {
     367                        if ( $state['editable'] ) {
     368                                wp_nonce_field( 'post_views_count', 'pvc_nonce' );
    91369                                ?>
    92                                 <a href="#post-views" class="edit-post-views hide-if-no-js"><?php _e( 'Edit', 'post-views-counter' ); ?></a>
     370                                <a href="#post-views" class="edit-post-views hide-if-no-js" role="button"><?php esc_html_e( 'Edit', 'post-views-counter' ); ?><span class="screen-reader-text"> <?php esc_html_e( 'Views', 'post-views-counter' ); ?></span></a>
    93371
    94372                                <div id="post-views-input-container" class="hide-if-js">
    95373
    96                                         <p><?php _e( 'Adjust the views count for this post.', 'post-views-counter' ); ?></p>
    97                                         <input type="hidden" name="current_post_views" id="post-views-current" value="<?php echo esc_attr( $count ); ?>" />
    98                                         <input type="text" name="post_views" id="post-views-input" value="<?php echo esc_attr( $count ); ?>"/><br />
     374                                        <p class="pvc-editor-counter-field">
     375                                                <label for="post-views-input"><?php esc_html_e( 'Views', 'post-views-counter' ); ?></label>
     376                                                <input type="hidden" name="current_post_views" id="post-views-current" value="<?php echo esc_attr( $state['views'] ); ?>" />
     377                                                <input type="number" min="0" step="1" inputmode="numeric" name="post_views" id="post-views-input" value="<?php echo esc_attr( $state['views'] ); ?>" />
     378                                        </p>
     379
     380                                        <p class="description"><?php esc_html_e( 'Lifetime totals. Changing them does not alter daily, weekly, monthly, or yearly history.', 'post-views-counter' ); ?></p>
     381
    99382                                        <p>
    100                                                 <a href="#post-views" class="save-post-views hide-if-no-js button"><?php _e( 'OK', 'post-views-counter' ); ?></a>
    101                                                 <a href="#post-views" class="cancel-post-views hide-if-no-js"><?php _e( 'Cancel', 'post-views-counter' ); ?></a>
     383                                                <a href="#post-views" class="save-post-views hide-if-no-js button"><?php esc_html_e( 'OK', 'post-views-counter' ); ?></a>
     384                                                <a href="#post-views" class="cancel-post-views hide-if-no-js button-cancel"><?php esc_html_e( 'Cancel', 'post-views-counter' ); ?></a>
    102385                                        </p>
    103386
    … …  
    123406                        return;
    124407
     408                // The bulk_edit_posts completion hook is the sole counter writer for
     409                // native Bulk Edit, so each updated post gets exactly one write attempt.
     410                if ( $this->is_native_bulk_edit_request() )
     411                        return;
     412
    125413                // break if current user can't edit this post
    126414                if ( ! current_user_can( 'edit_post', $post_id ) )
    127415                        return;
    128416
    129                 // is post views set
     417                // is the views total set
    130418                if ( ! isset( $_POST['post_views'] ) )
    131                         return;
    132 
    133                 $post_views = $this->sanitize_post_views_input( $_POST['post_views'] );
    134 
    135                 if ( is_null( $post_views ) )
    136419                        return;
    137420
    … …  
    163446
    164447                // validate data
    165                 if ( ! isset( $_POST['pvc_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['pvc_nonce'] ) ), 'post_views_count' ) )
    166                         return;
    167 
    168                 $current_post_views = null;
    169 
    170                 if ( isset( $_POST['current_post_views'] ) )
    171                         $current_post_views = $this->sanitize_post_views_input( $_POST['current_post_views'] );
    172 
    173                 // unchanged post views value?
    174                 if ( ! is_null( $current_post_views ) && $post_views === $current_post_views )
    175                         return;
    176 
    177                 // update post views
    178                 pvc_update_post_views( $post_id, $post_views );
    179 
    180                 do_action( 'pvc_after_update_post_views_count', $post_id );
     448                if ( ! isset( $_POST['pvc_nonce'] ) || ! is_scalar( $_POST['pvc_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( (string) $_POST['pvc_nonce'] ) ), 'post_views_count' ) )
     449                        return;
     450
     451                // The submitted total belongs to the post the form was rendered for.
     452                // Another post saved during the same request never receives it.
     453                if ( ! $this->is_submitted_post( $post_id ) )
     454                        return;
     455
     456                $updates = $this->get_submitted_counter_totals();
     457
     458                if ( is_null( $updates ) || empty( $updates ) )
     459                        return;
     460
     461                // Quick Edit belongs to the list column; the edit screen does not.
     462                if ( $this->is_quick_edit_request() ? ! $this->can_update_post_counter_totals( $post_id ) : ! $this->can_edit_post_counter_in_editor( $post_id ) )
     463                        return;
     464
     465                $this->update_post_counter_totals( $post_id, $updates );
    181466        }
    182467
    … …  
    231516         * Register sortable post views column.
    232517         *
     518         * The column sorts natively, descending first.
     519         *
    233520         * @param array $columns
    234521         * @return array
    … …  
    236523        public function register_sortable_custom_column( $columns ) {
    237524                global $post_type;
    238                
     525
    239526                // get main instance
    240527                $pvc = Post_Views_Counter();
    … …  
    250537
    251538                // add new sortable column
    252                 $columns['post_views'] = 'post_views';
     539                $columns['post_views'] = [ 'post_views', true ];
    253540
    254541                return $columns;
     542        }
     543
     544        /**
     545         * Prepare a native Views sort on a managed admin post list: keep posts
     546         * without a stored total listed and add the deterministic ID tie-break.
     547         *
     548         * @param WP_Query $query Query object.
     549         * @return void
     550         */
     551        public function prepare_admin_list_sort( $query ) {
     552                if ( ! $this->is_admin_post_list_query( $query ) )
     553                        return;
     554
     555                $orderby = $query->get( 'orderby' );
     556
     557                if ( ! is_string( $orderby ) || sanitize_key( $orderby ) !== 'post_views' )
     558                        return;
     559
     560                $order = $query->get( 'order' );
     561                $order = is_scalar( $order ) ? strtoupper( trim( (string) $order ) ) : '';
     562                $order = $order === 'ASC' ? 'ASC' : 'DESC';
     563                $metric_query = [ 'period' => 'total', 'hide_empty' => false ];
     564
     565                $query->set( 'orderby', 'post_views' );
     566                $query->set( 'order', $order );
     567                $query->set( 'views_query', $metric_query );
     568                $query->query['orderby'] = 'post_views';
     569                $query->query['order'] = $order;
     570                $query->query['views_query'] = $metric_query;
     571
     572                // Posts with equal totals keep a stable order across pages.
     573                $query->pvc_admin_column_sort = 'views';
     574        }
     575
     576        /**
     577         * Check whether the current query is the enabled Core post-list query.
     578         *
     579         * @param mixed $query Query object.
     580         * @return bool
     581         */
     582        private function is_admin_post_list_query( $query ) {
     583                global $pagenow;
     584
     585                if ( ! is_admin() || ! ( $query instanceof WP_Query ) || ! $query->is_main_query() || ! in_array( $pagenow, [ 'edit.php', 'upload.php' ], true ) )
     586                        return false;
     587
     588                $pvc = Post_Views_Counter();
     589
     590                if ( empty( $pvc->options['display']['post_views_column'] ) || apply_filters( 'pvc_admin_display_post_views', true ) === false )
     591                        return false;
     592
     593                $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
     594                $post_type = $query->get( 'post_type' );
     595
     596                if ( ! is_string( $post_type ) || $post_type === '' )
     597                        $post_type = $screen && ! empty( $screen->post_type ) ? $screen->post_type : ( $pagenow === 'upload.php' ? 'attachment' : 'post' );
     598
     599                return in_array( $post_type, (array) $pvc->options['general']['post_types_count'], true );
    255600        }
    256601
    … …  
    281626
    282627                // add post views column
    283                 $columns['post_views'] = '<span class="pvc-views-header" title="' . esc_attr__( 'Post Views', 'post-views-counter' ) . '"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor"><path d="M12 2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h1a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1h-1ZM6.5 6a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1V6ZM2 9a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V9Z" /></svg><span class="screen-reader-text">' . esc_attr__( 'Post Views', 'post-views-counter' ) . '</span></span>';
     628                $columns['post_views'] = $this->get_admin_column_header_markup();
    284629
    285630                // restore date column
    … …  
    295640
    296641        /**
     642         * Render the compact statistics-icon Views header. WordPress wraps it in
     643         * the native sort link.
     644         *
     645         * @return string
     646         */
     647        private function get_admin_column_header_markup() {
     648                $label = __( 'Views', 'post-views-counter' );
     649
     650                return '<span class="pvc-views-header" data-pvc-tooltip="' . esc_attr( $label ) . '">' . Post_Views_Counter()->functions->get_counter_icon_svg() . '<span class="screen-reader-text">' . esc_html( $label ) . '</span></span>';
     651        }
     652
     653        /**
    297654         * Add post views column content.
    298655         *
    … …  
    303660        public function add_new_column_content( $column_name, $id ) {
    304661                if ( $column_name === 'post_views' ) {
     662                        // check if user can see stats
     663                        if ( apply_filters( 'pvc_admin_display_post_views', true, $id ) === false ) {
     664                                echo $this->get_restricted_metric_markup();
     665                                return;
     666                        }
     667
    305668                        // get total post views
    306669                        $count = (int) pvc_get_post_views( $id );
    307                        
    308                         // check if user can see stats
    309                         if ( apply_filters( 'pvc_admin_display_post_views', true, $id ) === false ) {
    310                                 echo '—';
    311                                 return;
    312                         }
    313670
    314671                        // get post title
    … …  
    319676
    320677                        // get post type labels
     678                        $post_type_labels = null;
    321679                        $post_type_object = get_post_type_object( get_post_type( $id ) );
    322680
    … …  
    329687                        }
    330688
    331                         // clickable link (modal opening handled via JavaScript)
    332                         echo '<a href="#" class="pvc-view-chart" data-post-id="' . esc_attr( $id ) . '" data-post-title="' . esc_attr( $post_title ) . '" data-post-views="' . esc_attr( $count ) . '">' . esc_html( $count ) . '</a>';
     689                        echo $this->get_column_metric_markup( $count, $id, $post_title, $this->current_user_can_view_post_chart( $id ) );
    333690                }
     691        }
     692
     693        /**
     694         * Check whether the current user may open a post's Views chart: the post
     695         * type's list-screen capability plus read access to the post. Authors and
     696         * contributors keep the chart for other users' posts they can see in the
     697         * list; private posts and drafts they cannot read stay closed.
     698         *
     699         * @param int|WP_Post $post Post ID or object.
     700         * @return bool
     701         */
     702        public function current_user_can_view_post_chart( $post ) {
     703                $post = get_post( $post );
     704
     705                if ( ! $post )
     706                        return false;
     707
     708                $post_type_object = get_post_type_object( $post->post_type );
     709
     710                return $post_type_object && current_user_can( $post_type_object->cap->edit_posts ) && current_user_can( 'read_post', $post->ID );
     711        }
     712
     713        /**
     714         * Render the Views value, keeping the raw total for Quick Edit.
     715         *
     716         * @param int    $value Views total.
     717         * @param int    $post_id Post ID.
     718         * @param string $post_title Post title.
     719         * @param bool   $can_open_chart Whether the current user may open the chart.
     720         * @return string
     721         */
     722        private function get_column_metric_markup( $value, $post_id, $post_title, $can_open_chart ) {
     723                $label = __( 'Views', 'post-views-counter' );
     724                $value = (int) $value;
     725                $formatted = number_format_i18n( $value );
     726
     727                // Quick Edit reads the raw total, never the locale-formatted text.
     728                $data_views = ' data-post-views="' . esc_attr( $value ) . '"';
     729
     730                if ( ! $can_open_chart )
     731                        return '<span class="pvc-column-metric-value"' . $data_views . '><span aria-hidden="true">' . esc_html( $formatted ) . '</span><span class="screen-reader-text"> ' . esc_html( sprintf( __( '%1$s: %2$s.', 'post-views-counter' ), $label, $formatted ) ) . '</span></span>';
     732
     733                // A link whose visible text is only a number still needs an accessible
     734                // name. The tooltip is a data attribute, so it never replaces that name
     735                // or wraps the link in a tooltip role.
     736                $tooltip = sprintf( __( 'Total Views: %s', 'post-views-counter' ), $formatted );
     737                $accessible_label = sprintf( __( '%1$s: %2$s. Open chart.', 'post-views-counter' ), $label, $formatted );
     738
     739                return '<a href="#" class="pvc-view-chart" data-post-id="' . esc_attr( $post_id ) . '" data-post-title="' . esc_attr( $post_title ) . '" data-pvc-tooltip="' . esc_attr( $tooltip ) . '"' . $data_views . ' aria-label="' . esc_attr( $accessible_label ) . '">' . esc_html( $formatted ) . '</a>';
     740        }
     741
     742        /**
     743         * Render a count-free restricted-state value for the admin column.
     744         *
     745         * @return string
     746         */
     747        private function get_restricted_metric_markup() {
     748                return '<span class="pvc-column-metric-value"><span aria-hidden="true">&mdash;</span><span class="screen-reader-text"> ' . esc_html__( 'View statistics are restricted.', 'post-views-counter' ) . '</span></span>';
    334749        }
    335750
    … …  
    375790                <fieldset class="inline-edit-col-left">
    376791                        <div id="inline-edit-post_views" class="inline-edit-col">
     792                                <?php
     793                                // The Quick Edit template is rendered once and reused for every
     794                                // row, so the template-level marker records whether the field may
     795                                // ever be enabled.
     796                                ?>
    377797                                <label class="inline-edit-group">
    378                                         <span class="title"><?php _e( 'Post Views', 'post-views-counter' ); ?></span>
     798                                        <span class="title"><?php esc_html_e( 'Views', 'post-views-counter' ); ?></span>
    379799                                        <?php if ( $allow_edit === true && $allow_edit_condition === true ) { ?>
    380                                                 <span class="input-text-wrap"><input type="text" name="post_views" class="title text" value=""></span>
     800                                                <span class="input-text-wrap"><input type="number" min="0" step="1" inputmode="numeric" name="post_views" class="title text" value="" data-pvc-editable="1"></span>
    381801                                                <input type="hidden" name="current_post_views" value="" />
    382                                                 <?php wp_nonce_field( 'post_views_count', 'pvc_nonce' ); ?>
    383802                                        <?php } else { ?>
    384                                                 <span class="input-text-wrap"><input type="text" name="post_views" class="title text" value="" disabled readonly /></span>
     803                                                <span class="input-text-wrap"><input type="number" min="0" step="1" inputmode="numeric" name="post_views" class="title text" value="" disabled readonly /></span>
    385804                                        <?php } ?>
    386805                                </label>
     806                                <?php if ( $allow_edit === true && $allow_edit_condition === true ) wp_nonce_field( 'post_views_count', 'pvc_nonce' ); ?>
    387807                        </div>
    388808                </fieldset>
    … …  
    391811
    392812        /**
     813         * Apply Bulk Edit counter totals after WordPress has finished its own bulk
     814         * update. Only the IDs WordPress actually updated are written, so a locked
     815         * or skipped post never receives a counter change.
     816         *
     817         * @param array $updated Post IDs WordPress updated.
     818         * @param array $shared_post_data Submitted bulk fields.
     819         * @return void
     820         */
     821        public function bulk_edit_post_counters( $updated, $shared_post_data ) {
     822                if ( ! is_array( $updated ) || ! is_array( $shared_post_data ) )
     823                        return;
     824
     825                // Only the submitted bulk fields are considered, never ambient request data.
     826                if ( ! array_key_exists( 'post_views', $shared_post_data ) )
     827                        return;
     828
     829                // A request without the plugin's own bulk nonce is not one of ours.
     830                if ( ! isset( $shared_post_data['pvc_nonce'] ) || ! is_scalar( $shared_post_data['pvc_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( (string) $shared_post_data['pvc_nonce'] ) ), 'post_views_count' ) )
     831                        return;
     832
     833                $updates = $this->get_submitted_counter_totals( $shared_post_data );
     834
     835                // Blank means no change.
     836                if ( is_array( $updates ) && empty( $updates ) )
     837                        return;
     838
     839                $post_ids = array_values( array_filter( array_unique( array_map( 'absint', $updated ) ) ) );
     840                $pvc = Post_Views_Counter();
     841                $rejected = is_null( $updates )
     842                        || empty( $pvc->options['display']['restrict_edit_views'] )
     843                        || ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) );
     844
     845                if ( $rejected ) {
     846                        $this->set_bulk_counter_result( 0, count( $post_ids ) );
     847
     848                        return;
     849                }
     850
     851                $post_types = (array) $pvc->options['general']['post_types_count'];
     852                $succeeded = 0;
     853                $failed = 0;
     854
     855                foreach ( $post_ids as $post_id ) {
     856                        if ( ! in_array( get_post_type( $post_id ), $post_types, true ) || ! $this->can_update_post_counter_totals( $post_id ) || ! $this->update_post_counter_totals( $post_id, $updates ) )
     857                                $failed++;
     858                        else
     859                                $succeeded++;
     860                }
     861
     862                $this->set_bulk_counter_result( $succeeded, $failed );
     863        }
     864
     865        /**
     866         * Record one request-local Bulk Edit counter outcome and arm the narrowly
     867         * scoped redirect filter that reports it. No persistent state is stored.
     868         *
     869         * @param int $updated Successful counter writes.
     870         * @param int $failed Failed counter writes.
     871         * @return void
     872         */
     873        private function set_bulk_counter_result( $updated, $failed ) {
     874                $this->bulk_counter_result = [ 'updated' => (int) $updated, 'failed' => (int) $failed ];
     875
     876                add_filter( 'wp_redirect', [ $this, 'add_bulk_counter_redirect_args' ] );
     877        }
     878
     879        /**
     880         * Append sanitized counter outcome counts to the matching bulk redirect.
     881         *
     882         * @param string $location Redirect location.
     883         * @return string
     884         */
     885        public function add_bulk_counter_redirect_args( $location ) {
     886                if ( is_null( $this->bulk_counter_result ) || ! is_string( $location ) || $location === '' )
     887                        return $location;
     888
     889                $path = wp_parse_url( $location, PHP_URL_PATH );
     890
     891                // Only the post-list redirect that ends this bulk request is touched.
     892                if ( ! is_string( $path ) || basename( $path ) !== 'edit.php' )
     893                        return $location;
     894
     895                $result = $this->bulk_counter_result;
     896                $this->bulk_counter_result = null;
     897
     898                remove_filter( 'wp_redirect', [ $this, 'add_bulk_counter_redirect_args' ] );
     899
     900                if ( $result['updated'] < 1 && $result['failed'] < 1 )
     901                        return $location;
     902
     903                return add_query_arg( [ 'pvc_counters_updated' => $result['updated'], 'pvc_counters_failed' => $result['failed'] ], $location );
     904        }
     905
     906        /**
     907         * Report the Bulk Edit counter outcome on the resulting list page.
     908         *
     909         * WordPress content updates already completed, so a counter failure is
     910         * reported truthfully instead of implying the bulk operation was cancelled.
     911         *
     912         * @global string $pagenow
     913         *
     914         * @return void
     915         */
     916        public function bulk_counter_admin_notice() {
     917                global $pagenow;
     918
     919                if ( $pagenow !== 'edit.php' || ( ! isset( $_GET['pvc_counters_updated'] ) && ! isset( $_GET['pvc_counters_failed'] ) ) )
     920                        return;
     921
     922                $updated = isset( $_GET['pvc_counters_updated'] ) && is_scalar( $_GET['pvc_counters_updated'] ) ? absint( wp_unslash( $_GET['pvc_counters_updated'] ) ) : 0;
     923                $failed = isset( $_GET['pvc_counters_failed'] ) && is_scalar( $_GET['pvc_counters_failed'] ) ? absint( wp_unslash( $_GET['pvc_counters_failed'] ) ) : 0;
     924
     925                if ( $updated < 1 && $failed < 1 )
     926                        return;
     927
     928                $messages = [];
     929
     930                if ( $updated > 0 )
     931                        $messages[] = sprintf( _n( 'Counter totals updated for %s post.', 'Counter totals updated for %s posts.', $updated, 'post-views-counter' ), number_format_i18n( $updated ) );
     932
     933                if ( $failed > 0 )
     934                        $messages[] = sprintf( _n( 'Counter totals could not be updated for %s post.', 'Counter totals could not be updated for %s posts.', $failed, 'post-views-counter' ), number_format_i18n( $failed ) );
     935
     936                echo '<div class="notice notice-' . ( $failed > 0 ? 'error' : 'success' ) . ' is-dismissible"><p>' . esc_html( implode( ' ', $messages ) ) . '</p></div>';
     937        }
     938
     939        /**
     940         * Let WordPress strip the Bulk Edit outcome arguments from the list URL, so
     941         * the notice is not shown again on reload.
     942         *
     943         * @param array $args Removable query arguments.
     944         * @return array
     945         */
     946        public function add_removable_query_args( $args ) {
     947                if ( ! is_array( $args ) )
     948                        return $args;
     949
     950                $args[] = 'pvc_counters_updated';
     951                $args[] = 'pvc_counters_failed';
     952
     953                return $args;
     954        }
     955
     956        /**
    393957         * Bulk save post views.
     958         *
     959         * Deprecated for the shipped UI: native Bulk Edit now writes counter totals
     960         * from the bulk_edit_posts completion hook. The endpoint remains registered,
     961         * with unchanged security behavior, for compatibility.
    394962         *
    395963         * @return void
    … …  
    402970                        exit;
    403971
    404                 $count = isset( $_POST['post_views'] ) ? $this->sanitize_post_views_input( $_POST['post_views'] ) : null;
    405 
    406972                // check post ids
    407                 $post_ids = ( ! empty( $_POST['post_ids'] ) && is_array( $_POST['post_ids'] ) ) ? array_map( 'absint', wp_unslash( $_POST['post_ids'] ) ) : [];
    408 
    409                 if ( is_null( $count ) )
    410                         exit;
     973                $post_ids = ( ! empty( $_POST['post_ids'] ) && is_array( $_POST['post_ids'] ) ) ? array_values( array_filter( array_unique( array_map( 'absint', wp_unslash( $_POST['post_ids'] ) ) ) ) ) : [];
     974                $updates = $this->get_submitted_counter_totals();
     975
     976                if ( is_null( $updates ) )
     977                        wp_send_json_error( [ 'message' => __( 'Invalid counter totals.', 'post-views-counter' ) ] );
    411978
    412979                // allow editing
    … …  
    418985                // break if views editing not allowed or editing condition not met
    419986                if ( $allow_edit === false || $allow_edit_condition === false )
    420                         exit;
     987                        wp_send_json_error( [ 'message' => __( 'Permission denied.', 'post-views-counter' ) ] );
    421988
    422989                $post_types = (array) $pvc->options['general']['post_types_count'];
    423990
    424991                // any post ids?
    425                 if ( ! empty( $post_ids ) ) {
     992                $updated = 0;
     993                $failed = 0;
     994
     995                if ( ! empty( $post_ids ) && ! empty( $updates ) ) {
    426996                        foreach ( $post_ids as $post_id ) {
    427                                 // break if current user can't edit this post
    428                                 if ( ! current_user_can( 'edit_post', $post_id ) )
    429                                         continue;
    430 
    431                                 if ( ! in_array( get_post_type( $post_id ), $post_types, true ) )
    432                                         continue;
    433 
    434                                 if ( pvc_update_post_views( $post_id, $count ) !== false )
    435                                         do_action( 'pvc_after_update_post_views_count', $post_id );
     997                                if ( ! in_array( get_post_type( $post_id ), $post_types, true ) || ! $this->can_update_post_counter_totals( $post_id ) || ! $this->update_post_counter_totals( $post_id, $updates ) )
     998                                        $failed++;
     999                                else
     1000                                        $updated++;
    4361001                        }
    4371002                }
    4381003
    439                 exit;
     1004                wp_send_json_success( [ 'updated' => $updated, 'failed' => $failed ] );
    4401005        }
    4411006}
  • post-views-counter/tags/1.8.0/includes/class-counter.php

    r3640258 r3734102  
    1010 */
    1111class Post_Views_Counter_Counter {
     12        const REST_MAX_TARGETS = 100;
     13        const STORAGE_MAX_PAYLOAD_BYTES = 15920;
     14        const STORAGE_MAX_COOKIE_CHUNKS = 4;
     15        const STORAGE_MAX_BUCKETS = 8;
     16        const STORAGE_MAX_MEMBERS = 1000;
    1217
    1318        private $storage = [];
    … …  
    1520        private $queue = [];
    1621        private $queue_mode = false;
    17         private $db_insert_values = '';
     22        private $db_insert_values = [];
    1823        private $cookie = [];
     24        private $pending_storage_commit = null;
     25        private $pending_session_created = null;
     26        private $window_created_candidate = false;
     27        private $last_cache_flush_status = 'idle';
    1928
    2029        /**
    … …  
    98107
    99108                foreach ( $ids as $id ) {
    100                         $counted[$id] = ! ( $this->check_post( $id ) === null );
     109                        $counted[$id] = ! ( $this->check_post( $id, [], true ) === null );
    101110                }
    102111
    … …  
    219228         * @param int $post_id
    220229         * @param array $content_data
     230         * @param bool $views_only
    221231         *
    222232         * @return null|int
    223233         */
    224         public function check_post( $post_id = 0, $content_data = [] ) {
     234        public function check_post( $post_id = 0, $content_data = [], $views_only = false ) {
     235                $this->reset_pending_count_state();
     236
    225237                // force check cookie in short init mode
    226238                if ( defined( 'SHORTINIT' ) && SHORTINIT )
    … …  
    271283                }
    272284
     285                $visit_increment = 0;
     286
     287                if ( ! $views_only && $this->window_created_candidate ) {
     288                        $availability = $pvc->visits instanceof Post_Views_Counter_Visits ? $pvc->visits->get_visits_availability() : [ 'writable' => false ];
     289
     290                        if ( ! empty( $availability['writable'] ) )
     291                                $visit_increment = 1;
     292                }
     293
    273294                // filter visit counting
    274295                $count_visit = (bool) apply_filters( 'pvc_count_visit', $count_visit, $post_id, $user_id, $user_ip, 'post', $hook_content_data );
    … …  
    279300                        do_action( 'pvc_before_count_visit', $post_id, $user_id, $user_ip, 'post', $hook_content_data );
    280301
    281                         return $this->count_visit( $post_id );
    282                 }
     302                        $result = $this->count_visit( $post_id, $visit_increment );
     303
     304                        if ( $result !== null ) {
     305                                if ( $views_only )
     306                                        $this->discard_pending_storage();
     307                                else
     308                                        $this->commit_pending_storage();
     309
     310                                return $result;
     311                        }
     312                }
     313
     314                $this->discard_pending_storage();
     315
     316                return null;
    283317        }
    284318
    … …  
    483517                        $storage_data = [];
    484518
     519                $storage_data = $this->mark_storage_capability( $storage_data, isset( $_POST['storage_capable'] ) ? $_POST['storage_capable'] : null );
     520
    485521                echo wp_json_encode(
    486522                        [
    … …  
    558594                else
    559595                        $storage_data = [];
     596
     597                $storage_data = $this->mark_storage_capability( $storage_data, $request->get_param( 'storage_capable' ) );
    560598
    561599                return [
    … …  
    733771         * @return array
    734772         */
    735         public function build_session_storage_payload( $storage_state, $content_id = 0, $content_type = 'post', $default_expiration = 0, $current_time = 0 ) {
    736                 $session_state = $this->create_session_storage_state( $storage_state, $content_id, $content_type, $default_expiration, $current_time );
     773        public function build_session_storage_payload( $storage_state, $content_id = 0, $content_type = 'post', $default_expiration = 0, $current_time = 0, $emit_hook = true ) {
     774                $session_state = $this->create_session_storage_state( $storage_state, $content_id, $content_type, $default_expiration, $current_time, $emit_hook );
    737775
    738776                return $this->get_public_session_storage_payload( $session_state );
    … …  
    757795
    758796                foreach ( $storage_states as $storage_state ) {
     797                        if ( is_array( $storage_state ) && isset( $storage_state['_pvc_storage_capable'] ) && $storage_state['_pvc_storage_capable'] === false ) {
     798                                $merged_state['_pvc_storage_capable'] = false;
     799                                continue;
     800                        }
     801
    759802                        if ( ! $this->is_normalized_storage_state( $storage_state ) || ! $storage_state['is_valid'] )
    760803                                continue;
    … …  
    791834                                }
    792835                        }
     836                }
     837
     838                if ( count( $merged_state['visited'] ) > self::STORAGE_MAX_BUCKETS ) {
     839                        $merged_state['format'] = 'invalid';
     840                        $merged_state['is_valid'] = false;
     841                        $merged_state['_pvc_storage_capable'] = false;
     842
     843                        return $merged_state;
    793844                }
    794845
    … …  
    803854                        $merged_state['needs_writeback'] = false;
    804855
    805                         return $merged_state;
     856                        return $this->limit_storage_state_members( $merged_state );
    806857                }
    807858
    … …  
    811862                }
    812863
    813                 return $merged_state;
     864                return $this->limit_storage_state_members( $merged_state );
     865        }
     866
     867        /**
     868         * Keep merged storage membership within the request payload budget.
     869         *
     870         * @param array $storage_state Normalized storage state.
     871         * @return array
     872         */
     873        private function limit_storage_state_members( $storage_state ) {
     874                $member_count = 0;
     875
     876                foreach ( $storage_state['visited'] as $bucket => $bucket_members ) {
     877                        foreach ( array_keys( $bucket_members ) as $content_id ) {
     878                                $member_count++;
     879
     880                                if ( $member_count <= self::STORAGE_MAX_MEMBERS )
     881                                        continue;
     882
     883                                unset( $storage_state['visited'][$bucket][$content_id], $storage_state['legacy']['expirations'][$bucket][$content_id] );
     884                        }
     885                }
     886
     887                return $storage_state;
    814888        }
    815889
    … …  
    825899         * @return array
    826900         */
    827         private function create_session_storage_state( $storage_state, $content_id = 0, $content_type = 'post', $default_expiration = 0, $current_time = 0 ) {
     901        private function create_session_storage_state( $storage_state, $content_id = 0, $content_type = 'post', $default_expiration = 0, $current_time = 0, $emit_hook = true ) {
    828902                $content_type = $this->normalize_storage_bucket( $content_type );
    829903                $content_id = (int) $content_id;
    … …  
    848922
    849923                        // new session created -- entrance/visit hook for the triggering content item
    850                         if ( $content_id > 0 ) {
     924                        if ( $content_id > 0 && $emit_hook ) {
    851925                                /**
    852926                                 * Fires when a new anonymous session is created.
    … …  
    869943                $session_state['needs_writeback'] = false;
    870944
    871                 if ( $content_id > 0 )
     945                if ( $content_id > 0 && ! isset( $session_state['visited'][$content_type][$content_id] ) ) {
     946                        $member_count = 0;
     947
     948                        foreach ( $session_state['visited'] as $bucket_members )
     949                                $member_count += count( $bucket_members );
     950
     951                        if ( $member_count >= self::STORAGE_MAX_MEMBERS ) {
     952                                foreach ( $session_state['visited'] as $bucket => $bucket_members ) {
     953                                        if ( empty( $bucket_members ) )
     954                                                continue;
     955
     956                                        $remove_id = key( $bucket_members );
     957                                        unset( $session_state['visited'][$bucket][$remove_id] );
     958                                        break;
     959                                }
     960                        }
     961
    872962                        $session_state['visited'][$content_type][$content_id] = true;
     963                }
    873964
    874965                return $session_state;
    … …  
    9201011
    9211012        /**
     1013         * Emit one browser storage cookie chunk.
     1014         *
     1015         * Single emission point for every counter cookie so path, domain, secure,
     1016         * httponly and SameSite stay identical for writes and deletions. The plugin
     1017         * requires PHP 7.4, so the options-array form is always available.
     1018         *
     1019         * @param string $name Full cookie name including its chunk suffix.
     1020         * @param string $value Chunk value; an empty string deletes the chunk.
     1021         * @param int    $expires Expiry timestamp; 1 deletes the chunk.
     1022         *
     1023         * @return bool
     1024         */
     1025        private function set_storage_cookie( $name, $value, $expires ) {
     1026                return $this->send_cookie(
     1027                        $name,
     1028                        (string) $value,
     1029                        [
     1030                                'expires'       => (int) $expires,
     1031                                'path'          => COOKIEPATH,
     1032                                'domain'        => COOKIE_DOMAIN,
     1033                                'secure'        => is_ssl(),
     1034                                'httponly'      => false,
     1035                                'samesite'      => 'LAX'
     1036                        ]
     1037                );
     1038        }
     1039
     1040        /**
     1041         * Transport seam for cookie emission.
     1042         *
     1043         * Isolated so the prepared attributes can be observed without a real HTTP
     1044         * response. Nothing but set_storage_cookie() may call it.
     1045         *
     1046         * @param string $name Cookie name.
     1047         * @param string $value Cookie value.
     1048         * @param array  $options PHP 7.4-compatible setcookie() options array.
     1049         *
     1050         * @return bool
     1051         */
     1052        protected function send_cookie( $name, $value, $options ) {
     1053                return setcookie( $name, $value, $options );
     1054        }
     1055
     1056        /**
    9221057         * Clear stale cookie chunks that are no longer used by the current payload.
    9231058         *
    9241059         * @param string $cookie_name
    9251060         * @param int $valid_chunk_count
    926          * @param bool $php_at_least_73
    9271061         *
    9281062         * @return void
    9291063         */
    930         private function clear_stale_cookie_chunks( $cookie_name, $valid_chunk_count, $php_at_least_73 ) {
     1064        private function clear_stale_cookie_chunks( $cookie_name, $valid_chunk_count ) {
    9311065                if ( ! isset( $_COOKIE[$cookie_name] ) || ! is_array( $_COOKIE[$cookie_name] ) )
    9321066                        return;
    … …  
    9381072                                continue;
    9391073
    940                         if ( $php_at_least_73 ) {
    941                                 setcookie(
    942                                         $cookie_name . '[' . $chunk_index . ']',
    943                                         '',
    944                                         [
    945                                                 'expires'       => 1,
    946                                                 'path'          => COOKIEPATH,
    947                                                 'domain'        => COOKIE_DOMAIN,
    948                                                 'secure'        => is_ssl(),
    949                                                 'httponly'      => false,
    950                                                 'samesite'      => 'LAX'
    951                                         ]
    952                                 );
    953                         } else {
    954                                 setcookie( $cookie_name . '[' . $chunk_index . ']', '', 1, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), false );
    955                         }
     1074                        $this->set_storage_cookie( $cookie_name . '[' . $chunk_index . ']', '', 1 );
    9561075                }
    9571076        }
    … …  
    10041123                $content_type = $this->normalize_storage_bucket( $content_type );
    10051124                $storage_payloads = $this->parse_storage_payload_map( $storage_data_all );
     1125
     1126                if ( isset( $storage_payloads['_pvc_storage_capable'] ) && $storage_payloads['_pvc_storage_capable'] === false ) {
     1127                        $state = $this->get_empty_storage_state();
     1128                        $state['_pvc_storage_capable'] = false;
     1129
     1130                        return $state;
     1131                }
    10061132
    10071133                if ( empty( $storage_payloads ) ) {
    … …  
    10411167                                return [];
    10421168
     1169                        if ( strlen( $storage_data_all ) > self::STORAGE_MAX_PAYLOAD_BYTES )
     1170                                return [ '_pvc_storage_capable' => false ];
     1171
    10431172                        $decoded_payloads = json_decode( stripslashes( $storage_data_all ), true, 8 );
    10441173
    10451174                        if ( json_last_error() !== JSON_ERROR_NONE || ! is_array( $decoded_payloads ) )
    10461175                                return [];
    1047                 } elseif ( is_array( $storage_data_all ) )
     1176
     1177                        if ( count( $decoded_payloads ) > self::STORAGE_MAX_BUCKETS )
     1178                                return [ '_pvc_storage_capable' => false ];
     1179                } elseif ( is_array( $storage_data_all ) ) {
     1180                        if ( count( $storage_data_all ) > self::STORAGE_MAX_BUCKETS )
     1181                                return [ '_pvc_storage_capable' => false ];
     1182
    10481183                        $decoded_payloads = $storage_data_all;
    1049                 else
     1184                } else
    10501185                        return [];
    10511186
    … …  
    10611196
    10621197        /**
    1063          * Check whether the active Pro plugin supports session payload writes.
     1198         * Check whether the active extension plugin supports session payload writes.
    10641199         *
    10651200         * @return bool
    … …  
    10801215         * Check whether session payload writes are enabled.
    10811216         *
    1082          * Session payload writes are the default for PVC-only installs. When Pro is active,
    1083          * PVC uses Pro's explicit capability declaration and allows this filter to override
    1084          * the computed default for controlled testing or emergency rollback.
     1217         * Session payload writes are the default for Core-only installs. With an
     1218         * active extension, its declared capability determines the default. This
     1219         * filter can override it for controlled testing or emergency rollback.
    10851220         *
    10861221         * @return bool
    … …  
    11181253                ksort( $bucket_expirations, SORT_NUMERIC );
    11191254
     1255                while ( count( $bucket_expirations ) > self::STORAGE_MAX_MEMBERS || strlen( $this->serialize_legacy_cookie_payload( $bucket_expirations ) ) > self::STORAGE_MAX_PAYLOAD_BYTES ) {
     1256                        $remove_id = key( $bucket_expirations );
     1257
     1258                        if ( (int) $remove_id === $content_id ) {
     1259                                next( $bucket_expirations );
     1260                                $remove_id = key( $bucket_expirations );
     1261                        }
     1262
     1263                        if ( $remove_id === null )
     1264                                break;
     1265
     1266                        unset( $bucket_expirations[$remove_id] );
     1267                        reset( $bucket_expirations );
     1268                }
     1269
    11201270                return $bucket_expirations;
    11211271        }
    … …  
    11511301                ];
    11521302                $cookie_chunks = str_split( $payload, 3980 );
     1303
     1304                if ( count( $cookie_chunks ) > self::STORAGE_MAX_COOKIE_CHUNKS )
     1305                        return [ 'name' => [], 'value' => [], 'expiry' => [] ];
     1306
    11531307                $cookie_expiration = max( $bucket_expirations );
    11541308
    … …  
    12971451        private function combine_cookie_chunks( $cookie_chunks ) {
    12981452                $chunks = [];
     1453                $payload_bytes = 0;
     1454
     1455                if ( ! is_array( $cookie_chunks ) || count( $cookie_chunks ) > self::STORAGE_MAX_COOKIE_CHUNKS )
     1456                        return '__pvc_invalid_storage_payload__';
     1457
     1458                ksort( $cookie_chunks, SORT_NUMERIC );
     1459
     1460                if ( ! empty( $cookie_chunks ) && array_keys( $cookie_chunks ) !== range( 0, count( $cookie_chunks ) - 1 ) )
     1461                        return '__pvc_invalid_storage_payload__';
    12991462
    13001463                foreach ( $cookie_chunks as $chunk ) {
    1301                         if ( is_scalar( $chunk ) )
    1302                                 $chunks[] = (string) $chunk;
     1464                        if ( ! is_scalar( $chunk ) )
     1465                                return '__pvc_invalid_storage_payload__';
     1466
     1467                        $chunk = (string) $chunk;
     1468                        $payload_bytes += strlen( $chunk );
     1469
     1470                        if ( $payload_bytes > self::STORAGE_MAX_PAYLOAD_BYTES )
     1471                                return '__pvc_invalid_storage_payload__';
     1472
     1473                        $chunks[] = $chunk;
    13031474                }
    13041475
    … …  
    13311502                $state = $this->get_empty_storage_state();
    13321503
    1333                 if ( is_array( $storage_data ) )
     1504                if ( is_array( $storage_data ) ) {
     1505                        $encoded_storage = wp_json_encode( $storage_data );
     1506
     1507                        if ( ! is_string( $encoded_storage ) || strlen( $encoded_storage ) > self::STORAGE_MAX_PAYLOAD_BYTES ) {
     1508                                $state['format'] = 'invalid';
     1509                                $state['is_valid'] = false;
     1510                                $state['_pvc_storage_capable'] = false;
     1511
     1512                                return $state;
     1513                        }
     1514
    13341515                        return $this->normalize_json_storage_state( $storage_data, $content_type );
     1516                }
    13351517
    13361518                if ( ! is_scalar( $storage_data ) ) {
    … …  
    13421524
    13431525                $storage_data = trim( (string) $storage_data );
     1526
     1527                if ( $storage_data === '__pvc_invalid_storage_payload__' ) {
     1528                        $state['format'] = 'invalid';
     1529                        $state['is_valid'] = false;
     1530                        $state['_pvc_storage_capable'] = false;
     1531
     1532                        return $state;
     1533                }
     1534
     1535                if ( strlen( $storage_data ) > self::STORAGE_MAX_PAYLOAD_BYTES ) {
     1536                        $state['format'] = 'invalid';
     1537                        $state['is_valid'] = false;
     1538                        $state['_pvc_storage_capable'] = false;
     1539
     1540                        return $state;
     1541                }
    13441542
    13451543                if ( $storage_data === '' )
    … …  
    14031601                $expires_at = isset( $storage_data['expires_at'] ) ? (int) $storage_data['expires_at'] : 0;
    14041602
    1405                 if ( $session_id === '' || $started_at <= 0 || $expires_at <= 0 || $expires_at < $started_at || ! isset( $storage_data['visited'] ) || ! is_array( $storage_data['visited'] ) ) {
     1603                if ( $session_id === '' || $started_at <= 0 || $expires_at <= 0 || $expires_at < $started_at || ! isset( $storage_data['visited'] ) || ! is_array( $storage_data['visited'] ) || count( $storage_data['visited'] ) > self::STORAGE_MAX_BUCKETS ) {
    14061604                        $state['format'] = 'invalid';
    14071605                        $state['is_valid'] = false;
     1606
     1607                        if ( isset( $storage_data['visited'] ) && is_array( $storage_data['visited'] ) && count( $storage_data['visited'] ) > self::STORAGE_MAX_BUCKETS )
     1608                                $state['_pvc_storage_capable'] = false;
    14081609
    14091610                        return $state;
    … …  
    14331634                }
    14341635
     1636                if ( count( $state['visited'] ) > self::STORAGE_MAX_BUCKETS ) {
     1637                        $state['format'] = 'invalid';
     1638                        $state['is_valid'] = false;
     1639                        $state['_pvc_storage_capable'] = false;
     1640
     1641                        return $state;
     1642                }
     1643
     1644                $member_count = 0;
     1645
     1646                foreach ( $state['visited'] as $bucket_members )
     1647                        $member_count += count( $bucket_members );
     1648
     1649                if ( $member_count > self::STORAGE_MAX_MEMBERS ) {
     1650                        $state['format'] = 'invalid';
     1651                        $state['is_valid'] = false;
     1652                        $state['_pvc_storage_capable'] = false;
     1653                }
     1654
    14351655                return $state;
    14361656        }
    … …  
    14591679                        $state['legacy']['expirations'][$content_type][$content_id] = $expiration;
    14601680                        $valid_items++;
     1681
     1682                        if ( $valid_items > self::STORAGE_MAX_MEMBERS ) {
     1683                                $state['format'] = 'invalid';
     1684                                $state['is_valid'] = false;
     1685                                $state['_pvc_storage_capable'] = false;
     1686
     1687                                return $state;
     1688                        }
    14611689                }
    14621690
    … …  
    14951723                        $state['visited'][$content_type][$content_id] = true;
    14961724                        $state['legacy']['expirations'][$content_type][$content_id] = $expiration;
     1725
     1726                        if ( count( $state['visited'][$content_type] ) > self::STORAGE_MAX_MEMBERS ) {
     1727                                $state['format'] = 'invalid';
     1728                                $state['is_valid'] = false;
     1729                                $state['_pvc_storage_capable'] = false;
     1730
     1731                                return $state;
     1732                        }
    14971733                }
    14981734
    … …  
    15751811                        return [ 'post' => [] ];
    15761812
     1813                $buckets = array_slice( $buckets, 0, self::STORAGE_MAX_BUCKETS, true );
     1814
    15771815                // ensure all bucket values are arrays
    15781816                foreach ( $buckets as $key => $value ) {
    … …  
    16101848         */
    16111849        private function save_data_storage( $content, $content_type, $content_data ) {
     1850                if ( isset( $content_data['_pvc_storage_capable'] ) && $content_data['_pvc_storage_capable'] === false ) {
     1851                        $this->storage = [];
     1852
     1853                        return true;
     1854                }
     1855
    16121856                // get base instance
    16131857                $pvc = Post_Views_Counter();
    … …  
    16241868                }
    16251869
    1626                 if ( $this->use_session_storage_payload_writes() )
    1627                         $this->storage = $this->build_session_storage_payload( $content_data, $content, $content_type, $expiration, $current_time );
     1870                if ( $this->use_session_storage_payload_writes() ) {
     1871                        $this->window_created_candidate = $this->storage_state_creates_window( $content_data );
     1872                        $this->storage = $this->build_session_storage_payload( $content_data, $content, $content_type, $expiration, $current_time, false );
     1873                        $this->prepare_session_created_hook( $this->storage, $content, $content_type );
     1874                }
    16281875                else
    16291876                        $this->storage = [ $content_type => $this->build_legacy_storage_payload( $content_data, $content, $content_type, $expiration, $current_time ) ];
    16301877
     1878                $this->pending_storage_commit = [ 'type' => 'response' ];
     1879
    16311880                return $count_visit;
    16321881        }
    … …  
    16411890         */
    16421891        private function save_cookie_storage( $content, $content_data ) {
     1892                if ( isset( $content_data['_pvc_storage_capable'] ) && $content_data['_pvc_storage_capable'] === false ) {
     1893                        $this->storage = [];
     1894
     1895                        return true;
     1896                }
     1897
    16431898                // early return?
    16441899//TODO check this filter in js
    … …  
    16651920                if ( ! $this->use_session_storage_payload_writes() ) {
    16661921                        $this->storage = $this->build_legacy_cookie_storage_data( $content_data, $cookie_name, $content, 'post', $expiration, $current_time );
     1922                        $this->pending_storage_commit = [ 'type' => 'response' ];
    16671923
    16681924                        return $count_visit;
    16691925                }
    16701926
    1671                 $session_payload = $this->build_session_storage_payload( $content_data, $content, 'post', $expiration, $current_time );
     1927                $this->window_created_candidate = $this->storage_state_creates_window( $content_data );
     1928                $session_payload = $this->build_session_storage_payload( $content_data, $content, 'post', $expiration, $current_time, false );
    16721929                $session_json = wp_json_encode( $session_payload );
    16731930
    1674                 if ( ! is_string( $session_json ) || $session_json === '' ) {
     1931                if ( ! is_string( $session_json ) || $session_json === '' || strlen( $session_json ) > self::STORAGE_MAX_PAYLOAD_BYTES ) {
    16751932                        $this->storage = [];
    16761933
    … …  
    16931950
    16941951                $this->storage = $cookies_data;
     1952                $this->pending_storage_commit = [ 'type' => 'response' ];
     1953                $this->prepare_session_created_hook( $session_payload, $content, 'post' );
    16951954
    16961955                return $count_visit;
    … …  
    17231982                // assign cookie name
    17241983                $cookie_name = 'pvc_visits' . ( is_multisite() ? '_' . get_current_blog_id() : '' );
    1725                 $php_at_least_73 = version_compare( phpversion(), '7.3', '>=' );
    17261984
    17271985                if ( ! $this->use_session_storage_payload_writes() ) {
    17281986                        $legacy_payload = $this->serialize_legacy_cookie_payload( $this->build_legacy_storage_payload( $cookie, $id, 'post', $expiration, $current_time ) );
    17291987                        $cookies_data = $this->build_legacy_cookie_storage_data( $cookie, $cookie_name, $id, 'post', $expiration, $current_time );
    1730 
    1731                         foreach ( $cookies_data['name'] as $key => $cookie_chunk_name ) {
    1732                                 if ( $php_at_least_73 ) {
    1733                                         setcookie(
    1734                                                 $cookie_chunk_name,
    1735                                                 $cookies_data['value'][$key],
    1736                                                 [
    1737                                                         'expires'       => $cookies_data['expiry'][$key],
    1738                                                         'path'          => COOKIEPATH,
    1739                                                         'domain'        => COOKIE_DOMAIN,
    1740                                                         'secure'        => is_ssl(),
    1741                                                         'httponly'      => false,
    1742                                                         'samesite'      => 'LAX'
    1743                                                 ]
    1744                                         );
    1745                                 } else {
    1746                                         setcookie( $cookie_chunk_name, $cookies_data['value'][$key], $cookies_data['expiry'][$key], COOKIEPATH, COOKIE_DOMAIN, is_ssl(), false );
    1747                                 }
    1748                         }
    1749 
    1750                         $this->clear_stale_cookie_chunks( $cookie_name, count( $cookies_data['name'] ), $php_at_least_73 );
    1751 
    1752                         if ( $this->queue_mode )
    1753                                 $this->cookie = $this->sanitize_cookies_data( $legacy_payload, 'post' );
     1988                        $this->pending_storage_commit = [
     1989                                'type' => 'cookie',
     1990                                'cookies' => $cookies_data,
     1991                                'cookie_name' => $cookie_name,
     1992                                'normalized_payload' => $legacy_payload
     1993                        ];
    17541994
    17551995                        return $count_visit;
    17561996                }
    17571997
    1758                 $session_payload = $this->build_session_storage_payload( $cookie, $id, 'post', $expiration, $current_time );
     1998                $this->window_created_candidate = $this->storage_state_creates_window( $cookie );
     1999                $session_payload = $this->build_session_storage_payload( $cookie, $id, 'post', $expiration, $current_time, false );
    17592000                $session_json = wp_json_encode( $session_payload );
    17602001
    1761                 if ( ! is_string( $session_json ) || $session_json === '' )
     2002                if ( ! is_string( $session_json ) || $session_json === '' || strlen( $session_json ) > self::STORAGE_MAX_PAYLOAD_BYTES )
    17622003                        return false;
    17632004
    1764                 // check whether php version is at least 7.3
    17652005                $cookie_chunks = str_split( $session_json, 3980 );
    17662006                $cookie_expiration = (int) $session_payload['expires_at'];
    17672007
     2008                $cookies_data = [ 'name' => [], 'value' => [], 'expiry' => [] ];
     2009
    17682010                foreach ( $cookie_chunks as $key => $value ) {
    1769                         if ( $php_at_least_73 ) {
    1770                                 setcookie(
    1771                                         $cookie_name . '[' . $key . ']',
    1772                                         $value,
    1773                                         [
    1774                                                 'expires'       => $cookie_expiration,
    1775                                                 'path'          => COOKIEPATH,
    1776                                                 'domain'        => COOKIE_DOMAIN,
    1777                                                 'secure'        => is_ssl(),
    1778                                                 'httponly'      => false,
    1779                                                 'samesite'      => 'LAX'
    1780                                         ]
    1781                                 );
    1782                         } else {
    1783                                 setcookie( $cookie_name . '[' . $key . ']', $value, $cookie_expiration, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), false );
    1784                         }
    1785                 }
    1786 
    1787                 $this->clear_stale_cookie_chunks( $cookie_name, count( $cookie_chunks ), $php_at_least_73 );
    1788 
    1789                 if ( $this->queue_mode )
    1790                         $this->cookie = $this->sanitize_cookies_data( $session_json, 'post' );
     2011                        $cookies_data['name'][] = $cookie_name . '[' . $key . ']';
     2012                        $cookies_data['value'][] = $value;
     2013                        $cookies_data['expiry'][] = $cookie_expiration;
     2014                }
     2015
     2016                $this->pending_storage_commit = [
     2017                        'type' => 'cookie',
     2018                        'cookies' => $cookies_data,
     2019                        'cookie_name' => $cookie_name,
     2020                        'normalized_payload' => $session_json
     2021                ];
     2022                $this->prepare_session_created_hook( $session_payload, $id, 'post' );
    17912023
    17922024                return $count_visit;
    … …  
    17942026
    17952027        /**
    1796          * Count visit.
    1797          *
    1798          * @param int $post_id
    1799          *
    1800          * @return int|null
    1801          */
    1802         private function count_visit( $post_id ) {
     2028         * Reset temporary decision state at every public count operation boundary.
     2029         *
     2030         * @return void
     2031         */
     2032        private function reset_pending_count_state() {
     2033                $this->storage = [];
     2034                $this->pending_storage_commit = null;
     2035                $this->pending_session_created = null;
     2036                $this->window_created_candidate = false;
     2037        }
     2038
     2039        /**
     2040         * Drop uncommitted browser state after a filtered or failed writer attempt.
     2041         *
     2042         * @return void
     2043         */
     2044        private function discard_pending_storage() {
     2045                $this->storage = [];
     2046                $this->pending_storage_commit = null;
     2047                $this->pending_session_created = null;
     2048                $this->window_created_candidate = false;
     2049        }
     2050
     2051        /**
     2052         * Commit the prepared browser response/cookie only after writer success.
     2053         *
     2054         * @return void
     2055         */
     2056        private function commit_pending_storage() {
     2057                if ( is_array( $this->pending_storage_commit ) && $this->pending_storage_commit['type'] === 'cookie' ) {
     2058                        $this->emit_cookie_storage( $this->pending_storage_commit );
     2059                        $this->cookie = $this->sanitize_cookies_data( $this->pending_storage_commit['normalized_payload'], 'post' );
     2060                }
     2061
     2062                if ( is_array( $this->pending_session_created ) )
     2063                        do_action( 'pvc_session_created', $this->pending_session_created['state'], $this->pending_session_created['content_id'], $this->pending_session_created['content_type'] );
     2064
     2065                $this->pending_storage_commit = null;
     2066                $this->pending_session_created = null;
     2067                $this->window_created_candidate = false;
     2068        }
     2069
     2070        /**
     2071         * Emit prepared cookie chunks.
     2072         *
     2073         * @param array $commit Prepared cookie commit.
     2074         * @return void
     2075         */
     2076        protected function emit_cookie_storage( $commit ) {
     2077                $cookies_data = $commit['cookies'];
     2078
     2079                foreach ( $cookies_data['name'] as $key => $cookie_chunk_name ) {
     2080                        $this->set_storage_cookie( $cookie_chunk_name, $cookies_data['value'][$key], $cookies_data['expiry'][$key] );
     2081                }
     2082
     2083                $this->clear_stale_cookie_chunks( $commit['cookie_name'], count( $cookies_data['name'] ) );
     2084        }
     2085
     2086        /**
     2087         * A logical Visit can start only from empty or expired valid normalized
     2088         * session storage. Legacy per-content state cannot prove a global start.
     2089         *
     2090         * @param mixed $storage_state Normalized state.
     2091         * @return bool
     2092         */
     2093        private function storage_state_creates_window( $storage_state ) {
     2094                if ( ! $this->use_session_storage_payload_writes() || ! $this->is_normalized_storage_state( $storage_state ) || empty( $storage_state['is_valid'] ) )
     2095                        return false;
     2096
     2097                if ( empty( Post_Views_Counter()->options['general']['time_between_counts']['number'] ) )
     2098                        return false;
     2099
     2100                return $storage_state['format'] === 'empty' || ( $storage_state['format'] === 'session' && ! empty( $storage_state['is_expired'] ) );
     2101        }
     2102
     2103        /**
     2104         * Attach the browser's explicit storage capability result to normalized
     2105         * request state. Older clients omit the flag and retain legacy behavior.
     2106         *
     2107         * @param array $storage_state Normalized request storage.
     2108         * @param mixed $capable Client capability value.
     2109         * @return array
     2110         */
     2111        private function mark_storage_capability( $storage_state, $capable ) {
     2112                $storage_state = is_array( $storage_state ) ? $storage_state : [];
     2113
     2114                if ( isset( $storage_state['_pvc_storage_capable'] ) && $storage_state['_pvc_storage_capable'] === false )
     2115                        return $storage_state;
     2116
     2117                if ( $capable !== null )
     2118                        $storage_state['_pvc_storage_capable'] = is_bool( $capable ) ? $capable : ! in_array( strtolower( (string) $capable ), [ '0', 'false', 'no' ], true );
     2119
     2120                return $storage_state;
     2121        }
     2122
     2123        /**
     2124         * Prepare the deferred session-created hook for the triggering tuple.
     2125         *
     2126         * @param array  $payload Public session payload.
     2127         * @param int    $content_id Content ID.
     2128         * @param string $content_type Content type.
     2129         * @return void
     2130         */
     2131        private function prepare_session_created_hook( $payload, $content_id, $content_type ) {
     2132                if ( ! $this->window_created_candidate )
     2133                        return;
     2134
     2135                $state = $this->normalize_storage_state( $payload, $content_type, 'auto' );
     2136
     2137                if ( ! $this->is_normalized_storage_state( $state ) || empty( $state['is_valid'] ) || $state['format'] !== 'session' )
     2138                        return;
     2139
     2140                $this->pending_session_created = [
     2141                        'state' => $state,
     2142                        'content_id' => (int) $content_id,
     2143                        'content_type' => $content_type
     2144                ];
     2145        }
     2146
     2147        /**
     2148         * Record one countable event for a post.
     2149         *
     2150         * Terminology used across the counting pipeline:
     2151         * - View: an eligible content impression, written to the `count` column.
     2152         * - Visit: the first eligible content item that starts a fixed Count
     2153         *   Interval, written to the `visits` column. At most one Visit exists per
     2154         *   interval, so ancillary tuples in the same request receive Views only.
     2155         * - Legacy hook terminology predates the Visit metric and uses "visit" to
     2156         *   mean a countable View event. `count_visit()`, `pvc_count_visit`,
     2157         *   `pvc_before_count_visit` and `pvc_after_count_visit` all keep that older
     2158         *   meaning and are unchanged public contracts.
     2159         *
     2160         * @param int $post_id Post ID.
     2161         * @param int $visit_increment Visit delta for this event; 0 for Views-only.
     2162         *
     2163         * @return int|null Post ID on success, null when the write did not succeed.
     2164         */
     2165        private function count_visit( $post_id, $visit_increment = 0 ) {
    18032166                // increment amount
    1804                 $increment_amount = (int) apply_filters( 'pvc_views_increment_amount', 1, $post_id, 'post' );
    1805 
    1806                 if ( $increment_amount < 1 )
    1807                         $increment_amount = 1;
     2167                $view_increment = (int) apply_filters( 'pvc_views_increment_amount', 1, $post_id, 'post' );
     2168
     2169                if ( $view_increment < 1 )
     2170                        $view_increment = 1;
    18082171
    18092172                // get day, week, month and year
    … …  
    18142177                        'content_id'    => $post_id,
    18152178                        'content_type'  => 'post',
    1816                         'increment'             => $increment_amount,
    1817                         'visits'                => [
     2179                        'increment'             => $view_increment,
     2180                        'visit_increment' => (int) $visit_increment,
     2181                        'period_buckets' => [
    18182182                                0 => $date[3] . $date[2] . $date[1], // day like 20140324
    18192183                                1 => $date[4] . $date[0],                        // week like 201439
    … …  
    18242188                ];
    18252189
     2190                // Backward compatibility only. Replacements registered on the released
     2191                // `pvc_count_visit_multi` filter read their period buckets from the
     2192                // `visits` key, so it must keep carrying the buckets. It is NOT the new
     2193                // Visit metric; that value travels in `visit_increment`. New consumers
     2194                // should read `period_buckets`.
     2195                $count_data['visits'] = $count_data['period_buckets'];
     2196
    18262197                // attempt to count the visit and check for success
    18272198                if ( call_user_func( apply_filters( 'pvc_count_visit_multi', [ $this, 'count_visit_multi' ] ), $count_data ) ) {
    … …  
    18362207
    18372208        /**
    1838          * Prepare values to be inserted into database.
    1839          *
    1840          * @param array $data
     2209         * Public writer for one complete set of period rows.
     2210         *
     2211         * Accepts the released payload: `content_id`, `content_type`, `increment`
     2212         * (the View delta), `visit_increment` (the Visit delta), `period_buckets`,
     2213         * and the legacy `visits` alias that still carries period buckets. This must
     2214         * fail closed when it is called independently or through a
     2215         * `pvc_count_visit_multi` replacement, so it re-derives Visit availability
     2216         * rather than trusting the caller.
     2217         *
     2218         * @param array $data Count payload.
    18412219         *
    18422220         * @return bool
    … …  
    18472225                        return false;
    18482226
    1849                 $success = true;
    1850 
    1851                 foreach ( $data['visits'] as $type => $period ) {
    1852                         // hit the database directly and check for failure
    1853                         if ( ! $this->db_insert( $data['content_id'], $type, $period, $data['increment'] ) )
    1854                                 $success = false;
    1855                 }
    1856 
    1857                 return $success;
     2227                $period_buckets = isset( $data['period_buckets'] ) && is_array( $data['period_buckets'] ) ? $data['period_buckets'] : ( isset( $data['visits'] ) && is_array( $data['visits'] ) ? $data['visits'] : [] );
     2228
     2229                if ( empty( $period_buckets ) )
     2230                        return false;
     2231
     2232                $visits_delta = isset( $data['visit_increment'] ) ? max( 0, (int) $data['visit_increment'] ) : 0;
     2233                $visits_service = Post_Views_Counter()->visits instanceof Post_Views_Counter_Visits ? Post_Views_Counter()->visits : null;
     2234                $availability = $visits_service ? $visits_service->get_visits_availability() : [ 'writable' => false, 'write_reason' => 'writer_unsupported' ];
     2235
     2236                if ( isset( $availability['write_reason'] ) && $availability['write_reason'] === 'reset_in_progress' )
     2237                        return false;
     2238
     2239                $visit_aware = ! empty( $availability['writable'] );
     2240
     2241                if ( ! $visit_aware )
     2242                        $visits_delta = 0;
     2243
     2244                $rows = [];
     2245
     2246                foreach ( $period_buckets as $type => $period ) {
     2247                        if ( (bool) apply_filters( 'pvc_skip_single_query', false, $data['content_id'], $type, $period, $data['increment'], 'post', $visits_delta ) )
     2248                                continue;
     2249
     2250                        $rows[] = [
     2251                                'id' => (int) $data['content_id'],
     2252                                'type' => (int) $type,
     2253                                'period' => (string) $period,
     2254                                'count' => (int) $data['increment'],
     2255                                'visits' => $visits_delta
     2256                        ];
     2257                }
     2258
     2259                return empty( $rows ) || $this->write_period_rows( $rows, $visit_aware, true );
     2260        }
     2261
     2262        /**
     2263         * Declare the complete Core visit-aware writer capability.
     2264         *
     2265         * @return bool
     2266         */
     2267        public function supports_visit_writes() {
     2268                return true;
     2269        }
     2270
     2271        /**
     2272         * Atomically add one complete set of period rows using prepared values.
     2273         * A views-only fallback deliberately omits the visits column so a pending
     2274         * or failed migration cannot interrupt existing view counting.
     2275         *
     2276         * The physical shape of the shared table is re-verified here rather than
     2277         * trusted from the caller: `visit_aware` is downgraded when the Visits
     2278         * service is absent or its content-column probe is inconclusive, and the
     2279         * The extended `content` column is written only when it is known to exist.
     2280         *
     2281         * @param array $rows Period rows, each with id, type, period, count, visits.
     2282         * @param bool  $visit_aware Whether the verified writer may use visits.
     2283         *
     2284         * @return bool True on success, false when the database write failed.
     2285         */
     2286        private function write_period_rows( $rows, $visit_aware, $use_write_fence = false ) {
     2287                global $wpdb;
     2288
     2289                if ( empty( $rows ) )
     2290                        return true;
     2291
     2292                $has_content = false;
     2293
     2294                if ( Post_Views_Counter()->visits instanceof Post_Views_Counter_Visits ) {
     2295                        $content_status = Post_Views_Counter()->visits->get_shared_content_column_status();
     2296
     2297                        if ( $content_status === null )
     2298                                $visit_aware = false;
     2299                        else
     2300                                $has_content = $content_status;
     2301                } else {
     2302                        $visit_aware = false;
     2303                }
     2304
     2305                $write_fence = $use_write_fence && Post_Views_Counter()->visits instanceof Post_Views_Counter_Visits ? Post_Views_Counter()->visits->get_write_fence_token() : null;
     2306
     2307                if ( $use_write_fence && $write_fence === false )
     2308                        return false;
     2309
     2310                $columns = [ '`id`', '`type`', '`period`', '`count`' ];
     2311                $placeholders = [ '%d', '%d', '%s', '%d' ];
     2312
     2313                if ( $visit_aware ) {
     2314                        $columns[] = '`visits`';
     2315                        $placeholders[] = '%d';
     2316                }
     2317
     2318                if ( $has_content ) {
     2319                        $columns[] = '`content`';
     2320                        $placeholders[] = '%d';
     2321                }
     2322
     2323                $value_groups = [];
     2324                $values = [];
     2325
     2326                foreach ( $rows as $row ) {
     2327                        $value_groups[] = '(' . implode( ', ', $placeholders ) . ')';
     2328                        $values[] = (int) $row['id'];
     2329                        $values[] = (int) $row['type'];
     2330                        $values[] = (string) $row['period'];
     2331                        $values[] = (int) $row['count'];
     2332
     2333                        if ( $visit_aware )
     2334                                $values[] = max( 0, (int) $row['visits'] );
     2335
     2336                        if ( $has_content )
     2337                                $values[] = 0;
     2338                }
     2339
     2340                $target_table = '`' . $wpdb->prefix . 'post_views`';
     2341                $updates = [ '`count` = ' . $target_table . '.`count` + VALUES(`count`)' ];
     2342
     2343                if ( $visit_aware )
     2344                        $updates[] = '`visits` = ' . $target_table . '.`visits` + VALUES(`visits`)';
     2345
     2346                $base_values = $values;
     2347                $generation = is_array( $write_fence ) && isset( $write_fence['generation'] ) ? (int) $write_fence['generation'] : null;
     2348                $attempts = $write_fence === null ? 1 : 3;
     2349                $result = false;
     2350
     2351                for ( $attempt = 0; $attempt < $attempts; $attempt++ ) {
     2352                        $values = $base_values;
     2353
     2354                        if ( is_array( $write_fence ) ) {
     2355                                $selects = [];
     2356
     2357                                foreach ( $rows as $row_index => $row ) {
     2358                                        $aliases = $row_index === 0 ? ' AS `id`, %d AS `type`, %s AS `period`, %d AS `count`' : ', %d, %s, %d';
     2359                                        $select = 'SELECT %d' . $aliases;
     2360                                        $column_count = 4;
     2361
     2362                                        if ( $visit_aware ) {
     2363                                                $select .= $row_index === 0 ? ', %d AS `visits`' : ', %d';
     2364                                                $column_count++;
     2365                                        }
     2366
     2367                                        if ( $has_content ) {
     2368                                                $select .= $row_index === 0 ? ', %d AS `content`' : ', %d';
     2369                                                $column_count++;
     2370                                        }
     2371
     2372                                        $selects[] = $select;
     2373                                }
     2374
     2375                                $predicate = ! empty( $write_fence['exists'] ) ? "EXISTS (SELECT 1 FROM `{$wpdb->options}` WHERE `option_name` = %s AND BINARY `option_value` = BINARY %s)" : "NOT EXISTS (SELECT 1 FROM `{$wpdb->options}` WHERE `option_name` = %s)";
     2376                                $values[] = Post_Views_Counter_Visits::OPTION_NAME;
     2377
     2378                                if ( ! empty( $write_fence['exists'] ) )
     2379                                        $values[] = $write_fence['raw'];
     2380
     2381                                $sql = 'INSERT INTO ' . $target_table . ' (' . implode( ', ', $columns ) . ') SELECT * FROM (' . implode( ' UNION ALL ', $selects ) . ') AS `pvc_rows` WHERE ' . $predicate . ' ON DUPLICATE KEY UPDATE ' . implode( ', ', $updates );
     2382                        } else
     2383                                $sql = 'INSERT INTO ' . $target_table . ' (' . implode( ', ', $columns ) . ') VALUES ' . implode( ', ', $value_groups ) . ' ON DUPLICATE KEY UPDATE ' . implode( ', ', $updates );
     2384
     2385                        $result = $wpdb->query( $wpdb->prepare( $sql, $values ) );
     2386
     2387                        if ( $result !== 0 || ! is_array( $write_fence ) )
     2388                                break;
     2389
     2390                        $next_fence = Post_Views_Counter()->visits->get_write_fence_token( true );
     2391
     2392                        if ( $next_fence === false || (int) $next_fence['generation'] !== $generation )
     2393                                return false;
     2394
     2395                        $write_fence = $next_fence;
     2396                }
     2397
     2398                if ( $result === false || $result === 0 ) {
     2399                        error_log( 'Post Views Counter: shared counter write failed.' );
     2400                        return false;
     2401                }
     2402
     2403                if ( class_exists( 'Post_Views_Counter_Visits_Query' ) )
     2404                        Post_Views_Counter_Visits_Query::invalidate_read_cache();
     2405
     2406                return true;
    18582407        }
    18592408
    … …  
    18772426                $data = apply_filters( 'pvc_delete_post_views_where_clause', $data, $post_id );
    18782427
    1879                 $wpdb->delete( $wpdb->prefix . 'post_views', $data['where'], $data['format'] );
     2428                $deleted = $wpdb->delete( $wpdb->prefix . 'post_views', $data['where'], $data['format'] );
     2429
     2430                if ( $deleted > 0 && class_exists( 'Post_Views_Counter_Visits_Query' ) )
     2431                        Post_Views_Counter_Visits_Query::invalidate_read_cache();
    18802432        }
    18812433
    … …  
    19362488         */
    19372489        public function flush_cache_to_db() {
    1938                 // get keys
     2490                // The extended writer owns the four-part shared-row queue and its Visit
     2491                // companion. Keep existing base update/email preflight callers compatible by delegating
     2492                // through the active writer rather than parsing extended keys as base keys.
     2493                if ( function_exists( 'Post_Views_Counter_Pro' ) ) {
     2494                        $pro = Post_Views_Counter_Pro();
     2495
     2496                        if ( is_object( $pro ) && isset( $pro->counter ) && is_object( $pro->counter ) && $pro->counter !== $this && method_exists( $pro->counter, 'flush_cache_to_db' ) ) {
     2497                                $result = $pro->counter->flush_cache_to_db( 'pvc' );
     2498                                $this->last_cache_flush_status = method_exists( $pro->counter, 'get_cache_flush_status' ) ? $pro->counter->get_cache_flush_status() : ( $result ? 'completed' : 'failed' );
     2499                                return $result;
     2500                        }
     2501                }
     2502
     2503                $lock_token = $this->acquire_core_flush_lock();
     2504
     2505                if ( $lock_token === false ) {
     2506                        $this->last_cache_flush_status = 'busy';
     2507                        return false;
     2508                }
     2509
     2510                try {
     2511                        $result = $this->flush_core_cache_owner( $lock_token );
     2512                        $this->last_cache_flush_status = $result ? 'completed' : ( $this->core_flush_lock_owned( $lock_token ) ? 'failed' : 'ownership_lost' );
     2513                        return $result;
     2514                } finally {
     2515                        $this->release_core_flush_lock( $lock_token );
     2516                }
     2517        }
     2518
     2519        /**
     2520         * Return the outcome of this request's most recent cache flush attempt.
     2521         *
     2522         * @return string idle|busy|pending|failed|ownership_lost|completed
     2523         */
     2524        public function get_cache_flush_status() {
     2525                return $this->last_cache_flush_status;
     2526        }
     2527
     2528        /**
     2529         * Drain the legacy Core queue while the caller owns the drain lock.
     2530         *
     2531         * Database commit precedes cache acknowledgement. A crash can replay a
     2532         * committed snapshot, but it cannot silently discard acknowledged work.
     2533         *
     2534         * @return bool
     2535         */
     2536        private function flush_core_cache_owner( $lock_token ) {
     2537                global $wpdb;
     2538
    19392539                $key_names = wp_cache_get( 'cached_key_names', 'pvc' );
    1940 
    1941                 if ( ! $key_names )
    1942                         $key_names = [];
    1943                 else {
    1944                         // create an array out of a string that's stored in the cache
    1945                         $key_names = explode( '|', $key_names );
    1946                 }
    1947 
    1948                 // any data?
    1949                 if ( ! empty( $key_names ) ) {
    1950                         foreach ( $key_names as $key_name ) {
    1951                                 // get values stored within the key name itself
    1952                                 list( $id, $type, $period ) = explode( '.', $key_name );
    1953 
    1954                                 // get the cached count value
    1955                                 $count = wp_cache_get( $key_name, 'pvc' );
    1956 
    1957                                 // store cached value in the database
    1958                                 $this->db_prepare_insert( $id, $type, $period, $count );
    1959 
    1960                                 // clear the cache key we just flushed
    1961                                 wp_cache_delete( $key_name, 'pvc' );
     2540                $key_names = is_string( $key_names ) ? array_filter( explode( '|', $key_names ), 'strlen' ) : (array) $key_names;
     2541                $key_names = array_values( array_unique( array_filter( $key_names, 'strlen' ) ) );
     2542
     2543                if ( empty( $key_names ) ) {
     2544                        wp_cache_delete( 'last-flush', 'pvc' );
     2545                        return true;
     2546                }
     2547
     2548                $batch_size = max( 1, absint( apply_filters( 'pvc_core_flush_batch_rows', 100 ) ) );
     2549
     2550                foreach ( array_chunk( $key_names, $batch_size ) as $key_batch ) {
     2551                        $lock_ttl = $this->get_core_flush_lock_ttl();
     2552
     2553                        if ( ! $this->refresh_core_flush_lock( $lock_token, $lock_ttl ) )
     2554                                return false;
     2555
     2556                        $snapshots = [];
     2557                        $this->db_insert_values = [];
     2558
     2559                        foreach ( $key_batch as $key_name ) {
     2560                                $key = $this->parse_core_queue_key( $key_name );
     2561
     2562                                if ( $key === null )
     2563                                        continue;
     2564
     2565                                $count = (int) wp_cache_get( $key_name, 'pvc' );
     2566
     2567                                if ( $count <= 0 )
     2568                                        continue;
     2569
     2570                                $snapshots[ $key_name ] = $count;
     2571
     2572                                // Legacy three-part Core queue entries (id.type.period) carry a
     2573                                // View delta only; they have no Visit companion and predate the
     2574                                // Visits metric entirely. A Visit cannot be reconstructed from
     2575                                // them, so passing visit_increment = 0 here and committing with
     2576                                // visit_aware = false below is intentional: it preserves the
     2577                                // queued Views without inventing Visit data.
     2578                                if ( ! $this->db_prepare_insert( $key['id'], $key['type'], $key['period'], $count, 0, 'post' ) ) {
     2579                                        $this->db_insert_values = [];
     2580                                        return false;
     2581                                }
    19622582                        }
    19632583
    1964                         // flush values to database
    1965                         $this->db_commit_insert();
    1966 
    1967                         // delete the key holding the list
    1968                         wp_cache_delete( 'cached_key_names', 'pvc' );
    1969                 }
    1970 
    1971                 // remove last flush
    1972                 wp_cache_delete( 'last-flush', 'pvc' );
     2584                        // Renew immediately before acquiring the reset fence. The snapshot is
     2585                        // legacy generation 0 work, so only a generation read ordered by this
     2586                        // fence may authorize its database commit.
     2587                        if ( ! $this->refresh_core_flush_lock( $lock_token, $lock_ttl ) ) {
     2588                                $this->db_insert_values = [];
     2589                                return false;
     2590                        }
     2591
     2592                        // Build every helper-dependent part of the Views-only upsert before the
     2593                        // measurement fence. Cache backends and extension callbacks are not safe
     2594                        // while MySQL permits this connection to use only two locked tables.
     2595                        $commit_sql = $this->prepare_core_legacy_insert_sql();
     2596
     2597                        if ( $commit_sql === false ) {
     2598                                $this->db_insert_values = [];
     2599                                return false;
     2600                        }
     2601
     2602                        $fenced_renewal = $this->prepare_core_fenced_lock_renewal( $lock_token, $lock_ttl );
     2603
     2604                        if ( $fenced_renewal === false ) {
     2605                                $this->db_insert_values = [];
     2606                                return false;
     2607                        }
     2608
     2609                        $fenced_generation_read = $this->prepare_core_fenced_generation_read();
     2610
     2611                        $visits = Post_Views_Counter()->visits;
     2612
     2613                        if ( ! $visits instanceof Post_Views_Counter_Visits || ! $visits->begin_measurement_fence() ) {
     2614                                $this->db_insert_values = [];
     2615                                return false;
     2616                        }
     2617
     2618                        $generation = false;
     2619                        $committed = false;
     2620                        $fence_result = [ 'unlocked' => false, 'flushed' => false ];
     2621
     2622                        try {
     2623                                // This fenced region permits only the prebuilt options UPDATE, an
     2624                                // uncached options SELECT, and the prebuilt post_views upsert. Do not
     2625                                // call WordPress/cache helpers here: extension callbacks may query an
     2626                                // unrelated table while LOCK TABLES is active.
     2627                                if ( ! $this->renew_core_flush_lock_fenced( $fenced_renewal ) ) {
     2628                                        $this->db_insert_values = [];
     2629                                        return false;
     2630                                }
     2631
     2632                                $generation = $this->get_core_queue_generation_fenced( $fenced_generation_read );
     2633
     2634                                // A missing, malformed, or otherwise unusable durable state cannot
     2635                                // prove this legacy snapshot belongs before a reset. Fail closed and
     2636                                // leave the snapshot recoverable for a later retry.
     2637                                if ( $generation === false ) {
     2638                                        $this->db_insert_values = [];
     2639                                        return false;
     2640                                }
     2641
     2642                                if ( $generation === 0 ) {
     2643                                        if ( $commit_sql !== '' ) {
     2644                                                $result = $wpdb->query( $commit_sql );
     2645
     2646                                                if ( $result === false || $result === 0 )
     2647                                                        return false;
     2648
     2649                                                $this->db_insert_values = [];
     2650                                                $committed = true;
     2651                                        }
     2652                                } else
     2653                                        // Any supported Core scalar was published before the first reset.
     2654                                        // Retire it without callbacks or writes; retaining its cache entry
     2655                                        // makes a later retry prove the same generation before doing anything.
     2656                                        $this->db_insert_values = [];
     2657                        } finally {
     2658                                $fence_result = $visits->end_measurement_fence();
     2659                        }
     2660
     2661                        // No cache acknowledgement or derived-cache retirement is valid until
     2662                        // the restrictive SQL fence has been confirmed released and its FIFO
     2663                        // publication work has completed.
     2664                        if ( empty( $fence_result['unlocked'] ) || empty( $fence_result['flushed'] ) )
     2665                                return false;
     2666
     2667                        // The legacy commit changes the Views numerator used by the Visit-derived
     2668                        // cache, so retire that cache only after the table fence is released.
     2669                        if ( $committed && class_exists( 'Post_Views_Counter_Visits_Query' ) )
     2670                                Post_Views_Counter_Visits_Query::invalidate_read_cache();
     2671
     2672                        // The acknowledgement is outside the table fence, but must still prove
     2673                        // that this flusher owns the exact snapshot before changing cache state.
     2674                        if ( ! $this->refresh_core_flush_lock( $lock_token, $lock_ttl ) )
     2675                                return false;
     2676
     2677                        $completed = [];
     2678
     2679                        foreach ( $snapshots as $key_name => $count ) {
     2680                                $remainder = wp_cache_decr( $key_name, $count, 'pvc' );
     2681
     2682                                if ( $remainder === false )
     2683                                        return false;
     2684
     2685                                if ( (int) $remainder === 0 )
     2686                                        $completed[] = $key_name;
     2687                        }
     2688
     2689                        if ( ! $this->cleanup_core_queue_membership( $completed ) )
     2690                                return false;
     2691                }
     2692
     2693                if ( wp_cache_delete( 'last-flush', 'pvc' ) === false && wp_cache_get( 'last-flush', 'pvc' ) !== false )
     2694                        return false;
    19732695
    19742696                return true;
    … …  
    19762698
    19772699        /**
    1978          * Insert or update views count.
    1979          *
    1980          * @global object $wpdb
    1981          *
    1982          * @param int $id
    1983          * @param int $type
    1984          * @param string $period
    1985          * @param int $count
    1986          *
     2700         * Build the legacy Views-only upsert before acquiring the measurement fence.
     2701         *
     2702         * @return string|false Prepared SQL, an empty string, or false on an unsafe shape.
     2703         */
     2704        private function prepare_core_legacy_insert_sql() {
     2705                global $wpdb;
     2706
     2707                if ( empty( $this->db_insert_values ) )
     2708                        return '';
     2709
     2710                $has_content = false;
     2711                $visits = Post_Views_Counter()->visits;
     2712
     2713                if ( $visits instanceof Post_Views_Counter_Visits )
     2714                        $has_content = $visits->get_shared_content_column_status() === true;
     2715
     2716                $columns = [ '`id`', '`type`', '`period`', '`count`' ];
     2717                $placeholders = [ '%d', '%d', '%s', '%d' ];
     2718
     2719                if ( $has_content ) {
     2720                        $columns[] = '`content`';
     2721                        $placeholders[] = '%d';
     2722                }
     2723
     2724                $value_groups = [];
     2725                $values = [];
     2726
     2727                foreach ( $this->db_insert_values as $row ) {
     2728                        $value_groups[] = '(' . implode( ', ', $placeholders ) . ')';
     2729                        $values[] = (int) $row['id'];
     2730                        $values[] = (int) $row['type'];
     2731                        $values[] = (string) $row['period'];
     2732                        $values[] = (int) $row['count'];
     2733
     2734                        if ( $has_content )
     2735                                $values[] = 0;
     2736                }
     2737
     2738                $table = '`' . $wpdb->prefix . 'post_views`';
     2739                $sql = 'INSERT INTO ' . $table . ' (' . implode( ', ', $columns ) . ') VALUES ' . implode( ', ', $value_groups ) . ' ON DUPLICATE KEY UPDATE `count` = ' . $table . '.`count` + VALUES(`count`)';
     2740
     2741                return $wpdb->prepare( $sql, $values );
     2742        }
     2743
     2744        /**
     2745         * Capture an owner-checked lease replacement before the measurement fence.
     2746         *
     2747         * @param string $token Lock token.
     2748         * @param int    $ttl Lease duration in seconds.
     2749         * @return string|false Prebuilt constrained options UPDATE or false.
     2750         */
     2751        private function prepare_core_fenced_lock_renewal( $token, $ttl ) {
     2752                global $wpdb;
     2753
     2754                $name = 'post_views_counter_core_flush_lock';
     2755                $raw = $wpdb->get_var( $wpdb->prepare( "SELECT `option_value` FROM `{$wpdb->options}` WHERE `option_name` = %s", $name ) );
     2756                $current = $raw === null ? false : maybe_unserialize( $raw );
     2757
     2758                if ( ! is_array( $current ) || empty( $current['token'] ) || ! hash_equals( (string) $current['token'], (string) $token ) )
     2759                        return false;
     2760
     2761                $replacement = [
     2762                        'token' => $token,
     2763                        'expires_at' => max( time() + $ttl, (int) $current['expires_at'] + 1 )
     2764                ];
     2765
     2766                return $wpdb->prepare( "UPDATE `{$wpdb->options}` SET `option_value` = %s WHERE `option_name` = %s AND BINARY `option_value` = BINARY %s", maybe_serialize( $replacement ), $name, $raw );
     2767        }
     2768
     2769        /**
     2770         * Renew the Core lease while the measurement fence permits only options SQL.
     2771         *
     2772         * @param string $sql Prebuilt constrained UPDATE statement.
    19872773         * @return bool
    19882774         */
    1989         private function db_insert( $id, $type, $period, $count ) {
     2775        private function renew_core_flush_lock_fenced( $sql ) {
    19902776                global $wpdb;
    19912777
    1992                 // skip single query?
    1993                 if ( (bool) apply_filters( 'pvc_skip_single_query', false, $id, $type, $period, $count, 'post' ) )
    1994                         return true; // consider skipped as "successful" for this context
    1995 
    1996                 $result = $wpdb->query( $wpdb->prepare( 'INSERT INTO ' . $wpdb->prefix . 'post_views (`id`, `type`, `period`, `count`) VALUES (%d, %d, %s, %d) ON DUPLICATE KEY UPDATE count = count + %d', $id, $type, $period, $count, $count ) );
    1997 
    1998                 // check for query failure
    1999                 if ( $result === false ) {
    2000                         // log the error for debugging
    2001                         error_log( sprintf( 'Post Views Counter: Failed to insert/update views for ID %d, type %d, period %s. MySQL error: %s', $id, $type, $period, $wpdb->last_error ) );
     2778                return $wpdb->query( $sql ) === 1;
     2779        }
     2780
     2781        /**
     2782         * Build the durable queue-generation SELECT before the measurement fence.
     2783         *
     2784         * @return string
     2785         */
     2786        private function prepare_core_fenced_generation_read() {
     2787                global $wpdb;
     2788
     2789                return $wpdb->prepare( "SELECT `option_value` FROM `{$wpdb->options}` WHERE `option_name` = %s", Post_Views_Counter_Visits::OPTION_NAME );
     2790        }
     2791
     2792        /**
     2793         * Read the durable queue generation while the measurement fence is held.
     2794         *
     2795         * Missing state is the released generation-0 format. Existing state must be
     2796         * the exact current durable shape required by get_queue_generation().
     2797         *
     2798         * @param string $sql Prebuilt constrained options SELECT.
     2799         * @return int|false
     2800         */
     2801        private function get_core_queue_generation_fenced( $sql ) {
     2802                global $wpdb;
     2803
     2804                $wpdb->last_error = '';
     2805                $raw = $wpdb->get_var( $sql );
     2806
     2807                if ( $wpdb->last_error !== '' )
    20022808                        return false;
    2003                 }
    2004 
     2809
     2810                if ( $raw === null )
     2811                        return 0;
     2812
     2813                // Never instantiate serialized objects under the table fence. A poisoned
     2814                // __wakeup()/autoload callback could issue SQL for a table MySQL has not
     2815                // locked, so only a non-instantiating exact array decode is permitted here.
     2816                if ( ! is_string( $raw ) || $raw === '' || substr( $raw, 0, 2 ) !== 'a:' || substr( $raw, -1 ) !== '}' )
     2817                        return false;
     2818
     2819                set_error_handler( [ $this, 'suppress_core_fenced_decode_diagnostic' ] );
     2820
     2821                try {
     2822                        $state = unserialize( $raw, [ 'allowed_classes' => false ] );
     2823                } finally {
     2824                        restore_error_handler();
     2825                }
     2826
     2827                $visits = Post_Views_Counter()->visits;
     2828
     2829                if ( ! $visits instanceof Post_Views_Counter_Visits || ! $visits->is_current_normal_state_raw( $state ) || serialize( $state ) !== $raw )
     2830                        return false;
     2831
     2832                return $state['queue_generation'];
     2833        }
     2834
     2835        /**
     2836         * Contain unserialize diagnostics within the restricted fence without calling
     2837         * a third-party error handler that could issue arbitrary SQL.
     2838         *
     2839         * @return bool
     2840         */
     2841        private function suppress_core_fenced_decode_diagnostic() {
    20052842                return true;
    20062843        }
    20072844
    20082845        /**
    2009          * Prepare bulk insert or update views count.
    2010          *
    2011          * @param int $id
    2012          * @param int $type
    2013          * @param string $period
    2014          * @param int $count
    2015          *
     2846         * Parse one legacy base queue key without accepting the four-part extended
     2847         * shape.
     2848         *
     2849         * @param mixed $key_name Cache key.
     2850         * @return array|null
     2851         */
     2852        private function parse_core_queue_key( $key_name ) {
     2853                $raw_key_name = (string) $key_name;
     2854                $key_name = sanitize_text_field( $raw_key_name );
     2855
     2856                if ( $key_name !== $raw_key_name )
     2857                        return null;
     2858
     2859                $parts = explode( '.', $key_name );
     2860
     2861                if ( count( $parts ) !== 3 )
     2862                        return null;
     2863
     2864                if ( ! ctype_digit( $parts[0] ) || ! ctype_digit( $parts[1] ) )
     2865                        return null;
     2866
     2867                $id = (int) $parts[0];
     2868                $type = (int) $parts[1];
     2869                $period = sanitize_key( $parts[2] );
     2870
     2871                if ( $period !== $parts[2] || $id < 1 || $type < 0 || $type > 4 || $period === '' )
     2872                        return null;
     2873
     2874                if ( ! $this->is_valid_core_queue_period( (int) $type, $period ) )
     2875                        return null;
     2876
     2877                return [ 'id' => $id, 'type' => (int) $type, 'period' => $period ];
     2878        }
     2879
     2880        /**
     2881         * Validate the calendar identity encoded by a Core queue period.
     2882         *
     2883         * @param int    $type Storage type.
     2884         * @param string $period Storage period.
     2885         * @return bool
     2886         */
     2887        private function is_valid_core_queue_period( $type, $period ) {
     2888                if ( $type === 4 )
     2889                        return $period === 'total';
     2890
     2891                if ( $type === 0 && preg_match( '/^\d{8}$/', $period ) )
     2892                        return checkdate( (int) substr( $period, 4, 2 ), (int) substr( $period, 6, 2 ), (int) substr( $period, 0, 4 ) );
     2893
     2894                if ( $type === 1 && preg_match( '/^(\d{4})(\d{2})$/', $period, $matches ) ) {
     2895                        $year = (int) $matches[1];
     2896                        $week = (int) $matches[2];
     2897
     2898                        if ( $week < 1 || $week > 53 )
     2899                                return false;
     2900
     2901                        $date = ( new DateTimeImmutable( 'now', new DateTimeZone( 'UTC' ) ) )->setISODate( $year, $week, 1 );
     2902                        return $date->format( 'oW' ) === $period;
     2903                }
     2904
     2905                if ( $type === 2 && preg_match( '/^(\d{4})(\d{2})$/', $period, $matches ) )
     2906                        return (int) $matches[1] > 0 && (int) $matches[2] >= 1 && (int) $matches[2] <= 12;
     2907
     2908                return $type === 3 && preg_match( '/^\d{4}$/', $period ) && (int) $period > 0;
     2909        }
     2910
     2911        /**
     2912         * Remove completed Core scalars and only their membership entries.
     2913         *
     2914         * @param array $completed Completed keys.
     2915         * @return bool
     2916         */
     2917        private function cleanup_core_queue_membership( $completed ) {
     2918                if ( empty( $completed ) )
     2919                        return true;
     2920
     2921                $live = wp_cache_get( 'cached_key_names', 'pvc' );
     2922                $live = is_string( $live ) ? array_filter( explode( '|', $live ), 'strlen' ) : (array) $live;
     2923                $remaining = [];
     2924
     2925                foreach ( $live as $key_name ) {
     2926                        if ( in_array( $key_name, $completed, true ) && (int) wp_cache_get( $key_name, 'pvc' ) === 0 ) {
     2927                                if ( ! wp_cache_delete( $key_name, 'pvc' ) && wp_cache_get( $key_name, 'pvc' ) !== false )
     2928                                        return false;
     2929
     2930                                continue;
     2931                        }
     2932
     2933                        $remaining[] = $key_name;
     2934                }
     2935
     2936                $remaining = array_values( array_unique( array_filter( $remaining, 'strlen' ) ) );
     2937
     2938                if ( empty( $remaining ) )
     2939                        return wp_cache_delete( 'cached_key_names', 'pvc' ) || wp_cache_get( 'cached_key_names', 'pvc' ) === false;
     2940
     2941                return (bool) wp_cache_set( 'cached_key_names', implode( '|', $remaining ), 'pvc' );
     2942        }
     2943
     2944        /**
     2945         * Acquire the Core drain lock with token-conditioned stale takeover.
     2946         *
     2947         * @return string|false
     2948         */
     2949        private function acquire_core_flush_lock() {
     2950                $name = 'post_views_counter_core_flush_lock';
     2951                $token = wp_generate_uuid4();
     2952                $lock = [ 'token' => $token, 'expires_at' => time() + max( 30, absint( apply_filters( 'pvc_core_flush_lock_ttl', 300 ) ) ) ];
     2953
     2954                if ( add_option( $name, $lock, '', false ) )
     2955                        return $token;
     2956
     2957                $current = get_option( $name, [] );
     2958
     2959                if ( is_array( $current ) && ! empty( $current['expires_at'] ) && (int) $current['expires_at'] > time() )
     2960                        return false;
     2961
     2962                return $this->replace_core_lock_if_unchanged( $name, $current, $lock ) ? $token : false;
     2963        }
     2964
     2965        /**
     2966         * Refresh the Core drain lease only while the token still owns it.
     2967         *
     2968         * @param string   $token Lock token.
     2969         * @param int|null $ttl Optional pre-evaluated lease duration.
     2970         * @return bool
     2971         */
     2972        private function refresh_core_flush_lock( $token, $ttl = null ) {
     2973                $name = 'post_views_counter_core_flush_lock';
     2974                $current = get_option( $name, [] );
     2975
     2976                if ( ! is_array( $current ) || empty( $current['token'] ) || ! hash_equals( (string) $current['token'], (string) $token ) )
     2977                        return false;
     2978
     2979                $replacement = [
     2980                        'token' => $token,
     2981                        'expires_at' => max(
     2982                                time() + ( $ttl === null ? $this->get_core_flush_lock_ttl() : $ttl ),
     2983                                (int) $current['expires_at'] + 1
     2984                        )
     2985                ];
     2986
     2987                return $this->replace_core_lock_if_unchanged( $name, $current, $replacement );
     2988        }
     2989
     2990        /**
     2991         * Resolve the Core drain lease duration before entering a table fence.
     2992         *
     2993         * @return int
     2994         */
     2995        private function get_core_flush_lock_ttl() {
     2996                return max( 30, absint( apply_filters( 'pvc_core_flush_lock_ttl', 300 ) ) );
     2997        }
     2998
     2999        /**
     3000         * Check current Core drain ownership.
     3001         *
     3002         * @param string $token Lock token.
     3003         * @return bool
     3004         */
     3005        private function core_flush_lock_owned( $token ) {
     3006                $current = get_option( 'post_views_counter_core_flush_lock', [] );
     3007
     3008                return is_array( $current ) && ! empty( $current['token'] ) && hash_equals( (string) $current['token'], (string) $token );
     3009        }
     3010
     3011        /**
     3012         * Release the Core drain lock only while still its owner.
     3013         *
     3014         * @param string $token Owner token.
    20163015         * @return void
    20173016         */
    2018         private function db_prepare_insert( $id, $type, $period, $count = 1 ) {
    2019                 // cast count
     3017        private function release_core_flush_lock( $token ) {
     3018                global $wpdb;
     3019
     3020                $name = 'post_views_counter_core_flush_lock';
     3021                $current = get_option( $name, [] );
     3022
     3023                if ( ! is_array( $current ) || empty( $current['token'] ) || ! hash_equals( (string) $current['token'], (string) $token ) )
     3024                        return;
     3025
     3026                $result = $wpdb->query( $wpdb->prepare( "DELETE FROM `{$wpdb->options}` WHERE `option_name` = %s AND `option_value` = %s", $name, maybe_serialize( $current ) ) );
     3027
     3028                if ( $result === 1 )
     3029                        wp_cache_delete( $name, 'options' );
     3030        }
     3031
     3032        /**
     3033         * Replace an expired Core drain lock without delete-then-add takeover.
     3034         *
     3035         * @param string $name Option name.
     3036         * @param mixed  $expected Expected lock.
     3037         * @param array  $replacement Replacement lock.
     3038         * @return bool
     3039         */
     3040        private function replace_core_lock_if_unchanged( $name, $expected, $replacement ) {
     3041                global $wpdb;
     3042
     3043                $result = $wpdb->query( $wpdb->prepare( "UPDATE `{$wpdb->options}` SET `option_value` = %s WHERE `option_name` = %s AND `option_value` = %s", maybe_serialize( $replacement ), $name, maybe_serialize( $expected ) ) );
     3044
     3045                if ( $result === 1 )
     3046                        wp_cache_delete( $name, 'options' );
     3047
     3048                return $result === 1;
     3049        }
     3050
     3051        /**
     3052         * Buffer one period row for the next multi-row commit.
     3053         *
     3054         * Rows are accumulated in `$db_insert_values` and written by
     3055         * `db_commit_insert()`. A skipped row is reported as success because the
     3056         * caller asked for it to be omitted, not because a write failed.
     3057         *
     3058         * @param int    $id Content ID.
     3059         * @param int    $type Period type (0-4).
     3060         * @param string $period Period bucket value.
     3061         * @param int    $count View increment for this row.
     3062         * @param int    $visit_increment Visit increment for this row; 0 for Views-only.
     3063         * @param string $content_type Content type passed to `pvc_skip_single_query`.
     3064         *
     3065         * @return bool True when the row was buffered or deliberately skipped.
     3066         */
     3067        private function db_prepare_insert( $id, $type, $period, $count = 1, $visit_increment = 0, $content_type = 'post' ) {
    20203068                $count = (int) $count;
    2021 
    2022                 if ( ! $count )
    2023                         $count = 1;
    2024 
    2025                 // any queries?
    2026                 if ( ! empty( $this->db_insert_values ) )
    2027                         $this->db_insert_values .= ', ';
    2028 
    2029                 // append insert queries
    2030                 $this->db_insert_values .= sprintf( '(%d, %d, "%s", %d)', $id, $type, $period, $count );
    2031 
    2032                 if ( strlen( $this->db_insert_values ) > 25000 )
    2033                         $this->db_commit_insert();
    2034         }
    2035 
    2036         /**
    2037          * Insert accumulated values to database.
    2038          *
    2039          * @global object $wpdb
    2040          *
    2041          * @return int|bool
    2042          */
    2043         private function db_commit_insert() {
    2044                 global $wpdb;
    2045 
     3069                $visit_increment = max( 0, (int) $visit_increment );
     3070
     3071                if ( (bool) apply_filters( 'pvc_skip_single_query', false, $id, $type, $period, $count, $content_type, $visit_increment ) )
     3072                        return true;
     3073
     3074                $this->db_insert_values[] = [
     3075                        'id' => (int) $id,
     3076                        'type' => (int) $type,
     3077                        'period' => (string) $period,
     3078                        'count' => $count,
     3079                        'visits' => $visit_increment
     3080                ];
     3081
     3082                return true;
     3083        }
     3084
     3085        /**
     3086         * Write every buffered period row in one prepared multi-row upsert.
     3087         *
     3088         * @param bool $visit_aware Whether the verified writer may use the `visits`
     3089         *                          column. Callers that cannot reconstruct Visits
     3090         *                          must pass false so the column is left untouched.
     3091         *
     3092         * @return bool True on success, false when there was nothing buffered or the
     3093         *              write failed. The buffer is only cleared on success.
     3094         */
     3095        private function db_commit_insert( $visit_aware = false ) {
    20463096                if ( empty( $this->db_insert_values ) )
    20473097                        return false;
    20483098
    2049                 $result = $wpdb->query(
    2050                         "INSERT INTO " . $wpdb->prefix . "post_views (id, type, period, count)
    2051                         VALUES " . $this->db_insert_values . "
    2052                         ON DUPLICATE KEY UPDATE count = count + VALUES(count)"
    2053                 );
    2054 
    2055                 $this->db_insert_values = '';
     3099                $result = $this->write_period_rows( $this->db_insert_values, $visit_aware );
     3100
     3101                if ( $result !== false )
     3102                        $this->db_insert_values = [];
    20563103
    20573104                return $result;
    … …  
    23553402                                        'storage_data_all' => [
    23563403                                                'default'                        => ''
     3404                                        ],
     3405                                        'storage_capable' => [
     3406                                                'sanitize_callback' => 'rest_sanitize_boolean'
    23573407                                        ]
    23583408                                ] )
    … …  
    23703420                                'args'                                   => apply_filters( 'pvc_rest_api_get_post_views_args', [
    23713421                                        'id' => [
    2372                                                 'default'                       => 0,
    2373                                                 'sanitize_callback'     => [ $this, 'validate_rest_api_data' ]
    2374                                         ]
     3422                                                'default'                       => 0
     3423                                        ],
     3424                                        'metric' => [],
     3425                                        'period' => [ 'default' => 'total' ],
     3426                                        'type' => [ 'default' => 'post' ]
    23753427                                ] )
    23763428                        ]
    … …  
    23863438         */
    23873439        public function get_post_views_rest_api( $request ) {
    2388                 return pvc_get_post_views( $request->get_param( 'id' ) );
     3440                $raw_metric = $request->get_param( 'metric' );
     3441                $raw_id = $request->get_param( 'id' );
     3442
     3443                if ( ! is_scalar( $raw_id ) || ! preg_match( '/^[1-9]\d*(,[1-9]\d*)*$/', (string) $raw_id ) )
     3444                        return new WP_Error( 'pvc_invalid_metric', __( 'The requested PVC metric is not supported.', 'post-views-counter' ), [ 'status' => 400 ] );
     3445
     3446                $ids = array_values( array_filter( array_unique( array_map( 'absint', explode( ',', (string) $raw_id ) ) ) ) );
     3447
     3448                // Preserve the exact legacy scalar response when named metric mode is absent.
     3449                if ( $raw_metric === null )
     3450                        return pvc_get_post_views( $ids );
     3451
     3452                if ( ! is_scalar( $raw_metric ) )
     3453                        return new WP_Error( 'pvc_invalid_metric', __( 'The requested PVC metric is not supported.', 'post-views-counter' ), [ 'status' => 400 ] );
     3454
     3455                $metric = (string) $raw_metric;
     3456
     3457                if ( count( $ids ) > self::REST_MAX_TARGETS )
     3458                        return new WP_Error( 'pvc_too_many_targets', __( 'Too many PVC metric targets were requested.', 'post-views-counter' ), [ 'status' => 400 ] );
     3459                $raw_period = $request->get_param( 'period' );
     3460                $period = is_scalar( $raw_period ) ? (string) $raw_period : '';
     3461                $raw_type = $request->get_param( 'type' );
     3462                $type = is_scalar( $raw_type ) ? (string) $raw_type : '';
     3463
     3464                // Visits Cleanup Addendum -- 2026-09-26 (O2): the named read is Views-only
     3465                if ( $metric !== 'views' || $type !== 'post' || ! preg_match( '/^[a-z0-9_-]+$/', $period ) )
     3466                        return new WP_Error( 'pvc_invalid_metric', __( 'The requested PVC metric is not supported.', 'post-views-counter' ), [ 'status' => 400 ] );
     3467
     3468                $normalized = function_exists( 'pvc_normalize_views_period' ) ? pvc_normalize_views_period( $period ) : null;
     3469
     3470                if ( $normalized === null )
     3471                        $availability = [ 'readable' => false, 'read_reason' => 'invalid_period' ];
     3472                else {
     3473                        $content_status = isset( Post_Views_Counter()->visits ) ? Post_Views_Counter()->visits->get_shared_content_column_status() : false;
     3474                        $availability = $content_status === null ? [ 'readable' => false, 'read_reason' => 'schema_probe_failed' ] : [ 'readable' => true, 'read_reason' => 'ready' ];
     3475                }
     3476
     3477                $raw_values = [];
     3478
     3479                if ( ! empty( $availability['readable'] ) )
     3480                        $raw_values = $this->get_named_post_view_values( $ids, $period, $normalized );
     3481
     3482                $values = [];
     3483                $formatted = [];
     3484                $display_options = Post_Views_Counter()->options['display'];
     3485
     3486                foreach ( $ids as $id ) {
     3487                        $key = 'post:' . $id . ':' . $period;
     3488                        $value = empty( $availability['readable'] ) ? null : ( isset( $raw_values[$id] ) ? $raw_values[$id] : 0 );
     3489                        $values[$key] = $value;
     3490
     3491                        // An unreadable View is not a stored zero, so it never reaches numeric
     3492                        // formatting; it renders the unavailable dash instead.
     3493                        $display_value = $value === null ? '&mdash;' : ( ! empty( $display_options['use_format'] ) ? number_format_i18n( $value ) : (string) $value );
     3494                        $filtered_value = apply_filters( 'pvc_post_views_number_format', $display_value, $id );
     3495
     3496                        $filtered_value = is_scalar( $filtered_value ) ? (string) $filtered_value : $display_value;
     3497                        $formatted[$key] = html_entity_decode( wp_strip_all_tags( $filtered_value ), ENT_QUOTES, 'UTF-8' );
     3498                }
     3499
     3500                // Anonymous dynamic payloads expose only the stable raw-read result. They
     3501                // never expose schema internals or other internal state metadata.
     3502                $public_availability = [
     3503                        'readable' => ! empty( $availability['readable'] ),
     3504                        'reason' => isset( $availability['read_reason'] ) ? sanitize_key( $availability['read_reason'] ) : 'unavailable'
     3505                ];
     3506
     3507                return [
     3508                        'metric' => $metric,
     3509                        'type' => $type,
     3510                        'period' => $period,
     3511                        'values' => $values,
     3512                        'formatted' => $formatted,
     3513                        'availability' => $public_availability
     3514                ];
     3515        }
     3516
     3517        /**
     3518         * Read named post Views in one grouped query while preserving value filters.
     3519         *
     3520         * @param int[]  $ids Post IDs.
     3521         * @param string $period Requested period.
     3522         * @param array  $normalized Canonical period.
     3523         * @return int[] Values keyed by post ID.
     3524         */
     3525        private function get_named_post_view_values( $ids, $period, $normalized ) {
     3526                if ( $this->has_custom_post_views_period_filter() ) {
     3527                        $values = [];
     3528
     3529                        foreach ( $ids as $id )
     3530                                $values[$id] = pvc_get_post_views( $id, $period );
     3531
     3532                        return $values;
     3533                }
     3534
     3535                global $wpdb;
     3536                $params = $ids;
     3537                $where = [ 'id IN (' . implode( ', ', array_fill( 0, count( $ids ), '%d' ) ) . ')', 'type = %d' ];
     3538                $params[] = (int) $normalized['type'];
     3539
     3540                if ( (int) $normalized['type'] === 4 ) {
     3541                        $where[] = 'period = %s';
     3542                        $params[] = 'total';
     3543                } elseif ( $normalized['period_from'] === $normalized['period_to'] ) {
     3544                        $where[] = 'CAST(period AS SIGNED) = %d';
     3545                        $params[] = (int) $normalized['period_from'];
     3546                } else {
     3547                        $where[] = 'CAST(period AS SIGNED) BETWEEN %d AND %d';
     3548                        $params[] = (int) $normalized['period_from'];
     3549                        $params[] = (int) $normalized['period_to'];
     3550                }
     3551
     3552                $content_status = isset( Post_Views_Counter()->visits ) ? Post_Views_Counter()->visits->get_shared_content_column_status() : false;
     3553
     3554                if ( $content_status === null )
     3555                        return [];
     3556
     3557                if ( $content_status )
     3558                        $where[] = 'content = 0';
     3559
     3560                $sql = $wpdb->prepare( 'SELECT id, SUM(count) AS views FROM `' . $wpdb->prefix . 'post_views` WHERE ' . implode( ' AND ', $where ) . ' GROUP BY id', $params );
     3561                $cache_key = md5( $sql );
     3562                $rows = wp_cache_get( $cache_key, 'pvc-get_post_views' );
     3563
     3564                if ( $rows === false ) {
     3565                        $rows = $wpdb->get_results( $sql, ARRAY_A );
     3566                        $rows = is_array( $rows ) ? $rows : [];
     3567                        wp_cache_add( $cache_key, $rows, 'pvc-get_post_views', absint( apply_filters( 'pvc_object_cache_expire', 300 ) ) );
     3568                }
     3569
     3570                $values = array_fill_keys( $ids, 0 );
     3571
     3572                foreach ( $rows as $row ) {
     3573                        $id = isset( $row['id'] ) ? absint( $row['id'] ) : 0;
     3574
     3575                        if ( array_key_exists( $id, $values ) )
     3576                                $values[$id] = isset( $row['views'] ) ? (int) $row['views'] : 0;
     3577                }
     3578
     3579                foreach ( $values as $id => $value )
     3580                        $values[$id] = (int) apply_filters( 'pvc_get_post_views', $value, $id, $period, [] );
     3581
     3582                return $values;
     3583        }
     3584
     3585        /**
     3586         * Check for period filters beyond the canonical post discriminator.
     3587         *
     3588         * @return bool
     3589         */
     3590        private function has_custom_post_views_period_filter() {
     3591                global $wp_filter;
     3592
     3593                if ( empty( $wp_filter['pvc_get_post_views_period_where'] ) || ! isset( $wp_filter['pvc_get_post_views_period_where']->callbacks ) )
     3594                        return false;
     3595
     3596                foreach ( $wp_filter['pvc_get_post_views_period_where']->callbacks as $callbacks ) {
     3597                        foreach ( $callbacks as $callback ) {
     3598                                if ( ! isset( $callback['function'] ) || $callback['function'] !== 'pvc_get_post_views_period_where' )
     3599                                        return true;
     3600                        }
     3601                }
     3602
     3603                return false;
    23893604        }
    23903605
  • post-views-counter/tags/1.8.0/includes/class-cron.php

    r3556892 r3734102  
    3030         * @global object $wpdb
    3131         *
    32          * @return void
     32         * @return bool
    3333         */
    3434        public function reset_counts() {
    3535                global $wpdb;
    3636
     37                $days = $this->get_reset_interval_days();
     38
     39                // a zero or invalid interval keeps data regardless of age
     40                if ( $days === 0 )
     41                        return true;
     42
     43                // default where clause
     44                $where = [ 'type = 0', 'CAST( period AS SIGNED ) < CAST( ' . date( 'Ymd', strtotime( '-' . $days . ' days' ) ) . ' AS SIGNED)' ];
     45
     46                // update where clause
     47                $where = apply_filters( 'pvc_reset_counts_where_clause', $where );
     48                // Keep the established selection predicate. Retention deletes stored rows
     49                // only; it never changes raw-read or derived eligibility boundaries.
     50                $result = $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'post_views WHERE ' . implode( ' AND ', $where ) );
     51
     52                if ( $result === false )
     53                        return false;
     54
     55                if ( class_exists( 'Post_Views_Counter_Visits_Query' ) )
     56                        Post_Views_Counter_Visits_Query::invalidate_read_cache();
     57
     58                return true;
     59        }
     60
     61        /**
     62         * Get the cleanup interval in days.
     63         *
     64         * @return int Days, or 0 when the interval is zero or invalid
     65         */
     66        private function get_reset_interval_days() {
    3767                $counter = [
    3868                        'days'          => 1,
    … …  
    4575                $pvc = Post_Views_Counter();
    4676
    47                 // default where clause
    48                 $where = [ 'type = 0', 'CAST( period AS SIGNED ) < CAST( ' . date( 'Ymd', strtotime( '-' . ( (int) ( $counter[$pvc->options['general']['reset_counts']['type']] * $pvc->options['general']['reset_counts']['number'] ) ) . ' days' ) ) . ' AS SIGNED)' ];
     77                $interval = isset( $pvc->options['general']['reset_counts'] ) && is_array( $pvc->options['general']['reset_counts'] ) ? $pvc->options['general']['reset_counts'] : [];
     78                $type = isset( $interval['type'] ) && is_string( $interval['type'] ) ? $interval['type'] : '';
     79                $number = isset( $interval['number'] ) && is_numeric( $interval['number'] ) ? (int) $interval['number'] : 0;
    4980
    50                 // update where clause
    51                 $where = apply_filters( 'pvc_reset_counts_where_clause', $where );
     81                if ( ! isset( $counter[$type] ) || $number < 1 || $number > 999999 )
     82                        return 0;
    5283
    53                 // delete views
    54                 $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'post_views WHERE ' . implode( ' AND ', $where ) );
     84                return $counter[$type] * $number;
    5585        }
    5686
    … …  
    90120                $pvc = Post_Views_Counter();
    91121
    92                 // set wp cron task
    93                 if ( $pvc->options['general']['cron_run'] ) {
     122                // set wp cron task, only for a valid positive interval
     123                if ( $pvc->options['general']['cron_run'] && $this->get_reset_interval_days() > 0 ) {
    94124                        // not set or need to be updated?
    95125                        if ( ! wp_next_scheduled( 'pvc_reset_counts' ) || $pvc->options['general']['cron_update'] ) {
  • post-views-counter/tags/1.8.0/includes/class-dashboard.php

    r3537950 r3734102  
    1111class Post_Views_Counter_Dashboard {
    1212
     13        /**
     14         * The insight notice list this Core renders.
     15         *
     16         * A paired chart item carries a box that the dashboard script fills from a
     17         * widget payload's `insight_notices` on every successful load, and
     18         * Post_Views_Counter_Dashboard_Comparison is available. An extension
     19         * detects it on the loaded dashboard object's class, never by version.
     20         *
     21         * @since 1.7.16
     22         *
     23         * @var int
     24         */
     25        const NOTICES_FORMAT = 1;
     26
     27        /**
     28         * Fewest Views on each side of the comparison notice. This Core constant
     29         * is separate from the Traffic Signal floor.
     30         *
     31         * @internal
     32         *
     33         * @since 1.7.16
     34         *
     35         * @var int
     36         */
     37        const FREE_NOTICE_MIN_VIEWS = 10;
     38
    1339        private $widget_items = [];
     40
     41        /**
     42         * Current time of the period comparison; null is now. Set only by tests.
     43         *
     44         * @var DateTimeInterface|null
     45         */
     46        private $now = null;
    1447
    1548        /**
    … …  
    2154                // actions
    2255                add_action( 'admin_init', [ $this, 'init_admin_dashboard' ] );
     56                add_action( 'pvc_dashboard_widget_content_top', [ $this, 'render_widget_headline' ] );
    2357        }
    2458
    … …  
    76110
    77111                // scripts
    78                 wp_enqueue_script( 'pvc-admin-dashboard', POST_VIEWS_COUNTER_URL . '/js/admin-dashboard.js', [ 'jquery', 'pvc-chartjs' ], $pvc->defaults['version'], true );
     112                wp_enqueue_script( 'pvc-admin-dashboard', POST_VIEWS_COUNTER_URL . '/js/admin-dashboard.js', [ 'jquery', 'pvc-chartjs', 'pvc-metric-toggle' ], $pvc->defaults['version'], true );
    79113
    80114                // prepare script data
    … …  
    106140                                'title'                 => __( 'Post Views', 'post-views-counter' ),
    107141                                'description'   => __( 'Displays a chart of most viewed post types.', 'post-views-counter' ),
    108                                 'content'               => '<canvas id="pvc-post-views-chart" height="' . (int) $this->calculate_canvas_size( Post_Views_Counter()->options['general']['post_types_count'] ) . '"></canvas>',
     142                                // The first-party chart no longer derives a canvas height from
     143                                // the legend size. It lives in a fixed CSS wrapper so the plotted
     144                                // graph is the same height in every metric mode while the
     145                                // controls and the dataset legend grow the widget around it.
     146                                'content'               => '<div class="pvc-chart-wrapper"><canvas id="pvc-post-views-chart"></canvas></div>',
     147                                'metrics'               => true,
    109148                                'position'              => 2
    110149                        ],
    … …  
    228267                ];
    229268
     269                // Only first-party paired widgets opt in. A widget added through
     270                // pvc_dashboard_widget_items by an older extension or a third party never
     271                // gets the controls, so it keeps its exact current markup.
     272                $has_metrics = ! empty( $item['metrics'] );
     273
    230274                ob_start(); ?>
    231275
    … …  
    239283                        </div>
    240284                        <div class="pvc-accordion-content">
    241                                 <div class="pvc-dashboard-container loading">
     285                                <div class="pvc-dashboard-container loading<?php echo $has_metrics ? ' pvc-dashboard-container-chart' : ''; ?>">
    242286                                        <div class="pvc-dashboard-content-top">
    243287                                                <?php do_action( 'pvc_dashboard_widget_content_top', $item['id'] ); ?>
    244288                                        </div>
     289                                        <?php if ( $has_metrics ) : ?>
     290                                                <div class="pvc-dashboard-metric-controls">
     291                                                        <div class="pvc-dashboard-metrics" data-pvc-widget="<?php echo esc_attr( $item['id'] ); ?>" role="group" aria-label="<?php esc_attr_e( 'Chart metrics', 'post-views-counter' ); ?>"></div>
     292                                                        <p class="pvc-dashboard-visits-unavailable" id="pvc-<?php echo esc_attr( $item['id'] ); ?>-visits-unavailable" hidden></p>
     293                                                </div>
     294                                        <?php endif; ?>
    245295                                        <div class="pvc-data-container">
    246296                                                <?php echo wp_kses( $item['content'], $allowed_html ); ?>
    … …  
    248298                                                <span class="spinner"></span>
    249299                                        </div>
     300                                        <?php if ( $has_metrics ) : ?>
     301                                                <div class="pvc-dashboard-legend" data-pvc-widget="<?php echo esc_attr( $item['id'] ); ?>" role="group" aria-label="<?php esc_attr_e( 'Chart datasets', 'post-views-counter' ); ?>"></div>
     302                                                <div class="pvc-widget-insights" id="pvc-<?php echo esc_attr( $item['id'] ); ?>-widget-insights" aria-live="polite" hidden></div>
     303                                        <?php endif; ?>
     304                                        <?php $this->render_widget_insights( $item['id'] ); ?>
    250305                                        <div class="pvc-dashboard-content-bottom">
    251306                                                <div class="pvc-date-nav pvc-months">
    … …  
    267322
    268323        /**
     324         * Render monthly period controls and the chart's Views headline when no extension owns the header.
     325         *
     326         * @param string $widget_id Widget item ID.
     327         * @return void
     328         */
     329        public function render_widget_headline( $widget_id ) {
     330                if ( ! in_array( $widget_id, [ 'post-views', 'post-most-viewed' ], true ) || class_exists( 'Post_Views_Counter_Pro' ) )
     331                        return;
     332                ?>
     333                <div class="pvc-dashboard-headline">
     334                        <?php if ( $widget_id === 'post-views' ) : ?>
     335                                <div class="pvc-summary"><span class="pvc-data-count" aria-live="polite"></span></div>
     336                        <?php endif; ?>
     337                        <div class="pvc-date-select button-group <?php echo esc_attr( $widget_id ); ?>" role="group" aria-label="<?php echo esc_attr( $widget_id === 'post-views' ? __( 'Chart period', 'post-views-counter' ) : __( 'Views Period', 'post-views-counter' ) ); ?>">
     338                                <button type="button" class="button button-small button-secondary" data-datenav="yearly" title="<?php esc_attr_e( 'Yearly', 'post-views-counter' ); ?>" aria-label="<?php esc_attr_e( 'Yearly', 'post-views-counter' ); ?>" aria-pressed="false" disabled><span>Y</span></button>
     339                                <button type="button" class="button button-small button-primary" data-datenav="monthly" title="<?php esc_attr_e( 'Monthly', 'post-views-counter' ); ?>" aria-label="<?php esc_attr_e( 'Monthly', 'post-views-counter' ); ?>" aria-pressed="true">M</button>
     340                                <button type="button" class="button button-small button-secondary" data-datenav="weekly" title="<?php esc_attr_e( 'Weekly', 'post-views-counter' ); ?>" aria-label="<?php esc_attr_e( 'Weekly', 'post-views-counter' ); ?>" aria-pressed="false" disabled><span>W</span></button>
     341                        </div>
     342                </div>
     343                <?php
     344        }
     345
     346        /**
     347         * Fire the insights slot of a dashboard widget item.
     348         *
     349         * A slot between the chart legend and the date navigation, fired for every
     350         * item. Core prints no markup of its own there; an extension decides by the
     351         * widget ID whether anything belongs to it.
     352         *
     353         * @since 1.7.16
     354         *
     355         * @param string $widget_id Widget item ID.
     356         * @return void
     357         */
     358        public function render_widget_insights( $widget_id ) {
     359                do_action( 'pvc_dashboard_widget_content_insights', $widget_id );
     360        }
     361
     362        /**
     363         * Get the allow-listed metric display modes.
     364         *
     365         * @return array
     366         */
     367        public function get_metric_modes() {
     368                return [ 'both', 'views', 'visits' ];
     369        }
     370
     371        /**
     372         * Get the stored metric display mode of one widget.
     373         *
     374         * This is the preferred mode, not the effective one. A temporary Visits
     375         * outage never rewrites it.
     376         *
     377         * @param string $widget_id Dashboard widget identifier.
     378         * @param int    $user_id User ID.
     379         * @return string
     380         */
     381        public function get_widget_metric_mode( $widget_id, $user_id = 0 ) {
     382                $modes = $this->get_dashboard_user_options( $user_id, 'metrics' );
     383                $mode = isset( $modes[$widget_id] ) ? $modes[$widget_id] : '';
     384
     385                // both metrics are on for a user with no stored preference
     386                return in_array( $mode, $this->get_metric_modes(), true ) ? $mode : 'both';
     387        }
     388
     389        /**
     390         * Get the readable state of the dashboard Visit series.
     391         *
     392         * Raw dashboard buckets ignore derived_since entirely: a readable slot with
     393         * no stored row is zero, and only an unreadable series is withheld.
     394         *
     395         * @return array
     396         */
     397        public function get_dashboard_visits_availability() {
     398                $availability = isset( Post_Views_Counter()->visits ) ? Post_Views_Counter()->visits->get_visits_availability() : [ 'readable' => false, 'read_reason' => 'schema_failed' ];
     399                $readable = ! empty( $availability['readable'] );
     400
     401                return [
     402                        'readable'      => $readable,
     403                        'reason'        => $readable ? 'ready' : ( isset( $availability['read_reason'] ) ? $availability['read_reason'] : 'read_failed' )
     404                ];
     405        }
     406
     407        /**
     408         * Build the ordered chart buckets of one dashboard views_query.
     409         *
     410         * The order and the grain deliberately mirror what pvc_get_views() builds
     411         * for the same query with `date=>views`, so a Visit series lines up with the
     412         * Views series it is paired with. Weekly and monthly charts use daily
     413         * buckets; a year uses monthly buckets.
     414         *
     415         * @param array $views_query Dashboard views query.
     416         * @return array|null Storage type and ordered period keys, or null.
     417         */
     418        public function get_chart_buckets( $views_query ) {
     419                $views_query = is_array( $views_query ) ? $views_query : [];
     420
     421                $year = ! empty( $views_query['year'] ) ? str_pad( (int) $views_query['year'], 4, '0', STR_PAD_LEFT ) : '';
     422                $month = ! empty( $views_query['month'] ) ? str_pad( (int) $views_query['month'], 2, '0', STR_PAD_LEFT ) : '';
     423                $week = ! empty( $views_query['week'] ) ? str_pad( (int) $views_query['week'], 2, '0', STR_PAD_LEFT ) : '';
     424                $day = ! empty( $views_query['day'] ) ? str_pad( (int) $views_query['day'], 2, '0', STR_PAD_LEFT ) : '';
     425
     426                if ( $year === '' )
     427                        return null;
     428
     429                $periods = [];
     430
     431                // year and week: the seven days of that ISO week
     432                if ( $week !== '' ) {
     433                        $date = new DateTime( $year . 'W' . $week );
     434
     435                        for ( $i = 1; $i <= 7; $i++ ) {
     436                                $periods[] = $date->format( 'Ymd' );
     437                                $date->modify( '+1 days' );
     438                        }
     439
     440                        return [ 'type' => 0, 'periods' => $periods ];
     441                }
     442
     443                if ( $month !== '' ) {
     444                        // year, month and day: one daily bucket
     445                        if ( $day !== '' )
     446                                return [ 'type' => 0, 'periods' => [ $year . $month . $day ] ];
     447
     448                        // year and month: every day of that month
     449                        $date = new DateTime( $year . '-' . $month . '-01' );
     450                        $last = (int) $date->format( 't' );
     451
     452                        for ( $i = 1; $i <= $last; $i++ ) {
     453                                $periods[] = $year . $month . str_pad( $i, 2, '0', STR_PAD_LEFT );
     454                        }
     455
     456                        return [ 'type' => 0, 'periods' => $periods ];
     457                }
     458
     459                // year: twelve monthly buckets
     460                for ( $i = 1; $i <= 12; $i++ ) {
     461                        $periods[] = $year . str_pad( $i, 2, '0', STR_PAD_LEFT );
     462                }
     463
     464                return [ 'type' => 2, 'periods' => $periods ];
     465        }
     466
     467        /**
     468         * Key a paired chart's Views read by the Visits read generation.
     469         *
     470         * Views reads are cached by their SQL for the cache lifetime, while the
     471         * Visit series drawn beside them is keyed by the Visits read generation,
     472         * which every committed write, cleanup or reset advances. A trailing SQL
     473         * comment carrying that key gives the chart's Views the same freshness
     474         * boundary, so both series retire together. Nothing is flushed, and every
     475         * other Views read keeps its key.
     476         *
     477         * @internal Hooked at PHP_INT_MAX on a Views SQL filter only while a paired
     478         *           chart reads its Views, including extension-provided content types.
     479         *
     480         * @param string $query Views SQL.
     481         * @return string
     482         */
     483        public static function tag_paired_views_sql( $query ) {
     484                if ( ! is_string( $query ) || $query === '' || ! class_exists( 'Post_Views_Counter_Visits_Query' ) )
     485                        return $query;
     486
     487                // an md5 hex key, so the comment cannot close early
     488                return $query . ' /* pvc-visits-read ' . Post_Views_Counter_Visits_Query::get_read_cache_key( 'paired_views' ) . ' */';
     489        }
     490
     491        /**
     492         * Read the paired post Visit series for every counted post type at once.
     493         *
     494         * This is an internal dashboard reader, not a public helper: it exists so a
     495         * widget can draw Visits beside Views without adding one Visit query per
     496         * dimension. Exactly one bounded, cached query runs per widget request no
     497         * matter how many post types are counted, and a database failure is
     498         * reported as unavailable rather than measured zero.
     499         *
     500         * @global object $wpdb
     501         *
     502         * @param array $args Post types, the dashboard views_query, and the period.
     503         * @return array Availability plus visits keyed by post type and period.
     504         */
     505        public function get_paired_visit_series( $args = [] ) {
     506                global $wpdb;
     507
     508                $args = wp_parse_args( $args, [
     509                        'post_types'    => [],
     510                        'views_query'   => [],
     511                        'period'                => '',
     512                        'lang'                  => ''
     513                ] );
     514
     515                $availability = $this->get_dashboard_visits_availability();
     516                $buckets = $this->get_chart_buckets( $args['views_query'] );
     517                $post_types = array_values( array_filter( array_map( 'sanitize_key', (array) $args['post_types'] ) ) );
     518                $empty = [ 'available' => false, 'reason' => $availability['reason'], 'periods' => [], 'values' => [] ];
     519
     520                if ( ! $availability['readable'] )
     521                        return $empty;
     522
     523                if ( $buckets === null )
     524                        return array_merge( $empty, [ 'reason' => 'invalid_period' ] );
     525
     526                // no counted post type means nothing to pair, but the series is still
     527                // readable: every bucket is a measured zero
     528                if ( empty( $post_types ) )
     529                        return [ 'available' => true, 'reason' => 'ready', 'periods' => $buckets['periods'], 'values' => [] ];
     530
     531                // bucket keys share their row type's width (Ymd daily, Ym monthly), so the
     532                // (type, period) key serves the range instead of a cast scan
     533                $periods = array_map( 'strval', $buckets['periods'] );
     534                sort( $periods, SORT_STRING );
     535                $where = [ 'pvc.`type` = %d', 'pvc.`period` BETWEEN %s AND %s' ];
     536                $params = [ $buckets['type'], reset( $periods ), end( $periods ) ];
     537
     538                // tables with a content dimension identify post rows as 0
     539                if ( pvc_post_views_has_content_column() )
     540                        $where[] = 'pvc.`content` = 0';
     541
     542                $where[] = 'wpp.post_type IN (' . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ')';
     543                $params = array_merge( $params, $post_types );
     544
     545                $query = $wpdb->prepare(
     546                        'SELECT pvc.`period`, wpp.post_type, SUM(pvc.`visits`) AS post_visits
     547                        FROM `' . $wpdb->prefix . 'post_views` pvc
     548                        INNER JOIN `' . $wpdb->posts . '` wpp ON wpp.ID = pvc.id
     549                        WHERE ' . implode( ' AND ', $where ) . '
     550                        GROUP BY pvc.`period`, wpp.post_type',
     551                        $params
     552                );
     553
     554                // Additive companion to pvc_get_views_query_sql. It exists so the
     555                // translation integrations can scope the paired Visit read exactly as
     556                // they already scope the Views read; the `wpp` alias is deliberate.
     557                $query = apply_filters( 'pvc_dashboard_visit_series_sql', $query, $args, $buckets );
     558
     559                // keyed by the Visits generation, so every committed write, cleanup or state
     560                // change retires it, not only its lifetime
     561                $cache_key = Post_Views_Counter_Visits_Query::get_read_cache_key( 'dashboard_visit_series|' . $query );
     562                $values = wp_cache_get( $cache_key, Post_Views_Counter_Visits_Query::VISITS_CACHE_GROUP );
     563
     564                if ( $values === false ) {
     565                        $wpdb->last_error = '';
     566                        $rows = $wpdb->get_results( $query, ARRAY_A );
     567
     568                        // a failed read is unavailable, never a measured zero
     569                        if ( $wpdb->last_error !== '' )
     570                                return array_merge( $empty, [ 'reason' => 'database_error' ] );
     571
     572                        $values = [];
     573
     574                        foreach ( (array) $rows as $row ) {
     575                                if ( ! isset( $row['post_type'], $row['period'] ) )
     576                                        continue;
     577
     578                                $values[(string) $row['post_type']][(string) $row['period']] = isset( $row['post_visits'] ) ? (int) $row['post_visits'] : 0;
     579                        }
     580
     581                        wp_cache_add( $cache_key, $values, Post_Views_Counter_Visits_Query::VISITS_CACHE_GROUP, absint( apply_filters( 'pvc_object_cache_expire', 300 ) ) );
     582                }
     583
     584                return [ 'available' => true, 'reason' => 'ready', 'periods' => $buckets['periods'], 'values' => $values ];
     585        }
     586
     587        /**
     588         * Flatten one dimension of a paired Visit series into chart order.
     589         *
     590         * @param array  $series Result of get_paired_visit_series().
     591         * @param string $dimension Dimension key, or an empty string for the total.
     592         * @return array
     593         */
     594        private function get_visit_series_values( $series, $dimension = '' ) {
     595                $values = [];
     596
     597                foreach ( $series['periods'] as $period ) {
     598                        $value = 0;
     599
     600                        if ( $dimension === '' ) {
     601                                foreach ( $series['values'] as $dimension_values ) {
     602                                        $value += isset( $dimension_values[$period] ) ? (int) $dimension_values[$period] : 0;
     603                                }
     604                        } else {
     605                                $value = isset( $series['values'][$dimension][$period] ) ? (int) $series['values'][$dimension][$period] : 0;
     606                        }
     607
     608                        // a readable bucket with no stored row is a measured zero
     609                        $values[] = $value;
     610                }
     611
     612                return $values;
     613        }
     614
     615        /**
     616         * Describe the two metric buttons of a paired dashboard widget.
     617         *
     618         * Core owns the shared metric control and therefore its copy, so both
     619         * plugins show the same two translated labels.
     620         *
     621         * @param bool   $visits_available Whether the Visit series can be drawn.
     622         * @param string $reason Availability reason.
     623         * @return array
     624         */
     625        public function get_metric_buttons( $visits_available, $reason = 'ready' ) {
     626                $pvc = Post_Views_Counter();
     627                $views_style = $pvc->functions->get_metric_dataset_style( 'views', 'line' );
     628                $visits_style = $pvc->functions->get_metric_dataset_style( 'visits', 'line' );
     629
     630                return [
     631                        [
     632                                'key'           => 'views',
     633                                'label'         => _x( 'Views', 'dashboard chart metric', 'post-views-counter' ),
     634                                'available'     => true,
     635                                'reason'        => 'ready',
     636                                'color'         => $views_style['borderColor'],
     637                                'dashed'        => false
     638                        ],
     639                        [
     640                                'key'           => 'visits',
     641                                'label'         => _x( 'Visits', 'dashboard chart metric', 'post-views-counter' ),
     642                                'available'     => (bool) $visits_available,
     643                                'reason'        => $reason,
     644                                'color'         => $visits_style['borderColor'],
     645                                'dashed'        => true
     646                        ]
     647                ];
     648        }
     649
     650        /**
    269651         * Render dashboard widget with post views.
    270652         *
    … …  
    278660                $period = isset( $_POST['period'] ) && ! empty( $_POST['period'] ) ? preg_replace( '/[^a-z0-9_|]/', '', $_POST['period'] ) : apply_filters( 'pvc_dashboard_widget_default_period', 'this_month', 'post-views' );
    279661
     662                echo wp_json_encode( $this->get_post_views_chart_data( $period ) );
     663                exit;
     664        }
     665
     666        /**
     667         * Build the paired post views widget payload.
     668         *
     669         * Separated from the AJAX handler so the payload can be asserted without
     670         * the transport. The response contract, filters and arguments are unchanged.
     671         *
     672         * @param string $period Requested period.
     673         * @return array
     674         */
     675        public function get_post_views_chart_data( $period ) {
     676                $period = preg_replace( '/[^a-z0-9_|]/', '', (string) $period );
     677
    280678                // get post types
    281679                $post_types = Post_Views_Counter()->options['general']['post_types_count'];
    282                
     680
    283681                // empty options?
    284682                if ( empty( $post_types ) || ! is_array( $post_types ) )
    … …  
    290688                // get dashboard user options
    291689                $user_options = $this->get_dashboard_user_options( get_current_user_id(), 'post_types' );
    292                
     690
    293691                // empty options?
    294692                if ( empty( $user_options ) || ! is_array( $user_options ) )
    … …  
    300698                // get colors
    301699                $colors = Post_Views_Counter()->functions->get_colors();
    302                
     700
    303701                // parameters to be used in filter
    304702                $args = [
    … …  
    308706                        'user_options'  => $user_options
    309707                ];
    310                
     708
    311709                $data = [
    312710                        'widget'        => 'post-views',
     711                        // The flat design object is the legacy third-party contract. First
     712                        // party datasets carry their own style and no longer read it.
    313713                        'design'        => [
    314714                                'fill'                                  => true,
    … …  
    322722                        ],
    323723                        'data'          => [
    324                                 'datasets'      => [
    325                                         [
    326                                                 'label'                 => __( 'Total Views', 'post-views-counter' ),
    327                                                 'post_type'     => '_pvc_total_views',
    328                                                 'hidden'                => in_array( '_pvc_total_views', $user_options, true ),
    329                                                 'data'                  => []
    330                                         ]
    331                                 ]
     724                                'datasets'      => []
    332725                        ],
    333726                ];
    334                
     727
    335728                // convert period
    336729                $time = pvc_period2timestamp( $period );
    … …  
    357750                $data['dates'] = $this->generate_months( $time );
    358751
     752                // the Views query this request actually reads, so the paired Visit read
     753                // uses the same grain an extension may have switched it to
     754                $visits_views_query = [
     755                        'year'                  => $year,
     756                        'month'                 => $month,
     757                        'hide_empty'    => true
     758                ];
     759
    359760                // generate chart data
    360761                $sum = [];
     762                $views_datasets = [];
     763                $dimensions = [];
     764
     765                // whether every dimension reads the query Core built
     766                $core_chart = true;
     767
     768                $views_datasets[] = [
     769                        'label'                         => __( 'Total Views', 'post-views-counter' ),
     770                        'metric'                        => 'views',
     771                        'post_type'                     => '_pvc_total_views',
     772                        'dimension_hidden'      => in_array( '_pvc_total_views', $user_options, true ),
     773                        'hidden'                        => in_array( '_pvc_total_views', $user_options, true ),
     774                        'data'                          => []
     775                ];
    361776
    362777                // any post types?
    … …  
    366781
    367782                        $post_type_data = [];
     783
     784                        // the Views reads share the Visits series' freshness boundary
     785                        $tag_views = ! empty( $this->get_dashboard_visits_availability()['readable'] );
     786
     787                        if ( $tag_views )
     788                                add_filter( 'pvc_get_views_query_sql', [ __CLASS__, 'tag_paired_views_sql' ], PHP_INT_MAX );
    368789
    369790                        foreach ( $post_types as $id => $post_type ) {
    … …  
    376797                                        $label = $post_type_obj->labels->name;
    377798
    378                                 $data['data']['datasets'][$id]['label'] = $label;
    379                                 $data['data']['datasets'][$id]['post_type'] = $post_type;
    380                                 $data['data']['datasets'][$id]['hidden'] = in_array( $post_type, $user_options, true );
    381                                 $data['data']['datasets'][$id]['data'] = [];
     799                                $dimensions[$id] = [ 'key' => $post_type, 'label' => $label ];
     800
     801                                $views_datasets[$id] = [
     802                                        'label'                         => $label,
     803                                        'metric'                        => 'views',
     804                                        'post_type'                     => $post_type,
     805                                        'dimension_hidden'      => in_array( $post_type, $user_options, true ),
     806                                        'hidden'                        => in_array( $post_type, $user_options, true ),
     807                                        'data'                          => []
     808                                ];
     809
     810                                $core_query_args = [
     811                                        'fields'                => 'date=>views',
     812                                        'post_type'             => $post_type,
     813                                        'views_query'   => [
     814                                                'year'                  => $year,
     815                                                'month'                 => $month,
     816                                                'hide_empty'    => true
     817                                        ]
     818                                ];
     819
     820                                $query_args = apply_filters( 'pvc_dashboard_post_views_query_args', $core_query_args, $period );
     821
     822                                // any dimension a filter changed (its content, grain, language or
     823                                // anything else) makes a chart the Free notice cannot describe
     824                                if ( $query_args !== $core_query_args )
     825                                        $core_chart = false;
     826
     827                                // every dimension reads the same grain, so the paired Visit read
     828                                // follows whatever the filter settled on
     829                                if ( isset( $query_args['views_query'] ) && is_array( $query_args['views_query'] ) )
     830                                        $visits_views_query = $query_args['views_query'];
     831
     832                                if ( isset( $query_args['lang'] ) )
     833                                        $args['lang'] = $query_args['lang'];
    382834
    383835                                // get month views
    384                                 $post_type_data[$id] = array_values(
    385                                         pvc_get_views( apply_filters( 'pvc_dashboard_post_views_query_args',
    386                                                 [
    387                                                         'fields'                => 'date=>views',
    388                                                         'post_type'             => $post_type,
    389                                                         'views_query'   => [
    390                                                                 'year'                  => $year,
    391                                                                 'month'                 => $month,
    392                                                                 'hide_empty'    => true
    393                                                         ]
    394                                                 ], $period
    395                                         ) )
    396                                 );
     836                                $post_type_data[$id] = array_values( pvc_get_views( $query_args ) );
    397837                        }
     838
     839                        if ( $tag_views )
     840                                remove_filter( 'pvc_get_views_query_sql', [ __CLASS__, 'tag_paired_views_sql' ], PHP_INT_MAX );
    398841
    399842                        foreach ( $post_type_data as $post_type_id => $post_views ) {
    400843                                foreach ( $post_views as $id => $views ) {
    401844                                        // generate chart data for specific post types
    402                                         $data['data']['datasets'][$post_type_id]['data'][] = $views;
     845                                        $views_datasets[$post_type_id]['data'][] = $views;
    403846
    404847                                        if ( ! array_key_exists( $id, $sum ) )
    … …  
    409852                        }
    410853                }
     854
     855                // One bounded, cached read covers every dimension and the total, so the
     856                // paired payload never costs one extra Visit query per dimension.
     857                $visit_series = $this->get_paired_visit_series( [
     858                        'post_types'    => array_values( $post_types ),
     859                        'views_query'   => $visits_views_query,
     860                        'period'                => $period,
     861                        'lang'                  => isset( $args['lang'] ) ? $args['lang'] : ''
     862                ] );
     863
     864                $visits_available = ! empty( $visit_series['available'] );
     865                $visits_sum = $visits_available ? $this->get_visit_series_values( $visit_series ) : [];
    411866
    412867                // this month all days
    … …  
    415870                        $data['data']['labels'][] = ( $i % 2 === 0 ? '' : $i );
    416871                        $data['data']['dates'][] = date_i18n( get_option( 'date_format' ), strtotime( $year . '-' . $month . '-' . str_pad( $i, 2, '0', STR_PAD_LEFT ) ) );
    417                         $data['data']['datasets'][0]['data'][] = ( array_key_exists( $i - 1, $sum ) ? $sum[$i - 1] : 0 );
    418                 }
    419 
    420                 echo wp_json_encode( apply_filters( 'pvc_dashboard_post_views_data', $data, $args ) );
    421                 exit;
     872                        $views_datasets[0]['data'][] = ( array_key_exists( $i - 1, $sum ) ? $sum[$i - 1] : 0 );
     873                }
     874
     875                // Visits are paired with Views on every request even when the initial
     876                // mode only draws the two totals, so a metric switch never refetches.
     877                $visits_datasets = [];
     878
     879                if ( $visits_available ) {
     880                        $visits_datasets[] = [
     881                                'label'                         => __( 'Total Visits', 'post-views-counter' ),
     882                                'metric'                        => 'visits',
     883                                'post_type'                     => '_pvc_total_views',
     884                                'dimension_hidden'      => in_array( '_pvc_total_views', $user_options, true ),
     885                                'hidden'                        => true,
     886                                'data'                          => array_slice( $visits_sum, 0, $dates_number )
     887                        ];
     888
     889                        foreach ( $dimensions as $id => $dimension ) {
     890                                $visits_datasets[] = [
     891                                        'label'                         => $dimension['label'],
     892                                        'metric'                        => 'visits',
     893                                        'post_type'                     => $dimension['key'],
     894                                        'dimension_hidden'      => in_array( $dimension['key'], $user_options, true ),
     895                                        'hidden'                        => true,
     896                                        'data'                          => array_slice( $this->get_visit_series_values( $visit_series, $dimension['key'] ), 0, $dates_number )
     897                                ];
     898                        }
     899                }
     900
     901                // the helper also writes the metric state onto $data by reference, so
     902                // the assignment is deliberately kept as its own statement
     903                $datasets = $this->prepare_metric_datasets( $views_datasets, $visits_datasets, [
     904                        'widget'        => 'post-views',
     905                        'available'     => $visits_available,
     906                        'reason'        => isset( $visit_series['reason'] ) ? $visit_series['reason'] : 'read_failed'
     907                ], $data );
     908
     909                $data['data']['datasets'] = $datasets;
     910
     911                // The Free Views comparison, only on the month chart Core drew
     912                // itself, with every dimension's query as built, and never
     913                // beside an active extension of any version, which decides the list on its
     914                // own. No list means no box.
     915                if ( ! class_exists( 'Post_Views_Counter_Pro' ) && $core_chart ) {
     916                        $insight_notices = $this->get_free_insight_notices( $year . $month, array_values( $post_types ) );
     917
     918                        if ( ! empty( $insight_notices ) )
     919                                $data['insight_notices'] = $insight_notices;
     920                }
     921
     922                $data = apply_filters( 'pvc_dashboard_post_views_data', $data, $args );
     923
     924                // The headline describes the final chart's total, regardless of dataset
     925                // order or visibility. An explicit summary (including null) stays owned
     926                // by the filter; an unreadable series never becomes an invented zero.
     927                if ( ! class_exists( 'Post_Views_Counter_Pro' ) && is_array( $data ) && ! array_key_exists( 'summary', $data ) && isset( $data['data']['datasets'] ) && is_array( $data['data']['datasets'] ) ) {
     928                        foreach ( $data['data']['datasets'] as $dataset ) {
     929                                if ( ! is_array( $dataset ) || ! isset( $dataset['metric'], $dataset['post_type'] ) || $dataset['metric'] !== 'views' || $dataset['post_type'] !== '_pvc_total_views' )
     930                                        continue;
     931
     932                                if ( empty( $dataset['data'] ) || ! is_array( $dataset['data'] ) )
     933                                        break;
     934
     935                                $views = 0;
     936
     937                                foreach ( $dataset['data'] as $value ) {
     938                                        if ( ! is_numeric( $value ) || ! is_finite( (float) $value ) )
     939                                                break 2;
     940
     941                                        $views += $value;
     942                                }
     943
     944                                if ( is_finite( (float) $views ) ) {
     945                                        $data['summary'] = [
     946                                                'views' => $views,
     947                                                'views_html' => html_entity_decode( number_format_i18n( $views ), ENT_NOQUOTES, 'UTF-8' )
     948                                        ];
     949                                }
     950
     951                                break;
     952                        }
     953                }
     954
     955                // formatted after the filter, so tooltips match the values it returns
     956                if ( is_array( $data ) && isset( $data['data'] ) )
     957                        $data['data'] = Post_Views_Counter()->functions->format_chart_datasets( $data['data'] );
     958
     959                return $data;
     960        }
     961
     962        /**
     963         * Build the Free Views comparison notice.
     964         *
     965         * The month drawn against the previous month, on the shared comparison
     966         * of the counted post types, with no language and no history: a
     967         * running month on its completed days, a complete one on whole months.
     968         * - No valid comparison: null, so the payload has no list and no box.
     969         * - Fewer than FREE_NOTICE_MIN_VIEWS Views on either side: an empty list, no box.
     970         * - Otherwise the direction and the rounded change. A
     971         *   running month is "This month" against the same days last month; a
     972         *   complete one is named against the month before it. A change that
     973         *   rounds to 0 reads "less than 1%"; equal Views read "level with".
     974         *
     975         * @param string $period     The month drawn, YYYYMM.
     976         * @param array  $post_types Counted post types.
     977         * @return array|null The `insight_notices` list.
     978         */
     979        private function get_free_insight_notices( $period, $post_types ) {
     980                $comparison = Post_Views_Counter_Dashboard_Comparison::get( [
     981                        'grain'                 => 'month',
     982                        'period'                => $period,
     983                        'scope'                 => 'posts',
     984                        'post_types'    => $post_types,
     985                        'now'                   => $this->now
     986                ] );
     987
     988                if ( empty( $comparison['available'] ) )
     989                        return null;
     990
     991                $notices = [];
     992                $current = (int) $comparison['current']['views'];
     993                $previous = (int) $comparison['previous']['views'];
     994
     995                if ( $current >= self::FREE_NOTICE_MIN_VIEWS && $previous >= self::FREE_NOTICE_MIN_VIEWS ) {
     996                        // a running month compares its completed days with the same days
     997                        // of the month before, named relatively, never as a range of days
     998                        $running = $comparison['state'] !== 'complete';
     999                        $month = self::format_comparison_month( $comparison['current']['from'] );
     1000                        $previous_month = self::format_comparison_month( $comparison['previous']['from'] );
     1001
     1002                        if ( $current === $previous ) {
     1003                                $direction = 'same';
     1004                                $text = $running
     1005                                        ? __( 'This month is level with the same days last month.', 'post-views-counter' )
     1006                                        /* translators: 1: the month the chart shows, e.g. June 2026, 2: the month before it */
     1007                                        : sprintf( __( '%1$s was level with %2$s.', 'post-views-counter' ), self::ucfirst( $month ), $previous_month );
     1008                        } else {
     1009                                $direction = $current > $previous ? 'up' : 'down';
     1010
     1011                                // half away from zero; whole-number inputs keep 0.5% exact
     1012                                $percent = (int) round( abs( $current - $previous ) * 100 / $previous );
     1013
     1014                                if ( $percent < 1 )
     1015                                        $change = __( 'less than 1%', 'post-views-counter' );
     1016                                else
     1017                                        /* translators: %s: a change in percent, a whole number without a sign */
     1018                                        $change = sprintf( __( '%s%%', 'post-views-counter' ), number_format_i18n( $percent ) );
     1019
     1020                                if ( $running ) {
     1021                                        $text = $direction === 'up'
     1022                                                /* translators: %s: the change, e.g. 12% */
     1023                                                ? sprintf( __( 'This month is %s higher than the same days last month.', 'post-views-counter' ), $change )
     1024                                                /* translators: %s: the change, e.g. 12% */
     1025                                                : sprintf( __( 'This month is %s lower than the same days last month.', 'post-views-counter' ), $change );
     1026                                } else {
     1027                                        $text = $direction === 'up'
     1028                                                /* translators: 1: the month the chart shows, e.g. June 2026, 2: the change, e.g. 12%, 3: the month before it */
     1029                                                ? sprintf( __( '%1$s was %2$s higher than %3$s.', 'post-views-counter' ), self::ucfirst( $month ), $change, $previous_month )
     1030                                                /* translators: 1: the month the chart shows, e.g. June 2026, 2: the change, e.g. 12%, 3: the month before it */
     1031                                                : sprintf( __( '%1$s was %2$s lower than %3$s.', 'post-views-counter' ), self::ucfirst( $month ), $change, $previous_month );
     1032                                }
     1033                        }
     1034
     1035                        $notices[] = [
     1036                                'family'        => 'views',
     1037                                'class'         => 'comparison',
     1038                                'direction'     => $direction,
     1039                                'text'          => $text
     1040                        ];
     1041                }
     1042
     1043                return $notices;
     1044        }
     1045
     1046        /**
     1047         * Name a whole month of a comparison, e.g. "June 2026".
     1048         *
     1049         * Ranges are counting-clock buckets, so they are formatted as UTC days and
     1050         * never shifted by the site timezone.
     1051         *
     1052         * @param string $from Its first day, Ymd.
     1053         * @return string
     1054         */
     1055        private static function format_comparison_month( $from ) {
     1056                $utc = new DateTimeZone( 'UTC' );
     1057
     1058                return wp_date( _x( 'F Y', 'dashboard comparison: a month', 'post-views-counter' ), DateTimeImmutable::createFromFormat( '!Ymd', $from, $utc )->getTimestamp(), $utc );
     1059        }
     1060
     1061        /**
     1062         * Capitalise the first letter of a sentence's subject, for locales whose
     1063         * month names are lowercase.
     1064         *
     1065         * @param string $text
     1066         * @return string
     1067         */
     1068        private static function ucfirst( $text ) {
     1069                if ( $text === '' || ! function_exists( 'mb_substr' ) )
     1070                        return ucfirst( $text );
     1071
     1072                return mb_strtoupper( mb_substr( $text, 0, 1, 'UTF-8' ), 'UTF-8' ) . mb_substr( $text, 1, null, 'UTF-8' );
     1073        }
     1074
     1075        /**
     1076         * Attach the paired metric state, styles, and copy to a widget payload.
     1077         *
     1078         * Views datasets come first and Visits second so Chart.js draws the derived
     1079         * colour in front of the theme colour in the totals pair.
     1080         *
     1081         * @param array $views_datasets Ordered Views datasets.
     1082         * @param array $visits_datasets Ordered Visits datasets, empty when unreadable.
     1083         * @param array $context Widget id and Visit availability.
     1084         * @param array $data Payload being built, passed by reference.
     1085         * @return array Combined, styled datasets.
     1086         */
     1087        public function prepare_metric_datasets( $views_datasets, $visits_datasets, $context, &$data ) {
     1088                $pvc = Post_Views_Counter();
     1089                $context = wp_parse_args( $context, [
     1090                        'widget'        => '',
     1091                        'available'     => false,
     1092                        'reason'        => 'read_failed'
     1093                ] );
     1094
     1095                $available = (bool) $context['available'];
     1096                $preferred = $this->get_widget_metric_mode( $context['widget'], get_current_user_id() );
     1097
     1098                // An unreadable Visit series forces the Views breakdown for this render
     1099                // only. The stored preference is never rewritten, so the preferred mode
     1100                // returns by itself once Visits becomes readable again.
     1101                $effective = $available ? $preferred : 'views';
     1102
     1103                $datasets = [];
     1104
     1105                foreach ( [ 'views' => $views_datasets, 'visits' => $visits_datasets ] as $metric => $metric_datasets ) {
     1106                        foreach ( $metric_datasets as $index => $dataset ) {
     1107                                $dataset['metric'] = $metric;
     1108                                $dataset['is_total'] = $index === 0;
     1109
     1110                                if ( $effective === 'both' ) {
     1111                                        // both totals are always drawn and the saved breakdown
     1112                                        // visibility of Total is deliberately not consulted
     1113                                        $dataset['hidden'] = $index !== 0;
     1114                                } else {
     1115                                        $dataset['hidden'] = $metric !== $effective || ! empty( $dataset['dimension_hidden'] );
     1116                                }
     1117
     1118                                // The translucent area is part of the dashboard presentation in all
     1119                                // three metric modes, including multi-line breakdowns.
     1120                                $datasets[] = array_merge( $dataset, $pvc->functions->get_metric_dataset_style( $metric, 'line', [ 'fill' => true ] ) );
     1121                        }
     1122                }
     1123
     1124                $data['metrics'] = $this->get_metric_buttons( $available, $context['reason'] );
     1125                $data['metric_state'] = [
     1126                        'preferred'     => $preferred,
     1127                        'effective'     => $effective
     1128                ];
     1129                $data['availability'] = [
     1130                        'readable'      => $available,
     1131                        'reason'        => $available ? 'ready' : $context['reason']
     1132                ];
     1133                $data['notices'] = [
     1134                        'visits_unavailable'    => __( 'Visit data is not available right now.', 'post-views-counter' )
     1135                ];
     1136
     1137                return $datasets;
    4221138        }
    4231139
    … …  
    4311147                        wp_die( __( 'You do not have permission to access this page.', 'post-views-counter' ) );
    4321148
    433                 // get main instance
    434                 $pvc = Post_Views_Counter();
     1149                // get period
     1150                $period = isset( $_POST['period'] ) && ! empty( $_POST['period'] ) ? preg_replace( '/[^a-z0-9_|]/', '', $_POST['period'] ) : apply_filters( 'pvc_dashboard_widget_default_period', 'this_month', 'post-most-viewed' );
     1151
     1152                echo wp_json_encode( $this->get_post_most_viewed_data( $period ) );
     1153                exit;
     1154        }
     1155
     1156        /**
     1157         * Build the Top Posts widget payload.
     1158         *
     1159         * Separated from the AJAX handler so the list can be asserted without the
     1160         * transport. Rows are selected, ordered and ranked by Views.
     1161         *
     1162         * @param string $period Requested period.
     1163         * @return array
     1164         */
     1165        public function get_post_most_viewed_data( $period ) {
     1166                $period = preg_replace( '/[^a-z0-9_|]/', '', (string) $period );
    4351167
    4361168                // get post types
    … …  
    4441176                $post_types = map_deep( $post_types, 'sanitize_text_field' );
    4451177
    446                 // get period
    447                 $period = isset( $_POST['period'] ) && ! empty( $_POST['period'] ) ? preg_replace( '/[^a-z0-9_|]/', '', $_POST['period'] ) : apply_filters( 'pvc_dashboard_widget_default_period', 'this_month', 'post-most-viewed' );
    448                
    4491178                // parameters to be used in filter
    4501179                $args = [
    … …  
    4881217
    4891218                $posts = pvc_get_most_viewed_posts( $query_args );
    490                
     1219
    4911220                $html = '
    4921221                <table id="pvc-post-most-viewed-table" class="pvc-table pvc-table-hover">
    … …  
    5501279                $data['html'] = $html;
    5511280
    552                 echo wp_json_encode( apply_filters( 'pvc_dashboard_post_most_viewed_data', $data, $args ) );
    553                 exit;
     1281                return apply_filters( 'pvc_dashboard_post_most_viewed_data', $data, $args );
    5541282        }
    5551283
    … …  
    6001328                        $user_id = get_current_user_id();
    6011329
    602                         // get user dashboard data
    603                         $user_options = get_user_meta( $user_id, 'pvc_dashboard', true );
    604 
    605                         // empty userdata?
    606                         if ( ! is_array( $user_options ) || empty( $user_options ) )
    607                                 $user_options = [];
    608 
    609                         // empty post types?
    610                         if ( ! array_key_exists( 'post_types', $user_options ) || ! is_array( $user_options['post_types'] ) )
    611                                 $user_options['post_types'] = [];
    612 
    613                         // hide post type?
    614                         if ( ! empty( $update['post_type'] ) ) {
    615                                 // get allowed post types
    616                                 $allowed_post_types = Post_Views_Counter()->options['general']['post_types_count'];
    617 
    618                                 // simulate total post views as post type
    619                                 $allowed_post_types[] = '_pvc_total_views';
    620 
    621                                 if ( in_array( $update['post_type'], $allowed_post_types, true ) ) {
    622                                         if ( isset( $update['hidden'] ) && $update['hidden'] === 'true' ) {
    623                                                 if ( ! in_array( $update['post_type'], $user_options['post_types'], true ) )
    624                                                         $user_options['post_types'][] = $update['post_type'];
    625                                         } else {
    626                                                 if ( ( $key = array_search( $update['post_type'], $user_options['post_types'] ) ) !== false )
    627                                                         unset( $user_options['post_types'][$key] );
    628                                         }
     1330                        // update userdata
     1331                        update_user_meta( $user_id, 'pvc_dashboard', $this->merge_dashboard_user_options( $update, $user_id ) );
     1332
     1333                        echo wp_send_json_success();
     1334                }
     1335
     1336                echo wp_send_json_error();
     1337                exit;
     1338        }
     1339
     1340        /**
     1341         * Merge one dashboard preference update into the stored user options.
     1342         *
     1343         * Separated from the AJAX handler so the allow-lists can be asserted
     1344         * without the transport. The stored shape and the filter are unchanged.
     1345         *
     1346         * @param array $update Sanitized update payload.
     1347         * @param int   $user_id User ID.
     1348         * @return array
     1349         */
     1350        public function merge_dashboard_user_options( $update, $user_id ) {
     1351                $update = is_array( $update ) ? $update : [];
     1352
     1353                // get user dashboard data
     1354                $user_options = get_user_meta( $user_id, 'pvc_dashboard', true );
     1355
     1356                // empty userdata?
     1357                if ( ! is_array( $user_options ) || empty( $user_options ) )
     1358                        $user_options = [];
     1359
     1360                // empty post types?
     1361                if ( ! array_key_exists( 'post_types', $user_options ) || ! is_array( $user_options['post_types'] ) )
     1362                        $user_options['post_types'] = [];
     1363
     1364                // hide post type?
     1365                if ( ! empty( $update['post_type'] ) ) {
     1366                        // get allowed post types
     1367                        $allowed_post_types = Post_Views_Counter()->options['general']['post_types_count'];
     1368
     1369                        // simulate total post views as post type
     1370                        $allowed_post_types[] = '_pvc_total_views';
     1371
     1372                        if ( in_array( $update['post_type'], $allowed_post_types, true ) ) {
     1373                                if ( isset( $update['hidden'] ) && $update['hidden'] === 'true' ) {
     1374                                        if ( ! in_array( $update['post_type'], $user_options['post_types'], true ) )
     1375                                                $user_options['post_types'][] = $update['post_type'];
     1376                                } else {
     1377                                        if ( ( $key = array_search( $update['post_type'], $user_options['post_types'] ) ) !== false )
     1378                                                unset( $user_options['post_types'][$key] );
    6291379                                }
    6301380                        }
    631 
    632                         // empty menu items?
    633                         if ( ! array_key_exists( 'menu_items', $user_options ) || ! is_array( $user_options['menu_items'] ) )
    634                                 $user_options['menu_items'] = [];
    635 
    636                         if ( ! empty( $update['menu_items'] ) && is_array( $update['menu_items'] ) ) {
    637                                 $user_options['menu_items'] = [];
    638 
    639                                 // get allowed menu items
    640                                 $allowed_menu_items = array_column( $this->widget_items, 'id' );
    641 
    642                                 foreach ( $update['menu_items'] as $menu_item => $hidden ) {
    643                                         if ( in_array( $menu_item, $allowed_menu_items, true ) && $hidden === 'true' )
    644                                                 $user_options['menu_items'][] = $menu_item;
    645                                 }
     1381                }
     1382
     1383                // empty metric modes?
     1384                if ( ! array_key_exists( 'metrics', $user_options ) || ! is_array( $user_options['metrics'] ) )
     1385                        $user_options['metrics'] = [];
     1386
     1387                // Only an explicit metric-button action reaches this branch. An
     1388                // availability fallback renders in Views breakdown mode without ever
     1389                // sending an update, so a stored both or visits preference survives
     1390                // a Visits outage untouched.
     1391                if ( ! empty( $update['metrics'] ) && is_array( $update['metrics'] ) ) {
     1392                        // the widget list is normally built on admin_init; build it here too
     1393                        // so the allow-list is never silently empty
     1394                        if ( empty( $this->widget_items ) )
     1395                                $this->setup_widget_items();
     1396
     1397                        $allowed_widgets = array_column( $this->widget_items, 'id' );
     1398
     1399                        foreach ( $update['metrics'] as $widget_id => $mode ) {
     1400                                $widget_id = sanitize_key( $widget_id );
     1401
     1402                                if ( in_array( $widget_id, $allowed_widgets, true ) && in_array( $mode, $this->get_metric_modes(), true ) )
     1403                                        $user_options['metrics'][$widget_id] = $mode;
    6461404                        }
    647 
    648                         // filter user options
    649                         $user_options = apply_filters( 'pvc_update_dashboard_user_options', $user_options, $update, $user_id );
    650 
    651                         // update userdata
    652                         update_user_meta( $user_id, 'pvc_dashboard', $user_options );
    653                        
    654                         echo wp_send_json_success();
    655                 }
    656 
    657                 echo wp_send_json_error();
    658                 exit;
     1405                }
     1406
     1407                // empty menu items?
     1408                if ( ! array_key_exists( 'menu_items', $user_options ) || ! is_array( $user_options['menu_items'] ) )
     1409                        $user_options['menu_items'] = [];
     1410
     1411                if ( ! empty( $update['menu_items'] ) && is_array( $update['menu_items'] ) ) {
     1412                        $user_options['menu_items'] = [];
     1413
     1414                        // get allowed menu items
     1415                        $allowed_menu_items = array_column( $this->widget_items, 'id' );
     1416
     1417                        foreach ( $update['menu_items'] as $menu_item => $hidden ) {
     1418                                if ( in_array( $menu_item, $allowed_menu_items, true ) && $hidden === 'true' )
     1419                                        $user_options['menu_items'][] = $menu_item;
     1420                        }
     1421                }
     1422
     1423                // filter user options
     1424                $user_options = apply_filters( 'pvc_update_dashboard_user_options', $user_options, $update, $user_id );
     1425
     1426                return $user_options;
    6591427        }
    6601428
  • post-views-counter/tags/1.8.0/includes/class-emails-mailer.php

    r3537950 r3734102  
    6262
    6363                try {
    64                         $this->maybe_flush_pending_counts( $context );
     64                        $flush_result = $this->maybe_flush_pending_counts( $context );
     65
     66                        if ( is_wp_error( $flush_result ) )
     67                                return $this->finalize_failure( $this->get_default_result( $context['recipient'], [], [] ), 'pending_counts_not_flushed', $flush_result, $context['settings'], $context['period'], $context['source'], $context['is_test'], $context['summary_type'] );
    6568
    6669                        $rendered = $this->emails->render_summary(
    … …  
    290293         *
    291294         * @param array $context
    292          * @return void
     295         * @return true|WP_Error
    293296         */
    294297        private function maybe_flush_pending_counts( $context ) {
    295298                if ( ! $this->should_flush_pending_counts( $context ) )
    296                         return;
     299                        return true;
    297300
    298301                $counter = isset( $this->pvc->counter ) ? $this->pvc->counter : null;
    299302                $should_flush_base_counts = (bool) apply_filters( 'pvc_email_summary_should_flush_pending_counts', true, $context, $this );
    300303
    301                 if ( $should_flush_base_counts && $counter && method_exists( $counter, 'using_object_cache' ) && method_exists( $counter, 'flush_cache_to_db' ) && $counter->using_object_cache() )
    302                         $counter->flush_cache_to_db();
     304                if ( $should_flush_base_counts && $counter && method_exists( $counter, 'flush_cache_to_db' ) && ! $counter->flush_cache_to_db() ) {
     305                        $status = method_exists( $counter, 'get_cache_flush_status' ) ? sanitize_key( $counter->get_cache_flush_status() ) : 'failed';
     306
     307                        return new WP_Error( 'pvc_email_pending_counts_' . $status, __( 'Pending view counts could not be flushed before the summary query.', 'post-views-counter' ) );
     308                }
    303309
    304310                do_action( 'pvc_email_summary_before_query', $context, $this );
     311
     312                return true;
    305313        }
    306314
  • post-views-counter/tags/1.8.0/includes/class-emails-query.php

    r3537950 r3734102  
    1010 */
    1111class Post_Views_Counter_Emails_Query {
     12
     13        /**
     14         * Most ISO weeks a filtered day range may touch and still be proven complete.
     15         * Six cover any range of up to 31 days
     16         * however it falls; the built-in cadences touch one week per range. A longer
     17         * range is read without week checks and is unestablished.
     18         *
     19         * @var int
     20         */
     21        const MAX_CHECKED_WEEKS = 6;
    1222
    1323        /**
    … …  
    207217
    208218                $overview_trend = $this->build_trend_data( $current['total_views'], $previous['total_views'] );
     219
     220                // a comparison with a range that cannot be shown complete is not a trend
     221                if ( ! $current['complete'] || ! $previous['complete'] ) {
     222                        $overview_trend = $this->build_trend_data( $current['total_views'], 0 );
     223                        $overview_trend['views_change'] = (int) $current['total_views'] - (int) $previous['total_views'];
     224                }
     225
    209226                $threshold_met = (int) $current['total_views'] >= (int) $query_args['threshold'];
    210227                $should_send = $threshold_met || $query_args['send_empty_reports'];
    211                 $top_content = $this->build_top_content_data( $top_items, $previous_views );
     228                $unestablished = [];
     229                $top_content = $this->build_top_content_data( $top_items, $previous_views, $unestablished );
    212230
    213231                $overview = [
    … …  
    235253                        'overview'              => $overview,
    236254                        'top_content'   => $top_content,
    237                         'traffic_signals' => $this->build_traffic_signals_data( $overview, $top_content ),
     255                        'traffic_signals' => $this->build_traffic_signals_data( $overview, $top_content, $unestablished ),
    238256                        'status'                => $this->build_status_data( $overview, ! empty( $top_content ) )
    239257                ];
    … …  
    299317
    300318                $post_type_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) );
    301                 $period_query = $this->get_period_query_parts( $start_period, $end_period );
    302                 $params = array_merge( [ 0 ], $period_query['params'], [ 'publish', '' ], $post_types );
     319                $source = $this->get_range_source( $start_period, $end_period );
     320
     321                if ( $source !== null && $source['type'] === 0 ) {
     322                        $query = $wpdb->prepare(
     323                                ' SELECT COALESCE( SUM( summary.period_views ), 0 ) AS total_views, COALESCE( SUM( CASE WHEN summary.period_views > 0 THEN 1 ELSE 0 END ), 0 ) AS viewed_content_count, COALESCE( SUM( summary.incomplete ), 0 ) AS incomplete_count
     324                                FROM (
     325                                        SELECT pv.id AS post_id, SUM( CASE WHEN pv.type = 0 AND pv.period >= %s AND pv.period <= %s THEN pv.count ELSE 0 END ) AS period_views, CASE WHEN ' . $source['checks'] . ' THEN 0 ELSE 1 END AS incomplete
     326                                        FROM ' . $wpdb->prefix . 'post_views AS pv
     327                                        INNER JOIN ' . $wpdb->posts . ' AS p ON pv.id = p.ID
     328                                        WHERE ' . $source['where'] . $this->get_post_content_clause() . '
     329                                                AND p.post_status = %s
     330                                                AND p.post_password = %s
     331                                                AND p.post_type IN (' . $post_type_placeholders . ')
     332                                        GROUP BY pv.id
     333                                ) AS summary',
     334                                array_merge( [ $source['from'], $source['to'] ], $source['check_params'], $source['where_params'], [ 'publish', '' ], $post_types )
     335                        );
     336
     337                        $row = $wpdb->get_row( $query, ARRAY_A );
     338
     339                        return [
     340                                'total_views'                   => isset( $row['total_views'] ) ? (int) $row['total_views'] : 0,
     341                                'viewed_content_count'  => isset( $row['viewed_content_count'] ) ? (int) $row['viewed_content_count'] : 0,
     342                                'complete'                              => $source['checks'] !== '1 = 0' && isset( $row['incomplete_count'] ) && (int) $row['incomplete_count'] === 0
     343                        ];
     344                }
     345
     346                $period_query = $source === null ? $this->get_period_query_parts( $start_period, $end_period ) : [ 'clause' => 'pv.period = %s', 'params' => [ $source['period'] ] ];
     347                $params = array_merge( [ $source === null ? 0 : $source['type'] ], $period_query['params'], [ 'publish', '' ], $post_types );
    303348                $query = $wpdb->prepare(
    304349                        ' SELECT COALESCE( SUM( summary.period_views ), 0 ) AS total_views, COUNT( summary.post_id ) AS viewed_content_count
    … …  
    307352                                FROM ' . $wpdb->prefix . 'post_views AS pv
    308353                                INNER JOIN ' . $wpdb->posts . ' AS p ON pv.id = p.ID
    309                                 WHERE pv.type = %d
     354                                WHERE pv.type = %d' . $this->get_post_content_clause() . '
    310355                                        AND ' . $period_query['clause'] . '
    311356                                        AND p.post_status = %s
    … …  
    321366                return [
    322367                        'total_views'                   => isset( $row['total_views'] ) ? (int) $row['total_views'] : 0,
    323                         'viewed_content_count'  => isset( $row['viewed_content_count'] ) ? (int) $row['viewed_content_count'] : 0
     368                        'viewed_content_count'  => isset( $row['viewed_content_count'] ) ? (int) $row['viewed_content_count'] : 0,
     369                        'complete'                              => true
    324370                ];
    325371        }
    … …  
    339385
    340386                $post_type_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) );
    341                 $period_query = $this->get_period_query_parts( $start_period, $end_period );
    342                 $params = array_merge( [ 0 ], $period_query['params'], [ 'publish', '' ], $post_types, [ (int) $limit ] );
     387                $source = $this->get_range_source( $start_period, $end_period );
     388
     389                if ( $source !== null && $source['type'] === 0 ) {
     390                        $query = $wpdb->prepare(
     391                                ' SELECT pv.id AS post_id, p.post_title AS post_title, SUM( CASE WHEN pv.type = 0 AND pv.period >= %s AND pv.period <= %s THEN pv.count ELSE 0 END ) AS current_views, CASE WHEN ' . $source['checks'] . ' THEN 1 ELSE 0 END AS complete
     392                                FROM ' . $wpdb->prefix . 'post_views AS pv
     393                                INNER JOIN ' . $wpdb->posts . ' AS p ON pv.id = p.ID
     394                                WHERE ' . $source['where'] . $this->get_post_content_clause() . '
     395                                        AND p.post_status = %s
     396                                        AND p.post_password = %s
     397                                        AND p.post_type IN (' . $post_type_placeholders . ')
     398                                GROUP BY pv.id, p.post_title
     399                                HAVING current_views > 0
     400                                ORDER BY current_views DESC, pv.id ASC
     401                                LIMIT %d',
     402                                array_merge( [ $source['from'], $source['to'] ], $source['check_params'], $source['where_params'], [ 'publish', '' ], $post_types, [ (int) $limit ] )
     403                        );
     404
     405                        $items = $wpdb->get_results( $query, ARRAY_A );
     406
     407                        return is_array( $items ) ? $items : [];
     408                }
     409
     410                $period_query = $source === null ? $this->get_period_query_parts( $start_period, $end_period ) : [ 'clause' => 'pv.period = %s', 'params' => [ $source['period'] ] ];
     411                $params = array_merge( [ $source === null ? 0 : $source['type'] ], $period_query['params'], [ 'publish', '' ], $post_types, [ (int) $limit ] );
    343412                $query = $wpdb->prepare(
    344413                        ' SELECT pv.id AS post_id, p.post_title AS post_title, SUM( pv.count ) AS current_views
    345414                        FROM ' . $wpdb->prefix . 'post_views AS pv
    346415                        INNER JOIN ' . $wpdb->posts . ' AS p ON pv.id = p.ID
    347                         WHERE pv.type = %d
     416                        WHERE pv.type = %d' . $this->get_post_content_clause() . '
    348417                                AND ' . $period_query['clause'] . '
    349418                                AND p.post_status = %s
    … …  
    381450                $post_type_placeholders = implode( ', ', array_fill( 0, count( $post_types ), '%s' ) );
    382451                $post_id_placeholders = implode( ', ', array_fill( 0, count( $post_ids ), '%d' ) );
    383                 $period_query = $this->get_period_query_parts( $start_period, $end_period );
    384                 $params = array_merge( [ 0 ], $period_query['params'], [ 'publish', '' ], $post_types, $post_ids );
     452                $source = $this->get_range_source( $start_period, $end_period );
     453
     454                // a range that is not a whole stored period counts only for posts whose ISO weeks add up
     455                if ( $source !== null && $source['type'] === 0 ) {
     456                        $query = $wpdb->prepare(
     457                                ' SELECT pv.id AS post_id, SUM( CASE WHEN pv.type = 0 AND pv.period >= %s AND pv.period <= %s THEN pv.count ELSE 0 END ) AS previous_views, CASE WHEN ' . $source['checks'] . ' THEN 1 ELSE 0 END AS complete
     458                                FROM ' . $wpdb->prefix . 'post_views AS pv
     459                                INNER JOIN ' . $wpdb->posts . ' AS p ON pv.id = p.ID
     460                                WHERE ' . $source['where'] . $this->get_post_content_clause() . '
     461                                        AND p.post_status = %s
     462                                        AND p.post_password = %s
     463                                        AND p.post_type IN (' . $post_type_placeholders . ')
     464                                        AND pv.id IN (' . $post_id_placeholders . ')
     465                                GROUP BY pv.id',
     466                                array_merge( [ $source['from'], $source['to'] ], $source['check_params'], $source['where_params'], [ 'publish', '' ], $post_types, $post_ids )
     467                        );
     468
     469                        $results = $wpdb->get_results( $query, ARRAY_A );
     470                        $views = [];
     471
     472                        if ( is_array( $results ) ) {
     473                                foreach ( $results as $result ) {
     474                                        if ( empty( $result['complete'] ) )
     475                                                $views[(int) $result['post_id']] = null;
     476                                        elseif ( (int) $result['previous_views'] > 0 )
     477                                                $views[(int) $result['post_id']] = (int) $result['previous_views'];
     478                                }
     479                        }
     480
     481                        return $views;
     482                }
     483
     484                $period_query = $source === null ? $this->get_period_query_parts( $start_period, $end_period ) : [ 'clause' => 'pv.period = %s', 'params' => [ $source['period'] ] ];
     485                $params = array_merge( [ $source === null ? 0 : $source['type'] ], $period_query['params'], [ 'publish', '' ], $post_types, $post_ids );
    385486                $query = $wpdb->prepare(
    386487                        ' SELECT pv.id AS post_id, SUM( pv.count ) AS previous_views
    387488                        FROM ' . $wpdb->prefix . 'post_views AS pv
    388489                        INNER JOIN ' . $wpdb->posts . ' AS p ON pv.id = p.ID
    389                         WHERE pv.type = %d
     490                        WHERE pv.type = %d' . $this->get_post_content_clause() . '
    390491                                AND ' . $period_query['clause'] . '
    391492                                AND p.post_status = %s
    … …  
    410511
    411512        /**
     513         * Restrict the counter rows to posts when the table stores other content.
     514         *
     515         * @return string
     516         */
     517        private function get_post_content_clause() {
     518                return pvc_post_views_has_content_column() ? ' AND pv.content = 0' : '';
     519        }
     520
     521        /**
     522         * Get where the Views of a date range are read from.
     523         *
     524         * A range that is exactly one ISO week or one calendar month is read from
     525         * that period's own row, which the daily cleanup never deletes. Any other
     526         * range is read from daily rows, and counts only while the daily rows of
     527         * every ISO week it touches add up to that week's own row, checked in the same statement.
     528         *
     529         * @param string|int $start_period Ymd
     530         * @param string|int $end_period   Ymd
     531         * @return array|null Own row: [ 'type' => 1|2, 'period' ]. Daily rows:
     532         *                    [ 'type' => 0, 'from', 'to', 'where', 'where_params',
     533         *                    'checks', 'check_params' ]. Null when the bounds are not
     534         *                    dates, which keeps the released clause.
     535         */
     536        private function get_range_source( $start_period, $end_period ) {
     537                $start = $this->normalize_date_period_value( $start_period );
     538                $end = $this->normalize_date_period_value( $end_period );
     539
     540                if ( $start === '' || $end === '' )
     541                        return null;
     542
     543                if ( $start > $end ) {
     544                        $temp = $start;
     545                        $start = $end;
     546                        $end = $temp;
     547                }
     548
     549                $utc = new DateTimeZone( 'UTC' );
     550                $from = DateTimeImmutable::createFromFormat( '!Ymd', $start, $utc );
     551                $to = DateTimeImmutable::createFromFormat( '!Ymd', $end, $utc );
     552
     553                if ( ! $from || ! $to || $from->format( 'Ymd' ) !== $start || $to->format( 'Ymd' ) !== $end )
     554                        return null;
     555
     556                if ( $from->format( 'N' ) === '1' && $to->format( 'Ymd' ) === $from->modify( '+6 days' )->format( 'Ymd' ) )
     557                        return [ 'type' => 1, 'period' => $from->format( 'oW' ) ];
     558
     559                if ( $from->format( 'j' ) === '1' && $to->format( 'Ymd' ) === $from->format( 'Ymt' ) )
     560                        return [ 'type' => 2, 'period' => $from->format( 'Ym' ) ];
     561
     562                $monday = $from->modify( '-' . ( (int) $from->format( 'N' ) - 1 ) . ' days' );
     563                $last_sunday = $to->modify( '+' . ( 7 - (int) $to->format( 'N' ) ) . ' days' );
     564                $weeks = [];
     565                $checks = [];
     566                $check_params = [];
     567
     568                for ( $week = $monday; $week <= $last_sunday; $week = $week->modify( '+7 days' ) ) {
     569                        // Beyond the proof budget, totals remain readable but completeness is unestablished.
     570                        if ( count( $weeks ) >= self::MAX_CHECKED_WEEKS ) {
     571                                return [
     572                                        'type' => 0, 'from' => $start, 'to' => $end,
     573                                        'where' => '( pv.type = 0 AND pv.period >= %s AND pv.period <= %s )',
     574                                        'where_params' => [ $start, $end ],
     575                                        'checks' => '1 = 0', 'check_params' => []
     576                                ];
     577                        }
     578
     579                        $weeks[] = $week->format( 'oW' );
     580                        $checks[] = 'SUM( CASE WHEN pv.type = 0 AND pv.period >= %s AND pv.period <= %s THEN pv.count ELSE 0 END ) = SUM( CASE WHEN pv.type = 1 AND pv.period = %s THEN pv.count ELSE 0 END )';
     581                        $check_params = array_merge( $check_params, [ $week->format( 'Ymd' ), $week->modify( '+6 days' )->format( 'Ymd' ), $week->format( 'oW' ) ] );
     582                }
     583
     584                return [
     585                        'type'                  => 0,
     586                        'from'                  => $start,
     587                        'to'                    => $end,
     588                        'where'                 => '( ( pv.type = 0 AND pv.period >= %s AND pv.period <= %s ) OR ( pv.type = 1 AND pv.period IN (' . implode( ', ', array_fill( 0, count( $weeks ), '%s' ) ) . ') ) )',
     589                        'where_params'  => array_merge( [ $monday->format( 'Ymd' ), $last_sunday->format( 'Ymd' ) ], $weeks ),
     590                        'checks'                => implode( ' AND ', $checks ),
     591                        'check_params'  => $check_params
     592                ];
     593        }
     594
     595        /**
    412596         * Build the period comparison clause and parameters.
    413597         *
    … …  
    464648         * @return array
    465649         */
    466         private function build_top_content_data( $top_items, $previous_views ) {
     650        private function build_top_content_data( $top_items, $previous_views, &$unestablished = [] ) {
    467651                $content = [];
    468652
    … …  
    471655                        $current_views = (int) $item['current_views'];
    472656                        $item_previous_views = isset( $previous_views[$post_id] ) ? (int) $previous_views[$post_id] : 0;
    473                         $trend = $this->build_trend_data( $current_views, $item_previous_views );
     657                        $established = ( ! isset( $item['complete'] ) || ! empty( $item['complete'] ) ) && ! ( array_key_exists( $post_id, $previous_views ) && $previous_views[$post_id] === null );
     658                        $trend = $this->build_trend_data( $current_views, $established ? $item_previous_views : 0 );
     659
     660                        // an item whose ranges cannot be shown complete has no trend and no signal
     661                        if ( ! $established ) {
     662                                $trend['views_change'] = $current_views - $item_previous_views;
     663                                $unestablished[] = $post_id;
     664                        }
    474665                        $url = get_permalink( $post_id );
    475666
    … …  
    529720         * @param array $overview
    530721         * @param array $top_content
    531          * @return array
    532          */
    533         private function build_traffic_signals_data( $overview, $top_content ) {
     722         * @param array $unestablished Post IDs whose ranges cannot be shown complete
     723         * @return array
     724         */
     725        private function build_traffic_signals_data( $overview, $top_content, $unestablished = [] ) {
    534726                if ( (int) $overview['total_views'] === 0 ) {
    535727                        return [
    … …  
    549741
    550742                foreach ( $top_content as $item ) {
     743                        if ( ! empty( $item['post_id'] ) && in_array( (int) $item['post_id'], $unestablished, true ) )
     744                                continue;
     745
    551746                        $current_views = isset( $item['current_views'] ) ? (int) $item['current_views'] : 0;
    552747                        $previous_views = isset( $item['previous_views'] ) ? (int) $item['previous_views'] : 0;
  • post-views-counter/tags/1.8.0/includes/class-frontend.php

    r3640258 r3734102  
    4242         */
    4343        public function post_views_shortcode( $args ) {
    44                 $views = 0;
     44                $html = '';
    4545                $options = Post_Views_Counter()->options['display'];
     46                $raw_args = is_array( $args ) ? $args : [];
    4647
    4748                $defaults = [
    4849                        'id'            => get_the_ID(),
    4950                        'type'          => 'post',
    50                         'period'        => $options['display_period']
     51                        'period'        => $options['display_period'],
     52                        'format'        => null
    5153                ];
    5254
    5355                // combine attributes
    54                 $atts = shortcode_atts( $defaults, $args );
    55                 $atts = apply_filters( 'pvc_post_views_shortcode_atts', $atts, $args, $defaults );
    56                 $period = isset( $atts['period'] ) ? sanitize_key( $atts['period'] ) : sanitize_key( $defaults['period'] );
     56                $atts = shortcode_atts( $defaults, $raw_args );
     57                $atts = apply_filters( 'pvc_post_views_shortcode_atts', $atts, $raw_args, $defaults );
     58
     59                if ( ! is_array( $atts ) )
     60                        $atts = [];
     61
     62                $type = isset( $atts['type'] ) && is_scalar( $atts['type'] ) ? sanitize_key( (string) $atts['type'] ) : '';
     63                $period = isset( $atts['period'] ) && is_scalar( $atts['period'] ) ? sanitize_key( (string) $atts['period'] ) : '';
     64                $atts['type'] = $type;
     65                $atts['period'] = $period;
     66                $format_present = array_key_exists( 'format', $atts ) && $atts['format'] !== null;
     67                $post_id = isset( $atts['id'] ) && is_scalar( $atts['id'] ) && ctype_digit( (string) $atts['id'] ) ? (int) $atts['id'] : 0;
     68                $atts['id'] = $post_id;
    5769
    5870                // default type?
    59                 if ( $atts['type'] === 'post' )
    60                         $views = function_exists( 'pvc_post_views' ) ? pvc_post_views( $atts['id'], false, $period ) : 0;
    61 
    62                 return apply_filters( 'pvc_post_views_shortcode', $views, $atts );
     71                if ( $type === 'post' && $post_id > 0 && get_post( $post_id ) instanceof WP_Post ) {
     72                        $render_args = [];
     73
     74                        if ( $format_present )
     75                                $render_args['format'] = $atts['format'];
     76
     77                        $html = function_exists( 'pvc_post_views' ) ? pvc_post_views( $post_id, false, $period, $render_args ) : '';
     78                }
     79
     80                return apply_filters( 'pvc_post_views_shortcode', $html, $atts );
     81        }
     82
     83        /**
     84         * Render a complete custom metric template without parsing it as a shortcode.
     85         *
     86         * %%views%% renders the count and %%icon%% the counter icon. The icon is
     87         * placed by the template alone and is not a metric, so a template without
     88         * %%views%% still renders nothing.
     89         *
     90         * @param string        $format Template HTML.
     91         * @param int           $content_id Content ID.
     92         * @param string        $content_type Content type.
     93         * @param string        $period Display period.
     94         * @param callable      $views_renderer Views token renderer. It receives whether the template has visible words of its own, so the count can leave out its hidden label.
     95         * @param callable|null $icon_renderer Icon token renderer; null uses the counter icon without a content-type icon filter.
     96         * @return string
     97         */
     98        public function render_template( $format, $content_id, $content_type, $period, $views_renderer, $icon_renderer = null ) {
     99                if ( ! is_scalar( $format ) || ! is_callable( $views_renderer ) )
     100                        return '';
     101
     102                $tag_pattern = self::get_template_tag_pattern();
     103                $format = preg_replace_callback(
     104                        $tag_pattern,
     105                        static function ( $matches ) {
     106                                return preg_replace_callback(
     107                                        '/(["\'])(.*?)\1/',
     108                                        static function ( $attribute ) {
     109                                                return $attribute[1] . str_replace( '>', '&gt;', $attribute[2] ) . $attribute[1];
     110                                        },
     111                                        $matches[0]
     112                                );
     113                        },
     114                        (string) $format
     115                );
     116                $format = wp_kses_post( $format );
     117                $parts = preg_split( $tag_pattern, $format, -1, PREG_SPLIT_DELIM_CAPTURE );
     118                $token_pattern = '/%%(views|icon)%%/';
     119                $metrics = self::get_template_text_metrics( $format );
     120
     121                if ( empty( $metrics ) )
     122                        return '';
     123
     124                if ( ! is_callable( $icon_renderer ) ) {
     125                        $frontend = $this;
     126                        $icon_renderer = static function () use ( $frontend, $content_id, $content_type ) {
     127                                return $frontend->get_counter_icon( 'views', $content_id, $content_type );
     128                        };
     129                }
     130
     131                // A template that says "Views:" or "reads" itself already gives the count
     132                // its context, and a hidden label would make a screen reader announce it
     133                // twice. Only visible letters count: tokens, tags and attributes do not.
     134                $text = '';
     135
     136                foreach ( $parts as $index => $part ) {
     137                        if ( $index % 2 === 0 )
     138                                $text .= preg_replace( $token_pattern, ' ', $part );
     139                }
     140
     141                $has_text = preg_match( '/\p{L}/u', html_entity_decode( $text, ENT_QUOTES, 'UTF-8' ) ) === 1;
     142
     143                // the icon is resolved only when the template places it, so its filters
     144                // never run for a template without %%icon%%
     145                $fragments = [ 'views' => (string) call_user_func( $views_renderer, $has_text ) ];
     146
     147                foreach ( $parts as $index => $part ) {
     148                        if ( $index % 2 === 0 ) {
     149                                $parts[$index] = preg_replace_callback(
     150                                        $token_pattern,
     151                                        static function ( $matches ) use ( &$fragments, $icon_renderer ) {
     152                                                // the template owns the spacing around the icon
     153                                                if ( ! isset( $fragments[$matches[1]] ) )
     154                                                        $fragments[$matches[1]] = rtrim( (string) call_user_func( $icon_renderer ) );
     155
     156                                                return $fragments[$matches[1]];
     157                                        },
     158                                        $part
     159                                );
     160                        }
     161                }
     162
     163                $class = 'pvc-custom-format content-' . $content_type . ' ' . $content_type . '-' . $content_id . ' entry-meta';
     164                $html = '<div class="' . esc_attr( $class ) . '" data-pvc-type="' . esc_attr( $content_type ) . '" data-pvc-id="' . esc_attr( $content_id ) . '" data-pvc-period="' . esc_attr( $period ) . '">' . implode( '', $parts ) . '</div>';
     165                $html = apply_filters( 'pvc_frontend_template_html', $html, $format, $content_id, $content_type, $period, $metrics );
     166
     167                return wp_kses( $html, Post_Views_Counter()->functions->get_counter_allowed_html() );
     168        }
     169
     170        /**
     171         * Get the metric tokens present in the text parts of a sanitized template.
     172         *
     173         * Tokens in HTML attributes are not rendered and therefore are not metrics.
     174         *
     175         * @param string $format Sanitized template HTML.
     176         * @return array
     177         */
     178        public static function get_template_text_metrics( $format ) {
     179                if ( ! is_scalar( $format ) )
     180                        return [];
     181
     182                $parts = preg_split( self::get_template_tag_pattern(), (string) $format, -1, PREG_SPLIT_DELIM_CAPTURE );
     183                $metrics = [];
     184                $token_pattern = '/%%(views)%%/';
     185
     186                foreach ( $parts as $index => $part ) {
     187                        if ( $index % 2 !== 0 || preg_match_all( $token_pattern, $part, $matches ) === false )
     188                                continue;
     189
     190                        foreach ( $matches[1] as $metric ) {
     191                                if ( ! in_array( $metric, $metrics, true ) )
     192                                        $metrics[] = $metric;
     193                        }
     194                }
     195
     196                return $metrics;
     197        }
     198
     199        /**
     200         * Get the HTML-tag pattern used to separate rendered text nodes.
     201         *
     202         * @return string
     203         */
     204        private static function get_template_tag_pattern() {
     205                return '/(<(?:"[^"]*"|\'[^\']*\'|[^\'">])*>)/';
     206        }
     207
     208        /**
     209         * Get the validated and translated saved frontend template.
     210         *
     211         * @return string
     212         */
     213        public function get_frontend_template() {
     214                $pvc = Post_Views_Counter();
     215                $default = $pvc->defaults['display']['frontend_template'];
     216                $source = isset( $pvc->options['display']['frontend_template'] ) && is_scalar( $pvc->options['display']['frontend_template'] ) ? (string) $pvc->options['display']['frontend_template'] : $default;
     217
     218                if ( function_exists( 'icl_t' ) ) {
     219                        $translated = icl_t( 'Post Views Counter', 'Frontend Template', $source );
     220                        $source = is_scalar( $translated ) ? (string) $translated : $source;
     221                }
     222
     223                return wp_kses_post( $source );
     224        }
     225
     226        /**
     227         * Get the counter icon HTML for a metric.
     228         *
     229         * The icon is a CSS-masked glyph painted on an empty span, so the counter
     230         * never loads the Dashicons font. Returning a class from
     231         * 'pvc_counter_icon_class' replaces our glyph with that class instead; the
     232         * stored 'icon_class' setting no longer takes part in rendering.
     233         *
     234         * @param string $metric Metric name; always 'views'.
     235         * @param int    $content_id Content ID.
     236         * @param string $content_type Content type.
     237         * @param string $icon_filter Metric-specific filter applied to the icon HTML.
     238         * @return string
     239         */
     240        public function get_counter_icon( $metric = 'views', $content_id = 0, $content_type = 'post', $icon_filter = '' ) {
     241                $metric = 'views';
     242                $content_id = (int) $content_id;
     243                $content_type = sanitize_key( (string) $content_type );
     244
     245                // a custom class is the only supported way to replace the built-in glyph
     246                $class = apply_filters( 'pvc_counter_icon_class', '', $metric, $content_id, $content_type );
     247                $class = is_scalar( $class ) ? Post_Views_Counter()->functions->sanitize_html_class_list( (string) $class ) : '';
     248
     249                if ( $class !== '' )
     250                        $default = '<span class="pvc-icon pvc-icon-custom ' . esc_attr( $class ) . '" aria-hidden="true"></span> ';
     251                else
     252                        $default = '<span class="pvc-icon" aria-hidden="true"></span> ';
     253
     254                // the released metric-specific filters keep their markup-level authority
     255                if ( is_string( $icon_filter ) && $icon_filter !== '' ) {
     256                        $icon = apply_filters( $icon_filter, $default, $content_id, $content_type );
     257                        $icon = is_scalar( $icon ) ? (string) $icon : '';
     258                } else
     259                        $icon = $default;
     260
     261                $icon = apply_filters( 'pvc_counter_icon', $icon, $metric, $content_id, $content_type );
     262
     263                return is_scalar( $icon ) ? (string) $icon : '';
     264        }
     265
     266        /**
     267         * Render a single metric target.
     268         *
     269         * The single place a Views target is built, for every content type and render
     270         * path. Preset roots may be divs; custom tokens are spans. Each target carries
     271         * its loading/data identity and contains its own count element so current and
     272         * legacy extension loaders can consume it.
     273         *
     274         * @param string $metric Metric name; only 'views' is rendered.
     275         * @param int    $content_id Content ID.
     276         * @param string $content_type Content type.
     277         * @param string $period Display period.
     278         * @param array  $args Optional per-caller overrides.
     279         * @param array|null $resolved Resolved value, label and icon output.
     280         * @return string
     281         */
     282        public function render_metric_span( $metric, $content_id, $content_type, $period, $args = [], &$resolved = null ) {
     283                $resolved = [ 'value' => null, 'label' => '', 'icon' => '' ];
     284
     285                if ( $metric !== 'views' )
     286                        return '';
     287
     288                $options = Post_Views_Counter()->options['display'];
     289                $content_id = (int) $content_id;
     290                $content_type = sanitize_key( (string) $content_type );
     291                $period = sanitize_key( (string) $period );
     292                $supplied = is_array( $args ) ? $args : [];
     293
     294                $args = array_merge(
     295                        [
     296                                'base_class'                    => 'post-views content-' . $content_type . ' ' . $content_type . '-' . $content_id . ' entry-meta',
     297                                'class_filter'                  => 'pvc_post_views_class',
     298                                'label_filter'                  => 'pvc_post_views_label',
     299                                'icon_filter'                   => 'pvc_post_views_icon',
     300                                'html_filter'                   => '',
     301                                'number_format_filter'  => 'pvc_post_views_number_format',
     302                                'wpml_context'                  => 'Post Views Counter',
     303                                'wpml_name'                             => 'Post Views Label',
     304                                'wpml_fallbacks'                => [],
     305                                'inline_icon'                   => false,
     306                                'force_sr_label'                => false,
     307                                // a custom format with words of its own needs no label, visible or hidden
     308                                'omit_label'                    => false,
     309                                // null follows the dynamic_loading option; false forces a static span for
     310                                // content that is not a dynamic target, such as site-wide totals
     311                                'dynamic'                               => null,
     312                                // a pre-resolved icon, so a container that already rendered one does not
     313                                // fire the icon filter a second time
     314                                'icon'                                  => null,
     315                                // an explicit label bypasses the option and WPML lookup entirely
     316                                'label'                                 => null,
     317                                // preset output uses a div root; custom tokens use spans
     318                                'tag'                                   => 'span',
     319                                'before'                                => '',
     320                                'after'                                 => ''
     321                        ],
     322                        $supplied
     323                );
     324
     325                // resolve the value
     326                if ( array_key_exists( 'value', $supplied ) )
     327                        $views = $supplied['value'];
     328                else {
     329                        $getter = 'pvc_get_' . $content_type . '_views';
     330                        $views = function_exists( $getter ) ? call_user_func( $getter, $content_id, $period ) : 0;
     331                }
     332
     333                $value = ! empty( $options['use_format'] ) ? number_format_i18n( $views ) : $views;
     334
     335                if ( $args['number_format_filter'] !== '' )
     336                        $value = apply_filters( $args['number_format_filter'], $value, $content_id, $content_type, $period );
     337
     338                // resolve the label
     339                $raw_label = isset( $options['label'] ) && is_scalar( $options['label'] ) ? (string) $options['label'] : 'Views:';
     340                $label_supplied = array_key_exists( 'label', $supplied ) && is_scalar( $supplied['label'] );
     341                $label_default = $label_supplied ? (string) $supplied['label'] : $raw_label;
     342
     343                if ( ! $label_supplied && function_exists( 'icl_t' ) ) {
     344                        $translated = icl_t( $args['wpml_context'], $args['wpml_name'], $raw_label );
     345                        $label_default = is_scalar( $translated ) ? (string) $translated : $raw_label;
     346
     347                        // read-fallback: honor the legacy per-content-type string slots for one release
     348                        if ( $label_default === $raw_label && is_array( $args['wpml_fallbacks'] ) ) {
     349                                foreach ( $args['wpml_fallbacks'] as $fallback ) {
     350                                        if ( ! is_array( $fallback ) || count( $fallback ) < 2 )
     351                                                continue;
     352
     353                                        $candidate = icl_t( $fallback[0], $fallback[1], $raw_label );
     354
     355                                        if ( is_scalar( $candidate ) && (string) $candidate !== $raw_label ) {
     356                                                $label_default = (string) $candidate;
     357
     358                                                break;
     359                                        }
     360                                }
     361                        }
     362                }
     363
     364                $label = apply_filters( $args['label_filter'], $label_default, $content_id, $content_type );
     365                $label = is_scalar( $label ) ? (string) $label : '';
     366                $metric_label = $label !== '' ? $label : __( 'Views:', 'post-views-counter' );
     367
     368                // build the class string
     369                $class = apply_filters( $args['class_filter'], $args['base_class'], $content_id, $content_type );
     370                $class = is_scalar( $class ) ? (string) $class : $args['base_class'];
     371                $dynamic = $args['dynamic'] === null ? ! empty( $options['dynamic_loading'] ) : (bool) $args['dynamic'];
     372                $class .= $dynamic ? ' load-dynamic' : ' load-static';
     373
     374                $icon = is_string( $args['icon'] ) ? $args['icon'] : $this->get_counter_icon( 'views', $content_id, $content_type, $args['icon_filter'] );
     375                $show_icon = ! empty( $options['display_style']['icon'] ) && ! empty( $args['inline_icon'] );
     376                $show_text = ! empty( $options['display_style']['text'] ) && empty( $args['force_sr_label'] );
     377
     378                if ( ! empty( $args['omit_label'] ) )
     379                        $label_html = '';
     380                elseif ( $show_text )
     381                        $label_html = '<span class="post-views-label pvc-label">' . esc_html( $label ) . '</span> ';
     382                else
     383                        $label_html = '<span class="screen-reader-text pvc-metric-label pvc-label">' . esc_html( $metric_label ) . '</span> ';
     384
     385                $tag = $args['tag'] === 'div' ? 'div' : 'span';
     386                $before = is_scalar( $args['before'] ) ? (string) $args['before'] : '';
     387                $after = is_scalar( $args['after'] ) ? (string) $args['after'] : '';
     388                $html = '<' . $tag . ' class="' . esc_attr( $class ) . '" data-pvc-metric="views" data-pvc-type="' . esc_attr( $content_type ) . '" data-pvc-id="' . esc_attr( $content_id ) . '" data-pvc-period="' . esc_attr( $period ) . '"'
     389                        . ' data-pvc-server-formatted="true">'
     390                        . $before
     391                        . ( $show_icon ? $icon : '' )
     392                        . $label_html
     393                        . '<span class="post-views-count pvc-count">' . wp_kses_post( $value ) . '</span>'
     394                        . $after
     395                        . '</' . $tag . '>';
     396
     397                // report the resolved parts so a caller filtering at container level can
     398                // pass the same arguments without re-running the value and label filters
     399                $resolved = [ 'value' => $value, 'label' => $label, 'icon' => $icon ];
     400
     401                if ( is_string( $args['html_filter'] ) && $args['html_filter'] !== '' ) {
     402                        $html = apply_filters( $args['html_filter'], $html, $content_id, $value, $label, $icon, $content_type, $period );
     403                        $html = is_scalar( $html ) ? (string) $html : '';
     404                }
     405
     406                return $html;
     407        }
     408
     409        /**
     410         * Render the unified counter container.
     411         *
     412         * Preserves the released post-views root as the Views target and adds one
     413         * icon and the container classes, so context and object classes remain on
     414         * the root once.
     415         *
     416         * @param array  $metrics Metric names; only 'views' is rendered.
     417         * @param int    $content_id Content ID.
     418         * @param string $content_type Content type.
     419         * @param string $period Display period.
     420         * @param array  $args Optional; 'metric_args' holds the 'views' overrides.
     421         * @param array|null $resolved_out Resolved values keyed by metric.
     422         * @return string
     423         */
     424        public function render_counter( $metrics, $content_id, $content_type, $period, $args = [], &$resolved_out = null ) {
     425                $resolved_out = [];
     426                $metrics = is_array( $metrics ) ? $metrics : [ $metrics ];
     427
     428                if ( ! in_array( 'views', $metrics, true ) )
     429                        return '';
     430
     431                $content_id = (int) $content_id;
     432                $content_type = sanitize_key( (string) $content_type );
     433                $period = sanitize_key( (string) $period );
     434                $args = is_array( $args ) ? $args : [];
     435                $per_metric = isset( $args['metric_args'] ) && is_array( $args['metric_args'] ) ? $args['metric_args'] : [];
     436                $root_args = isset( $per_metric['views'] ) && is_array( $per_metric['views'] ) ? $per_metric['views'] : [];
     437
     438                // One container, one icon. The released metric-specific icon filter still
     439                // runs. Callers such as pvc_site_views() may pass a pre-resolved icon to
     440                // preserve their per-call icon contract.
     441                $icon_filter = isset( $root_args['icon_filter'] ) && is_string( $root_args['icon_filter'] ) ? $root_args['icon_filter'] : 'pvc_post_views_icon';
     442                $icon = isset( $root_args['icon'] ) && is_string( $root_args['icon'] )
     443                        ? $root_args['icon']
     444                        : $this->get_counter_icon( 'views', $content_id, $content_type, $icon_filter );
     445
     446                // Every root keeps the released `.post-views` class. Two independent
     447                // reasons, both load-bearing:
     448                //
     449                // 1. It is the released styling hook. The shipped 1.7.15 stylesheet is
     450                //    written entirely against `.post-views`, and a decade of theme and user
     451                //    CSS targets it.
     452                // 2. Replacing the Core frontend script can leave only a legacy loader,
     453                //    which collects targets
     454                //    with getElementsByClassName( 'post-views' ). A dynamic count is held at
     455                //    `color: transparent` until JS adds `.loaded` or `.load-error`, so a root
     456                //    that loader never claims stays permanently invisible.
     457                $root_base = isset( $root_args['base_class'] ) && is_scalar( $root_args['base_class'] )
     458                        ? (string) $root_args['base_class']
     459                        : 'post-views content-' . $content_type . ' ' . $content_type . '-' . $content_id . ' entry-meta';
     460
     461                $root_args['base_class'] = trim( (string) $root_base ) . ' pvc-container pvc-views';
     462                $root_args['inline_icon'] = true;
     463                $root_args['icon'] = $icon;
     464                $root_args['tag'] = 'div';
     465                $html = $this->render_metric_span( 'views', $content_id, $content_type, $period, $root_args, $root_resolved );
     466
     467                if ( $html === '' )
     468                        return '';
     469
     470                $html = apply_filters( 'pvc_counter_html', $html, [ 'views' ], $content_id, $content_type, $period );
     471
     472                // hand back what the counter resolved to, so a caller applying its own
     473                // complete-counter filter can report the displayed value
     474                $resolved_out = [ 'views' => $root_resolved ];
     475
     476                return is_scalar( $html ) ? (string) $html : '';
    63477        }
    64478
    … …  
    164578                if ( (bool) apply_filters( 'pvc_display_views_count', $display ) === true ) {
    165579                        $filter = apply_filters( 'pvc_shortcode_filter_hook', $pvc->options['display']['position'] );
    166 
    167                         switch ( $filter ) {
    168                                 case 'after':
    169                                         $content = $content . do_shortcode( '[post-views]' );
    170                                         break;
    171 
    172                                 case 'before':
    173                                         $content = do_shortcode( '[post-views]' ) . $content;
     580                                $custom = isset( $pvc->options['display']['frontend_counter_metrics'] ) && $pvc->options['display']['frontend_counter_metrics'] === 'custom';
     581                                $output = $custom ? $this->post_views_shortcode( [ 'format' => $this->get_frontend_template() ] ) : do_shortcode( '[post-views]' );
     582
     583                                switch ( $filter ) {
     584                                        case 'after':
     585                                                $content = $content . $output;
     586                                                break;
     587
     588                                        case 'before':
     589                                                $content = $output . $content;
    174590                                        break;
    175591
    … …  
    203619                // enable styles?
    204620                if ( (bool) apply_filters( 'pvc_enqueue_styles', true ) === true ) {
    205                         // load dashicons
    206                         wp_enqueue_style( 'dashicons' );
    207 
    208621                        // load style
    209622                        wp_enqueue_style( 'post-views-counter-frontend', POST_VIEWS_COUNTER_URL . '/css/frontend.css', [], $pvc->defaults['version'] );
    … …  
    214627                        return;
    215628
     629                $dynamic_loading = ! empty( $pvc->options['display']['dynamic_loading'] );
     630
    216631                // get countable post types
    217632                $post_types = (array) $pvc->options['general']['post_types_count'];
    218 
    219                 // whether to count this post type or not
    220                 if ( empty( $post_types ) || ! is_singular( $post_types ) )
    221                         return;
    222 
    223                 // get current post id
    224633                $post_id = (int) get_the_ID();
    225 
    226                 // allow to run check post?
    227                 if ( ! (bool) apply_filters( 'pvc_run_check_post', true, $post_id ) )
    228                         return;
    229 
    230                 // get counter mode
    231634                $mode = $pvc->options['general']['counter_mode'];
    232 
    233                 // specific counter mode?
    234                 if ( in_array( $mode, [ 'js', 'rest_api' ], true ) ) {
     635                $should_count = ! empty( $post_types ) && is_singular( $post_types ) && (bool) apply_filters( 'pvc_run_check_post', true, $post_id );
     636
     637                if ( $dynamic_loading || ( $should_count && in_array( $mode, [ 'js', 'rest_api' ], true ) ) ) {
    235638                        wp_enqueue_script( 'post-views-counter-frontend', POST_VIEWS_COUNTER_URL . '/js/frontend.js', [], $pvc->defaults['version'], false );
    236639
    … …  
    238641                        $args = [
    239642                                'mode'                  => $mode,
     643                                'countEnabled'  => $should_count && in_array( $mode, [ 'js', 'rest_api' ], true ),
    240644                                'postID'                => $post_id,
    241645                                'requestURL'    => '',
     646                                'displayURL'    => untrailingslashit( rest_url( 'post-views-counter/get-post-views' ) ),
    242647                                'nonce'                 => '',
    243                                 'dataStorage'   => $pvc->options['general']['data_storage'],
    244                                 'multisite'             => ( is_multisite() ? (int) get_current_blog_id() : false ),
     648                                        'dataStorage'   => $pvc->options['general']['data_storage'],
     649                                        'multisite'             => ( is_multisite() ? (int) get_current_blog_id() : false ),
    245650                                'path'                  => empty( COOKIEPATH ) || ! is_string( COOKIEPATH ) ? '/' : COOKIEPATH,
    246651                                'domain'                => empty( COOKIE_DOMAIN ) || ! is_string( COOKIE_DOMAIN ) ? '' : COOKIE_DOMAIN
  • post-views-counter/tags/1.8.0/includes/class-functions.php

    r3280246 r3734102  
    196196                return $color;
    197197        }
     198
     199        /**
     200         * Convert an RGB color to HSL.
     201         *
     202         * Deliberately dependency-free: the only consumer is the derived Visits
     203         * color, which needs a hue rotation and nothing a perceptual color library
     204         * would add.
     205         *
     206         * @param int $r Red channel, 0-255.
     207         * @param int $g Green channel, 0-255.
     208         * @param int $b Blue channel, 0-255.
     209         * @return array Hue in degrees, saturation and lightness as 0-1 floats.
     210         */
     211        public function rgb2hsl( $r, $g, $b ) {
     212                $r = (int) $r / 255;
     213                $g = (int) $g / 255;
     214                $b = (int) $b / 255;
     215
     216                $max = max( $r, $g, $b );
     217                $min = min( $r, $g, $b );
     218                $delta = $max - $min;
     219                $l = ( $max + $min ) / 2;
     220
     221                // gray has no hue and no saturation
     222                if ( $delta == 0 )
     223                        return [ 'h' => 0.0, 's' => 0.0, 'l' => (float) $l ];
     224
     225                $s = $l > 0.5 ? $delta / ( 2 - $max - $min ) : $delta / ( $max + $min );
     226
     227                if ( $max === $r )
     228                        $h = fmod( ( $g - $b ) / $delta, 6 );
     229                elseif ( $max === $g )
     230                        $h = ( $b - $r ) / $delta + 2;
     231                else
     232                        $h = ( $r - $g ) / $delta + 4;
     233
     234                $h *= 60;
     235
     236                if ( $h < 0 )
     237                        $h += 360;
     238
     239                return [ 'h' => (float) $h, 's' => (float) $s, 'l' => (float) $l ];
     240        }
     241
     242        /**
     243         * Resolve one HSL channel back to its 0-1 RGB value.
     244         *
     245         * @param float $p Lower bound.
     246         * @param float $q Upper bound.
     247         * @param float $t Normalized hue offset.
     248         * @return float
     249         */
     250        private function hue2channel( $p, $q, $t ) {
     251                if ( $t < 0 )
     252                        $t += 1;
     253
     254                if ( $t > 1 )
     255                        $t -= 1;
     256
     257                if ( $t < 1 / 6 )
     258                        return $p + ( $q - $p ) * 6 * $t;
     259
     260                if ( $t < 1 / 2 )
     261                        return $q;
     262
     263                if ( $t < 2 / 3 )
     264                        return $p + ( $q - $p ) * ( 2 / 3 - $t ) * 6;
     265
     266                return $p;
     267        }
     268
     269        /**
     270         * Convert an HSL color to RGB.
     271         *
     272         * @param float $h Hue in degrees.
     273         * @param float $s Saturation, 0-1.
     274         * @param float $l Lightness, 0-1.
     275         * @return array
     276         */
     277        public function hsl2rgb( $h, $s, $l ) {
     278                $h = fmod( (float) $h, 360 );
     279
     280                if ( $h < 0 )
     281                        $h += 360;
     282
     283                $h /= 360;
     284                $s = min( max( (float) $s, 0 ), 1 );
     285                $l = min( max( (float) $l, 0 ), 1 );
     286
     287                if ( $s == 0 ) {
     288                        $value = (int) round( $l * 255 );
     289
     290                        return [ 'r' => $value, 'g' => $value, 'b' => $value ];
     291                }
     292
     293                $q = $l < 0.5 ? $l * ( 1 + $s ) : $l + $s - $l * $s;
     294                $p = 2 * $l - $q;
     295
     296                return [
     297                        'r' => (int) round( $this->hue2channel( $p, $q, $h + 1 / 3 ) * 255 ),
     298                        'g' => (int) round( $this->hue2channel( $p, $q, $h ) * 255 ),
     299                        'b' => (int) round( $this->hue2channel( $p, $q, $h - 1 / 3 ) * 255 )
     300                ];
     301        }
     302
     303        /**
     304         * Derive the secondary Visits color from the Views color.
     305         *
     306         * Color means metric everywhere PVC draws a chart, so Visits needs exactly
     307         * one companion color to whatever the admin theme gives Views. Rotating the
     308         * hue by a fixed 45 degrees, capping saturation and clamping lightness keeps
     309         * the result distinguishable and legible on every supported admin scheme
     310         * without carrying a perceptual-color dependency. The dashed line remains
     311         * the authoritative non-color distinction.
     312         *
     313         * @param array|null $rgb Source Views color; the admin-theme color by default.
     314         * @return array
     315         */
     316        public function get_visits_color( $rgb = null ) {
     317                // the historical Visits purple, used whenever the source is unusable
     318                $fallback = [ 'r' => 116, 'g' => 72, 'b' => 157 ];
     319
     320                if ( $rgb === null )
     321                        $rgb = $this->get_colors();
     322
     323                if ( ! is_array( $rgb ) )
     324                        return $fallback;
     325
     326                $source = [];
     327
     328                foreach ( [ 'r', 'g', 'b' ] as $channel ) {
     329                        if ( ! isset( $rgb[$channel] ) || ! is_numeric( $rgb[$channel] ) )
     330                                return $fallback;
     331
     332                        $value = (int) $rgb[$channel];
     333
     334                        if ( $value < 0 || $value > 255 )
     335                                return $fallback;
     336
     337                        $source[$channel] = $value;
     338                }
     339
     340                $hsl = $this->rgb2hsl( $source['r'], $source['g'], $source['b'] );
     341
     342                if ( ! is_array( $hsl ) || ! isset( $hsl['h'], $hsl['s'], $hsl['l'] ) )
     343                        return $fallback;
     344
     345                // rotate hue with wraparound, cap saturation, clamp lightness
     346                $derived = $this->hsl2rgb( fmod( $hsl['h'] + 45, 360 ), min( $hsl['s'], 0.6 ), min( max( $hsl['l'], 0.35 ), 0.5 ) );
     347
     348                if ( ! is_array( $derived ) || ! isset( $derived['r'], $derived['g'], $derived['b'] ) )
     349                        return $fallback;
     350
     351                return $derived;
     352        }
     353
     354        /**
     355         * Get the canonical Chart.js dataset style for one metric.
     356         *
     357         * Views and Visits look the same wherever they are drawn: the dashboard
     358         * charts, the column modal, and the extension-provided charts. Views uses the admin-theme color, Visits the color
     359         * derived from it, and the line variant separates them a second time with a
     360         * dash pattern so the two series stay distinguishable without color.
     361         *
     362         * @param string $metric Metric identifier, 'views' or 'visits'.
     363         * @param string $type Chart type, 'line' or 'bar'.
     364         * @param array  $context Optional presentation context. Only 'fill' is read:
     365         *                        true draws the filled one- or two-line presentation.
     366         * @return array
     367         */
     368        public function get_metric_dataset_style( $metric = 'views', $type = 'line', $context = [] ) {
     369                $metric = $metric === 'visits' ? 'visits' : 'views';
     370                $type = $type === 'bar' ? 'bar' : 'line';
     371                $context = is_array( $context ) ? $context : [];
     372                $context = [ 'fill' => ! empty( $context['fill'] ) ];
     373
     374                $color = $metric === 'visits' ? $this->get_visits_color() : $this->get_colors();
     375                $rgb = $color['r'] . ',' . $color['g'] . ',' . $color['b'];
     376
     377                if ( $type === 'bar' ) {
     378                        // Only the ranked Views reports draw bars, one dataset per chart, so
     379                        // there is no Visits bar variant. borderDash is deliberately absent -
     380                        // the Chart.js bar element has no such property and silently ignores
     381                        // it. Bar geometry never reads the fill context. The 0.2 fill is
     382                        // legacy report bars.
     383                        $style = [
     384                                'backgroundColor'               => 'rgba(' . $rgb . ',0.2)',
     385                                'borderColor'                   => 'rgba(' . $rgb . ',1)',
     386                                'borderWidth'                   => 0,
     387                                'grouped'                               => false,
     388                                'categoryPercentage'    => 0.9,
     389                                'barPercentage'                 => 0.9
     390                        ];
     391                } else {
     392                        // One unified line presentation for every PVC line chart: a 2-pixel
     393                        // rounded stroke, a 0.4 tension curve, and white points with a
     394                        // 2-pixel stroke in the series color. The area fill varies only when a
     395                        // caller explicitly requests the unfilled presentation.
     396                        $style = [
     397                                'borderColor'                   => 'rgba(' . $rgb . ',1)',
     398                                'backgroundColor'               => 'rgba(' . $rgb . ',' . ( $context['fill'] ? '0.1' : '0.15' ) . ')',
     399                                'borderWidth'                   => 2,
     400                                'borderCapStyle'                => 'round',
     401                                'borderJoinStyle'               => 'round',
     402                                'borderDash'                    => $metric === 'visits' ? [ 6, 4 ] : [],
     403                                'fill'                                  => $context['fill'] ? 'origin' : false,
     404                                'tension'                               => 0.4,
     405                                'pointRadius'                   => 3,
     406                                'pointHoverRadius'              => 5,
     407                                'pointBackgroundColor'  => 'rgba(255,255,255,1)',
     408                                'pointBorderColor'              => 'rgba(' . $rgb . ',1)',
     409                                'pointBorderWidth'              => 2
     410                        ];
     411                }
     412
     413                // the context is additive; the first three arguments keep their meaning
     414                return apply_filters( 'pvc_metric_dataset_style', $style, $metric, $type, $context );
     415        }
     416
     417        /**
     418         * Attach locale-formatted tooltip values to Chart.js datasets.
     419         *
     420         * Tooltips print these strings, so they match the admin column, which
     421         * formats with number_format_i18n(). Entities are decoded because Chart.js
     422         * draws text on a canvas. A value that is not numeric stays null.
     423         *
     424         * @param array $chart Chart.js data object holding a datasets list.
     425         * @return array
     426         */
     427        public function format_chart_datasets( $chart ) {
     428                if ( ! is_array( $chart ) || empty( $chart['datasets'] ) || ! is_array( $chart['datasets'] ) )
     429                        return $chart;
     430
     431                foreach ( $chart['datasets'] as $index => $dataset ) {
     432                        if ( ! is_array( $dataset ) || ! isset( $dataset['data'] ) || ! is_array( $dataset['data'] ) )
     433                                continue;
     434
     435                        $chart['datasets'][$index]['formatted'] = array_map( static function ( $value ) {
     436                                return is_numeric( $value ) ? html_entity_decode( number_format_i18n( (float) $value ), ENT_QUOTES, 'UTF-8' ) : null;
     437                        }, $dataset['data'] );
     438                }
     439
     440                return $chart;
     441        }
     442
     443        /**
     444         * Get the allowed HTML for sanitizing rendered counter markup.
     445         *
     446         * wp_kses_post() strips inline SVG, which would silently empty the counter
     447         * icon. This is the post allow-list plus the two elements the shared icon
     448         * uses, so counter output can still be sanitized before it is echoed.
     449         *
     450         * @return array
     451         */
     452        public function get_counter_allowed_html() {
     453                $allowed = wp_kses_allowed_html( 'post' );
     454
     455                // kses lowercases attribute names; the HTML parser maps viewbox back to
     456                // viewBox for inline SVG, so the rendered icon is unaffected.
     457                $allowed['svg'] = [
     458                        'aria-hidden'   => true,
     459                        'class'                 => true,
     460                        'fill'                  => true,
     461                        'focusable'             => true,
     462                        'height'                => true,
     463                        'role'                  => true,
     464                        'viewbox'               => true,
     465                        'width'                 => true,
     466                        'xmlns'                 => true
     467                ];
     468                $allowed['path'] = [
     469                        'd'             => true,
     470                        'fill'  => true
     471                ];
     472
     473                return $allowed;
     474        }
     475
     476        /**
     477         * Sanitize a space-separated list of HTML classes.
     478         *
     479         * @param string $class Raw class list.
     480         * @return string
     481         */
     482        public function sanitize_html_class_list( $class ) {
     483                if ( ! is_scalar( $class ) )
     484                        return '';
     485
     486                $classes = preg_split( '/\s+/', trim( (string) $class ) );
     487                $classes = array_filter( array_map( 'sanitize_html_class', is_array( $classes ) ? $classes : [] ) );
     488
     489                return implode( ' ', array_unique( $classes ) );
     490        }
     491
     492        /**
     493         * Get the shared inline bar-chart counter icon.
     494         *
     495         * The single source of the plugin's bar-chart glyph: the admin columns inline
     496         * it and the admin menu icon is built from it. The frontend paints the same
     497         * glyph through a CSS mask, so its path data is duplicated in the stylesheet
     498         * and locked to this one by test.
     499         *
     500         * @return string
     501         */
     502        public function get_counter_icon_svg() {
     503                return '<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3.5 15v6a1.5 1.5 0 0 0 3 0v-6a1.5 1.5 0 0 0-3 0Zm7-6v12a1.5 1.5 0 0 0 3 0V9a1.5 1.5 0 0 0-3 0Zm7-6v18a1.5 1.5 0 0 0 3 0V3a1.5 1.5 0 0 0-3 0Z" /></svg>';
     504        }
     505
     506        /**
     507         * Get the counter icon as an admin menu icon.
     508         *
     509         * WordPress recolours a menu icon to the admin colour scheme only when it is
     510         * a base64 SVG data URI, by rewriting its fill attributes. The literal fill is
     511         * the default scheme's icon colour, so the icon is right before that script
     512         * runs.
     513         *
     514         * @return string
     515         */
     516        public function get_menu_icon_data_uri() {
     517                $svg = str_replace( 'fill="currentColor"', 'fill="#a7aaad"', $this->get_counter_icon_svg() );
     518
     519                return 'data:image/svg+xml;base64,' . base64_encode( $svg );
     520        }
    198521}
  • post-views-counter/tags/1.8.0/includes/class-import.php

    r3647380 r3734102  
    295295
    296296                // Preserve the pre-1.7.15 contract for filtered definitions that expose
    297                 // only the legacy Pro badge/availability flag.
     297                // only the legacy premium badge/availability flag.
    298298                if ( ! empty( $definition['pro_only'] ) ) {
    299299                        return class_exists( 'Post_Views_Counter_Pro' );
    … …  
    16811681         * Rejects malformed values, zero dates, year zero and impossible calendar
    16821682         * dates. This strict policy applies to Page Views Count source rows and to
    1683          * the reported source date range; Statify row handling is unchanged (D8-8).
     1683         * the reported source date range; Statify row handling is unchanged.
    16841684         *
    16851685         * @param mixed $value Source date.
  • post-views-counter/tags/1.8.0/includes/class-query.php

    r3514398 r3734102  
    5151         */
    5252        public function extend_pre_query( $query ) {
     53                $this->join_sql = '';
     54                $query->pvc_orderby = false;
     55                $query->pvc_query = false;
     56                $query->total_views = 0;
     57
     58                $orderby = isset( $query->query_vars['orderby'] ) ? $query->query_vars['orderby'] : '';
     59                $has_orderby = function( $metric ) use ( $orderby ) {
     60                        if ( is_array( $orderby ) )
     61                                return array_key_exists( $metric, $orderby );
     62
     63                        return is_string( $orderby ) && preg_match( '/(?:^|\s)' . preg_quote( $metric, '/' ) . '(?:\s|$)/', $orderby ) === 1;
     64                };
     65                $query->pvc_orderby = $has_orderby( 'post_views' );
     66
    5367                // skip empty sort order
    5468                if ( empty( $query->query_vars['orderby'] ) )
    … …  
    253267
    254268                // is it sorted by post views?
    255                 if ( ( $sql === '' && isset( $query->pvc_orderby ) && $query->pvc_orderby ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true )
     269                if ( ( $sql === '' && ! empty( $query->pvc_orderby ) ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true )
    256270                        $sql = ' AND pvc.type = 4';
    257271
    … …  
    260274                        global $wpdb;
    261275
    262                         $join .= " LEFT JOIN " . $wpdb->prefix . "post_views pvc ON pvc.id = " . $wpdb->prefix . "posts.ID" . $sql;
     276                        $content_sql = pvc_post_views_has_content_column() ? ' AND pvc.content = 0' : '';
     277                        $join .= " LEFT JOIN " . $wpdb->prefix . "post_views pvc ON pvc.id = " . $wpdb->prefix . "posts.ID" . $content_sql . $sql;
    263278
    264279                        $this->join_sql = $join;
    … …  
    280295        public function posts_groupby( $groupby, $query ) {
    281296                // is it sorted by post views or views_query is used?
    282                 if ( ( isset( $query->pvc_orderby ) && $query->pvc_orderby ) || ( isset( $query->pvc_query ) && $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true ) {
     297                if ( ! empty( $query->pvc_orderby ) || ! empty( $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true ) {
    283298                        global $pagenow;
    284299
    … …  
    334349
    335350                        // hide empty?
    336                         if ( ! isset( $query->query['views_query']['hide_empty'] ) || $query->query['views_query']['hide_empty'] === true )
    337                                 $groupby .= ' HAVING post_views > 0';
     351                        $views_query = isset( $query->query['views_query'] ) ? $query->query['views_query'] : [];
     352
     353                        if ( ! isset( $views_query['hide_empty'] ) || $views_query['hide_empty'] === true )
     354                                $groupby .= ' HAVING SUM( COALESCE( pvc.count, 0 ) ) > 0';
    338355                }
    339356
    … …  
    349366         */
    350367        public function posts_orderby( $orderby, $query ) {
    351                 // is it sorted by post views?
    352                 if ( ( isset( $query->pvc_orderby ) && $query->pvc_orderby ) ) {
    353                         // get order
    354                         $order = strtoupper( (string) $query->get( 'order' ) );
    355                         $org_orderby = $query->get( 'orderby' );
    356 
    357                         if ( ! in_array( $order, [ 'ASC', 'DESC' ], true ) )
    358                                 $order = 'DESC';
    359 
    360                         if ( is_array( $org_orderby ) && array_key_exists( 'post_views', $org_orderby ) ) {
    361                                 $post_views_order = strtoupper( (string) $org_orderby['post_views'] );
    362 
    363                                 if ( in_array( $post_views_order, [ 'ASC', 'DESC' ], true ) )
    364                                         $order = $post_views_order;
    365                         }
    366 
    367                         $post_views_orderby = 'post_views ' . $order;
    368 
    369                         if ( ! is_string( $orderby ) || trim( $orderby ) === '' )
    370                                 $orderby = $post_views_orderby;
    371                         elseif ( preg_match( '/\bpost_views\b/i', $orderby ) !== 1 )
    372                                 $orderby = $post_views_orderby . ', ' . $orderby;
    373                 }
    374 
    375                 return $orderby;
     368                global $wpdb;
     369
     370                if ( empty( $query->pvc_orderby ) )
     371                        return $orderby;
     372
     373                $requested = $query->get( 'orderby' );
     374                $default_order = strtoupper( (string) $query->get( 'order' ) );
     375
     376                if ( ! in_array( $default_order, [ 'ASC', 'DESC' ], true ) )
     377                        $default_order = 'DESC';
     378
     379                $tokens = $this->get_metric_orderby_tokens( $requested, $default_order );
     380                $nonmetric = $this->split_top_level_orderby( $orderby );
     381                $translated = [];
     382
     383                foreach ( $tokens as $token ) {
     384                        if ( $token['metric'] === 'post_views' && ! empty( $query->pvc_orderby ) )
     385                                $translated[] = 'SUM( COALESCE( pvc.count, 0 ) ) ' . $token['order'] . ( isset( $query->pvc_admin_column_sort ) && $query->pvc_admin_column_sort === 'views' ? ', ' . $wpdb->posts . '.ID ASC' : '' );
     386                        elseif ( $this->did_wordpress_orderby_token_emit_clause( $token['metric'], $query ) && ! empty( $nonmetric ) )
     387                                $translated[] = array_shift( $nonmetric );
     388                }
     389
     390                return implode( ', ', array_merge( $translated, $nonmetric ) );
     391        }
     392
     393        /**
     394         * Split rendered ORDER BY SQL at commas outside function calls and quoted
     395         * strings. WordPress uses FIELD( ... ) for the inclusion-order tokens.
     396         *
     397         * @param mixed $orderby Rendered ORDER BY SQL.
     398         * @return array
     399         */
     400        private function split_top_level_orderby( $orderby ) {
     401                if ( ! is_string( $orderby ) || trim( $orderby ) === '' )
     402                        return [];
     403
     404                $clauses = [];
     405                $start = 0;
     406                $depth = 0;
     407                $quote = '';
     408                $length = strlen( $orderby );
     409
     410                for ( $index = 0; $index < $length; $index++ ) {
     411                        $character = $orderby[$index];
     412
     413                        if ( $quote !== '' ) {
     414                                if ( $character === $quote && ( $index === 0 || $orderby[$index - 1] !== '\\' ) )
     415                                        $quote = '';
     416                                continue;
     417                        }
     418
     419                        if ( $character === "'" || $character === '"' || $character === '`' )
     420                                $quote = $character;
     421                        elseif ( $character === '(' )
     422                                $depth++;
     423                        elseif ( $character === ')' && $depth > 0 )
     424                                $depth--;
     425                        elseif ( $character === ',' && $depth === 0 ) {
     426                                $clause = trim( substr( $orderby, $start, $index - $start ) );
     427                                if ( $clause !== '' )
     428                                        $clauses[] = $clause;
     429                                $start = $index + 1;
     430                        }
     431                }
     432
     433                $clause = trim( substr( $orderby, $start ) );
     434                if ( $clause !== '' )
     435                        $clauses[] = $clause;
     436
     437                return $clauses;
     438        }
     439
     440        /**
     441         * Check whether a requested token emitted one rendered WordPress clause.
     442         *
     443         * This mirrors the supported WP 6.3 parse_orderby() branches. Requested
     444         * inclusion and meta tokens are not necessarily rendered, so they must not
     445         * consume a later clause while metric ordering is reconstructed.
     446         *
     447         * @param string $token Requested orderby token.
     448         * @param object $query WP_Query instance.
     449         * @return bool
     450         */
     451        private function did_wordpress_orderby_token_emit_clause( $token, $query ) {
     452                $token = (string) $token;
     453                $meta_clauses = is_object( $query->meta_query ) && method_exists( $query->meta_query, 'get_clauses' ) ? $query->meta_query->get_clauses() : [];
     454                $meta_clauses = is_array( $meta_clauses ) ? $meta_clauses : [];
     455                $primary_meta_clause = empty( $meta_clauses ) ? [] : reset( $meta_clauses );
     456                $primary_meta_key = is_array( $primary_meta_clause ) && ! empty( $primary_meta_clause['key'] ) ? (string) $primary_meta_clause['key'] : '';
     457
     458                switch ( $token ) {
     459                        // WP_Query::parse_orderby() renders these direct fields and aliases.
     460                        case 'post_name':
     461                        case 'post_author':
     462                        case 'post_date':
     463                        case 'post_title':
     464                        case 'post_modified':
     465                        case 'post_parent':
     466                        case 'post_type':
     467                        case 'name':
     468                        case 'author':
     469                        case 'date':
     470                        case 'title':
     471                        case 'modified':
     472                        case 'parent':
     473                        case 'type':
     474                        case 'ID':
     475                        case 'menu_order':
     476                        case 'comment_count':
     477                        case 'rand':
     478                                return true;
     479
     480                        case 'post__in':
     481                                return ! empty( $query->get( 'post__in' ) );
     482
     483                        case 'post_name__in':
     484                                return ! empty( $query->get( 'post_name__in' ) );
     485
     486                        case 'post_parent__in':
     487                                return ! empty( $query->get( 'post_parent__in' ) );
     488
     489                        case 'meta_value':
     490                        case 'meta_value_num':
     491                                return ! empty( $meta_clauses );
     492
     493                        case 'relevance':
     494                                // Core only emits search relevance for the standalone string token.
     495                                return ! empty( $query->get( 's' ) ) && $query->get( 'orderby' ) === 'relevance';
     496                }
     497
     498                if ( $primary_meta_key !== '' && $token === $primary_meta_key )
     499                        return true;
     500
     501                if ( array_key_exists( $token, $meta_clauses ) )
     502                        return true;
     503
     504                return preg_match( '/^RAND\([0-9]+\)$/i', $token ) === 1;
     505        }
     506
     507        /**
     508         * Preserve metric tokens at their original WP_Query orderby positions.
     509         *
     510         * @param mixed  $orderby Requested orderby value.
     511         * @param string $default_order Validated query-wide direction.
     512         * @return array
     513         */
     514        private function get_metric_orderby_tokens( $orderby, $default_order ) {
     515                $tokens = [];
     516
     517                if ( is_array( $orderby ) ) {
     518                        foreach ( $orderby as $key => $direction ) {
     519                                // a non-scalar direction (orderby[title][]=x) takes the default order
     520                                $order = is_scalar( $direction ) ? strtoupper( (string) $direction ) : $default_order;
     521                                $tokens[] = [
     522                                        'metric' => (string) $key,
     523                                        'order' => in_array( $order, [ 'ASC', 'DESC' ], true ) ? $order : $default_order
     524                                ];
     525                        }
     526
     527                        return $tokens;
     528                }
     529
     530                $parts = preg_split( '/\s+/', trim( (string) $orderby ) );
     531
     532                for ( $index = 0; $index < count( $parts ); $index++ ) {
     533                        $order = $default_order;
     534
     535                        if ( isset( $parts[$index + 1] ) && in_array( strtoupper( $parts[$index + 1] ), [ 'ASC', 'DESC' ], true ) )
     536                                $order = strtoupper( $parts[++$index] );
     537
     538                        $tokens[] = [ 'metric' => $parts[$index], 'order' => $order ];
     539                }
     540
     541                return $tokens;
    376542        }
    377543
    … …  
    384550         */
    385551        public function posts_distinct( $distinct, $query ) {
    386                 if ( ( ( isset( $query->pvc_groupby ) && $query->pvc_groupby ) || ( isset( $query->pvc_orderby ) && $query->pvc_orderby ) || ( isset( $query->pvc_query ) && $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true ) && ( strpos( $distinct, 'DISTINCT' ) === false ) )
     552                if ( ( ! empty( $query->pvc_groupby ) || ! empty( $query->pvc_orderby ) || ! empty( $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true ) && strpos( $distinct, 'DISTINCT' ) === false )
    387553                        $distinct = $distinct . ' DISTINCT ';
    388554
    … …  
    398564         */
    399565        public function posts_fields( $fields, $query ) {
    400                 if ( ( ! isset( $query->query['fields'] ) || $query->query['fields'] === '' || $query->query['fields'] === 'all' ) && ( ( isset( $query->pvc_orderby ) && $query->pvc_orderby ) || ( isset( $query->pvc_query ) && $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true ) )
    401                         $fields = $fields . ', SUM( COALESCE( pvc.count, 0 ) ) AS post_views';
     566                if ( ! isset( $query->query['fields'] ) || $query->query['fields'] === '' || $query->query['fields'] === 'all' ) {
     567                        if ( ! empty( $query->pvc_orderby ) || ! empty( $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true )
     568                                $fields .= ', SUM( COALESCE( pvc.count, 0 ) ) AS post_views';
     569                }
    402570
    403571                return $fields;
    … …  
    495663         */
    496664        public function the_posts( $posts, $query ) {
    497                 if ( ( isset( $query->pvc_orderby ) && $query->pvc_orderby ) || ( isset( $query->pvc_query ) && $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true ) {
     665                if ( ! empty( $query->pvc_orderby ) || ! empty( $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true ) {
    498666                        $sum = 0;
    499667
    … …  
    501669                        if ( ! empty( $posts ) ) {
    502670                                foreach ( $posts as $post ) {
    503                                         if ( ! empty( $post->post_views ) )
     671                                        if ( is_object( $post ) && ! empty( $post->post_views ) )
    504672                                                $sum += (int) $post->post_views;
    505673                                }
  • post-views-counter/tags/1.8.0/includes/class-settings-api.php

    r3640258 r3734102  
    742742                                        'target'        => ! empty( $condition['target'] ) ? sanitize_text_field( $condition['target'] ) : '',
    743743                                        'container'     => ! empty( $condition['container'] ) ? sanitize_text_field( $condition['container'] ) : '',
     744                                        // Opt-in only: a conditionally disabled checkbox or radio target is
     745                                        // cleared before it is disabled. Ordinary disabled controls keep
     746                                        // their value.
     747                                        'clear_on_disable' => ! empty( $condition['clear_on_disable'] ),
    744748                                ];
    745749                        }
    … …  
    10541058                }
    10551059
    1056                 return stripslashes_deep( $value );
     1060                return $value;
    10571061        }
    10581062
  • post-views-counter/tags/1.8.0/includes/class-settings-display.php

    r3470602 r3734102  
    6868                // get post types
    6969                $post_types = $this->pvc->functions->get_post_types();
     70                $frontend_template = isset( $this->pvc->options['display']['frontend_template'] ) && is_scalar( $this->pvc->options['display']['frontend_template'] ) ? (string) $this->pvc->options['display']['frontend_template'] : $this->pvc->defaults['display']['frontend_template'];
     71                $frontend_template = wp_kses_post( $frontend_template );
     72                $template_notice = empty( Post_Views_Counter_Frontend::get_template_text_metrics( $frontend_template ) ) ? ' <span class="pvc-frontend-template-empty-notice">' . esc_html__( 'This custom format currently displays no counter.', 'post-views-counter' ) . '</span>' : '';
    7073
    7174                // user groups
     75                // Rendering order only. Guests first, then the two mutually exclusive
     76                // logged-in audiences. The stored keys are unchanged.
    7277                $groups = [
    73                         'users'         => __( 'logged in users', 'post-views-counter' ),
    74                         'guests'        => __( 'guests', 'post-views-counter' ),
    75                         'roles'         => __( 'selected user roles', 'post-views-counter' )
     78                        'guests'        => __( 'Guests', 'post-views-counter' ),
     79                        'users'         => __( 'All logged-in users', 'post-views-counter' ),
     80                        'roles'         => __( 'Selected user roles', 'post-views-counter' )
    7681                ];
    7782
    … …  
    8085
    8186                return [
     87                        'frontend_counter_metrics' => [
     88                                'tab'                   => 'display',
     89                                'title'                 => __( 'Display Format', 'post-views-counter' ),
     90                                'section'               => 'post_views_counter_display_appearance',
     91                                'type'                  => 'select',
     92                                'html_id'               => 'pvc-frontend-counter-metrics',
     93                                /* translators: %s: [post-views] shortcode */
     94                                'description'   => sprintf( __( 'Choose the format of the counter inserted automatically on the frontend. A manually placed %s shortcode keeps the Views format.', 'post-views-counter' ), '<code>[post-views]</code>' ),
     95                                'validate'              => [ $this, 'validate_frontend_counter_metrics' ],
     96                                        'options'               => [
     97                                                'views'         => __( 'Views', 'post-views-counter' ),
     98                                                'custom'        => __( 'Custom format', 'post-views-counter' )
     99                                        ]
     100                                ],
     101                                'frontend_template' => [
     102                                        'tab'                   => 'display',
     103                                        'title'                 => __( 'Custom Format', 'post-views-counter' ),
     104                                        'section'               => 'post_views_counter_display_appearance',
     105                                        'type'                  => 'input',
     106                                        'html_id'               => 'pvc-frontend-template',
     107                                        'before_field'  => '<label class="screen-reader-text" for="pvc-frontend-template">' . esc_html__( 'Custom Format', 'post-views-counter' ) . '</label>',
     108                                        /* translators: 1: views token, 2: icon token */
     109                                        'description'   => sprintf( __( 'Build the counter with %1$s (the view count) and %2$s (the counter icon). Safe HTML is allowed. A format without %1$s displays nothing.', 'post-views-counter' ), '<code>%%views%%</code>', '<code>%%icon%%</code>' ) . $template_notice,
     110                                        'subclass'              => 'regular-text',
     111                                        'validate'              => [ $this, 'validate_frontend_template' ],
     112                                        'reset'                 => [ $this, 'reset_frontend_template' ],
     113                                        'logic'                 => [
     114                                                'field'         => 'pvc-frontend-counter-metrics',
     115                                                'operator'      => 'is',
     116                                                'value'         => 'custom'
     117                                        ],
     118                                        'animation'             => 'slide'
     119                                ],
    82120                        'label' => [
    83121                                'tab'                   => 'display',
    … …  
    89127                                'subclass'              => 'regular-text',
    90128                                'validate'              => [ $this, 'validate_label' ],
    91                                 'reset'                 => [ $this, 'reset_label' ]
     129                                'reset'                 => [ $this, 'reset_label' ],
     130                                        'logic'                 => [
     131                                                [
     132                                                        'field'         => 'pvc-frontend-counter-metrics',
     133                                                        'operator'      => 'isnot',
     134                                                        'value'         => 'custom'
     135                                                ]
     136                                        ],
     137                                'animation'             => 'slide'
    92138                        ],
    93139                        'display_period' => [
    … …  
    117163                                        'icon'  => __( 'Icon', 'post-views-counter' ),
    118164                                        'text'  => __( 'Label', 'post-views-counter' )
    119                                 ]
     165                                ],
     166                                // a custom format places the icon itself with %%icon%%, so the
     167                                // preset's icon and label choices do not apply to it
     168                                'logic'                 => [
     169                                        'field'         => 'pvc-frontend-counter-metrics',
     170                                        'operator'      => 'isnot',
     171                                        'value'         => 'custom'
     172                                ],
     173                                'animation'             => 'slide'
    120174                        ],
    121175                        'icon_class' => [
    … …  
    133187                                ],
    134188                                'animation'             => 'slide',
    135                                 'description'   => sprintf( __( 'Enter the CSS class for the views icon. Any Dashicons class is supported.', 'post-views-counter' ), 'https://developer.wordpress.org/resource/dashicons/' ),
     189                                // Retired: the counter icon is painted by a CSS mask and no longer uses
     190                                // an icon font. The stored value is kept untouched for a possible later
     191                                // use, but takes no part in rendering; 'pvc_counter_icon_class' is the
     192                                // supported way to supply a custom icon. skip_saving is what carries the
     193                                // stored value through a save of this tab, since an unrendered field
     194                                // submits nothing and would otherwise be reset to the default.
     195                                'skip_rendering'        => true,
     196                                'skip_saving'   => true,
     197                                'description'   => __( 'Retired. The counter icon no longer uses the Dashicons font. Use the pvc_counter_icon_class filter to supply a custom icon class.', 'post-views-counter' ),
    136198                                'subclass'              => 'regular-text'
    137199                        ],
    … …  
    163225                                'type'                  => 'boolean',
    164226                                'description'   => '',
    165                                 'label'                 => __( 'Allow editing the view count on the post edit screen.', 'post-views-counter' )
     227                                'label'                 => __( 'Allow manual editing of the Views total.', 'post-views-counter' )
    166228                        ],
    167229                        'dynamic_loading' => [
    … …  
    255317                                'section' => 'post_views_counter_display_visibility',
    256318                                'type' => 'checkbox',
    257                                 'description' => __( 'Hide the view counter for selected visitor groups.', 'post-views-counter' ),
     319                                'description' => __( 'Hide the automatically inserted counter from the selected visitor groups. Note: selecting both "Guests" and "All logged-in users" hides the automatic counter from everyone.', 'post-views-counter' ),
    258320                                'options' => $groups,
    259321                                'name' => 'post_views_counter_settings_display[restrict_display][groups]',
    260322                                'value' => $this->pvc->options['display']['restrict_display']['groups'],
    261323                                'validate' => [ $this, 'validate_restrict_display_groups' ],
     324                                // "Selected user roles" is unreachable while every logged-in user is
     325                                // already hidden, so the option is cleared and disabled in that state.
     326                                'logic' => [
     327                                        [
     328                                                'field' => 'restrict_display_groups',
     329                                                'operator' => 'containsnot',
     330                                                'value' => 'users',
     331                                                'scope' => 'option',
     332                                                'action' => 'enable',
     333                                                'target' => '#pvc-display-restrict-display-groups-roles',
     334                                                'clear_on_disable' => true
     335                                        ]
     336                                ],
    262337                        ],
    263338                        'restrict_display_roles' => [
    … …  
    266341                                'section' => 'post_views_counter_display_visibility',
    267342                                'type' => 'checkbox',
    268                                 'description' => __( 'Hide the view counter for selected user roles.', 'post-views-counter' ),
     343                                'description' => __( 'Hide the automatically inserted counter from the selected user roles.', 'post-views-counter' ),
    269344                                'options' => $user_roles,
    270345                                'name' => 'post_views_counter_settings_display[restrict_display][roles]',
    … …  
    325400                if ( function_exists( 'icl_register_string' ) )
    326401                        icl_register_string( 'Post Views Counter', 'Post Views Label', $default );
     402
     403                return $default;
     404        }
     405
     406        /**
     407         * Validate the metric selected for automatic frontend injection.
     408         *
     409         * @param mixed $input Submitted value.
     410         * @param array $field Field definition.
     411         * @return string
     412         */
     413        public function validate_frontend_counter_metrics( $input, $field ) {
     414                $input = is_scalar( $input ) ? sanitize_key( (string) $input ) : '';
     415
     416                return in_array( $input, [ 'views', 'custom' ], true ) ? $input : 'views';
     417        }
     418
     419        /**
     420         * Validate and register the custom frontend template.
     421         *
     422         * @param mixed $input Submitted value.
     423         * @param array $field Field definition.
     424         * @return string
     425         */
     426        public function validate_frontend_template( $input, $field ) {
     427                if ( is_scalar( $input ) )
     428                        $template = wp_kses_post( (string) $input );
     429                else {
     430                        $current = isset( $this->pvc->options['display']['frontend_template'] ) ? $this->pvc->options['display']['frontend_template'] : null;
     431                        $template = is_scalar( $current ) ? wp_kses_post( (string) $current ) : $this->pvc->defaults['display']['frontend_template'];
     432                }
     433
     434                if ( function_exists( 'icl_register_string' ) )
     435                        icl_register_string( 'Post Views Counter', 'Frontend Template', $template );
     436
     437                return $template;
     438        }
     439
     440        /**
     441         * Restore and register the default custom frontend template.
     442         *
     443         * @param string $default Default value.
     444         * @param array $field Field definition.
     445         * @return string
     446         */
     447        public function reset_frontend_template( $default, $field ) {
     448                $default = wp_kses_post( (string) $default );
     449
     450                if ( function_exists( 'icl_register_string' ) )
     451                        icl_register_string( 'Post Views Counter', 'Frontend Template', $default );
    327452
    328453                return $default;
    … …  
    407532                }
    408533
    409                 return array_unique( $groups );
     534                $groups = array_unique( $groups );
     535
     536                // "All logged-in users" and "Selected user roles" are alternatives: the role
     537                // branch is unreachable while every logged-in user is already hidden. Drop
     538                // the unreachable marker so a no-JavaScript submission cannot store a
     539                // contradictory state. The separately stored role list is never touched.
     540                if ( in_array( 'users', $groups, true ) )
     541                        $groups = array_diff( $groups, [ 'roles' ] );
     542
     543                return array_values( $groups );
    410544        }
    411545
  • post-views-counter/tags/1.8.0/includes/class-settings-emails.php

    r3556892 r3734102  
    618618
    619619        /**
    620          * Determine whether PVC Pro is active.
     620         * Determine whether the extension plugin is active.
    621621         *
    622622         * @return bool
  • post-views-counter/tags/1.8.0/includes/class-settings.php

    r3537950 r3734102  
    681681                        // add parameters
    682682                        $pages['post-views-counter']['type'] = 'page';
    683                         $pages['post-views-counter']['icon'] = 'dashicons-chart-bar';
     683                        $pages['post-views-counter']['icon'] = $pvc->functions->get_menu_icon_data_uri();
    684684                        $pages['post-views-counter']['position'] = '99.301';
    685685
    … …  
    938938                        // make sure we do not change anything in the settings
    939939                        $input = $pvc->options['other'];
    940 
    941                         if ( $wpdb->query( 'TRUNCATE TABLE ' . $wpdb->prefix . 'post_views' ) )
    942                                 add_settings_error( 'reset_post_views', 'reset_post_views', __( 'All existing data deleted successfully.', 'post-views-counter' ), 'updated' );
    943                         else
    944                                 add_settings_error( 'reset_post_views', 'reset_post_views', __( 'Error occurred. All existing data were not deleted.', 'post-views-counter' ), 'error' );
     940                        $this->reset_views_data();
    945941                // save general settings
    946942                } elseif ( isset( $_POST['save_post_views_counter_settings_general'] ) ) {
    … …  
    988984
    989985                return $input;
     986        }
     987
     988        /**
     989         * Delete shared counter data under a Visit-state write fence.
     990         *
     991         * @return void
     992         */
     993        private function reset_views_data() {
     994                global $wpdb;
     995
     996                $pvc = Post_Views_Counter();
     997                $visits = $pvc->visits instanceof Post_Views_Counter_Visits ? $pvc->visits : null;
     998
     999                if ( function_exists( 'Post_Views_Counter_Pro' ) ) {
     1000                        $pro = Post_Views_Counter_Pro();
     1001                        $pro_counter = isset( $pro->counter ) ? $pro->counter : null;
     1002
     1003                        if ( ! is_object( $pro_counter ) || ! method_exists( $pro_counter, 'supports_queue_generation' ) || ! $pro_counter->supports_queue_generation() ) {
     1004                                        add_settings_error( 'reset_post_views', 'reset_post_views', __( 'Queued counts cannot be retired safely. Update or deactivate Post Views Counter Pro before retrying.', 'post-views-counter' ), 'error' );
     1005                                return;
     1006                        }
     1007                }
     1008
     1009                $reset_locked = $visits === null || $visits->begin_measurement_fence();
     1010
     1011                if ( ! $reset_locked ) {
     1012                        add_settings_error( 'reset_post_views', 'reset_post_views', __( 'Error occurred. All existing data were not deleted.', 'post-views-counter' ), 'error' );
     1013                        return;
     1014                }
     1015
     1016                $timestamp = time();
     1017                $entry = [ 'code' => 'completed', 'raw' => null ];
     1018                $completion = [ 'code' => 'completed', 'raw' => null ];
     1019                $result = 'failed';
     1020                $fence_result = [ 'unlocked' => false, 'flushed' => false ];
     1021
     1022                try {
     1023                        if ( $visits !== null ) {
     1024                                $entry = $visits->enter_visit_reset_v0( $timestamp );
     1025
     1026                                if ( $entry['code'] !== 'completed' ) {
     1027                                        $result = 'entry_failed';
     1028                                } else {
     1029                                        // The descriptor is inert while fenced and is drained exactly once by
     1030                                        // the checked outermost unlock.
     1031                                        $visits->defer_measurement_fence_work( [ 'type' => 'state_caches', 'retire_reads' => true ] );
     1032                                }
     1033                        }
     1034
     1035                        if ( $result !== 'entry_failed' && $wpdb->query( 'TRUNCATE TABLE ' . $wpdb->prefix . 'post_views' ) === false )
     1036                                $result = 'truncate_failed';
     1037
     1038                        if ( $result === 'failed' && $visits !== null ) {
     1039                                $completion = $visits->complete_visit_reset_v0( $entry['raw'], $timestamp );
     1040                                $result = $completion['code'] === 'completed' ? 'completed' : 'completion_failed';
     1041                        } elseif ( $result === 'failed' )
     1042                                $result = 'completed';
     1043                } finally {
     1044                        if ( $visits !== null )
     1045                                $fence_result = $visits->end_measurement_fence();
     1046                }
     1047
     1048                if ( $visits !== null && ( empty( $fence_result['unlocked'] ) || empty( $fence_result['flushed'] ) ) )
     1049                        $result = 'retirement_failed';
     1050
     1051                // The failed unlock deliberately retains the SQL fence. Do not invoke a
     1052                // translated admin notice (or any post-fence publication) until recovery
     1053                // can release it; the version-zero state remains the durable failure signal.
     1054                if ( $visits !== null && $visits->is_measurement_fence_active() ) {
     1055                        error_log( 'Post Views Counter: measurement reset could not release its protected table fence.' );
     1056                        return;
     1057                }
     1058
     1059                if ( $visits !== null && $result !== 'entry_failed' && $result !== 'retirement_failed' ) {
     1060                        $promotion_raw = $result === 'completed' ? $completion['raw'] : $entry['raw'];
     1061
     1062                        if ( ! is_string( $promotion_raw ) || ! $visits->promote_visit_reset_v0( $promotion_raw ) )
     1063                                $result = 'promotion_failed';
     1064                }
     1065
     1066                if ( $result === 'completed' )
     1067                        add_settings_error( 'reset_post_views', 'reset_post_views', __( 'All existing data deleted successfully.', 'post-views-counter' ), 'updated' );
     1068                elseif ( $result === 'entry_failed' )
     1069                        add_settings_error( 'reset_post_views', 'reset_post_views', __( 'Visits state could not be reset safely, so no data was deleted.', 'post-views-counter' ), 'error' );
     1070                else
     1071                        add_settings_error( 'reset_post_views', 'reset_post_views', __( 'Error occurred. All existing data were not deleted.', 'post-views-counter' ), 'error' );
    9901072        }
    9911073
  • post-views-counter/tags/1.8.0/includes/class-traffic-signals.php

    r3514398 r3734102  
    1010 */
    1111class Post_Views_Counter_Traffic_Signals {
     12
     13        /**
     14         * Views floor on both sides of the comparison.
     15         *
     16         * @var int
     17         */
     18        const MIN_VIEWS = 10;
     19
     20        /**
     21         * The Views comparison fires above this change, in percent.
     22         *
     23         * @var int
     24         */
     25        const VIEWS_CHANGE = 25;
     26
     27        /**
     28         * The Entrances state fires at this change or more, in percent.
     29         *
     30         * @var int
     31         */
     32        const ENTRANCES_CHANGE = 25;
     33
     34        /**
     35         * Month comparisons read for this request, by period and post ID.
     36         *
     37         * @var array
     38         */
     39        private static $primed = [];
    1240
    1341        /**
    … …  
    2957                add_action( 'transition_post_status', [ $this, 'invalidate_signal_cache_on_status_change' ], 10, 3 );
    3058                add_action( 'deleted_post', [ $this, 'invalidate_signal_cache' ], 10, 1 );
     59                add_filter( 'the_posts', [ $this, 'prime_list_signals' ], 10, 2 );
    3160        }
    3261
    … …  
    93122
    94123                $signal_column = [
    95                         'traffic_signal' => '<span class="pvc-signal-header" title="' . esc_attr__( 'Traffic Signals', 'post-views-counter' ) . '"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M14.828 14.828 21 21"/><path d="M21 16v5h-5"/><path d="m21 3-9 9-4-4-6 6"/><path d="M21 8V3h-5"/></svg><span class="screen-reader-text">' . esc_html__( 'Traffic Signals', 'post-views-counter' ) . '</span></span>'
     124                        'traffic_signal' => '<span class="pvc-signal-header" data-pvc-tooltip="' . esc_attr__( 'Traffic Signals', 'post-views-counter' ) . '"><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M14.828 14.828 21 21"/><path d="M21 16v5h-5"/><path d="m21 3-9 9-4-4-6 6"/><path d="M21 8V3h-5"/></svg><span class="screen-reader-text">' . esc_html__( 'Traffic Signals', 'post-views-counter' ) . '</span></span>'
    96125                ];
    97126
    … …  
    118147                        return;
    119148                }
     149
     150                // signals disabled after the column was registered: no read, no state
     151                if ( ! $this->is_enabled() )
     152                        return;
    120153
    121154                // check if user can see stats
    … …  
    134167                        );
    135168                } else {
    136                         // anomaly detected - generic signal
     169                        // anomaly detected - generic signal; an Entrances state looks the same, Free never says which
    137170                        printf(
    138171                                '<span class="pvc-signal" role="tooltip" aria-label="%s" data-microtip-position="top" data-microtip-size="medium"><span class="pvc-insight pvc-insight-anomaly"></span></span>',
    … …  
    143176
    144177        /**
     178         * Read the signals of a posts list page with one query.
     179         *
     180         * Only rows whose signal is not cached are read; the result is kept for
     181         * this request, never in the object cache.
     182         *
     183         * @param array    $posts Posts returned by the main list query
     184         * @param WP_Query $query Query object
     185         * @return array Posts
     186         */
     187        public function prime_list_signals( $posts, $query = null ) {
     188                if ( ! is_admin() || empty( $posts ) || ! ( $query instanceof WP_Query ) || ! $query->is_main_query() || ! $this->is_enabled() )
     189                        return $posts;
     190
     191                $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
     192
     193                if ( ! $screen || $screen->base !== 'edit' || empty( $screen->post_type ) || $screen->post_type === 'attachment' )
     194                        return $posts;
     195
     196                $pvc = Post_Views_Counter();
     197
     198                if ( ! $pvc->options['display']['post_views_column'] || ! in_array( $screen->post_type, (array) $pvc->options['general']['post_types_count'], true ) )
     199                        return $posts;
     200
     201                // the Views column is removed when stats are denied, and the signal column with it
     202                if ( apply_filters( 'pvc_admin_display_post_views', true ) === false )
     203                        return $posts;
     204
     205                $post_ids = [];
     206
     207                // read only the rows the cell may show
     208                foreach ( $posts as $post ) {
     209                        if ( isset( $post->ID ) && apply_filters( 'pvc_admin_display_post_views', true, (int) $post->ID ) !== false )
     210                                $post_ids[] = (int) $post->ID;
     211                }
     212
     213                self::prime( $post_ids, $this->get_signal_now() );
     214
     215                return $posts;
     216        }
     217
     218        /**
     219         * Read and keep the month comparisons of uncached rows for this request.
     220         *
     221         * @internal Called for a list page and by tests; not a public API.
     222         *
     223         * @param array             $post_ids Post IDs
     224         * @param DateTimeInterface $now      Current time in the counting clock
     225         * @return void
     226         */
     227        public static function prime( $post_ids, $now ) {
     228                $periods = self::get_signal_periods( $now );
     229                $misses = [];
     230
     231                foreach ( array_unique( array_filter( array_map( 'absint', (array) $post_ids ) ) ) as $post_id ) {
     232                        if ( wp_cache_get( 'pvc_signal_' . $post_id . '_' . $periods['period'], 'post_views_counter' ) === false )
     233                                $misses[] = $post_id;
     234                }
     235
     236                if ( empty( $misses ) )
     237                        return;
     238
     239                $key = self::get_primed_key( $periods );
     240
     241                foreach ( self::get_month_comparisons( $misses, $periods, $now ) as $post_id => $values ) {
     242                        self::$primed[$key][$post_id] = $values;
     243                }
     244        }
     245
     246        /**
     247         * Forget the comparisons kept for this request.
     248         *
     249         * @internal For tests.
     250         *
     251         * @return void
     252         */
     253        public static function reset_primed() {
     254                self::$primed = [];
     255        }
     256
     257        /**
    145258         * Detect traffic signal using simple Month-over-Month comparison.
    146259         *
    147          * @param int $post_id Post ID
     260         * @param int                    $post_id Post ID
     261         * @param DateTimeInterface|null $now     Current time in the counting clock
    148262         * @return array|null Signal data or null if no anomaly
    149263         */
    150         private function detect_signal( $post_id ) {
     264        private function detect_signal( $post_id, $now = null ) {
     265                $now = $now instanceof DateTimeInterface ? $now : $this->get_signal_now();
     266                $periods = self::get_signal_periods( $now );
     267
    151268                // check cache first
    152                 $cache_key = 'pvc_signal_' . $post_id;
     269                $cache_key = 'pvc_signal_' . $post_id . '_' . $periods['period'];
    153270                $cache_group = 'post_views_counter';
    154271                $cached = wp_cache_get( $cache_key, $cache_group );
    155272
    156273                if ( $cached !== false ) {
     274                        // a cached Entrances state lasts only while both ranges stay derived
     275                        if ( is_array( $cached ) && ! empty( $cached['entrances'] ) && ! self::are_entrances_derived( $periods, $now ) ) {
     276                                wp_cache_delete( $cache_key, $cache_group );
     277                                return null;
     278                        }
     279
    157280                        return $cached;
    158281                }
    159282
    160                 // get current month views
    161                 $current_date = new DateTime();
    162                 $current_period = $current_date->format( 'Ym' );
    163 
    164                 $current_total = (int) pvc_get_post_views( $post_id, $current_period );
    165 
    166                 // minimum views threshold
    167                 if ( $current_total < 10 ) {
    168                         wp_cache_set( $cache_key, null, $cache_group, HOUR_IN_SECONDS );
     283                $values = self::get_month_comparison( $post_id, $periods, $now );
     284
     285                // a failed read is silence, never cached
     286                if ( $values === false ) {
    169287                        return null;
    170288                }
    171289
    172                 // get previous month views (same number of days as current month-to-date)
    173                 $prev_date = clone $current_date;
    174                 $prev_date->modify( '-1 month' );
    175                 $prev_year = $prev_date->format( 'Y' );
    176                 $prev_month = $prev_date->format( 'm' );
    177                 $prev_last_day = (int) $prev_date->format( 't' );
    178                 $current_day = (int) $current_date->format( 'd' );
    179                 // compare against the same number of days from last month (clamp to last day)
    180                 $end_day = min( $current_day, $prev_last_day );
    181 
    182                 $prev_start = (int) ( $prev_year . $prev_month . '01' );
    183                 $prev_end = (int) ( $prev_year . $prev_month . str_pad( (string) $end_day, 2, '0', STR_PAD_LEFT ) );
    184 
    185                 $prev_total = (int) pvc_get_post_views(
    186                         $post_id,
    187                         '',
    188                         [
    189                                 'type'         => 0,
    190                                 'period_range' => [ $prev_start, $prev_end ]
    191                         ]
    192                 );
    193 
    194                 // need both periods to compare
    195                 if ( $prev_total < 10 ) {
    196                         wp_cache_set( $cache_key, null, $cache_group, HOUR_IN_SECONDS );
    197                         return null;
    198                 }
    199 
    200                 // calculate change percentage
    201                 $change_percent = ( ( $current_total - $prev_total ) / $prev_total ) * 100;
    202 
    203                 // threshold: 25% change (up or down)
    204                 if ( abs( $change_percent ) > 25 ) {
     290                // current month views: month to date, or its first days on the 29th-31st; null when unproven
     291                $current_total = $values['current'];
     292
     293                // previous month views (same number of days as current month-to-date), null when pruned
     294                $prev_total = $values['previous'];
     295
     296                if ( self::is_views_anomaly( $current_total, $prev_total ) ) {
    205297                        $result = [
    206298                                'anomaly' => true,
    207                                 'change_percent' => round( $change_percent )
     299                                'change_percent' => round( ( ( $current_total - $prev_total ) / $prev_total ) * 100 )
    208300                        ];
     301                } elseif ( self::evaluate_entrances_state( self::get_entrances_evidence( $values ) ) !== null ) {
     302                        // the state carries no number and no direction
     303                        $result = [ 'entrances' => true ];
    209304                } else {
    210305                        $result = null;
    211306                }
    212307
    213                 // cache for 1 hour
    214                 wp_cache_set( $cache_key, $result, $cache_group, HOUR_IN_SECONDS );
     308                // cache for 1 hour, unless the Entrances read failed
     309                if ( ! isset( $values['cacheable'] ) || $values['cacheable'] ) {
     310                        wp_cache_set( $cache_key, $result, $cache_group, HOUR_IN_SECONDS );
     311                }
    215312
    216313                return $result;
     314        }
     315
     316        /**
     317         * Whether the Views comparison fires.
     318         *
     319         * @param int|null $current  Current month Views on the compared days, null when unknown
     320         * @param int|null $previous Views on the same days of the previous month, null when unknown
     321         * @return bool
     322         */
     323        private static function is_views_anomaly( $current, $previous ) {
     324                // minimum views threshold on both sides
     325                if ( ! is_int( $current ) || ! is_int( $previous ) || $current < self::MIN_VIEWS || $previous < self::MIN_VIEWS )
     326                        return false;
     327
     328                // threshold: more than 25% change (up or down)
     329                return abs( ( ( $current - $previous ) / $previous ) * 100 ) > self::VIEWS_CHANGE;
     330        }
     331
     332        /**
     333         * Decide the Entrances state.
     334         *
     335         * Shown only while the Views comparison is silent: month-to-date Entrances
     336         * against the same days of the previous month, on eligible data. A
     337         * previous value of 0 is a measured zero (the range is derived) and
     338         * triggers a rise without a percentage.
     339         *
     340         * @internal Shared by Traffic Signals and extension insights; not a public API.
     341         *
     342         * @param array $evidence {
     343         *     @type int|null $views_current      Month-to-date Views
     344         *     @type int|null $views_previous     Views of the previous range; null unless its month is complete
     345         *     @type int|null $entrances_current  Month-to-date Entrances
     346         *     @type int|null $entrances_previous Entrances of the previous range; null unless complete and at parity
     347         *     @type bool     $derived_current    Visits derived for the current range
     348         *     @type bool     $derived_previous   Visits derived for the previous range
     349         * }
     350         * @return array|null [ 'direction' => up|down ], or null
     351         */
     352        public static function evaluate_entrances_state( $evidence ) {
     353                $evidence = is_array( $evidence ) ? $evidence : [];
     354                $value = static function( $key ) use ( $evidence ) {
     355                        return isset( $evidence[$key] ) && is_int( $evidence[$key] ) ? $evidence[$key] : null;
     356                };
     357                $views_current = $value( 'views_current' );
     358                $views_previous = $value( 'views_previous' );
     359                $current = $value( 'entrances_current' );
     360                $previous = $value( 'entrances_previous' );
     361
     362                // the Views state wins
     363                if ( $views_current === null || $views_previous === null || self::is_views_anomaly( $views_current, $views_previous ) )
     364                        return null;
     365
     366                $check = Post_Views_Counter_Entrances_Eligibility::check(
     367                        [
     368                                [ 'views' => $views_current, 'entrances' => $current, 'derived_available' => ! empty( $evidence['derived_current'] ) ],
     369                                [ 'views' => $views_previous, 'entrances' => $previous, 'derived_available' => ! empty( $evidence['derived_previous'] ) ]
     370                        ],
     371                        self::MIN_VIEWS
     372                );
     373
     374                if ( ! $check['eligible'] || $current === $previous )
     375                        return null;
     376
     377                // measured-zero trigger: from 0 any rise to the floor is unusual
     378                if ( $previous > 0 && abs( $current - $previous ) * 100 < self::ENTRANCES_CHANGE * $previous )
     379                        return null;
     380
     381                return [ 'direction' => $current > $previous ? 'up' : 'down' ];
     382        }
     383
     384        /**
     385         * Build the Entrances evidence of a month comparison.
     386         *
     387         * @param array $values Result of get_month_comparison()
     388         * @return array
     389         */
     390        private static function get_entrances_evidence( $values ) {
     391                $entrances = isset( $values['entrances'] ) && is_array( $values['entrances'] ) ? $values['entrances'] : [];
     392
     393                return [
     394                        'views_current'                 => $values['current'],
     395                        'views_previous'                => $values['previous'],
     396                        'entrances_current'             => isset( $entrances['current'] ) ? $entrances['current'] : null,
     397                        'entrances_previous'    => isset( $entrances['previous'] ) ? $entrances['previous'] : null,
     398                        'derived_current'               => ! empty( $entrances['derived_current'] ),
     399                        'derived_previous'              => ! empty( $entrances['derived_previous'] )
     400                ];
     401        }
     402
     403        /**
     404         * Read a post's month-to-date Views and its equal-day comparison.
     405         *
     406         * A primed list page answers from memory; otherwise the post is read on its
     407         * own with the list statement.
     408         *
     409         * @internal Shared with the Entrances state; not a public API.
     410         *
     411         * @param int                    $post_id Post ID
     412         * @param array                  $periods Result of get_signal_periods()
     413         * @param DateTimeInterface|null $now     Current time in the counting clock
     414         * @return array|false See get_month_comparisons(); false when the read failed
     415         */
     416        public static function get_month_comparison( $post_id, $periods, $now = null ) {
     417                $post_id = (int) $post_id;
     418                $key = self::get_primed_key( $periods );
     419
     420                if ( isset( self::$primed[$key] ) && array_key_exists( $post_id, self::$primed[$key] ) )
     421                        return self::$primed[$key][$post_id];
     422
     423                $now = $now instanceof DateTimeInterface ? $now : new DateTimeImmutable( 'now', Post_Views_Counter_Visits_Query::get_timezone() );
     424                $values = self::get_month_comparisons( [ $post_id ], $periods, $now );
     425
     426                return isset( $values[$post_id] ) ? $values[$post_id] : false;
     427        }
     428
     429        /**
     430         * Read month-to-date Views and their equal-day comparison for posts, in one query.
     431         *
     432         * The previous range counts only when the previous month's daily rows are
     433         * complete: their sum equals its monthly row. The daily cleanup deletes only
     434         * daily rows, so a pruned day with Views leaves the two unequal. Entrances
     435         * (the `visits` column) are read in the same statement only while both
     436         * ranges are derived; the previous range's Entrances also need parity:
     437         * the month's daily `visits` sum equals its monthly row's. A failed read
     438         * with Entrances falls back to one Views-only read, and nothing from it is
     439         * cached.
     440         *
     441         * When the previous range is shorter than month to date (the 29th-31st after a shorter month), the current side is the same number of first
     442         * days of the current month, under the same rule: its daily rows must add
     443         * up to its monthly row, and for Entrances their `visits` too. Otherwise
     444         * the current value is null and the signal is silent.
     445         *
     446         * @param array             $post_ids Post IDs
     447         * @param array             $periods  Result of get_signal_periods()
     448         * @param DateTimeInterface $now      Current time in the counting clock
     449         * @return array Values by post ID: [ 'current' => int|null, 'previous' => int|null,
     450         *               'entrances' => [ 'current' => int|null, 'previous' => int|null,
     451         *               'derived_current' => bool, 'derived_previous' => bool ],
     452         *               'cacheable' => bool ], or false per post when the read failed
     453         */
     454        private static function get_month_comparisons( $post_ids, $periods, $now ) {
     455                $post_ids = array_values( array_unique( array_filter( array_map( 'absint', (array) $post_ids ) ) ) );
     456                $result = [];
     457
     458                if ( empty( $post_ids ) )
     459                        return $result;
     460
     461                $pvc = Post_Views_Counter();
     462                $no_entrances = [ 'current' => null, 'previous' => null, 'derived_current' => false, 'derived_previous' => false ];
     463
     464                // no site data yet: nothing to compare
     465                if ( isset( $pvc->visits ) && method_exists( $pvc->visits, 'is_site_data_accessible' ) && ! $pvc->visits->is_site_data_accessible() ) {
     466                        foreach ( $post_ids as $post_id ) {
     467                                $result[$post_id] = [ 'current' => 0, 'previous' => null, 'entrances' => $no_entrances, 'cacheable' => true ];
     468                        }
     469
     470                        return $result;
     471                }
     472
     473                $derived = self::get_entrances_availability( $periods, $now );
     474                $with_entrances = $derived['current'] && $derived['previous'];
     475                $rows = self::query_month_comparisons( $post_ids, $periods, $with_entrances );
     476                $cacheable = true;
     477
     478                // Entrances must never cost the Views: one Views-only read, nothing cached
     479                if ( $rows === false && $with_entrances ) {
     480                        $with_entrances = false;
     481                        $cacheable = false;
     482                        $rows = self::query_month_comparisons( $post_ids, $periods, false );
     483                }
     484
     485                if ( $rows === false ) {
     486                        foreach ( $post_ids as $post_id ) {
     487                                $result[$post_id] = false;
     488                        }
     489
     490                        return $result;
     491                }
     492
     493                foreach ( $post_ids as $post_id ) {
     494                        $row = isset( $rows[$post_id] ) ? $rows[$post_id] : [];
     495                        $sum = static function( $column ) use ( $row ) {
     496                                return (int) ( isset( $row[$column] ) ? $row[$column] : 0 );
     497                        };
     498                        $complete = $sum( 'previous_daily_views' ) === $sum( 'previous_month_views' );
     499                        $current = $sum( 'current_views' );
     500                        $current_entrances = $with_entrances ? $sum( 'current_entrances' ) : null;
     501
     502                        // the current month's first days count only when its daily rows add up to its monthly row
     503                        if ( ! empty( $periods['current'] ) ) {
     504                                $current = $sum( 'current_daily_views' ) === $sum( 'current_views' ) ? $sum( 'current_range_views' ) : null;
     505                                $current_entrances = $current !== null && $with_entrances && $sum( 'current_daily_entrances' ) === $sum( 'current_entrances' ) ? $sum( 'current_range_entrances' ) : null;
     506                        }
     507
     508                        $entrances = $no_entrances;
     509
     510                        if ( $with_entrances ) {
     511                                $entrances = [
     512                                        'current'                       => $current_entrances,
     513                                        'previous'                      => $complete && $sum( 'previous_daily_entrances' ) === $sum( 'previous_month_entrances' ) ? $sum( 'previous_entrances' ) : null,
     514                                        'derived_current'       => true,
     515                                        'derived_previous'      => true
     516                                ];
     517                        }
     518
     519                        $result[$post_id] = [
     520                                'current'       => $current,
     521                                'previous'      => $complete ? $sum( 'previous_views' ) : null,
     522                                'entrances'     => $entrances,
     523                                'cacheable'     => $cacheable
     524                        ];
     525                }
     526
     527                return $result;
     528        }
     529
     530        /**
     531         * Run the month comparison statement for posts.
     532         *
     533         * @param array $post_ids       Post IDs
     534         * @param array $periods        Result of get_signal_periods()
     535         * @param bool  $with_entrances Whether to sum `visits` too
     536         * @return array|false Rows by post ID, or false when the read failed
     537         */
     538        private static function query_month_comparisons( $post_ids, $periods, $with_entrances ) {
     539                global $wpdb;
     540
     541                $from = (string) $periods['comparison'][0];
     542                $to = (string) $periods['comparison'][1];
     543                $previous_month = substr( $from, 0, 6 );
     544                $month_start = DateTimeImmutable::createFromFormat( '!Ymd', $previous_month . '01', new DateTimeZone( 'UTC' ) );
     545                $month_end = $previous_month . $month_start->format( 't' );
     546                $content_sql = pvc_post_views_has_content_column() ? ' AND `content` = 0' : '';
     547                $columns = [ 'count' => 'views' ];
     548
     549                // `visits` is in no index; keep the per-post index instead of the (type, period) primary key
     550                if ( $with_entrances )
     551                        $columns['visits'] = 'entrances';
     552
     553                // on the 29th-31st after a shorter month: the current month's first days and its daily sum
     554                $current = empty( $periods['current'] ) ? null : [ (string) $periods['current'][0], (string) $periods['current'][1] ];
     555                $current_end = $current === null ? '' : $periods['period'] . DateTimeImmutable::createFromFormat( '!Ymd', $periods['period'] . '01', new DateTimeZone( 'UTC' ) )->format( 't' );
     556                $select = [];
     557                $params = [];
     558
     559                foreach ( $columns as $column => $name ) {
     560                        $select[] = 'COALESCE( SUM( CASE WHEN `type` = 2 AND `period` = %s THEN `' . $column . '` ELSE 0 END ), 0 ) AS current_' . $name;
     561                        $select[] = 'COALESCE( SUM( CASE WHEN `type` = 0 AND `period` >= %s AND `period` <= %s THEN `' . $column . '` ELSE 0 END ), 0 ) AS previous_' . $name;
     562                        $params = array_merge( $params, [ (string) $periods['period'], $from, $to ] );
     563
     564                        // the daily rows read are the previous month's only, unless the current month's are read too
     565                        if ( $current === null )
     566                                $select[] = 'COALESCE( SUM( CASE WHEN `type` = 0 THEN `' . $column . '` ELSE 0 END ), 0 ) AS previous_daily_' . $name;
     567                        else {
     568                                $select[] = 'COALESCE( SUM( CASE WHEN `type` = 0 AND `period` >= %s AND `period` <= %s THEN `' . $column . '` ELSE 0 END ), 0 ) AS previous_daily_' . $name;
     569                                $params = array_merge( $params, [ $previous_month . '01', $month_end ] );
     570                        }
     571
     572                        $select[] = 'COALESCE( SUM( CASE WHEN `type` = 2 AND `period` = %s THEN `' . $column . '` ELSE 0 END ), 0 ) AS previous_month_' . $name;
     573                        $params[] = $previous_month;
     574
     575                        if ( $current !== null ) {
     576                                $select[] = 'COALESCE( SUM( CASE WHEN `type` = 0 AND `period` >= %s AND `period` <= %s THEN `' . $column . '` ELSE 0 END ), 0 ) AS current_range_' . $name;
     577                                $select[] = 'COALESCE( SUM( CASE WHEN `type` = 0 AND `period` >= %s AND `period` <= %s THEN `' . $column . '` ELSE 0 END ), 0 ) AS current_daily_' . $name;
     578                                $params = array_merge( $params, [ $current[0], $current[1], $periods['period'] . '01', $current_end ] );
     579                        }
     580                }
     581
     582                $sql = $wpdb->prepare(
     583                        'SELECT `id`,
     584                                ' . implode( ",\n\t\t\t\t", $select ) . '
     585                        FROM `' . $wpdb->prefix . 'post_views`' . ( $with_entrances ? ' FORCE INDEX (`id_type_period_count`)' : '' ) . '
     586                        WHERE `id` IN (' . implode( ', ', array_fill( 0, count( $post_ids ), '%d' ) ) . ')' . $content_sql . '
     587                        AND ( ( `type` = 2 AND `period` IN ( %s, %s ) ) OR ( `type` = 0 AND `period` >= %s AND `period` <= %s )' . ( $current === null ? '' : ' OR ( `type` = 0 AND `period` >= %s AND `period` <= %s )' ) . ' )
     588                        GROUP BY `id`',
     589                        array_merge( $params, $post_ids, [ (string) $periods['period'], $previous_month, $previous_month . '01', $month_end ], $current === null ? [] : [ $periods['period'] . '01', $current_end ] )
     590                );
     591
     592                // a table without the index fails here and takes the Views-only fallback
     593                $suppress = $with_entrances ? $wpdb->suppress_errors( true ) : null;
     594                $wpdb->last_error = '';
     595                $rows = $wpdb->get_results( $sql, ARRAY_A );
     596                $failed = $wpdb->last_error !== '' || ! is_array( $rows );
     597
     598                if ( $with_entrances )
     599                        $wpdb->suppress_errors( $suppress );
     600
     601                if ( $failed )
     602                        return false;
     603
     604                $by_id = [];
     605
     606                foreach ( $rows as $row ) {
     607                        if ( isset( $row['id'] ) )
     608                                $by_id[(int) $row['id']] = $row;
     609                }
     610
     611                return $by_id;
     612        }
     613
     614        /**
     615         * Whether Visits are derived for both compared ranges.
     616         *
     617         * @param array             $periods Result of get_signal_periods()
     618         * @param DateTimeInterface $now     Current time in the counting clock
     619         * @return array [ 'current' => bool, 'previous' => bool ]
     620         */
     621        private static function get_entrances_availability( $periods, $now ) {
     622                $current = Post_Views_Counter_Entrances_Eligibility::get_day_range_availability( $periods['period'] . '01', empty( $periods['current'] ) ? $now->format( 'Ymd' ) : (string) $periods['current'][1], $now );
     623                $previous = Post_Views_Counter_Entrances_Eligibility::get_day_range_availability( $periods['comparison'][0], $periods['comparison'][1], $now );
     624
     625                return [ 'current' => $current['derived_available'], 'previous' => $previous['derived_available'] ];
     626        }
     627
     628        /**
     629         * Whether a cached Entrances state still stands on derived ranges.
     630         *
     631         * @param array             $periods Result of get_signal_periods()
     632         * @param DateTimeInterface $now     Current time in the counting clock
     633         * @return bool
     634         */
     635        private static function are_entrances_derived( $periods, $now ) {
     636                $derived = self::get_entrances_availability( $periods, $now );
     637
     638                return $derived['current'] && $derived['previous'];
     639        }
     640
     641        /**
     642         * Key of the comparisons kept for this request.
     643         *
     644         * @param array $periods Result of get_signal_periods()
     645         * @return string
     646         */
     647        private static function get_primed_key( $periods ) {
     648                return get_current_blog_id() . ':' . $periods['period'] . ':' . implode( '-', $periods['comparison'] ) . ( empty( $periods['current'] ) ? '' : ':' . implode( '-', $periods['current'] ) );
     649        }
     650
     651        /**
     652         * Get the current time in the clock that buckets the stored periods.
     653         *
     654         * @return DateTimeImmutable
     655         */
     656        private function get_signal_now() {
     657                return new DateTimeImmutable( 'now', Post_Views_Counter_Visits_Query::get_timezone() );
     658        }
     659
     660        /**
     661         * Get the current month and its equal-day comparison range.
     662         *
     663         * The previous month is stepped back from the first day of the current
     664         * month, so the 29th-31st cannot roll over into the current month. Both
     665         * sides hold the same days: on the 29th-31st after a shorter
     666         * month the current side stops where the previous month does.
     667         *
     668         * @param DateTimeInterface $now Current time in the counting clock
     669         * @return array Current period (Ym), comparison day range [Ymd, Ymd], and
     670         *               the current day range [Ymd, Ymd] when it is shorter than
     671         *               month to date, else null (the monthly row)
     672         */
     673        private static function get_signal_periods( $now ) {
     674                $month_start = DateTimeImmutable::createFromFormat( '!Y-m-d', $now->format( 'Y-m-01' ), $now->getTimezone() );
     675                $prev_month = $month_start->modify( '-1 month' );
     676                $day = (int) $now->format( 'j' );
     677
     678                // compare against the same number of days from last month (clamp to last day)
     679                $end_day = min( $day, (int) $prev_month->format( 't' ) );
     680                $end = str_pad( (string) $end_day, 2, '0', STR_PAD_LEFT );
     681
     682                return [
     683                        'period'                => $now->format( 'Ym' ),
     684                        'comparison'    => [
     685                                (int) ( $prev_month->format( 'Ym' ) . '01' ),
     686                                (int) ( $prev_month->format( 'Ym' ) . $end )
     687                        ],
     688                        'current'               => $end_day < $day ? [ (int) ( $now->format( 'Ym' ) . '01' ), (int) ( $now->format( 'Ym' ) . $end ) ] : null
     689                ];
    217690        }
    218691
    … …  
    260733         */
    261734        public function invalidate_signal_cache( $post_id ) {
    262                 $cache_key = 'pvc_signal_' . $post_id;
     735                // older periods' entries expire within their one-hour lifetime
     736                $periods = self::get_signal_periods( $this->get_signal_now() );
     737                $cache_key = 'pvc_signal_' . $post_id . '_' . $periods['period'];
    263738                $cache_group = 'post_views_counter';
    264739                wp_cache_delete( $cache_key, $cache_group );
     740                unset( self::$primed[self::get_primed_key( $periods )][(int) $post_id] );
    265741        }
    266742
    … …  
    271747         */
    272748        public function flush_all_signals_cache() {
     749                // comparisons read earlier in this request predate the flush or reset
     750                self::$primed = [];
     751
    273752                // WordPress doesn't support wildcard cache deletion
    274753                // We rely on natural cache expiration (1 hour) for bulk invalidation
  • post-views-counter/tags/1.8.0/includes/class-update.php

    r3537950 r3734102  
    9999                                if ( $pvc->counter->using_object_cache( true ) ) {
    100100                                        // flush data from cache
    101                                         $pvc->counter->flush_cache_to_db();
     101                                        if ( ! $pvc->counter->flush_cache_to_db() )
     102                                                return;
    102103                                }
    103104
    … …  
    184185
    185186                        $pvc->options['general']['time_between_counts'] = $normalized_time_between_counts;
     187                }
     188
     189                // update 1.7.16+ - unify frontend counter labels
     190                if ( version_compare( $current_db_version, '1.7.16', '<' ) ) {
     191                        $display = get_option( 'post_views_counter_settings_display', [] );
     192
     193                        if ( ! is_array( $display ) )
     194                                $display = [];
     195
     196                        // rewrite ONLY the exact legacy defaults; every customised value is preserved
     197                        $legacy_labels = [
     198                                'label'                 => [ 'Post Views:', 'Views:' ]
     199                        ];
     200                        $labels_changed = false;
     201
     202                        foreach ( $legacy_labels as $key => $pair ) {
     203                                list( $legacy, $unified ) = $pair;
     204
     205                                if ( ! isset( $display[$key] ) || ! is_string( $display[$key] ) || $display[$key] !== $legacy )
     206                                        continue;
     207
     208                                $display[$key] = $unified;
     209                                $pvc->options['display'][$key] = $unified;
     210                                $labels_changed = true;
     211                        }
     212
     213                        if ( $labels_changed )
     214                                update_option( 'post_views_counter_settings_display', $display );
    186215                }
    187216
  • post-views-counter/tags/1.8.0/includes/functions.php

    r3640258 r3734102  
    4949                }
    5050
     51                if ( isset( Post_Views_Counter()->visits ) && method_exists( Post_Views_Counter()->visits, 'is_site_data_accessible' ) && ! Post_Views_Counter()->visits->is_site_data_accessible() )
     52                        return (int) apply_filters( 'pvc_get_post_views', 0, $post_id, $period, $args );
     53
     54                // Resolve Core-supported named and storage periods before the compatibility
     55                // filter runs. Unknown helper periods still receive one filter opportunity.
     56                $has_explicit_range = isset( $args['period_range'] ) || isset( $args['period_from'], $args['period_to'] );
     57                $normalized_period = $has_explicit_range ? null : pvc_normalize_views_period( $period, $args );
     58
    5159                // set where clause
    5260                $where = [ 'type' => 'type = 4' ];
    5361
    54                 // override type if explicitly provided in args
     62                if ( $normalized_period !== null ) {
     63                        $where['type'] = 'type = ' . (int) $normalized_period['type'];
     64
     65                        if ( (int) $normalized_period['type'] === 4 )
     66                                $where['period'] = "period = 'total'";
     67                        elseif ( $normalized_period['period_from'] === $normalized_period['period_to'] )
     68                                $where['period'] = 'CAST( period AS SIGNED ) = ' . (int) $normalized_period['period_from'];
     69                        else
     70                                $where['period'] = 'CAST( period AS SIGNED ) <= ' . (int) $normalized_period['period_to'] . ' AND CAST( period AS SIGNED ) >= ' . (int) $normalized_period['period_from'];
     71                }
     72
     73                $has_explicit_type = false;
     74
     75                // Override type if explicitly provided in args. Historically an explicit
     76                // type without a range selected every row of that storage type, so it must
     77                // not retain a canonical period predicate from the named period argument.
    5578                if ( isset( $args['type'] ) ) {
    5679                        $where['type'] = 'type = ' . (int) $args['type'];
     80                        $has_explicit_type = true;
    5781                } elseif ( isset( $args['period_type'] ) ) {
    5882                        $period_type = sanitize_key( $args['period_type'] );
    … …  
    6690                        if ( isset( $type_map[ $period_type ] ) ) {
    6791                                $where['type'] = 'type = ' . $type_map[ $period_type ];
     92                                $has_explicit_type = true;
    6893                        }
    6994                }
     95
     96                if ( $has_explicit_type && ! $has_explicit_range )
     97                        unset( $where['period'] );
    7098
    7199                // optional period range (e.g., for day-based ranges within a month)
    … …  
    91119
    92120                        // default to day type when using explicit ranges and no type set
    93                         if ( ! isset( $where['type'] ) ) {
     121                        if ( ! isset( $args['type'] ) && ! isset( $args['period_type'] ) ) {
    94122                                $where['type'] = 'type = 0';
    95123                        }
    … …  
    130158
    131159                // update where clause
    132                 $where = apply_filters( 'pvc_get_post_views_period_where', $where, $period, $post_id, $args );
     160                $filter_args = $args;
     161
     162                if ( $normalized_period !== null )
     163                        $filter_args['_pvc_core_period_resolved'] = $normalized_period;
     164
     165                $where = apply_filters( 'pvc_get_post_views_period_where', $where, $period, $post_id, $filter_args );
     166
     167                if ( ! is_array( $where ) )
     168                        return 0;
     169
     170                // Explicit content is authoritative across Core and extension callbacks.
     171                if ( isset( $args['content'] ) )
     172                        $where['content'] = 'content = ' . (int) $args['content'];
    133173
    134174                // updated where clause
    135175                $_where = [];
     176                $strict_period_selector = false;
    136177
    137178                // sanitize where clause
    138179                foreach ( $where as $index => $value ) {
    139                         if ( $index === 'type' || $index === 'content' )
    140                                 $_where[$index] = preg_replace( '/[^0-9]/', '', $value );
     180                        if ( $index === 'type' || $index === 'content' ) {
     181                                if ( ! is_scalar( $value ) )
     182                                        continue;
     183
     184                                $selector = preg_replace( '/[^0-9]/', '', (string) $value );
     185
     186                                if ( $selector !== '' )
     187                                        $_where[$index] = $selector;
     188                        }
    141189                        elseif ( $index === 'period' ) {
    142                                 if ( is_string( $value ) && preg_match( "/period\s*=\s*'?total'?/i", $value ) ) {
     190                                if ( ! is_string( $value ) )
     191                                        continue;
     192
     193                                $period_operand = '(?:CAST\s*\(\s*period\s+AS\s+(?:SIGNED|UNSIGNED)\s*\)|period)';
     194
     195                                if ( preg_match( "/^\s*period\s*=\s*'?total'?\s*$/i", $value ) ) {
    143196                                        $_where['period'] = [ 'total' ];
     197                                        $strict_period_selector = true;
    144198                                        continue;
    145199                                }
    … …  
    148202
    149203                                // any values?
    150                                 if ( $values !== false && $values > 0 )
     204                                if ( $values !== false && $values > 0 && $values <= 2 )
    151205                                        $_where['period'] = $matches[0];
     206
     207                                if ( preg_match( '/^\s*' . $period_operand . "\s*=\s*'?\d+'?\s*$/i", $value ) )
     208                                        $strict_period_selector = true;
     209                                elseif ( preg_match( '/^\s*' . $period_operand . "\s*<=\s*'?\d+'?\s+AND\s+" . $period_operand . "\s*>=\s*'?\d+'?\s*$/i", $value ) )
     210                                        $strict_period_selector = true;
     211                                elseif ( preg_match( '/^\s*' . $period_operand . "\s*>=\s*'?\d+'?\s+AND\s+" . $period_operand . "\s*<=\s*'?\d+'?\s*$/i", $value ) )
     212                                        $strict_period_selector = true;
     213                                elseif ( preg_match( '/^\s*' . $period_operand . "\s+BETWEEN\s+'?\d+'?\s+AND\s+'?\d+'?\s*$/i", $value ) )
     214                                        $strict_period_selector = true;
    152215                        }
    153216                }
     217
     218                // A raw SQL-fragment filter remains a helper-only compatibility mechanism.
     219                // Unknown periods fail closed unless it supplied a complete, strictly
     220                // recognized storage selector. REST never consults this hook.
     221                if ( $normalized_period === null && ! $range_from && ! $range_to && ( ! isset( $_where['type'], $_where['period'] ) || ! $strict_period_selector ) )
     222                        return 0;
    154223
    155224                // get current number of ids
    156225                $ids_count = count( $numbers );
    157226
    158                 $where_clause = '';
    159                
     227                $where_parts = [];
     228
    160229                // add post ids if needed
    161                 if ( $ids_count ) {
    162                         $where_clause .= " WHERE id IN (" . implode( ',', array_fill( 0, $ids_count, '%d' ) ) . ") AND";
    163                 } else {
    164                         $where_clause .= " WHERE";
    165                 }
     230                if ( $ids_count )
     231                        $where_parts[] = "id IN (" . implode( ',', array_fill( 0, $ids_count, '%d' ) ) . ")";
    166232
    167233                // validate where clause
    168234                foreach( $_where as $index => $value ) {
    169235                        if ( $index === 'type' ) {
    170                                 $where_clause .= ' type = %d';
     236                                $where_parts[] = 'type = %d';
    171237                                $numbers[] = (int) $value;
    172238                        } elseif ( $index === 'content' && pvc_post_views_has_content_column() ) {
    173                                 $where_clause .= ' AND content = %d';
     239                                $where_parts[] = 'content = %d';
    174240                                $numbers[] = (int) $value;
    175241                        } elseif ( $index === 'period' ) {
    … …  
    178244                                if ( $nop === 1 ) {
    179245                                        if ( $_where['period'][0] === 'total' ) {
    180                                                 $where_clause .= ' AND period = %s';
     246                                                $where_parts[] = 'period = %s';
    181247                                                $numbers[] = 'total';
    182248                                        } else {
    183                                                 $where_clause .= ' AND CAST( period AS SIGNED ) = %d';
     249                                                $where_parts[] = 'CAST( period AS SIGNED ) = %d';
    184250                                                $numbers[] = (int) $_where['period'][0];
    185251                                        }
    186252                                } elseif ( $nop === 2 ) {
    187                                         $where_clause .= ' AND CAST( period AS SIGNED ) <= %d AND CAST( period AS SIGNED ) >= %d';
    188                                         $numbers[] = (int) $_where['period'][0];
    189                                         $numbers[] = (int) $_where['period'][1];
     253                                        // Recognized selectors may list bounds in either order, so normalize
     254                                        // them instead of trusting the extracted positional order.
     255                                        $bounds = [
     256                                                (int) $_where['period'][0],
     257                                                (int) $_where['period'][1]
     258                                        ];
     259
     260                                        $where_parts[] = 'CAST( period AS SIGNED ) <= %d AND CAST( period AS SIGNED ) >= %d';
     261                                        $numbers[] = max( $bounds );
     262                                        $numbers[] = min( $bounds );
    190263                                }
    191264                        }
    192265                }
     266
     267                if ( empty( $where_parts ) )
     268                        return 0;
     269
     270                $where_clause = ' WHERE ' . implode( ' AND ', $where_parts );
    193271
    194272                // prepare query
    … …  
    225303 */
    226304function pvc_post_views_has_content_column() {
    227         global $wpdb;
    228 
    229         static $has_content_column = null;
    230 
    231         // check column existence only once
    232         if ( $has_content_column === null ) {
    233                 // check content column using SHOW COLUMNS (more robust than information_schema)
    234                 $columns = $wpdb->get_col( "SHOW COLUMNS FROM " . $wpdb->prefix . "post_views LIKE 'content'" );
    235 
    236                 // cache result
    237                 $has_content_column = ! empty( $columns );
    238         }
     305        $status = isset( Post_Views_Counter()->visits ) ? Post_Views_Counter()->visits->get_shared_content_column_status() : false;
     306
     307        // On an ambiguous probe, requiring the discriminator fails closed on a legacy
     308        // schema instead of silently mixing extended content rows that share an ID.
     309        $has_content_column = $status === null ? true : $status;
    239310
    240311        // always allow override via filter (applied on every call)
    … …  
    322393                        $query_data = array_merge( $query_data, array_values( $args['post_id'] ), array_values( $args['post_type'] ) );
    323394               
    324                 // set where clause
    325                 $where = apply_filters( 'pvc_get_views_period_where', [], $args );
     395                // set where clause, post rows only when the table stores other content
     396                $where = [];
     397
     398                if ( pvc_post_views_has_content_column() )
     399                        $where['content'] = 'pvc.content = 0';
     400
     401                $where = apply_filters( 'pvc_get_views_period_where', $where, $args );
     402
     403                // every period of one type has one length of digits (Ymd, oW, Ym, Y), so
     404                // a same-length string bound selects the rows a numeric one would and
     405                // keeps the (type, period) keys usable, which CAST( period ) defeats
     406                $period_sql = static function( $period ) {
     407                        return "'" . preg_replace( '/[^0-9]/', '', (string) $period ) . "'";
     408                };
    326409
    327410                // check fields
    … …  
    409492                                                        if ( isset( $chunk['week'] ) ) {
    410493                                                                $where['type'] = 'pvc.type = 1';
    411                                                                 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['year'] . $chunk['week'] );
     494                                                                $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period " . $chunk['type'] . " " . $period_sql( $chunk['year'] . $chunk['week'] );
    412495                                                        }
    413496                                                        // year, month
    … …  
    416499                                                                if ( isset( $chunk['day'] ) ) {
    417500                                                                        $where['type'] = 'pvc.type = 0';
    418                                                                         $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['year'] . $chunk['month'] . $chunk['day'] );
     501                                                                        $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period " . $chunk['type'] . " " . $period_sql( $chunk['year'] . $chunk['month'] . $chunk['day'] );
    419502                                                                }
    420503                                                                // year, month
    421504                                                                else {
    422505                                                                        $where['type'] = 'pvc.type = 2';
    423                                                                         $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['year'] . $chunk['month'] );
     506                                                                        $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period " . $chunk['type'] . " " . $period_sql( $chunk['year'] . $chunk['month'] );
    424507                                                                }
    425508                                                        // year
    426509                                                        } else {
    427510                                                                $where['type'] = 'pvc.type = 3';
    428                                                                 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) " . $chunk['type'] . " " . (int) ( $chunk['year'] );
     511                                                                $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period " . $chunk['type'] . " " . $period_sql( $chunk['year'] );
    429512                                                        }
    430513                                                // month
    … …  
    487570                                                $monday_month = $date->format( 'm' );
    488571
     572                                                // and its calendar year: week 1 may start in the previous one
     573                                                $monday_year = $date->format( 'Y' );
     574
    489575                                                // prepare range
    490576                                                for( $i = 1; $i <= 6; $i++ ) {
    … …  
    500586
    501587                                                $where['type'] = 'pvc.type = 0';
    502                                                 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) >= " . (int) ( $year . $monday_month . $monday ) . " AND CAST( pvc.period AS SIGNED ) <= " . (int) ( $date->format( 'Y' ) . $sunday_month . $date->format( 'd' ) );
     588                                                $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period >= " . $period_sql( $monday_year . $monday_month . $monday ) . " AND pvc.period <= " . $period_sql( $date->format( 'Y' ) . $sunday_month . $date->format( 'd' ) );
    503589                                        } else {
    504590                                                $where['type'] = 'pvc.type = 1';
    505                                                 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) = " . (int) ( $year . $week );
     591                                                $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period = " . $period_sql( $year . $week );
    506592                                        }
    507593                                // year, month
    … …  
    514600
    515601                                                $where['type'] = 'pvc.type = 0';
    516                                                 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) = " . (int) ( $year . $month . $day );
     602                                                $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period = " . $period_sql( $year . $month . $day );
    517603                                        // year, month
    518604                                        } else {
    … …  
    530616
    531617                                                        $where['type'] = 'pvc.type = 0';
    532                                                         $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) >= " . (int) ( $year . $month ) . "01 AND CAST( pvc.period AS SIGNED ) <= " . (int) ( $year . $month . $last );
     618                                                        $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period >= " . $period_sql( $year . $month . '01' ) . " AND pvc.period <= " . $period_sql( $year . $month . $last );
    533619                                                } else {
    534620                                                        $where['type'] = 'pvc.type = 2';
    535                                                         $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) = " . (int) ( $year . $month );
     621                                                        $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period = " . $period_sql( $year . $month );
    536622                                                }
    537623                                        }
    … …  
    548634
    549635                                                $where['type'] = 'pvc.type = 2';
    550                                                 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) >= " . (int) ( $year ) . "01 AND CAST( pvc.period AS SIGNED ) <= " . (int) ( $year ) . "12";
     636                                                $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period >= " . $period_sql( $year . '01' ) . " AND pvc.period <= " . $period_sql( $year . '12' );
    551637                                        } else {
    552638                                                $where['type'] = 'pvc.type = 3';
    553                                                 $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND CAST( pvc.period AS SIGNED ) = " . (int) ( $year );
     639                                                $views_query .= ' AND ' . implode( ' AND ', $where ) . " AND pvc.period = " . $period_sql( $year );
    554640                                        }
    555641                                }
    … …  
    577663
    578664                // update where clause
    579                 $where['type'] = [ 'pvc.type = 4' ];
     665                $where['type'] = 'pvc.type = 4';
    580666
    581667                $query = $wpdb->prepare(
    … …  
    619705
    620706/**
     707 * Normalize a Views period to the canonical shared-table selector.
     708 *
     709 * Pluggable: a theme or plugin may define this before the plugin loads.
     710 *
     711 * @since 1.7.16
     712 *
     713 * @param mixed $period Requested period.
     714 * @param array $args Optional explicit period arguments.
     715 *
     716 * @return array|null Normalized period, or null when the request is invalid.
     717 */
     718if ( ! function_exists( 'pvc_normalize_views_period' ) ) {
     719        function pvc_normalize_views_period( $period = 'total', $args = [] ) {
     720                return Post_Views_Counter_Visits_Query::normalize_views_period( $period, $args );
     721        }
     722}
     723
     724/**
     725 * Normalize one Visit period into storage and derived-range semantics.
     726 *
     727 * Availability ranges are half-open. Storage day ranges remain inclusive because
     728 * the table stores one YYYYMMDD key per calendar day.
     729 *
     730 * Pluggable: a theme or plugin may define this before the plugin loads.
     731 *
     732 * @since 1.7.16
     733 *
     734 * @param string|array $period Named or explicit storage period.
     735 * @param array        $args Optional explicit range/type arguments.
     736 *
     737 * @return array|null Normalized period, or null when the request is invalid.
     738 */
     739if ( ! function_exists( 'pvc_normalize_visits_period' ) ) {
     740        function pvc_normalize_visits_period( $period = 'total', $args = [] ) {
     741                return Post_Views_Counter_Visits_Query::normalize_period( $period, $args );
     742        }
     743}
     744
     745/**
     746 * Get aggregate post Visits for an explicit range.
     747 *
     748 * @since 1.7.16
     749 *
     750 * @param array $args Query arguments.
     751 *
     752 * @return int|array|null Scalar or availability-bearing date result. Integer
     753 *                        zero is a stored zero; null means Visits are
     754 *                        unavailable. There is no public Visit increment or
     755 *                        update API.
     756 */
     757if ( ! function_exists( 'pvc_get_visits' ) ) {
     758        function pvc_get_visits( $args = [] ) {
     759                global $wpdb;
     760
     761                $args = is_array( $args ) ? $args : [];
     762                $range = Post_Views_Counter_Visits_Query::parse_range( $args );
     763                $availability = isset( Post_Views_Counter()->visits ) ? Post_Views_Counter()->visits->get_visits_availability( $range ) : [ 'readable' => false, 'read_reason' => 'schema_failed' ];
     764                $fields = isset( $args['fields'] ) && $args['fields'] === 'date=>visits' ? 'date=>visits' : 'visits';
     765
     766                if ( $range === null || empty( $availability['readable'] ) )
     767                        return $fields === 'date=>visits' ? [ 'values' => [], 'availability' => $availability ] : null;
     768
     769                // daily keys are fixed-width Ymd strings, so the (type, period) key serves the range
     770                $where = [ 'pvc.`type` = 0', 'pvc.`period` BETWEEN %s AND %s' ];
     771                $params = [ sprintf( '%08d', $range['period_from'] ), sprintf( '%08d', $range['period_to'] ) ];
     772
     773                if ( pvc_post_views_has_content_column() )
     774                        $where[] = 'pvc.`content` = 0';
     775
     776                $post_types = isset( $args['post_type'] ) ? array_values( array_filter( array_map( 'sanitize_key', (array) $args['post_type'] ) ) ) : [];
     777                $join = '';
     778
     779                if ( ! empty( $post_types ) ) {
     780                        $join = ' INNER JOIN `' . $wpdb->posts . '` posts ON posts.ID = pvc.id';
     781                        $where[] = 'posts.post_type IN (' . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ')';
     782                        $params = array_merge( $params, $post_types );
     783                }
     784
     785                $select = $fields === 'date=>visits' ? 'pvc.`period`, SUM(pvc.`visits`) AS post_visits' : 'SUM(pvc.`visits`)';
     786                $group = $fields === 'date=>visits' ? ' GROUP BY pvc.`period` ORDER BY pvc.`period` ASC' : '';
     787                $sql = $wpdb->prepare( 'SELECT ' . $select . ' FROM `' . $wpdb->prefix . 'post_views` pvc' . $join . ' WHERE ' . implode( ' AND ', $where ) . $group, $params );
     788                $cache_key = Post_Views_Counter_Visits_Query::get_read_cache_key( $sql );
     789
     790                if ( $fields === 'date=>visits' ) {
     791                        $values = wp_cache_get( $cache_key, Post_Views_Counter_Visits_Query::VISITS_CACHE_GROUP );
     792
     793                        if ( $values === false ) {
     794                                $values = [];
     795
     796                                $wpdb->last_error = '';
     797                                $rows = $wpdb->get_results( $sql );
     798
     799                                if ( $wpdb->last_error !== '' )
     800                                        return [ 'values' => [], 'availability' => array_merge( $availability, [ 'readable' => false, 'read_reason' => 'database_error' ] ) ];
     801
     802                                foreach ( (array) $rows as $row )
     803                                        $values[(string) $row->period] = (int) $row->post_visits;
     804
     805                                wp_cache_add( $cache_key, $values, Post_Views_Counter_Visits_Query::VISITS_CACHE_GROUP, absint( apply_filters( 'pvc_visits_object_cache_expire', 300 ) ) );
     806                        }
     807
     808                        return apply_filters( 'pvc_get_visits', [ 'values' => $values, 'availability' => $availability ], $args );
     809                }
     810
     811                $value = wp_cache_get( $cache_key, Post_Views_Counter_Visits_Query::VISITS_CACHE_GROUP );
     812
     813                if ( $value === false ) {
     814                        $result = Post_Views_Counter_Visits_Query::get_scalar_result( $sql );
     815
     816                        if ( ! $result['success'] )
     817                                return null;
     818
     819                        $value = $result['value'];
     820                        wp_cache_add( $cache_key, $value, Post_Views_Counter_Visits_Query::VISITS_CACHE_GROUP, absint( apply_filters( 'pvc_visits_object_cache_expire', 300 ) ) );
     821                }
     822
     823                return (int) apply_filters( 'pvc_get_visits', (int) $value, $args, $availability );
     824        }
     825}
     826
     827/**
     828 * Get aggregate post Views per Visit for one derived-eligible range.
     829 *
     830 * @since 1.7.16
     831 *
     832 * @param array $args Explicit start/end and optional post_type scope.
     833 *
     834 * @return float|null Float zero is a stored zero; null means the range is not
     835 *                    derived-eligible or Visits are unavailable.
     836 */
     837if ( ! function_exists( 'pvc_get_views_per_visit' ) ) {
     838        function pvc_get_views_per_visit( $args ) {
     839                global $wpdb;
     840
     841                $args = is_array( $args ) ? $args : [];
     842
     843                if ( ! empty( $args['post_id'] ) || ( isset( $args['content_type'] ) && $args['content_type'] !== 'post' ) )
     844                        return null;
     845
     846                $range = Post_Views_Counter_Visits_Query::parse_range( $args );
     847                $availability = isset( Post_Views_Counter()->visits ) ? Post_Views_Counter()->visits->get_visits_availability( $range ) : [ 'derived_available' => false ];
     848
     849                if ( $range === null || empty( $availability['derived_available'] ) )
     850                        return null;
     851
     852                // daily keys are fixed-width Ymd strings, so the (type, period) key serves the range
     853                $where = [ 'pvc.`type` = 0', 'pvc.`period` BETWEEN %s AND %s' ];
     854                $params = [ sprintf( '%08d', $range['period_from'] ), sprintf( '%08d', $range['period_to'] ) ];
     855
     856                if ( pvc_post_views_has_content_column() )
     857                        $where[] = 'pvc.`content` = 0';
     858
     859                $post_types = isset( $args['post_type'] ) ? array_values( array_filter( array_map( 'sanitize_key', (array) $args['post_type'] ) ) ) : [];
     860                $join = '';
     861
     862                if ( ! empty( $post_types ) ) {
     863                        $join = ' INNER JOIN `' . $wpdb->posts . '` posts ON posts.ID = pvc.id';
     864                        $where[] = 'posts.post_type IN (' . implode( ', ', array_fill( 0, count( $post_types ), '%s' ) ) . ')';
     865                        $params = array_merge( $params, $post_types );
     866                }
     867
     868                $sql = $wpdb->prepare( 'SELECT SUM(pvc.`count`) AS views, SUM(pvc.`visits`) AS visits FROM `' . $wpdb->prefix . 'post_views` pvc' . $join . ' WHERE ' . implode( ' AND ', $where ), $params );
     869                $cache_key = Post_Views_Counter_Visits_Query::get_read_cache_key( $sql );
     870                $row = wp_cache_get( $cache_key, Post_Views_Counter_Visits_Query::VIEWS_PER_VISIT_CACHE_GROUP );
     871
     872                if ( $row === false ) {
     873                        $wpdb->last_error = '';
     874                        $row = $wpdb->get_row( $sql, ARRAY_A );
     875
     876                        if ( $wpdb->last_error !== '' )
     877                                return null;
     878
     879                        $row = is_array( $row ) ? $row : [ 'views' => 0, 'visits' => 0 ];
     880                        wp_cache_add( $cache_key, $row, Post_Views_Counter_Visits_Query::VIEWS_PER_VISIT_CACHE_GROUP, absint( apply_filters( 'pvc_visits_object_cache_expire', 300 ) ) );
     881                }
     882                $visits = isset( $row['visits'] ) ? (int) $row['visits'] : 0;
     883
     884                if ( $visits === 0 )
     885                        return null;
     886
     887                return (float) apply_filters( 'pvc_get_views_per_visit', (int) $row['views'] / $visits, $args, $availability );
     888        }
     889}
     890
     891/**
    621892 * Display post views for a given post.
    622893 *
    … …  
    627898 */
    628899if ( ! function_exists( 'pvc_post_views' ) ) {
    629         function pvc_post_views( $post_id = 0, $display = true, $period = '' ) {
     900        function pvc_post_views( $post_id = 0, $display = true, $period = '', $args = [] ) {
    630901                // get all data
    631902                $post_id = (int) ( empty( $post_id ) ? get_the_ID() : $post_id );
    632 
    633                 // get display options
    634903                $options = Post_Views_Counter()->options['display'];
    635 
    636                 // get post views
    637                 $views = pvc_get_post_views( $post_id, $period !== '' ? $period : $options['display_period'] );
    638 
    639                 // use number format?
    640                 $views = $options['use_format'] ? number_format_i18n( $views ) : $views;
    641 
    642                 // container class
    643                 $class = apply_filters( 'pvc_post_views_class', 'post-views content-post post-' . $post_id . ' entry-meta', $post_id );
    644 
    645                 // dynamic loading?
    646                 $class .= $options['dynamic_loading'] === true ? ' load-dynamic' : ' load-static';
    647 
    648                 // prepare display
    649                 $label = apply_filters( 'pvc_post_views_label', ( function_exists( 'icl_t' ) ? icl_t( 'Post Views Counter', 'Post Views Label', $options['label'] ) : $options['label'] ), $post_id );
    650 
    651                 // add dashicons class if needed
    652                 $icon_class = strpos( $options['icon_class'], 'dashicons' ) === false ? $options['icon_class'] : 'dashicons ' . $options['icon_class'];
    653 
    654                 // prepare icon output
    655                 $icon = apply_filters( 'pvc_post_views_icon', '<span class="post-views-icon ' . esc_attr( $icon_class ) . '"></span> ', $post_id );
    656 
    657                 // final views
    658                 $views = apply_filters( 'pvc_post_views_number_format', $views, $post_id );
    659 
    660                 $html = apply_filters(
    661                         'pvc_post_views_html',
    662                         '<div class="' . esc_attr( $class ) . '" data-pvc-type="post" data-pvc-id="' . esc_attr( $post_id ) . '">
    663                                 ' . ( $options['display_style']['icon'] ? $icon : '' )
    664                                 . ( $options['display_style']['text'] ? '<span class="post-views-label">' . esc_html( $label ) . '</span> ' : '' )
    665                                 . '<span class="post-views-count">' . $views . '</span>
    666                         </div>',
    667                         $post_id,
    668                         $views,
    669                         $label,
    670                         $icon
    671                 );
     904                $args = is_array( $args ) ? $args : [];
     905                $format_present = array_key_exists( 'format', $args );
     906                $format = $format_present ? $args['format'] : null;
     907                $resolved_period = is_scalar( $period ) ? sanitize_key( $period !== '' ? (string) $period : $options['display_period'] ) : '';
     908                $frontend = Post_Views_Counter()->frontend;
     909
     910                if ( $format_present ) {
     911                        if ( ! is_scalar( $format ) )
     912                                $html = '';
     913                        else {
     914                                // the format places the icon itself with %%icon%%, so the Views token
     915                                // renders the count alone, whatever the Display Style, and resolves no
     916                                // icon it would never draw; its hidden label is left out when the
     917                                // format supplies words of its own
     918                                $views_token_renderer = static function ( $format_has_text = false ) use ( $frontend, $post_id, $resolved_period ) {
     919                                        return $frontend->render_metric_span(
     920                                                'views',
     921                                                $post_id,
     922                                                'post',
     923                                                $resolved_period,
     924                                                [
     925                                                        'force_sr_label'        => true,
     926                                                        'omit_label'            => (bool) $format_has_text,
     927                                                        'icon'                          => ''
     928                                                ]
     929                                        );
     930                                };
     931                                $icon_token_renderer = static function () use ( $frontend, $post_id ) {
     932                                        return $frontend->get_counter_icon( 'views', $post_id, 'post', 'pvc_post_views_icon' );
     933                                };
     934                                $html = $frontend->render_template( (string) $format, $post_id, 'post', $resolved_period, $views_token_renderer, $icon_token_renderer );
     935                        }
     936                } else {
     937                        $html = $frontend->render_counter(
     938                                [ 'views' ],
     939                                $post_id,
     940                                'post',
     941                                $resolved_period,
     942                                [
     943                                        'metric_args' => [
     944                                                'views' => [ 'html_filter' => 'pvc_post_views_html' ]
     945                                        ]
     946                                ]
     947                        );
     948                }
    672949
    673950                if ( $display )
    674                         echo wp_kses_post( $html );
     951                        echo wp_kses( $html, Post_Views_Counter()->functions->get_counter_allowed_html() );
    675952                else
    676953                        return $html;
    … …  
    8961173                $pvc->counter->add_to_queue( $post_id );
    8971174        else
    898                 $pvc->counter->check_post( $post_id );
     1175                $pvc->counter->check_post( $post_id, [], true );
    8991176
    9001177        return true;
  • post-views-counter/tags/1.8.0/js/admin-columns.js

    r3452736 r3734102  
    11;(function($, window, document){
    2 !function($){if(void 0!==$){var a=null,e=null;if("undefined"!=typeof pvcColumnModal){var t=function(a){var e=$(".pvc-modal-tab");e.prop("disabled",a),a?e.addClass("pvc-tab-disabled"):e.removeClass("pvc-tab-disabled");var t=$(".pvc-modal-nav a");a?t.each(function(){$(this).addClass("pvc-temp-disabled pvc-disabled").attr("aria-disabled","true")}):$(".pvc-modal-nav a.pvc-temp-disabled").each(function(){$(this).removeClass("pvc-temp-disabled pvc-disabled").removeAttr("aria-disabled")})},o=function(a,e){var o=$(".pvc-modal-chart-container");o.addClass("loading"),o.find(".spinner").addClass("is-active"),t(!0),$.ajax({url:pvcColumnModal.ajaxURL,type:"POST",dataType:"json",data:{action:"pvc_column_chart",nonce:pvcColumnModal.nonce,post_id:a,period:e},success:function(a){a.success?n(a.data):d(a.data.message||pvcColumnModal.i18n.error)},error:function(){d(pvcColumnModal.i18n.error)},complete:function(){t(!1),o.removeClass("loading"),o.find(".spinner").removeClass("is-active")}})},n=function(e){var t=document.getElementById("pvc-modal-chart");if(t){a&&(a.destroy(),a=null),$(".pvc-modal-error").remove(),$(t).show(),$(".pvc-modal-views-label").text(pvcColumnModal.i18n.summary),$(".pvc-modal-count").text(e.total_views.toLocaleString()),$(".pvc-modal-dates").html(e.dates_html);var o={type:"line",data:e.data,options:{maintainAspectRatio:!1,responsive:!0,plugins:{legend:{display:!1},tooltip:{callbacks:{title:function(a){return e.data.dates[a[0].dataIndex]},label:function(a){var e=a.parsed.y,t=1===e?pvcColumnModal.i18n.view:pvcColumnModal.i18n.views;return"".concat(e.toLocaleString()," ").concat(t)}}}},scales:{x:{display:!0,grid:{display:!1}},y:{display:!0,beginAtZero:!0,ticks:{precision:0},grid:{color:"rgba(0, 0, 0, 0.05)"}}}}};e.design&&o.data.datasets.forEach(function(a){Object.assign(a,e.design),a.tension=.4}),a=new Chart(t.getContext("2d"),o),$(".pvc-modal-content-middle").show()}},d=function(e){a&&(a.destroy(),a=null),$(".pvc-modal-summary").text(""),$(".pvc-modal-dates").html(""),$(".pvc-modal-content-middle").hide();var t=$(".pvc-modal-chart-container");t.removeClass("loading"),t.find(".spinner").removeClass("is-active"),$(".pvc-modal-error").remove(),t.before('<p class="pvc-modal-error">'.concat(e,"</p>")),t.find("canvas").hide()};$(function(){0!==$("#pvc-chart-modal").length&&"undefined"!=typeof MicroModal&&(MicroModal.init({disableScroll:!0,awaitCloseAnimation:!0}),1)&&($(document).on("click",".pvc-view-chart",function(n){n.preventDefault();var d=$(n.currentTarget),l=d.data("post-id"),i=d.data("post-title");l&&function(a,o){if(!a)return!1;e=a,$("#pvc-modal-title").text(o);var n=$(".pvc-modal-chart-container");return n.addClass("loading"),n.find(".spinner").addClass("is-active"),t(!0),$(".pvc-modal-views-label").text(""),$(".pvc-modal-count").text(""),$(".pvc-modal-dates").html(""),$(".pvc-modal-content-middle").hide(),!0}(l,i)&&"undefined"!=typeof MicroModal&&MicroModal.show("pvc-chart-modal",{onShow:function(){e&&o(e,"")},onClose:function(){a&&(a.destroy(),a=null),e=null,function(){$("#pvc-modal-title").text(""),$(".pvc-modal-views-label").text(""),$(".pvc-modal-count").text(""),$(".pvc-modal-dates").html(""),$(".pvc-modal-content-middle").hide();var a=$(".pvc-modal-chart-container");a.removeClass("loading"),a.find(".spinner").removeClass("is-active"),$(".pvc-modal-error").remove()}()},disableScroll:!0,awaitCloseAnimation:!0})}),$(document).on("click",".pvc-modal-nav-prev, .pvc-modal-nav-next",function(a){a.preventDefault();var t=$(a.currentTarget);if(!t.hasClass("pvc-disabled")){var n=t.data("period");n&&e&&o(e,n)}}))})}}}(jQuery);
     2!function($){if(void 0!==$){var a=null,t=null,e="",o=null,n=null,l="";if("undefined"!=typeof pvcColumnModal){var c="pro"===pvcColumnModal.modalOwner,d=function(a){$(".pvc-modal-tab.pvc-pro").addClass("pvc-tab-disabled");var t=$(".pvc-modal-nav a");a?t.each(function(){$(this).addClass("pvc-temp-disabled pvc-disabled").attr("aria-disabled","true")}):$(".pvc-modal-nav a.pvc-temp-disabled").each(function(){$(this).removeClass("pvc-temp-disabled pvc-disabled").removeAttr("aria-disabled")})},r=function(a){$(".pvc-modal-status").text(a||"")},i=function(){$(".pvc-modal-count-views").text("")},v=function(a,t){n&&(n.abort(),n=null);var o=$(".pvc-modal-chart-container");e=t||"",o.addClass("loading"),o.find(".spinner").addClass("is-active"),$("#pvc-modal-chart-panel").attr("aria-busy","true"),r(pvcColumnModal.i18n.loading),d(!0),n=$.ajax({url:pvcColumnModal.ajaxURL,type:"POST",dataType:"json",data:{action:"pvc_column_chart",nonce:pvcColumnModal.nonce,post_id:a,period:t},success:function(a){a.success?s(a.data):p(a.data.message||pvcColumnModal.i18n.error)},error:function(a,t){"abort"!==t&&p(pvcColumnModal.i18n.error)},complete:function(){n=null,d(!1),function(){if(l){var a=l;l="";var t=document.querySelector("a.pvc-modal-nav-".concat(a))||document.querySelector(".pvc-modal-nav a")||document.querySelector(".pvc-modal-tab.active");t&&"function"==typeof t.focus&&t.focus()}}(),o.removeClass("loading"),o.find(".spinner").removeClass("is-active"),$("#pvc-modal-chart-panel").attr("aria-busy","false")}})},s=function(t){var o=document.getElementById("pvc-modal-chart");if(o){a&&(a.destroy(),a=null),$(".pvc-modal-error").remove(),r(""),$(o).show(),e=t.period||e,function(a){var t=a.totals||{};$(".pvc-modal-count-views").text(Number(t.views||0).toLocaleString())}(t),$(".pvc-modal-dates").html(t.dates_html);var n={type:"line",data:t.data,options:{maintainAspectRatio:!1,responsive:!0,interaction:{intersect:!1,mode:"index"},plugins:{legend:{display:!1},tooltip:{callbacks:{title:function(a){return t.data.dates[a[0].dataIndex]},label:function(a){return"".concat(a.dataset.label,": ").concat(function(a){var t=a.dataset.formatted;return Array.isArray(t)&&"string"==typeof t[a.dataIndex]?t[a.dataIndex]:a.formattedValue}(a))}}}},scales:{x:{display:!0,grid:{display:!1}},y:{display:!0,beginAtZero:!0,ticks:{precision:0},grid:{color:"rgba(0, 0, 0, 0.05)"}}}}};n.data.datasets.forEach(function(a){a.tension=.4}),a=new Chart(o.getContext("2d"),n),$(".pvc-modal-content-middle").show()}},p=function(t){a&&(a.destroy(),a=null),i(),$(".pvc-modal-dates").html(""),l="",$(".pvc-modal-content-middle").hide();var e=$(".pvc-modal-chart-container");e.removeClass("loading"),e.find(".spinner").removeClass("is-active"),$(".pvc-modal-error").remove();var o=$("<p>",{class:"pvc-modal-error",role:"alert"}).text(t);o.append(" ").append($("<button>",{type:"button",class:"button-link pvc-modal-retry"}).text(pvcColumnModal.i18n.retry)),e.before(o),r(""),e.find("canvas").hide()};$(function(){if(!c&&0!==$("#pvc-chart-modal").length&&"undefined"!=typeof MicroModal&&(MicroModal.init({disableScroll:!0,awaitCloseAnimation:!0}),1)){var s=$(document);s.off("click.pvcCoreModal",".pvc-view-chart").on("click.pvcCoreModal",".pvc-view-chart",function(c){c.preventDefault();var s=$(c.currentTarget),p=s.data("post-id"),u=s.data("post-title");p&&function(a,n,c){if(!a)return!1;t=a,o=c||null,e="",l="",$("#pvc-modal-title").text(n);var v=$(".pvc-modal-chart-container");return v.addClass("loading"),v.find(".spinner").addClass("is-active"),$("#pvc-modal-chart-panel").attr("aria-busy","true"),d(!0),i(),$(".pvc-modal-dates").html(""),r(pvcColumnModal.i18n.loading),$(".pvc-modal-content-middle").hide(),!0}(p,u,c.currentTarget)&&"undefined"!=typeof MicroModal&&MicroModal.show("pvc-chart-modal",{onShow:function(){t&&v(t,"")},onClose:function(){l="",n&&(n.abort(),n=null),a&&(a.destroy(),a=null),t=null,e="",function(){$("#pvc-modal-title").text(""),i(),$(".pvc-modal-dates").html(""),$(".pvc-modal-content-middle").hide();var a=$(".pvc-modal-chart-container");a.removeClass("loading"),a.find(".spinner").removeClass("is-active"),$(".pvc-modal-error").remove(),r("")}(),o&&document.body.contains(o)&&o.focus(),o=null},disableScroll:!0,awaitCloseAnimation:!0})}),s.off("click.pvcCoreModal",".pvc-modal-retry").on("click.pvcCoreModal",".pvc-modal-retry",function(a){a.preventDefault(),t&&v(t,e)}),s.off("click.pvcCoreModal",".pvc-modal-nav-prev, .pvc-modal-nav-next").on("click.pvcCoreModal",".pvc-modal-nav-prev, .pvc-modal-nav-next",function(a){a.preventDefault();var e=$(a.currentTarget);if(!n&&!e.hasClass("pvc-disabled")&&"true"!==e.attr("aria-disabled")){var o=e.data("period");o&&t&&(l=e.hasClass("pvc-modal-nav-next")?"next":"prev",v(t,o))}})}})}}}(jQuery);
    33
    44})(jQuery, window, document);
  • post-views-counter/tags/1.8.0/js/admin-dashboard.js

    r3578455 r3734102  
    11;(function($, window, document){
    2 function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);t&&(o=o.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,o)}return n}function t(t){for(var o=1;o<arguments.length;o++){var r=null!=arguments[o]?arguments[o]:{};o%2?e(Object(r),!0).forEach(function(e){n(t,e,r[e])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):e(Object(r)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))})}return t}function n(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=o(e)||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,t);if("object"!=o(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==o(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e){return o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o(e)}!function($){var e=["#pvc-dashboard-accordion"],n={weekly:"this_week",monthly:"this_month",yearly:"this_year"},r={loadError:window.pvcArgs&&window.pvcArgs.i18n&&window.pvcArgs.i18n.loadError?window.pvcArgs.i18n.loadError:"Failed to load this widget.",retry:window.pvcArgs&&window.pvcArgs.i18n&&window.pvcArgs.i18n.retry?window.pvcArgs.i18n.retry:"Retry"},a={},i={},s=$({});"function"!=typeof $.pvcAjaxQueue&&($.pvcAjaxQueue=function(e){var t,n=$.Deferred(),o=n.promise(),r=function(o){(t=$.ajax(e)).done(n.resolve).fail(n.reject).then(o,o)};return s.queue(r),o.abort=function(a){if(t)return t.abort(a);var i=s.queue(),d=$.inArray(r,i);return d>-1&&i.splice(d,1),n.rejectWith(e.context||e,[o,a,""]),o},o});var d=function(e){return e&&e.id?e.id.replace(/^pvc-/,""):""},c=function(e){return"string"==typeof e?{period:e}:e&&"object"===o(e)?t({},e):{}},l=function(e){return i[e]||(i[e]={loaded:!1,loading:!1,lastOptions:{},lastRequestId:0,activeRequest:null,error:null,lastResponse:null,promise:null}),i[e]},p=function(e,t){e&&0!==e.length&&(e.toggleClass("loading",t),e.find(".spinner").toggleClass("is-active",t))},u=function(e){if(!e||0===e.length)return $();var t=e.find(".pvc-dashboard-message").first();if(0===t.length){var n=e.find(".pvc-data-container").first();if(n.length>0){t=$('<div class="pvc-dashboard-message" aria-live="polite"></div>');var o=n.find(".spinner").first();o.length>0?o.before(t):n.append(t)}}return t},v=function(e){e&&0!==e.length&&(e.removeClass("pvc-has-error"),u(e).empty())},f=function(e,t){if(e&&0!==e.length&&!1!==t){var n=e.find(".pvc-date-nav").first();0===n.length&&(n=e.find(".pvc-months").first()),n.length>0&&(n[0].innerHTML=t)}},g=function(e){"function"==typeof window.pvcUpdateUserOptions&&window.pvcUpdateUserOptions(e)},w=function(e,n){var o=t({},n);delete o.dates,delete o.html,delete o.design,window.dispatchEvent(new CustomEvent(e,{detail:o}))},h=function(e,t){a[e]=t,l(e)},b=function(e){return!!a[e]},m=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],i=function(e){return a[e]||null}(e),s=l(e),d=function(e){var t=document.getElementById("pvc-".concat(e));return t?$(t).find(".pvc-dashboard-container").first():$()}(e);if(!i||0===d.length)return null;if(s.loading&&!o)return s.promise||s.activeRequest;if(s.loaded&&!o)return s.promise||s.lastResponse;var f=s.lastRequestId+1,g=t(t({},s.lastOptions),c(n)),w={container:d,widgetId:e,isInitialLoad:!s.loaded};s.lastRequestId=f,s.loading=!0,s.lastOptions=g,s.error=null,s.promise=null,v(d),p(d,!0),s.activeRequest&&"function"==typeof s.activeRequest.abort&&s.activeRequest.abort("pvc-dashboard-refresh");var h=$.ajax({url:i.ajaxURL||pvcArgs.ajaxURL,type:"POST",dataType:"json",data:i.buildRequest(g,w,s)});return s.activeRequest=h,s.promise=h,h.done(function(e){s.lastRequestId===f&&(s.loaded=!0,s.loading=!1,s.error=null,s.activeRequest=null,s.lastResponse=e,v(d),p(d,!1),i.render(e,w,g,s))}),h.fail(function(t,n,o){s.lastRequestId===f&&(s.loading=!1,s.activeRequest=null,s.error={statusText:n,errorThrown:o},p(d,!1),"abort"!==n&&function(e,t,n){if(e&&0!==e.length){var o=['<div class="pvc-dashboard-error" role="alert">','<div class="pvc-dashboard-error-message">','<span class="pvc-dashboard-error-text">',n,"</span>","</div>",'<button type="button" class="button button-secondary pvc-dashboard-retry" data-widget-id="',t,'">',r.retry,"</button>","</div>"].join("");e.addClass("pvc-has-error"),u(e).html(o)}}(d,e,i.errorMessage||r.loadError))}),h},y=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=l(e);return n.loaded||n.loading?n.promise||n.activeRequest||n.lastResponse||null:m(e,t,!1)},C=function(e){return m(e,arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},!0)},j=function(e){var t=$(e);0!==t.length&&t.find(".pvc-accordion-item:not(.pvc-collapsed)").each(function(e,t){var n=d(t);n&&b(n)&&y(n)})},O=function(e){var t=e.target.closest(".pvc-dashboard-retry");if(t){var o=t.dataset.widgetId;o&&b(o)&&(e.preventDefault(),e.stopImmediatePropagation(),C(o))}else{var r=e.target.closest(".pvc-date-select .button");if(r){var a=r.closest(".pvc-accordion-item"),i=d(a);i&&b(i)&&(e.preventDefault(),e.stopImmediatePropagation(),function(e){var t=String($(e).data("datenav")),o=$(e).closest(".pvc-date-select"),r=$(e).closest(".pvc-accordion-item"),a=d(r[0]);if(a&&b(a)){var i=o.find(".button-primary").first(),s=i.length>0?String(i.data("datenav")):"";if(o.find(".button").each(function(e,n){String($(n).data("datenav"))===t?$(n).removeClass("button-secondary").addClass("button-primary").blur():$(n).removeClass("button-primary").addClass("button-secondary")}),t!==s){C(a,{datenav:t,period:n[t]||""});var c=a.replace(/^pvc-/,""),l={datenav:{}};l.datenav[c]=t,g(l)}}}(r))}else{var s=e.target.closest(".pvc-date-nav .prev, .pvc-date-nav .next");if(s){var c=s.closest(".pvc-accordion-item"),l=d(c);l&&b(l)&&(e.preventDefault(),e.stopImmediatePropagation(),C(l,{period:s.dataset.date}))}else{var p=e.target.closest(".pvc-accordion-header");if(p){var u,v=p.closest(".pvc-accordion-item"),f=d(v);if(f&&b(f))e.preventDefault(),e.stopImmediatePropagation(),v.classList.toggle("pvc-collapsed"),g({menu_items:(u={},["#pvc-dashboard-accordion .pvc-accordion-item","#pvc-dashboard-traffic-accordion .pvc-accordion-item"].forEach(function(e){$(e).each(function(e,t){var n=$(t).attr("id");void 0!==n&&(u[n.replace("pvc-","")]=$(t).hasClass("pvc-collapsed"))})}),u)}),v.classList.contains("pvc-collapsed")||y(f)}}}}},R=window.pvcDashboard||{};Object.assign(R,{registerWidget:h,ensureWidgetLoaded:y,refreshWidget:C,loadExpandedWidgets:j,isWidgetLoaded:function(e){return l(e).loaded},isWidgetRegistered:b}),window.pvcDashboard=R,window.pvcDashboardEventsBound||(window.pvcDashboardEventsBound=!0,document.addEventListener("click",O,!0)),window.addEventListener("load",function(){j(e[0])}),window.dispatchEvent(new CustomEvent("pvc-dashboard-controller-ready",{detail:R})),window.pvcUpdateUserOptions=function(e){$.pvcAjaxQueue({url:pvcArgs.ajaxURL,type:"POST",dataType:"json",data:{action:"pvc_dashboard_user_options",nonce:pvcArgs.nonceUser,options:e},success:function(){}})},window.pvcUpdateConfig=function(e,t){return e.data=t.data,e.options.plugins.tooltip={callbacks:{title:function(e){return t.data.dates[e[0].dataIndex]}}},$.each(e.data.datasets,function(e,n){n.fill=t.design.fill,n.tension=.4,n.borderColor=t.design.borderColor,n.backgroundColor=t.design.backgroundColor,n.borderWidth=t.design.borderWidth,n.borderDash=t.design.borderDash,n.pointBorderColor=t.design.pointBorderColor,n.pointBackgroundColor=t.design.pointBackgroundColor,n.pointBorderWidth=t.design.pointBorderWidth}),e},h("post-views",{buildRequest:function(e){return{action:"pvc_dashboard_post_views_chart",nonce:pvcArgs.nonce,period:e.period||"",datenav:e.datenav||"",lang:pvcArgs.lang?pvcArgs.lang:""}},render:function(e,t,n,o){if(f(t.container,e.dates),t.isInitialLoad||!window.postViewsChart){var r={type:"line",options:{maintainAspectRatio:!1,responsive:!0,plugins:{legend:{display:!0,position:"bottom",align:"center",fullSize:!0,onHover:function(e){e.native.target.style.cursor="pointer"},onLeave:function(e){e.native.target.style.cursor="default"},onClick:function(e,t,n){var o=t.datasetIndex,r=n.chart,a=r.getDatasetMeta(o);a.hidden=!!r.isDatasetVisible(o),r.update(),window.pvcUpdateUserOptions({post_type:r.data.datasets[o].post_type,hidden:a.hidden})},labels:{boxWidth:8,boxHeight:8,font:{size:13,weight:"normal",family:"'-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Oxygen-Sans', 'Ubuntu', 'Cantarell', 'Helvetica Neue', 'sans-serif'"},padding:10,usePointStyle:!1,textAlign:"center"}}},scales:{x:{display:!0,title:{display:!1}},y:{display:!0,grace:0,beginAtZero:!0,title:{display:!1},ticks:{precision:0,maxTicksLimit:12}}},hover:{mode:"label"}}};return r=window.pvcUpdateConfig(r,e),window.postViewsChart=new Chart(document.getElementById("pvc-post-views-chart").getContext("2d"),r),void w("pvc-dashboard-widget-loaded",e)}window.postViewsChart.config=window.pvcUpdateConfig(window.postViewsChart.config,e),window.postViewsChart.update(),w("pvc-dashboard-widget-loaded",e)},errorMessage:r.loadError}),h("post-most-viewed",{buildRequest:function(e){return{action:"pvc_dashboard_post_most_viewed",nonce:pvcArgs.nonce,period:e.period||"",datenav:e.datenav||"",lang:pvcArgs.lang?pvcArgs.lang:""}},render:function(e,t){f(t.container,e.dates),t.container.find("#pvc-post-most-viewed-content").html(e.html),w("pvc-dashboard-widget-loaded",e)},errorMessage:r.loadError}),window.pvcUpdatePostViewsWidget=function(){C("post-views",c(arguments.length>0&&void 0!==arguments[0]?arguments[0]:""))},window.pvcUpdatePostMostViewedWidget=function(){C("post-most-viewed",c(arguments.length>0&&void 0!==arguments[0]?arguments[0]:""))},window.pvcLoadPostViewsData=function(){C("post-views",c(arguments.length>0&&void 0!==arguments[0]?arguments[0]:""))},window.pvcLoadPostMostViewedData=function(){C("post-most-viewed",c(arguments.length>0&&void 0!==arguments[0]?arguments[0]:""))}}(jQuery);
     2function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),n.push.apply(n,r)}return n}function t(t){for(var r=1;r<arguments.length;r++){var i=null!=arguments[r]?arguments[r]:{};r%2?e(Object(i),!0).forEach(function(e){n(t,e,i[e])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))})}return t}function n(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=r(e)||!e)return e;var n;if("undefined"!=typeof Symbol&&void 0!==(n=e[Symbol.toPrimitive])){var i=n.call(e,t);if("object"!=r(i))return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==r(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}!function($){var e=["#pvc-dashboard-accordion"],n={weekly:"this_week",monthly:"this_month",yearly:"this_year"},i={loadError:window.pvcArgs&&window.pvcArgs.i18n&&window.pvcArgs.i18n.loadError?window.pvcArgs.i18n.loadError:"Failed to load this widget.",retry:window.pvcArgs&&window.pvcArgs.i18n&&window.pvcArgs.i18n.retry?window.pvcArgs.i18n.retry:"Retry"},a={},o={},s=$({});"function"!=typeof $.pvcAjaxQueue&&($.pvcAjaxQueue=function(e){var t,n=$.Deferred(),r=n.promise(),i=function(r){(t=$.ajax(e)).done(n.resolve).fail(n.reject).then(r,r)};return s.queue(i),r.abort=function(a){if(t)return t.abort(a);var o=s.queue(),d=$.inArray(i,o);return d>-1&&o.splice(d,1),n.rejectWith(e.context||e,[r,a,""]),r},r});var d=function(e){return e&&e.id?e.id.replace(/^pvc-/,""):""},c=function(e){return"string"==typeof e?{period:e}:e&&"object"===r(e)?t({},e):{}},l=function(e){return o[e]||(o[e]={loaded:!1,loading:!1,lastOptions:{},lastRequestId:0,activeRequest:null,error:null,lastResponse:null,promise:null}),o[e]},p=function(e,t){e&&0!==e.length&&(e.toggleClass("loading",t),e.find(".spinner").toggleClass("is-active",t))},v=function(e){if(!e||0===e.length)return $();var t=e.find(".pvc-dashboard-message").first();if(0===t.length){var n=e.find(".pvc-data-container").first();if(n.length>0){t=$('<div class="pvc-dashboard-message" aria-live="polite"></div>');var r=n.find(".spinner").first();r.length>0?r.before(t):n.append(t)}}return t},u=function(e){e&&0!==e.length&&(e.removeClass("pvc-has-error"),v(e).empty())},f=function(e,t){if(e&&0!==e.length&&!1!==t){var n=e.find(".pvc-date-nav").first();0===n.length&&(n=e.find(".pvc-months").first()),n.length>0&&(n[0].innerHTML=t)}},g=function(e){"function"==typeof window.pvcUpdateUserOptions&&window.pvcUpdateUserOptions(e)},h=function(e,n){var r=t({},n);delete r.dates,delete r.html,delete r.design,window.dispatchEvent(new CustomEvent(e,{detail:r}))},w=function(e,t){a[e]=t,l(e)},b=function(e){return!!a[e]},m=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],o=function(e){return a[e]||null}(e),s=l(e),d=function(e){var t=document.getElementById("pvc-".concat(e));return t?$(t).find(".pvc-dashboard-container").first():$()}(e);if(!o||0===d.length)return null;if(s.loading&&!r)return s.promise||s.activeRequest;if(s.loaded&&!r)return s.promise||s.lastResponse;var f=s.lastRequestId+1,g=t(t({},s.lastOptions),c(n)),h={container:d,widgetId:e,isInitialLoad:!s.loaded};s.lastRequestId=f,s.loading=!0,s.lastOptions=g,s.error=null,s.promise=null,u(d),p(d,!0),s.activeRequest&&"function"==typeof s.activeRequest.abort&&s.activeRequest.abort("pvc-dashboard-refresh");var w=$.ajax({url:o.ajaxURL||pvcArgs.ajaxURL,type:"POST",dataType:"json",data:o.buildRequest(g,h,s)});return s.activeRequest=w,s.promise=w,w.done(function(e){s.lastRequestId===f&&(s.loaded=!0,s.loading=!1,s.error=null,s.activeRequest=null,s.lastResponse=e,u(d),p(d,!1),o.render(e,h,g,s))}),w.fail(function(t,n,r){s.lastRequestId===f&&(s.loading=!1,s.activeRequest=null,s.error={statusText:n,errorThrown:r},p(d,!1),"abort"!==n&&function(e,t,n){if(e&&0!==e.length){var r=['<div class="pvc-dashboard-error" role="alert">','<div class="pvc-dashboard-error-message">','<span class="pvc-dashboard-error-text">',n,"</span>","</div>",'<button type="button" class="button button-secondary pvc-dashboard-retry" data-widget-id="',t,'">',i.retry,"</button>","</div>"].join("");e.addClass("pvc-has-error"),v(e).html(r)}}(d,e,o.errorMessage||i.loadError))}),w},y=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=l(e);return n.loaded||n.loading?n.promise||n.activeRequest||n.lastResponse||null:m(e,t,!1)},x=function(e){return m(e,arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},!0)},C=function(e){var t=$(e);0!==t.length&&t.find(".pvc-accordion-item:not(.pvc-collapsed)").each(function(e,t){var n=d(t);n&&b(n)&&y(n)})},A=function(e){var t=e.target.closest(".pvc-dashboard-retry");if(t){var r=t.dataset.widgetId;r&&b(r)&&(e.preventDefault(),e.stopImmediatePropagation(),x(r))}else{var i=e.target.closest(".pvc-date-select .button");if(i){var a=i.closest(".pvc-accordion-item"),o=d(a);o&&b(o)&&(e.preventDefault(),e.stopImmediatePropagation(),function(e){if(!e.disabled&&"true"!==e.getAttribute("aria-disabled")){var t=String($(e).data("datenav")),r=$(e).closest(".pvc-date-select"),i=$(e).closest(".pvc-accordion-item"),a=d(i[0]);if(a&&b(a)){var o=r.find(".button-primary").first(),s=o.length>0?String(o.data("datenav")):"";if(r.find(".button").each(function(e,n){String($(n).data("datenav"))===t?$(n).removeClass("button-secondary").addClass("button-primary").blur():$(n).removeClass("button-primary").addClass("button-secondary")}),t!==s){x(a,{datenav:t,period:n[t]||""});var c=a.replace(/^pvc-/,""),l={datenav:{}};l.datenav[c]=t,g(l)}}}}(i))}else{var s=e.target.closest(".pvc-date-nav .prev, .pvc-date-nav .next");if(s){var c=s.closest(".pvc-accordion-item"),l=d(c);l&&b(l)&&(e.preventDefault(),e.stopImmediatePropagation(),x(l,{period:s.dataset.date}))}else{var p=e.target.closest(".pvc-accordion-header");if(p){var v,u=p.closest(".pvc-accordion-item"),f=d(u);if(f&&b(f))e.preventDefault(),e.stopImmediatePropagation(),u.classList.toggle("pvc-collapsed"),g({menu_items:(v={},["#pvc-dashboard-accordion .pvc-accordion-item","#pvc-dashboard-traffic-accordion .pvc-accordion-item"].forEach(function(e){$(e).each(function(e,t){var n=$(t).attr("id");void 0!==n&&(v[n.replace("pvc-","")]=$(t).hasClass("pvc-collapsed"))})}),v)}),u.classList.contains("pvc-collapsed")||y(f)}}}}},_={},O=["both","views","visits"],j=function(e){return _[e]||(_[e]={preferred:"",bothHidden:{}}),_[e]},E=function(e){return!!(e&&e.metric_state&&Array.isArray(e.metrics)&&e.data&&Array.isArray(e.data.datasets))},R=function(e,t){var n=j(e),r=!(!t.availability||!t.availability.readable);if(!n.preferred){var i=t.metric_state?t.metric_state.preferred:"";n.preferred=-1!==O.indexOf(i)?i:"both"}return{available:r,preferred:n.preferred,effective:r?n.preferred:"views"}},k=function(e,t,n,r){if(t&&E(n)){var i=j(e),a=R(e,n);n.data.datasets.forEach(function(e,n){e.fill="origin",e.backgroundColor=function(e,t){if("string"!=typeof e)return e;var n=e.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*[\d.]+)?\s*\)$/i);return n?"rgba(".concat(n[1],",").concat(n[2],",").concat(n[3],",").concat(t,")"):e}(e.borderColor,.1),t.setDatasetVisibility(n,function(e,t,n){return"both"===t?!!e.is_total&&!0!==n.bothHidden[e.metric]:e.metric===t&&!0!==e.dimension_hidden}(e,a.effective,i))}),t.update(),function(e,t,n){var r=e.find(".pvc-dashboard-metrics").get(0);if(r&&window.pvcMetricToggle&&"function"==typeof window.pvcMetricToggle.render){var i=r.getAttribute("data-pvc-widget")?"pvc-".concat(r.getAttribute("data-pvc-widget"),"-visits-unavailable"):"",a=t.metrics.map(function(e){var t=!1!==e.available&&("visits"!==e.key||n.available);return{metric:e.key,key:e.key,label:e.label,pressed:t&&("both"===n.effective||n.effective===e.key),disabled:!t,describedBy:t?"":i,color:e.color,dashed:!0===e.dashed}});window.pvcMetricToggle.render(r,a,{containerClasses:["pvc-dashboard-metric-buttons"]});var o=e.find("#".concat(i));o.length>0&&(n.available?o.attr("hidden","hidden").text(""):o.removeAttr("hidden").text(t.notices&&t.notices.visits_unavailable||""))}}(r,n,a),function(e,t,n,r){var i=e.find(".pvc-dashboard-legend").get(0);i&&window.pvcMetricToggle&&"function"==typeof window.pvcMetricToggle.render&&window.pvcMetricToggle.render(i,function(e,t,n){return t.data.datasets.map(function(e,t){return{dataset:e,index:t}}).filter(function(e){return"both"===n?!!e.dataset.is_total:e.dataset.metric===n}).map(function(t){return{metric:t.dataset.metric,label:t.dataset.label,datasetIndex:t.index,pressed:e.isDatasetVisible(t.index),color:t.dataset.borderColor,circle:!0}})}(t,n,r),{containerClasses:["pvc-dashboard-dataset-legend"]})}(r,t,n,a.effective)}},P=function(e,t,n){var r=t.get(0);r&&"1"!==r.dataset.pvcMetricControlsBound&&(r.dataset.pvcMetricControlsBound="1",r.addEventListener("click",function(r){var i=r.target.closest(".pvc-metric-toggle-item");if(i&&!i.disabled){var a=n();a&&a.chart&&E(a.response)&&(r.preventDefault(),r.stopImmediatePropagation(),i.closest(".pvc-dashboard-metrics")?function(e,t,n,r,i){var a=j(e),o=R(e,n);if("visits"!==i||o.available){var s="both"===o.effective?["views","visits"]:[o.effective],d="";if(-1!==s.indexOf(i)){if(1===s.length)return;d="views"===i?"visits":"views"}else d="both";if(d!==o.effective){a.bothHidden={},a.preferred=d,k(e,t,n,r);var c={};c[e]=d,g({metrics:c})}}}(e,a.chart,a.response,t,i.getAttribute("data-pvc-metric")):i.closest(".pvc-dashboard-legend")&&function(e,t,n,r,i){var a=n.data.datasets[i];if(a){var o=j(e),s=R(e,n),d=!t.isDatasetVisible(i);if("both"===s.effective)o.bothHidden[a.metric]=!d;else{var c=function(e){for(var t=["post_type","content_type","taxonomy"],n=0;n<t.length;n++)if(e[t[n]])return{key:t[n],value:e[t[n]]};return null}(a);if(n.data.datasets.forEach(function(e){c&&e[c.key]===c.value&&(e.dimension_hidden=!d)}),c){var l={hidden:!d};l[c.key]=c.value,g(l)}}k(e,t,n,r)}}(e,a.chart,a.response,t,parseInt(i.getAttribute("data-pvc-dataset-index"),10)))}}))},I=window.pvcDashboard||{};Object.assign(I,{registerWidget:w,ensureWidgetLoaded:y,refreshWidget:x,loadExpandedWidgets:C,isWidgetLoaded:function(e){return l(e).loaded},isWidgetRegistered:b,metrics:{version:1,isPairedResponse:E,applyMetricMode:k,bindMetricControls:P,getMetricState:j}}),window.pvcDashboard=I,window.pvcDashboardEventsBound||(window.pvcDashboardEventsBound=!0,document.addEventListener("click",A,!0));var D={up:"up",down:"down",mixed:"neutral",same:"neutral"},S=["locked","not_enough_data","no_insights"];window.addEventListener("pvc-dashboard-widget-loaded",function(e){return function(e){if(e&&"string"==typeof e.widget){var t=document.getElementById("pvc-".concat(e.widget,"-widget-insights"));t&&(t.textContent="",(Array.isArray(e.insight_notices)?e.insight_notices.slice(0,4):[]).forEach(function(e){if(e&&"string"==typeof e.text&&""!==e.text){var n=document.createElement("p"),r=["pvc-widget-insight"];if("string"==typeof e.state){if(!S.includes(e.state))return;if(r.push("pvc-widget-insight-state","locked"===e.state?"pvc-widget-insight-locked":"pvc-widget-insight-neutral"),"locked"===e.state&&"string"==typeof e.url&&/^https?:\/\//.test(e.url)){var i=document.createElement("a");i.setAttribute("href",e.url),i.setAttribute("target","_blank"),i.setAttribute("rel","noopener noreferrer"),"string"==typeof e.link_text&&""!==e.link_text?(n.appendChild(document.createTextNode("".concat(e.text," "))),i.textContent=e.link_text):i.textContent=e.text,n.appendChild(i)}else n.textContent=e.text}else r.push("pvc-widget-insight-".concat(D[e.direction]||"neutral")),n.textContent=e.text;n.className=r.join(" "),t.appendChild(n)}}),t.hidden=0===t.childNodes.length)}}(e.detail)}),window.addEventListener("load",function(){C(e[0])}),window.dispatchEvent(new CustomEvent("pvc-dashboard-controller-ready",{detail:I})),window.pvcUpdateUserOptions=function(e){$.pvcAjaxQueue({url:pvcArgs.ajaxURL,type:"POST",dataType:"json",data:{action:"pvc_dashboard_user_options",nonce:pvcArgs.nonceUser,options:e},success:function(){}})},window.pvcUpdateConfig=function(e,t){e.data=t.data,e.options.plugins.tooltip={callbacks:{title:function(e){return t.data.dates[e[0].dataIndex]},label:function(e){var t=e.dataset.formatted,n=Array.isArray(t)&&"string"==typeof t[e.dataIndex]?t[e.dataIndex]:e.formattedValue;return e.dataset.label?"".concat(e.dataset.label,": ").concat(n):n}}};var n=t.design||{},r=["fill","tension","borderColor","backgroundColor","borderWidth","borderDash","pointBorderColor","pointBackgroundColor","pointBorderWidth"];return $.each(e.data.datasets,function(e,t){r.forEach(function(e){Object.prototype.hasOwnProperty.call(n,e)&&(Object.prototype.hasOwnProperty.call(t,e)||(t[e]=n[e]))}),Object.prototype.hasOwnProperty.call(t,"tension")||(t.tension=.4)}),e};w("post-views",{buildRequest:function(e){return{action:"pvc_dashboard_post_views_chart",nonce:pvcArgs.nonce,period:e.period||"",datenav:e.datenav||"",lang:pvcArgs.lang?pvcArgs.lang:""}},render:function(e,t,n,r){f(t.container,e.dates),function(e,t){var n=e[0]&&e[0].querySelector(".pvc-dashboard-headline .pvc-data-count");if(n){var r=t&&t.summary;n.textContent=r&&"string"==typeof r.views_html&&""!==r.views_html.trim()?r.views_html:""}}(t.container,e);var i=E(e);if(t.isInitialLoad||!window.postViewsChart){var a={type:"line",options:{maintainAspectRatio:!1,responsive:!0,interaction:{intersect:!1,mode:"index"},plugins:{legend:{display:!i,position:"bottom",align:"center",fullSize:!0,onHover:function(e){e.native.target.style.cursor="pointer"},onLeave:function(e){e.native.target.style.cursor="default"},onClick:function(e,t,n){var r=t.datasetIndex,i=n.chart,a=i.getDatasetMeta(r);a.hidden=!!i.isDatasetVisible(r),i.update(),window.pvcUpdateUserOptions({post_type:i.data.datasets[r].post_type,hidden:a.hidden})},labels:{boxWidth:8,boxHeight:8,font:{size:13,weight:"normal",family:"'-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'Oxygen-Sans', 'Ubuntu', 'Cantarell', 'Helvetica Neue', 'sans-serif'"},padding:10,usePointStyle:!1,textAlign:"center"}}},scales:{x:{display:!0,title:{display:!1}},y:{display:!0,grace:0,beginAtZero:!0,title:{display:!1},ticks:{precision:0,maxTicksLimit:12}}}}};return a=window.pvcUpdateConfig(a,e),window.postViewsChart=new Chart(document.getElementById("pvc-post-views-chart").getContext("2d"),a),i&&(P("post-views",t.container,function(){return{chart:window.postViewsChart,response:l("post-views").lastResponse}}),k("post-views",window.postViewsChart,e,t.container)),void h("pvc-dashboard-widget-loaded",e)}window.postViewsChart.config=window.pvcUpdateConfig(window.postViewsChart.config,e),window.postViewsChart.config.options.plugins.legend.display=!i,window.postViewsChart.update(),i&&k("post-views",window.postViewsChart,e,t.container),h("pvc-dashboard-widget-loaded",e)},errorMessage:i.loadError}),w("post-most-viewed",{buildRequest:function(e){return{action:"pvc_dashboard_post_most_viewed",nonce:pvcArgs.nonce,period:e.period||"",datenav:e.datenav||"",lang:pvcArgs.lang?pvcArgs.lang:""}},render:function(e,t){f(t.container,e.dates),t.container.find("#pvc-post-most-viewed-content").html(e.html),h("pvc-dashboard-widget-loaded",e)},errorMessage:i.loadError}),window.pvcUpdatePostViewsWidget=function(){x("post-views",c(arguments.length>0&&void 0!==arguments[0]?arguments[0]:""))},window.pvcUpdatePostMostViewedWidget=function(){x("post-most-viewed",c(arguments.length>0&&void 0!==arguments[0]?arguments[0]:""))},window.pvcLoadPostViewsData=function(){x("post-views",c(arguments.length>0&&void 0!==arguments[0]?arguments[0]:""))},window.pvcLoadPostMostViewedData=function(){x("post-most-viewed",c(arguments.length>0&&void 0!==arguments[0]?arguments[0]:""))}}(jQuery);
    33
    44})(jQuery, window, document);
  • post-views-counter/tags/1.8.0/js/admin-post.js

    r3443281 r3734102  
    11;(function($, window, document){
    2 var $;($=jQuery)(function(){var t=$("#post-views-input-container"),s=$("#post-views-display b"),e=$("#post-views-input"),i=$("#post-views .edit-post-views");i.on("click",function(s){return t.is(":hidden")&&(t.slideDown("fast"),$(s.currentTarget).hide()),!1}),$("#post-views .save-post-views").on("click",function(){var n=s.text().trim();return t.slideUp("fast"),i.show(),n=parseInt(e.val(),10),e.val(n),s.text(n),!1}),$("#post-views .cancel-post-views").on("click",function(){var n=s.text().trim();return t.slideUp("fast"),i.show(),n=parseInt($("#post-views-current").val(),10),s.text(n),e.val(n),!1})});
     2var $;($=jQuery)(function(){var t=$("#post-views"),n=$("#post-views-input-container"),i=t.find(".edit-post-views"),e=document.documentElement.lang||void 0,r=[{$display:$("#post-views-display b"),$input:$("#post-views-input"),$current:$("#post-views-current")}],s=function(t){try{return new Intl.NumberFormat(e).format(t)}catch(n){return String(t)}};i.on("click",function(t){return n.is(":hidden")&&(n.slideDown("fast"),$(t.currentTarget).hide(),$("#post-views-input").trigger("focus")),!1}),t.find(".save-post-views").on("click",function(){return r.forEach(function(t){var n=t.$display,i=t.$input;if(i.length&&!i.prop("disabled")){var e=String(i.val()).trim();/^-?\d+$/.test(e)&&n.text(s(Math.max(0,parseInt(e,10))))}}),n.slideUp("fast"),i.show().trigger("focus"),!1}),t.find(".cancel-post-views").on("click",function(){return r.forEach(function(t){var n=t.$display,i=t.$input,e=t.$current;if(i.length&&e.length&&!i.prop("disabled")){var r=parseInt(e.val(),10);i.val(r),n.text(s(r))}}),n.slideUp("fast"),i.show().trigger("focus"),!1})});
    33
    44})(jQuery, window, document);
  • post-views-counter/tags/1.8.0/js/admin-quick-edit.js

    r3578455 r3734102  
    11;(function($, window, document){
    2 function t(n){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(n)}var $;($=jQuery)(function(){var n=window.pvcArgsQuickEdit||{nonce:"",wpVersion59:!1},i=["aa","mm","jj","hh","mn","ss"],e=function(t){if("string"!=typeof t)return"";var n=t.trim();if(""===n)return"";var i=n.replace(/[^0-9-]+/g,"");return""===i||/^-?$/.test(i)||!/^-?\d+$/.test(i)?"":i},r=function(t){if(!(t<=0)){var n=$("#edit-".concat(t));n.length&&n.find(':input[name="post_views"]').length&&i.forEach(function(i){var e=n.find(':input[name="'.concat(i,'"]')).first();if(e.length&&!/^\d+$/.test(String(e.val()||"").trim())){var r=function(t,n){return $("#inline_".concat(t)).find(".".concat(n)).first().text().trim()}(t,i);/^\d+$/.test(r)&&e.val(r)}})}},o=function(t){if(!(t<=0)){var n=$("#edit-".concat(t)),i=function(t){var n=$(".column-post_views",t),i=n.find("[data-post-views]").first(),r=e(i.attr("data-post-views")||"");return""!==r?r:e(n.text())}($("#post-".concat(t)));$(':input[name="post_views"]',n).val(i),$(':input[name="current_post_views"]',n).val(i)}},a=function(){var n=window.inlineEditPost;if(!n||"function"!=typeof n.edit)return!1;if(n._pvcPostViewsWrapped)return!0;var i=n.edit;return n.edit=function(n){var e=i.apply(this,arguments),r=function(n,i){return"object"===t(i)?parseInt(n.getId(i),10):parseInt(i,10)}(this,n);return o(r),e},n._pvcPostViewsWrapped=!0,!0},c=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;a()||t>=40||window.setTimeout(function(){c(t+1)},50)};c(),document.addEventListener("click",function(t){if(t.target&&t.target.closest){var n=t.target.closest("button.save");if(n){var i=$(n.closest('tr.inline-editor[id^="edit-"]'));i.length&&function(t){var n=e(String(t.find(':input[name="post_views"]').val()||"")),i=e(String(t.find(':input[name="current_post_views"]').val()||""));return""!==n&&n!==i}(i)&&r(parseInt(i.attr("id").replace("edit-",""),10))}}},!0),$(document).off("click.pvcBulkEdit","#bulk_edit").on("click.pvcBulkEdit","#bulk_edit",function(){var t=$("#bulk-edit"),i=[];n.wpVersion59?t.find("#bulk-titles-list").children(".ntdelitem").each(function(){var t=$(this).find("button").attr("id")||"";i.push(t.replace(/[^0-9]/g,""))}):t.find("#bulk-titles").children().each(function(){var t=$(this).attr("id")||"";i.push(t.replace(/^(ttle)/i,""))});var e=t.find('input[name="post_views"]').val();$.ajax({url:ajaxurl,type:"post",async:!1,cache:!1,data:{action:"save_bulk_post_views",post_ids:i.filter(Boolean),post_views:e,current_post_views:e,nonce:n.nonce}})})});
     2function t(n){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},t(n)}var $;($=jQuery)(function(){var n=["aa","mm","jj","hh","mn","ss"],e=function(t){if("string"!=typeof t)return"";var n=t.trim();if(""===n)return"";var e=n.replace(/[^0-9-]+/g,"");return""===e||/^-?$/.test(e)||!/^-?\d+$/.test(e)?"":e},i=function(t){if(!(t<=0)){var e=$("#edit-".concat(t));e.length&&e.find(':input[name="post_views"]').length&&n.forEach(function(n){var i=e.find(':input[name="'.concat(n,'"]')).first();if(i.length&&!/^\d+$/.test(String(i.val()||"").trim())){var r=function(t,n){return $("#inline_".concat(t)).find(".".concat(n)).first().text().trim()}(t,n);/^\d+$/.test(r)&&i.val(r)}})}},r=function(t){if(!(t<=0)){var n=$("#edit-".concat(t)),i=$(':input[name="post_views"]',n);if(i.length){var r=$(':input[name="current_post_views"]',n);if("1"!==i.attr("data-pvc-editable"))return i.prop("disabled",!0).prop("readonly",!0).val(""),void r.val("");var o,a=(o=$("#post-".concat(t)),e($(".column-post_views",o).find("[data-post-views]").first().attr("data-post-views")||""));i.prop("disabled",!1).prop("readonly",!1).val(a),r.val(a)}}},o=function(){var n=window.inlineEditPost;if(!n||"function"!=typeof n.edit)return!1;if(n._pvcPostViewsWrapped)return!0;var e=n.edit;return n.edit=function(n){var i=e.apply(this,arguments),o=function(n,e){return"object"===t(e)?parseInt(n.getId(e),10):parseInt(e,10)}(this,n);return r(o),i},n._pvcPostViewsWrapped=!0,!0},a=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;o()||t>=40||window.setTimeout(function(){a(t+1)},50)};a(),document.addEventListener("click",function(t){if(t.target&&t.target.closest){var n=t.target.closest("button.save");if(n){var r=$(n.closest('tr.inline-editor[id^="edit-"]'));r.length&&function(t){var n=e(String(t.find(':input[name="post_views"]').val()||"")),i=e(String(t.find(':input[name="current_post_views"]').val()||""));return""!==n&&n!==i}(r)&&i(parseInt(r.attr("id").replace("edit-",""),10))}}},!0)});
    33
    44})(jQuery, window, document);
  • post-views-counter/tags/1.8.0/js/admin-settings.js

    r3578455 r3734102  
    11;(function($, window, document){
    2 const e=(e,t=0,n=1)=>e>n?n:e<t?t:e,t=(e,t=0,n=Math.pow(10,t))=>Math.round(n*e)/n,n=e=>("#"===e[0]&&(e=e.substring(1)),e.length<6?{r:parseInt(e[0]+e[0],16),g:parseInt(e[1]+e[1],16),b:parseInt(e[2]+e[2],16),a:4===e.length?t(parseInt(e[3]+e[3],16)/255,2):1}:{r:parseInt(e.substring(0,2),16),g:parseInt(e.substring(2,4),16),b:parseInt(e.substring(4,6),16),a:8===e.length?t(parseInt(e.substring(6,8),16)/255,2):1}),r=e=>{const{h:n,s:r,l:o}=(({h:e,s:n,v:r,a:o})=>{const i=(200-n)*r/100;return{h:t(e),s:t(i>0&&i<200?n*r/100/(i<=100?i:200-i)*100:0),l:t(i/2),a:t(o,2)}})(e);return`hsl(${n}, ${r}%, ${o}%)`},o=({h:e,s:n,v:r,a:o})=>{e=e/360*6,n/=100,r/=100;const i=Math.floor(e),a=r*(1-n),s=r*(1-(e-i)*n),c=r*(1-(1-e+i)*n),u=i%6;return{r:t(255*[r,s,a,a,c,r][u]),g:t(255*[c,r,r,s,a,a][u]),b:t(255*[a,a,c,r,r,s][u]),a:t(o,2)}},i=e=>{const t=e.toString(16);return t.length<2?"0"+t:t},a=({r:e,g:n,b:r,a:o})=>{const a=o<1?i(t(255*o)):"";return"#"+i(e)+i(n)+i(r)+a},s=({r:e,g:n,b:r,a:o})=>{const i=Math.max(e,n,r),a=i-Math.min(e,n,r),s=a?i===e?(n-r)/a:i===n?2+(r-e)/a:4+(e-n)/a:0;return{h:t(60*(s<0?s+6:s)),s:t(i?a/i*100:0),v:t(i/255*100),a:o}},c=(e,t)=>{if(e===t)return!0;for(const n in e)if(e[n]!==t[n])return!1;return!0},u={},l=e=>{let t=u[e];return t||(t=document.createElement("template"),t.innerHTML=e,u[e]=t),t},d=(e,t,n)=>{e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:n}))};let f=!1;const v=e=>"touches"in e,p=(t,n)=>{const r=v(n)?n.touches[0]:n,o=t.el.getBoundingClientRect();d(t.el,"move",t.getMove({x:e((r.pageX-(o.left+window.pageXOffset))/o.width),y:e((r.pageY-(o.top+window.pageYOffset))/o.height)}))};class h{constructor(e,t,n,r){const o=l(`<div role="slider" tabindex="0" part="${t}" ${n}><div part="${t}-pointer"></div></div>`);e.appendChild(o.content.cloneNode(!0));const i=e.querySelector(`[part=${t}]`);i.addEventListener("mousedown",this),i.addEventListener("touchstart",this),i.addEventListener("keydown",this),this.el=i,this.xy=r,this.nodes=[i.firstChild,i]}set dragging(e){const t=e?document.addEventListener:document.removeEventListener;t(f?"touchmove":"mousemove",this),t(f?"touchend":"mouseup",this)}handleEvent(e){switch(e.type){case"mousedown":case"touchstart":if(e.preventDefault(),!(e=>!(f&&!v(e)||(f||(f=v(e)),0)))(e)||!f&&0!=e.button)return;this.el.focus(),p(this,e),this.dragging=!0;break;case"mousemove":case"touchmove":e.preventDefault(),p(this,e);break;case"mouseup":case"touchend":this.dragging=!1;break;case"keydown":((e,t)=>{const n=t.keyCode;n>40||e.xy&&n<37||n<33||(t.preventDefault(),d(e.el,"move",e.getMove({x:39===n?.01:37===n?-.01:34===n?.05:33===n?-.05:35===n?1:36===n?-1:0,y:40===n?.01:38===n?-.01:0},!0)))})(this,e)}}style(e){e.forEach((e,t)=>{for(const n in e)this.nodes[t].style.setProperty(n,e[n])})}}class m extends h{constructor(e){super(e,"hue",'aria-label="Hue" aria-valuemin="0" aria-valuemax="360"',!1)}update({h:e}){this.h=e,this.style([{left:e/360*100+"%",color:r({h:e,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuenow",`${t(e)}`)}getMove(t,n){return{h:n?e(this.h+360*t.x,0,360):360*t.x}}}class g extends h{constructor(e){super(e,"saturation",'aria-label="Color"',!0)}update(e){this.hsva=e,this.style([{top:100-e.v+"%",left:`${e.s}%`,color:r(e)},{"background-color":r({h:e.h,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuetext",`Saturation ${t(e.s)}%, Brightness ${t(e.v)}%`)}getMove(t,n){return{s:n?e(this.hsva.s+100*t.x,0,100):100*t.x,v:n?e(this.hsva.v-100*t.y,0,100):Math.round(100-100*t.y)}}}const b=Symbol("same"),y=Symbol("color"),x=Symbol("hsva"),w=Symbol("update"),E=Symbol("parts"),L=Symbol("css"),A=Symbol("sliders");class S extends HTMLElement{static get observedAttributes(){return["color"]}get[L](){return[':host{display:flex;flex-direction:column;position:relative;width:200px;height:200px;user-select:none;-webkit-user-select:none;cursor:default}:host([hidden]){display:none!important}[role=slider]{position:relative;touch-action:none;user-select:none;-webkit-user-select:none;outline:0}[role=slider]:last-child{border-radius:0 0 8px 8px}[part$=pointer]{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;display:flex;place-content:center center;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}[part$=pointer]::after{content:"";width:100%;height:100%;border-radius:inherit;background-color:currentColor}[role=slider]:focus [part$=pointer]{transform:translate(-50%,-50%) scale(1.1)}',"[part=hue]{flex:0 0 24px;background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}[part=hue-pointer]{top:50%;z-index:2}","[part=saturation]{flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(to top,#000,transparent),linear-gradient(to right,#fff,rgba(255,255,255,0));box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}[part=saturation-pointer]{z-index:3}"]}get[A](){return[g,m]}get color(){return this[y]}set color(e){if(!this[b](e)){const t=this.colorModel.toHsva(e);this[w](t),this[y]=e}}constructor(){super();const e=l(`<style>${this[L].join("")}</style>`),t=this.attachShadow({mode:"open"});t.appendChild(e.content.cloneNode(!0)),t.addEventListener("move",this),this[E]=this[A].map(e=>new e(t))}connectedCallback(){if(this.hasOwnProperty("color")){const e=this.color;delete this.color,this.color=e}else this.color||(this.color=this.colorModel.defaultColor)}attributeChangedCallback(e,t,n){const r=this.colorModel.fromAttr(n);this[b](r)||(this.color=r)}handleEvent(e){const t=this[x],n={...t,...e.detail};let r;this[w](n),c(n,t)||this[b](r=this.colorModel.fromHsva(n))||(this[y]=r,d(this,"color-changed",{value:r}))}[b](e){return this.color&&this.colorModel.equal(e,this.color)}[w](e){this[x]=e,this[E].forEach(t=>t.update(e))}}const k={defaultColor:"#000",toHsva:e=>s(n(e)),fromHsva:({h:e,s:t,v:n})=>a(o({h:e,s:t,v:n,a:1})),equal:(e,t)=>e.toLowerCase()===t.toLowerCase()||c(n(e),n(t)),fromAttr:e=>e};class _ extends S{get colorModel(){return k}}function q(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return C(e,t);var n={}.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?C(e,t):void 0}}(e))||t){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function C(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function M(e){return M="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},M(e)}customElements.define("hex-color-picker",class extends _{});var $,I=/^[0-9a-f]{3}([0-9a-f]{3})?$/i,P=function(e){if(!e)return"";var t=e.trim();if(!t)return"";var n=t.startsWith("#")?t.slice(1):t;return I.test(n)?"#".concat(n.toLowerCase()):""};function O(){var e=document.querySelector("[data-settings-prefix]"),t=e?e.dataset.settingsPrefix:"";if(t){var n=".".concat(t,"-hex-color-picker"),r=".".concat(t,"-field-type-color"),o=".".concat(t,"-color-input"),i=".".concat(t,"-color-swatch"),a=".".concat(t,"-color-popover"),s=".".concat(t,"-color-control"),c="".concat(t,"ColorInitialized");document.querySelectorAll(r).forEach(function(e){if("true"!==e.dataset[c]){var t=e.querySelector(o),r=e.querySelector(i),u=e.querySelector(a),l=e.querySelector(n),d=e.querySelector(s);if(t&&r&&u&&l&&d){l.addEventListener("mousedown",function(e){return e.stopPropagation()}),e.dataset[c]="true";var f=P(t.value)||"#000000",v=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=n.setInput,i=void 0===o||o,a=n.dispatch,s=void 0===a||a,c=n.setPicker,u=void 0===c||c,d=P(e);return!!d&&(f=d,i&&(t.value=d),r.style.backgroundColor=d,u&&l.color!==d&&(l.color=d),s&&t.dispatchEvent(new Event("input",{bubbles:!0})),!0)},p=function(){e.classList.contains("is-open")||(e.classList.add("is-open"),u.removeAttribute("aria-hidden"),r.setAttribute("aria-expanded","true"),document.addEventListener("mousedown",m,!0),document.addEventListener("keydown",g))},h=function(){e.classList.contains("is-open")&&(e.classList.remove("is-open"),u.setAttribute("aria-hidden","true"),r.setAttribute("aria-expanded","false"),document.removeEventListener("mousedown",m,!0),document.removeEventListener("keydown",g))},m=function(e){var t="function"==typeof e.composedPath?e.composedPath():[];d.contains(e.target)||t.includes(d)||t.includes(u)||t.includes(l)||h()},g=function(e){"Escape"===e.key&&h()};v(f,{setInput:!0,dispatch:!1}),r.addEventListener("click",function(t){t.preventDefault(),e.classList.contains("is-open")?h():p()}),t.addEventListener("click",p),t.addEventListener("input",function(){var e=P(t.value);e&&(t.value!==e&&(t.value=e),f=e,r.style.backgroundColor=e,l.color!==e&&(l.color=e))}),t.addEventListener("blur",function(){v(t.value,{setInput:!0,dispatch:!1})||v(f,{setInput:!0,dispatch:!1})}),l.addEventListener("color-changed",function(e){v(e.detail.value,{setPicker:!1})})}}})}}function T(){document.querySelectorAll('input[type="range"][data-range-output]').forEach(function(e){var t=e.getAttribute("data-range-output"),n=document.getElementById(t);n&&e.addEventListener("input",function(){n.textContent=e.value})})}function H(){var e=document.querySelector("[data-settings-prefix]"),t=e?e.dataset.settingsPrefix:"";if(t){var n="data-".concat(t,"-logic"),r="data-".concat(t,"-fallback-option"),o="".concat(t,"-hidden"),i="".concat(t,"CondDisabled"),a="_".concat(t,"AnimHandler"),s=[],c=new Map;document.querySelectorAll("[".concat(n,"]")).forEach(function(e){var o,i,a=function(e){var t=e.getAttribute(n);if(!t)return[];try{var r=JSON.parse(t);if(Array.isArray(r))return r;if(r&&"object"===M(r))return[r]}catch(o){return[]}return[]}(e);if(a.length){var u=a.map(function(e){return{field:e.field,operator:e.operator,value:e.value,scope:e.scope||"field",action:e.action||"",target:e.target||"",container:e.container||""}}),l=(null===(o=u.find(function(e){return e.container}))||void 0===o?void 0:o.container)||"",d=null;"self"===l?d=e:l&&(d=e.querySelector(l)||document.querySelector(l)),d||(d=e.closest("tr")||e);var f=e.getAttribute("data-".concat(t,"-animation")),v=(null===(i=u.find(function(e){return e.action}))||void 0===i?void 0:i.action)||"show",p=e.getAttribute(r)||"",h={el:e,row:d,logic:u,animation:f,defaultAction:v,fallbackOption:p};s.push(h),u.forEach(function(e){c.has(e.field)||c.set(e.field,[]);var t=c.get(e.field);t.includes(h)||t.push(h)})}});var u=function(e){var t=function(e){var t=document.getElementById(e);if(t)return"checkbox"===t.type||"radio"===t.type?t.checked?t.value:"":t.value;var n=document.querySelectorAll('input[type="radio"][id^="'.concat(e,'-"]'));if(n.length>0){var r,o=q(n);try{for(o.s();!(r=o.n()).done;){var i=r.value;if(i.checked)return i.value}}catch(c){o.e(c)}finally{o.f()}return null}var a=document.querySelectorAll('input[type="checkbox"][id^="'.concat(e,'-"]'));if(a.length>0){var s=[];return a.forEach(function(e){e.checked&&s.push(e.value)}),s}return null}(e.field);if(null===t)return!1;switch(e.operator){case"is":return t===e.value;case"isnot":return t!==e.value;case"contains":return("string"==typeof t||Array.isArray(t))&&t.includes(e.value);case"containsnot":return("string"==typeof t||Array.isArray(t))&&!t.includes(e.value);default:return!1}},l=function(e,n){var r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],i=e.row,s=e.animation,c=i.classList.contains(o);if(n===!c)return Promise.resolve();if(!r||!s||!["fade","slide"].includes(s))return i.classList.toggle(o,!n),Promise.resolve();var u="".concat(t,"-anim-in"),l="".concat(t,"-anim-out"),d="".concat(t,"-anim-").concat(s);return new Promise(function(e){i[a]&&(i.removeEventListener("animationend",i[a]),i[a]=null);var t=function(){i.classList.remove(u,l,d),n?i.classList.remove(o):i.classList.add(o),i.removeEventListener("animationend",t),i[a]=null,e()};i.classList.remove(u,l,d),n?(i.classList.remove(o),i.classList.add(u,d)):i.classList.add(l,d),i[a]=t,i.addEventListener("animationend",t,{once:!0})})},d=function(e,t){e.el.querySelectorAll("input, select, textarea, button").forEach(function(e){t?e.disabled||(e.dataset[i]="true",e.disabled=!0):"true"===e.dataset[i]&&(e.disabled=!1,delete e.dataset[i])})},f=function(e,t,n,r){var a=e.action||r;if(a){var s=function(e,t){if(e.target)return Array.from(document.querySelectorAll(e.target));var n=t.el.querySelectorAll('input[type="radio"]');if(n.length)return Array.from(n).filter(function(t){return t.value===e.value});var r=t.el.querySelectorAll('input[type="checkbox"]');if(r.length)return Array.from(r).filter(function(t){return t.value===e.value});var o=t.el.querySelector("select");return o?Array.from(o.options).filter(function(t){return t.value===e.value}):[]}(e,t);s.length&&s.forEach(function(e){if("disable"===a||"enable"===a)!function(e,t){t?e.disabled||(e.dataset[i]="true",e.disabled=!0):"true"===e.dataset[i]&&(e.disabled=!1,delete e.dataset[i])}(e,"disable"===a?n:!n);else if("show"===a||"hide"===a){var t="show"===a?n:!n;e.classList.toggle(o,!t)}})}},v=function(e){if(e.fallbackOption){var t=e.el.querySelectorAll('input[type="radio"]');if(t.length){var n=Array.from(t).find(function(e){return e.checked});if(!n||n.disabled){var r=Array.from(t).find(function(t){return t.value===e.fallbackOption&&!t.disabled});r||(r=Array.from(t).find(function(e){return!e.disabled})||null),r&&!r.checked&&(r.checked=!0,r.dispatchEvent(new Event("change",{bubbles:!0})))}}else{var o=e.el.querySelector("select");if(o){var i=o.options[o.selectedIndex];if(i&&!i.disabled)return;var a=Array.from(o.options).find(function(t){return t.value===e.fallbackOption&&!t.disabled});a||(a=Array.from(o.options).find(function(e){return!e.disabled})),a&&(o.value=a.value,o.dispatchEvent(new Event("change",{bubbles:!0})))}}}},p=new Set,h=new Map;c.forEach(function(e,t){if(!p.has(t)){h.has(t)||h.set(t,{seq:0});!function(e,t){var n=document.getElementById(e);if(n)return n.addEventListener("change",t),void n.addEventListener("input",t);document.querySelectorAll('input[type="radio"][id^="'.concat(e,'-"]')).forEach(function(e){e.addEventListener("change",t)}),document.querySelectorAll('input[type="checkbox"][id^="'.concat(e,'-"]')).forEach(function(e){e.addEventListener("change",t)})}(t,function(){var n=h.get(t),r=++n.seq,i=[],a=[];if(e.forEach(function(e){var t,n=e.logic.filter(function(e){return"option"===e.scope}),r=e.logic.filter(function(e){return"option"!==e.scope});if(n.forEach(function(t){var n=u(t);f(t,e,n,e.defaultAction)}),v(e),r.length){var s=(null===(t=r.find(function(e){return e.action}))||void 0===t?void 0:t.action)||e.defaultAction,c=r.every(function(e){return u(e)});if("enable"!==s&&"disable"!==s){var l=e.row.classList.contains(o),p="show"===s?c:!c;p&&l?a.push(e):p||l||i.push(e)}else{d(e,"disable"===s?c:!c)}}}),i.length>0){var s=i.map(function(e){return l(e,!1,!0)});Promise.all(s).then(function(){n.seq===r&&a.forEach(function(e){return l(e,!0,!0)})})}else a.forEach(function(e){return l(e,!0,!0)})}),p.add(t)}}),s.forEach(function(e){var t,n=e.logic.filter(function(e){return"option"===e.scope}),r=e.logic.filter(function(e){return"option"!==e.scope});if(n.forEach(function(t){var n=u(t);f(t,e,n,e.defaultAction)}),v(e),r.length){var o=(null===(t=r.find(function(e){return e.action}))||void 0===t?void 0:t.action)||e.defaultAction,i=r.every(function(e){return u(e)});if("enable"!==o&&"disable"!==o)l(e,"show"===o?i:!i,!1);else d(e,"disable"===o?i:!i)}})}}function j(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"slide",r=document.querySelector("[data-settings-prefix]"),o=r?r.dataset.settingsPrefix:"";if(!o)return e.style.display=t?"":"none",Promise.resolve();var i="".concat(o,"-hidden"),a="".concat(o,"-anim-in"),s="".concat(o,"-anim-out"),c="".concat(o,"-anim-").concat(n),u="_".concat(o,"AnimHandler");return new Promise(function(n){e[u]&&(e.removeEventListener("animationend",e[u]),e[u]=null);var r=function(){e.classList.remove(a,s,c),t?e.classList.remove(i):e.classList.add(i),e.removeEventListener("animationend",r),e[u]=null,n()};e.classList.remove(a,s,c),t?(e.classList.remove(i),e.classList.add(a,c)):e.classList.add(s,c),e[u]=r,e.addEventListener("animationend",r,{once:!0})})}function D(e){return"sent"===e?"success":"not_enough_data"===e?"warning":"error"}function N(e,t){var n,r,o=document.getElementById("pvc-email-test-notice"),i="notice notice-error inline";o&&(t?("success"===e?i="notice notice-success inline":"warning"===e&&(i="notice notice-warning inline"),o.innerHTML='<div class="'.concat(i,'"><p>').concat((n=t,(r=document.createElement("div")).textContent=null==n?"":String(n),r.innerHTML),"</p></div>")):o.innerHTML="")}document.addEventListener("DOMContentLoaded",function(){O(),T(),H()}),document.addEventListener("wpAjaxContentLoaded",function(){O(),T(),H()}),($=jQuery)(function(){var e=$("#pvc-general-exclude-ips-setting"),t=window.pvcArgsSettings||{},n=t.testEmail||{},r=e.find(".pvc-ip-box").length,o=function(){e.find(".pvc-remove-exclude-ip").each(function(e,t){0===e?t.classList.add("pvc-hidden"):r>1?t.classList.remove("pvc-hidden"):t.classList.add("pvc-hidden")})};o(),$(document).on("click",".reset_pvc_settings",function(){var e=confirm(t.resetToDefaults);return e&&$(this).hasClass("reset_post_views_counter_settings_other")&&$('input[data-pvc-menu="submenu"]').after($('input[data-pvc-menu="topmenu"]')),e}),$(document).on("click",'input[name="post_views_counter_reset_views"]',function(){return confirm(t.resetViews)}),$(document).on("click",'input[name="post_views_counter_import_views"]',function(){return confirm(t.importViews)}),$(document).on("click",".pvc-remove-exclude-ip",function(e){e.preventDefault();var t=$(this).closest(".pvc-ip-box")[0];j(t,!1).then(function(){return t.remove(),r--,o(),null}).catch(function(){return null})});var i=/^(25[0-5]|2[0-4]\d|1?\d?\d)(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/,a=function(e){if(!e)return!1;var t=e.trim();return function(e){var t=-1!==e.indexOf("*")?e.replace(/\*/g,"0"):e;return i.test(t)}(t)||function(e){if(-1===e.indexOf(":")||-1!==e.indexOf("*"))return!1;try{return""!==new URL("http://[".concat(e,"]")).hostname}catch(t){return!1}}(t)};$(document).on("click",".pvc-add-exclude-ip",function(){var e=$(this).parents("#pvc-general-exclude-ips-setting"),t=e.find('.pvc-ip-box:last input[type="text"]'),n=t.val().trim();if(a(n)){r++;var i=e.find(".pvc-ip-box:last").clone();i.find("input").val(""),i[0].classList.add("pvc-hidden"),e.find(".pvc-ip-box:last").after(i),j(i[0],!0),o()}else t.focus()}),$(document).on("click",".pvc-add-current-ip",function(){$(this).parents("#pvc-general-exclude-ips-setting").find(".pvc-ip-box").last().find("input").val($(this).attr("data-rel"))}),$('input[name="post_views_counter_settings_display[menu_position]"], input[name="post_views_counter_settings_other[menu_position]"]').on("change",function(){"top"===$(this).val()?$('input[data-pvc-menu="submenu"]').after($('input[data-pvc-menu="topmenu"]')):$('input[data-pvc-menu="submenu"]').before($('input[data-pvc-menu="topmenu"]'))}),$(document).on("click",".pvc-send-test-email",function(e){e.preventDefault();var t=e.currentTarget,r=t.closest(".pvc-email-test-scaffold"),o=r?r.querySelector("#pvc-test-email-recipient"):null,i=t.dataset.pvcEmailTestNonce||"",a=t.dataset.defaultLabel||t.textContent.trim(),s=t.dataset.sendingLabel||a,c=n.unexpectedError||"The test email request could not be completed. Please try again.",u=n.invalidResponse||"The test email request did not return a valid response.";o&&"string"==typeof ajaxurl&&""!==ajaxurl?(t.disabled=!0,t.textContent=s,N("success",""),$.ajax({url:ajaxurl,method:"POST",dataType:"json",data:{action:"pvc_send_test_email",nonce:i,pvc_test_email_recipient:o.value}}).done(function(e){e&&void 0!==e.success&&e.data&&e.data.message?N(D(e.success?"sent":e.data.code),e.data.message):N("error",u)}).fail(function(e){var t=e.responseJSON&&e.responseJSON.data?e.responseJSON.data:null,n=t&&t.code?t.code:"",r=t&&t.message?t.message:c;N(D(n),r)}).always(function(){t.disabled=!1,t.textContent=a})):N("error",c)})});
     2const e=(e,t=0,n=1)=>e>n?n:e<t?t:e,t=(e,t=0,n=Math.pow(10,t))=>Math.round(n*e)/n,n=e=>("#"===e[0]&&(e=e.substring(1)),e.length<6?{r:parseInt(e[0]+e[0],16),g:parseInt(e[1]+e[1],16),b:parseInt(e[2]+e[2],16),a:4===e.length?t(parseInt(e[3]+e[3],16)/255,2):1}:{r:parseInt(e.substring(0,2),16),g:parseInt(e.substring(2,4),16),b:parseInt(e.substring(4,6),16),a:8===e.length?t(parseInt(e.substring(6,8),16)/255,2):1}),r=e=>{const{h:n,s:r,l:o}=(({h:e,s:n,v:r,a:o})=>{const i=(200-n)*r/100;return{h:t(e),s:t(i>0&&i<200?n*r/100/(i<=100?i:200-i)*100:0),l:t(i/2),a:t(o,2)}})(e);return`hsl(${n}, ${r}%, ${o}%)`},o=({h:e,s:n,v:r,a:o})=>{e=e/360*6,n/=100,r/=100;const i=Math.floor(e),a=r*(1-n),s=r*(1-(e-i)*n),c=r*(1-(1-e+i)*n),l=i%6;return{r:t(255*[r,s,a,a,c,r][l]),g:t(255*[c,r,r,s,a,a][l]),b:t(255*[a,a,c,r,r,s][l]),a:t(o,2)}},i=e=>{const t=e.toString(16);return t.length<2?"0"+t:t},a=({r:e,g:n,b:r,a:o})=>{const a=o<1?i(t(255*o)):"";return"#"+i(e)+i(n)+i(r)+a},s=({r:e,g:n,b:r,a:o})=>{const i=Math.max(e,n,r),a=i-Math.min(e,n,r),s=a?i===e?(n-r)/a:i===n?2+(r-e)/a:4+(e-n)/a:0;return{h:t(60*(s<0?s+6:s)),s:t(i?a/i*100:0),v:t(i/255*100),a:o}},c=(e,t)=>{if(e===t)return!0;for(const n in e)if(e[n]!==t[n])return!1;return!0},l={},u=e=>{let t=l[e];return t||(t=document.createElement("template"),t.innerHTML=e,l[e]=t),t},d=(e,t,n)=>{e.dispatchEvent(new CustomEvent(t,{bubbles:!0,detail:n}))};let f=!1;const v=e=>"touches"in e,p=(t,n)=>{const r=v(n)?n.touches[0]:n,o=t.el.getBoundingClientRect();d(t.el,"move",t.getMove({x:e((r.pageX-(o.left+window.pageXOffset))/o.width),y:e((r.pageY-(o.top+window.pageYOffset))/o.height)}))};class h{constructor(e,t,n,r){const o=u(`<div role="slider" tabindex="0" part="${t}" ${n}><div part="${t}-pointer"></div></div>`);e.appendChild(o.content.cloneNode(!0));const i=e.querySelector(`[part=${t}]`);i.addEventListener("mousedown",this),i.addEventListener("touchstart",this),i.addEventListener("keydown",this),this.el=i,this.xy=r,this.nodes=[i.firstChild,i]}set dragging(e){const t=e?document.addEventListener:document.removeEventListener;t(f?"touchmove":"mousemove",this),t(f?"touchend":"mouseup",this)}handleEvent(e){switch(e.type){case"mousedown":case"touchstart":if(e.preventDefault(),!(e=>!(f&&!v(e)||(f||(f=v(e)),0)))(e)||!f&&0!=e.button)return;this.el.focus(),p(this,e),this.dragging=!0;break;case"mousemove":case"touchmove":e.preventDefault(),p(this,e);break;case"mouseup":case"touchend":this.dragging=!1;break;case"keydown":((e,t)=>{const n=t.keyCode;n>40||e.xy&&n<37||n<33||(t.preventDefault(),d(e.el,"move",e.getMove({x:39===n?.01:37===n?-.01:34===n?.05:33===n?-.05:35===n?1:36===n?-1:0,y:40===n?.01:38===n?-.01:0},!0)))})(this,e)}}style(e){e.forEach((e,t)=>{for(const n in e)this.nodes[t].style.setProperty(n,e[n])})}}class m extends h{constructor(e){super(e,"hue",'aria-label="Hue" aria-valuemin="0" aria-valuemax="360"',!1)}update({h:e}){this.h=e,this.style([{left:e/360*100+"%",color:r({h:e,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuenow",`${t(e)}`)}getMove(t,n){return{h:n?e(this.h+360*t.x,0,360):360*t.x}}}class g extends h{constructor(e){super(e,"saturation",'aria-label="Color"',!0)}update(e){this.hsva=e,this.style([{top:100-e.v+"%",left:`${e.s}%`,color:r(e)},{"background-color":r({h:e.h,s:100,v:100,a:1})}]),this.el.setAttribute("aria-valuetext",`Saturation ${t(e.s)}%, Brightness ${t(e.v)}%`)}getMove(t,n){return{s:n?e(this.hsva.s+100*t.x,0,100):100*t.x,v:n?e(this.hsva.v-100*t.y,0,100):Math.round(100-100*t.y)}}}const b=Symbol("same"),y=Symbol("color"),x=Symbol("hsva"),w=Symbol("update"),E=Symbol("parts"),L=Symbol("css"),A=Symbol("sliders");class S extends HTMLElement{static get observedAttributes(){return["color"]}get[L](){return[':host{display:flex;flex-direction:column;position:relative;width:200px;height:200px;user-select:none;-webkit-user-select:none;cursor:default}:host([hidden]){display:none!important}[role=slider]{position:relative;touch-action:none;user-select:none;-webkit-user-select:none;outline:0}[role=slider]:last-child{border-radius:0 0 8px 8px}[part$=pointer]{position:absolute;z-index:1;box-sizing:border-box;width:28px;height:28px;display:flex;place-content:center center;transform:translate(-50%,-50%);background-color:#fff;border:2px solid #fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2)}[part$=pointer]::after{content:"";width:100%;height:100%;border-radius:inherit;background-color:currentColor}[role=slider]:focus [part$=pointer]{transform:translate(-50%,-50%) scale(1.1)}',"[part=hue]{flex:0 0 24px;background:linear-gradient(to right,red 0,#ff0 17%,#0f0 33%,#0ff 50%,#00f 67%,#f0f 83%,red 100%)}[part=hue-pointer]{top:50%;z-index:2}","[part=saturation]{flex-grow:1;border-color:transparent;border-bottom:12px solid #000;border-radius:8px 8px 0 0;background-image:linear-gradient(to top,#000,transparent),linear-gradient(to right,#fff,rgba(255,255,255,0));box-shadow:inset 0 0 0 1px rgba(0,0,0,.05)}[part=saturation-pointer]{z-index:3}"]}get[A](){return[g,m]}get color(){return this[y]}set color(e){if(!this[b](e)){const t=this.colorModel.toHsva(e);this[w](t),this[y]=e}}constructor(){super();const e=u(`<style>${this[L].join("")}</style>`),t=this.attachShadow({mode:"open"});t.appendChild(e.content.cloneNode(!0)),t.addEventListener("move",this),this[E]=this[A].map(e=>new e(t))}connectedCallback(){if(this.hasOwnProperty("color")){const e=this.color;delete this.color,this.color=e}else this.color||(this.color=this.colorModel.defaultColor)}attributeChangedCallback(e,t,n){const r=this.colorModel.fromAttr(n);this[b](r)||(this.color=r)}handleEvent(e){const t=this[x],n={...t,...e.detail};let r;this[w](n),c(n,t)||this[b](r=this.colorModel.fromHsva(n))||(this[y]=r,d(this,"color-changed",{value:r}))}[b](e){return this.color&&this.colorModel.equal(e,this.color)}[w](e){this[x]=e,this[E].forEach(t=>t.update(e))}}const k={defaultColor:"#000",toHsva:e=>s(n(e)),fromHsva:({h:e,s:t,v:n})=>a(o({h:e,s:t,v:n,a:1})),equal:(e,t)=>e.toLowerCase()===t.toLowerCase()||c(n(e),n(t)),fromAttr:e=>e};class _ extends S{get colorModel(){return k}}function q(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!n){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return C(e,t);var n={}.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?C(e,t):void 0}}(e))||t){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){s=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(s)throw i}}}}function C(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function M(e){return M="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},M(e)}customElements.define("hex-color-picker",class extends _{});var $,I=/^[0-9a-f]{3}([0-9a-f]{3})?$/i,O=function(e){if(!e)return"";var t=e.trim();if(!t)return"";var n=t.startsWith("#")?t.slice(1):t;return I.test(n)?"#".concat(n.toLowerCase()):""};function P(){var e=document.querySelector("[data-settings-prefix]"),t=e?e.dataset.settingsPrefix:"";if(t){var n=".".concat(t,"-hex-color-picker"),r=".".concat(t,"-field-type-color"),o=".".concat(t,"-color-input"),i=".".concat(t,"-color-swatch"),a=".".concat(t,"-color-popover"),s=".".concat(t,"-color-control"),c="".concat(t,"ColorInitialized");document.querySelectorAll(r).forEach(function(e){if("true"!==e.dataset[c]){var t=e.querySelector(o),r=e.querySelector(i),l=e.querySelector(a),u=e.querySelector(n),d=e.querySelector(s);if(t&&r&&l&&u&&d){u.addEventListener("mousedown",function(e){return e.stopPropagation()}),e.dataset[c]="true";var f=O(t.value)||"#000000",v=function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=n.setInput,i=void 0===o||o,a=n.dispatch,s=void 0===a||a,c=n.setPicker,l=void 0===c||c,d=O(e);return!!d&&(f=d,i&&(t.value=d),r.style.backgroundColor=d,l&&u.color!==d&&(u.color=d),s&&t.dispatchEvent(new Event("input",{bubbles:!0})),!0)},p=function(){e.classList.contains("is-open")||(e.classList.add("is-open"),l.removeAttribute("aria-hidden"),r.setAttribute("aria-expanded","true"),document.addEventListener("mousedown",m,!0),document.addEventListener("keydown",g))},h=function(){e.classList.contains("is-open")&&(e.classList.remove("is-open"),l.setAttribute("aria-hidden","true"),r.setAttribute("aria-expanded","false"),document.removeEventListener("mousedown",m,!0),document.removeEventListener("keydown",g))},m=function(e){var t="function"==typeof e.composedPath?e.composedPath():[];d.contains(e.target)||t.includes(d)||t.includes(l)||t.includes(u)||h()},g=function(e){"Escape"===e.key&&h()};v(f,{setInput:!0,dispatch:!1}),r.addEventListener("click",function(t){t.preventDefault(),e.classList.contains("is-open")?h():p()}),t.addEventListener("click",p),t.addEventListener("input",function(){var e=O(t.value);e&&(t.value!==e&&(t.value=e),f=e,r.style.backgroundColor=e,u.color!==e&&(u.color=e))}),t.addEventListener("blur",function(){v(t.value,{setInput:!0,dispatch:!1})||v(f,{setInput:!0,dispatch:!1})}),u.addEventListener("color-changed",function(e){v(e.detail.value,{setPicker:!1})})}}})}}function T(){document.querySelectorAll('input[type="range"][data-range-output]').forEach(function(e){var t=e.getAttribute("data-range-output"),n=document.getElementById(t);n&&e.addEventListener("input",function(){n.textContent=e.value})})}function H(){var e=document.querySelector("[data-settings-prefix]"),t=e?e.dataset.settingsPrefix:"";if(t){var n=!1,r="data-".concat(t,"-logic"),o="data-".concat(t,"-fallback-option"),i="".concat(t,"-hidden"),a="".concat(t,"CondDisabled"),s="_".concat(t,"AnimHandler"),c=[],l=new Map;document.querySelectorAll("[".concat(r,"]")).forEach(function(e){var n,i,a=function(e){var t=e.getAttribute(r);if(!t)return[];try{var n=JSON.parse(t);if(Array.isArray(n))return n;if(n&&"object"===M(n))return[n]}catch(o){return[]}return[]}(e);if(a.length){var s=a.map(function(e){return{field:e.field,operator:e.operator,value:e.value,scope:e.scope||"field",action:e.action||"",target:e.target||"",container:e.container||"",clearOnDisable:!0===e.clear_on_disable}}),u=(null===(n=s.find(function(e){return e.container}))||void 0===n?void 0:n.container)||"",d=null;"self"===u?d=e:u&&(d=e.querySelector(u)||document.querySelector(u)),d||(d=e.closest("tr")||e);var f=e.getAttribute("data-".concat(t,"-animation")),v=(null===(i=s.find(function(e){return e.action}))||void 0===i?void 0:i.action)||"show",p=e.getAttribute(o)||"",h={el:e,row:d,logic:s,animation:f,defaultAction:v,fallbackOption:p};c.push(h),s.forEach(function(e){l.has(e.field)||l.set(e.field,[]);var t=l.get(e.field);t.includes(h)||t.push(h)})}});var u=function(e){var t=function(e){var t=document.getElementById(e);if(t)return"checkbox"===t.type||"radio"===t.type?t.checked?t.value:"":t.value;var n=document.querySelectorAll('input[type="radio"][id^="'.concat(e,'-"]'));if(n.length>0){var r,o=q(n);try{for(o.s();!(r=o.n()).done;){var i=r.value;if(i.checked)return i.value}}catch(c){o.e(c)}finally{o.f()}return null}var a=document.querySelectorAll('input[type="checkbox"][id^="'.concat(e,'-"]'));if(a.length>0){var s=[];return a.forEach(function(e){e.checked&&s.push(e.value)}),s}return null}(e.field);if(null===t)return!1;switch(e.operator){case"is":return t===e.value;case"isnot":return t!==e.value;case"contains":return("string"==typeof t||Array.isArray(t))&&t.includes(e.value);case"containsnot":return("string"==typeof t||Array.isArray(t))&&!t.includes(e.value);default:return!1}},d=function(e,r){var o=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],a=e.row,c=e.animation,l=a.classList.contains(i);if(r===!l)return Promise.resolve();if(n||!o||!c||!["fade","slide"].includes(c))return a.classList.toggle(i,!r),Promise.resolve();var u="".concat(t,"-anim-in"),d="".concat(t,"-anim-out"),f="".concat(t,"-anim-").concat(c);return new Promise(function(e){a[s]&&(a.removeEventListener("animationend",a[s]),a[s]=null);var t=function(){a.classList.remove(u,d,f),r?a.classList.remove(i):a.classList.add(i),a.removeEventListener("animationend",t),a[s]=null,e()};a.classList.remove(u,d,f),r?(a.classList.remove(i),a.classList.add(u,f)):a.classList.add(d,f),a[s]=t,a.addEventListener("animationend",t,{once:!0})})},f=function(e,t){e.el.querySelectorAll("input, select, textarea, button").forEach(function(e){t?e.disabled||(e.dataset[a]="true",e.disabled=!0):"true"===e.dataset[a]&&(e.disabled=!1,delete e.dataset[a])})},v=function(e,t,n,r){var o=e.action||r;if(o){var s=function(e,t){if(e.target)return Array.from(document.querySelectorAll(e.target));var n=t.el.querySelectorAll('input[type="radio"]');if(n.length)return Array.from(n).filter(function(t){return t.value===e.value});var r=t.el.querySelectorAll('input[type="checkbox"]');if(r.length)return Array.from(r).filter(function(t){return t.value===e.value});var o=t.el.querySelector("select");return o?Array.from(o.options).filter(function(t){return t.value===e.value}):[]}(e,t);s.length&&s.forEach(function(t){if("disable"===o||"enable"===o)!function(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(t){if(!e.disabled){var r=!!n&&function(e){return!("checkbox"!==e.type&&"radio"!==e.type||!e.checked||(e.checked=!1,0))}(e);e.dataset[a]="true",e.disabled=!0,r&&e.dispatchEvent(new Event("change",{bubbles:!0}))}}else"true"===e.dataset[a]&&(e.disabled=!1,delete e.dataset[a])}(t,"disable"===o?n:!n,e.clearOnDisable);else if("show"===o||"hide"===o){var r="show"===o?n:!n;t.classList.toggle(i,!r)}})}},p=function(e){if(e.fallbackOption){var t=e.el.querySelectorAll('input[type="radio"]');if(t.length){var n=Array.from(t).find(function(e){return e.checked});if(!n||n.disabled){var r=Array.from(t).find(function(t){return t.value===e.fallbackOption&&!t.disabled});r||(r=Array.from(t).find(function(e){return!e.disabled})||null),r&&!r.checked&&(r.checked=!0,r.dispatchEvent(new Event("change",{bubbles:!0})))}}else{var o=e.el.querySelector("select");if(o){var i=o.options[o.selectedIndex];if(i&&!i.disabled)return;var a=Array.from(o.options).find(function(t){return t.value===e.fallbackOption&&!t.disabled});a||(a=Array.from(o.options).find(function(e){return!e.disabled})),a&&(o.value=a.value,o.dispatchEvent(new Event("change",{bubbles:!0})))}}}},h=new Set,m=new Map;l.forEach(function(e,t){if(!h.has(t)){m.has(t)||m.set(t,{seq:0});!function(e,t){var n=document.getElementById(e);if(n)return n.addEventListener("change",t),void n.addEventListener("input",t);document.querySelectorAll('input[type="radio"][id^="'.concat(e,'-"]')).forEach(function(e){e.addEventListener("change",t)}),document.querySelectorAll('input[type="checkbox"][id^="'.concat(e,'-"]')).forEach(function(e){e.addEventListener("change",t)})}(t,function(){var n=m.get(t);if(!n.running){n.running=!0;try{!function(t){var n=++t.seq,r=[],o=[];if(e.forEach(function(e){var t,n=e.logic.filter(function(e){return"option"===e.scope}),a=e.logic.filter(function(e){return"option"!==e.scope});if(n.forEach(function(t){var n=u(t);v(t,e,n,e.defaultAction)}),p(e),a.length){var s=(null===(t=a.find(function(e){return e.action}))||void 0===t?void 0:t.action)||e.defaultAction,c=a.every(function(e){return u(e)});if("enable"!==s&&"disable"!==s){var l=e.row.classList.contains(i),d="show"===s?c:!c;d&&l?o.push(e):d||l||r.push(e)}else f(e,"disable"===s?c:!c)}}),r.length>0){var a=r.map(function(e){return d(e,!1,!0)});Promise.all(a).then(function(){t.seq===n&&o.forEach(function(e){return d(e,!0,!0)})})}else o.forEach(function(e){return d(e,!0,!0)})}(n)}finally{n.running=!1}}}),h.add(t)}}),n=!0,c.forEach(function(e){var t,n=e.logic.filter(function(e){return"option"===e.scope}),r=e.logic.filter(function(e){return"option"!==e.scope});if(n.forEach(function(t){var n=u(t);v(t,e,n,e.defaultAction)}),p(e),r.length){var o=(null===(t=r.find(function(e){return e.action}))||void 0===t?void 0:t.action)||e.defaultAction,i=r.every(function(e){return u(e)});if("enable"!==o&&"disable"!==o)d(e,"show"===o?i:!i,!1);else f(e,"disable"===o?i:!i)}}),n=!1}}function j(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"slide",r=document.querySelector("[data-settings-prefix]"),o=r?r.dataset.settingsPrefix:"";if(!o)return e.style.display=t?"":"none",Promise.resolve();var i="".concat(o,"-hidden"),a="".concat(o,"-anim-in"),s="".concat(o,"-anim-out"),c="".concat(o,"-anim-").concat(n),l="_".concat(o,"AnimHandler");return new Promise(function(n){e[l]&&(e.removeEventListener("animationend",e[l]),e[l]=null);var r=function(){e.classList.remove(a,s,c),t?e.classList.remove(i):e.classList.add(i),e.removeEventListener("animationend",r),e[l]=null,n()};e.classList.remove(a,s,c),t?(e.classList.remove(i),e.classList.add(a,c)):e.classList.add(s,c),e[l]=r,e.addEventListener("animationend",r,{once:!0})})}function D(e){return"sent"===e?"success":"not_enough_data"===e?"warning":"error"}function N(e,t){var n,r,o=document.getElementById("pvc-email-test-notice"),i="notice notice-error inline";o&&(t?("success"===e?i="notice notice-success inline":"warning"===e&&(i="notice notice-warning inline"),o.innerHTML='<div class="'.concat(i,'"><p>').concat((n=t,(r=document.createElement("div")).textContent=null==n?"":String(n),r.innerHTML),"</p></div>")):o.innerHTML="")}document.addEventListener("DOMContentLoaded",function(){P(),T(),H()}),document.addEventListener("wpAjaxContentLoaded",function(){P(),T(),H()}),($=jQuery)(function(){var e=$("#pvc-general-exclude-ips-setting"),t=window.pvcArgsSettings||{},n=t.testEmail||{},r=e.find(".pvc-ip-box").length,o=function(){e.find(".pvc-remove-exclude-ip").each(function(e,t){0===e?t.classList.add("pvc-hidden"):r>1?t.classList.remove("pvc-hidden"):t.classList.add("pvc-hidden")})};o(),$(document).on("click",".reset_pvc_settings",function(){var e=confirm(t.resetToDefaults);return e&&$(this).hasClass("reset_post_views_counter_settings_other")&&$('input[data-pvc-menu="submenu"]').after($('input[data-pvc-menu="topmenu"]')),e}),$(document).on("click",'input[name="post_views_counter_reset_views"]',function(){return confirm(t.resetViews)}),$(document).on("click",'input[name="post_views_counter_import_views"]',function(){return confirm(t.importViews)}),$(document).on("click",".pvc-remove-exclude-ip",function(e){e.preventDefault();var t=$(this).closest(".pvc-ip-box")[0];j(t,!1).then(function(){return t.remove(),r--,o(),null}).catch(function(){return null})});var i=/^(25[0-5]|2[0-4]\d|1?\d?\d)(\.(25[0-5]|2[0-4]\d|1?\d?\d)){3}$/,a=function(e){if(!e)return!1;var t=e.trim();return function(e){var t=-1!==e.indexOf("*")?e.replace(/\*/g,"0"):e;return i.test(t)}(t)||function(e){if(-1===e.indexOf(":")||-1!==e.indexOf("*"))return!1;try{return""!==new URL("http://[".concat(e,"]")).hostname}catch(t){return!1}}(t)};$(document).on("click",".pvc-add-exclude-ip",function(){var e=$(this).parents("#pvc-general-exclude-ips-setting"),t=e.find('.pvc-ip-box:last input[type="text"]'),n=t.val().trim();if(a(n)){r++;var i=e.find(".pvc-ip-box:last").clone();i.find("input").val(""),i[0].classList.add("pvc-hidden"),e.find(".pvc-ip-box:last").after(i),j(i[0],!0),o()}else t.focus()}),$(document).on("click",".pvc-add-current-ip",function(){$(this).parents("#pvc-general-exclude-ips-setting").find(".pvc-ip-box").last().find("input").val($(this).attr("data-rel"))}),$('input[name="post_views_counter_settings_display[menu_position]"], input[name="post_views_counter_settings_other[menu_position]"]').on("change",function(){"top"===$(this).val()?$('input[data-pvc-menu="submenu"]').after($('input[data-pvc-menu="topmenu"]')):$('input[data-pvc-menu="submenu"]').before($('input[data-pvc-menu="topmenu"]'))}),$(document).on("click",".pvc-send-test-email",function(e){e.preventDefault();var t=e.currentTarget,r=t.closest(".pvc-email-test-scaffold"),o=r?r.querySelector("#pvc-test-email-recipient"):null,i=t.dataset.pvcEmailTestNonce||"",a=t.dataset.defaultLabel||t.textContent.trim(),s=t.dataset.sendingLabel||a,c=n.unexpectedError||"The test email request could not be completed. Please try again.",l=n.invalidResponse||"The test email request did not return a valid response.";o&&"string"==typeof ajaxurl&&""!==ajaxurl?(t.disabled=!0,t.textContent=s,N("success",""),$.ajax({url:ajaxurl,method:"POST",dataType:"json",data:{action:"pvc_send_test_email",nonce:i,pvc_test_email_recipient:o.value}}).done(function(e){e&&void 0!==e.success&&e.data&&e.data.message?N(D(e.success?"sent":e.data.code),e.data.message):N("error",l)}).fail(function(e){var t=e.responseJSON&&e.responseJSON.data?e.responseJSON.data:null,n=t&&t.code?t.code:"",r=t&&t.message?t.message:c;N(D(n),r)}).always(function(){t.disabled=!1,t.textContent=a})):N("error",c)})});
    33
    44})(jQuery, window, document);
  • post-views-counter/tags/1.8.0/js/block-editor.js

    r3647380 r3734102  
    11(function(){
    2 function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(e,t){for(var i=0;i<t.length;i++){var o=t[i];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,n(o.key),o)}}function n(t){var n=function(t,n){if("object"!=e(t)||!t)return t;var i=t[Symbol.toPrimitive];if(void 0!==i){var o=i.call(t,n);if("object"!=e(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(t)}(t,"string");return"symbol"==e(n)?n:n+""}function i(t,n,i){return n=s(n),function(t,n){if(n&&("object"==e(n)||"function"==typeof n))return n;if(void 0!==n)throw new TypeError("Derived constructors may only return object or undefined");return function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(t)}(t,o()?Reflect.construct(n,i||[],s(t).constructor):n.apply(t,i))}function o(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(o=function(){return!!e})()}function s(e){return s=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)},s(e)}function r(e,t){return r=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e},r(e,t)}var l=wp.element,a=l.Fragment,c=l.Component,p=wp.data.withSelect,u=wp.plugins.registerPlugin,d=wp.components,w=d.TextControl,m=d.Button,v=d.Popover,f=wp.editor.PluginPostStatusInfo,h=function(){function e(){var t;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),(t=i(this,e,arguments)).state={postViews:pvcEditorArgs.postViews,isVisible:!1},t.handleClick=t.handleClick.bind(t),t.handleClickOutside=t.handleClickOutside.bind(t),t.handleCancel=t.handleCancel.bind(t),t.handleSetViews=t.handleSetViews.bind(t),t}return function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),Object.defineProperty(e,"prototype",{writable:!1}),t&&r(e,t)}(e,c),n=e,o=[{key:"handleClick",value:function(e){e.target.classList.contains("edit-post-post-views-toggle-link")&&this.setState(function(e){return{isVisible:!e.isVisible}})}},{key:"handleClickOutside",value:function(e){e.target.classList.contains("edit-post-post-views-toggle-link")||this.setState(function(e){return{isVisible:!e.isVisible}})}},{key:"handleCancel",value:function(e){this.setState(function(e){return{postViews:pvcEditorArgs.postViews,isVisible:!e.isVisible}})}},{key:"handleSetViews",value:function(e){wp.data.dispatch("core/editor").editPost({meta:{_pvc_post_views:e}}),this.setState(function(){return{postViews:e}})}},{key:"render",value:function(){return wp.element.createElement(b,{postViews:this.state.postViews,isVisible:this.state.isVisible,handleClick:this.handleClick,handleClickOutside:this.handleClickOutside,handleCancel:this.handleCancel,handleSetViews:this.handleSetViews})}}],s=[{key:"getDerivedStateFromProps",value:function(e,t){!e.isPublishing&&!e.isSaving||e.isAutoSaving||wp.apiRequest({path:"/post-views-counter/update-post-views/?id=".concat(e.postId),method:"POST",data:{post_views:t.postViews}}).then(function(e){return e},function(e){return e})}}],o&&t(n.prototype,o),s&&t(n,s),Object.defineProperty(n,"prototype",{writable:!1}),n;var n,o,s}(),b=function(e){return wp.element.createElement(a,null,wp.element.createElement(f,{className:"edit-post-post-views"},wp.element.createElement("div",{className:"editor-post-panel__row-label"},wp.element.createElement("span",null,pvcEditorArgs.textPostViews)),!pvcEditorArgs.canEdit&&wp.element.createElement("div",{className:"editor-post-panel__row-control"},wp.element.createElement("div",{className:"components-dropdown edit-post-post-views-popover-wrapper"},wp.element.createElement(m,{size:"compact",variant:"tertiary",disabled:!0,className:"components-button edit-post-post-views-toggle-link"},e.postViews))),pvcEditorArgs.canEdit&&wp.element.createElement("div",{className:"editor-post-panel__row-control"},wp.element.createElement("div",{className:"components-dropdown edit-post-post-views-popover-wrapper"},wp.element.createElement(m,{size:"compact",variant:"tertiary",className:"components-button edit-post-post-views-toggle-link",onClick:e.handleClick},e.postViews,e.isVisible&&(pvcEditorArgs.wpGreater53?wp.element.createElement(v,{position:"bottom right",className:"edit-post-post-views-popover",onFocusOutside:e.handleClickOutside},wp.element.createElement("legend",null,pvcEditorArgs.textPostViews),wp.element.createElement(w,{className:"edit-post-post-views-input",type:"number",key:"post_views",value:e.postViews,onChange:e.handleSetViews}),wp.element.createElement("p",{className:"description"},pvcEditorArgs.textHelp),wp.element.createElement(m,{variant:"link",className:"edit-post-post-views-cancel-link",onClick:e.handleCancel},pvcEditorArgs.textCancel)):wp.element.createElement(v,{position:"bottom right",className:"edit-post-post-views-popover",onClickOutside:e.handleClickOutside},wp.element.createElement("legend",null,pvcEditorArgs.textPostViews),wp.element.createElement(w,{className:"edit-post-post-views-input",type:"number",key:"post_views",value:e.postViews,onChange:e.handleSetViews}),wp.element.createElement("p",{className:"description"},pvcEditorArgs.textHelp),wp.element.createElement(m,{variant:"link",className:"edit-post-post-views-cancel-link",onClick:e.handleCancel},pvcEditorArgs.textCancel))))))))};u("post-views-counter",{icon:"",render:p(function(e,t){var n=t.forceIsSaving,i=e("core/editor"),o=i.getCurrentPostId,s=i.isSavingPost,r=i.isPublishingPost,l=i.isAutosavingPost;return{postId:o(),isSaving:n||s(),isAutoSaving:l(),isPublishing:r()}})(h)});
     2function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e(t)}function t(t,n,r){return(n=function(t){var n=function(t,n){if("object"!=e(t)||!t)return t;var r;if("undefined"!=typeof Symbol&&void 0!==(r=t[Symbol.toPrimitive])){var o=r.call(t,n);if("object"!=e(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===n?String:Number)(t)}(t,"string");return"symbol"==e(n)?n:n+""}(n))in t?Object.defineProperty(t,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):t[n]=r,t}var n=["views"],r=function(e){return null==e?"":String(e)},o=function(e){return r(e).trim()},i=function(e,t){try{return new Intl.NumberFormat(t||void 0).format(e)}catch(n){return(new Intl.NumberFormat).format(e)}};function a(e){return a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},a(e)}function s(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var r,o,i,a,s=[],u=!0,c=!1;try{if(i=(n=n.call(e)).next,0===t);else for(;!(u=(r=i.call(n)).done)&&(s.push(r.value),s.length!==t);u=!0);}catch(e){c=!0,o=e}finally{try{if(!u&&null!=n.return&&(a=n.return(),Object(a)!==a))return}finally{if(c)throw o}}return s}}(e,t)||function(e,t){if(e){if("string"==typeof e)return u(e,t);var n={}.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?u(e,t):void 0}}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function u(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=Array(t);n<t;n++)r[n]=e[n];return r}function c(e,t,n){return(t=function(e){var t=function(e,t){if("object"!=a(e)||!e)return e;var n;if("undefined"!=typeof Symbol&&void 0!==(n=e[Symbol.toPrimitive])){var r=n.call(e,t);if("object"!=a(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==a(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var l,p,v,f,m,d,b,w,y,g,S,h,E,P,N,j,O,A,C=wp.element,I=C.useState,_=C.useEffect,T=wp.plugins.registerPlugin,k=wp.components,D=k.TextControl,x=k.Button,F=k.Dropdown,q=wp.editor&&wp.editor.PluginPostStatusInfo||wp.editPost&&wp.editPost.PluginPostStatusInfo,M=window.pvcEditorArgs,B=M.i18n,V="pvc-counter-totals",z="pvc_counter_totals",H=(l={views:M.views,canEdit:M.canEdit,request:function(e){return wp.apiFetch({path:"/post-views-counter/update-post-views/?id=".concat(M.postID),method:"POST",data:e}).then(function(e){return wp.data.dispatch("core/notices").removeNotice(V),e})},onError:function(e){var t=e&&e.message?" ".concat(e.message):"";wp.data.dispatch("core/notices").createErrorNotice(B.saveFailed+t,{id:V,isDismissible:!0})},onPending:function(e){var t=wp.data.select("core/editor"),n=Object.prototype.hasOwnProperty.call(t.getPostEdits(),z);e&&!n?wp.data.dispatch("core/editor").editPost(c({},z,Date.now())):!e&&n&&wp.data.dispatch("core/editor").editPost(c({},z,void 0))}},p=l.views,v=l.canEdit,f=l.request,m=l.onError,d=l.onPending,b={baseline:{views:Number(p)||0},values:{views:r(p)},saving:!1},w=!1,y=new Set,S=function(e){if(!g(e))return!1;var t=o(b.values[e]);return""!==t&&t!==r(b.baseline[e])},E=function(e){b=Object.assign({},b,e),y.forEach(function(e){return e(b)}),d&&!b.saving&&d(h())},P=function(){return n.reduce(function(e,t){return S(t)&&(e[t]=o(b.values[t])),e},{})},N=function(){if(b.saving)return w=!0,Promise.resolve(!1);var e=P();return Object.keys(e).length?(E({saving:!0}),Promise.resolve().then(function(){return f(e)}).then(function(t){var i={views:Number(t.views)||0},a=Object.assign({},b.values);return n.forEach(function(t){var n=o(b.values[t]);(Object.prototype.hasOwnProperty.call(e,t)?n===e[t]:!S(t))&&(a[t]=r(i[t]))}),E({baseline:i,values:a,saving:!1}),!0},function(e){return E({saving:!1}),m&&m(e),!1}).then(function(e){return w?(w=!1,N()):e})):Promise.resolve(!1)},{getState:function(){return b},subscribe:function(e){return y.add(e),function(){return y.delete(e)}},isEditable:g=function(e){return!!v&&n.includes(e)},isDirty:S,hasPendingEdits:h=function(){return n.some(S)},getPayload:P,setValue:function(e,n){g(e)&&E({values:Object.assign({},b.values,t({},e,r(n)))})},reset:function(e){E({values:Object.assign({},b.values,t({},e,r(b.baseline[e])))})},submit:N});if(M.canEdit){var L=(j=function(){return H.submit()},O=!1,A=!1,function(e){var t=e.isSaving,n=e.isAutosaving,r=e.didSucceed;if(t)return O||(A=!1),O=!0,void(A=A||!!n);O&&(O=!1,!A&&r&&j())});wp.data.subscribe(function(){var e=wp.data.select("core/editor");L({isSaving:e.isSavingPost(),isAutosaving:e.isAutosavingPost(),didSucceed:e.didPostSaveRequestSucceed()})})}var R=function(e){var t,n=e.metric,r=e.label,a=function(){var e=s(I(H.getState()),2),t=e[0],n=e[1];return _(function(){var e=H.subscribe(n);return function(){e()}},[]),t}(),u=function(e,t){var n=o(e.values[t]);return/^\d+$/.test(n)?i(Number(n),M.locale):""===n?i(e.baseline[t],M.locale):n}(a,n);return t=H.isEditable(n)?wp.element.createElement(F,{className:"edit-post-post-views-popover-wrapper",contentClassName:"edit-post-post-views-popover",popoverProps:{placement:"bottom-end"},renderToggle:function(e){var t=e.isOpen,n=e.onToggle;return wp.element.createElement(x,{size:"compact",variant:"tertiary",className:"edit-post-post-views-toggle-link","aria-expanded":t,"aria-label":B.valueLabel.replace("%1$s",r).replace("%2$s",u),onClick:n},u)},renderContent:function(e){var t=e.onClose;return wp.element.createElement("div",{className:"edit-post-post-views-popover-content"},wp.element.createElement(D,{__nextHasNoMarginBottom:!0,label:r,className:"edit-post-post-views-input",type:"number",min:0,step:1,inputMode:"numeric",value:a.values[n],onChange:function(e){return H.setValue(n,e)}}),wp.element.createElement("p",{className:"description"},B.help),wp.element.createElement(x,{variant:"link",className:"edit-post-post-views-cancel-link",onClick:function(){H.reset(n),t()}},B.cancel))}}):wp.element.createElement("span",{className:"edit-post-post-views-value"},u),wp.element.createElement(q,{className:"edit-post-post-views edit-post-post-".concat(n)},wp.element.createElement("div",{className:"editor-post-panel__row-label"},wp.element.createElement("span",null,r)),wp.element.createElement("div",{className:"editor-post-panel__row-control"},t))};T("post-views-counter",{icon:"",render:function(){return wp.element.createElement(R,{metric:"views",label:B.views})}});
    33
    44})();
  • post-views-counter/tags/1.8.0/js/frontend.js

    r3578455 r3734102  
    1 function t(t){return function(t){if(Array.isArray(t))return e(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,o){if(t){if("string"==typeof t)return e(t,o);var n={}.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?e(t,o):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function e(t,e){(null==e||e>t.length)&&(e=t.length);for(var o=0,n=Array(e);o<e;o++)n[o]=t[o];return n}function o(t){return o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o(t)}window.PostViewsCounter={promise:null,args:{},init:function(t){this.args=t;var e={storage_type:"cookies",storage_data:this.readCookieData("pvc_visits".concat(!1!==t.multisite?"_".concat(parseInt(t.multisite,10)):""))};"rest_api"===t.mode?this.promise=this.request(t.requestURL,e,"POST",{"Content-Type":"application/x-www-form-urlencoded; charset=utf-8","X-WP-Nonce":t.nonce}):(e.action="pvc-check-post",e.pvc_nonce=t.nonce,e.id=t.postID,this.promise=this.request(t.requestURL,e,"POST",{"Content-Type":"application/x-www-form-urlencoded; charset=utf-8"}))},request:function(t,e,n,r){var a=this,c={method:n,mode:"cors",cache:"no-cache",credentials:"same-origin",headers:r,body:this.prepareRequestData(e)};return fetch(t,c).then(function(t){if(!t.ok)throw Error(t.statusText);return t.json()}).then(function(t){try{"object"===o(t)&&null!==t?"success"in t&&!1===t.success?(console.log("PVC: Request error."),console.log(t.data)):(a.saveCookieData(t.storage),a.triggerEvent("pvcCheckPost",t)):(console.log("PVC: Invalid object."),console.log(t))}catch(e){console.log("PVC: Invalid JSON data."),console.log(e)}}).catch(function(t){console.log("PVC: Invalid response."),console.log(t)})},prepareRequestData:function(t){return Object.keys(t).map(function(e){return"".concat(encodeURIComponent(e),"=").concat(encodeURIComponent(t[e]))}).join("&").replace(/%20/g,"+")},triggerEvent:function(t,e){var o=new CustomEvent(t,{bubbles:!0,detail:e});document.dispatchEvent(o)},getCookiePath:function(){var t="string"==typeof this.args.path?this.args.path.trim():"";if(!t||"/"===t)return"/";var e=t.replace(/\/{2,}/g,"/").replace(/^\/+|\/+$/g,"");return e?"/".concat(e,"/"):"/"},saveCookieData:function(e){var o=this;if(e&&Object.prototype.hasOwnProperty.call(e,"name")){var n=t(new Set(e.name.map(function(t){return t.replace(/\[\d+\]$/,"")}))),r="";"https:"===document.location.protocol&&(r=";secure"),n.forEach(function(t){document.cookie.split(";").forEach(function(e){if(e&&e.trim()){var n=e.split("=")[0];if(n){var a=n.trim();new RegExp("^".concat(t,"\\[(\\d+)\\]$")).test(a)&&(document.cookie="".concat(a,"=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=").concat(o.getCookiePath(),";domain=").concat(o.args.domain).concat(r,";SameSite=Lax"))}}})});for(var a=0;a<e.name.length;a++){var c=new Date,i=parseInt(e.expiry[a],10),s=e.value[a];"string"!=typeof s&&(s=String(null!=s?s:"")),i?i*=1e3:i=c.getTime()+864e5,c.setTime(i),document.cookie="".concat(e.name[a],"=").concat(encodeURIComponent(s),";expires=").concat(c.toUTCString(),";path=").concat(this.getCookiePath(),";domain=").concat(this.args.domain).concat(r,";SameSite=Lax")}}},readCookieData:function(t){var e=[];document.cookie.split(";").forEach(function(o){if(o&&o.trim()){var n=o.split("="),r=n[0],a=n.slice(1).join("=");if(r){var c=r.trim(),i=new RegExp("^".concat(t,"\\[(\\d+)\\]$")),s=c.match(i);if(s){var u=a||"";try{u=decodeURIComponent(u)}catch(l){u=a||""}e.push({index:parseInt(s[1],10),value:u})}}}}),e.sort(function(t,e){return t.index-e.index});var n=e.map(function(t){return t.value}),r=n.join("");if(r)try{var a=JSON.parse(r);if(a&&"object"===o(a)&&1===a.version)return r}catch(c){}return n.join("a")}};var n=function(){return window.PostViewsCounter.init(pvcArgsFrontend)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",n):n();
     1function t(t){return function(t){if(Array.isArray(t))return e(t)}(t)||function(t){if("undefined"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t["@@iterator"])return Array.from(t)}(t)||function(t,o){if(t){if("string"==typeof t)return e(t,o);var n={}.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?e(t,o):void 0}}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function e(t,e){(null==e||e>t.length)&&(e=t.length);for(var o=0,n=Array(e);o<e;o++)n[o]=t[o];return n}function o(t){return o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},o(t)}window.PostViewsCounter={promise:null,args:{},init:function(t){if(this.args=t,this.loadDynamicMetrics(),!0===t.countEnabled){var e={storage_type:"cookies",storage_data:this.readCookieData("pvc_visits".concat(!1!==t.multisite?"_".concat(parseInt(t.multisite,10)):"")),storage_capable:this.isCookieStorageAvailable()?1:0};"rest_api"===t.mode?this.promise=this.request(t.requestURL,e,"POST",{"Content-Type":"application/x-www-form-urlencoded; charset=utf-8","X-WP-Nonce":t.nonce}):(e.action="pvc-check-post",e.pvc_nonce=t.nonce,e.id=t.postID,this.promise=this.request(t.requestURL,e,"POST",{"Content-Type":"application/x-www-form-urlencoded; charset=utf-8"}))}},loadDynamicMetrics:function(){var t=this;if("string"==typeof this.args.displayURL&&""!==this.args.displayURL){var e=new Map;new Set(document.querySelectorAll(".post-views.load-dynamic, [data-pvc-metric].load-dynamic")).forEach(function(t){var o=t.getAttribute("data-pvc-metric"),n=t.getAttribute("data-pvc-type"),r=t.getAttribute("data-pvc-id"),a=t.getAttribute("data-pvc-period"),i=null===o?"views":o,c=null===a?"total":a,s=n,u=r;if(null===n&&null===r){var l=t.className.match(/(?:^|\s)post-([1-9]\d*)(?=\s|$)/);s=null===l?null:"post",u=null===l?null:l[1]}if("post"!==s||null===u||!/^[1-9]\d*$/.test(u)||"views"!==i||!/^[a-z0-9_-]+$/.test(c))return t.classList.add("loaded","load-error"),t.classList.remove("loading"),void t.removeAttribute("aria-busy");var d=parseInt(u,10),p="".concat(i,":").concat(s,":").concat(d,":").concat(c);e.has(p)||e.set(p,{metric:i,id:d,type:s,period:c,elements:[]}),e.get(p).elements.push(t),t.classList.add("loading"),t.classList.remove("loaded","load-error"),t.setAttribute("aria-busy","true")}),e.forEach(function(e){var n=e.metric,r=e.id,a=e.type,i=e.period,c=e.elements,s="total"!==i||c.some(function(t){return"true"===t.getAttribute("data-pvc-server-formatted")}),u=s?"?metric=".concat(encodeURIComponent(n),"&type=").concat(encodeURIComponent(a),"&period=").concat(encodeURIComponent(i)):"";fetch("".concat(t.args.displayURL,"/").concat(r).concat(u),{method:"GET",credentials:"same-origin",cache:"no-cache"}).then(function(t){if(!t.ok)throw new Error(t.statusText);return t.json()}).then(function(t){var e=t,n=t;if(s){var u="".concat(a,":").concat(r,":").concat(i);if(!t||"object"!==o(t)||!t.values||!Object.prototype.hasOwnProperty.call(t.values,u))throw new Error("Invalid display response");e=t.values[u],n=t.formatted&&Object.prototype.hasOwnProperty.call(t.formatted,u)?t.formatted[u]:e}if(!(("number"==typeof e||"string"==typeof e)&&""!==e&&Number.isFinite(Number(e))))throw new Error("Invalid metric value");if(s&&!["string","number"].includes(o(n)))throw new Error("Invalid formatted metric value");c.forEach(function(t){var o=t.querySelector(".post-views-count");o&&(o.textContent=String(s?n:e)),t.classList.add("loaded"),t.classList.remove("loading","load-error"),t.removeAttribute("aria-busy")})}).catch(function(){c.forEach(function(t){t.classList.add("loaded","load-error"),t.classList.remove("loading"),t.removeAttribute("aria-busy")})})})}},request:function(t,e,n,r){var a=this,i={method:n,mode:"cors",cache:"no-cache",credentials:"same-origin",headers:r,body:this.prepareRequestData(e)};return fetch(t,i).then(function(t){if(!t.ok)throw Error(t.statusText);return t.json()}).then(function(t){try{"object"===o(t)&&null!==t?"success"in t&&!1===t.success?(console.log("PVC: Request error."),console.log(t.data)):(a.saveCookieData(t.storage),a.triggerEvent("pvcCheckPost",t)):(console.log("PVC: Invalid object."),console.log(t))}catch(e){console.log("PVC: Invalid JSON data."),console.log(e)}}).catch(function(t){console.log("PVC: Invalid response."),console.log(t)})},prepareRequestData:function(t){return Object.keys(t).map(function(e){return"".concat(encodeURIComponent(e),"=").concat(encodeURIComponent(t[e]))}).join("&").replace(/%20/g,"+")},triggerEvent:function(t,e){var o=new CustomEvent(t,{bubbles:!0,detail:e});document.dispatchEvent(o)},getCookiePath:function(){var t="string"==typeof this.args.path?this.args.path.trim():"";if(!t||"/"===t)return"/";var e=t.replace(/\/{2,}/g,"/").replace(/^\/+|\/+$/g,"");return e?"/".concat(e,"/"):"/"},saveCookieData:function(e){var n=this;if(e&&Object.prototype.hasOwnProperty.call(e,"name")&&!(!Array.isArray(e.name)||!Array.isArray(e.value)||e.name.length>4||e.name.some(function(t){return!/\[[0-3]\]$/.test(String(t))})||new Blob(e.value.map(function(t){return String(null!=t?t:"")})).size>15920)){var r=e.value.map(function(t){return String(null!=t?t:"")}).join("");if(r.trim().startsWith("{"))try{var a=JSON.parse(r);if(a&&"object"===o(a)&&Object.prototype.hasOwnProperty.call(a,"version")&&!this.isBoundedSessionPayload(a))return}catch(p){return}var i=t(new Set(e.name.map(function(t){return t.replace(/\[\d+\]$/,"")}))),c="";"https:"===document.location.protocol&&(c=";secure"),i.forEach(function(t){document.cookie.split(";").forEach(function(e){if(e&&e.trim()){var o=e.split("=")[0];if(o){var r=o.trim();new RegExp("^".concat(t,"\\[(\\d+)\\]$")).test(r)&&(document.cookie="".concat(r,"=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=").concat(n.getCookiePath(),";domain=").concat(n.args.domain).concat(c,";SameSite=Lax"))}}})});for(var s=0;s<e.name.length;s++){var u=new Date,l=parseInt(e.expiry[s],10),d=e.value[s];"string"!=typeof d&&(d=String(null!=d?d:"")),l?l*=1e3:l=u.getTime()+864e5,u.setTime(l),document.cookie="".concat(e.name[s],"=").concat(encodeURIComponent(d),";expires=").concat(u.toUTCString(),";path=").concat(this.getCookiePath(),";domain=").concat(this.args.domain).concat(c,";SameSite=Lax")}}},readCookieData:function(t){var e=[],n=!1;if(document.cookie.split(";").forEach(function(o){if(o&&o.trim()){var r=o.split("="),a=r[0],i=r.slice(1).join("=");if(a){var c=a.trim(),s=new RegExp("^".concat(t,"\\[(\\d+)\\]$")),u=c.match(s);if(u){if(parseInt(u[1],10)>=4)return void(n=!0);var l=i||"";try{l=decodeURIComponent(l)}catch(d){l=i||""}e.push({index:parseInt(u[1],10),value:l})}}}}),e.sort(function(t,e){return t.index-e.index}),n||e.some(function(t,e){return t.index!==e}))return"__pvc_invalid_storage_payload__";var r=e.map(function(t){return t.value});if(new Blob(r).size>15920)return"__pvc_invalid_storage_payload__";var a=r.join("");if(a)try{var i=JSON.parse(a);if(this.isBoundedSessionPayload(i))return a;if(i&&"object"===o(i)&&1===i.version)return"__pvc_invalid_storage_payload__"}catch(s){}var c=r.join("a");return c&&c.split("a").length>1e3?"__pvc_invalid_storage_payload__":c},isBoundedSessionPayload:function(t){if(!t||"object"!==o(t)||1!==t.version||!t.visited||"object"!==o(t.visited)||Array.isArray(t.visited))return!1;var e=Object.values(t.visited);return!(e.length>8||!e.every(Array.isArray))&&e.reduce(function(t,e){return t+new Set(e.map(function(t){return String(t)})).size},0)<=1e3},isCookieStorageAvailable:function(){var t="__pvcStorageTest_".concat(Date.now()),e="";"https:"===document.location.protocol&&(e=";secure");try{document.cookie="".concat(t,"=1;path=").concat(this.getCookiePath(),";domain=").concat(this.args.domain).concat(e,";SameSite=Lax");var o=document.cookie.split(";").some(function(e){return e.trim().startsWith("".concat(t,"="))});return document.cookie="".concat(t,"=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=").concat(this.getCookiePath(),";domain=").concat(this.args.domain).concat(e,";SameSite=Lax"),o}catch(n){return!1}}};var n=function(){return window.PostViewsCounter.init(pvcArgsFrontend)};"loading"===document.readyState?document.addEventListener("DOMContentLoaded",n):n();
  • post-views-counter/tags/1.8.0/languages/post-views-counter.pot

    r3647380 r3734102  
    1 #, fuzzy
     1# Copyright (C) 2026 dFactory
     2# This file is distributed under the MIT License.
    23msgid ""
    34msgstr ""
    4 "Project-Id-Version: Post Views Counter\n"
    5 "POT-Creation-Date: 2026-07-29 11:20+0200\n"
    6 "PO-Revision-Date: 2015-04-08 18:59+0100\n"
    7 "Last-Translator: Bartosz Arendt <info@dfactory.eu>\n"
    8 "Language-Team: dFactory <info@dfactory.eu>\n"
    9 "Language: en\n"
     5"Project-Id-Version: Post Views Counter 1.8.0\n"
     6"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/post-views-counter\n"
     7"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
     8"Language-Team: LANGUAGE <LL@li.org>\n"
    109"MIME-Version: 1.0\n"
    1110"Content-Type: text/plain; charset=UTF-8\n"
    1211"Content-Transfer-Encoding: 8bit\n"
    13 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
    14 "X-Generator: Poedit 3.9\n"
    15 "X-Poedit-KeywordsList: "
    16 "gettext;gettext_noop;__;_e;esc_attr__;esc_attr_e;esc_html__;esc_html_e;_x;_n\n"
    17 "X-Poedit-Basepath: ..\n"
    18 "X-Poedit-SourceCharset: UTF-8\n"
    19 "X-Poedit-SearchPath-0: .\n"
    20 "X-Poedit-SearchPathExcluded-0: node_modules\n"
    21 
    22 #: blocks/most-viewed-posts/index.js:1 blocks/post-views/index.js:1
     12"POT-Creation-Date: 2026-10-07T18:29:06+00:00\n"
     13"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
     14"X-Generator: WP-CLI 2.12.0\n"
     15"X-Domain: post-views-counter\n"
     16
     17#. translators: %s: request parameter name
     18#: class-admin.php:95
     19#, php-format
     20msgid "%s must be a positive whole number."
     21msgstr ""
     22
     23#. translators: %s: request parameter name
     24#: class-admin.php:114
     25#, php-format
     26msgid "%s must be a whole number."
     27msgstr ""
     28
     29#: class-admin.php:130
     30#: class-admin.php:136
     31#: class-columns.php:169
     32msgid "You are not allowed to edit this item."
     33msgstr ""
     34
     35#: class-admin.php:133
     36#: class-columns-modal.php:171
     37#: class-columns.php:166
     38#: class-counter.php:556
     39msgid "Invalid post ID."
     40msgstr ""
     41
     42#: class-admin.php:154
     43#: class-columns.php:177
     44msgid "The counter totals could not be saved."
     45msgstr ""
     46
     47#: class-admin.php:164
     48msgid "No counter totals were submitted."
     49msgstr ""
     50
     51#: class-admin.php:220
     52#: class-columns-modal.php:242
     53#: class-columns.php:363
     54#: class-columns.php:370
     55#: class-columns.php:375
     56#: class-columns.php:648
     57#: class-columns.php:723
     58#: class-columns.php:798
     59#: class-dashboard.php:1226
     60#: class-settings-display.php:97
     61msgid "Views"
     62msgstr ""
     63
     64#. translators: 1: metric name, 2: formatted count
     65#: class-admin.php:222
     66#: class-columns.php:731
     67#, php-format
     68msgid "%1$s: %2$s."
     69msgstr ""
     70
     71#: class-admin.php:223
     72#: class-columns.php:380
     73msgid "Lifetime totals. Changing them does not alter daily, weekly, monthly, or yearly history."
     74msgstr ""
     75
     76#: class-admin.php:224
     77#: class-columns.php:384
     78msgid "Cancel"
     79msgstr ""
     80
     81#: class-admin.php:225
     82msgid "Views could not be saved."
     83msgstr ""
     84
     85#: class-columns-modal.php:95
     86msgid "Loading..."
     87msgstr ""
     88
     89#: class-columns-modal.php:96
     90#: class-columns-modal.php:361
     91msgid "Close"
     92msgstr ""
     93
     94#: class-columns-modal.php:97
     95msgid "An error occurred while loading data."
     96msgstr ""
     97
     98#: class-columns-modal.php:98
     99#: class-columns-modal.php:366
     100msgid "Views in this period:"
     101msgstr ""
     102
     103#: class-columns-modal.php:99
     104#: class-dashboard.php:121
     105msgid "Retry"
     106msgstr ""
     107
     108#: class-columns-modal.php:100
     109msgid "view"
     110msgstr ""
     111
     112#: class-columns-modal.php:101
     113#: class-emails-template.php:1610
     114msgid "views"
     115msgstr ""
     116
     117#: class-columns-modal.php:161
     118#: class-columns.php:987
     119msgid "Permission denied."
     120msgstr ""
     121
     122#: class-columns-modal.php:177
     123msgid "Post not found."
     124msgstr ""
     125
     126#: class-columns-modal.php:181
     127msgid "Admin column disabled."
     128msgstr ""
     129
     130#: class-columns-modal.php:185
     131msgid "Post type is not tracked."
     132msgstr ""
     133
     134#: class-columns-modal.php:189
     135#: class-columns-modal.php:193
     136msgid "Access denied for this post."
     137msgstr ""
     138
     139#: class-columns-modal.php:197
     140msgid "Invalid period."
     141msgstr ""
     142
     143#: class-columns-modal.php:371
     144#: class-dashboard.php:337
     145msgid "Chart period"
     146msgstr ""
     147
     148#: class-columns-modal.php:372
     149msgid "Year"
     150msgstr ""
     151
     152#: class-columns-modal.php:373
     153msgid "Month"
     154msgstr ""
     155
     156#: class-columns-modal.php:374
     157msgid "Week"
     158msgstr ""
     159
     160#: class-columns-modal.php:385
     161msgid "More insights available"
     162msgstr ""
     163
     164#: class-columns-modal.php:386
     165msgid "Upgrade to Pro to unlock it"
     166msgstr ""
     167
     168#: class-columns.php:174
     169#: class-columns.php:977
     170msgid "Invalid counter totals."
     171msgstr ""
     172
     173#: class-columns.php:185
     174msgid "The saved counter totals could not be read back."
     175msgstr ""
     176
     177#: class-columns.php:370
     178#: class-dashboard.php:1258
     179msgid "Edit"
     180msgstr ""
     181
     182#: class-columns.php:383
     183msgid "OK"
     184msgstr ""
     185
     186#: class-columns.php:675
     187msgid "(no title)"
     188msgstr ""
     189
     190#: class-columns.php:736
     191#, php-format
     192msgid "Total Views: %s"
     193msgstr ""
     194
     195#: class-columns.php:737
     196#, php-format
     197msgid "%1$s: %2$s. Open chart."
     198msgstr ""
     199
     200#: class-columns.php:748
     201msgid "View statistics are restricted."
     202msgstr ""
     203
     204#: class-columns.php:931
     205#, php-format
     206msgid "Counter totals updated for %s post."
     207msgid_plural "Counter totals updated for %s posts."
     208msgstr[0] ""
     209msgstr[1] ""
     210
     211#: class-columns.php:934
     212#, php-format
     213msgid "Counter totals could not be updated for %s post."
     214msgid_plural "Counter totals could not be updated for %s posts."
     215msgstr[0] ""
     216msgstr[1] ""
     217
     218#: class-counter.php:82
     219msgid "Missing or invalid queue parameters."
     220msgstr ""
     221
     222#: class-counter.php:89
     223msgid "Security check failed."
     224msgstr ""
     225
     226#: class-counter.php:102
     227msgid "No valid post IDs were provided."
     228msgstr ""
     229
     230#: class-counter.php:549
     231msgid "REST API method is disabled."
     232msgstr ""
     233
     234#: class-counter.php:563
     235msgid "Post type excluded."
     236msgstr ""
     237
     238#: class-counter.php:570
     239msgid "Invalid storage type."
     240msgstr ""
     241
     242#: class-counter.php:3444
     243#: class-counter.php:3453
     244#: class-counter.php:3466
     245msgid "The requested PVC metric is not supported."
     246msgstr ""
     247
     248#: class-counter.php:3458
     249msgid "Too many PVC metric targets were requested."
     250msgstr ""
     251
     252#: class-counter.php:3638
     253msgid "You do not have permission to count post views via REST API."
     254msgstr ""
     255
     256#: class-cron.php:99
     257msgid "Post Views Counter reset daily counts interval"
     258msgstr ""
     259
     260#: class-dashboard.php:95
     261#: class-dashboard.php:140
     262#: class-settings.php:633
     263msgid "Post Views"
     264msgstr ""
     265
     266#: class-dashboard.php:120
     267msgid "Failed to load this widget."
     268msgstr ""
     269
     270#: class-dashboard.php:141
     271msgid "Displays a chart of most viewed post types."
     272msgstr ""
     273
     274#: class-dashboard.php:152
     275msgid "Top Posts"
     276msgstr ""
     277
     278#: class-dashboard.php:153
     279msgid "Displays a list of most viewed single posts or pages."
     280msgstr ""
     281
     282#: class-dashboard.php:235
     283msgid "More Insights"
     284msgstr ""
     285
     286#: class-dashboard.php:237
     287msgid "Go beyond basic post views."
     288msgstr ""
     289
     290#: class-dashboard.php:238
     291msgid "See site views, referrers, popular searches, top users, and more."
     292msgstr ""
     293
     294#: class-dashboard.php:240
     295msgid "Unlock more insights"
     296msgstr ""
     297
     298#: class-dashboard.php:246
     299msgid "Powered by"
     300msgstr ""
     301
     302#: class-dashboard.php:291
     303msgid "Chart metrics"
     304msgstr ""
     305
     306#: class-dashboard.php:301
     307msgid "Chart datasets"
     308msgstr ""
     309
     310#: class-dashboard.php:337
     311#: class-settings-display.php:141
     312#: class-widgets.php:171
     313msgid "Views Period"
     314msgstr ""
     315
     316#: class-dashboard.php:338
     317msgid "Yearly"
     318msgstr ""
     319
     320#: class-dashboard.php:339
     321#: class-settings-emails.php:91
     322msgid "Monthly"
     323msgstr ""
     324
     325#: class-dashboard.php:340
     326#: class-settings-emails.php:90
     327msgid "Weekly"
     328msgstr ""
     329
     330#: class-dashboard.php:633
     331msgctxt "dashboard chart metric"
     332msgid "Views"
     333msgstr ""
     334
     335#: class-dashboard.php:641
     336msgctxt "dashboard chart metric"
     337msgid "Visits"
     338msgstr ""
     339
     340#: class-dashboard.php:657
     341#: class-dashboard.php:1147
     342#: class-dashboard.php:1320
     343msgid "You do not have permission to access this page."
     344msgstr ""
     345
     346#: class-dashboard.php:769
     347#: class-settings-display.php:150
     348#: class-widgets.php:85
     349#: functions.php:1022
     350#: post-views-counter.php:534
     351msgid "Total Views"
     352msgstr ""
     353
     354#: class-dashboard.php:881
     355msgid "Total Visits"
     356msgstr ""
     357
     358#: class-dashboard.php:1005
     359msgid "This month is level with the same days last month."
     360msgstr ""
     361
     362#. translators: 1: the month the chart shows, e.g. June 2026, 2: the month before it
     363#: class-dashboard.php:1007
     364#, php-format
     365msgid "%1$s was level with %2$s."
     366msgstr ""
     367
     368#: class-dashboard.php:1015
     369msgid "less than 1%"
     370msgstr ""
     371
     372#. translators: %s: a change in percent, a whole number without a sign
     373#: class-dashboard.php:1018
     374#, php-format
     375msgid "%s%%"
     376msgstr ""
     377
     378#. translators: %s: the change, e.g. 12%
     379#: class-dashboard.php:1023
     380#, php-format
     381msgid "This month is %s higher than the same days last month."
     382msgstr ""
     383
     384#. translators: %s: the change, e.g. 12%
     385#: class-dashboard.php:1025
     386#, php-format
     387msgid "This month is %s lower than the same days last month."
     388msgstr ""
     389
     390#. translators: 1: the month the chart shows, e.g. June 2026, 2: the change, e.g. 12%, 3: the month before it
     391#: class-dashboard.php:1029
     392#, php-format
     393msgid "%1$s was %2$s higher than %3$s."
     394msgstr ""
     395
     396#. translators: 1: the month the chart shows, e.g. June 2026, 2: the change, e.g. 12%, 3: the month before it
     397#: class-dashboard.php:1031
     398#, php-format
     399msgid "%1$s was %2$s lower than %3$s."
     400msgstr ""
     401
     402#: class-dashboard.php:1058
     403msgctxt "dashboard comparison: a month"
     404msgid "F Y"
     405msgstr ""
     406
     407#: class-dashboard.php:1134
     408msgid "Visit data is not available right now."
     409msgstr ""
     410
     411#: class-dashboard.php:1225
     412msgid "Post"
     413msgstr ""
     414
     415#: class-dashboard.php:1271
     416#: functions.php:1013
     417msgid "No most viewed posts found."
     418msgstr ""
     419
     420#: class-emails-mailer.php:169
     421msgid "WordPress could not send the content views summary email."
     422msgstr ""
     423
     424#: class-emails-mailer.php:229
     425msgid "The content views summary recipient is invalid."
     426msgstr ""
     427
     428#: class-emails-mailer.php:307
     429msgid "Pending view counts could not be flushed before the summary query."
     430msgstr ""
     431
     432#: class-emails-mailer.php:892
     433#, php-format
     434msgid "Post Views Counter on %s"
     435msgstr ""
     436
     437#: class-emails-scheduler.php:46
     438msgid "Post Views Counter weekly email summary interval"
     439msgstr ""
     440
     441#. Plugin Name of the plugin
     442#: class-emails-template.php:506
     443#: class-emails-template.php:880
     444#: class-settings.php:273
     445#: class-settings.php:632
     446#: class-settings.php:633
     447#: post-views-counter.php
     448msgid "Post Views Counter"
     449msgstr ""
     450
     451#: class-emails-template.php:1146
     452#: class-emails-template.php:1188
     453msgid "Content views"
     454msgstr ""
     455
     456#: class-emails-template.php:1148
     457#: class-emails-template.php:1175
     458#, php-format
     459msgid "%s total views"
     460msgstr ""
     461
     462#: class-emails-template.php:1158
     463#: class-emails-template.php:1185
     464#, php-format
     465msgid "%s content items received views"
     466msgstr ""
     467
     468#: class-emails-template.php:1205
     469#: class-emails-template.php:1228
     470msgid "No top content was available for this period."
     471msgstr ""
     472
     473#: class-emails-template.php:1207
     474#: class-emails-template.php:1242
     475msgid "Top content"
     476msgstr ""
     477
     478#: class-emails-template.php:1210
     479#: class-emails-template.php:1233
     480msgid "(Untitled)"
     481msgstr ""
     482
     483#: class-emails-template.php:1212
     484#, php-format
     485msgid "%1$s. %2$s - %3$s views"
     486msgstr ""
     487
     488#: class-emails-template.php:1238
     489#, php-format
     490msgid "%s views"
     491msgstr ""
     492
     493#: class-emails-template.php:1268
     494#: class-emails-template.php:1301
     495#: class-emails-template.php:1306
     496msgid "Traffic signals"
     497msgstr ""
     498
     499#: class-emails-template.php:1297
     500#: class-emails-template.php:1375
     501#, php-format
     502msgid "Unusual traffic activity was detected for %s."
     503msgstr ""
     504
     505#: class-emails-template.php:1318
     506#, php-format
     507msgid "Manage Emails in WordPress: %s"
     508msgstr ""
     509
     510#: class-emails-template.php:1330
     511msgid "Manage Emails in WordPress"
     512msgstr ""
     513
     514#: class-emails-template.php:1340
     515#, php-format
     516msgid "Visit your site: %s"
     517msgstr ""
     518
     519#: class-emails-template.php:1351
     520msgid "your site"
     521msgstr ""
     522
     523#: class-emails-template.php:1353
     524#, php-format
     525msgid "Visit %s"
     526msgstr ""
     527
     528#: class-emails-template.php:1377
     529msgid "Unusual traffic activity was detected during this report period."
     530msgstr ""
     531
     532#: class-emails-template.php:1380
     533msgid "No unusual traffic activity was detected during this report period."
     534msgstr ""
     535
     536#: class-emails-template.php:1418
     537msgid "day"
     538msgstr ""
     539
     540#: class-emails-template.php:1421
     541msgid "month"
     542msgstr ""
     543
     544#: class-emails-template.php:1424
     545msgid "week"
     546msgstr ""
     547
     548#: class-emails-template.php:1469
     549msgid "weekly"
     550msgstr ""
     551
     552#: class-emails-template.php:1484
     553msgid "Weekly Content Views Summary"
     554msgstr ""
     555
     556#: class-emails-template.php:1643
     557#, php-format
     558msgid "No previous %s comparison is available yet"
     559msgstr ""
     560
     561#: class-emails-template.php:1649
     562#, php-format
     563msgid "No change from the previous %s"
     564msgstr ""
     565
     566#: class-emails-template.php:1654
     567#, php-format
     568msgid "Up %1$s views from the previous %2$s"
     569msgstr ""
     570
     571#: class-emails-template.php:1656
     572#, php-format
     573msgid "Down %1$s views from the previous %2$s"
     574msgstr ""
     575
     576#: class-frontend.php:366
     577msgid "Views:"
     578msgstr ""
     579
     580#: class-import.php:67
     581msgid "Custom Meta Key"
     582msgstr ""
     583
     584#: class-import.php:208
     585msgid "Override existing views"
     586msgstr ""
     587
     588#: class-import.php:209
     589msgid "Replace stored counts with the imported values."
     590msgstr ""
     591
     592#: class-import.php:214
     593msgid "Merge with existing views"
     594msgstr ""
     595
     596#: class-import.php:215
     597msgid "Add imported counts on top of the existing values."
     598msgstr ""
     599
     600#: class-import.php:220
     601msgid "Skip Existing"
     602msgstr ""
     603
     604#: class-import.php:221
     605msgid "Only import data when the target record does not exist yet."
     606msgstr ""
     607
     608#: class-import.php:226
     609msgid "Keep Higher Count"
     610msgstr ""
     611
     612#: class-import.php:227
     613msgid "Keep whichever value is higher when comparing imported and stored counts."
     614msgstr ""
     615
     616#: class-import.php:232
     617msgid "Fill Empty Counts"
     618msgstr ""
     619
     620#: class-import.php:233
     621msgid "Only import data for posts or periods that currently store zero views."
     622msgstr ""
     623
     624#: class-import.php:331
     625msgctxt "view_counts"
     626msgid "total"
     627msgstr ""
     628
     629#: class-import.php:334
     630msgctxt "view_counts"
     631msgid "yearly"
     632msgstr ""
     633
     634#: class-import.php:337
     635msgctxt "view_counts"
     636msgid "monthly"
     637msgstr ""
     638
     639#: class-import.php:340
     640msgctxt "view_counts"
     641msgid "weekly"
     642msgstr ""
     643
     644#: class-import.php:343
     645msgctxt "view_counts"
     646msgid "daily"
     647msgstr ""
     648
     649#: class-import.php:348
     650msgctxt "view_counts"
     651msgid "post types"
     652msgstr ""
     653
     654#: class-import.php:361
     655#, php-format
     656msgid "Imports %s view counts for %s"
     657msgstr ""
     658
     659#: class-import.php:363
     660#, php-format
     661msgid "Imports %s view counts"
     662msgstr ""
     663
     664#: class-import.php:402
     665msgctxt "view_counts"
     666msgid "and"
     667msgstr ""
     668
     669#: class-import.php:428
     670msgid "Invalid import provider selected."
     671msgstr ""
     672
     673#: class-import.php:594
     674msgid "Enter the meta key from which the views data is to be retrieved during import."
     675msgstr ""
     676
     677#: class-import.php:636
     678#: class-import.php:783
     679msgid "The source data could not be analyzed because a database operation failed."
     680msgstr ""
     681
     682#: class-import.php:645
     683#: class-import.php:794
     684#: class-import.php:1067
     685#: class-import.php:1439
     686#, php-format
     687msgid "No valid views data found for %s."
     688msgstr ""
     689
     690#: class-import.php:645
     691#, php-format
     692msgid "meta key: %s"
     693msgstr ""
     694
     695#: class-import.php:653
     696#: class-import.php:719
     697#, php-format
     698msgid "Meta key \"%s\"."
     699msgstr ""
     700
     701#: class-import.php:686
     702#: class-import.php:832
     703#: class-import.php:1071
     704#: class-import.php:1440
     705msgid "No valid post data found to import."
     706msgstr ""
     707
     708#: class-import.php:1052
     709msgid "The Statify source could not be analyzed because a database operation failed."
     710msgstr ""
     711
     712#: class-import.php:1075
     713msgid "No post types are selected for tracking in settings."
     714msgstr ""
     715
     716#: class-import.php:1142
     717#, php-format
     718msgid "Would skip %s non-post URLs."
     719msgstr ""
     720
     721#: class-import.php:1142
     722#, php-format
     723msgid "Skipped %s non-post URLs."
     724msgstr ""
     725
     726#: class-import.php:1431
     727msgid "The Page Views Count source could not be analyzed because a database operation failed."
     728msgstr ""
     729
     730#: class-import.php:2078
     731msgid "The import stopped after a database error. Verify destination totals and restore a database backup before retrying or correcting the import."
     732msgstr ""
     733
     734#: class-import.php:2081
     735msgid "The database transaction outcome could not be confirmed, so part of this import may have been stored."
     736msgstr ""
     737
     738#: class-import.php:2084
     739#, php-format
     740msgid "Support reference: %s."
     741msgstr ""
     742
     743#: class-import.php:2264
     744#, php-format
     745msgid "Analysis of %s:"
     746msgstr ""
     747
     748#: class-import.php:2266
     749#, php-format
     750msgid "Import from %s:"
     751msgstr ""
     752
     753#: class-import.php:2273
     754msgid "No post view data was found. The valid source rows map to other content types."
     755msgstr ""
     756
     757#: class-import.php:2275
     758msgid "No post view data was imported. The valid source rows map to other content types."
     759msgstr ""
     760
     761#: class-import.php:2280
     762#, php-format
     763msgid "Found period view data for %s posts, but no valid source total rows."
     764msgstr ""
     765
     766#: class-import.php:2285
     767#, php-format
     768msgid "Imported period view data for %s posts; no valid source total rows were available."
     769msgstr ""
     770
     771#: class-import.php:2292
     772#, php-format
     773msgid "Found %s total views for %s posts."
     774msgstr ""
     775
     776#: class-import.php:2298
     777#, php-format
     778msgid "Successfully imported %s total views for %s posts."
     779msgstr ""
     780
     781#: class-import.php:2309
     782#, php-format
     783msgid "Also found period view data for %s additional posts without source totals."
     784msgstr ""
     785
     786#: class-import.php:2314
     787#, php-format
     788msgid "Also imported period view data for %s additional posts without source totals."
     789msgstr ""
     790
     791#: class-import.php:2327
     792#, php-format
     793msgid "Skipped %1$s total views for %2$s posts."
     794msgstr ""
     795
     796#: class-integrations.php:110
     797msgid "Integrate with WordPress block editor to order posts by views in Query Loop and Latest Posts blocks."
     798msgstr ""
     799
     800#: class-integrations.php:117
     801msgid "Query Loop Block"
     802msgstr ""
     803
     804#: class-integrations.php:118
     805msgid "Enables ordering posts by view count in the core Query Loop block."
     806msgstr ""
     807
     808#: class-integrations.php:122
     809msgid "Latest Posts Block"
     810msgstr ""
     811
     812#: class-integrations.php:123
     813msgid "Enables ordering posts by view count in the core Latest Posts block."
     814msgstr ""
     815
     816#: class-integrations.php:130
     817msgid "Integrate with AMP plugin to handle post views on AMP pages."
     818msgstr ""
     819
     820#: class-integrations.php:137
     821msgid "AMP Support"
     822msgstr ""
     823
     824#: class-integrations.php:138
     825msgid "Tracks and displays views on AMP-enabled pages."
     826msgstr ""
     827
     828#: class-integrations.php:145
     829msgid "Integrate with Beaver Builder to order module posts by views."
     830msgstr ""
     831
     832#: class-integrations.php:152
     833msgid "Post Grid Module"
     834msgstr ""
     835
     836#: class-integrations.php:153
     837msgid "Orders posts by views in the Beaver Builder Post Grid module."
     838msgstr ""
     839
     840#: class-integrations.php:157
     841msgid "Post Carousel Module"
     842msgstr ""
     843
     844#: class-integrations.php:158
     845msgid "Orders posts by views in the Beaver Builder Post Carousel module."
     846msgstr ""
     847
     848#: class-integrations.php:162
     849#: class-integrations.php:202
     850msgid "Post Slider Module"
     851msgstr ""
     852
     853#: class-integrations.php:163
     854msgid "Orders posts by views in the Beaver Builder Post Slider module."
     855msgstr ""
     856
     857#: class-integrations.php:170
     858msgid "Integrate with CoBlocks to order post feed results by views."
     859msgstr ""
     860
     861#: class-integrations.php:177
     862#: class-integrations.php:282
     863msgid "Posts Block"
     864msgstr ""
     865
     866#: class-integrations.php:178
     867msgid "Adds post views ordering to the CoBlocks Posts block."
     868msgstr ""
     869
     870#: class-integrations.php:182
     871msgid "Post Carousel Block"
     872msgstr ""
     873
     874#: class-integrations.php:183
     875msgid "Adds post views ordering to the CoBlocks Post Carousel block."
     876msgstr ""
     877
     878#: class-integrations.php:190
     879msgid "Integrate with Divi Theme to order module posts by views when the module uses the \"orderby-post-views\" CSS class."
     880msgstr ""
     881
     882#: class-integrations.php:197
     883msgid "Blog Module"
     884msgstr ""
     885
     886#: class-integrations.php:198
     887msgid "Orders posts by views in the Divi Blog module."
     888msgstr ""
     889
     890#: class-integrations.php:203
     891msgid "Orders posts by views in the Divi Post Slider module."
     892msgstr ""
     893
     894#: class-integrations.php:207
     895msgid "Portfolio Module"
     896msgstr ""
     897
     898#: class-integrations.php:208
     899msgid "Orders posts by views in the Divi Portfolio module."
     900msgstr ""
     901
     902#: class-integrations.php:215
     903msgid "Integrate with Elementor Pro to order posts by views when a widget query uses the \"post_views\" Query ID."
     904msgstr ""
     905
     906#: class-integrations.php:222
     907msgid "Posts Widget"
     908msgstr ""
     909
     910#: class-integrations.php:223
     911msgid "Orders posts by views in the Elementor Pro Posts widget when its Query ID is set to \"post_views\"."
     912msgstr ""
     913
     914#: class-integrations.php:227
     915msgid "Portfolio Widget"
     916msgstr ""
     917
     918#: class-integrations.php:228
     919msgid "Orders items by views in the Elementor Pro Portfolio widget when its Query ID is set to \"post_views\"."
     920msgstr ""
     921
     922#: class-integrations.php:232
     923msgid "Loop Grid / Loop Carousel"
     924msgstr ""
     925
     926#: class-integrations.php:233
     927msgid "Orders items by views in the Elementor Pro Loop Grid and Loop Carousel widgets when the Query ID is set to \"post_views\"."
     928msgstr ""
     929
     930#: class-integrations.php:240
     931msgid "Integrate with GenerateBlocks to order Query block results by views."
     932msgstr ""
     933
     934#: class-integrations.php:247
     935msgid "Dynamic Content"
     936msgstr ""
     937
     938#: class-integrations.php:248
     939msgid "Adds post_views ordering to the GenerateBlocks Query block."
     940msgstr ""
     941
     942#: class-integrations.php:255
     943msgid "Integrate with JetEngine plugin to display post views in custom listings and queries."
     944msgstr ""
     945
     946#: class-integrations.php:262
     947msgid "Query Builder"
     948msgstr ""
     949
     950#: class-integrations.php:263
     951msgid "Enables ordering posts by view count in Query Builder posts queries."
     952msgstr ""
     953
     954#: class-integrations.php:267
     955msgid "Listing Grid"
     956msgstr ""
     957
     958#: class-integrations.php:268
     959msgid "Enables ordering posts by view count in Listing Grid, Maps Listing and Calendar widgets."
     960msgstr ""
     961
     962#: class-integrations.php:275
     963msgid "Integrate with Kadence Blocks to order post results by views in the Posts block."
     964msgstr ""
     965
     966#: class-integrations.php:283
     967msgid "Adds post views ordering to the Posts block."
     968msgstr ""
     969
     970#: class-integrations.php:290
     971msgid "Integrate with Polylang to filter reports, exports, and dashboard widgets by language."
     972msgstr ""
     973
     974#: class-integrations.php:297
     975#: class-integrations.php:317
     976msgid "Reports Language Filters"
     977msgstr ""
     978
     979#: class-integrations.php:298
     980#: class-integrations.php:318
     981msgid "Adds language filtering and a language column to Reports tables, charts, and exports."
     982msgstr ""
     983
     984#: class-integrations.php:302
     985#: class-integrations.php:322
     986msgid "Dashboard Widgets"
     987msgstr ""
     988
     989#: class-integrations.php:303
     990#: class-integrations.php:323
     991msgid "Filters dashboard widgets (site, post, and term views) by the selected language."
     992msgstr ""
     993
     994#: class-integrations.php:310
     995msgid "Integrate with WPML to filter reports, exports, and dashboard widgets by language."
     996msgstr ""
     997
     998#: class-settings-display.php:41
     999msgid "Counter Appearance"
     1000msgstr ""
     1001
     1002#: class-settings-display.php:46
     1003msgid "Display Targets"
     1004msgstr ""
     1005
     1006#: class-settings-display.php:51
     1007msgid "Display Audience"
     1008msgstr ""
     1009
     1010#: class-settings-display.php:56
     1011msgid "Admin Interface"
     1012msgstr ""
     1013
     1014#: class-settings-display.php:72
     1015msgid "This custom format currently displays no counter."
     1016msgstr ""
     1017
     1018#: class-settings-display.php:78
     1019msgid "Guests"
     1020msgstr ""
     1021
     1022#: class-settings-display.php:79
     1023msgid "All logged-in users"
     1024msgstr ""
     1025
     1026#: class-settings-display.php:80
     1027msgid "Selected user roles"
     1028msgstr ""
     1029
     1030#: class-settings-display.php:89
     1031msgid "Display Format"
     1032msgstr ""
     1033
     1034#. translators: %s: [post-views] shortcode
     1035#: class-settings-display.php:94
     1036#, php-format
     1037msgid "Choose the format of the counter inserted automatically on the frontend. A manually placed %s shortcode keeps the Views format."
     1038msgstr ""
     1039
     1040#: class-settings-display.php:98
     1041msgid "Custom format"
     1042msgstr ""
     1043
     1044#: class-settings-display.php:103
     1045#: class-settings-display.php:107
     1046msgid "Custom Format"
     1047msgstr ""
     1048
     1049#. translators: 1: views token, 2: icon token
     1050#: class-settings-display.php:109
     1051#, php-format
     1052msgid "Build the counter with %1$s (the view count) and %2$s (the counter icon). Safe HTML is allowed. A format without %1$s displays nothing."
     1053msgstr ""
     1054
     1055#: class-settings-display.php:122
     1056msgid "Views Label"
     1057msgstr ""
     1058
     1059#: class-settings-display.php:126
     1060msgid "Text shown next to the view count."
     1061msgstr ""
     1062
     1063#: class-settings-display.php:147
     1064msgid "Time range used when displaying the number of views."
     1065msgstr ""
     1066
     1067#: class-settings-display.php:155
     1068#: class-widgets.php:207
     1069msgid "Display Style"
     1070msgstr ""
     1071
     1072#: class-settings-display.php:159
     1073msgid "Choose whether to show an icon, label text, or both."
     1074msgstr ""
     1075
     1076#: class-settings-display.php:163
     1077msgid "Icon"
     1078msgstr ""
     1079
     1080#: class-settings-display.php:164
     1081msgid "Label"
     1082msgstr ""
     1083
     1084#: class-settings-display.php:177
     1085msgid "Icon Class"
     1086msgstr ""
     1087
     1088#: class-settings-display.php:197
     1089msgid "Retired. The counter icon no longer uses the Dashicons font. Use the pvc_counter_icon_class filter to supply a custom icon class."
     1090msgstr ""
     1091
     1092#: class-settings-display.php:202
     1093msgid "Position"
     1094msgstr ""
     1095
     1096#: class-settings-display.php:206
     1097#, php-format
     1098msgid "Where to insert the counter automatically. Use %s shortcode for manual placement."
     1099msgstr ""
     1100
     1101#: class-settings-display.php:208
     1102msgid "Before the content"
     1103msgstr ""
     1104
     1105#: class-settings-display.php:209
     1106msgid "After the content"
     1107msgstr ""
     1108
     1109#: class-settings-display.php:210
     1110msgid "Manual only"
     1111msgstr ""
     1112
     1113#: class-settings-display.php:215
     1114msgid "Admin Column"
     1115msgstr ""
     1116
     1117#: class-settings-display.php:219
     1118msgid "Show a \"Views\" column on post and page list screens."
     1119msgstr ""
     1120
     1121#: class-settings-display.php:223
     1122msgid "Admin Edit"
     1123msgstr ""
     1124
     1125#: class-settings-display.php:227
     1126msgid "Allow manual editing of the Views total."
     1127msgstr ""
     1128
     1129#: class-settings-display.php:231
     1130msgid "Dynamic Loading"
     1131msgstr ""
     1132
     1133#: class-settings-display.php:238
     1134msgid "Load the view count dynamically to avoid caching the displayed value."
     1135msgstr ""
     1136
     1137#: class-settings-display.php:243
     1138msgid "Format Number"
     1139msgstr ""
     1140
     1141#: class-settings-display.php:246
     1142msgid "Format the view count according to the site locale (uses the WordPress number_format_i18n function)."
     1143msgstr ""
     1144
     1145#: class-settings-display.php:250
     1146#: class-settings-general.php:135
     1147msgid "Taxonomies"
     1148msgstr ""
     1149
     1150#: class-settings-display.php:257
     1151msgid "Display the view count on category, tag, or custom taxonomy archive pages."
     1152msgstr ""
     1153
     1154#: class-settings-display.php:265
     1155msgid "Select taxonomies where the view counter will be displayed."
     1156msgstr ""
     1157
     1158#: class-settings-display.php:280
     1159#: class-settings-general.php:147
     1160msgid "Author Archives"
     1161msgstr ""
     1162
     1163#: class-settings-display.php:287
     1164msgid "Display the view count on author archive pages."
     1165msgstr ""
     1166
     1167#: class-settings-display.php:292
     1168#: class-settings-general.php:127
     1169#: class-widgets.php:160
     1170msgid "Post Types"
     1171msgstr ""
     1172
     1173#: class-settings-display.php:295
     1174msgid "Select post types where the view counter will be displayed."
     1175msgstr ""
     1176
     1177#: class-settings-display.php:300
     1178msgid "Page Type"
     1179msgstr ""
     1180
     1181#: class-settings-display.php:303
     1182msgid "Select page contexts where the view counter will be displayed."
     1183msgstr ""
     1184
     1185#: class-settings-display.php:307
     1186msgid "Home"
     1187msgstr ""
     1188
     1189#: class-settings-display.php:308
     1190msgid "Archives"
     1191msgstr ""
     1192
     1193#: class-settings-display.php:309
     1194msgid "Single posts and pages"
     1195msgstr ""
     1196
     1197#: class-settings-display.php:310
     1198msgid "Search results"
     1199msgstr ""
     1200
     1201#: class-settings-display.php:316
     1202msgid "User Type"
     1203msgstr ""
     1204
     1205#: class-settings-display.php:319
     1206msgid "Hide the automatically inserted counter from the selected visitor groups. Note: selecting both \"Guests\" and \"All logged-in users\" hides the automatic counter from everyone."
     1207msgstr ""
     1208
     1209#: class-settings-display.php:340
     1210msgid "User Roles"
     1211msgstr ""
     1212
     1213#: class-settings-display.php:343
     1214msgid "Hide the automatically inserted counter from the selected user roles."
     1215msgstr ""
     1216
     1217#: class-settings-display.php:353
     1218msgid "Toolbar Chart"
     1219msgstr ""
     1220
     1221#: class-settings-display.php:356
     1222msgid "A views chart is shown for content types that are being counted."
     1223msgstr ""
     1224
     1225#: class-settings-display.php:357
     1226msgid "Show a views chart in the admin toolbar."
     1227msgstr ""
     1228
     1229#: class-settings-display.php:361
     1230msgid "Menu Position"
     1231msgstr ""
     1232
     1233#: class-settings-display.php:364
     1234msgid "Top menu"
     1235msgstr ""
     1236
     1237#: class-settings-display.php:365
     1238msgid "Settings submenu"
     1239msgstr ""
     1240
     1241#: class-settings-display.php:367
     1242msgid "Choose where the plugin menu appears in the admin sidebar."
     1243msgstr ""
     1244
     1245#: class-settings-display.php:568
     1246msgid "Choose where the counter is inserted and which content types it attaches to."
     1247msgstr ""
     1248
     1249#: class-settings-display.php:577
     1250msgid "Control which visitor groups can see the counter. These rules apply on top of the display targets."
     1251msgstr ""
     1252
     1253#: class-settings-display.php:586
     1254msgid "Adjust the label, period, icon, and formatting used when the counter is rendered."
     1255msgstr ""
     1256
     1257#: class-settings-display.php:595
     1258msgid "Control how view counts are shown and managed in WordPress admin (columns, edit permissions, toolbar chart)."
     1259msgstr ""
     1260
     1261#: class-settings-emails.php:37
     1262msgid "Email Summaries"
     1263msgstr ""
     1264
     1265#: class-settings-emails.php:42
     1266msgid "Email Recipients"
     1267msgstr ""
     1268
     1269#: class-settings-emails.php:47
     1270msgid "Email Template"
     1271msgstr ""
     1272
     1273#: class-settings-emails.php:52
     1274msgid "Test Email"
     1275msgstr ""
     1276
     1277#: class-settings-emails.php:57
     1278msgid "Delivery & Status"
     1279msgstr ""
     1280
     1281#: class-settings-emails.php:73
     1282msgid "Email Summary"
     1283msgstr ""
     1284
     1285#: class-settings-emails.php:76
     1286msgid "Send a summary of tracked content views to the selected recipient."
     1287msgstr ""
     1288
     1289#: class-settings-emails.php:80
     1290msgid "Summary Frequency"
     1291msgstr ""
     1292
     1293#: class-settings-emails.php:89
     1294msgid "Daily"
     1295msgstr ""
     1296
     1297#: class-settings-emails.php:93
     1298msgid "Choose how often views tracking summaries are sent."
     1299msgstr ""
     1300
     1301#: class-settings-emails.php:98
     1302msgid "Minimum Tracked Views"
     1303msgstr ""
     1304
     1305#: class-settings-emails.php:103
     1306msgid "Do not send the summary if the report period has fewer than this many tracked views."
     1307msgstr ""
     1308
     1309#: class-settings-emails.php:109
     1310msgid "Top Content Items"
     1311msgstr ""
     1312
     1313#: class-settings-emails.php:114
     1314msgid "Choose how many top content items are included in each summary."
     1315msgstr ""
     1316
     1317#: class-settings-emails.php:119
     1318msgid "Top Gainers and Decliners"
     1319msgstr ""
     1320
     1321#: class-settings-emails.php:127
     1322msgid "Show which content is gaining or losing momentum compared with previous periods."
     1323msgstr ""
     1324
     1325#: class-settings-emails.php:128
     1326msgid "When matching data is available, this appears in the Traffic signals section if the email template includes %%traffic_signals%%."
     1327msgstr ""
     1328
     1329#: class-settings-emails.php:132
     1330msgid "Author Summary"
     1331msgstr ""
     1332
     1333#: class-settings-emails.php:140
     1334msgid "Include the authors whose content received the most tracked views during the report period."
     1335msgstr ""
     1336
     1337#: class-settings-emails.php:141
     1338msgid "When matching data is available, this appears in the Report summary section if the email template includes %%report_summary%%."
     1339msgstr ""
     1340
     1341#: class-settings-emails.php:145
     1342msgid "Source Summary"
     1343msgstr ""
     1344
     1345#: class-settings-emails.php:153
     1346msgid "Include top referrers or source dimensions when source tracking is available."
     1347msgstr ""
     1348
     1349#: class-settings-emails.php:154
     1350msgid "When source tracking data is available, this appears in the Report summary section if the email template includes %%report_summary%%."
     1351msgstr ""
     1352
     1353#: class-settings-emails.php:159
     1354msgid "Recipient Email"
     1355msgstr ""
     1356
     1357#: class-settings-emails.php:163
     1358msgid "The email address that receives summary emails. Defaults to the site administrator email."
     1359msgstr ""
     1360
     1361#: class-settings-emails.php:168
     1362msgid "Additional Recipients"
     1363msgstr ""
     1364
     1365#: class-settings-emails.php:176
     1366msgid "Send summaries to multiple people with CC and BCC recipients."
     1367msgstr ""
     1368
     1369#: class-settings-emails.php:180
     1370msgid "CC Recipients"
     1371msgstr ""
     1372
     1373#: class-settings-emails.php:195
     1374msgid "Send visible copies to additional recipients."
     1375msgstr ""
     1376
     1377#: class-settings-emails.php:199
     1378msgid "BCC Recipients"
     1379msgstr ""
     1380
     1381#: class-settings-emails.php:214
     1382msgid "Send private copies to additional recipients."
     1383msgstr ""
     1384
     1385#: class-settings-emails.php:219
     1386msgid "Email Subject"
     1387msgstr ""
     1388
     1389#: class-settings-emails.php:229
     1390msgid "Email Content"
     1391msgstr ""
     1392
     1393#: class-settings-emails.php:239
     1394msgid "Send Test To"
     1395msgstr ""
     1396
     1397#: class-settings-emails.php:247
     1398msgid "Schedule Status"
     1399msgstr ""
     1400
     1401#: class-settings-emails.php:250
     1402msgid "Weekly summaries are scheduled for Monday after the previous full week has ended."
     1403msgstr ""
     1404
     1405#: class-settings-emails.php:251
     1406msgid "Summary emails are scheduled with WordPress cron. Delivery time may vary depending on site traffic and hosting configuration."
     1407msgstr ""
     1408
     1409#: class-settings-emails.php:256
     1410msgid "Next Scheduled Email"
     1411msgstr ""
     1412
     1413#: class-settings-emails.php:264
     1414msgid "Last Sent"
     1415msgstr ""
     1416
     1417#: class-settings-emails.php:272
     1418msgid "Last Error"
     1419msgstr ""
     1420
     1421#: class-settings-emails.php:287
     1422msgid "Send email summaries with your most viewed content and view totals."
     1423msgstr ""
     1424
     1425#: class-settings-emails.php:296
     1426msgid "Choose who should receive your email summaries."
     1427msgstr ""
     1428
     1429#: class-settings-emails.php:305
     1430msgid "Review scheduling, delivery, and recent email status information."
     1431msgstr ""
     1432
     1433#: class-settings-emails.php:314
     1434msgid "Customize the subject and message used for content views summaries."
     1435msgstr ""
     1436
     1437#: class-settings-emails.php:324
     1438msgid "Send a test using the latest available summary. This does not change the saved recipient or scheduled delivery."
     1439msgstr ""
     1440
     1441#: class-settings-emails.php:337
     1442msgid "email@example.com"
     1443msgstr ""
     1444
     1445#: class-settings-emails.php:337
     1446msgid "Test email recipient"
     1447msgstr ""
     1448
     1449#: class-settings-emails.php:338
     1450msgid "Send Test Email"
     1451msgstr ""
     1452
     1453#: class-settings-emails.php:338
     1454msgid "Sending..."
     1455msgstr ""
     1456
     1457#: class-settings-emails.php:356
     1458msgid "Sorry, you are not allowed to do that."
     1459msgstr ""
     1460
     1461#: class-settings-emails.php:395
     1462#: class-settings-emails.php:494
     1463msgid "Enter a valid test recipient email address."
     1464msgstr ""
     1465
     1466#: class-settings-emails.php:417
     1467msgid "There is not enough tracked data from the latest complete week to send a real test summary yet."
     1468msgstr ""
     1469
     1470#: class-settings-emails.php:437
     1471msgid "The summary test email was sent successfully."
     1472msgstr ""
     1473
     1474#: class-settings-emails.php:445
     1475#, php-format
     1476msgid "The test email could not be sent. %s"
     1477msgstr ""
     1478
     1479#: class-settings-emails.php:446
     1480msgid "The test email could not be sent. Check your WordPress mail configuration and try again."
     1481msgstr ""
     1482
     1483#: class-settings-emails.php:463
     1484msgid "Enter a valid recipient email address for the views summary email."
     1485msgstr ""
     1486
     1487#: class-settings-emails.php:469
     1488msgid "Recipient email is required while the views summary email is enabled."
     1489msgstr ""
     1490
     1491#: class-settings-emails.php:559
     1492msgid "Minimum tracked views cannot be lower than 0."
     1493msgstr ""
     1494
     1495#: class-settings-emails.php:588
     1496#, php-format
     1497msgid "%1$s contained tags that are not available in this field. Removed tags: %2$s. Supported tags here: %3$s"
     1498msgstr ""
     1499
     1500#: class-settings-emails.php:711
     1501msgid "Customize the subject line for summary emails."
     1502msgstr ""
     1503
     1504#: class-settings-emails.php:713
     1505#, php-format
     1506msgid "Use supported subject tags such as %1$s and %2$s."
     1507msgstr ""
     1508
     1509#: class-settings-emails.php:727
     1510msgid "Customize the main message sent with each email summary."
     1511msgstr ""
     1512
     1513#: class-settings-emails.php:729
     1514#, php-format
     1515msgid "Use supported content tags such as %s."
     1516msgstr ""
     1517
     1518#: class-settings-emails.php:814
     1519msgid "Not sent yet."
     1520msgstr ""
     1521
     1522#: class-settings-emails.php:831
     1523msgid "Not scheduled."
     1524msgstr ""
     1525
     1526#: class-settings-emails.php:844
     1527msgid "No email errors recorded yet."
     1528msgstr ""
     1529
     1530#: class-settings-general.php:54
     1531msgid "PHP"
     1532msgstr ""
     1533
     1534#: class-settings-general.php:55
     1535msgid "JavaScript"
     1536msgstr ""
     1537
     1538#: class-settings-general.php:56
     1539msgid "REST API"
     1540msgstr ""
     1541
     1542#: class-settings-general.php:57
     1543msgid "Fast AJAX"
     1544msgstr ""
     1545
     1546#: class-settings-general.php:72
     1547msgid "Tracking Targets"
     1548msgstr ""
     1549
     1550#: class-settings-general.php:77
     1551msgid "Tracking Behavior"
     1552msgstr ""
     1553
     1554#: class-settings-general.php:82
     1555msgid "Visitor Exclusions"
     1556msgstr ""
     1557
     1558#: class-settings-general.php:87
     1559msgid "Performance & Caching"
     1560msgstr ""
     1561
     1562#: class-settings-general.php:101
     1563#: class-settings-general.php:452
     1564msgid "minutes"
     1565msgstr ""
     1566
     1567#: class-settings-general.php:102
     1568#: class-settings-general.php:217
     1569msgid "hours"
     1570msgstr ""
     1571
     1572#: class-settings-general.php:103
     1573msgid "days"
     1574msgstr ""
     1575
     1576#: class-settings-general.php:104
     1577msgid "weeks"
     1578msgstr ""
     1579
     1580#: class-settings-general.php:105
     1581msgid "months"
     1582msgstr ""
     1583
     1584#: class-settings-general.php:106
     1585msgid "years"
     1586msgstr ""
     1587
     1588#: class-settings-general.php:111
     1589msgid "crawlers"
     1590msgstr ""
     1591
     1592#: class-settings-general.php:112
     1593msgid "AI bots"
     1594msgstr ""
     1595
     1596#: class-settings-general.php:113
     1597msgid "logged in users"
     1598msgstr ""
     1599
     1600#: class-settings-general.php:114
     1601msgid "guests"
     1602msgstr ""
     1603
     1604#: class-settings-general.php:115
     1605msgid "selected user roles"
     1606msgstr ""
     1607
     1608#: class-settings-general.php:130
     1609msgid "Select post types whose views should be counted."
     1610msgstr ""
     1611
     1612#: class-settings-general.php:138
     1613msgid "Enable counting views on taxonomy term archive pages."
     1614msgstr ""
     1615
     1616#: class-settings-general.php:150
     1617msgid "Enable counting views on author archive pages."
     1618msgstr ""
     1619
     1620#: class-settings-general.php:159
     1621msgid "Other Pages"
     1622msgstr ""
     1623
     1624#: class-settings-general.php:162
     1625msgid "Track views on the front page, post type archives, date archives, search results, 404 pages, and WordPress authentication pages such as login, registration, and password reset."
     1626msgstr ""
     1627
     1628#: class-settings-general.php:171
     1629msgid "Traffic Sources"
     1630msgstr ""
     1631
     1632#: class-settings-general.php:174
     1633msgid "Collect aggregate stats about visitors' browsers, devices, operating systems and referrers."
     1634msgstr ""
     1635
     1636#: class-settings-general.php:183
     1637msgid "Counter Mode"
     1638msgstr ""
     1639
     1640#: class-settings-general.php:186
     1641msgid "Choose how views are recorded. If you use caching, select JavaScript, REST API or Fast AJAX (up to <code>10+</code> times faster)."
     1642msgstr ""
     1643
     1644#: class-settings-general.php:194
     1645msgid "Data Storage"
     1646msgstr ""
     1647
     1648#: class-settings-general.php:199
     1649msgid "Choose how to store the content views data in the user's browser - with or without cookies."
     1650msgstr ""
     1651
     1652#: class-settings-general.php:202
     1653msgid "Cookies"
     1654msgstr ""
     1655
     1656#: class-settings-general.php:203
     1657msgid "Cookieless"
     1658msgstr ""
     1659
     1660#: class-settings-general.php:210
     1661msgid "Count Interval"
     1662msgstr ""
     1663
     1664#: class-settings-general.php:223
     1665msgid "Count Time"
     1666msgstr ""
     1667
     1668#: class-settings-general.php:229
     1669msgid "Whether to store the views using GMT timezone or adjust it to the GMT offset of the site."
     1670msgstr ""
     1671
     1672#: class-settings-general.php:231
     1673msgid "GMT Time"
     1674msgstr ""
     1675
     1676#: class-settings-general.php:232
     1677msgid "Local Time"
     1678msgstr ""
     1679
     1680#: class-settings-general.php:238
     1681msgid "Strict Counts"
     1682msgstr ""
     1683
     1684#: class-settings-general.php:246
     1685msgid "Prevent bypassing the count interval (for example by using incognito mode or clearing cookies)."
     1686msgstr ""
     1687
     1688#: class-settings-general.php:251
     1689msgid "Cleanup Interval"
     1690msgstr ""
     1691
     1692#: class-settings-general.php:254
     1693#, php-format
     1694msgid "Delete daily content view data older than the period specified above. Enter %s to keep data regardless of age. Cleanup runs once per day."
     1695msgstr ""
     1696
     1697#: class-settings-general.php:263
     1698msgid "Caching Compatibility"
     1699msgstr ""
     1700
     1701#: class-settings-general.php:278
     1702msgid "Object Cache Support"
     1703msgstr ""
     1704
     1705#: class-settings-general.php:293
     1706msgid "Exclude Visitors"
     1707msgstr ""
     1708
     1709#: class-settings-general.php:296
     1710msgid "Use this to exclude specific visitor groups from counting views."
     1711msgstr ""
     1712
     1713#: class-settings-general.php:307
     1714msgid "Exclude User Roles"
     1715msgstr ""
     1716
     1717#: class-settings-general.php:310
     1718msgid "Use this to exclude specific user roles from counting views."
     1719msgstr ""
     1720
     1721#: class-settings-general.php:326
     1722msgid "Exclude IPs"
     1723msgstr ""
     1724
     1725#: class-settings-general.php:375
     1726msgid "Minimum time between counting new views from the same visitor, in hours. Enter <code>0</code> to count every page view."
     1727msgstr ""
     1728
     1729#: class-settings-general.php:453
     1730msgid "Persistent Object Cache"
     1731msgstr ""
     1732
     1733#: class-settings-general.php:453
     1734msgid "available"
     1735msgstr ""
     1736
     1737#. translators: 1: read state, 2: reason code
     1738#. translators: 1: live write state, 2: reason code
     1739#: class-settings-general.php:453
     1740#: class-visits.php:1874
     1741#: class-visits.php:1875
     1742msgid "unavailable"
     1743msgstr ""
     1744
     1745#: class-settings-general.php:454
     1746#, php-format
     1747msgid "How often to flush cached view counts from the object cache into the database. This feature is used only if a persistent object cache like %s or %s is detected and the interval is greater than %s. When used, view counts will be collected and stored in the object cache instead of the database and will then be asynchronously flushed to the database according to the specified interval. The maximum value is %s which means 24 hours.%sNotice:%s Potential data loss may occur if the object cache is cleared/unavailable for the duration of the interval."
     1748msgstr ""
     1749
     1750#: class-settings-general.php:533
     1751#: class-settings-general.php:539
     1752msgid "Remove"
     1753msgstr ""
     1754
     1755#: class-settings-general.php:547
     1756msgid "Add new"
     1757msgstr ""
     1758
     1759#: class-settings-general.php:547
     1760msgid "Add my current IP"
     1761msgstr ""
     1762
     1763#: class-settings-general.php:550
     1764msgid "Add IPv4 or IPv6 addresses to exclude them from counting views. Wildcards are supported for IPv4 only (e.g. 192.168.0.*)."
     1765msgstr ""
     1766
     1767#: class-settings-general.php:589
     1768msgid "Control which post types, archives and other content types are included in view counting."
     1769msgstr ""
     1770
     1771#: class-settings-general.php:598
     1772msgid "Control how views are recorded — counting mode, intervals, time zone, and cleanup."
     1773msgstr ""
     1774
     1775#: class-settings-general.php:607
     1776msgid "Exclude specific visitor groups or IP addresses from incrementing view counts."
     1777msgstr ""
     1778
     1779#: class-settings-general.php:616
     1780msgid "Configure caching compatibility and object-cache handling for counting."
     1781msgstr ""
     1782
     1783#: class-settings-general.php:631
     1784msgid "Currently detected active caching plugins"
     1785msgstr ""
     1786
     1787#: class-settings-general.php:639
     1788msgid "No compatible caching plugins found."
     1789msgstr ""
     1790
     1791#: class-settings-general.php:651
     1792msgid "Enable compatibility tweaks for supported caching plugins."
     1793msgstr ""
     1794
     1795#: class-settings-general.php:655
     1796#: class-settings-general.php:671
     1797#: class-settings-integrations.php:97
     1798msgid "(available)"
     1799msgstr ""
     1800
     1801#: class-settings-general.php:667
     1802msgid "Enable Redis or Memcached object cache optimization."
     1803msgstr ""
     1804
     1805#: class-settings-general.php:704
     1806msgid "Persistent object cache has been detected."
     1807msgstr ""
     1808
     1809#: class-settings-general.php:707
     1810#, php-format
     1811msgid "This feature requires a persistent object cache like %s or %s to be installed and activated."
     1812msgstr ""
     1813
     1814#: class-settings-integrations.php:22
     1815#: class-settings.php:653
     1816#: class-settings.php:721
     1817#: class-settings.php:722
     1818msgid "Integrations"
     1819msgstr ""
     1820
     1821#: class-settings-integrations.php:65
     1822msgid "Manage available Post Views Counter integrations."
     1823msgstr ""
     1824
     1825#: class-settings-other.php:36
     1826msgid "Plugin Status"
     1827msgstr ""
     1828
     1829#: class-settings-other.php:41
     1830msgid "Data Import"
     1831msgstr ""
     1832
     1833#: class-settings-other.php:46
     1834msgid "Data Removal"
     1835msgstr ""
     1836
     1837#: class-settings-other.php:61
     1838msgid "License Key"
     1839msgstr ""
     1840
     1841#: class-settings-other.php:66
     1842#, php-format
     1843msgid "Enter your %s license key (requires Pro version to be installed and active)."
     1844msgstr ""
     1845
     1846#: class-settings-other.php:73
     1847msgid "Import From"
     1848msgstr ""
     1849
     1850#: class-settings-other.php:81
     1851msgid "Import Strategy"
     1852msgstr ""
     1853
     1854#: class-settings-other.php:92
     1855msgid "Click Analyse Views to check how many views are available for import, or Import Views to begin the import process."
     1856msgstr ""
     1857
     1858#: class-settings-other.php:98
     1859#: class-settings-other.php:275
     1860msgid "Delete Views"
     1861msgstr ""
     1862
     1863#: class-settings-other.php:101
     1864msgid "Delete ALL the existing post views data. Note that this is an irreversible process!"
     1865msgstr ""
     1866
     1867#: class-settings-other.php:107
     1868msgid "Delete on Deactivation"
     1869msgstr ""
     1870
     1871#: class-settings-other.php:110
     1872msgid "When enabled, deactivating the plugin will delete all plugin data from the database, including all content view counts."
     1873msgstr ""
     1874
     1875#: class-settings-other.php:111
     1876msgid "Delete all plugin data on deactivation."
     1877msgstr ""
     1878
     1879#: class-settings-other.php:156
     1880#, php-format
     1881msgid "%s is not currently available. Please install and activate the required plugin."
     1882msgstr ""
     1883
     1884#: class-settings-other.php:166
     1885msgid "Choose a data source to import existing view counts from."
     1886msgstr ""
     1887
     1888#: class-settings-other.php:178
     1889#, php-format
     1890msgid "%s is not available. Please install and activate the required plugin to use this import source."
     1891msgstr ""
     1892
     1893#: class-settings-other.php:237
     1894msgid "Choose how to handle existing view counts when importing."
     1895msgstr ""
     1896
     1897#: class-settings-other.php:262
     1898msgid "Analyse Views"
     1899msgstr ""
     1900
     1901#: class-settings-other.php:263
     1902msgid "Import Views"
     1903msgstr ""
     1904
     1905#: class-settings-other.php:286
     1906msgid "View license details and other status information."
     1907msgstr ""
     1908
     1909#: class-settings-other.php:326
     1910msgid "Active"
     1911msgstr ""
     1912
     1913#: class-settings-other.php:328
     1914msgid "Not Detected"
     1915msgstr ""
     1916
     1917#. translators: 1: Core version, 2: extension version
     1918#: class-settings-other.php:371
     1919#: class-visits.php:1884
     1920msgid "unknown"
     1921msgstr ""
     1922
     1923#: class-settings-other.php:385
     1924msgid "Plugin Version"
     1925msgstr ""
     1926
     1927#: class-settings-other.php:393
     1928msgid "Database Tables"
     1929msgstr ""
     1930
     1931#: class-settings-other.php:474
     1932msgid "Import view counts when migrating from custom meta key or other analytics plugins."
     1933msgstr ""
     1934
     1935#: class-settings-other.php:483
     1936msgid "Choose what happens to stored view data on uninstall, and manage other removal-related tools."
     1937msgstr ""
     1938
     1939#: class-settings-reports.php:57
     1940msgid "Display Reports and Export Views to CSV/XML"
     1941msgstr ""
     1942
     1943#: class-settings-reports.php:58
     1944msgid "View detailed stats about the popularity of your content."
     1945msgstr ""
     1946
     1947#: class-settings-reports.php:59
     1948msgid "Generate views reports in any date range you need."
     1949msgstr ""
     1950
     1951#: class-settings-reports.php:60
     1952msgid "Export, download and share your website views data."
     1953msgstr ""
     1954
     1955#: class-settings-reports.php:61
     1956#: class-settings.php:192
     1957msgid "Upgrade to Pro"
     1958msgstr ""
     1959
     1960#: class-settings.php:193
     1961msgid "Starting from $29 per year"
     1962msgstr ""
     1963
     1964#: class-settings.php:193
     1965msgid "14-day money back guarantee."
     1966msgstr ""
     1967
     1968#: class-settings.php:210
     1969msgid "Reliable view counting"
     1970msgstr ""
     1971
     1972#: class-settings.php:212
     1973msgid "Accurate counts with caching enabled"
     1974msgstr ""
     1975
     1976#: class-settings.php:213
     1977msgid "Better performance under high traffic"
     1978msgstr ""
     1979
     1980#: class-settings.php:214
     1981msgid "Protection against fake and repeated views"
     1982msgstr ""
     1983
     1984#: class-settings.php:216
     1985msgid "Upgrade to Pro to make your view counts reliable on real-world, cached WordPress sites."
     1986msgstr ""
     1987
     1988#: class-settings.php:219
     1989msgid "Accurate & meaningful view display"
     1990msgstr ""
     1991
     1992#: class-settings.php:221
     1993msgid "Choose which time period is displayed"
     1994msgstr ""
     1995
     1996#: class-settings.php:222
     1997msgid "Always show up-to-date view counts"
     1998msgstr ""
     1999
     2000#: class-settings.php:223
     2001msgid "Control where the counter appears"
     2002msgstr ""
     2003
     2004#: class-settings.php:225
     2005msgid "Upgrade to Pro to control what view data visitors actually see - not just total counts."
     2006msgstr ""
     2007
     2008#: class-settings.php:228
     2009msgid "Advanced view reports"
     2010msgstr ""
     2011
     2012#: class-settings.php:230
     2013msgid "Upgrade to Pro to access detailed reports and visual insights for your content."
     2014msgstr ""
     2015
     2016#: class-settings.php:233
     2017msgid "Use view counts where they actually matter"
     2018msgstr ""
     2019
     2020#: class-settings.php:235
     2021msgid "Display and sort content by popularity"
     2022msgstr ""
     2023
     2024#: class-settings.php:236
     2025msgid "Works with the tools you already use"
     2026msgstr ""
     2027
     2028#: class-settings.php:237
     2029msgid "No custom queries or advanced setup"
     2030msgstr ""
     2031
     2032#: class-settings.php:239
     2033msgid "Upgrade to Pro to use view data across layouts and blocks - not just store it."
     2034msgstr ""
     2035
     2036#: class-settings.php:242
     2037msgid "Deeper email summaries"
     2038msgstr ""
     2039
     2040#: class-settings.php:244
     2041msgid "Spot content gaining or losing momentum"
     2042msgstr ""
     2043
     2044#: class-settings.php:245
     2045msgid "Include author and source summaries"
     2046msgstr ""
     2047
     2048#: class-settings.php:246
     2049msgid "Send daily or monthly summaries"
     2050msgstr ""
     2051
     2052#: class-settings.php:247
     2053msgid "Add CC/BCC recipients for your team"
     2054msgstr ""
     2055
     2056#: class-settings.php:249
     2057msgid "Go beyond a weekly top-content list. Pro adds richer summaries with trends, authors, sources, and flexible delivery options."
     2058msgstr ""
     2059
     2060#: class-settings.php:252
     2061msgid "Migrate your view data safely"
     2062msgstr ""
     2063
     2064#: class-settings.php:254
     2065msgid "Import view data from other WordPress plugins"
     2066msgstr ""
     2067
     2068#: class-settings.php:255
     2069msgid "Choose how existing data is handled"
     2070msgstr ""
     2071
     2072#: class-settings.php:256
     2073msgid "Support for posts and other content types"
     2074msgstr ""
     2075
     2076#: class-settings.php:258
     2077msgid "Upgrade to Pro to migrate historical view data without losing control over existing counts."
     2078msgstr ""
     2079
     2080#: class-settings.php:638
     2081#: class-settings.php:691
     2082#: class-settings.php:692
     2083msgid "Counting"
     2084msgstr ""
     2085
     2086#: class-settings.php:643
     2087#: class-settings.php:701
     2088#: class-settings.php:702
     2089msgid "Display"
     2090msgstr ""
     2091
     2092#: class-settings.php:648
     2093#: class-settings.php:711
     2094#: class-settings.php:712
     2095#: class-settings.php:754
     2096msgid "Reports"
     2097msgstr ""
     2098
     2099#: class-settings.php:658
     2100#: class-settings.php:731
     2101#: class-settings.php:732
     2102msgid "Emails"
     2103msgstr ""
     2104
     2105#: class-settings.php:663
     2106#: class-settings.php:741
     2107#: class-settings.php:742
     2108msgid "Other"
     2109msgstr ""
     2110
     2111#: class-settings.php:1004
     2112msgid "Queued counts cannot be retired safely. Update or deactivate Post Views Counter Pro before retrying."
     2113msgstr ""
     2114
     2115#: class-settings.php:1012
     2116#: class-settings.php:1071
     2117msgid "Error occurred. All existing data were not deleted."
     2118msgstr ""
     2119
     2120#: class-settings.php:1067
     2121msgid "All existing data deleted successfully."
     2122msgstr ""
     2123
     2124#: class-settings.php:1069
     2125msgid "Visits state could not be reset safely, so no data was deleted."
     2126msgstr ""
     2127
     2128#: class-settings.php:1170
     2129msgid "NEW!"
     2130msgstr ""
     2131
     2132#: class-toolbar.php:134
     2133#, php-format
     2134msgid "%s post view"
     2135msgid_plural "%s post views"
     2136msgstr[0] ""
     2137msgstr[1] ""
     2138
     2139#. translators: 1: month and year, 2: total number of post views
     2140#: class-toolbar.php:140
     2141#, php-format
     2142msgid "Post views chart for %1$s, %2$s total"
     2143msgstr ""
     2144
     2145#: class-traffic-signals.php:124
     2146msgid "Traffic Signals"
     2147msgstr ""
     2148
     2149#: class-traffic-signals.php:166
     2150msgid "No unusual activity detected."
     2151msgstr ""
     2152
     2153#: class-traffic-signals.php:172
     2154msgid "Unusual traffic pattern detected. More insights available in Post Views Counter Pro."
     2155msgstr ""
     2156
     2157#: class-update.php:241
     2158msgid "<strong>Post Views Counter</strong> - this version requires a database update. Make sure to back up your database first."
     2159msgstr ""
     2160
     2161#: class-update.php:242
     2162msgid "Run the Update"
     2163msgstr ""
     2164
     2165#: class-update.php:336
     2166msgid "Thank you! Datebase was successfully updated."
     2167msgstr ""
     2168
     2169#: class-visits.php:1872
     2170msgid "Visits Readiness"
     2171msgstr ""
     2172
     2173#. translators: 1: read state, 2: reason code
     2174#: class-visits.php:1874
     2175#, php-format
     2176msgid "Read: %1$s (%2$s)"
     2177msgstr ""
     2178
     2179#. translators: 1: read state, 2: reason code
     2180#. translators: 1: live write state, 2: reason code
     2181#: class-visits.php:1874
     2182#: class-visits.php:1875
     2183msgid "ready"
     2184msgstr ""
     2185
     2186#. translators: 1: live write state, 2: reason code
     2187#: class-visits.php:1875
     2188#, php-format
     2189msgid "Live write: %1$s (%2$s)"
     2190msgstr ""
     2191
     2192#. translators: 1: schema state, 2: schema status code
     2193#: class-visits.php:1876
     2194#, php-format
     2195msgid "Schema: %1$s (%2$s)"
     2196msgstr ""
     2197
     2198#. translators: derived metrics start timestamp or none
     2199#: class-visits.php:1877
     2200#, php-format
     2201msgid "Derived metrics since: %s"
     2202msgstr ""
     2203
     2204#. translators: derived metrics start timestamp or none
     2205#: class-visits.php:1877
     2206msgid "none"
     2207msgstr ""
     2208
     2209#. translators: 1: last migration attempt timestamp or never, 2: last compatibility check timestamp or never
     2210#: class-visits.php:1880
     2211#, php-format
     2212msgid "Checks: migration attempt %1$s; compatibility %2$s"
     2213msgstr ""
     2214
     2215#: class-visits.php:1881
     2216#: class-visits.php:1882
     2217msgid "never"
     2218msgstr ""
     2219
     2220#. translators: 1: Core version, 2: extension version
     2221#: class-visits.php:1884
     2222#, php-format
     2223msgid "Versions: Core %1$s; Pro %2$s"
     2224msgstr ""
     2225
     2226#. translators: 1: Core version, 2: extension version
     2227#: class-visits.php:1884
     2228msgid "inactive"
     2229msgstr ""
     2230
     2231#: class-widgets.php:55
     2232#: class-widgets.php:63
     2233msgid "Most Viewed Posts"
     2234msgstr ""
     2235
     2236#: class-widgets.php:57
     2237msgid "Displays a list of the most viewed posts"
     2238msgstr ""
     2239
     2240#: class-widgets.php:74
     2241msgid "No most viewed posts found"
     2242msgstr ""
     2243
     2244#: class-widgets.php:79
     2245#: blocks/most-viewed-posts/index.js:1
     2246#: src/blocks/most-viewed-posts/edit.js:98
     2247msgid "Ascending"
     2248msgstr ""
     2249
     2250#: class-widgets.php:80
     2251#: blocks/most-viewed-posts/index.js:1
     2252#: src/blocks/most-viewed-posts/edit.js:99
     2253msgid "Descending"
     2254msgstr ""
     2255
     2256#: class-widgets.php:90
     2257#: blocks/most-viewed-posts/index.js:1
     2258#: src/blocks/most-viewed-posts/edit.js:108
     2259msgid "Unordered list"
     2260msgstr ""
     2261
     2262#: class-widgets.php:91
     2263#: blocks/most-viewed-posts/index.js:1
     2264#: src/blocks/most-viewed-posts/edit.js:111
     2265msgid "Ordered list"
     2266msgstr ""
     2267
     2268#: class-widgets.php:156
     2269#: blocks/most-viewed-posts/index.js:1
     2270#: src/blocks/most-viewed-posts/edit.js:43
     2271msgid "Title"
     2272msgstr ""
     2273
     2274#: class-widgets.php:186
     2275#: blocks/most-viewed-posts/index.js:1
     2276#: src/blocks/most-viewed-posts/edit.js:84
     2277msgid "Number of posts to show"
     2278msgstr ""
     2279
     2280#: class-widgets.php:190
     2281#: blocks/most-viewed-posts/index.js:1
     2282#: src/blocks/most-viewed-posts/edit.js:90
     2283msgid "No posts message"
     2284msgstr ""
     2285
     2286#: class-widgets.php:194
     2287#: blocks/most-viewed-posts/index.js:1
     2288#: src/blocks/most-viewed-posts/edit.js:95
     2289msgid "Order"
     2290msgstr ""
     2291
     2292#: class-widgets.php:220
     2293msgid "Display post views?"
     2294msgstr ""
     2295
     2296#: class-widgets.php:222
     2297msgid "Display post excerpt?"
     2298msgstr ""
     2299
     2300#: class-widgets.php:224
     2301msgid "Display post author?"
     2302msgstr ""
     2303
     2304#: class-widgets.php:226
     2305msgid "Display post thumbnail?"
     2306msgstr ""
     2307
     2308#: class-widgets.php:229
     2309#: blocks/most-viewed-posts/index.js:1
     2310#: src/blocks/most-viewed-posts/edit.js:146
     2311msgid "Thumbnail size"
     2312msgstr ""
     2313
     2314#. Plugin URI of the plugin
     2315#: post-views-counter.php
     2316msgid "https://postviewscounter.com/"
     2317msgstr ""
     2318
     2319#. Description of the plugin
     2320#: post-views-counter.php
     2321msgid "Count and display post views, track site-wide visits, and see what content works – inside WordPress. Fast, easy to use and privacy-first."
     2322msgstr ""
     2323
     2324#. Author of the plugin
     2325#: post-views-counter.php
     2326msgid "dFactory"
     2327msgstr ""
     2328
     2329#. Author URI of the plugin
     2330#: post-views-counter.php
     2331msgid "https://dfactory.co/"
     2332msgstr ""
     2333
     2334#: blocks/most-viewed-posts/index.js:1
     2335#: blocks/post-views/index.js:1
    232336#: src/blocks/utils.js:13
    242337msgid "Something went wrong. Try again or refresh the page."
    252338msgstr ""
    262339
    27 #: blocks/most-viewed-posts/index.js:1 blocks/post-views/index.js:1
    28 #: post-views-counter.php:1325 src/blocks/most-viewed-posts/edit.js:40
     2340#: blocks/most-viewed-posts/index.js:1
     2341#: blocks/post-views/index.js:1
     2342#: src/blocks/most-viewed-posts/edit.js:40
    292343#: src/blocks/post-views/edit.js:14
     2344#: post-views-counter.php:1500
    302345msgid "Settings"
    312346msgstr ""
    322347
    33 #: blocks/most-viewed-posts/index.js:1 includes/class-widgets.php:156
    34 #: src/blocks/most-viewed-posts/edit.js:43
    35 msgid "Title"
    36 msgstr ""
    37 
    38 #: blocks/most-viewed-posts/index.js:1 src/blocks/most-viewed-posts/edit.js:46
     2348#: blocks/most-viewed-posts/index.js:1
     2349#: src/blocks/most-viewed-posts/edit.js:46
    392350msgid "Enter empty text to hide title."
    402351msgstr ""
    412352
    42 #: blocks/most-viewed-posts/index.js:1 src/blocks/most-viewed-posts/edit.js:50
     2353#: blocks/most-viewed-posts/index.js:1
     2354#: src/blocks/most-viewed-posts/edit.js:50
    432355msgid "Post types"
    442356msgstr ""
    452357
    46 #: blocks/most-viewed-posts/index.js:1 blocks/post-views/index.js:1
    47 #: src/blocks/most-viewed-posts/edit.js:77 src/blocks/post-views/edit.js:25
     2358#: blocks/most-viewed-posts/index.js:1
     2359#: blocks/post-views/index.js:1
     2360#: src/blocks/most-viewed-posts/edit.js:77
     2361#: src/blocks/post-views/edit.js:25
    482362msgid "Views period"
    492363msgstr ""
    502364
    51 #: blocks/most-viewed-posts/index.js:1 includes/class-widgets.php:186
    52 #: src/blocks/most-viewed-posts/edit.js:84
    53 msgid "Number of posts to show"
    54 msgstr ""
    55 
    56 #: blocks/most-viewed-posts/index.js:1 includes/class-widgets.php:190
    57 #: src/blocks/most-viewed-posts/edit.js:90
    58 msgid "No posts message"
    59 msgstr ""
    60 
    61 #: blocks/most-viewed-posts/index.js:1 includes/class-widgets.php:194
    62 #: src/blocks/most-viewed-posts/edit.js:95
    63 msgid "Order"
    64 msgstr ""
    65 
    66 #: blocks/most-viewed-posts/index.js:1 includes/class-widgets.php:79
    67 #: src/blocks/most-viewed-posts/edit.js:98
    68 msgid "Ascending"
    69 msgstr ""
    70 
    71 #: blocks/most-viewed-posts/index.js:1 includes/class-widgets.php:80
    72 #: src/blocks/most-viewed-posts/edit.js:99
    73 msgid "Descending"
    74 msgstr ""
    75 
    76 #: blocks/most-viewed-posts/index.js:1 src/blocks/most-viewed-posts/edit.js:104
     2365#: blocks/most-viewed-posts/index.js:1
     2366#: src/blocks/most-viewed-posts/edit.js:104
    772367msgid "Display style"
    782368msgstr ""
    792369
    80 #: blocks/most-viewed-posts/index.js:1 includes/class-widgets.php:90
    81 #: src/blocks/most-viewed-posts/edit.js:108
    82 msgid "Unordered list"
    83 msgstr ""
    84 
    85 #: blocks/most-viewed-posts/index.js:1 includes/class-widgets.php:91
    86 #: src/blocks/most-viewed-posts/edit.js:111
    87 msgid "Ordered list"
    88 msgstr ""
    89 
    90 #: blocks/most-viewed-posts/index.js:1 src/blocks/most-viewed-posts/edit.js:117
     2370#: blocks/most-viewed-posts/index.js:1
     2371#: src/blocks/most-viewed-posts/edit.js:117
    912372msgid "Display Data"
    922373msgstr ""
    932374
    94 #: blocks/most-viewed-posts/index.js:1 src/blocks/most-viewed-posts/edit.js:121
     2375#: blocks/most-viewed-posts/index.js:1
     2376#: src/blocks/most-viewed-posts/edit.js:121
    952377msgid "Post views"
    962378msgstr ""
    972379
    98 #: blocks/most-viewed-posts/index.js:1 src/blocks/most-viewed-posts/edit.js:127
     2380#: blocks/most-viewed-posts/index.js:1
     2381#: src/blocks/most-viewed-posts/edit.js:127
    992382msgid "Post excerpt"
    1002383msgstr ""
    1012384
    102 #: blocks/most-viewed-posts/index.js:1 src/blocks/most-viewed-posts/edit.js:133
     2385#: blocks/most-viewed-posts/index.js:1
     2386#: src/blocks/most-viewed-posts/edit.js:133
    1032387msgid "Post author"
    1042388msgstr ""
    1052389
    106 #: blocks/most-viewed-posts/index.js:1 src/blocks/most-viewed-posts/edit.js:139
     2390#: blocks/most-viewed-posts/index.js:1
     2391#: src/blocks/most-viewed-posts/edit.js:139
    1072392msgid "Post thumbnail"
    1082393msgstr ""
    1092394
    110 #: blocks/most-viewed-posts/index.js:1 includes/class-widgets.php:229
    111 #: src/blocks/most-viewed-posts/edit.js:146
    112 msgid "Thumbnail size"
    113 msgstr ""
    114 
    115 #: blocks/post-views/index.js:1 src/blocks/post-views/edit.js:17
     2395#: blocks/post-views/index.js:1
     2396#: src/blocks/post-views/edit.js:17
    1162397msgid "Post ID"
    1172398msgstr ""
    1182399
    119 #: blocks/post-views/index.js:1 src/blocks/post-views/edit.js:20
     2400#: blocks/post-views/index.js:1
     2401#: src/blocks/post-views/edit.js:20
    1202402msgid "Enter 0 to use current visited post."
    1212403msgstr ""
    1222404
    123 #: blocks/pro-placeholder/index.js:1 src/blocks/pro-placeholder/index.js:47
     2405#: blocks/pro-placeholder/index.js:1
     2406#: src/blocks/pro-placeholder/index.js:47
    1242407msgid "This block requires Post Views Counter Pro."
    1252408msgstr ""
    1262409
    127 #: blocks/pro-placeholder/index.js:1 src/blocks/pro-placeholder/index.js:57
     2410#: blocks/pro-placeholder/index.js:1
     2411#: src/blocks/pro-placeholder/index.js:57
    1282412msgid "Learn More"
    1292413msgstr ""
    1302414
    131 #: includes/class-admin.php:75 includes/class-admin.php:79
    132 #: includes/class-admin.php:112 includes/class-admin.php:116
    133 msgid "You are not allowed to edit this item."
    134 msgstr ""
    135 
    136 #: includes/class-admin.php:108 includes/class-columns-modal.php:101
    137 #: includes/class-counter.php:520
    138 msgid "Invalid post ID."
    139 msgstr ""
    140 
    141 #: includes/class-admin.php:172 includes/class-columns.php:80
    142 #: includes/class-columns.php:283 includes/class-columns.php:378
    143 #: includes/class-dashboard.php:61 includes/class-dashboard.php:106
    144 #: includes/class-settings.php:633
     2415#: src/js/integration-gutenberg.jsx:265
     2416#: src/js/integration-gutenberg.jsx:398
     2417msgid "Most viewed"
     2418msgstr ""
     2419
     2420#: src/js/integration-gutenberg.jsx:266
     2421#: src/js/integration-gutenberg.jsx:399
     2422msgid "Least viewed"
     2423msgstr ""
     2424
     2425#: src/js/integration-gutenberg.jsx:294
     2426#: src/js/integration-gutenberg.jsx:499
     2427msgid "Post Views Settings"
     2428msgstr ""
     2429
     2430#: src/js/integration-gutenberg.jsx:299
     2431#: src/js/integration-gutenberg.jsx:504
     2432msgid "Include posts with zero views"
     2433msgstr ""
     2434
     2435#: src/js/integration-gutenberg.jsx:306
     2436#: src/js/integration-gutenberg.jsx:509
     2437msgid "When enabled, posts with no views will be included in the results."
     2438msgstr ""
     2439
     2440#: blocks/most-viewed-posts/block.json
     2441#: src/blocks/most-viewed-posts/block.json
     2442msgctxt "block title"
     2443msgid "Most Viewed Posts"
     2444msgstr ""
     2445
     2446#: blocks/most-viewed-posts/block.json
     2447#: src/blocks/most-viewed-posts/block.json
     2448msgctxt "block description"
     2449msgid "Displays a list of the most viewed posts."
     2450msgstr ""
     2451
     2452#: blocks/most-viewed-posts/block.json
     2453#: blocks/post-views/block.json
     2454#: blocks/pro-placeholder/most-viewed-terms/block.json
     2455#: blocks/pro-placeholder/most-viewed-users/block.json
     2456#: blocks/pro-placeholder/site-views/block.json
     2457#: blocks/pro-placeholder/term-views/block.json
     2458#: blocks/pro-placeholder/user-views/block.json
     2459#: src/blocks/most-viewed-posts/block.json
     2460#: src/blocks/post-views/block.json
     2461msgctxt "block keyword"
     2462msgid "views"
     2463msgstr ""
     2464
     2465#: blocks/most-viewed-posts/block.json
     2466#: blocks/pro-placeholder/most-viewed-terms/block.json
     2467#: blocks/pro-placeholder/most-viewed-users/block.json
     2468#: src/blocks/most-viewed-posts/block.json
     2469msgctxt "block keyword"
     2470msgid "popular"
     2471msgstr ""
     2472
     2473#: blocks/most-viewed-posts/block.json
     2474#: blocks/post-views/block.json
     2475#: src/blocks/most-viewed-posts/block.json
     2476#: src/blocks/post-views/block.json
     2477msgctxt "block keyword"
     2478msgid "counter"
     2479msgstr ""
     2480
     2481#: blocks/most-viewed-posts/block.json
     2482#: blocks/post-views/block.json
     2483#: blocks/pro-placeholder/site-views/block.json
     2484#: src/blocks/most-viewed-posts/block.json
     2485#: src/blocks/post-views/block.json
     2486msgctxt "block keyword"
     2487msgid "analytics"
     2488msgstr ""
     2489
     2490#: blocks/most-viewed-posts/block.json
     2491#: src/blocks/most-viewed-posts/block.json
     2492msgctxt "block keyword"
     2493msgid "trending"
     2494msgstr ""
     2495
     2496#: blocks/post-views/block.json
     2497#: src/blocks/post-views/block.json
     2498msgctxt "block title"
    1452499msgid "Post Views"
    1462500msgstr ""
    1472501
    148 #: includes/class-admin.php:173 includes/class-columns.php:96
    149 msgid "Adjust the views count for this post."
    150 msgstr ""
    151 
    152 #: includes/class-admin.php:174 includes/class-columns.php:101
    153 msgid "Cancel"
    154 msgstr ""
    155 
    156 #: includes/class-columns-modal.php:70
    157 msgid "Loading..."
    158 msgstr ""
    159 
    160 #: includes/class-columns-modal.php:71 includes/class-columns-modal.php:264
    161 msgid "Close"
    162 msgstr ""
    163 
    164 #: includes/class-columns-modal.php:72
    165 msgid "An error occurred while loading data."
    166 msgstr ""
    167 
    168 #: includes/class-columns-modal.php:73
    169 msgid "Total views in this period:"
    170 msgstr ""
    171 
    172 #: includes/class-columns-modal.php:74
    173 msgid "view"
    174 msgstr ""
    175 
    176 #: includes/class-columns-modal.php:75 includes/class-emails-template.php:1610
    177 msgid "views"
    178 msgstr ""
    179 
    180 #: includes/class-columns-modal.php:91
    181 msgid "Permission denied."
    182 msgstr ""
    183 
    184 #: includes/class-columns-modal.php:107
    185 msgid "Post not found."
    186 msgstr ""
    187 
    188 #: includes/class-columns-modal.php:111
    189 msgid "Admin column disabled."
    190 msgstr ""
    191 
    192 #: includes/class-columns-modal.php:115
    193 msgid "Post type is not tracked."
    194 msgstr ""
    195 
    196 #: includes/class-columns-modal.php:119
    197 msgid "Access denied for this post."
    198 msgstr ""
    199 
    200 #: includes/class-columns-modal.php:275
    201 msgid "Year"
    202 msgstr ""
    203 
    204 #: includes/class-columns-modal.php:276
    205 msgid "Month"
    206 msgstr ""
    207 
    208 #: includes/class-columns-modal.php:277
    209 msgid "Week"
    210 msgstr ""
    211 
    212 #: includes/class-columns-modal.php:287
    213 msgid "More insights available"
    214 msgstr ""
    215 
    216 #: includes/class-columns-modal.php:288
    217 msgid "Upgrade to Pro to unlock it"
    218 msgstr ""
    219 
    220 #: includes/class-columns.php:92 includes/class-dashboard.php:529
    221 msgid "Edit"
    222 msgstr ""
    223 
    224 #: includes/class-columns.php:100
    225 msgid "OK"
    226 msgstr ""
    227 
    228 #: includes/class-columns.php:318 includes/class-dashboard.php:522
    229 msgid "(no title)"
    230 msgstr ""
    231 
    232 #: includes/class-counter.php:73
    233 msgid "Missing or invalid queue parameters."
    234 msgstr ""
    235 
    236 #: includes/class-counter.php:80
    237 msgid "Security check failed."
    238 msgstr ""
    239 
    240 #: includes/class-counter.php:93
    241 msgid "No valid post IDs were provided."
    242 msgstr ""
    243 
    244 #: includes/class-counter.php:513
    245 msgid "REST API method is disabled."
    246 msgstr ""
    247 
    248 #: includes/class-counter.php:527
    249 msgid "Post type excluded."
    250 msgstr ""
    251 
    252 #: includes/class-counter.php:534
    253 msgid "Invalid storage type."
    254 msgstr ""
    255 
    256 #: includes/class-counter.php:2423
    257 msgid "You do not have permission to count post views via REST API."
    258 msgstr ""
    259 
    260 #: includes/class-cron.php:69
    261 msgid "Post Views Counter reset daily counts interval"
    262 msgstr ""
    263 
    264 #: includes/class-dashboard.php:86
    265 msgid "Failed to load this widget."
    266 msgstr ""
    267 
    268 #: includes/class-dashboard.php:87
    269 msgid "Retry"
    270 msgstr ""
    271 
    272 #: includes/class-dashboard.php:107
    273 msgid "Displays a chart of most viewed post types."
    274 msgstr ""
    275 
    276 #: includes/class-dashboard.php:113
    277 msgid "Top Posts"
    278 msgstr ""
    279 
    280 #: includes/class-dashboard.php:114
    281 msgid "Displays a list of most viewed single posts or pages."
    282 msgstr ""
    283 
    284 #: includes/class-dashboard.php:196
    285 msgid "More Insights"
    286 msgstr ""
    287 
    288 #: includes/class-dashboard.php:198
    289 msgid "Go beyond basic post views."
    290 msgstr ""
    291 
    292 #: includes/class-dashboard.php:199
    293 msgid "See site views, referrers, popular searches, top users, and more."
    294 msgstr ""
    295 
    296 #: includes/class-dashboard.php:201
    297 msgid "Unlock more insights"
    298 msgstr ""
    299 
    300 #: includes/class-dashboard.php:207
    301 msgid "Powered by"
    302 msgstr ""
    303 
    304 #: includes/class-dashboard.php:275 includes/class-dashboard.php:431
    305 #: includes/class-dashboard.php:592
    306 msgid "You do not have permission to access this page."
    307 msgstr ""
    308 
    309 #: includes/class-dashboard.php:326 includes/class-settings-display.php:104
    310 #: includes/class-widgets.php:85 includes/functions.php:745
    311 #: post-views-counter.php:510
    312 msgid "Total Views"
    313 msgstr ""
    314 
    315 #: includes/class-dashboard.php:496
    316 msgid "Post"
    317 msgstr ""
    318 
    319 #: includes/class-dashboard.php:497
    320 msgid "Views"
    321 msgstr ""
    322 
    323 #: includes/class-dashboard.php:542 includes/functions.php:736
    324 msgid "No most viewed posts found."
    325 msgstr ""
    326 
    327 #: includes/class-emails-mailer.php:166
    328 msgid "WordPress could not send the content views summary email."
    329 msgstr ""
    330 
    331 #: includes/class-emails-mailer.php:226
    332 msgid "The content views summary recipient is invalid."
    333 msgstr ""
    334 
    335 #: includes/class-emails-mailer.php:884
    336 #, php-format
    337 msgid "Post Views Counter on %s"
    338 msgstr ""
    339 
    340 #: includes/class-emails-scheduler.php:46
    341 msgid "Post Views Counter weekly email summary interval"
    342 msgstr ""
    343 
    344 #: includes/class-emails-template.php:506
    345 #: includes/class-emails-template.php:880 includes/class-settings.php:273
    346 #: includes/class-settings.php:632 includes/class-settings.php:633
    347 msgid "Post Views Counter"
    348 msgstr ""
    349 
    350 #: includes/class-emails-template.php:1146
    351 #: includes/class-emails-template.php:1188
    352 msgid "Content views"
    353 msgstr ""
    354 
    355 #: includes/class-emails-template.php:1148
    356 #: includes/class-emails-template.php:1175
    357 #, php-format
    358 msgid "%s total views"
    359 msgstr ""
    360 
    361 #: includes/class-emails-template.php:1158
    362 #: includes/class-emails-template.php:1185
    363 #, php-format
    364 msgid "%s content items received views"
    365 msgstr ""
    366 
    367 #: includes/class-emails-template.php:1205
    368 #: includes/class-emails-template.php:1228
    369 msgid "No top content was available for this period."
    370 msgstr ""
    371 
    372 #: includes/class-emails-template.php:1207
    373 #: includes/class-emails-template.php:1242
    374 msgid "Top content"
    375 msgstr ""
    376 
    377 #: includes/class-emails-template.php:1210
    378 #: includes/class-emails-template.php:1233
    379 msgid "(Untitled)"
    380 msgstr ""
    381 
    382 #: includes/class-emails-template.php:1212
    383 #, php-format
    384 msgid "%1$s. %2$s - %3$s views"
    385 msgstr ""
    386 
    387 #: includes/class-emails-template.php:1238
    388 #, php-format
    389 msgid "%s views"
    390 msgstr ""
    391 
    392 #: includes/class-emails-template.php:1268
    393 #: includes/class-emails-template.php:1301
    394 #: includes/class-emails-template.php:1306
    395 msgid "Traffic signals"
    396 msgstr ""
    397 
    398 #: includes/class-emails-template.php:1297
    399 #: includes/class-emails-template.php:1375
    400 #, php-format
    401 msgid "Unusual traffic activity was detected for %s."
    402 msgstr ""
    403 
    404 #: includes/class-emails-template.php:1318
    405 #, php-format
    406 msgid "Manage Emails in WordPress: %s"
    407 msgstr ""
    408 
    409 #: includes/class-emails-template.php:1330
    410 msgid "Manage Emails in WordPress"
    411 msgstr ""
    412 
    413 #: includes/class-emails-template.php:1340
    414 #, php-format
    415 msgid "Visit your site: %s"
    416 msgstr ""
    417 
    418 #: includes/class-emails-template.php:1351
    419 msgid "your site"
    420 msgstr ""
    421 
    422 #: includes/class-emails-template.php:1353
    423 #, php-format
    424 msgid "Visit %s"
    425 msgstr ""
    426 
    427 #: includes/class-emails-template.php:1377
    428 msgid "Unusual traffic activity was detected during this report period."
    429 msgstr ""
    430 
    431 #: includes/class-emails-template.php:1380
    432 msgid "No unusual traffic activity was detected during this report period."
    433 msgstr ""
    434 
    435 #: includes/class-emails-template.php:1418
    436 msgid "day"
    437 msgstr ""
    438 
    439 #: includes/class-emails-template.php:1421
    440 msgid "month"
    441 msgstr ""
    442 
    443 #: includes/class-emails-template.php:1424
    444 msgid "week"
    445 msgstr ""
    446 
    447 #: includes/class-emails-template.php:1469 includes/class-import.php:332
    448 msgid "weekly"
    449 msgstr ""
    450 
    451 #: includes/class-emails-template.php:1484
    452 msgid "Weekly Content Views Summary"
    453 msgstr ""
    454 
    455 #: includes/class-emails-template.php:1643
    456 #, php-format
    457 msgid "No previous %s comparison is available yet"
    458 msgstr ""
    459 
    460 #: includes/class-emails-template.php:1649
    461 #, php-format
    462 msgid "No change from the previous %s"
    463 msgstr ""
    464 
    465 #: includes/class-emails-template.php:1654
    466 #, php-format
    467 msgid "Up %1$s views from the previous %2$s"
    468 msgstr ""
    469 
    470 #: includes/class-emails-template.php:1656
    471 #, php-format
    472 msgid "Down %1$s views from the previous %2$s"
    473 msgstr ""
    474 
    475 #: includes/class-import.php:62
    476 msgid "Custom Meta Key"
    477 msgstr ""
    478 
    479 #: includes/class-import.php:203
    480 msgid "Override existing views"
    481 msgstr ""
    482 
    483 #: includes/class-import.php:204
    484 msgid "Replace stored counts with the imported values."
    485 msgstr ""
    486 
    487 #: includes/class-import.php:208
    488 msgid "Merge with existing views"
    489 msgstr ""
    490 
    491 #: includes/class-import.php:209
    492 msgid "Add imported counts on top of the existing values."
    493 msgstr ""
    494 
    495 #: includes/class-import.php:213
    496 msgid "Skip Existing"
    497 msgstr ""
    498 
    499 #: includes/class-import.php:214
    500 msgid "Only import data when the target record does not exist yet."
    501 msgstr ""
    502 
    503 #: includes/class-import.php:218
    504 msgid "Keep Higher Count"
    505 msgstr ""
    506 
    507 #: includes/class-import.php:219
    508 msgid ""
    509 "Keep whichever value is higher when comparing imported and stored counts."
    510 msgstr ""
    511 
    512 #: includes/class-import.php:223
    513 msgid "Fill Empty Counts"
    514 msgstr ""
    515 
    516 #: includes/class-import.php:224
    517 msgid "Only import data for posts or periods that currently store zero views."
    518 msgstr ""
    519 
    520 #: includes/class-import.php:323
     2502#: blocks/post-views/block.json
     2503#: src/blocks/post-views/block.json
     2504msgctxt "block description"
     2505msgid "Display post views for a given post."
     2506msgstr ""
     2507
     2508#: blocks/post-views/block.json
     2509#: src/blocks/post-views/block.json
     2510msgctxt "block keyword"
     2511msgid "display"
     2512msgstr ""
     2513
     2514#: blocks/pro-placeholder/most-viewed-terms/block.json
     2515msgctxt "block title"
     2516msgid "Most Viewed Terms"
     2517msgstr ""
     2518
     2519#: blocks/pro-placeholder/most-viewed-terms/block.json
     2520msgctxt "block description"
     2521msgid "Displays a list of the most viewed terms. Requires Post Views Counter Pro."
     2522msgstr ""
     2523
     2524#: blocks/pro-placeholder/most-viewed-terms/block.json
     2525#: blocks/pro-placeholder/term-views/block.json
     2526msgctxt "block keyword"
     2527msgid "taxonomy"
     2528msgstr ""
     2529
     2530#: blocks/pro-placeholder/most-viewed-terms/block.json
     2531#: blocks/pro-placeholder/term-views/block.json
     2532msgctxt "block keyword"
     2533msgid "category"
     2534msgstr ""
     2535
     2536#: blocks/pro-placeholder/most-viewed-terms/block.json
     2537#: blocks/pro-placeholder/term-views/block.json
     2538msgctxt "block keyword"
     2539msgid "tag"
     2540msgstr ""
     2541
     2542#: blocks/pro-placeholder/most-viewed-terms/block.json
     2543#: blocks/pro-placeholder/most-viewed-users/block.json
     2544#: blocks/pro-placeholder/site-views/block.json
     2545#: blocks/pro-placeholder/term-views/block.json
     2546#: blocks/pro-placeholder/user-views/block.json
     2547msgctxt "block keyword"
     2548msgid "pro"
     2549msgstr ""
     2550
     2551#: blocks/pro-placeholder/most-viewed-users/block.json
     2552msgctxt "block title"
     2553msgid "Most Viewed Authors"
     2554msgstr ""
     2555
     2556#: blocks/pro-placeholder/most-viewed-users/block.json
     2557msgctxt "block description"
     2558msgid "Displays a list of the most viewed authors. Requires Post Views Counter Pro."
     2559msgstr ""
     2560
     2561#: blocks/pro-placeholder/most-viewed-users/block.json
     2562#: blocks/pro-placeholder/user-views/block.json
     2563msgctxt "block keyword"
     2564msgid "author"
     2565msgstr ""
     2566
     2567#: blocks/pro-placeholder/most-viewed-users/block.json
     2568#: blocks/pro-placeholder/user-views/block.json
     2569msgctxt "block keyword"
     2570msgid "user"
     2571msgstr ""
     2572
     2573#: blocks/pro-placeholder/site-views/block.json
     2574msgctxt "block title"
     2575msgid "Site Views"
     2576msgstr ""
     2577
     2578#: blocks/pro-placeholder/site-views/block.json
     2579msgctxt "block description"
     2580msgid "Display site views. Requires Post Views Counter Pro."
     2581msgstr ""
     2582
     2583#: blocks/pro-placeholder/site-views/block.json
     2584msgctxt "block keyword"
     2585msgid "site"
     2586msgstr ""
     2587
     2588#: blocks/pro-placeholder/site-views/block.json
     2589msgctxt "block keyword"
    5212590msgid "total"
    5222591msgstr ""
    5232592
    524 #: includes/class-import.php:326
    525 msgid "yearly"
    526 msgstr ""
    527 
    528 #: includes/class-import.php:329
    529 msgid "monthly"
    530 msgstr ""
    531 
    532 #: includes/class-import.php:335
    533 msgid "daily"
    534 msgstr ""
    535 
    536 #: includes/class-import.php:340
    537 msgid "post types"
    538 msgstr ""
    539 
    540 #: includes/class-import.php:341
    541 msgid "taxonomies"
    542 msgstr ""
    543 
    544 #: includes/class-import.php:342
    545 msgid "author archives"
    546 msgstr ""
    547 
    548 #: includes/class-import.php:343
    549 msgid "other pages"
    550 msgstr ""
    551 
    552 #: includes/class-import.php:344
    553 msgid "traffic sources"
    554 msgstr ""
    555 
    556 #: includes/class-import.php:358
    557 #, php-format
    558 msgid "Imports %s view counts for %s"
    559 msgstr ""
    560 
    561 #: includes/class-import.php:360
    562 #, php-format
    563 msgid "Imports %s view counts"
    564 msgstr ""
    565 
    566 #: includes/class-import.php:372
    567 #, php-format
    568 msgid "PVC Pro additionally imports %s view counts"
    569 msgstr ""
    570 
    571 #: includes/class-import.php:394
    572 msgid "and"
    573 msgstr ""
    574 
    575 #: includes/class-import.php:420
    576 msgid "Invalid import provider selected."
    577 msgstr ""
    578 
    579 #: includes/class-import.php:561
    580 msgid ""
    581 "Enter the meta key from which the views data is to be retrieved during "
    582 "import."
    583 msgstr ""
    584 
    585 #: includes/class-import.php:607 includes/class-import.php:732
    586 #: includes/class-import.php:869
    587 #, php-format
    588 msgid "No valid views data found for %s."
    589 msgstr ""
    590 
    591 #: includes/class-import.php:607
    592 #, php-format
    593 msgid "meta key: %s"
    594 msgstr ""
    595 
    596 #: includes/class-import.php:621 includes/class-import.php:673
    597 #, php-format
    598 msgid "Meta key \"%s\"."
    599 msgstr ""
    600 
    601 #: includes/class-import.php:650 includes/class-import.php:772
    602 #: includes/class-import.php:1125 includes/class-import.php:1393
    603 msgid "No valid post data found to import."
    604 msgstr ""
    605 
    606 #: includes/class-import.php:973
    607 #, php-format
    608 msgid "Would skip %s non-post URLs."
    609 msgstr ""
    610 
    611 #: includes/class-import.php:1129
    612 msgid "No post types are selected for tracking in settings."
    613 msgstr ""
    614 
    615 #: includes/class-import.php:1134
    616 #, php-format
    617 msgid "All %s URLs were skipped. Sample URLs: %s"
    618 msgstr ""
    619 
    620 #: includes/class-import.php:1181
    621 #, php-format
    622 msgid "Skipped %s non-post URLs."
    623 msgstr ""
    624 
    625 #: includes/class-import.php:1671
    626 #, php-format
    627 msgid "Analysis of %s:"
    628 msgstr ""
    629 
    630 #: includes/class-import.php:1673
    631 #, php-format
    632 msgid "Import from %s:"
    633 msgstr ""
    634 
    635 #: includes/class-import.php:1681
    636 #, php-format
    637 msgid "Found %s total views for %s posts."
    638 msgstr ""
    639 
    640 #: includes/class-import.php:1687
    641 #, php-format
    642 msgid "Successfully imported %s total views for %s posts."
    643 msgstr ""
    644 
    645 #: includes/class-import.php:1701
    646 #, php-format
    647 msgid "Skipped %1$s total views for %2$s posts."
    648 msgstr ""
    649 
    650 #: includes/class-integrations.php:110
    651 msgid ""
    652 "Integrate with WordPress block editor to order posts by views in Query Loop "
    653 "and Latest Posts blocks."
    654 msgstr ""
    655 
    656 #: includes/class-integrations.php:117
    657 msgid "Query Loop Block"
    658 msgstr ""
    659 
    660 #: includes/class-integrations.php:118
    661 msgid "Enables ordering posts by view count in the core Query Loop block."
    662 msgstr ""
    663 
    664 #: includes/class-integrations.php:122
    665 msgid "Latest Posts Block"
    666 msgstr ""
    667 
    668 #: includes/class-integrations.php:123
    669 msgid "Enables ordering posts by view count in the core Latest Posts block."
    670 msgstr ""
    671 
    672 #: includes/class-integrations.php:130
    673 msgid "Integrate with AMP plugin to handle post views on AMP pages."
    674 msgstr ""
    675 
    676 #: includes/class-integrations.php:137
    677 msgid "AMP Support"
    678 msgstr ""
    679 
    680 #: includes/class-integrations.php:138
    681 msgid "Tracks and displays views on AMP-enabled pages."
    682 msgstr ""
    683 
    684 #: includes/class-integrations.php:145
    685 msgid "Integrate with Beaver Builder to order module posts by views."
    686 msgstr ""
    687 
    688 #: includes/class-integrations.php:152
    689 msgid "Post Grid Module"
    690 msgstr ""
    691 
    692 #: includes/class-integrations.php:153
    693 msgid "Orders posts by views in the Beaver Builder Post Grid module."
    694 msgstr ""
    695 
    696 #: includes/class-integrations.php:157
    697 msgid "Post Carousel Module"
    698 msgstr ""
    699 
    700 #: includes/class-integrations.php:158
    701 msgid "Orders posts by views in the Beaver Builder Post Carousel module."
    702 msgstr ""
    703 
    704 #: includes/class-integrations.php:162 includes/class-integrations.php:202
    705 msgid "Post Slider Module"
    706 msgstr ""
    707 
    708 #: includes/class-integrations.php:163
    709 msgid "Orders posts by views in the Beaver Builder Post Slider module."
    710 msgstr ""
    711 
    712 #: includes/class-integrations.php:170
    713 msgid "Integrate with CoBlocks to order post feed results by views."
    714 msgstr ""
    715 
    716 #: includes/class-integrations.php:177 includes/class-integrations.php:282
    717 msgid "Posts Block"
    718 msgstr ""
    719 
    720 #: includes/class-integrations.php:178
    721 msgid "Adds post views ordering to the CoBlocks Posts block."
    722 msgstr ""
    723 
    724 #: includes/class-integrations.php:182
    725 msgid "Post Carousel Block"
    726 msgstr ""
    727 
    728 #: includes/class-integrations.php:183
    729 msgid "Adds post views ordering to the CoBlocks Post Carousel block."
    730 msgstr ""
    731 
    732 #: includes/class-integrations.php:190
    733 msgid ""
    734 "Integrate with Divi Theme to order module posts by views when the module "
    735 "uses the \"orderby-post-views\" CSS class."
    736 msgstr ""
    737 
    738 #: includes/class-integrations.php:197
    739 msgid "Blog Module"
    740 msgstr ""
    741 
    742 #: includes/class-integrations.php:198
    743 msgid "Orders posts by views in the Divi Blog module."
    744 msgstr ""
    745 
    746 #: includes/class-integrations.php:203
    747 msgid "Orders posts by views in the Divi Post Slider module."
    748 msgstr ""
    749 
    750 #: includes/class-integrations.php:207
    751 msgid "Portfolio Module"
    752 msgstr ""
    753 
    754 #: includes/class-integrations.php:208
    755 msgid "Orders posts by views in the Divi Portfolio module."
    756 msgstr ""
    757 
    758 #: includes/class-integrations.php:215
    759 msgid ""
    760 "Integrate with Elementor Pro to order posts by views when a widget query "
    761 "uses the \"post_views\" Query ID."
    762 msgstr ""
    763 
    764 #: includes/class-integrations.php:222
    765 msgid "Posts Widget"
    766 msgstr ""
    767 
    768 #: includes/class-integrations.php:223
    769 msgid ""
    770 "Orders posts by views in the Elementor Pro Posts widget when its Query ID is "
    771 "set to \"post_views\"."
    772 msgstr ""
    773 
    774 #: includes/class-integrations.php:227
    775 msgid "Portfolio Widget"
    776 msgstr ""
    777 
    778 #: includes/class-integrations.php:228
    779 msgid ""
    780 "Orders items by views in the Elementor Pro Portfolio widget when its Query "
    781 "ID is set to \"post_views\"."
    782 msgstr ""
    783 
    784 #: includes/class-integrations.php:232
    785 msgid "Loop Grid / Loop Carousel"
    786 msgstr ""
    787 
    788 #: includes/class-integrations.php:233
    789 msgid ""
    790 "Orders items by views in the Elementor Pro Loop Grid and Loop Carousel "
    791 "widgets when the Query ID is set to \"post_views\"."
    792 msgstr ""
    793 
    794 #: includes/class-integrations.php:240
    795 msgid "Integrate with GenerateBlocks to order Query block results by views."
    796 msgstr ""
    797 
    798 #: includes/class-integrations.php:247
    799 msgid "Dynamic Content"
    800 msgstr ""
    801 
    802 #: includes/class-integrations.php:248
    803 msgid "Adds post_views ordering to the GenerateBlocks Query block."
    804 msgstr ""
    805 
    806 #: includes/class-integrations.php:255
    807 msgid ""
    808 "Integrate with JetEngine plugin to display post views in custom listings and "
    809 "queries."
    810 msgstr ""
    811 
    812 #: includes/class-integrations.php:262
    813 msgid "Query Builder"
    814 msgstr ""
    815 
    816 #: includes/class-integrations.php:263
    817 msgid "Enables ordering posts by view count in Query Builder posts queries."
    818 msgstr ""
    819 
    820 #: includes/class-integrations.php:267
    821 msgid "Listing Grid"
    822 msgstr ""
    823 
    824 #: includes/class-integrations.php:268
    825 msgid ""
    826 "Enables ordering posts by view count in Listing Grid, Maps Listing and "
    827 "Calendar widgets."
    828 msgstr ""
    829 
    830 #: includes/class-integrations.php:275
    831 msgid ""
    832 "Integrate with Kadence Blocks to order post results by views in the Posts "
    833 "block."
    834 msgstr ""
    835 
    836 #: includes/class-integrations.php:283
    837 msgid "Adds post views ordering to the Posts block."
    838 msgstr ""
    839 
    840 #: includes/class-integrations.php:290
    841 msgid ""
    842 "Integrate with Polylang to filter reports, exports, and dashboard widgets by "
    843 "language."
    844 msgstr ""
    845 
    846 #: includes/class-integrations.php:297 includes/class-integrations.php:317
    847 msgid "Reports Language Filters"
    848 msgstr ""
    849 
    850 #: includes/class-integrations.php:298 includes/class-integrations.php:318
    851 msgid ""
    852 "Adds language filtering and a language column to Reports tables, charts, and "
    853 "exports."
    854 msgstr ""
    855 
    856 #: includes/class-integrations.php:302 includes/class-integrations.php:322
    857 msgid "Dashboard Widgets"
    858 msgstr ""
    859 
    860 #: includes/class-integrations.php:303 includes/class-integrations.php:323
    861 msgid ""
    862 "Filters dashboard widgets (site, post, and term views) by the selected "
    863 "language."
    864 msgstr ""
    865 
    866 #: includes/class-integrations.php:310
    867 msgid ""
    868 "Integrate with WPML to filter reports, exports, and dashboard widgets by "
    869 "language."
    870 msgstr ""
    871 
    872 #: includes/class-settings-api.php:448
    873 msgid "Reset to defaults"
    874 msgstr ""
    875 
    876 #: includes/class-settings-api.php:878
    877 msgid "Open color picker"
    878 msgstr ""
    879 
    880 #: includes/class-settings-api.php:1129
    881 msgid "Settings saved."
    882 msgstr ""
    883 
    884 #: includes/class-settings-api.php:1159
    885 msgid "Settings restored to defaults."
    886 msgstr ""
    887 
    888 #: includes/class-settings-display.php:41
    889 msgid "Counter Appearance"
    890 msgstr ""
    891 
    892 #: includes/class-settings-display.php:46
    893 msgid "Display Targets"
    894 msgstr ""
    895 
    896 #: includes/class-settings-display.php:51
    897 msgid "Display Audience"
    898 msgstr ""
    899 
    900 #: includes/class-settings-display.php:56
    901 msgid "Admin Interface"
    902 msgstr ""
    903 
    904 #: includes/class-settings-display.php:73
    905 #: includes/class-settings-general.php:113
    906 msgid "logged in users"
    907 msgstr ""
    908 
    909 #: includes/class-settings-display.php:74
    910 #: includes/class-settings-general.php:114
    911 msgid "guests"
    912 msgstr ""
    913 
    914 #: includes/class-settings-display.php:75
    915 #: includes/class-settings-general.php:115
    916 msgid "selected user roles"
    917 msgstr ""
    918 
    919 #: includes/class-settings-display.php:84
    920 msgid "Views Label"
    921 msgstr ""
    922 
    923 #: includes/class-settings-display.php:88
    924 msgid "Text shown next to the view count."
    925 msgstr ""
    926 
    927 #: includes/class-settings-display.php:95 includes/class-widgets.php:171
    928 msgid "Views Period"
    929 msgstr ""
    930 
    931 #: includes/class-settings-display.php:101
    932 msgid "Time range used when displaying the number of views."
    933 msgstr ""
    934 
    935 #: includes/class-settings-display.php:109 includes/class-widgets.php:207
    936 msgid "Display Style"
    937 msgstr ""
    938 
    939 #: includes/class-settings-display.php:113
    940 msgid "Choose whether to show an icon, label text, or both."
    941 msgstr ""
    942 
    943 #: includes/class-settings-display.php:117
    944 msgid "Icon"
    945 msgstr ""
    946 
    947 #: includes/class-settings-display.php:118
    948 msgid "Label"
    949 msgstr ""
    950 
    951 #: includes/class-settings-display.php:123
    952 msgid "Icon Class"
    953 msgstr ""
    954 
    955 #: includes/class-settings-display.php:135
    956 msgid ""
    957 "Enter the CSS class for the views icon. Any Dashicons class is supported."
    958 msgstr ""
    959 
    960 #: includes/class-settings-display.php:140
    961 msgid "Position"
    962 msgstr ""
    963 
    964 #: includes/class-settings-display.php:144
    965 #, php-format
    966 msgid ""
    967 "Where to insert the counter automatically. Use %s shortcode for manual "
    968 "placement."
    969 msgstr ""
    970 
    971 #: includes/class-settings-display.php:146
    972 msgid "Before the content"
    973 msgstr ""
    974 
    975 #: includes/class-settings-display.php:147
    976 msgid "After the content"
    977 msgstr ""
    978 
    979 #: includes/class-settings-display.php:148
    980 msgid "Manual only"
    981 msgstr ""
    982 
    983 #: includes/class-settings-display.php:153
    984 msgid "Admin Column"
    985 msgstr ""
    986 
    987 #: includes/class-settings-display.php:157
    988 msgid "Show a \"Views\" column on post and page list screens."
    989 msgstr ""
    990 
    991 #: includes/class-settings-display.php:161
    992 msgid "Admin Edit"
    993 msgstr ""
    994 
    995 #: includes/class-settings-display.php:165
    996 msgid "Allow editing the view count on the post edit screen."
    997 msgstr ""
    998 
    999 #: includes/class-settings-display.php:169
    1000 msgid "Dynamic Loading"
    1001 msgstr ""
    1002 
    1003 #: includes/class-settings-display.php:176
    1004 msgid "Load the view count dynamically to avoid caching the displayed value."
    1005 msgstr ""
    1006 
    1007 #: includes/class-settings-display.php:181
    1008 msgid "Format Number"
    1009 msgstr ""
    1010 
    1011 #: includes/class-settings-display.php:184
    1012 msgid ""
    1013 "Format the view count according to the site locale (uses the WordPress "
    1014 "number_format_i18n function)."
    1015 msgstr ""
    1016 
    1017 #: includes/class-settings-display.php:188
    1018 #: includes/class-settings-general.php:135
    1019 msgid "Taxonomies"
    1020 msgstr ""
    1021 
    1022 #: includes/class-settings-display.php:195
    1023 msgid ""
    1024 "Display the view count on category, tag, or custom taxonomy archive pages."
    1025 msgstr ""
    1026 
    1027 #: includes/class-settings-display.php:203
    1028 msgid "Select taxonomies where the view counter will be displayed."
    1029 msgstr ""
    1030 
    1031 #: includes/class-settings-display.php:218
    1032 #: includes/class-settings-general.php:147
    1033 msgid "Author Archives"
    1034 msgstr ""
    1035 
    1036 #: includes/class-settings-display.php:225
    1037 msgid "Display the view count on author archive pages."
    1038 msgstr ""
    1039 
    1040 #: includes/class-settings-display.php:230
    1041 #: includes/class-settings-general.php:127 includes/class-widgets.php:160
    1042 msgid "Post Types"
    1043 msgstr ""
    1044 
    1045 #: includes/class-settings-display.php:233
    1046 msgid "Select post types where the view counter will be displayed."
    1047 msgstr ""
    1048 
    1049 #: includes/class-settings-display.php:238
    1050 msgid "Page Type"
    1051 msgstr ""
    1052 
    1053 #: includes/class-settings-display.php:241
    1054 msgid "Select page contexts where the view counter will be displayed."
    1055 msgstr ""
    1056 
    1057 #: includes/class-settings-display.php:245
    1058 msgid "Home"
    1059 msgstr ""
    1060 
    1061 #: includes/class-settings-display.php:246
    1062 msgid "Archives"
    1063 msgstr ""
    1064 
    1065 #: includes/class-settings-display.php:247
    1066 msgid "Single posts and pages"
    1067 msgstr ""
    1068 
    1069 #: includes/class-settings-display.php:248
    1070 msgid "Search results"
    1071 msgstr ""
    1072 
    1073 #: includes/class-settings-display.php:254
    1074 msgid "User Type"
    1075 msgstr ""
    1076 
    1077 #: includes/class-settings-display.php:257
    1078 msgid "Hide the view counter for selected visitor groups."
    1079 msgstr ""
    1080 
    1081 #: includes/class-settings-display.php:265
    1082 msgid "User Roles"
    1083 msgstr ""
    1084 
    1085 #: includes/class-settings-display.php:268
    1086 msgid "Hide the view counter for selected user roles."
    1087 msgstr ""
    1088 
    1089 #: includes/class-settings-display.php:278
    1090 msgid "Toolbar Chart"
    1091 msgstr ""
    1092 
    1093 #: includes/class-settings-display.php:281
    1094 msgid "A views chart is shown for content types that are being counted."
    1095 msgstr ""
    1096 
    1097 #: includes/class-settings-display.php:282
    1098 msgid "Show a views chart in the admin toolbar."
    1099 msgstr ""
    1100 
    1101 #: includes/class-settings-display.php:286
    1102 msgid "Menu Position"
    1103 msgstr ""
    1104 
    1105 #: includes/class-settings-display.php:289
    1106 msgid "Top menu"
    1107 msgstr ""
    1108 
    1109 #: includes/class-settings-display.php:290
    1110 msgid "Settings submenu"
    1111 msgstr ""
    1112 
    1113 #: includes/class-settings-display.php:292
    1114 msgid "Choose where the plugin menu appears in the admin sidebar."
    1115 msgstr ""
    1116 
    1117 #: includes/class-settings-display.php:434
    1118 msgid ""
    1119 "Choose where the counter is inserted and which content types it attaches to."
    1120 msgstr ""
    1121 
    1122 #: includes/class-settings-display.php:443
    1123 msgid ""
    1124 "Control which visitor groups can see the counter. These rules apply on top "
    1125 "of the display targets."
    1126 msgstr ""
    1127 
    1128 #: includes/class-settings-display.php:452
    1129 msgid ""
    1130 "Adjust the label, period, icon, and formatting used when the counter is "
    1131 "rendered."
    1132 msgstr ""
    1133 
    1134 #: includes/class-settings-display.php:461
    1135 msgid ""
    1136 "Control how view counts are shown and managed in WordPress admin (columns, "
    1137 "edit permissions, toolbar chart)."
    1138 msgstr ""
    1139 
    1140 #: includes/class-settings-emails.php:37
    1141 msgid "Email Summaries"
    1142 msgstr ""
    1143 
    1144 #: includes/class-settings-emails.php:42
    1145 msgid "Email Recipients"
    1146 msgstr ""
    1147 
    1148 #: includes/class-settings-emails.php:47
    1149 msgid "Email Template"
    1150 msgstr ""
    1151 
    1152 #: includes/class-settings-emails.php:52
    1153 msgid "Test Email"
    1154 msgstr ""
    1155 
    1156 #: includes/class-settings-emails.php:57
    1157 msgid "Delivery & Status"
    1158 msgstr ""
    1159 
    1160 #: includes/class-settings-emails.php:73
    1161 msgid "Email Summary"
    1162 msgstr ""
    1163 
    1164 #: includes/class-settings-emails.php:76
    1165 msgid "Send a summary of tracked content views to the selected recipient."
    1166 msgstr ""
    1167 
    1168 #: includes/class-settings-emails.php:80
    1169 msgid "Summary Frequency"
    1170 msgstr ""
    1171 
    1172 #: includes/class-settings-emails.php:89
    1173 msgid "Daily"
    1174 msgstr ""
    1175 
    1176 #: includes/class-settings-emails.php:90
    1177 msgid "Weekly"
    1178 msgstr ""
    1179 
    1180 #: includes/class-settings-emails.php:91
    1181 msgid "Monthly"
    1182 msgstr ""
    1183 
    1184 #: includes/class-settings-emails.php:93
    1185 msgid "Choose how often views tracking summaries are sent."
    1186 msgstr ""
    1187 
    1188 #: includes/class-settings-emails.php:98
    1189 msgid "Minimum Tracked Views"
    1190 msgstr ""
    1191 
    1192 #: includes/class-settings-emails.php:103
    1193 msgid ""
    1194 "Do not send the summary if the report period has fewer than this many "
    1195 "tracked views."
    1196 msgstr ""
    1197 
    1198 #: includes/class-settings-emails.php:109
    1199 msgid "Top Content Items"
    1200 msgstr ""
    1201 
    1202 #: includes/class-settings-emails.php:114
    1203 msgid "Choose how many top content items are included in each summary."
    1204 msgstr ""
    1205 
    1206 #: includes/class-settings-emails.php:119
    1207 msgid "Top Gainers and Decliners"
    1208 msgstr ""
    1209 
    1210 #: includes/class-settings-emails.php:127
    1211 msgid ""
    1212 "Show which content is gaining or losing momentum compared with previous "
    1213 "periods."
    1214 msgstr ""
    1215 
    1216 #: includes/class-settings-emails.php:128
    1217 #, php-format
    1218 msgid ""
    1219 "When matching data is available, this appears in the Traffic signals section "
    1220 "if the email template includes %%traffic_signals%%."
    1221 msgstr ""
    1222 
    1223 #: includes/class-settings-emails.php:132
    1224 msgid "Author Summary"
    1225 msgstr ""
    1226 
    1227 #: includes/class-settings-emails.php:140
    1228 msgid ""
    1229 "Include the authors whose content received the most tracked views during the "
    1230 "report period."
    1231 msgstr ""
    1232 
    1233 #: includes/class-settings-emails.php:141
    1234 #, php-format
    1235 msgid ""
    1236 "When matching data is available, this appears in the Report summary section "
    1237 "if the email template includes %%report_summary%%."
    1238 msgstr ""
    1239 
    1240 #: includes/class-settings-emails.php:145
    1241 msgid "Source Summary"
    1242 msgstr ""
    1243 
    1244 #: includes/class-settings-emails.php:153
    1245 msgid ""
    1246 "Include top referrers or source dimensions when source tracking is available."
    1247 msgstr ""
    1248 
    1249 #: includes/class-settings-emails.php:154
    1250 #, php-format
    1251 msgid ""
    1252 "When source tracking data is available, this appears in the Report summary "
    1253 "section if the email template includes %%report_summary%%."
    1254 msgstr ""
    1255 
    1256 #: includes/class-settings-emails.php:159
    1257 msgid "Recipient Email"
    1258 msgstr ""
    1259 
    1260 #: includes/class-settings-emails.php:163
    1261 msgid ""
    1262 "The email address that receives summary emails. Defaults to the site "
    1263 "administrator email."
    1264 msgstr ""
    1265 
    1266 #: includes/class-settings-emails.php:168
    1267 msgid "Additional Recipients"
    1268 msgstr ""
    1269 
    1270 #: includes/class-settings-emails.php:176
    1271 msgid "Send summaries to multiple people with CC and BCC recipients."
    1272 msgstr ""
    1273 
    1274 #: includes/class-settings-emails.php:180
    1275 msgid "CC Recipients"
    1276 msgstr ""
    1277 
    1278 #: includes/class-settings-emails.php:195
    1279 msgid "Send visible copies to additional recipients."
    1280 msgstr ""
    1281 
    1282 #: includes/class-settings-emails.php:199
    1283 msgid "BCC Recipients"
    1284 msgstr ""
    1285 
    1286 #: includes/class-settings-emails.php:214
    1287 msgid "Send private copies to additional recipients."
    1288 msgstr ""
    1289 
    1290 #: includes/class-settings-emails.php:219
    1291 msgid "Email Subject"
    1292 msgstr ""
    1293 
    1294 #: includes/class-settings-emails.php:229
    1295 msgid "Email Content"
    1296 msgstr ""
    1297 
    1298 #: includes/class-settings-emails.php:239
    1299 msgid "Send Test To"
    1300 msgstr ""
    1301 
    1302 #: includes/class-settings-emails.php:247
    1303 msgid "Schedule Status"
    1304 msgstr ""
    1305 
    1306 #: includes/class-settings-emails.php:250
    1307 msgid ""
    1308 "Weekly summaries are scheduled for Monday after the previous full week has "
    1309 "ended."
    1310 msgstr ""
    1311 
    1312 #: includes/class-settings-emails.php:251
    1313 msgid ""
    1314 "Summary emails are scheduled with WordPress cron. Delivery time may vary "
    1315 "depending on site traffic and hosting configuration."
    1316 msgstr ""
    1317 
    1318 #: includes/class-settings-emails.php:256
    1319 msgid "Next Scheduled Email"
    1320 msgstr ""
    1321 
    1322 #: includes/class-settings-emails.php:264
    1323 msgid "Last Sent"
    1324 msgstr ""
    1325 
    1326 #: includes/class-settings-emails.php:272
    1327 msgid "Last Error"
    1328 msgstr ""
    1329 
    1330 #: includes/class-settings-emails.php:287
    1331 msgid "Send email summaries with your most viewed content and view totals."
    1332 msgstr ""
    1333 
    1334 #: includes/class-settings-emails.php:296
    1335 msgid "Choose who should receive your email summaries."
    1336 msgstr ""
    1337 
    1338 #: includes/class-settings-emails.php:305
    1339 msgid "Review scheduling, delivery, and recent email status information."
    1340 msgstr ""
    1341 
    1342 #: includes/class-settings-emails.php:314
    1343 msgid "Customize the subject and message used for content views summaries."
    1344 msgstr ""
    1345 
    1346 #: includes/class-settings-emails.php:324
    1347 msgid ""
    1348 "Send a test using the latest available summary. This does not change the "
    1349 "saved recipient or scheduled delivery."
    1350 msgstr ""
    1351 
    1352 #: includes/class-settings-emails.php:337
    1353 msgid "email@example.com"
    1354 msgstr ""
    1355 
    1356 #: includes/class-settings-emails.php:337
    1357 msgid "Test email recipient"
    1358 msgstr ""
    1359 
    1360 #: includes/class-settings-emails.php:338
    1361 msgid "Send Test Email"
    1362 msgstr ""
    1363 
    1364 #: includes/class-settings-emails.php:338
    1365 msgid "Sending..."
    1366 msgstr ""
    1367 
    1368 #: includes/class-settings-emails.php:356
    1369 msgid "Sorry, you are not allowed to do that."
    1370 msgstr ""
    1371 
    1372 #: includes/class-settings-emails.php:395
    1373 #: includes/class-settings-emails.php:494
    1374 msgid "Enter a valid test recipient email address."
    1375 msgstr ""
    1376 
    1377 #: includes/class-settings-emails.php:417
    1378 msgid ""
    1379 "There is not enough tracked data from the latest complete week to send a "
    1380 "real test summary yet."
    1381 msgstr ""
    1382 
    1383 #: includes/class-settings-emails.php:437
    1384 msgid "The summary test email was sent successfully."
    1385 msgstr ""
    1386 
    1387 #: includes/class-settings-emails.php:445
    1388 #, php-format
    1389 msgid "The test email could not be sent. %s"
    1390 msgstr ""
    1391 
    1392 #: includes/class-settings-emails.php:446
    1393 msgid ""
    1394 "The test email could not be sent. Check your WordPress mail configuration "
    1395 "and try again."
    1396 msgstr ""
    1397 
    1398 #: includes/class-settings-emails.php:463
    1399 msgid "Enter a valid recipient email address for the views summary email."
    1400 msgstr ""
    1401 
    1402 #: includes/class-settings-emails.php:469
    1403 msgid "Recipient email is required while the views summary email is enabled."
    1404 msgstr ""
    1405 
    1406 #: includes/class-settings-emails.php:559
    1407 msgid "Minimum tracked views cannot be lower than 0."
    1408 msgstr ""
    1409 
    1410 #: includes/class-settings-emails.php:588
    1411 #, php-format
    1412 msgid ""
    1413 "%1$s contained tags that are not available in this field. Removed tags: "
    1414 "%2$s. Supported tags here: %3$s"
    1415 msgstr ""
    1416 
    1417 #: includes/class-settings-emails.php:711
    1418 msgid "Customize the subject line for summary emails."
    1419 msgstr ""
    1420 
    1421 #: includes/class-settings-emails.php:713
    1422 #, php-format
    1423 msgid "Use supported subject tags such as %1$s and %2$s."
    1424 msgstr ""
    1425 
    1426 #: includes/class-settings-emails.php:727
    1427 msgid "Customize the main message sent with each email summary."
    1428 msgstr ""
    1429 
    1430 #: includes/class-settings-emails.php:729
    1431 #, php-format
    1432 msgid "Use supported content tags such as %s."
    1433 msgstr ""
    1434 
    1435 #: includes/class-settings-emails.php:814
    1436 msgid "Not sent yet."
    1437 msgstr ""
    1438 
    1439 #: includes/class-settings-emails.php:831
    1440 msgid "Not scheduled."
    1441 msgstr ""
    1442 
    1443 #: includes/class-settings-emails.php:844
    1444 msgid "No email errors recorded yet."
    1445 msgstr ""
    1446 
    1447 #: includes/class-settings-general.php:54
    1448 msgid "PHP"
    1449 msgstr ""
    1450 
    1451 #: includes/class-settings-general.php:55
    1452 msgid "JavaScript"
    1453 msgstr ""
    1454 
    1455 #: includes/class-settings-general.php:56
    1456 msgid "REST API"
    1457 msgstr ""
    1458 
    1459 #: includes/class-settings-general.php:57
    1460 msgid "Fast AJAX"
    1461 msgstr ""
    1462 
    1463 #: includes/class-settings-general.php:72
    1464 msgid "Tracking Targets"
    1465 msgstr ""
    1466 
    1467 #: includes/class-settings-general.php:77
    1468 msgid "Tracking Behavior"
    1469 msgstr ""
    1470 
    1471 #: includes/class-settings-general.php:82
    1472 msgid "Visitor Exclusions"
    1473 msgstr ""
    1474 
    1475 #: includes/class-settings-general.php:87
    1476 msgid "Performance & Caching"
    1477 msgstr ""
    1478 
    1479 #: includes/class-settings-general.php:101
    1480 #: includes/class-settings-general.php:452
    1481 msgid "minutes"
    1482 msgstr ""
    1483 
    1484 #: includes/class-settings-general.php:102
    1485 #: includes/class-settings-general.php:217
    1486 msgid "hours"
    1487 msgstr ""
    1488 
    1489 #: includes/class-settings-general.php:103
    1490 msgid "days"
    1491 msgstr ""
    1492 
    1493 #: includes/class-settings-general.php:104
    1494 msgid "weeks"
    1495 msgstr ""
    1496 
    1497 #: includes/class-settings-general.php:105
    1498 msgid "months"
    1499 msgstr ""
    1500 
    1501 #: includes/class-settings-general.php:106
    1502 msgid "years"
    1503 msgstr ""
    1504 
    1505 #: includes/class-settings-general.php:111
    1506 msgid "crawlers"
    1507 msgstr ""
    1508 
    1509 #: includes/class-settings-general.php:112
    1510 msgid "AI bots"
    1511 msgstr ""
    1512 
    1513 #: includes/class-settings-general.php:130
    1514 msgid "Select post types whose views should be counted."
    1515 msgstr ""
    1516 
    1517 #: includes/class-settings-general.php:138
    1518 msgid "Enable counting views on taxonomy term archive pages."
    1519 msgstr ""
    1520 
    1521 #: includes/class-settings-general.php:150
    1522 msgid "Enable counting views on author archive pages."
    1523 msgstr ""
    1524 
    1525 #: includes/class-settings-general.php:159
    1526 msgid "Other Pages"
    1527 msgstr ""
    1528 
    1529 #: includes/class-settings-general.php:162
    1530 msgid ""
    1531 "Track views on the front page, post type archives, date archives, search "
    1532 "results, 404 pages, and WordPress authentication pages such as login, "
    1533 "registration, and password reset."
    1534 msgstr ""
    1535 
    1536 #: includes/class-settings-general.php:171
    1537 msgid "Traffic Sources"
    1538 msgstr ""
    1539 
    1540 #: includes/class-settings-general.php:174
    1541 msgid ""
    1542 "Collect aggregate stats about visitors' browsers, devices, operating systems "
    1543 "and referrers."
    1544 msgstr ""
    1545 
    1546 #: includes/class-settings-general.php:183
    1547 msgid "Counter Mode"
    1548 msgstr ""
    1549 
    1550 #: includes/class-settings-general.php:186
    1551 msgid ""
    1552 "Choose how views are recorded. If you use caching, select JavaScript, REST "
    1553 "API or Fast AJAX (up to <code>10+</code> times faster)."
    1554 msgstr ""
    1555 
    1556 #: includes/class-settings-general.php:194
    1557 msgid "Data Storage"
    1558 msgstr ""
    1559 
    1560 #: includes/class-settings-general.php:199
    1561 msgid ""
    1562 "Choose how to store the content views data in the user's browser - with or "
    1563 "without cookies."
    1564 msgstr ""
    1565 
    1566 #: includes/class-settings-general.php:202
    1567 msgid "Cookies"
    1568 msgstr ""
    1569 
    1570 #: includes/class-settings-general.php:203
    1571 msgid "Cookieless"
    1572 msgstr ""
    1573 
    1574 #: includes/class-settings-general.php:210
    1575 msgid "Count Interval"
    1576 msgstr ""
    1577 
    1578 #: includes/class-settings-general.php:223
    1579 msgid "Count Time"
    1580 msgstr ""
    1581 
    1582 #: includes/class-settings-general.php:229
    1583 msgid ""
    1584 "Whether to store the views using GMT timezone or adjust it to the GMT offset "
    1585 "of the site."
    1586 msgstr ""
    1587 
    1588 #: includes/class-settings-general.php:231
    1589 msgid "GMT Time"
    1590 msgstr ""
    1591 
    1592 #: includes/class-settings-general.php:232
    1593 msgid "Local Time"
    1594 msgstr ""
    1595 
    1596 #: includes/class-settings-general.php:238
    1597 msgid "Strict Counts"
    1598 msgstr ""
    1599 
    1600 #: includes/class-settings-general.php:246
    1601 msgid ""
    1602 "Prevent bypassing the count interval (for example by using incognito mode or "
    1603 "clearing cookies)."
    1604 msgstr ""
    1605 
    1606 #: includes/class-settings-general.php:251
    1607 msgid "Cleanup Interval"
    1608 msgstr ""
    1609 
    1610 #: includes/class-settings-general.php:254
    1611 #, php-format
    1612 msgid ""
    1613 "Delete daily content view data older than the period specified above. Enter "
    1614 "%s to keep data regardless of age. Cleanup runs once per day."
    1615 msgstr ""
    1616 
    1617 #: includes/class-settings-general.php:263
    1618 msgid "Caching Compatibility"
    1619 msgstr ""
    1620 
    1621 #: includes/class-settings-general.php:278
    1622 msgid "Object Cache Support"
    1623 msgstr ""
    1624 
    1625 #: includes/class-settings-general.php:293
    1626 msgid "Exclude Visitors"
    1627 msgstr ""
    1628 
    1629 #: includes/class-settings-general.php:296
    1630 msgid "Use this to exclude specific visitor groups from counting views."
    1631 msgstr ""
    1632 
    1633 #: includes/class-settings-general.php:307
    1634 msgid "Exclude User Roles"
    1635 msgstr ""
    1636 
    1637 #: includes/class-settings-general.php:310
    1638 msgid "Use this to exclude specific user roles from counting views."
    1639 msgstr ""
    1640 
    1641 #: includes/class-settings-general.php:326
    1642 msgid "Exclude IPs"
    1643 msgstr ""
    1644 
    1645 #: includes/class-settings-general.php:375
    1646 msgid ""
    1647 "Minimum time between counting new views from the same visitor, in hours. "
    1648 "Enter <code>0</code> to count every page view."
    1649 msgstr ""
    1650 
    1651 #: includes/class-settings-general.php:453
    1652 msgid "Persistent Object Cache"
    1653 msgstr ""
    1654 
    1655 #: includes/class-settings-general.php:453
    1656 msgid "available"
    1657 msgstr ""
    1658 
    1659 #: includes/class-settings-general.php:453
    1660 msgid "unavailable"
    1661 msgstr ""
    1662 
    1663 #: includes/class-settings-general.php:454
    1664 #, php-format
    1665 msgid ""
    1666 "How often to flush cached view counts from the object cache into the "
    1667 "database. This feature is used only if a persistent object cache like %s or "
    1668 "%s is detected and the interval is greater than %s. When used, view counts "
    1669 "will be collected and stored in the object cache instead of the database and "
    1670 "will then be asynchronously flushed to the database according to the "
    1671 "specified interval. The maximum value is %s which means 24 hours.%sNotice:%s "
    1672 "Potential data loss may occur if the object cache is cleared/unavailable for "
    1673 "the duration of the interval."
    1674 msgstr ""
    1675 
    1676 #: includes/class-settings-general.php:533
    1677 #: includes/class-settings-general.php:539
    1678 msgid "Remove"
    1679 msgstr ""
    1680 
    1681 #: includes/class-settings-general.php:547
    1682 msgid "Add new"
    1683 msgstr ""
    1684 
    1685 #: includes/class-settings-general.php:547
    1686 msgid "Add my current IP"
    1687 msgstr ""
    1688 
    1689 #: includes/class-settings-general.php:550
    1690 msgid ""
    1691 "Add IPv4 or IPv6 addresses to exclude them from counting views. Wildcards "
    1692 "are supported for IPv4 only (e.g. 192.168.0.*)."
    1693 msgstr ""
    1694 
    1695 #: includes/class-settings-general.php:589
    1696 msgid ""
    1697 "Control which post types, archives and other content types are included in "
    1698 "view counting."
    1699 msgstr ""
    1700 
    1701 #: includes/class-settings-general.php:598
    1702 msgid ""
    1703 "Control how views are recorded — counting mode, intervals, time zone, and "
    1704 "cleanup."
    1705 msgstr ""
    1706 
    1707 #: includes/class-settings-general.php:607
    1708 msgid ""
    1709 "Exclude specific visitor groups or IP addresses from incrementing view "
    1710 "counts."
    1711 msgstr ""
    1712 
    1713 #: includes/class-settings-general.php:616
    1714 msgid "Configure caching compatibility and object-cache handling for counting."
    1715 msgstr ""
    1716 
    1717 #: includes/class-settings-general.php:631
    1718 msgid "Currently detected active caching plugins"
    1719 msgstr ""
    1720 
    1721 #: includes/class-settings-general.php:639
    1722 msgid "No compatible caching plugins found."
    1723 msgstr ""
    1724 
    1725 #: includes/class-settings-general.php:651
    1726 msgid "Enable compatibility tweaks for supported caching plugins."
    1727 msgstr ""
    1728 
    1729 #: includes/class-settings-general.php:655
    1730 #: includes/class-settings-general.php:671
    1731 #: includes/class-settings-integrations.php:97
    1732 msgid "(available)"
    1733 msgstr ""
    1734 
    1735 #: includes/class-settings-general.php:667
    1736 msgid "Enable Redis or Memcached object cache optimization."
    1737 msgstr ""
    1738 
    1739 #: includes/class-settings-general.php:704
    1740 msgid "Persistent object cache has been detected."
    1741 msgstr ""
    1742 
    1743 #: includes/class-settings-general.php:707
    1744 #, php-format
    1745 msgid ""
    1746 "This feature requires a persistent object cache like %s or %s to be "
    1747 "installed and activated."
    1748 msgstr ""
    1749 
    1750 #: includes/class-settings-integrations.php:22 includes/class-settings.php:653
    1751 #: includes/class-settings.php:721 includes/class-settings.php:722
    1752 msgid "Integrations"
    1753 msgstr ""
    1754 
    1755 #: includes/class-settings-integrations.php:65
    1756 msgid "Manage available Post Views Counter integrations."
    1757 msgstr ""
    1758 
    1759 #: includes/class-settings-other.php:36
    1760 msgid "Plugin Status"
    1761 msgstr ""
    1762 
    1763 #: includes/class-settings-other.php:41
    1764 msgid "Data Import"
    1765 msgstr ""
    1766 
    1767 #: includes/class-settings-other.php:46
    1768 msgid "Data Removal"
    1769 msgstr ""
    1770 
    1771 #: includes/class-settings-other.php:61
    1772 msgid "License Key"
    1773 msgstr ""
    1774 
    1775 #: includes/class-settings-other.php:66
    1776 #, php-format
    1777 msgid ""
    1778 "Enter your %s license key (requires Pro version to be installed and active)."
    1779 msgstr ""
    1780 
    1781 #: includes/class-settings-other.php:73
    1782 msgid "Import From"
    1783 msgstr ""
    1784 
    1785 #: includes/class-settings-other.php:81
    1786 msgid "Import Strategy"
    1787 msgstr ""
    1788 
    1789 #: includes/class-settings-other.php:93
    1790 msgid ""
    1791 "Click Analyse Views to check how many views are available for import, or "
    1792 "Import Views to begin the import process."
    1793 msgstr ""
    1794 
    1795 #: includes/class-settings-other.php:99 includes/class-settings-other.php:274
    1796 msgid "Delete Views"
    1797 msgstr ""
    1798 
    1799 #: includes/class-settings-other.php:102
    1800 msgid ""
    1801 "Delete ALL the existing post views data. Note that this is an irreversible "
    1802 "process!"
    1803 msgstr ""
    1804 
    1805 #: includes/class-settings-other.php:108
    1806 msgid "Delete on Deactivation"
    1807 msgstr ""
    1808 
    1809 #: includes/class-settings-other.php:111
    1810 msgid ""
    1811 "When enabled, deactivating the plugin will delete all plugin data from the "
    1812 "database, including all content view counts."
    1813 msgstr ""
    1814 
    1815 #: includes/class-settings-other.php:112
    1816 msgid "Delete all plugin data on deactivation."
    1817 msgstr ""
    1818 
    1819 #: includes/class-settings-other.php:157
    1820 #, php-format
    1821 msgid ""
    1822 "%s is not currently available. Please install and activate the required "
    1823 "plugin."
    1824 msgstr ""
    1825 
    1826 #: includes/class-settings-other.php:167
    1827 msgid "Choose a data source to import existing view counts from."
    1828 msgstr ""
    1829 
    1830 #: includes/class-settings-other.php:179
    1831 #, php-format
    1832 msgid ""
    1833 "%s is not available. Please install and activate the required plugin to use "
    1834 "this import source."
    1835 msgstr ""
    1836 
    1837 #: includes/class-settings-other.php:236
    1838 msgid "Choose how to handle existing view counts when importing."
    1839 msgstr ""
    1840 
    1841 #: includes/class-settings-other.php:261
    1842 msgid "Analyse Views"
    1843 msgstr ""
    1844 
    1845 #: includes/class-settings-other.php:262
    1846 msgid "Import Views"
    1847 msgstr ""
    1848 
    1849 #: includes/class-settings-other.php:285
    1850 msgid "View license details and other status information."
    1851 msgstr ""
    1852 
    1853 #: includes/class-settings-other.php:325
    1854 msgid "Active"
    1855 msgstr ""
    1856 
    1857 #: includes/class-settings-other.php:327
    1858 msgid "Not Detected"
    1859 msgstr ""
    1860 
    1861 #: includes/class-settings-other.php:370
    1862 msgid "unknown"
    1863 msgstr ""
    1864 
    1865 #: includes/class-settings-other.php:384
    1866 msgid "Plugin Version"
    1867 msgstr ""
    1868 
    1869 #: includes/class-settings-other.php:392
    1870 msgid "Database Tables"
    1871 msgstr ""
    1872 
    1873 #: includes/class-settings-other.php:473
    1874 msgid ""
    1875 "Import view counts when migrating from custom meta key or other analytics "
    1876 "plugins."
    1877 msgstr ""
    1878 
    1879 #: includes/class-settings-other.php:482
    1880 msgid ""
    1881 "Choose what happens to stored view data on uninstall, and manage other "
    1882 "removal-related tools."
    1883 msgstr ""
    1884 
    1885 #: includes/class-settings-reports.php:57
    1886 msgid "Display Reports and Export Views to CSV/XML"
    1887 msgstr ""
    1888 
    1889 #: includes/class-settings-reports.php:58
    1890 msgid "View detailed stats about the popularity of your content."
    1891 msgstr ""
    1892 
    1893 #: includes/class-settings-reports.php:59
    1894 msgid "Generate views reports in any date range you need."
    1895 msgstr ""
    1896 
    1897 #: includes/class-settings-reports.php:60
    1898 msgid "Export, download and share your website views data."
    1899 msgstr ""
    1900 
    1901 #: includes/class-settings-reports.php:61 includes/class-settings.php:192
    1902 msgid "Upgrade to Pro"
    1903 msgstr ""
    1904 
    1905 #: includes/class-settings.php:193
    1906 msgid "Starting from $29 per year"
    1907 msgstr ""
    1908 
    1909 #: includes/class-settings.php:193
    1910 msgid "14-day money back guarantee."
    1911 msgstr ""
    1912 
    1913 #: includes/class-settings.php:210
    1914 msgid "Reliable view counting"
    1915 msgstr ""
    1916 
    1917 #: includes/class-settings.php:212
    1918 msgid "Accurate counts with caching enabled"
    1919 msgstr ""
    1920 
    1921 #: includes/class-settings.php:213
    1922 msgid "Better performance under high traffic"
    1923 msgstr ""
    1924 
    1925 #: includes/class-settings.php:214
    1926 msgid "Protection against fake and repeated views"
    1927 msgstr ""
    1928 
    1929 #: includes/class-settings.php:216
    1930 msgid ""
    1931 "Upgrade to Pro to make your view counts reliable on real-world, cached "
    1932 "WordPress sites."
    1933 msgstr ""
    1934 
    1935 #: includes/class-settings.php:219
    1936 msgid "Accurate & meaningful view display"
    1937 msgstr ""
    1938 
    1939 #: includes/class-settings.php:221
    1940 msgid "Choose which time period is displayed"
    1941 msgstr ""
    1942 
    1943 #: includes/class-settings.php:222
    1944 msgid "Always show up-to-date view counts"
    1945 msgstr ""
    1946 
    1947 #: includes/class-settings.php:223
    1948 msgid "Control where the counter appears"
    1949 msgstr ""
    1950 
    1951 #: includes/class-settings.php:225
    1952 msgid ""
    1953 "Upgrade to Pro to control what view data visitors actually see - not just "
    1954 "total counts."
    1955 msgstr ""
    1956 
    1957 #: includes/class-settings.php:228
    1958 msgid "Advanced view reports"
    1959 msgstr ""
    1960 
    1961 #: includes/class-settings.php:230
    1962 msgid ""
    1963 "Upgrade to Pro to access detailed reports and visual insights for your "
    1964 "content."
    1965 msgstr ""
    1966 
    1967 #: includes/class-settings.php:233
    1968 msgid "Use view counts where they actually matter"
    1969 msgstr ""
    1970 
    1971 #: includes/class-settings.php:235
    1972 msgid "Display and sort content by popularity"
    1973 msgstr ""
    1974 
    1975 #: includes/class-settings.php:236
    1976 msgid "Works with the tools you already use"
    1977 msgstr ""
    1978 
    1979 #: includes/class-settings.php:237
    1980 msgid "No custom queries or advanced setup"
    1981 msgstr ""
    1982 
    1983 #: includes/class-settings.php:239
    1984 msgid ""
    1985 "Upgrade to Pro to use view data across layouts and blocks - not just store "
    1986 "it."
    1987 msgstr ""
    1988 
    1989 #: includes/class-settings.php:242
    1990 msgid "Deeper email summaries"
    1991 msgstr ""
    1992 
    1993 #: includes/class-settings.php:244
    1994 msgid "Spot content gaining or losing momentum"
    1995 msgstr ""
    1996 
    1997 #: includes/class-settings.php:245
    1998 msgid "Include author and source summaries"
    1999 msgstr ""
    2000 
    2001 #: includes/class-settings.php:246
    2002 msgid "Send daily or monthly summaries"
    2003 msgstr ""
    2004 
    2005 #: includes/class-settings.php:247
    2006 msgid "Add CC/BCC recipients for your team"
    2007 msgstr ""
    2008 
    2009 #: includes/class-settings.php:249
    2010 msgid ""
    2011 "Go beyond a weekly top-content list. Pro adds richer summaries with trends, "
    2012 "authors, sources, and flexible delivery options."
    2013 msgstr ""
    2014 
    2015 #: includes/class-settings.php:252
    2016 msgid "Migrate your view data safely"
    2017 msgstr ""
    2018 
    2019 #: includes/class-settings.php:254
    2020 msgid "Import view data from other WordPress plugins"
    2021 msgstr ""
    2022 
    2023 #: includes/class-settings.php:255
    2024 msgid "Choose how existing data is handled"
    2025 msgstr ""
    2026 
    2027 #: includes/class-settings.php:256
    2028 msgid "Support for posts and other content types"
    2029 msgstr ""
    2030 
    2031 #: includes/class-settings.php:258
    2032 msgid ""
    2033 "Upgrade to Pro to migrate historical view data without losing control over "
    2034 "existing counts."
    2035 msgstr ""
    2036 
    2037 #: includes/class-settings.php:638 includes/class-settings.php:691
    2038 #: includes/class-settings.php:692
    2039 msgid "Counting"
    2040 msgstr ""
    2041 
    2042 #: includes/class-settings.php:643 includes/class-settings.php:701
    2043 #: includes/class-settings.php:702
    2044 msgid "Display"
    2045 msgstr ""
    2046 
    2047 #: includes/class-settings.php:648 includes/class-settings.php:711
    2048 #: includes/class-settings.php:712 includes/class-settings.php:754
    2049 msgid "Reports"
    2050 msgstr ""
    2051 
    2052 #: includes/class-settings.php:658 includes/class-settings.php:731
    2053 #: includes/class-settings.php:732
    2054 msgid "Emails"
    2055 msgstr ""
    2056 
    2057 #: includes/class-settings.php:663 includes/class-settings.php:741
    2058 #: includes/class-settings.php:742
    2059 msgid "Other"
    2060 msgstr ""
    2061 
    2062 #: includes/class-settings.php:942
    2063 msgid "All existing data deleted successfully."
    2064 msgstr ""
    2065 
    2066 #: includes/class-settings.php:944
    2067 msgid "Error occurred. All existing data were not deleted."
    2068 msgstr ""
    2069 
    2070 #: includes/class-settings.php:1088
    2071 msgid "NEW!"
    2072 msgstr ""
    2073 
    2074 #: includes/class-toolbar.php:132
    2075 #, php-format
    2076 msgid "%s post view"
    2077 msgstr ""
    2078 
    2079 #. translators: 1: month and year, 2: total number of post views
    2080 #: includes/class-toolbar.php:140
    2081 #, php-format
    2082 msgid "Post views chart for %1$s, %2$s total"
    2083 msgstr ""
    2084 
    2085 #: includes/class-traffic-signals.php:95
    2086 msgid "Traffic Signals"
    2087 msgstr ""
    2088 
    2089 #: includes/class-traffic-signals.php:133
    2090 msgid "No unusual activity detected."
    2091 msgstr ""
    2092 
    2093 #: includes/class-traffic-signals.php:139
    2094 msgid ""
    2095 "Unusual traffic pattern detected. More insights available in Post Views "
    2096 "Counter Pro."
    2097 msgstr ""
    2098 
    2099 #: includes/class-update.php:212
    2100 msgid ""
    2101 "<strong>Post Views Counter</strong> - this version requires a database "
    2102 "update. Make sure to back up your database first."
    2103 msgstr ""
    2104 
    2105 #: includes/class-update.php:213
    2106 msgid "Run the Update"
    2107 msgstr ""
    2108 
    2109 #: includes/class-update.php:307
    2110 msgid "Thank you! Datebase was successfully updated."
    2111 msgstr ""
    2112 
    2113 #: includes/class-widgets.php:55 includes/class-widgets.php:63
    2114 msgid "Most Viewed Posts"
    2115 msgstr ""
    2116 
    2117 #: includes/class-widgets.php:57
    2118 msgid "Displays a list of the most viewed posts"
    2119 msgstr ""
    2120 
    2121 #: includes/class-widgets.php:74
    2122 msgid "No most viewed posts found"
    2123 msgstr ""
    2124 
    2125 #: includes/class-widgets.php:220
    2126 msgid "Display post views?"
    2127 msgstr ""
    2128 
    2129 #: includes/class-widgets.php:222
    2130 msgid "Display post excerpt?"
    2131 msgstr ""
    2132 
    2133 #: includes/class-widgets.php:224
    2134 msgid "Display post author?"
    2135 msgstr ""
    2136 
    2137 #: includes/class-widgets.php:226
    2138 msgid "Display post thumbnail?"
    2139 msgstr ""
    2140 
    2141 #: post-views-counter.php:772
    2142 #, php-format
    2143 msgid ""
    2144 "Hey, you've been using <strong>Post Views Counter</strong> for more than %s."
    2145 msgstr ""
    2146 
    2147 #: post-views-counter.php:772
    2148 msgid ""
    2149 "Could you please do me a BIG favor and give it a 5-star rating on WordPress "
    2150 "to help us spread the word and boost our motivation."
    2151 msgstr ""
    2152 
    2153 #: post-views-counter.php:772
     2593#: blocks/pro-placeholder/term-views/block.json
     2594msgctxt "block title"
     2595msgid "Term Views"
     2596msgstr ""
     2597
     2598#: blocks/pro-placeholder/term-views/block.json
     2599msgctxt "block description"
     2600msgid "Display views for a given term. Requires Post Views Counter Pro."
     2601msgstr ""
     2602
     2603#: blocks/pro-placeholder/term-views/block.json
     2604msgctxt "block keyword"
     2605msgid "term"
     2606msgstr ""
     2607
     2608#: blocks/pro-placeholder/user-views/block.json
     2609msgctxt "block title"
     2610msgid "Author Views"
     2611msgstr ""
     2612
     2613#: blocks/pro-placeholder/user-views/block.json
     2614msgctxt "block description"
     2615msgid "Display views for a given author. Requires Post Views Counter Pro."
     2616msgstr ""
     2617
     2618#: blocks/pro-placeholder/user-views/block.json
     2619msgctxt "block keyword"
     2620msgid "profile"
     2621msgstr ""
     2622
     2623#: post-views-counter.php:796
     2624#, php-format
     2625msgid "Hey, you've been using <strong>Post Views Counter</strong> for more than %s."
     2626msgstr ""
     2627
     2628#: post-views-counter.php:796
     2629msgid "Could you please do me a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation."
     2630msgstr ""
     2631
     2632#: post-views-counter.php:796
    21542633msgid "Your help is much appreciated. Thank you very much"
    21552634msgstr ""
    21562635
    2157 #: post-views-counter.php:772
     2636#: post-views-counter.php:796
    21582637msgid "founder of"
    21592638msgstr ""
    21602639
    2161 #: post-views-counter.php:772
     2640#: post-views-counter.php:796
    21622641msgid "Ok, you deserve it"
    21632642msgstr ""
    21642643
    2165 #: post-views-counter.php:772
     2644#: post-views-counter.php:796
    21662645msgid "Nope, maybe later"
    21672646msgstr ""
    21682647
    2169 #: post-views-counter.php:772
     2648#: post-views-counter.php:796
    21702649msgid "I already did"
    21712650msgstr ""
    21722651
    2173 #: post-views-counter.php:1155
     2652#: post-views-counter.php:990
     2653msgid "Post Views Counter could not safely recover data from a previous destructive deactivation. Retry activation after checking the database."
     2654msgstr ""
     2655
     2656#: post-views-counter.php:1327
    21742657msgid "Are you sure you want to reset these settings to defaults?"
    21752658msgstr ""
    21762659
    2177 #: post-views-counter.php:1156
     2660#: post-views-counter.php:1328
    21782661msgid "Are you sure you want to delete all existing data?"
    21792662msgstr ""
    21802663
    2181 #: post-views-counter.php:1157
     2664#: post-views-counter.php:1329
    21822665msgid "Are you sure you want to import views now?"
    21832666msgstr ""
    21842667
    2185 #: post-views-counter.php:1159
     2668#: post-views-counter.php:1331
    21862669msgid "The test email request did not return a valid response."
    21872670msgstr ""
    21882671
    2189 #: post-views-counter.php:1160
     2672#: post-views-counter.php:1332
    21902673msgid "The test email request could not be completed. Please try again."
    21912674msgstr ""
  • post-views-counter/tags/1.8.0/post-views-counter.php

    r3647380 r3734102  
    22/*
    33Plugin Name: Post Views Counter
    4 Description: Post Views Counter allows you to collect and display how many times a post, page, or other content has been viewed in a simple, fast and reliable way.
    5 Version: 1.7.15
     4Description: Count and display post views, track site-wide visits, and see what content works – inside WordPress. Fast, easy to use and privacy-first.
     5Version: 1.8.0
    66Author: dFactory
    77Author URI: https://dfactory.co/
    … …  
    1111Text Domain: post-views-counter
    1212Domain Path: /languages
     13Requires at least: 6.4
     14Requires PHP: 7.4
    1315
    1416Post Views Counter
    … …  
    3133         *
    3234         * @class Post_Views_Counter
    33          * @version     1.7.15
     35         * @version     1.8.0
    3436         */
    3537        final class Post_Views_Counter {
    … …  
    7880                        ],
    7981                        'display'       => [
    80                                 'label'                                 => 'Post Views:',
     82                                'label'                                 => 'Views:',
     83                                'frontend_counter_metrics'      => 'views',
     84                                'frontend_template'             => '%%icon%% Views: %%views%%',
    8185                                'display_period'                => 'total',
    8286                                'taxonomies'                    => false,
    … …  
    144148                                'schedule_version'              => 1
    145149                        ],
    146                         'version'       => '1.7.15'
     150                        'version'       => '1.8.0'
    147151                ];
    148152
    149153                // instances
     154                public $columns;
    150155                public $counter;
    151156                public $crawler;
    … …  
    158163                public $settings_api;
    159164                public $import;
     165                public $visits;
    160166
    161167                /**
    … …  
    184190                                // short init?
    185191                                if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
     192                                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-visits-query.php' );
     193                                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-visits.php' );
    186194                                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-counter.php' );
    187195                                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-crawler-detect.php' );
    188196                                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/functions.php' );
    189197
     198                                        self::$instance->visits = new Post_Views_Counter_Visits();
    190199                                        self::$instance->counter = new Post_Views_Counter_Counter();
    191200                                        self::$instance->crawler = new Post_Views_Counter_Crawler_Detect();
    … …  
    211220                                        // initialize other classes
    212221                                        self::$instance->functions = new Post_Views_Counter_Functions();
     222                                        self::$instance->visits = new Post_Views_Counter_Visits();
    213223
    214224                                        new Post_Views_Counter_Update();
    … …  
    224234                                        self::$instance->counter = new Post_Views_Counter_Counter();
    225235
    226                                         new Post_Views_Counter_Columns();
     236                                        self::$instance->columns = new Post_Views_Counter_Columns();
    227237                                        new Post_Views_Counter_Columns_Modal();
    228238                                        new Post_Views_Counter_Traffic_Signals();
    … …  
    262272                 */
    263273                private function includes() {
     274                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-visits-query.php' );
    264275                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-functions.php' );
     276                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-visits.php' );
    265277                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-update.php' );
    266278                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings-api.php' );
    … …  
    290302                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-frontend.php' );
    291303                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-dashboard.php' );
     304                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-dashboard-comparison.php' );
    292305                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-widgets.php' );
     306                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-entrances-eligibility.php' );
    293307                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-traffic-signals.php' );
    294308                        include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-integration-gutenberg.php' );
    … …  
    939953                 */
    940954                public function activation( $network ) {
     955                        $activation_failed = false;
     956
    941957                        // network activation?
    942958                        if ( is_multisite() && $network ) {
    943959                                global $wpdb;
    944960
     961                                if ( $this->visits instanceof Post_Views_Counter_Visits )
     962                                        $this->visits->reset_network_schema_migration();
     963
    945964                                // get all available sites
    946965                                $blogs_ids = $wpdb->get_col( 'SELECT blog_id FROM ' . $wpdb->blogs );
    … …  
    950969                                        switch_to_blog( (int) $blog_id );
    951970
    952                                         // run current site activation process
    953                                         $this->activate_site();
    954 
    955                                         restore_current_blog();
    956                                 }
     971                                        try {
     972                                                // Keep network activation on its existing work profile. The bounded
     973                                                // network cursor adds and verifies Visits after activation returns.
     974                                                        $activation_failed = ! $this->activate_site_internal( false, true );
     975                                                } finally {
     976                                                        restore_current_blog();
     977                                                }
     978
     979                                                if ( $activation_failed )
     980                                                        break;
     981                                        }
     982
     983                                if ( $this->visits instanceof Post_Views_Counter_Visits )
     984                                        $this->visits->schedule_schema_sweep();
     985
    957986                        } else
    958                                 $this->activate_site();
     987                                $activation_failed = ! $this->activate_site_internal( true, true );
     988
     989                        if ( $activation_failed )
     990                                wp_die( esc_html__( 'Post Views Counter could not safely recover data from a previous destructive deactivation. Retry activation after checking the database.', 'post-views-counter' ) );
    959991                }
    960992
    … …  
    9681000                 */
    9691001                public function activate_site() {
     1002                        $this->activate_site_internal( true, false );
     1003                }
     1004
     1005                /**
     1006                 * Activate one site with internal migration control.
     1007                 *
     1008                 * @global object $wpdb
     1009                 * @global string $charset_collate
     1010                 *
     1011                 * @param bool $migrate_visits Whether to add and verify the Visits schema now.
     1012                 * @param bool $explicit Whether explicit activation may clear a tombstone.
     1013                 * @return bool
     1014                 */
     1015                private function activate_site_internal( $migrate_visits, $explicit = false ) {
    9701016                        global $wpdb, $charset_collate;
    971 
    972                         // required for dbdelta
    973                         require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    974 
    975                         // create post views table
    976                         dbDelta( '
     1017                        $activation_state = $this->visits instanceof Post_Views_Counter_Visits ? $this->visits->prepare_site_activation( $explicit ) : 'normal';
     1018
     1019                        if ( $activation_state === false )
     1020                                return false;
     1021
     1022                        try {
     1023                                if ( $activation_state === 'tombstone' ) {
     1024                                        $table = $wpdb->prefix . 'post_views';
     1025                                        $wpdb->last_error = '';
     1026                                        $dropped = $wpdb->query( 'DROP TABLE IF EXISTS ' . $table );
     1027                                        $remaining = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
     1028
     1029                                        if ( $dropped === false || $wpdb->last_error !== '' || $remaining !== null )
     1030                                                return false;
     1031                                }
     1032
     1033                                // required for dbdelta
     1034                                require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
     1035
     1036                                // create post views table
     1037                                dbDelta( '
    9771038                                CREATE TABLE IF NOT EXISTS ' . $wpdb->prefix . 'post_views (
    9781039                                        `id` bigint unsigned NOT NULL,
    … …  
    9801041                                        `period` varchar(8) NOT NULL,
    9811042                                        `count` bigint unsigned NOT NULL,
     1043                                        ' . ( $migrate_visits ? '`visits` bigint unsigned NOT NULL DEFAULT 0,' : '' ) . '
    9821044                                        PRIMARY KEY  (type, period, id),
    9831045                                        UNIQUE INDEX id_type_period_count (id, type, period, count) USING BTREE,
    9841046                                        INDEX type_period_count (type, period, count) USING BTREE
    9851047                                ) ' . $charset_collate . ';'
    986                         );
    987 
    988                         // add default options
    989                         add_option( 'post_views_counter_settings_general', $this->defaults['general'], null, false );
    990                         add_option( 'post_views_counter_settings_display', $this->defaults['display'], null, false );
    991                         add_option( 'post_views_counter_settings_other', $this->defaults['other'], null, false );
    992                         add_option( 'post_views_counter_settings_emails', $this->get_default_emails_settings(), null, false );
    993                         add_option( 'post_views_counter_version', $this->defaults['version'], null, false );
    994 
    995                         if ( $this->emails_scheduler instanceof Post_Views_Counter_Emails_Scheduler )
    996                                 $this->emails_scheduler->maybe_schedule( get_option( 'post_views_counter_settings_emails', $this->get_default_emails_settings() ) );
     1048                                );
     1049
     1050                                if ( $activation_state === 'tombstone' ) {
     1051                                        $wpdb->last_error = '';
     1052                                        $row_count = $wpdb->get_var( 'SELECT COUNT(*) FROM ' . $wpdb->prefix . 'post_views' );
     1053
     1054                                        if ( $wpdb->last_error !== '' || (int) $row_count !== 0 || ! $this->visits->complete_site_activation() )
     1055                                                return false;
     1056                                }
     1057
     1058                                // Verify the additive Visits schema for single-site activation and
     1059                                // newly initialized sites. Network activation defers this work.
     1060                                $visits_ready = ! $migrate_visits || ( $this->visits instanceof Post_Views_Counter_Visits && $this->visits->migrate_schema( true ) );
     1061
     1062                                // add default options
     1063                                add_option( 'post_views_counter_settings_general', $this->defaults['general'], null, false );
     1064                                add_option( 'post_views_counter_settings_display', $this->defaults['display'], null, false );
     1065                                add_option( 'post_views_counter_settings_other', $this->defaults['other'], null, false );
     1066                                add_option( 'post_views_counter_settings_emails', $this->get_default_emails_settings(), null, false );
     1067                                add_option( 'post_views_counter_version', $this->defaults['version'], null, false );
     1068
     1069                                if ( $migrate_visits && $visits_ready && $this->visits instanceof Post_Views_Counter_Visits )
     1070                                        $this->visits->refresh_availability( true );
     1071
     1072                                if ( $this->emails_scheduler instanceof Post_Views_Counter_Emails_Scheduler )
     1073                                        $this->emails_scheduler->maybe_schedule( get_option( 'post_views_counter_settings_emails', $this->get_default_emails_settings() ) );
     1074
     1075                                if ( $this->visits instanceof Post_Views_Counter_Visits )
     1076                                        $this->visits->schedule_schema_sweep();
     1077
     1078                                return $visits_ready;
     1079                        } finally {
     1080                                if ( $this->visits instanceof Post_Views_Counter_Visits )
     1081                                        $this->visits->finish_site_activation();
     1082                        }
    9971083                }
    9981084
    … …  
    10251111                        if ( is_multisite() && $network ) {
    10261112                                global $wpdb;
     1113                                $blocked_deactivation = false;
    10271114
    10281115                                // get all available sites
    … …  
    10301117
    10311118                                foreach ( $blogs_ids as $blog_id ) {
    1032                                         // change to another site
    1033                                         switch_to_blog( (int) $blog_id );
    1034 
    1035                                         // run current site deactivation process
    1036                                         $this->deactivate_site( true );
    1037 
    1038                                         restore_current_blog();
    1039                                 }
     1119                                        $switched = (int) get_current_blog_id() !== (int) $blog_id;
     1120
     1121                                        // Avoid an unnecessary callback-capable switch for the origin site.
     1122                                        if ( $switched )
     1123                                                switch_to_blog( (int) $blog_id );
     1124
     1125                                        try {
     1126                                                // run current site deactivation process
     1127                                                $deactivation = $this->deactivate_site_internal( true );
     1128                                                $blocked_deactivation = in_array( $deactivation, [ 'measurement_fence_unlock_failed', 'schema_lease_retry_blocked' ], true );
     1129                                        } finally {
     1130                                                // A failed UNLOCK TABLES leaves this connection restricted to the
     1131                                                // current site's locked tables. restore_current_blog() can dispatch
     1132                                                // callbacks, so preserve the switched context for request recovery.
     1133                                                if ( ! $blocked_deactivation && $switched )
     1134                                                        restore_current_blog();
     1135                                        }
     1136
     1137                                        if ( $blocked_deactivation )
     1138                                                break;
     1139                                }
     1140
     1141                                if ( ! $blocked_deactivation && $this->visits instanceof Post_Views_Counter_Visits )
     1142                                        $this->visits->delete_network_state();
    10401143                        } else
    10411144                                $this->deactivate_site();
    … …  
    10521155                 */
    10531156                public function deactivate_site( $multi = false ) {
    1054                         if ( $this->emails_scheduler instanceof Post_Views_Counter_Emails_Scheduler )
    1055                                 $this->emails_scheduler->clear();
    1056                         else
    1057                                 wp_clear_scheduled_hook( 'pvc_weekly_content_summary_send' );
    1058 
     1157                        $this->deactivate_site_internal( $multi );
     1158                }
     1159
     1160                /**
     1161                 * Deactivate one site and return an internal failed-unlock signal only to
     1162                 * the network coordinator. The public deactivate_site() API stays void.
     1163                 *
     1164                 * @param bool $multi
     1165                 * @return bool|string
     1166                 */
     1167                private function deactivate_site_internal( $multi = false ) {
    10591168                        if ( $multi === true ) {
    10601169                                $options = get_option( 'post_views_counter_settings_other', [] );
    … …  
    10631172                                $check = $this->options['other']['deactivation_delete'];
    10641173
     1174                        if ( $check && $this->visits instanceof Post_Views_Counter_Visits ) {
     1175                                $deletion = $this->visits->delete_site_state();
     1176
     1177                                if ( $deletion === 'measurement_fence_unlock_failed' ) {
     1178                                        // Deactivation is a one-shot lifecycle event. The failed unlock keeps
     1179                                        // Core's FIFO, tombstone, and schema lease as durable retry obligations;
     1180                                        // do not move the derived boundary, clear schedules, or publish any acknowledgement.
     1181                                        error_log( 'Post Views Counter: destructive deactivation stopped because its protected table fence could not be released; retry deactivation after recovery.' );
     1182                                        return 'measurement_fence_unlock_failed';
     1183                                }
     1184
     1185                                if ( $deletion === 'schema_lease_retry_blocked' ) {
     1186                                        // A fresh retry cannot safely continue while the failed request's
     1187                                        // durable schema lease remains unexpired. Keep all lifecycle cleanup
     1188                                        // and acknowledgement work for the later takeover-capable retry.
     1189                                        error_log( 'Post Views Counter: destructive deactivation remains blocked by its retained schema lease; retry after lease expiry or takeover.' );
     1190                                        return 'schema_lease_retry_blocked';
     1191                                }
     1192
     1193                                if ( ! $deletion ) {
     1194                                        $check = false;
     1195                                        error_log( 'Post Views Counter: destructive deactivation was skipped because Visit cache state could not be retired safely.' );
     1196                                }
     1197                        }
     1198
     1199                        if ( $check ) {
     1200                                global $wpdb;
     1201                                $table = $wpdb->prefix . 'post_views';
     1202                                $wpdb->last_error = '';
     1203                                $dropped = $wpdb->query( 'DROP TABLE IF EXISTS ' . $table );
     1204                                $remaining = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
     1205
     1206                                if ( $dropped === false || $wpdb->last_error !== '' || $remaining !== null ) {
     1207                                        $check = false;
     1208                                        error_log( 'Post Views Counter: destructive deactivation table cleanup failed; the queue-generation tombstone was retained.' );
     1209                                }
     1210
     1211                                if ( $this->visits instanceof Post_Views_Counter_Visits )
     1212                                        $this->visits->finish_site_state_deletion();
     1213                        }
     1214
     1215                        if ( ! $check && $this->visits instanceof Post_Views_Counter_Visits )
     1216                                $this->visits->suspend_live_visit_writes( 'core_deactivated' );
     1217
     1218                        if ( $this->emails_scheduler instanceof Post_Views_Counter_Emails_Scheduler )
     1219                                $this->emails_scheduler->clear();
     1220                        else
     1221                                wp_clear_scheduled_hook( 'pvc_weekly_content_summary_send' );
     1222
    10651223                        // delete options if needed
    10661224                        if ( $check ) {
    1067                                 // Give a compatible Pro client one non-blocking opportunity to
     1225                                // Give a compatible extension client one non-blocking opportunity to
    10681226                                // release the remote license before Free removes shared settings.
    10691227                                if ( function_exists( 'Post_Views_Counter_Pro' ) ) {
    … …  
    10851243                                delete_transient( 'post_views_counter_ip_cache' );
    10861244
    1087                                 global $wpdb;
    1088 
    1089                                 // delete table from database
    1090                                 $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'post_views' );
    10911245                        }
    10921246
    … …  
    10941248                        wp_clear_scheduled_hook( 'pvc_reset_counts' );
    10951249
     1250                        if ( $this->visits instanceof Post_Views_Counter_Visits )
     1251                                $this->visits->clear_schema_sweep();
     1252
    10961253                        remove_action( 'pvc_reset_counts', [ $this->cron, 'reset_counts' ] );
     1254
     1255                        return true;
    10971256                }
    10981257
    … …  
    11091268                                switch_to_blog( $site->blog_id );
    11101269
    1111                                 // run current site activation process
    1112                                 $this->activate_site();
    1113 
    1114                                 restore_current_blog();
     1270                                try {
     1271                                        // run current site activation process
     1272                                        if ( ! $this->activate_site_internal( true, false ) )
     1273                                                error_log( 'Post Views Counter: new-site initialization remained fail-closed.' );
     1274                                } finally {
     1275                                        restore_current_blog();
     1276                                }
    11151277                        }
    11161278                }
    … …  
    12131375
    12141376                                // prepare script data
     1377                                // Bulk Edit counter writes happen server-side on the native
     1378                                // bulk_edit_posts completion hook. No shipped script calls the
     1379                                // retained save_bulk_post_views compatibility endpoint, so its
     1380                                // nonce is no longer printed.
    12151381                                $script_data = [
    1216                                         'nonce'                 => wp_create_nonce( 'pvc_save_bulk_post_views' ),
    12171382                                        'wpVersion59'   => version_compare( $wp_version, '5.9', '>=' )
    12181383                                ];
  • post-views-counter/tags/1.8.0/readme.txt

    r3647380 r3734102  
    22Contributors: dfactory
    33Tags: counter, postviews, statistics, analytics, pageviews
    4 Requires at least: 6.3.0
    5 Requires PHP: 7.0
     4Requires at least: 6.4
     5Requires PHP: 7.4
    66Tested up to: 7.1
    7 Stable tag: 1.7.15
     7Stable tag: 1.8.0
    88License: MIT
    99License URI: http://opensource.org/licenses/MIT
    1010
    11 Post Views Counter allows you to collect and display how many times a post, page, or other content has been viewed in a simple, fast and reliable way.
     11Count and display post views, track site-wide visits, and see what content works – inside WordPress. Fast, easy to use and privacy-first.
    1212
    1313== Description ==
    1414
    15 Post Views Counter allows you to collect and display how many times a post, page, or other content has been viewed in a simple, fast and reliable way.
    16 
    17 [Post Views Counter](https://postviewscounter.com/) gives you clear, accurate post view stats — right inside WordPress. No external tools. No bloat. Just the numbers you need to see what’s working.
     15[Post Views Counter](https://postviewscounter.com/) counts how many times your posts, pages and other content are viewed, shows those counts on your site, and helps you see which content works – right inside WordPress. No external tools. No bloat. Just the numbers you need to see what’s working.
     16
     17It counts two things:
     18
     19- **Views** – Counted views of your selected posts, pages and other content, according to your counting settings. Views can be displayed on your site and used to sort content.
     20- **Visits** – A Visit starts with a browser’s first counted view and covers subsequent counted content views within the fixed Count Interval. Visits are shown as site-wide totals beside Views, not per post.
    1821
    1922= Key Benefits =
    … …  
    2124Clarity, speed, and control:
    2225
    23 - **Clear, Focused Metrics** — You get a clear picture of how your posts are performing.
    24 - **Made for WordPress** — Runs entirely in your site. No GA, no third-party pipes; accurate counts in your Dashboard.
    25 - **Privacy-first** — Data lives on your server, with controls that respect visitors’ rights and privacy regulations.
    26 - **Works at scale** — Minimal overhead, no external scripts, Multisite-ready.
    27 - **Display anywhere** — Automatically show counts, or place them exactly where you want via blocks or shortcode.
     26- **Clear, Focused Metrics** – Views for every post and Visits for the whole site give you a clear picture of how your content is performing.
     27- **Made for WordPress** – Runs entirely in your site. No GA, no third-party pipes; accurate counts in your Dashboard and post lists.
     28- **Your counting rules** – Choose which content to count, exclude selected visitors, and set the interval for repeat counts.
     29- **Privacy-first** – Views and Visits are stored as aggregate totals on your server. Browser storage helps prevent repeat counting within your Count Interval. Pro offers cookieless browser storage; its optional Strict Counts feature also caches IP-derived data to limit repeat counts.
     30- **Works at scale** – Minimal overhead, no external scripts, Multisite-ready.
     31- **Display anywhere** – Automatically show counts, or place them exactly where you want via blocks, shortcode or PHP.
    2832
    2933= Features =
    … …  
    3236
    3337- Count & display views for **any post type** you select.
     38- Count **Visits** and see them beside Views in the dashboard chart.
    3439- Three counting modes: **PHP, JavaScript, REST API**
    35 - Dashboard post views **stats widget**
    36 - Sortable Post Views **admin column**
     40- **Post Views dashboard widget**: a chart of Views, Visits or both, with a Views comparison for the selected month. A month in progress is compared with the same days of the previous month.
     41- **Top Posts dashboard widget**, month by month.
     42- Sortable Post Views **admin column**, with a Views chart for each post.
     43- **Traffic Signals** admin column that flags unusual traffic changes compared with the same days of the previous month.
     44- **Weekly email summary** of how your content performed.
    3745- Exclude bots, logged-in users, specific roles, or IPs
    3846- Manually adjust a post’s views when needed.
    3947- Query and **order content by views** (developer-friendly)
    4048- Custom REST API endpoints
    41 - Option to set count interval
     49- **Count Interval** to set the fixed window for repeat counts from the same browser.
    4250- One-click data import from **WP-PostViews**, **Statify** and **Page Views Count**
    43 - Post views **display position**, automatic or manual via shortcode
    44 - **Multisite** compatibile
     51- Show the counter automatically, or place it with the **Post Views block**, shortcode or PHP function.
     52- **Most Viewed Posts** block and widget.
     53- **Multisite** compatible
    4554- **WPML/Polylang** compatible; translation-ready (.pot)
    4655
    … …  
    4958More capability without extra complexity:
    5059
    51 - **Fast AJAX counting** for more accurate data.
    52 - **Caching optimization** that guarantees performance even under heavy traffic.
    53 - **Reports**: Views by Date, Post, Author to spot winners, trends, and top contributors.
     60- **Fast AJAX counting** that keeps counting light on busy sites.
     61- **Caching compatibility** and dynamic loading to count through cached pages and refresh displayed counts when a compatible counting mode is selected.
     62- **Reports**: Views by Date, Post and Author to spot winners, trends and top contributors, plus **Visits in aggregate date reports**.
     63- **Visit trends and Views per visit** in dashboard insights and email summaries.
     64- **Performance Insights** that explain how a post’s Views changed, in the admin column, Traffic Signals and email summaries.
     65- **Daily, weekly and monthly email summaries** with insights.
     66- **Traffic sources**: aggregate stats on referrers, devices, browsers, operating systems and languages.
     67- Views for **taxonomy terms, authors** and other archives.
    5468- Customizable **Views Period** (e.g., last 7/30 days) to control the views count timeframe.
    5569- **Export to CSV/XML** to download and share data.
    … …  
    7084= Why use Post Views Counter vs Google Analytics? =
    7185
    72 Post Views Counter gives you clean, per-post view counts inside WordPress — fast, cache-friendly, and privacy-first, with data that stays on your server. Google Analytics might be an overkill when you just need accurate post/page views for editorial decisions.
     86Post Views Counter gives you per-post Views and site-wide Visits inside WordPress. It is fast, easy to use and privacy-first, with counting data stored on your server and control over what, who and when you count. Google Analytics may be more than you need when you want content view statistics for editorial decisions.
    7387
    7488= Can I use Post Views Counter alongside Google Analytics? =
    7589
    76 Of course — many sites use both. Post Views Counter handles on-site, per-post view counts inside WordPress (no third-party scripts), while Google Analytics covers marketing funnels and acquisition.
     90Of course – many sites use both. Post Views Counter handles on-site, per-post view counts inside WordPress (no third-party scripts), while Google Analytics covers marketing funnels and acquisition.
     91
     92= What is the difference between Views and Visits? =
     93
     94A View belongs to the content being viewed and is counted according to your counting settings. A Visit starts with a browser’s first counted view and covers subsequent counted content views within the fixed Count Interval. Viewing another eligible post within that window adds a View without starting another Visit. Later views do not extend the window.
     95
     96= Why do Visits differ from sessions in Google Analytics? =
     97
     98Post Views Counter follows the counting rules you choose: which content to count, which visitors to include or exclude, and how often to count them again. These rules may differ from your Google Analytics setup, so the totals are not expected to match.
     99
     100The time windows also differ. PVC starts a fixed Visit window with the first counted view, using your Count Interval – 24 hours by default. Later views do not extend it. Google Analytics 4 uses an inactivity timeout, which defaults to 30 minutes.
     101
     102= Why can’t I see Visits for a single post? =
     103
     104A Visit belongs to your site, not to a post – one Visit often covers several posts. Post Views Counter shows Views for each post and Visits as site-wide totals. Traffic Signals still notice when the visits that start on a post change unusually.
     105
     106= Why does the Visits chart show no data? =
     107
     108Visits start accumulating when Visit counting becomes available; earlier Views do not automatically become Visits. Setting Count Interval to 0 stops new Visits but preserves previously counted totals. If browser storage is unavailable, Views can still be counted but Visits are not inferred. Right after an update, the chart can briefly say that Visit data is not available while the database is prepared.
    77109
    78110= Is Post Views Counter GDPR compliant? =
    79111
    80 Post Views Counter runs entirely inside WordPress with no third-party scripts and keeps data on your server — aligning with GDPR-style expectations.
     112Post Views Counter counts views and visits inside WordPress without third-party tracking scripts. Views and Visits are stored as aggregate totals on your server. To apply the Count Interval, it uses a first-party cookie in the visitor’s browser. Pro also offers cookieless browser storage; its optional Strict Counts feature caches IP-derived data associated with content and timestamps to limit repeat counts.
    81113
    82114= How do I get support? =
    … …  
    92124
    93125== Changelog ==
     126
     127= 1.8.0 =
     128* New: Site-wide Visits tracking.
     129* New: Views, Visits and combined display modes in the Post Views dashboard chart.
     130* New: Monthly Views comparisons in the Post Views dashboard widget.
     131* New: Custom Format for frontend Views counters in Display settings, shortcodes and PHP functions.
     132* New: Traffic Signals for changes in visits starting on a post.
     133* Fix: Block editor counter editing permissions, duplicate saves and error reporting.
     134* Fix: Counter input validation and Bulk Edit updates for saved posts.
     135* Fix: Access checks for admin column Views charts.
     136* Fix: Exclude non-post counts from post charts and summary emails.
     137* Fix: Traffic Signals and modal periods follow Count Time, including month-end comparisons.
     138* Fix: Preserve daily history when Cleanup Interval is set to 0.
     139* Fix: Prevent false Traffic Signals when daily history is incomplete.
     140* Fix: Keep keyboard focus inside the Views chart modal after navigating between periods.
     141* Fix: Restore the Views counter in the block editor on WordPress 6.4.
     142* Fix: Restore lifetime totals in pvc_get_views() when no period is specified.
     143* Tweak: Limit form-based counter edits to the submitted post; use pvc_update_post_views() for programmatic updates.
     144* Tweak: Use stored totals for manual Views edits, independently of pvc_get_post_views filters.
     145* Tweak: Remove pvcArgsQuickEdit.nonce from post lists; retain the save_bulk_post_views AJAX action for compatibility.
     146* Tweak: Reduce Traffic Signals reads to one database query per posts list page.
     147* Tweak: Use native Views column sorting with a stable tie-break.
     148* Tweak: Unify the frontend counter label to Views.
     149* Tweak: Replace frontend Dashicons with a CSS counter icon.
     150* Tweak: Retire the Icon Class setting; custom icons now require the pvc_counter_icon_class or pvc_counter_icon filter.
    94151
    95152= 1.7.15 =
    … …  
    495552== Upgrade Notice ==
    496553
    497 = 1.7.15 =
    498 Improves Statify import reliability and WordPress 7.1 editor compatibility.
     554= 1.8.0 =
     555Adds site-wide Visits, dashboard comparisons and Custom Format for the Views counter, with counting and editing fixes. If you use Pro, update both plugins and purge full-page caches so dynamic counters use the new markup.
Note: See TracChangeset for help on using the changeset viewer.