Post Views Counter - Version 1.3.8

Version Description

  • Tweak: Improved user input escaping
Download this release

Release Info

Developer dfactory
Plugin Icon 128x128 Post Views Counter
Version 1.3.8
Comparing to
See all releases

Code changes from version 1.3.7 to 1.3.8

assets/chartjs/chart.min.js CHANGED
@@ -1,7 +1,7 @@
1
- /*!
2
- * Chart.js v2.9.3
3
- * https://www.chartjs.org
4
- * (c) 2019 Chart.js Contributors
5
- * Released under the MIT License
6
- */
7
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(function(){try{return require("moment")}catch(t){}}()):"function"==typeof define&&define.amd?define(["require"],(function(t){return e(function(){try{return t("moment")}catch(t){}}())})):(t=t||self).Chart=e(t.moment)}(this,(function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},n=function(t,e){return t(e={exports:{}},e.exports),e.exports}((function(t){var n={};for(var i in e)e.hasOwnProperty(i)&&(n[e[i]]=i);var a=t.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var r in a)if(a.hasOwnProperty(r)){if(!("channels"in a[r]))throw new Error("missing channels property: "+r);if(!("labels"in a[r]))throw new Error("missing channel labels property: "+r);if(a[r].labels.length!==a[r].channels)throw new Error("channel and label counts mismatch: "+r);var o=a[r].channels,s=a[r].labels;delete a[r].channels,delete a[r].labels,Object.defineProperty(a[r],"channels",{value:o}),Object.defineProperty(a[r],"labels",{value:s})}a.rgb.hsl=function(t){var e,n,i=t[0]/255,a=t[1]/255,r=t[2]/255,o=Math.min(i,a,r),s=Math.max(i,a,r),l=s-o;return s===o?e=0:i===s?e=(a-r)/l:a===s?e=2+(r-i)/l:r===s&&(e=4+(i-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),n=(o+s)/2,[e,100*(s===o?0:n<=.5?l/(s+o):l/(2-s-o)),100*n]},a.rgb.hsv=function(t){var e,n,i,a,r,o=t[0]/255,s=t[1]/255,l=t[2]/255,u=Math.max(o,s,l),d=u-Math.min(o,s,l),h=function(t){return(u-t)/6/d+.5};return 0===d?a=r=0:(r=d/u,e=h(o),n=h(s),i=h(l),o===u?a=i-n:s===u?a=1/3+e-i:l===u&&(a=2/3+n-e),a<0?a+=1:a>1&&(a-=1)),[360*a,100*r,100*u]},a.rgb.hwb=function(t){var e=t[0],n=t[1],i=t[2];return[a.rgb.hsl(t)[0],100*(1/255*Math.min(e,Math.min(n,i))),100*(i=1-1/255*Math.max(e,Math.max(n,i)))]},a.rgb.cmyk=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255;return[100*((1-n-(e=Math.min(1-n,1-i,1-a)))/(1-e)||0),100*((1-i-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]},a.rgb.keyword=function(t){var i=n[t];if(i)return i;var a,r,o,s=1/0;for(var l in e)if(e.hasOwnProperty(l)){var u=e[l],d=(r=t,o=u,Math.pow(r[0]-o[0],2)+Math.pow(r[1]-o[1],2)+Math.pow(r[2]-o[2],2));d<s&&(s=d,a=l)}return a},a.keyword.rgb=function(t){return e[t]},a.rgb.xyz=function(t){var e=t[0]/255,n=t[1]/255,i=t[2]/255;return[100*(.4124*(e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]},a.rgb.lab=function(t){var e=a.rgb.xyz(t),n=e[0],i=e[1],r=e[2];return i/=100,r/=108.883,n=(n/=95.047)>.008856?Math.pow(n,1/3):7.787*n+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(n-i),200*(i-(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116))]},a.hsl.rgb=function(t){var e,n,i,a,r,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0===s)return[r=255*l,r,r];e=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a},a.hsl.hsv=function(t){var e=t[0],n=t[1]/100,i=t[2]/100,a=n,r=Math.max(i,.01);return n*=(i*=2)<=1?i:2-i,a*=r<=1?r:2-r,[e,100*(0===i?2*a/(r+a):2*n/(i+n)),100*((i+n)/2)]},a.hsv.rgb=function(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),s=255*i*(1-n*r),l=255*i*(1-n*(1-r));switch(i*=255,a){case 0:return[i,l,o];case 1:return[s,i,o];case 2:return[o,i,l];case 3:return[o,s,i];case 4:return[l,o,i];case 5:return[i,o,s]}},a.hsv.hsl=function(t){var e,n,i,a=t[0],r=t[1]/100,o=t[2]/100,s=Math.max(o,.01);return i=(2-r)*o,n=r*s,[a,100*(n=(n/=(e=(2-r)*s)<=1?e:2-e)||0),100*(i/=2)]},a.hwb.rgb=function(t){var e,n,i,a,r,o,s,l=t[0]/360,u=t[1]/100,d=t[2]/100,h=u+d;switch(h>1&&(u/=h,d/=h),i=6*l-(e=Math.floor(6*l)),0!=(1&e)&&(i=1-i),a=u+i*((n=1-d)-u),e){default:case 6:case 0:r=n,o=a,s=u;break;case 1:r=a,o=n,s=u;break;case 2:r=u,o=n,s=a;break;case 3:r=u,o=a,s=n;break;case 4:r=a,o=u,s=n;break;case 5:r=n,o=u,s=a}return[255*r,255*o,255*s]},a.cmyk.rgb=function(t){var e=t[0]/100,n=t[1]/100,i=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a))]},a.xyz.rgb=function(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=(e=3.2406*a+-1.5372*r+-.4986*o)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:12.92*i,[255*(e=Math.min(Math.max(0,e),1)),255*(n=Math.min(Math.max(0,n),1)),255*(i=Math.min(Math.max(0,i),1))]},a.xyz.lab=function(t){var e=t[0],n=t[1],i=t[2];return n/=100,i/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(e-n),200*(n-(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116))]},a.lab.xyz=function(t){var e,n,i,a=t[0];e=t[1]/500+(n=(a+16)/116),i=n-t[2]/200;var r=Math.pow(n,3),o=Math.pow(e,3),s=Math.pow(i,3);return n=r>.008856?r:(n-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,i=s>.008856?s:(i-16/116)/7.787,[e*=95.047,n*=100,i*=108.883]},a.lab.lch=function(t){var e,n=t[0],i=t[1],a=t[2];return(e=360*Math.atan2(a,i)/2/Math.PI)<0&&(e+=360),[n,Math.sqrt(i*i+a*a),e]},a.lch.lab=function(t){var e,n=t[0],i=t[1];return e=t[2]/360*2*Math.PI,[n,i*Math.cos(e),i*Math.sin(e)]},a.rgb.ansi16=function(t){var e=t[0],n=t[1],i=t[2],r=1 in arguments?arguments[1]:a.rgb.hsv(t)[2];if(0===(r=Math.round(r/50)))return 30;var o=30+(Math.round(i/255)<<2|Math.round(n/255)<<1|Math.round(e/255));return 2===r&&(o+=60),o},a.hsv.ansi16=function(t){return a.rgb.ansi16(a.hsv.rgb(t),t[2])},a.rgb.ansi256=function(t){var e=t[0],n=t[1],i=t[2];return e===n&&n===i?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(n/255*5)+Math.round(i/255*5)},a.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var n=.5*(1+~~(t>50));return[(1&e)*n*255,(e>>1&1)*n*255,(e>>2&1)*n*255]},a.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var n;return t-=16,[Math.floor(t/36)/5*255,Math.floor((n=t%36)/6)/5*255,n%6/5*255]},a.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},a.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var n=e[0];3===e[0].length&&(n=n.split("").map((function(t){return t+t})).join(""));var i=parseInt(n,16);return[i>>16&255,i>>8&255,255&i]},a.rgb.hcg=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255,r=Math.max(Math.max(n,i),a),o=Math.min(Math.min(n,i),a),s=r-o;return e=s<=0?0:r===n?(i-a)/s%6:r===i?2+(a-n)/s:4+(n-i)/s+4,e/=6,[360*(e%=1),100*s,100*(s<1?o/(1-s):0)]},a.hsl.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=1,a=0;return(i=n<.5?2*e*n:2*e*(1-n))<1&&(a=(n-.5*i)/(1-i)),[t[0],100*i,100*a]},a.hsv.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=e*n,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.hcg.rgb=function(t){var e=t[0]/360,n=t[1]/100,i=t[2]/100;if(0===n)return[255*i,255*i,255*i];var a,r=[0,0,0],o=e%1*6,s=o%1,l=1-s;switch(Math.floor(o)){case 0:r[0]=1,r[1]=s,r[2]=0;break;case 1:r[0]=l,r[1]=1,r[2]=0;break;case 2:r[0]=0,r[1]=1,r[2]=s;break;case 3:r[0]=0,r[1]=l,r[2]=1;break;case 4:r[0]=s,r[1]=0,r[2]=1;break;default:r[0]=1,r[1]=0,r[2]=l}return a=(1-n)*i,[255*(n*r[0]+a),255*(n*r[1]+a),255*(n*r[2]+a)]},a.hcg.hsv=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e),i=0;return n>0&&(i=e/n),[t[0],100*i,100*n]},a.hcg.hsl=function(t){var e=t[1]/100,n=t[2]/100*(1-e)+.5*e,i=0;return n>0&&n<.5?i=e/(2*n):n>=.5&&n<1&&(i=e/(2*(1-n))),[t[0],100*i,100*n]},a.hcg.hwb=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e);return[t[0],100*(n-e),100*(1-n)]},a.hwb.hcg=function(t){var e=t[1]/100,n=1-t[2]/100,i=n-e,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},a.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},a.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},a.gray.hsl=a.gray.hsv=function(t){return[0,0,t[0]]},a.gray.hwb=function(t){return[0,100,t[0]]},a.gray.cmyk=function(t){return[0,0,0,t[0]]},a.gray.lab=function(t){return[t[0],0,0]},a.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),n=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(n.length)+n},a.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}));n.rgb,n.hsl,n.hsv,n.hwb,n.cmyk,n.xyz,n.lab,n.lch,n.hex,n.keyword,n.ansi16,n.ansi256,n.hcg,n.apple,n.gray;function i(t){var e=function(){for(var t={},e=Object.keys(n),i=e.length,a=0;a<i;a++)t[e[a]]={distance:-1,parent:null};return t}(),i=[t];for(e[t].distance=0;i.length;)for(var a=i.pop(),r=Object.keys(n[a]),o=r.length,s=0;s<o;s++){var l=r[s],u=e[l];-1===u.distance&&(u.distance=e[a].distance+1,u.parent=a,i.unshift(l))}return e}function a(t,e){return function(n){return e(t(n))}}function r(t,e){for(var i=[e[t].parent,t],r=n[e[t].parent][t],o=e[t].parent;e[o].parent;)i.unshift(e[o].parent),r=a(n[e[o].parent][o],r),o=e[o].parent;return r.conversion=i,r}var o={};Object.keys(n).forEach((function(t){o[t]={},Object.defineProperty(o[t],"channels",{value:n[t].channels}),Object.defineProperty(o[t],"labels",{value:n[t].labels});var e=function(t){for(var e=i(t),n={},a=Object.keys(e),o=a.length,s=0;s<o;s++){var l=a[s];null!==e[l].parent&&(n[l]=r(l,e))}return n}(t);Object.keys(e).forEach((function(n){var i=e[n];o[t][n]=function(t){var e=function(e){if(null==e)return e;arguments.length>1&&(e=Array.prototype.slice.call(arguments));var n=t(e);if("object"==typeof n)for(var i=n.length,a=0;a<i;a++)n[a]=Math.round(n[a]);return n};return"conversion"in t&&(e.conversion=t.conversion),e}(i),o[t][n].raw=function(t){var e=function(e){return null==e?e:(arguments.length>1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(i)}))}));var s=o,l={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},u={getRgba:d,getHsla:h,getRgb:function(t){var e=d(t);return e&&e.slice(0,3)},getHsl:function(t){var e=h(t);return e&&e.slice(0,3)},getHwb:c,getAlpha:function(t){var e=d(t);if(e)return e[3];if(e=h(t))return e[3];if(e=c(t))return e[3]},hexString:function(t,e){e=void 0!==e&&3===t.length?e:t[3];return"#"+v(t[0])+v(t[1])+v(t[2])+(e>=0&&e<1?v(Math.round(255*e)):"")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return f(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:f,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return g(t,e);var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+n+"%, "+i+"%, "+a+"%)"},percentaString:g,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return p(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:p,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return b[t.slice(0,3)]}};function d(t){if(t){var e=[0,0,0],n=1,i=t.match(/^#([a-fA-F0-9]{3,4})$/i),a="";if(i){a=(i=i[1])[3];for(var r=0;r<e.length;r++)e[r]=parseInt(i[r]+i[r],16);a&&(n=Math.round(parseInt(a+a,16)/255*100)/100)}else if(i=t.match(/^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i)){a=i[2],i=i[1];for(r=0;r<e.length;r++)e[r]=parseInt(i.slice(2*r,2*r+2),16);a&&(n=Math.round(parseInt(a,16)/255*100)/100)}else if(i=t.match(/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=parseInt(i[r+1]);n=parseFloat(i[4])}else if(i=t.match(/^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=Math.round(2.55*parseFloat(i[r+1]));n=parseFloat(i[4])}else if(i=t.match(/(\w+)/)){if("transparent"==i[1])return[0,0,0,0];if(!(e=l[i[1]]))return}for(r=0;r<e.length;r++)e[r]=m(e[r],0,255);return n=n||0==n?m(n,0,1):1,e[3]=n,e}}function h(t){if(t){var e=t.match(/^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[m(parseInt(e[1]),0,360),m(parseFloat(e[2]),0,100),m(parseFloat(e[3]),0,100),m(isNaN(n)?1:n,0,1)]}}}function c(t){if(t){var e=t.match(/^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[m(parseInt(e[1]),0,360),m(parseFloat(e[2]),0,100),m(parseFloat(e[3]),0,100),m(isNaN(n)?1:n,0,1)]}}}function f(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+e+")"}function g(t,e){return"rgba("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%, "+(e||t[3]||1)+")"}function p(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+e+")"}function m(t,e,n){return Math.min(Math.max(e,t),n)}function v(t){var e=t.toString(16).toUpperCase();return e.length<2?"0"+e:e}var b={};for(var x in l)b[l[x]]=x;var y=function(t){return t instanceof y?t:this instanceof y?(this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1},void("string"==typeof t?(e=u.getRgba(t))?this.setValues("rgb",e):(e=u.getHsla(t))?this.setValues("hsl",e):(e=u.getHwb(t))&&this.setValues("hwb",e):"object"==typeof t&&(void 0!==(e=t).r||void 0!==e.red?this.setValues("rgb",e):void 0!==e.l||void 0!==e.lightness?this.setValues("hsl",e):void 0!==e.v||void 0!==e.value?this.setValues("hsv",e):void 0!==e.w||void 0!==e.whiteness?this.setValues("hwb",e):void 0===e.c&&void 0===e.cyan||this.setValues("cmyk",e)))):new y(t);var e};y.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:function(){var t=this.values;return t.hsl.concat([t.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues("alpha",t),this)},red:function(t){return this.setChannel("rgb",0,t)},green:function(t){return this.setChannel("rgb",1,t)},blue:function(t){return this.setChannel("rgb",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel("hsl",0,t)},saturation:function(t){return this.setChannel("hsl",1,t)},lightness:function(t){return this.setChannel("hsl",2,t)},saturationv:function(t){return this.setChannel("hsv",1,t)},whiteness:function(t){return this.setChannel("hwb",1,t)},blackness:function(t){return this.setChannel("hwb",2,t)},value:function(t){return this.setChannel("hsv",2,t)},cyan:function(t){return this.setChannel("cmyk",0,t)},magenta:function(t){return this.setChannel("cmyk",1,t)},yellow:function(t){return this.setChannel("cmyk",2,t)},black:function(t){return this.setChannel("cmyk",3,t)},hexString:function(){return u.hexString(this.values.rgb)},rgbString:function(){return u.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return u.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return u.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return u.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return u.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return u.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return u.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){var t=this.values.rgb;return t[0]<<16|t[1]<<8|t[2]},luminosity:function(){for(var t=this.values.rgb,e=[],n=0;n<t.length;n++){var i=t[n]/255;e[n]=i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4)}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast:function(t){var e=this.luminosity(),n=t.luminosity();return e>n?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=t,i=void 0===e?.5:e,a=2*i-1,r=this.alpha()-n.alpha(),o=((a*r==-1?a:(a+r)/(1+a*r))+1)/2,s=1-o;return this.rgb(o*this.red()+s*n.red(),o*this.green()+s*n.green(),o*this.blue()+s*n.blue()).alpha(this.alpha()*i+n.alpha()*(1-i))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new y,i=this.values,a=n.values;for(var r in i)i.hasOwnProperty(r)&&(t=i[r],"[object Array]"===(e={}.toString.call(t))?a[r]=t.slice(0):"[object Number]"===e?a[r]=t:console.error("unexpected color value:",t));return n}},y.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},y.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},y.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i<t.length;i++)n[t.charAt(i)]=e[t][i];return 1!==e.alpha&&(n.a=e.alpha),n},y.prototype.setValues=function(t,e){var n,i,a=this.values,r=this.spaces,o=this.maxes,l=1;if(this.valid=!0,"alpha"===t)l=e;else if(e.length)a[t]=e.slice(0,t.length),l=e[t.length];else if(void 0!==e[t.charAt(0)]){for(n=0;n<t.length;n++)a[t][n]=e[t.charAt(n)];l=e.a}else if(void 0!==e[r[t][0]]){var u=r[t];for(n=0;n<t.length;n++)a[t][n]=e[u[n]];l=e.alpha}if(a.alpha=Math.max(0,Math.min(1,void 0===l?a.alpha:l)),"alpha"===t)return!1;for(n=0;n<t.length;n++)i=Math.max(0,Math.min(o[t][n],a[t][n])),a[t][n]=Math.round(i);for(var d in r)d!==t&&(a[d]=s[t][d](a[t]));return!0},y.prototype.setSpace=function(t,e){var n=e[0];return void 0===n?this.getValues(t):("number"==typeof n&&(n=Array.prototype.slice.call(e)),this.setValues(t,n),this)},y.prototype.setChannel=function(t,e,n){var i=this.values[t];return void 0===n?i[e]:n===i[e]?this:(i[e]=n,this.setValues(t,i),this)},"undefined"!=typeof window&&(window.Color=y);var _,k=y,w={noop:function(){},uid:(_=0,function(){return _++}),isNullOrUndef:function(t){return null==t},isArray:function(t){if(Array.isArray&&Array.isArray(t))return!0;var e=Object.prototype.toString.call(t);return"[object"===e.substr(0,7)&&"Array]"===e.substr(-6)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},isFinite:function(t){return("number"==typeof t||t instanceof Number)&&isFinite(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,n){return w.valueOrDefault(w.isArray(t)?t[e]:t,n)},callback:function(t,e,n){if(t&&"function"==typeof t.call)return t.apply(n,e)},each:function(t,e,n,i){var a,r,o;if(w.isArray(t))if(r=t.length,i)for(a=r-1;a>=0;a--)e.call(n,t[a],a);else for(a=0;a<r;a++)e.call(n,t[a],a);else if(w.isObject(t))for(r=(o=Object.keys(t)).length,a=0;a<r;a++)e.call(n,t[o[a]],o[a])},arrayEquals:function(t,e){var n,i,a,r;if(!t||!e||t.length!==e.length)return!1;for(n=0,i=t.length;n<i;++n)if(a=t[n],r=e[n],a instanceof Array&&r instanceof Array){if(!w.arrayEquals(a,r))return!1}else if(a!==r)return!1;return!0},clone:function(t){if(w.isArray(t))return t.map(w.clone);if(w.isObject(t)){for(var e={},n=Object.keys(t),i=n.length,a=0;a<i;++a)e[n[a]]=w.clone(t[n[a]]);return e}return t},_merger:function(t,e,n,i){var a=e[t],r=n[t];w.isObject(a)&&w.isObject(r)?w.merge(a,r,i):e[t]=w.clone(r)},_mergerIf:function(t,e,n){var i=e[t],a=n[t];w.isObject(i)&&w.isObject(a)?w.mergeIf(i,a):e.hasOwnProperty(t)||(e[t]=w.clone(a))},merge:function(t,e,n){var i,a,r,o,s,l=w.isArray(e)?e:[e],u=l.length;if(!w.isObject(t))return t;for(i=(n=n||{}).merger||w._merger,a=0;a<u;++a)if(e=l[a],w.isObject(e))for(s=0,o=(r=Object.keys(e)).length;s<o;++s)i(r[s],t,e,n);return t},mergeIf:function(t,e){return w.merge(t,e,{merger:w._mergerIf})},extend:Object.assign||function(t){return w.merge(t,[].slice.call(arguments,1),{merger:function(t,e,n){e[t]=n[t]}})},inherits:function(t){var e=this,n=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return e.apply(this,arguments)},i=function(){this.constructor=n};return i.prototype=e.prototype,n.prototype=new i,n.extend=w.inherits,t&&w.extend(n.prototype,t),n.__super__=e.prototype,n},_deprecated:function(t,e,n,i){void 0!==e&&console.warn(t+': "'+n+'" is deprecated. Please use "'+i+'" instead')}},M=w;w.callCallback=w.callback,w.indexOf=function(t,e,n){return Array.prototype.indexOf.call(t,e,n)},w.getValueOrDefault=w.valueOrDefault,w.getValueAtIndexOrDefault=w.valueAtIndexOrDefault;var S={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return(t-=1)*t*t+1},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-((t-=1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return(t-=1)*t*t*t*t+1},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return 1-Math.cos(t*(Math.PI/2))},easeOutSine:function(t){return Math.sin(t*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},easeInExpo:function(t){return 0===t?0:Math.pow(2,10*(t-1))},easeOutExpo:function(t){return 1===t?1:1-Math.pow(2,-10*t)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*--t))},easeInCirc:function(t){return t>=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var 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:function(t){return 1-S.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*S.easeInBounce(2*t):.5*S.easeOutBounce(2*t-1)+.5}},C={effects:S};M.easingEffects=S;var P=Math.PI,A=P/180,D=2*P,T=P/2,I=P/4,F=2*P/3,L={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,a/2,i/2),s=e+o,l=n+o,u=e+i-o,d=n+a-o;t.moveTo(e,l),s<u&&l<d?(t.arc(s,l,o,-P,-T),t.arc(u,l,o,-T,0),t.arc(u,d,o,0,T),t.arc(s,d,o,T,P)):s<u?(t.moveTo(s,n),t.arc(u,l,o,-T,T),t.arc(s,l,o,T,P+T)):l<d?(t.arc(s,l,o,-P,0),t.arc(s,d,o,0,P)):t.arc(s,l,o,-P,P),t.closePath(),t.moveTo(e,n)}else t.rect(e,n,i,a)},drawPoint:function(t,e,n,i,a,r){var o,s,l,u,d,h=(r||0)*A;if(e&&"object"==typeof e&&("[object HTMLImageElement]"===(o=e.toString())||"[object HTMLCanvasElement]"===o))return t.save(),t.translate(i,a),t.rotate(h),t.drawImage(e,-e.width/2,-e.height/2,e.width,e.height),void t.restore();if(!(isNaN(n)||n<=0)){switch(t.beginPath(),e){default:t.arc(i,a,n,0,D),t.closePath();break;case"triangle":t.moveTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=F,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=F,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),t.closePath();break;case"rectRounded":u=n-(d=.516*n),s=Math.cos(h+I)*u,l=Math.sin(h+I)*u,t.arc(i-s,a-l,d,h-P,h-T),t.arc(i+l,a-s,d,h-T,h),t.arc(i+s,a+l,d,h,h+T),t.arc(i-l,a+s,d,h+T,h+P),t.closePath();break;case"rect":if(!r){u=Math.SQRT1_2*n,t.rect(i-u,a-u,2*u,2*u);break}h+=I;case"rectRot":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+l,a-s),t.lineTo(i+s,a+l),t.lineTo(i-l,a+s),t.closePath();break;case"crossRot":h+=I;case"cross":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"star":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s),h+=I,s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"line":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l);break;case"dash":t.moveTo(i,a),t.lineTo(i+Math.cos(h)*n,a+Math.sin(h)*n)}t.fill(),t.stroke()}},_isPointInArea:function(t,e){return t.x>e.left-1e-6&&t.x<e.right+1e-6&&t.y>e.top-1e-6&&t.y<e.bottom+1e-6},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,n,i){var a=n.steppedLine;if(a){if("middle"===a){var r=(e.x+n.x)/2;t.lineTo(r,i?n.y:e.y),t.lineTo(r,i?e.y:n.y)}else"after"===a&&!i||"after"!==a&&i?t.lineTo(e.x,n.y):t.lineTo(n.x,e.y);t.lineTo(n.x,n.y)}else n.tension?t.bezierCurveTo(i?e.controlPointPreviousX:e.controlPointNextX,i?e.controlPointPreviousY:e.controlPointNextY,i?n.controlPointNextX:n.controlPointPreviousX,i?n.controlPointNextY:n.controlPointPreviousY,n.x,n.y):t.lineTo(n.x,n.y)}},O=L;M.clear=L.clear,M.drawRoundedRectangle=function(t){t.beginPath(),L.roundedRect.apply(L,arguments)};var R={_set:function(t,e){return M.merge(this[t]||(this[t]={}),e)}};R._set("global",{defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",defaultLineHeight:1.2,showLines:!0});var z=R,N=M.valueOrDefault;var B={toLineHeight:function(t,e){var n=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!n||"normal"===n[1])return 1.2*e;switch(t=+n[2],n[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,n,i,a;return M.isObject(t)?(e=+t.top||0,n=+t.right||0,i=+t.bottom||0,a=+t.left||0):e=n=i=a=+t||0,{top:e,right:n,bottom:i,left:a,height:e+i,width:a+n}},_parseFont:function(t){var e=z.global,n=N(t.fontSize,e.defaultFontSize),i={family:N(t.fontFamily,e.defaultFontFamily),lineHeight:M.options.toLineHeight(N(t.lineHeight,e.defaultLineHeight),n),size:n,style:N(t.fontStyle,e.defaultFontStyle),weight:null,string:""};return i.string=function(t){return!t||M.isNullOrUndef(t.size)||M.isNullOrUndef(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}(i),i},resolve:function(t,e,n,i){var a,r,o,s=!0;for(a=0,r=t.length;a<r;++a)if(void 0!==(o=t[a])&&(void 0!==e&&"function"==typeof o&&(o=o(e),s=!1),void 0!==n&&M.isArray(o)&&(o=o[n],s=!1),void 0!==o))return i&&!s&&(i.cacheable=!1),o}},E={_factorize:function(t){var e,n=[],i=Math.sqrt(t);for(e=1;e<i;e++)t%e==0&&(n.push(e),n.push(t/e));return i===(0|i)&&n.push(i),n.sort((function(t,e){return t-e})).pop(),n},log10:Math.log10||function(t){var e=Math.log(t)*Math.LOG10E,n=Math.round(e);return t===Math.pow(10,n)?n:e}},W=E;M.log10=E.log10;var V=M,H=C,j=O,q=B,U=W,Y={getRtlAdapter:function(t,e,n){return t?function(t,e){return{x:function(n){return t+t+e-n},setWidth:function(t){e=t},textAlign:function(t){return"center"===t?t:"right"===t?"left":"right"},xPlus:function(t,e){return t-e},leftForLtr:function(t,e){return t-e}}}(e,n):{x:function(t){return t},setWidth:function(t){},textAlign:function(t){return t},xPlus:function(t,e){return t+e},leftForLtr:function(t,e){return t}}},overrideTextDirection:function(t,e){var n,i;"ltr"!==e&&"rtl"!==e||(i=[(n=t.canvas.style).getPropertyValue("direction"),n.getPropertyPriority("direction")],n.setProperty("direction",e,"important"),t.prevTextDirection=i)},restoreTextDirection:function(t){var e=t.prevTextDirection;void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}};V.easing=H,V.canvas=j,V.options=q,V.math=U,V.rtl=Y;var G=function(t){V.extend(this,t),this.initialize.apply(this,arguments)};V.extend(G.prototype,{_type:void 0,initialize:function(){this.hidden=!1},pivot:function(){var t=this;return t._view||(t._view=V.extend({},t._model)),t._start={},t},transition:function(t){var e=this,n=e._model,i=e._start,a=e._view;return n&&1!==t?(a||(a=e._view={}),i||(i=e._start={}),function(t,e,n,i){var a,r,o,s,l,u,d,h,c,f=Object.keys(n);for(a=0,r=f.length;a<r;++a)if(u=n[o=f[a]],e.hasOwnProperty(o)||(e[o]=u),(s=e[o])!==u&&"_"!==o[0]){if(t.hasOwnProperty(o)||(t[o]=s),(d=typeof u)===typeof(l=t[o]))if("string"===d){if((h=k(l)).valid&&(c=k(u)).valid){e[o]=c.mix(h,i).rgbString();continue}}else if(V.isFinite(l)&&V.isFinite(u)){e[o]=l+(u-l)*i;continue}e[o]=u}}(i,a,n,t),e):(e._view=V.extend({},n),e._start=null,e)},tooltipPosition:function(){return{x:this._model.x,y:this._model.y}},hasValue:function(){return V.isNumber(this._model.x)&&V.isNumber(this._model.y)}}),G.extend=V.inherits;var X=G,K=X.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),Z=K;Object.defineProperty(K.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(K.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}}),z._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:V.noop,onComplete:V.noop}});var $={animations:[],request:null,addAnimation:function(t,e,n,i){var a,r,o=this.animations;for(e.chart=t,e.startTime=Date.now(),e.duration=n,i||(t.animating=!0),a=0,r=o.length;a<r;++a)if(o[a].chart===t)return void(o[a]=e);o.push(e),1===o.length&&this.requestAnimationFrame()},cancelAnimation:function(t){var e=V.findIndex(this.animations,(function(e){return e.chart===t}));-1!==e&&(this.animations.splice(e,1),t.animating=!1)},requestAnimationFrame:function(){var t=this;null===t.request&&(t.request=V.requestAnimFrame.call(window,(function(){t.request=null,t.startDigest()})))},startDigest:function(){this.advance(),this.animations.length>0&&this.requestAnimationFrame()},advance:function(){for(var t,e,n,i,a=this.animations,r=0;r<a.length;)e=(t=a[r]).chart,n=t.numSteps,i=Math.floor((Date.now()-t.startTime)/t.duration*n)+1,t.currentStep=Math.min(i,n),V.callback(t.render,[e,t],e),V.callback(t.onAnimationProgress,[t],e),t.currentStep>=n?(V.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(r,1)):++r}},J=V.options.resolve,Q=["push","pop","shift","splice","unshift"];function tt(t,e){var n=t._chartjs;if(n){var i=n.listeners,a=i.indexOf(e);-1!==a&&i.splice(a,1),i.length>0||(Q.forEach((function(e){delete t[e]})),delete t._chartjs)}}var et=function(t,e){this.initialize(t,e)};V.extend(et.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth"],_dataElementOptions:["backgroundColor","borderColor","borderWidth","pointStyle"],initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements(),n._type=n.getMeta().type},updateIndex:function(t){this.index=t},linkScales:function(){var t=this.getMeta(),e=this.chart,n=e.scales,i=this.getDataset(),a=e.options.scales;null!==t.xAxisID&&t.xAxisID in n&&!i.xAxisID||(t.xAxisID=i.xAxisID||a.xAxes[0].id),null!==t.yAxisID&&t.yAxisID in n&&!i.yAxisID||(t.yAxisID=i.yAxisID||a.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this._update(!0)},destroy:function(){this._data&&tt(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,n=this.getMeta(),i=this.getDataset().data||[],a=n.data;for(t=0,e=i.length;t<e;++t)a[t]=a[t]||this.createMetaData(t);n.dataset=n.dataset||this.createMetaDataset()},addElementAndReset:function(t){var e=this.createMetaData(t);this.getMeta().data.splice(t,0,e),this.updateElement(e,t,!0)},buildOrUpdateElements:function(){var t,e,n=this,i=n.getDataset(),a=i.data||(i.data=[]);n._data!==a&&(n._data&&tt(n._data,n),a&&Object.isExtensible(a)&&(e=n,(t=a)._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),Q.forEach((function(e){var n="onData"+e.charAt(0).toUpperCase()+e.slice(1),i=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value:function(){var e=Array.prototype.slice.call(arguments),a=i.apply(this,e);return V.each(t._chartjs.listeners,(function(t){"function"==typeof t[n]&&t[n].apply(t,e)})),a}})})))),n._data=a),n.resyncElements()},_configure:function(){this._config=V.merge({},[this.chart.options.datasets[this._type],this.getDataset()],{merger:function(t,e,n){"_meta"!==t&&"data"!==t&&V._merger(t,e,n)}})},_update:function(t){this._configure(),this._cachedDataOpts=null,this.update(t)},update:V.noop,transition:function(t){for(var e=this.getMeta(),n=e.data||[],i=n.length,a=0;a<i;++a)n[a].transition(t);e.dataset&&e.dataset.transition(t)},draw:function(){var t=this.getMeta(),e=t.data||[],n=e.length,i=0;for(t.dataset&&t.dataset.draw();i<n;++i)e[i].draw()},getStyle:function(t){var e,n=this.getMeta(),i=n.dataset;return this._configure(),i&&void 0===t?e=this._resolveDatasetElementOptions(i||{}):(t=t||0,e=this._resolveDataElementOptions(n.data[t]||{},t)),!1!==e.fill&&null!==e.fill||(e.backgroundColor=e.borderColor),e},_resolveDatasetElementOptions:function(t,e){var n,i,a,r,o=this,s=o.chart,l=o._config,u=t.custom||{},d=s.options.elements[o.datasetElementType.prototype._type]||{},h=o._datasetElementOptions,c={},f={chart:s,dataset:o.getDataset(),datasetIndex:o.index,hover:e};for(n=0,i=h.length;n<i;++n)a=h[n],r=e?"hover"+a.charAt(0).toUpperCase()+a.slice(1):a,c[a]=J([u[r],l[r],d[r]],f);return c},_resolveDataElementOptions:function(t,e){var n=this,i=t&&t.custom,a=n._cachedDataOpts;if(a&&!i)return a;var r,o,s,l,u=n.chart,d=n._config,h=u.options.elements[n.dataElementType.prototype._type]||{},c=n._dataElementOptions,f={},g={chart:u,dataIndex:e,dataset:n.getDataset(),datasetIndex:n.index},p={cacheable:!i};if(i=i||{},V.isArray(c))for(o=0,s=c.length;o<s;++o)f[l=c[o]]=J([i[l],d[l],h[l]],g,e,p);else for(o=0,s=(r=Object.keys(c)).length;o<s;++o)f[l=r[o]]=J([i[l],d[c[l]],d[l],h[l]],g,e,p);return p.cacheable&&(n._cachedDataOpts=Object.freeze(f)),f},removeHoverStyle:function(t){V.merge(t._model,t.$previousStyle||{}),delete t.$previousStyle},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t._index,i=t.custom||{},a=t._model,r=V.getHoverColor;t.$previousStyle={backgroundColor:a.backgroundColor,borderColor:a.borderColor,borderWidth:a.borderWidth},a.backgroundColor=J([i.hoverBackgroundColor,e.hoverBackgroundColor,r(a.backgroundColor)],void 0,n),a.borderColor=J([i.hoverBorderColor,e.hoverBorderColor,r(a.borderColor)],void 0,n),a.borderWidth=J([i.hoverBorderWidth,e.hoverBorderWidth,a.borderWidth],void 0,n)},_removeDatasetHoverStyle:function(){var t=this.getMeta().dataset;t&&this.removeHoverStyle(t)},_setDatasetHoverStyle:function(){var t,e,n,i,a,r,o=this.getMeta().dataset,s={};if(o){for(r=o._model,a=this._resolveDatasetElementOptions(o,!0),t=0,e=(i=Object.keys(a)).length;t<e;++t)s[n=i[t]]=r[n],r[n]=a[n];o.$previousStyle=s}},resyncElements:function(){var t=this.getMeta(),e=this.getDataset().data,n=t.data.length,i=e.length;i<n?t.data.splice(i,n-i):i>n&&this.insertElements(n,i-n)},insertElements:function(t,e){for(var n=0;n<e;++n)this.addElementAndReset(t+n)},onDataPush:function(){var t=arguments.length;this.insertElements(this.getDataset().data.length-t,t)},onDataPop:function(){this.getMeta().data.pop()},onDataShift:function(){this.getMeta().data.shift()},onDataSplice:function(t,e){this.getMeta().data.splice(t,e),this.insertElements(t,arguments.length-2)},onDataUnshift:function(){this.insertElements(0,arguments.length)}}),et.extend=V.inherits;var nt=et,it=2*Math.PI;function at(t,e){var n=e.startAngle,i=e.endAngle,a=e.pixelMargin,r=a/e.outerRadius,o=e.x,s=e.y;t.beginPath(),t.arc(o,s,e.outerRadius,n-r,i+r),e.innerRadius>a?(r=a/e.innerRadius,t.arc(o,s,e.innerRadius-a,i+r,n-r,!0)):t.arc(o,s,a,i+Math.PI/2,n-Math.PI/2),t.closePath(),t.clip()}function rt(t,e,n){var i="inner"===e.borderAlign;i?(t.lineWidth=2*e.borderWidth,t.lineJoin="round"):(t.lineWidth=e.borderWidth,t.lineJoin="bevel"),n.fullCircles&&function(t,e,n,i){var a,r=n.endAngle;for(i&&(n.endAngle=n.startAngle+it,at(t,n),n.endAngle=r,n.endAngle===n.startAngle&&n.fullCircles&&(n.endAngle+=it,n.fullCircles--)),t.beginPath(),t.arc(n.x,n.y,n.innerRadius,n.startAngle+it,n.startAngle,!0),a=0;a<n.fullCircles;++a)t.stroke();for(t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.startAngle+it),a=0;a<n.fullCircles;++a)t.stroke()}(t,e,n,i),i&&at(t,n),t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.endAngle),t.arc(n.x,n.y,n.innerRadius,n.endAngle,n.startAngle,!0),t.closePath(),t.stroke()}z._set("global",{elements:{arc:{backgroundColor:z.global.defaultColor,borderColor:"#fff",borderWidth:2,borderAlign:"center"}}});var ot=X.extend({_type:"arc",inLabelRange:function(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hoverRadius,2)},inRange:function(t,e){var n=this._view;if(n){for(var i=V.getAngleFromPoint(n,{x:t,y:e}),a=i.angle,r=i.distance,o=n.startAngle,s=n.endAngle;s<o;)s+=it;for(;a>s;)a-=it;for(;a<o;)a+=it;var l=a>=o&&a<=s,u=r>=n.innerRadius&&r<=n.outerRadius;return l&&u}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t,e=this._chart.ctx,n=this._view,i="inner"===n.borderAlign?.33:0,a={x:n.x,y:n.y,innerRadius:n.innerRadius,outerRadius:Math.max(n.outerRadius-i,0),pixelMargin:i,startAngle:n.startAngle,endAngle:n.endAngle,fullCircles:Math.floor(n.circumference/it)};if(e.save(),e.fillStyle=n.backgroundColor,e.strokeStyle=n.borderColor,a.fullCircles){for(a.endAngle=a.startAngle+it,e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),t=0;t<a.fullCircles;++t)e.fill();a.endAngle=a.startAngle+n.circumference%it}e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),e.fill(),n.borderWidth&&rt(e,n,a),e.restore()}}),st=V.valueOrDefault,lt=z.global.defaultColor;z._set("global",{elements:{line:{tension:.4,backgroundColor:lt,borderWidth:3,borderColor:lt,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}});var ut=X.extend({_type:"line",draw:function(){var t,e,n,i=this,a=i._view,r=i._chart.ctx,o=a.spanGaps,s=i._children.slice(),l=z.global,u=l.elements.line,d=-1,h=i._loop;if(s.length){if(i._loop){for(t=0;t<s.length;++t)if(e=V.previousItem(s,t),!s[t]._view.skip&&e._view.skip){s=s.slice(t).concat(s.slice(0,t)),h=o;break}h&&s.push(s[0])}for(r.save(),r.lineCap=a.borderCapStyle||u.borderCapStyle,r.setLineDash&&r.setLineDash(a.borderDash||u.borderDash),r.lineDashOffset=st(a.borderDashOffset,u.borderDashOffset),r.lineJoin=a.borderJoinStyle||u.borderJoinStyle,r.lineWidth=st(a.borderWidth,u.borderWidth),r.strokeStyle=a.borderColor||l.defaultColor,r.beginPath(),(n=s[0]._view).skip||(r.moveTo(n.x,n.y),d=0),t=1;t<s.length;++t)n=s[t]._view,e=-1===d?V.previousItem(s,t):s[d],n.skip||(d!==t-1&&!o||-1===d?r.moveTo(n.x,n.y):V.canvas.lineTo(r,e._view,n),d=t);h&&r.closePath(),r.stroke(),r.restore()}}}),dt=V.valueOrDefault,ht=z.global.defaultColor;function ct(t){var e=this._view;return!!e&&Math.abs(t-e.x)<e.radius+e.hitRadius}z._set("global",{elements:{point:{radius:3,pointStyle:"circle",backgroundColor:ht,borderColor:ht,borderWidth:1,hitRadius:1,hoverRadius:4,hoverBorderWidth:1}}});var ft=X.extend({_type:"point",inRange:function(t,e){var n=this._view;return!!n&&Math.pow(t-n.x,2)+Math.pow(e-n.y,2)<Math.pow(n.hitRadius+n.radius,2)},inLabelRange:ct,inXRange:ct,inYRange:function(t){var e=this._view;return!!e&&Math.abs(t-e.y)<e.radius+e.hitRadius},getCenterPoint:function(){var t=this._view;return{x:t.x,y:t.y}},getArea:function(){return Math.PI*Math.pow(this._view.radius,2)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y,padding:t.radius+t.borderWidth}},draw:function(t){var e=this._view,n=this._chart.ctx,i=e.pointStyle,a=e.rotation,r=e.radius,o=e.x,s=e.y,l=z.global,u=l.defaultColor;e.skip||(void 0===t||V.canvas._isPointInArea(e,t))&&(n.strokeStyle=e.borderColor||u,n.lineWidth=dt(e.borderWidth,l.elements.point.borderWidth),n.fillStyle=e.backgroundColor||u,V.canvas.drawPoint(n,i,r,o,s,a))}}),gt=z.global.defaultColor;function pt(t){return t&&void 0!==t.width}function mt(t){var e,n,i,a,r;return pt(t)?(r=t.width/2,e=t.x-r,n=t.x+r,i=Math.min(t.y,t.base),a=Math.max(t.y,t.base)):(r=t.height/2,e=Math.min(t.x,t.base),n=Math.max(t.x,t.base),i=t.y-r,a=t.y+r),{left:e,top:i,right:n,bottom:a}}function vt(t,e,n){return t===e?n:t===n?e:t}function bt(t,e,n){var i,a,r,o,s=t.borderWidth,l=function(t){var e=t.borderSkipped,n={};return e?(t.horizontal?t.base>t.x&&(e=vt(e,"left","right")):t.base<t.y&&(e=vt(e,"bottom","top")),n[e]=!0,n):n}(t);return V.isObject(s)?(i=+s.top||0,a=+s.right||0,r=+s.bottom||0,o=+s.left||0):i=a=r=o=+s||0,{t:l.top||i<0?0:i>n?n:i,r:l.right||a<0?0:a>e?e:a,b:l.bottom||r<0?0:r>n?n:r,l:l.left||o<0?0:o>e?e:o}}function xt(t,e,n){var i=null===e,a=null===n,r=!(!t||i&&a)&&mt(t);return r&&(i||e>=r.left&&e<=r.right)&&(a||n>=r.top&&n<=r.bottom)}z._set("global",{elements:{rectangle:{backgroundColor:gt,borderColor:gt,borderSkipped:"bottom",borderWidth:0}}});var yt=X.extend({_type:"rectangle",draw:function(){var t=this._chart.ctx,e=this._view,n=function(t){var e=mt(t),n=e.right-e.left,i=e.bottom-e.top,a=bt(t,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+a.l,y:e.top+a.t,w:n-a.l-a.r,h:i-a.t-a.b}}}(e),i=n.outer,a=n.inner;t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h),i.w===a.w&&i.h===a.h||(t.save(),t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return xt(this._view,t,e)},inLabelRange:function(t,e){var n=this._view;return pt(n)?xt(n,t,null):xt(n,null,e)},inXRange:function(t){return xt(this._view,t,null)},inYRange:function(t){return xt(this._view,null,t)},getCenterPoint:function(){var t,e,n=this._view;return pt(n)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return pt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),_t={},kt=ot,wt=ut,Mt=ft,St=yt;_t.Arc=kt,_t.Line=wt,_t.Point=Mt,_t.Rectangle=St;var Ct=V._deprecated,Pt=V.valueOrDefault;function At(t,e,n){var i,a,r=n.barThickness,o=e.stackCount,s=e.pixels[t],l=V.isNullOrUndef(r)?function(t,e){var n,i,a,r,o=t._length;for(a=1,r=e.length;a<r;++a)o=Math.min(o,Math.abs(e[a]-e[a-1]));for(a=0,r=t.getTicks().length;a<r;++a)i=t.getPixelForTick(a),o=a>0?Math.min(o,Math.abs(i-n)):o,n=i;return o}(e.scale,e.pixels):-1;return V.isNullOrUndef(r)?(i=l*n.categoryPercentage,a=n.barPercentage):(i=r*o,a=1),{chunk:i/o,ratio:a,start:s-i/2}}z._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),z._set("global",{datasets:{bar:{categoryPercentage:.8,barPercentage:.9}}});var Dt=nt.extend({dataElementType:_t.Rectangle,_dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","barPercentage","barThickness","categoryPercentage","maxBarThickness","minBarLength"],initialize:function(){var t,e,n=this;nt.prototype.initialize.apply(n,arguments),(t=n.getMeta()).stack=n.getDataset().stack,t.bar=!0,e=n._getIndexScale().options,Ct("bar chart",e.barPercentage,"scales.[x/y]Axes.barPercentage","dataset.barPercentage"),Ct("bar chart",e.barThickness,"scales.[x/y]Axes.barThickness","dataset.barThickness"),Ct("bar chart",e.categoryPercentage,"scales.[x/y]Axes.categoryPercentage","dataset.categoryPercentage"),Ct("bar chart",n._getValueScale().options.minBarLength,"scales.[x/y]Axes.minBarLength","dataset.minBarLength"),Ct("bar chart",e.maxBarThickness,"scales.[x/y]Axes.maxBarThickness","dataset.maxBarThickness")},update:function(t){var e,n,i=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,n=i.length;e<n;++e)this.updateElement(i[e],e,t)},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=i.getDataset(),o=i._resolveDataElementOptions(t,e);t._xScale=i.getScaleForId(a.xAxisID),t._yScale=i.getScaleForId(a.yAxisID),t._datasetIndex=i.index,t._index=e,t._model={backgroundColor:o.backgroundColor,borderColor:o.borderColor,borderSkipped:o.borderSkipped,borderWidth:o.borderWidth,datasetLabel:r.label,label:i.chart.data.labels[e]},V.isArray(r.data[e])&&(t._model.borderSkipped=null),i._updateElementGeometry(t,e,n,o),t.pivot()},_updateElementGeometry:function(t,e,n,i){var a=this,r=t._model,o=a._getValueScale(),s=o.getBasePixel(),l=o.isHorizontal(),u=a._ruler||a.getRuler(),d=a.calculateBarValuePixels(a.index,e,i),h=a.calculateBarIndexPixels(a.index,e,u,i);r.horizontal=l,r.base=n?s:d.base,r.x=l?n?s:d.head:h.center,r.y=l?h.center:n?s:d.head,r.height=l?h.size:void 0,r.width=l?void 0:h.size},_getStacks:function(t){var e,n,i=this._getIndexScale(),a=i._getMatchingVisibleMetas(this._type),r=i.options.stacked,o=a.length,s=[];for(e=0;e<o&&(n=a[e],(!1===r||-1===s.indexOf(n.stack)||void 0===r&&void 0===n.stack)&&s.push(n.stack),n.index!==t);++e);return s},getStackCount:function(){return this._getStacks().length},getStackIndex:function(t,e){var n=this._getStacks(t),i=void 0!==e?n.indexOf(e):-1;return-1===i?n.length-1:i},getRuler:function(){var t,e,n=this._getIndexScale(),i=[];for(t=0,e=this.getMeta().data.length;t<e;++t)i.push(n.getPixelForValue(null,t,this.index));return{pixels:i,start:n._startPixel,end:n._endPixel,stackCount:this.getStackCount(),scale:n}},calculateBarValuePixels:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._getValueScale(),c=h.isHorizontal(),f=d.data.datasets,g=h._getMatchingVisibleMetas(this._type),p=h._parseValue(f[t].data[e]),m=n.minBarLength,v=h.options.stacked,b=this.getMeta().stack,x=void 0===p.start?0:p.max>=0&&p.min>=0?p.min:p.max,y=void 0===p.start?p.end:p.max>=0&&p.min>=0?p.max-p.min:p.min-p.max,_=g.length;if(v||void 0===v&&void 0!==b)for(i=0;i<_&&(a=g[i]).index!==t;++i)a.stack===b&&(r=void 0===(u=h._parseValue(f[a.index].data[e])).start?u.end:u.min>=0&&u.max>=0?u.max:u.min,(p.min<0&&r<0||p.max>=0&&r>0)&&(x+=r));return o=h.getPixelForValue(x),l=(s=h.getPixelForValue(x+y))-o,void 0!==m&&Math.abs(l)<m&&(l=m,s=y>=0&&!c||y<0&&c?o-m:o+m),{size:l,base:o,head:s,center:s+l/2}},calculateBarIndexPixels:function(t,e,n,i){var a="flex"===i.barThickness?function(t,e,n){var i,a=e.pixels,r=a[t],o=t>0?a[t-1]:null,s=t<a.length-1?a[t+1]:null,l=n.categoryPercentage;return null===o&&(o=r-(null===s?e.end-e.start:s-r)),null===s&&(s=r+r-o),i=r-(r-Math.min(o,s))/2*l,{chunk:Math.abs(s-o)/2*l/e.stackCount,ratio:n.barPercentage,start:i}}(e,n,i):At(e,n,i),r=this.getStackIndex(t,this.getMeta().stack),o=a.start+a.chunk*r+a.chunk/2,s=Math.min(Pt(i.maxBarThickness,1/0),a.chunk*a.ratio);return{base:o-s/2,head:o+s/2,center:o,size:s}},draw:function(){var t=this.chart,e=this._getValueScale(),n=this.getMeta().data,i=this.getDataset(),a=n.length,r=0;for(V.canvas.clipArea(t.ctx,t.chartArea);r<a;++r){var o=e._parseValue(i.data[r]);isNaN(o.min)||isNaN(o.max)||n[r].draw()}V.canvas.unclipArea(t.ctx)},_resolveDataElementOptions:function(){var t=this,e=V.extend({},nt.prototype._resolveDataElementOptions.apply(t,arguments)),n=t._getIndexScale().options,i=t._getValueScale().options;return e.barPercentage=Pt(n.barPercentage,e.barPercentage),e.barThickness=Pt(n.barThickness,e.barThickness),e.categoryPercentage=Pt(n.categoryPercentage,e.categoryPercentage),e.maxBarThickness=Pt(n.maxBarThickness,e.maxBarThickness),e.minBarLength=Pt(i.minBarLength,e.minBarLength),e}}),Tt=V.valueOrDefault,It=V.options.resolve;z._set("bubble",{hover:{mode:"single"},scales:{xAxes:[{type:"linear",position:"bottom",id:"x-axis-0"}],yAxes:[{type:"linear",position:"left",id:"y-axis-0"}]},tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.datasets[t.datasetIndex].label||"",i=e.datasets[t.datasetIndex].data[t.index];return n+": ("+t.xLabel+", "+t.yLabel+", "+i.r+")"}}}});var Ft=nt.extend({dataElementType:_t.Point,_dataElementOptions:["backgroundColor","borderColor","borderWidth","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth","hoverRadius","hitRadius","pointStyle","rotation"],update:function(t){var e=this,n=e.getMeta().data;V.each(n,(function(n,i){e.updateElement(n,i,t)}))},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=t.custom||{},o=i.getScaleForId(a.xAxisID),s=i.getScaleForId(a.yAxisID),l=i._resolveDataElementOptions(t,e),u=i.getDataset().data[e],d=i.index,h=n?o.getPixelForDecimal(.5):o.getPixelForValue("object"==typeof u?u:NaN,e,d),c=n?s.getBasePixel():s.getPixelForValue(u,e,d);t._xScale=o,t._yScale=s,t._options=l,t._datasetIndex=d,t._index=e,t._model={backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,hitRadius:l.hitRadius,pointStyle:l.pointStyle,rotation:l.rotation,radius:n?0:l.radius,skip:r.skip||isNaN(h)||isNaN(c),x:h,y:c},t.pivot()},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Tt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Tt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Tt(n.hoverBorderWidth,n.borderWidth),e.radius=n.radius+n.hoverRadius},_resolveDataElementOptions:function(t,e){var n=this,i=n.chart,a=n.getDataset(),r=t.custom||{},o=a.data[e]||{},s=nt.prototype._resolveDataElementOptions.apply(n,arguments),l={chart:i,dataIndex:e,dataset:a,datasetIndex:n.index};return n._cachedDataOpts===s&&(s=V.extend({},s)),s.radius=It([r.radius,o.r,n._config.radius,i.options.elements.point.radius],l,e),s}}),Lt=V.valueOrDefault,Ot=Math.PI,Rt=2*Ot,zt=Ot/2;z._set("doughnut",{animation:{animateRotate:!0,animateScale:!1},hover:{mode:"single"},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r]&&(a.data[r].hidden=!a.data[r].hidden);o.update()}},cutoutPercentage:50,rotation:-zt,circumference:Rt,tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.labels[t.index],i=": "+e.datasets[t.datasetIndex].data[t.index];return V.isArray(n)?(n=n.slice())[0]+=i:n+=i,n}}}});var Nt=nt.extend({dataElementType:_t.Arc,linkScales:V.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],getRingIndex:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&++e;return e},update:function(t){var e,n,i,a,r=this,o=r.chart,s=o.chartArea,l=o.options,u=1,d=1,h=0,c=0,f=r.getMeta(),g=f.data,p=l.cutoutPercentage/100||0,m=l.circumference,v=r._getRingWeight(r.index);if(m<Rt){var b=l.rotation%Rt,x=(b+=b>=Ot?-Rt:b<-Ot?Rt:0)+m,y=Math.cos(b),_=Math.sin(b),k=Math.cos(x),w=Math.sin(x),M=b<=0&&x>=0||x>=Rt,S=b<=zt&&x>=zt||x>=Rt+zt,C=b<=-zt&&x>=-zt||x>=Ot+zt,P=b===-Ot||x>=Ot?-1:Math.min(y,y*p,k,k*p),A=C?-1:Math.min(_,_*p,w,w*p),D=M?1:Math.max(y,y*p,k,k*p),T=S?1:Math.max(_,_*p,w,w*p);u=(D-P)/2,d=(T-A)/2,h=-(D+P)/2,c=-(T+A)/2}for(i=0,a=g.length;i<a;++i)g[i]._options=r._resolveDataElementOptions(g[i],i);for(o.borderWidth=r.getMaxBorderWidth(),e=(s.right-s.left-o.borderWidth)/u,n=(s.bottom-s.top-o.borderWidth)/d,o.outerRadius=Math.max(Math.min(e,n)/2,0),o.innerRadius=Math.max(o.outerRadius*p,0),o.radiusLength=(o.outerRadius-o.innerRadius)/(r._getVisibleDatasetWeightTotal()||1),o.offsetX=h*o.outerRadius,o.offsetY=c*o.outerRadius,f.total=r.calculateTotal(),r.outerRadius=o.outerRadius-o.radiusLength*r._getRingWeightOffset(r.index),r.innerRadius=Math.max(r.outerRadius-o.radiusLength*v,0),i=0,a=g.length;i<a;++i)r.updateElement(g[i],i,t)},updateElement:function(t,e,n){var i=this,a=i.chart,r=a.chartArea,o=a.options,s=o.animation,l=(r.left+r.right)/2,u=(r.top+r.bottom)/2,d=o.rotation,h=o.rotation,c=i.getDataset(),f=n&&s.animateRotate?0:t.hidden?0:i.calculateCircumference(c.data[e])*(o.circumference/Rt),g=n&&s.animateScale?0:i.innerRadius,p=n&&s.animateScale?0:i.outerRadius,m=t._options||{};V.extend(t,{_datasetIndex:i.index,_index:e,_model:{backgroundColor:m.backgroundColor,borderColor:m.borderColor,borderWidth:m.borderWidth,borderAlign:m.borderAlign,x:l+a.offsetX,y:u+a.offsetY,startAngle:d,endAngle:h,circumference:f,outerRadius:p,innerRadius:g,label:V.valueAtIndexOrDefault(c.label,e,a.data.labels[e])}});var v=t._model;n&&s.animateRotate||(v.startAngle=0===e?o.rotation:i.getMeta().data[e-1]._model.endAngle,v.endAngle=v.startAngle+v.circumference),t.pivot()},calculateTotal:function(){var t,e=this.getDataset(),n=this.getMeta(),i=0;return V.each(n.data,(function(n,a){t=e.data[a],isNaN(t)||n.hidden||(i+=Math.abs(t))})),i},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?Rt*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,n,i,a,r,o,s,l,u=0,d=this.chart;if(!t)for(e=0,n=d.data.datasets.length;e<n;++e)if(d.isDatasetVisible(e)){t=(i=d.getDatasetMeta(e)).data,e!==this.index&&(r=i.controller);break}if(!t)return 0;for(e=0,n=t.length;e<n;++e)a=t[e],r?(r._configure(),o=r._resolveDataElementOptions(a,e)):o=a._options,"inner"!==o.borderAlign&&(s=o.borderWidth,u=(l=o.hoverBorderWidth)>(u=s>u?s:u)?l:u);return u},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Lt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Lt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Lt(n.hoverBorderWidth,n.borderWidth)},_getRingWeightOffset:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&(e+=this._getRingWeight(n));return e},_getRingWeight:function(t){return Math.max(Lt(this.chart.data.datasets[t].weight,1),0)},_getVisibleDatasetWeightTotal:function(){return this._getRingWeightOffset(this.chart.data.datasets.length)}});z._set("horizontalBar",{hover:{mode:"index",axis:"y"},scales:{xAxes:[{type:"linear",position:"bottom"}],yAxes:[{type:"category",position:"left",offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:"left"}},tooltips:{mode:"index",axis:"y"}}),z._set("global",{datasets:{horizontalBar:{categoryPercentage:.8,barPercentage:.9}}});var Bt=Dt.extend({_getValueScaleId:function(){return this.getMeta().xAxisID},_getIndexScaleId:function(){return this.getMeta().yAxisID}}),Et=V.valueOrDefault,Wt=V.options.resolve,Vt=V.canvas._isPointInArea;function Ht(t,e){var n=t&&t.options.ticks||{},i=n.reverse,a=void 0===n.min?e:0,r=void 0===n.max?e:0;return{start:i?r:a,end:i?a:r}}function jt(t,e,n){var i=n/2,a=Ht(t,i),r=Ht(e,i);return{top:r.end,right:a.end,bottom:r.start,left:a.start}}function qt(t){var e,n,i,a;return V.isObject(t)?(e=t.top,n=t.right,i=t.bottom,a=t.left):e=n=i=a=t,{top:e,right:n,bottom:i,left:a}}z._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}});var Ut=nt.extend({datasetElementType:_t.Line,dataElementType:_t.Point,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth","cubicInterpolationMode","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.options,l=i._config,u=i._showLine=Et(l.showLine,s.showLines);for(i._xScale=i.getScaleForId(a.xAxisID),i._yScale=i.getScaleForId(a.yAxisID),u&&(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=i._yScale,r._datasetIndex=i.index,r._children=o,r._model=i._resolveDatasetElementOptions(r),r.pivot()),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(u&&0!==r._model.tension&&i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i,a,r=this,o=r.getMeta(),s=t.custom||{},l=r.getDataset(),u=r.index,d=l.data[e],h=r._xScale,c=r._yScale,f=o.dataset._model,g=r._resolveDataElementOptions(t,e);i=h.getPixelForValue("object"==typeof d?d:NaN,e,u),a=n?c.getBasePixel():r.calculatePointY(d,e,u),t._xScale=h,t._yScale=c,t._options=g,t._datasetIndex=u,t._index=e,t._model={x:i,y:a,skip:s.skip||isNaN(i)||isNaN(a),radius:g.radius,pointStyle:g.pointStyle,rotation:g.rotation,backgroundColor:g.backgroundColor,borderColor:g.borderColor,borderWidth:g.borderWidth,tension:Et(s.tension,f?f.tension:0),steppedLine:!!f&&f.steppedLine,hitRadius:g.hitRadius}},_resolveDatasetElementOptions:function(t){var e=this,n=e._config,i=t.custom||{},a=e.chart.options,r=a.elements.line,o=nt.prototype._resolveDatasetElementOptions.apply(e,arguments);return o.spanGaps=Et(n.spanGaps,a.spanGaps),o.tension=Et(n.lineTension,r.tension),o.steppedLine=Wt([i.steppedLine,n.steppedLine,r.stepped]),o.clip=qt(Et(n.clip,jt(e._xScale,e._yScale,o.borderWidth))),o},calculatePointY:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._yScale,c=0,f=0;if(h.options.stacked){for(s=+h.getRightValue(t),u=(l=d._getSortedVisibleDatasetMetas()).length,i=0;i<u&&(r=l[i]).index!==n;++i)a=d.data.datasets[r.index],"line"===r.type&&r.yAxisID===h.id&&((o=+h.getRightValue(a.data[e]))<0?f+=o||0:c+=o||0);return s<0?h.getPixelForValue(f+s):h.getPixelForValue(c+s)}return h.getPixelForValue(t)},updateBezierControlPoints:function(){var t,e,n,i,a=this.chart,r=this.getMeta(),o=r.dataset._model,s=a.chartArea,l=r.data||[];function u(t,e,n){return Math.max(Math.min(t,n),e)}if(o.spanGaps&&(l=l.filter((function(t){return!t._model.skip}))),"monotone"===o.cubicInterpolationMode)V.splineCurveMonotone(l);else for(t=0,e=l.length;t<e;++t)n=l[t]._model,i=V.splineCurve(V.previousItem(l,t)._model,n,V.nextItem(l,t)._model,o.tension),n.controlPointPreviousX=i.previous.x,n.controlPointPreviousY=i.previous.y,n.controlPointNextX=i.next.x,n.controlPointNextY=i.next.y;if(a.options.elements.line.capBezierPoints)for(t=0,e=l.length;t<e;++t)n=l[t]._model,Vt(n,s)&&(t>0&&Vt(l[t-1]._model,s)&&(n.controlPointPreviousX=u(n.controlPointPreviousX,s.left,s.right),n.controlPointPreviousY=u(n.controlPointPreviousY,s.top,s.bottom)),t<l.length-1&&Vt(l[t+1]._model,s)&&(n.controlPointNextX=u(n.controlPointNextX,s.left,s.right),n.controlPointNextY=u(n.controlPointNextY,s.top,s.bottom)))},draw:function(){var t,e=this.chart,n=this.getMeta(),i=n.data||[],a=e.chartArea,r=e.canvas,o=0,s=i.length;for(this._showLine&&(t=n.dataset._model.clip,V.canvas.clipArea(e.ctx,{left:!1===t.left?0:a.left-t.left,right:!1===t.right?r.width:a.right+t.right,top:!1===t.top?0:a.top-t.top,bottom:!1===t.bottom?r.height:a.bottom+t.bottom}),n.dataset.draw(),V.canvas.unclipArea(e.ctx));o<s;++o)i[o].draw(a)},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Et(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Et(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Et(n.hoverBorderWidth,n.borderWidth),e.radius=Et(n.hoverRadius,n.radius)}}),Yt=V.options.resolve;z._set("polarArea",{scale:{type:"radialLinear",angleLines:{display:!1},gridLines:{circular:!0},pointLabels:{display:!1},ticks:{beginAtZero:!0}},animation:{animateRotate:!0,animateScale:!0},startAngle:-.5*Math.PI,legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r].hidden=!a.data[r].hidden;o.update()}},tooltips:{callbacks:{title:function(){return""},label:function(t,e){return e.labels[t.index]+": "+t.yLabel}}}});var Gt=nt.extend({dataElementType:_t.Arc,linkScales:V.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i,a=this,r=a.getDataset(),o=a.getMeta(),s=a.chart.options.startAngle||0,l=a._starts=[],u=a._angles=[],d=o.data;for(a._updateRadius(),o.count=a.countVisibleElements(),e=0,n=r.data.length;e<n;e++)l[e]=s,i=a._computeAngle(e),u[e]=i,s+=i;for(e=0,n=d.length;e<n;++e)d[e]._options=a._resolveDataElementOptions(d[e],e),a.updateElement(d[e],e,t)},_updateRadius:function(){var t=this,e=t.chart,n=e.chartArea,i=e.options,a=Math.min(n.right-n.left,n.bottom-n.top);e.outerRadius=Math.max(a/2,0),e.innerRadius=Math.max(i.cutoutPercentage?e.outerRadius/100*i.cutoutPercentage:1,0),e.radiusLength=(e.outerRadius-e.innerRadius)/e.getVisibleDatasetCount(),t.outerRadius=e.outerRadius-e.radiusLength*t.index,t.innerRadius=t.outerRadius-e.radiusLength},updateElement:function(t,e,n){var i=this,a=i.chart,r=i.getDataset(),o=a.options,s=o.animation,l=a.scale,u=a.data.labels,d=l.xCenter,h=l.yCenter,c=o.startAngle,f=t.hidden?0:l.getDistanceFromCenterForValue(r.data[e]),g=i._starts[e],p=g+(t.hidden?0:i._angles[e]),m=s.animateScale?0:l.getDistanceFromCenterForValue(r.data[e]),v=t._options||{};V.extend(t,{_datasetIndex:i.index,_index:e,_scale:l,_model:{backgroundColor:v.backgroundColor,borderColor:v.borderColor,borderWidth:v.borderWidth,borderAlign:v.borderAlign,x:d,y:h,innerRadius:0,outerRadius:n?m:f,startAngle:n&&s.animateRotate?c:g,endAngle:n&&s.animateRotate?c:p,label:V.valueAtIndexOrDefault(u,e,u[e])}}),t.pivot()},countVisibleElements:function(){var t=this.getDataset(),e=this.getMeta(),n=0;return V.each(e.data,(function(e,i){isNaN(t.data[i])||e.hidden||n++})),n},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor,a=V.valueOrDefault;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=a(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=a(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=a(n.hoverBorderWidth,n.borderWidth)},_computeAngle:function(t){var e=this,n=this.getMeta().count,i=e.getDataset(),a=e.getMeta();if(isNaN(i.data[t])||a.data[t].hidden)return 0;var r={chart:e.chart,dataIndex:t,dataset:i,datasetIndex:e.index};return Yt([e.chart.options.elements.arc.angle,2*Math.PI/n],r,t)}});z._set("pie",V.clone(z.doughnut)),z._set("pie",{cutoutPercentage:0});var Xt=Nt,Kt=V.valueOrDefault;z._set("radar",{spanGaps:!1,scale:{type:"radialLinear"},elements:{line:{fill:"start",tension:0}}});var Zt=nt.extend({datasetElementType:_t.Line,dataElementType:_t.Point,linkScales:V.noop,_datasetElementOptions:["backgroundColor","borderWidth","borderColor","borderCapStyle","borderDash","borderDashOffset","borderJoinStyle","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.scale,l=i._config;for(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=s,r._datasetIndex=i.index,r._children=o,r._loop=!0,r._model=i._resolveDatasetElementOptions(r),r.pivot(),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i=this,a=t.custom||{},r=i.getDataset(),o=i.chart.scale,s=o.getPointPositionForValue(e,r.data[e]),l=i._resolveDataElementOptions(t,e),u=i.getMeta().dataset._model,d=n?o.xCenter:s.x,h=n?o.yCenter:s.y;t._scale=o,t._options=l,t._datasetIndex=i.index,t._index=e,t._model={x:d,y:h,skip:a.skip||isNaN(d)||isNaN(h),radius:l.radius,pointStyle:l.pointStyle,rotation:l.rotation,backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,tension:Kt(a.tension,u?u.tension:0),hitRadius:l.hitRadius}},_resolveDatasetElementOptions:function(){var t=this,e=t._config,n=t.chart.options,i=nt.prototype._resolveDatasetElementOptions.apply(t,arguments);return i.spanGaps=Kt(e.spanGaps,n.spanGaps),i.tension=Kt(e.lineTension,n.elements.line.tension),i},updateBezierControlPoints:function(){var t,e,n,i,a=this.getMeta(),r=this.chart.chartArea,o=a.data||[];function s(t,e,n){return Math.max(Math.min(t,n),e)}for(a.dataset._model.spanGaps&&(o=o.filter((function(t){return!t._model.skip}))),t=0,e=o.length;t<e;++t)n=o[t]._model,i=V.splineCurve(V.previousItem(o,t,!0)._model,n,V.nextItem(o,t,!0)._model,n.tension),n.controlPointPreviousX=s(i.previous.x,r.left,r.right),n.controlPointPreviousY=s(i.previous.y,r.top,r.bottom),n.controlPointNextX=s(i.next.x,r.left,r.right),n.controlPointNextY=s(i.next.y,r.top,r.bottom)},setHoverStyle:function(t){var e=t._model,n=t._options,i=V.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Kt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Kt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Kt(n.hoverBorderWidth,n.borderWidth),e.radius=Kt(n.hoverRadius,n.radius)}});z._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}}),z._set("global",{datasets:{scatter:{showLine:!1}}});var $t={bar:Dt,bubble:Ft,doughnut:Nt,horizontalBar:Bt,line:Ut,polarArea:Gt,pie:Xt,radar:Zt,scatter:Ut};function Jt(t,e){return t.native?{x:t.x,y:t.y}:V.getRelativePosition(t,e)}function Qt(t,e){var n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas();for(i=0,r=l.length;i<r;++i)for(a=0,o=(n=l[i].data).length;a<o;++a)(s=n[a])._view.skip||e(s)}function te(t,e){var n=[];return Qt(t,(function(t){t.inRange(e.x,e.y)&&n.push(t)})),n}function ee(t,e,n,i){var a=Number.POSITIVE_INFINITY,r=[];return Qt(t,(function(t){if(!n||t.inRange(e.x,e.y)){var o=t.getCenterPoint(),s=i(e,o);s<a?(r=[t],a=s):s===a&&r.push(t)}})),r}function ne(t){var e=-1!==t.indexOf("x"),n=-1!==t.indexOf("y");return function(t,i){var a=e?Math.abs(t.x-i.x):0,r=n?Math.abs(t.y-i.y):0;return Math.sqrt(Math.pow(a,2)+Math.pow(r,2))}}function ie(t,e,n){var i=Jt(e,t);n.axis=n.axis||"x";var a=ne(n.axis),r=n.intersect?te(t,i):ee(t,i,!1,a),o=[];return r.length?(t._getSortedVisibleDatasetMetas().forEach((function(t){var e=t.data[r[0]._index];e&&!e._view.skip&&o.push(e)})),o):[]}var ae={modes:{single:function(t,e){var n=Jt(e,t),i=[];return Qt(t,(function(t){if(t.inRange(n.x,n.y))return i.push(t),i})),i.slice(0,1)},label:ie,index:ie,dataset:function(t,e,n){var i=Jt(e,t);n.axis=n.axis||"xy";var a=ne(n.axis),r=n.intersect?te(t,i):ee(t,i,!1,a);return r.length>0&&(r=t.getDatasetMeta(r[0]._datasetIndex).data),r},"x-axis":function(t,e){return ie(t,e,{intersect:!1})},point:function(t,e){return te(t,Jt(e,t))},nearest:function(t,e,n){var i=Jt(e,t);n.axis=n.axis||"xy";var a=ne(n.axis);return ee(t,i,n.intersect,a)},x:function(t,e,n){var i=Jt(e,t),a=[],r=!1;return Qt(t,(function(t){t.inXRange(i.x)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a},y:function(t,e,n){var i=Jt(e,t),a=[],r=!1;return Qt(t,(function(t){t.inYRange(i.y)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a}}},re=V.extend;function oe(t,e){return V.where(t,(function(t){return t.pos===e}))}function se(t,e){return t.sort((function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i.index-a.index:i.weight-a.weight}))}function le(t,e,n,i){return Math.max(t[n],e[n])+Math.max(t[i],e[i])}function ue(t,e,n){var i,a,r=n.box,o=t.maxPadding;if(n.size&&(t[n.pos]-=n.size),n.size=n.horizontal?r.height:r.width,t[n.pos]+=n.size,r.getPadding){var s=r.getPadding();o.top=Math.max(o.top,s.top),o.left=Math.max(o.left,s.left),o.bottom=Math.max(o.bottom,s.bottom),o.right=Math.max(o.right,s.right)}if(i=e.outerWidth-le(o,t,"left","right"),a=e.outerHeight-le(o,t,"top","bottom"),i!==t.w||a!==t.h)return t.w=i,t.h=a,n.horizontal?i!==t.w:a!==t.h}function de(t,e){var n=e.maxPadding;function i(t){var i={left:0,top:0,right:0,bottom:0};return t.forEach((function(t){i[t]=Math.max(e[t],n[t])})),i}return i(t?["left","right"]:["top","bottom"])}function he(t,e,n){var i,a,r,o,s,l,u=[];for(i=0,a=t.length;i<a;++i)(o=(r=t[i]).box).update(r.width||e.w,r.height||e.h,de(r.horizontal,e)),ue(e,n,r)&&(l=!0,u.length&&(s=!0)),o.fullWidth||u.push(r);return s&&he(u,e,n)||l}function ce(t,e,n){var i,a,r,o,s=n.padding,l=e.x,u=e.y;for(i=0,a=t.length;i<a;++i)o=(r=t[i]).box,r.horizontal?(o.left=o.fullWidth?s.left:e.left,o.right=o.fullWidth?n.outerWidth-s.right:e.left+e.w,o.top=u,o.bottom=u+o.height,o.width=o.right-o.left,u=o.bottom):(o.left=l,o.right=l+o.width,o.top=e.top,o.bottom=e.top+e.h,o.height=o.bottom-o.top,l=o.right);e.x=l,e.y=u}z._set("global",{layout:{padding:{top:0,right:0,bottom:0,left:0}}});var fe,ge={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw:function(){e.draw.apply(e,arguments)}}]},t.boxes.push(e)},removeBox:function(t,e){var n=t.boxes?t.boxes.indexOf(e):-1;-1!==n&&t.boxes.splice(n,1)},configure:function(t,e,n){for(var i,a=["fullWidth","position","weight"],r=a.length,o=0;o<r;++o)i=a[o],n.hasOwnProperty(i)&&(e[i]=n[i])},update:function(t,e,n){if(t){var i=t.options.layout||{},a=V.options.toPadding(i.padding),r=e-a.width,o=n-a.height,s=function(t){var e=function(t){var e,n,i,a=[];for(e=0,n=(t||[]).length;e<n;++e)i=t[e],a.push({index:e,box:i,pos:i.position,horizontal:i.isHorizontal(),weight:i.weight});return a}(t),n=se(oe(e,"left"),!0),i=se(oe(e,"right")),a=se(oe(e,"top"),!0),r=se(oe(e,"bottom"));return{leftAndTop:n.concat(a),rightAndBottom:i.concat(r),chartArea:oe(e,"chartArea"),vertical:n.concat(i),horizontal:a.concat(r)}}(t.boxes),l=s.vertical,u=s.horizontal,d=Object.freeze({outerWidth:e,outerHeight:n,padding:a,availableWidth:r,vBoxMaxWidth:r/2/l.length,hBoxMaxHeight:o/2}),h=re({maxPadding:re({},a),w:r,h:o,x:a.left,y:a.top},a);!function(t,e){var n,i,a;for(n=0,i=t.length;n<i;++n)(a=t[n]).width=a.horizontal?a.box.fullWidth&&e.availableWidth:e.vBoxMaxWidth,a.height=a.horizontal&&e.hBoxMaxHeight}(l.concat(u),d),he(l,h,d),he(u,h,d)&&he(l,h,d),function(t){var e=t.maxPadding;function n(n){var i=Math.max(e[n]-t[n],0);return t[n]+=i,i}t.y+=n("top"),t.x+=n("left"),n("right"),n("bottom")}(h),ce(s.leftAndTop,h,d),h.x+=h.w,h.y+=h.h,ce(s.rightAndBottom,h,d),t.chartArea={left:h.left,top:h.top,right:h.left+h.w,bottom:h.top+h.h},V.each(s.chartArea,(function(e){var n=e.box;re(n,t.chartArea),n.update(h.w,h.h)}))}}},pe=(fe=Object.freeze({__proto__:null,default:"@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}"}))&&fe.default||fe,me="$chartjs",ve="chartjs-size-monitor",be="chartjs-render-monitor",xe="chartjs-render-animation",ye=["animationstart","webkitAnimationStart"],_e={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function ke(t,e){var n=V.getStyle(t,e),i=n&&n.match(/^(\d+)(\.\d+)?px$/);return i?Number(i[1]):void 0}var we=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function Me(t,e,n){t.addEventListener(e,n,we)}function Se(t,e,n){t.removeEventListener(e,n,we)}function Ce(t,e,n,i,a){return{type:t,chart:e,native:a||null,x:void 0!==n?n:null,y:void 0!==i?i:null}}function Pe(t){var e=document.createElement("div");return e.className=t||"",e}function Ae(t,e,n){var i,a,r,o,s=t[me]||(t[me]={}),l=s.resizer=function(t){var e=Pe(ve),n=Pe(ve+"-expand"),i=Pe(ve+"-shrink");n.appendChild(Pe()),i.appendChild(Pe()),e.appendChild(n),e.appendChild(i),e._reset=function(){n.scrollLeft=1e6,n.scrollTop=1e6,i.scrollLeft=1e6,i.scrollTop=1e6};var a=function(){e._reset(),t()};return Me(n,"scroll",a.bind(n,"expand")),Me(i,"scroll",a.bind(i,"shrink")),e}((i=function(){if(s.resizer){var i=n.options.maintainAspectRatio&&t.parentNode,a=i?i.clientWidth:0;e(Ce("resize",n)),i&&i.clientWidth<a&&n.canvas&&e(Ce("resize",n))}},r=!1,o=[],function(){o=Array.prototype.slice.call(arguments),a=a||this,r||(r=!0,V.requestAnimFrame.call(window,(function(){r=!1,i.apply(a,o)})))}));!function(t,e){var n=t[me]||(t[me]={}),i=n.renderProxy=function(t){t.animationName===xe&&e()};V.each(ye,(function(e){Me(t,e,i)})),n.reflow=!!t.offsetParent,t.classList.add(be)}(t,(function(){if(s.resizer){var e=t.parentNode;e&&e!==l.parentNode&&e.insertBefore(l,e.firstChild),l._reset()}}))}function De(t){var e=t[me]||{},n=e.resizer;delete e.resizer,function(t){var e=t[me]||{},n=e.renderProxy;n&&(V.each(ye,(function(e){Se(t,e,n)})),delete e.renderProxy),t.classList.remove(be)}(t),n&&n.parentNode&&n.parentNode.removeChild(n)}var Te={disableCSSInjection:!1,_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,_ensureLoaded:function(t){if(!this.disableCSSInjection){var e=t.getRootNode?t.getRootNode():document;!function(t,e){var n=t[me]||(t[me]={});if(!n.containsStyles){n.containsStyles=!0,e="/* Chart.js */\n"+e;var i=document.createElement("style");i.setAttribute("type","text/css"),i.appendChild(document.createTextNode(e)),t.appendChild(i)}}(e.host?e:document.head,pe)}},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var n=t&&t.getContext&&t.getContext("2d");return n&&n.canvas===t?(this._ensureLoaded(t),function(t,e){var n=t.style,i=t.getAttribute("height"),a=t.getAttribute("width");if(t[me]={initial:{height:i,width:a,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||"block",null===a||""===a){var r=ke(t,"width");void 0!==r&&(t.width=r)}if(null===i||""===i)if(""===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var o=ke(t,"height");void 0!==r&&(t.height=o)}}(t,e),n):null},releaseContext:function(t){var e=t.canvas;if(e[me]){var n=e[me].initial;["height","width"].forEach((function(t){var i=n[t];V.isNullOrUndef(i)?e.removeAttribute(t):e.setAttribute(t,i)})),V.each(n.style||{},(function(t,n){e.style[n]=t})),e.width=e.width,delete e[me]}},addEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=n[me]||(n[me]={});Me(i,e,(a.proxies||(a.proxies={}))[t.id+"_"+e]=function(e){n(function(t,e){var n=_e[t.type]||t.type,i=V.getRelativePosition(t,e);return Ce(n,e,i.x,i.y,t)}(e,t))})}else Ae(i,n,t)},removeEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=((n[me]||{}).proxies||{})[t.id+"_"+e];a&&Se(i,e,a)}else De(i)}};V.addEvent=Me,V.removeEvent=Se;var Ie=Te._enabled?Te:{acquireContext:function(t){return t&&t.canvas&&(t=t.canvas),t&&t.getContext("2d")||null}},Fe=V.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},Ie);z._set("global",{plugins:{}});var Le={_plugins:[],_cacheId:0,register:function(t){var e=this._plugins;[].concat(t).forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),this._cacheId++},unregister:function(t){var e=this._plugins;[].concat(t).forEach((function(t){var n=e.indexOf(t);-1!==n&&e.splice(n,1)})),this._cacheId++},clear:function(){this._plugins=[],this._cacheId++},count:function(){return this._plugins.length},getAll:function(){return this._plugins},notify:function(t,e,n){var i,a,r,o,s,l=this.descriptors(t),u=l.length;for(i=0;i<u;++i)if("function"==typeof(s=(r=(a=l[i]).plugin)[e])&&((o=[t].concat(n||[])).push(a.options),!1===s.apply(r,o)))return!1;return!0},descriptors:function(t){var e=t.$plugins||(t.$plugins={});if(e.id===this._cacheId)return e.descriptors;var n=[],i=[],a=t&&t.config||{},r=a.options&&a.options.plugins||{};return this._plugins.concat(a.plugins||[]).forEach((function(t){if(-1===n.indexOf(t)){var e=t.id,a=r[e];!1!==a&&(!0===a&&(a=V.clone(z.global.plugins[e])),n.push(t),i.push({plugin:t,options:a||{}}))}})),e.descriptors=i,e.id=this._cacheId,i},_invalidate:function(t){delete t.$plugins}},Oe={constructors:{},defaults:{},registerScaleType:function(t,e,n){this.constructors[t]=e,this.defaults[t]=V.clone(n)},getScaleConstructor:function(t){return this.constructors.hasOwnProperty(t)?this.constructors[t]:void 0},getScaleDefaults:function(t){return this.defaults.hasOwnProperty(t)?V.merge({},[z.scale,this.defaults[t]]):{}},updateScaleDefaults:function(t,e){this.defaults.hasOwnProperty(t)&&(this.defaults[t]=V.extend(this.defaults[t],e))},addScalesToLayout:function(t){V.each(t.scales,(function(e){e.fullWidth=e.options.fullWidth,e.position=e.options.position,e.weight=e.options.weight,ge.addBox(t,e)}))}},Re=V.valueOrDefault,ze=V.rtl.getRtlAdapter;z._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:V.noop,title:function(t,e){var n="",i=e.labels,a=i?i.length:0;if(t.length>0){var r=t[0];r.label?n=r.label:r.xLabel?n=r.xLabel:a>0&&r.index<a&&(n=i[r.index])}return n},afterTitle:V.noop,beforeBody:V.noop,beforeLabel:V.noop,label:function(t,e){var n=e.datasets[t.datasetIndex].label||"";return n&&(n+=": "),V.isNullOrUndef(t.value)?n+=t.yLabel:n+=t.value,n},labelColor:function(t,e){var n=e.getDatasetMeta(t.datasetIndex).data[t.index]._view;return{borderColor:n.borderColor,backgroundColor:n.backgroundColor}},labelTextColor:function(){return this._options.bodyFontColor},afterLabel:V.noop,afterBody:V.noop,beforeFooter:V.noop,footer:V.noop,afterFooter:V.noop}}});var Ne={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,r=0;for(e=0,n=t.length;e<n;++e){var o=t[e];if(o&&o.hasValue()){var s=o.tooltipPosition();i+=s.x,a+=s.y,++r}}return{x:i/r,y:a/r}},nearest:function(t,e){var n,i,a,r=e.x,o=e.y,s=Number.POSITIVE_INFINITY;for(n=0,i=t.length;n<i;++n){var l=t[n];if(l&&l.hasValue()){var u=l.getCenterPoint(),d=V.distanceBetweenPoints(e,u);d<s&&(s=d,a=l)}}if(a){var h=a.tooltipPosition();r=h.x,o=h.y}return{x:r,y:o}}};function Be(t,e){return e&&(V.isArray(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function Ee(t){return("string"==typeof t||t instanceof String)&&t.indexOf("\n")>-1?t.split("\n"):t}function We(t){var e=z.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,rtl:t.rtl,textDirection:t.textDirection,bodyFontColor:t.bodyFontColor,_bodyFontFamily:Re(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:Re(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:Re(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:Re(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:Re(t.titleFontStyle,e.defaultFontStyle),titleFontSize:Re(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:Re(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:Re(t.footerFontStyle,e.defaultFontStyle),footerFontSize:Re(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function Ve(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function He(t){return Be([],Ee(t))}var je=X.extend({initialize:function(){this._model=We(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options,n=e.callbacks,i=n.beforeTitle.apply(t,arguments),a=n.title.apply(t,arguments),r=n.afterTitle.apply(t,arguments),o=[];return o=Be(o,Ee(i)),o=Be(o,Ee(a)),o=Be(o,Ee(r))},getBeforeBody:function(){return He(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var n=this,i=n._options.callbacks,a=[];return V.each(t,(function(t){var r={before:[],lines:[],after:[]};Be(r.before,Ee(i.beforeLabel.call(n,t,e))),Be(r.lines,i.label.call(n,t,e)),Be(r.after,Ee(i.afterLabel.call(n,t,e))),a.push(r)})),a},getAfterBody:function(){return He(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this,e=t._options.callbacks,n=e.beforeFooter.apply(t,arguments),i=e.footer.apply(t,arguments),a=e.afterFooter.apply(t,arguments),r=[];return r=Be(r,Ee(n)),r=Be(r,Ee(i)),r=Be(r,Ee(a))},update:function(t){var e,n,i,a,r,o,s,l,u,d,h=this,c=h._options,f=h._model,g=h._model=We(c),p=h._active,m=h._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},x={width:f.width,height:f.height},y={x:f.caretX,y:f.caretY};if(p.length){g.opacity=1;var _=[],k=[];y=Ne[c.position].call(h,p,h._eventPosition);var w=[];for(e=0,n=p.length;e<n;++e)w.push((i=p[e],a=void 0,r=void 0,o=void 0,s=void 0,l=void 0,u=void 0,d=void 0,a=i._xScale,r=i._yScale||i._scale,o=i._index,s=i._datasetIndex,l=i._chart.getDatasetMeta(s).controller,u=l._getIndexScale(),d=l._getValueScale(),{xLabel:a?a.getLabelForIndex(o,s):"",yLabel:r?r.getLabelForIndex(o,s):"",label:u?""+u.getLabelForIndex(o,s):"",value:d?""+d.getLabelForIndex(o,s):"",index:o,datasetIndex:s,x:i._model.x,y:i._model.y}));c.filter&&(w=w.filter((function(t){return c.filter(t,m)}))),c.itemSort&&(w=w.sort((function(t,e){return c.itemSort(t,e,m)}))),V.each(w,(function(t){_.push(c.callbacks.labelColor.call(h,t,h._chart)),k.push(c.callbacks.labelTextColor.call(h,t,h._chart))})),g.title=h.getTitle(w,m),g.beforeBody=h.getBeforeBody(w,m),g.body=h.getBody(w,m),g.afterBody=h.getAfterBody(w,m),g.footer=h.getFooter(w,m),g.x=y.x,g.y=y.y,g.caretPadding=c.caretPadding,g.labelColors=_,g.labelTextColors=k,g.dataPoints=w,x=function(t,e){var n=t._chart.ctx,i=2*e.yPadding,a=0,r=e.body,o=r.reduce((function(t,e){return t+e.before.length+e.lines.length+e.after.length}),0);o+=e.beforeBody.length+e.afterBody.length;var s=e.title.length,l=e.footer.length,u=e.titleFontSize,d=e.bodyFontSize,h=e.footerFontSize;i+=s*u,i+=s?(s-1)*e.titleSpacing:0,i+=s?e.titleMarginBottom:0,i+=o*d,i+=o?(o-1)*e.bodySpacing:0,i+=l?e.footerMarginTop:0,i+=l*h,i+=l?(l-1)*e.footerSpacing:0;var c=0,f=function(t){a=Math.max(a,n.measureText(t).width+c)};return n.font=V.fontString(u,e._titleFontStyle,e._titleFontFamily),V.each(e.title,f),n.font=V.fontString(d,e._bodyFontStyle,e._bodyFontFamily),V.each(e.beforeBody.concat(e.afterBody),f),c=e.displayColors?d+2:0,V.each(r,(function(t){V.each(t.before,f),V.each(t.lines,f),V.each(t.after,f)})),c=0,n.font=V.fontString(h,e._footerFontStyle,e._footerFontFamily),V.each(e.footer,f),{width:a+=2*e.xPadding,height:i}}(this,g),b=function(t,e,n,i){var a=t.x,r=t.y,o=t.caretSize,s=t.caretPadding,l=t.cornerRadius,u=n.xAlign,d=n.yAlign,h=o+s,c=l+s;return"right"===u?a-=e.width:"center"===u&&((a-=e.width/2)+e.width>i.width&&(a=i.width-e.width),a<0&&(a=0)),"top"===d?r+=h:r-="bottom"===d?e.height+h:e.height/2,"center"===d?"left"===u?a+=h:"right"===u&&(a-=h):"left"===u?a-=c:"right"===u&&(a+=c),{x:a,y:r}}(g,x,v=function(t,e){var n,i,a,r,o,s=t._model,l=t._chart,u=t._chart.chartArea,d="center",h="center";s.y<e.height?h="top":s.y>l.height-e.height&&(h="bottom");var c=(u.left+u.right)/2,f=(u.top+u.bottom)/2;"center"===h?(n=function(t){return t<=c},i=function(t){return t>c}):(n=function(t){return t<=e.width/2},i=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},r=function(t){return t-e.width-s.caretSize-s.caretPadding<0},o=function(t){return t<=f?"top":"bottom"},n(s.x)?(d="left",a(s.x)&&(d="center",h=o(s.y))):i(s.x)&&(d="right",r(s.x)&&(d="center",h=o(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:h}}(this,x),h._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=x.width,g.height=x.height,g.caretX=y.x,g.caretY=y.y,h._model=g,t&&c.custom&&c.custom.call(h,g),h},drawCaret:function(t,e){var n=this._chart.ctx,i=this._view,a=this.getCaretPosition(t,e,i);n.lineTo(a.x1,a.y1),n.lineTo(a.x2,a.y2),n.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,n){var i,a,r,o,s,l,u=n.caretSize,d=n.cornerRadius,h=n.xAlign,c=n.yAlign,f=t.x,g=t.y,p=e.width,m=e.height;if("center"===c)s=g+m/2,"left"===h?(a=(i=f)-u,r=i,o=s+u,l=s-u):(a=(i=f+p)+u,r=i,o=s-u,l=s+u);else if("left"===h?(i=(a=f+d+u)-u,r=a+u):"right"===h?(i=(a=f+p-d-u)-u,r=a+u):(i=(a=n.caretX)-u,r=a+u),"top"===c)s=(o=g)-u,l=o;else{s=(o=g+m)+u,l=o;var v=r;r=i,i=v}return{x1:i,x2:a,x3:r,y1:o,y2:s,y3:l}},drawTitle:function(t,e,n){var i,a,r,o=e.title,s=o.length;if(s){var l=ze(e.rtl,e.x,e.width);for(t.x=Ve(e,e._titleAlign),n.textAlign=l.textAlign(e._titleAlign),n.textBaseline="middle",i=e.titleFontSize,a=e.titleSpacing,n.fillStyle=e.titleFontColor,n.font=V.fontString(i,e._titleFontStyle,e._titleFontFamily),r=0;r<s;++r)n.fillText(o[r],l.x(t.x),t.y+i/2),t.y+=i+a,r+1===s&&(t.y+=e.titleMarginBottom-a)}},drawBody:function(t,e,n){var i,a,r,o,s,l,u,d,h=e.bodyFontSize,c=e.bodySpacing,f=e._bodyAlign,g=e.body,p=e.displayColors,m=0,v=p?Ve(e,"left"):0,b=ze(e.rtl,e.x,e.width),x=function(e){n.fillText(e,b.x(t.x+m),t.y+h/2),t.y+=h+c},y=b.textAlign(f);for(n.textAlign=f,n.textBaseline="middle",n.font=V.fontString(h,e._bodyFontStyle,e._bodyFontFamily),t.x=Ve(e,y),n.fillStyle=e.bodyFontColor,V.each(e.beforeBody,x),m=p&&"right"!==y?"center"===f?h/2+1:h+2:0,s=0,u=g.length;s<u;++s){for(i=g[s],a=e.labelTextColors[s],r=e.labelColors[s],n.fillStyle=a,V.each(i.before,x),l=0,d=(o=i.lines).length;l<d;++l){if(p){var _=b.x(v);n.fillStyle=e.legendColorBackground,n.fillRect(b.leftForLtr(_,h),t.y,h,h),n.lineWidth=1,n.strokeStyle=r.borderColor,n.strokeRect(b.leftForLtr(_,h),t.y,h,h),n.fillStyle=r.backgroundColor,n.fillRect(b.leftForLtr(b.xPlus(_,1),h-2),t.y+1,h-2,h-2),n.fillStyle=a}x(o[l])}V.each(i.after,x)}m=0,V.each(e.afterBody,x),t.y-=c},drawFooter:function(t,e,n){var i,a,r=e.footer,o=r.length;if(o){var s=ze(e.rtl,e.x,e.width);for(t.x=Ve(e,e._footerAlign),t.y+=e.footerMarginTop,n.textAlign=s.textAlign(e._footerAlign),n.textBaseline="middle",i=e.footerFontSize,n.fillStyle=e.footerFontColor,n.font=V.fontString(i,e._footerFontStyle,e._footerFontFamily),a=0;a<o;++a)n.fillText(r[a],s.x(t.x),t.y+i/2),t.y+=i+e.footerSpacing}},drawBackground:function(t,e,n,i){n.fillStyle=e.backgroundColor,n.strokeStyle=e.borderColor,n.lineWidth=e.borderWidth;var a=e.xAlign,r=e.yAlign,o=t.x,s=t.y,l=i.width,u=i.height,d=e.cornerRadius;n.beginPath(),n.moveTo(o+d,s),"top"===r&&this.drawCaret(t,i),n.lineTo(o+l-d,s),n.quadraticCurveTo(o+l,s,o+l,s+d),"center"===r&&"right"===a&&this.drawCaret(t,i),n.lineTo(o+l,s+u-d),n.quadraticCurveTo(o+l,s+u,o+l-d,s+u),"bottom"===r&&this.drawCaret(t,i),n.lineTo(o+d,s+u),n.quadraticCurveTo(o,s+u,o,s+u-d),"center"===r&&"left"===a&&this.drawCaret(t,i),n.lineTo(o,s+d),n.quadraticCurveTo(o,s,o+d,s),n.closePath(),n.fill(),e.borderWidth>0&&n.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(t.save(),t.globalAlpha=a,this.drawBackground(i,e,t,n),i.y+=e.yPadding,V.rtl.overrideTextDirection(t,e.textDirection),this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),V.rtl.restoreTextDirection(t,e.textDirection),t.restore())}},handleEvent:function(t){var e,n=this,i=n._options;return n._lastActive=n._lastActive||[],"mouseout"===t.type?n._active=[]:(n._active=n._chart.getElementsAtEventForMode(t,i.mode,i),i.reverse&&n._active.reverse()),(e=!V.arrayEquals(n._active,n._lastActive))&&(n._lastActive=n._active,(i.enabled||i.custom)&&(n._eventPosition={x:t.x,y:t.y},n.update(!0),n.pivot())),e}}),qe=Ne,Ue=je;Ue.positioners=qe;var Ye=V.valueOrDefault;function Ge(){return V.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){if("xAxes"===t||"yAxes"===t){var a,r,o,s=n[t].length;for(e[t]||(e[t]=[]),a=0;a<s;++a)o=n[t][a],r=Ye(o.type,"xAxes"===t?"category":"linear"),a>=e[t].length&&e[t].push({}),!e[t][a].type||o.type&&o.type!==e[t][a].type?V.merge(e[t][a],[Oe.getScaleDefaults(r),o]):V.merge(e[t][a],o)}else V._merger(t,e,n,i)}})}function Xe(){return V.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){var a=e[t]||{},r=n[t];"scales"===t?e[t]=Ge(a,r):"scale"===t?e[t]=V.merge(a,[Oe.getScaleDefaults(r.type),r]):V._merger(t,e,n,i)}})}function Ke(t){var e=t.options;V.each(t.scales,(function(e){ge.removeBox(t,e)})),e=Xe(z.global,z[t.config.type],e),t.options=t.config.options=e,t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.tooltip._options=e.tooltips,t.tooltip.initialize()}function Ze(t,e,n){var i,a=function(t){return t.id===i};do{i=e+n++}while(V.findIndex(t,a)>=0);return i}function $e(t){return"top"===t||"bottom"===t}function Je(t,e){return function(n,i){return n[t]===i[t]?n[e]-i[e]:n[t]-i[t]}}z._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var Qe=function(t,e){return this.construct(t,e),this};V.extend(Qe.prototype,{construct:function(t,e){var n=this;e=function(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=Xe(z.global,z[t.type],t.options||{}),t}(e);var i=Fe.acquireContext(t,e),a=i&&i.canvas,r=a&&a.height,o=a&&a.width;n.id=V.uid(),n.ctx=i,n.canvas=a,n.config=e,n.width=o,n.height=r,n.aspectRatio=r?o/r:null,n.options=e.options,n._bufferedRender=!1,n._layers=[],n.chart=n,n.controller=n,Qe.instances[n.id]=n,Object.defineProperty(n,"data",{get:function(){return n.config.data},set:function(t){n.config.data=t}}),i&&a?(n.initialize(),n.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return Le.notify(t,"beforeInit"),V.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.initToolTip(),Le.notify(t,"afterInit"),t},clear:function(){return V.canvas.clear(this),this},stop:function(){return $.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,a=n.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(V.getMaximumWidth(i))),o=Math.max(0,Math.floor(a?r/a:V.getMaximumHeight(i)));if((e.width!==r||e.height!==o)&&(i.width=e.width=r,i.height=e.height=o,i.style.width=r+"px",i.style.height=o+"px",V.retinaScale(e,n.devicePixelRatio),!t)){var s={width:r,height:o};Le.notify(e,"resize",[s]),n.onResize&&n.onResize(e,s),e.stop(),e.update({duration:n.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;V.each(e.xAxes,(function(t,n){t.id||(t.id=Ze(e.xAxes,"x-axis-",n))})),V.each(e.yAxes,(function(t,n){t.id||(t.id=Ze(e.yAxes,"y-axis-",n))})),n&&(n.id=n.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,n=t.scales||{},i=[],a=Object.keys(n).reduce((function(t,e){return t[e]=!1,t}),{});e.scales&&(i=i.concat((e.scales.xAxes||[]).map((function(t){return{options:t,dtype:"category",dposition:"bottom"}})),(e.scales.yAxes||[]).map((function(t){return{options:t,dtype:"linear",dposition:"left"}})))),e.scale&&i.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),V.each(i,(function(e){var i=e.options,r=i.id,o=Ye(i.type,e.dtype);$e(i.position)!==$e(e.dposition)&&(i.position=e.dposition),a[r]=!0;var s=null;if(r in n&&n[r].type===o)(s=n[r]).options=i,s.ctx=t.ctx,s.chart=t;else{var l=Oe.getScaleConstructor(o);if(!l)return;s=new l({id:r,type:o,options:i,ctx:t.ctx,chart:t}),n[s.id]=s}s.mergeTicksOptions(),e.isDefault&&(t.scale=s)})),V.each(a,(function(t,e){t||delete n[e]})),t.scales=n,Oe.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t,e,n=this,i=[],a=n.data.datasets;for(t=0,e=a.length;t<e;t++){var r=a[t],o=n.getDatasetMeta(t),s=r.type||n.config.type;if(o.type&&o.type!==s&&(n.destroyDatasetMeta(t),o=n.getDatasetMeta(t)),o.type=s,o.order=r.order||0,o.index=t,o.controller)o.controller.updateIndex(t),o.controller.linkScales();else{var l=$t[o.type];if(void 0===l)throw new Error('"'+o.type+'" is not a chart type.');o.controller=new l(n,t),i.push(o.controller)}}return i},resetElements:function(){var t=this;V.each(t.data.datasets,(function(e,n){t.getDatasetMeta(n).controller.reset()}),t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e,n,i=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),Ke(i),Le._invalidate(i),!1!==Le.notify(i,"beforeUpdate")){i.tooltip._data=i.data;var a=i.buildOrUpdateControllers();for(e=0,n=i.data.datasets.length;e<n;e++)i.getDatasetMeta(e).controller.buildOrUpdateElements();i.updateLayout(),i.options.animation&&i.options.animation.duration&&V.each(a,(function(t){t.reset()})),i.updateDatasets(),i.tooltip.initialize(),i.lastActive=[],Le.notify(i,"afterUpdate"),i._layers.sort(Je("z","_idx")),i._bufferedRender?i._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:i.render(t)}},updateLayout:function(){var t=this;!1!==Le.notify(t,"beforeLayout")&&(ge.update(this,this.width,this.height),t._layers=[],V.each(t.boxes,(function(e){e._configure&&e._configure(),t._layers.push.apply(t._layers,e._layers())}),t),t._layers.forEach((function(t,e){t._idx=e})),Le.notify(t,"afterScaleUpdate"),Le.notify(t,"afterLayout"))},updateDatasets:function(){if(!1!==Le.notify(this,"beforeDatasetsUpdate")){for(var t=0,e=this.data.datasets.length;t<e;++t)this.updateDataset(t);Le.notify(this,"afterDatasetsUpdate")}},updateDataset:function(t){var e=this.getDatasetMeta(t),n={meta:e,index:t};!1!==Le.notify(this,"beforeDatasetUpdate",[n])&&(e.controller._update(),Le.notify(this,"afterDatasetUpdate",[n]))},render:function(t){var e=this;t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]});var n=e.options.animation,i=Ye(t.duration,n&&n.duration),a=t.lazy;if(!1!==Le.notify(e,"beforeRender")){var r=function(t){Le.notify(e,"afterRender"),V.callback(n&&n.onComplete,[t],e)};if(n&&i){var o=new Z({numSteps:i/16.66,easing:t.easing||n.easing,render:function(t,e){var n=V.easing.effects[e.easing],i=e.currentStep,a=i/e.numSteps;t.draw(n(a),a,i)},onAnimationProgress:n.onProgress,onAnimationComplete:r});$.addAnimation(e,o,i,a)}else e.draw(),r(new Z({numSteps:0,chart:e}));return e}},draw:function(t){var e,n,i=this;if(i.clear(),V.isNullOrUndef(t)&&(t=1),i.transition(t),!(i.width<=0||i.height<=0)&&!1!==Le.notify(i,"beforeDraw",[t])){for(n=i._layers,e=0;e<n.length&&n[e].z<=0;++e)n[e].draw(i.chartArea);for(i.drawDatasets(t);e<n.length;++e)n[e].draw(i.chartArea);i._drawTooltip(t),Le.notify(i,"afterDraw",[t])}},transition:function(t){for(var e=0,n=(this.data.datasets||[]).length;e<n;++e)this.isDatasetVisible(e)&&this.getDatasetMeta(e).controller.transition(t);this.tooltip.transition(t)},_getSortedDatasetMetas:function(t){var e,n,i=[];for(e=0,n=(this.data.datasets||[]).length;e<n;++e)t&&!this.isDatasetVisible(e)||i.push(this.getDatasetMeta(e));return i.sort(Je("order","index")),i},_getSortedVisibleDatasetMetas:function(){return this._getSortedDatasetMetas(!0)},drawDatasets:function(t){var e,n;if(!1!==Le.notify(this,"beforeDatasetsDraw",[t])){for(n=(e=this._getSortedVisibleDatasetMetas()).length-1;n>=0;--n)this.drawDataset(e[n],t);Le.notify(this,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n={meta:t,index:t.index,easingValue:e};!1!==Le.notify(this,"beforeDatasetDraw",[n])&&(t.controller.draw(e),Le.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,n={tooltip:e,easingValue:t};!1!==Le.notify(this,"beforeTooltipDraw",[n])&&(e.draw(),Le.notify(this,"afterTooltipDraw",[n]))},getElementAtEvent:function(t){return ae.modes.single(this,t)},getElementsAtEvent:function(t){return ae.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return ae.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=ae.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return ae.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var n=e._meta[this.id];return n||(n=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e.order||0,index:t}),n},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e<n;++e)this.isDatasetVisible(e)&&t++;return t},isDatasetVisible:function(t){var e=this.getDatasetMeta(t);return"boolean"==typeof e.hidden?!e.hidden:!this.data.datasets[t].hidden},generateLegend:function(){return this.options.legendCallback(this)},destroyDatasetMeta:function(t){var e=this.id,n=this.data.datasets[t],i=n._meta&&n._meta[e];i&&(i.controller.destroy(),delete n._meta[e])},destroy:function(){var t,e,n=this,i=n.canvas;for(n.stop(),t=0,e=n.data.datasets.length;t<e;++t)n.destroyDatasetMeta(t);i&&(n.unbindEvents(),V.canvas.clear(n),Fe.releaseContext(n.ctx),n.canvas=null,n.ctx=null),Le.notify(n,"destroy"),delete Qe.instances[n.id]},toBase64Image:function(){return this.canvas.toDataURL.apply(this.canvas,arguments)},initToolTip:function(){var t=this;t.tooltip=new Ue({_chart:t,_chartInstance:t,_data:t.data,_options:t.options.tooltips},t)},bindEvents:function(){var t=this,e=t._listeners={},n=function(){t.eventHandler.apply(t,arguments)};V.each(t.options.events,(function(i){Fe.addEventListener(t,i,n),e[i]=n})),t.options.responsive&&(n=function(){t.resize()},Fe.addEventListener(t,"resize",n),e.resize=n)},unbindEvents:function(){var t=this,e=t._listeners;e&&(delete t._listeners,V.each(e,(function(e,n){Fe.removeEventListener(t,n,e)})))},updateHoverStyle:function(t,e,n){var i,a,r,o=n?"set":"remove";for(a=0,r=t.length;a<r;++a)(i=t[a])&&this.getDatasetMeta(i._datasetIndex).controller[o+"HoverStyle"](i);"dataset"===e&&this.getDatasetMeta(t[0]._datasetIndex).controller["_"+o+"DatasetHoverStyle"]()},eventHandler:function(t){var e=this,n=e.tooltip;if(!1!==Le.notify(e,"beforeEvent",[t])){e._bufferedRender=!0,e._bufferedRequest=null;var i=e.handleEvent(t);n&&(i=n._start?n.handleEvent(t):i|n.handleEvent(t)),Le.notify(e,"afterEvent",[t]);var a=e._bufferedRequest;return a?e.render(a):i&&!e.animating&&(e.stop(),e.render({duration:e.options.hover.animationDuration,lazy:!0})),e._bufferedRender=!1,e._bufferedRequest=null,e}},handleEvent:function(t){var e,n=this,i=n.options||{},a=i.hover;return n.lastActive=n.lastActive||[],"mouseout"===t.type?n.active=[]:n.active=n.getElementsAtEventForMode(t,a.mode,a),V.callback(i.onHover||i.hover.onHover,[t.native,n.active],n),"mouseup"!==t.type&&"click"!==t.type||i.onClick&&i.onClick.call(n,t.native,n.active),n.lastActive.length&&n.updateHoverStyle(n.lastActive,a.mode,!1),n.active.length&&a.mode&&n.updateHoverStyle(n.active,a.mode,!0),e=!V.arrayEquals(n.active,n.lastActive),n.lastActive=n.active,e}}),Qe.instances={};var tn=Qe;Qe.Controller=Qe,Qe.types={},V.configMerge=Xe,V.scaleMerge=Ge;function en(){throw new Error("This method is not implemented: either no adapter can be found or an incomplete integration was provided.")}function nn(t){this.options=t||{}}V.extend(nn.prototype,{formats:en,parse:en,format:en,add:en,diff:en,startOf:en,endOf:en,_create:function(t){return t}}),nn.override=function(t){V.extend(nn.prototype,t)};var an={_date:nn},rn={formatters:{values:function(t){return V.isArray(t)?t:""+t},linear:function(t,e,n){var i=n.length>3?n[2]-n[1]:n[1]-n[0];Math.abs(i)>1&&t!==Math.floor(t)&&(i=t-Math.floor(t));var a=V.log10(Math.abs(i)),r="";if(0!==t)if(Math.max(Math.abs(n[0]),Math.abs(n[n.length-1]))<1e-4){var o=V.log10(Math.abs(t)),s=Math.floor(o)-Math.floor(a);s=Math.max(Math.min(s,20),0),r=t.toExponential(s)}else{var l=-1*Math.floor(a);l=Math.max(Math.min(l,20),0),r=t.toFixed(l)}else r="0";return r},logarithmic:function(t,e,n){var i=t/Math.pow(10,Math.floor(V.log10(t)));return 0===t?"0":1===i||2===i||5===i||0===e||e===n.length-1?t.toExponential():""}}},on=V.isArray,sn=V.isNullOrUndef,ln=V.valueOrDefault,un=V.valueAtIndexOrDefault;function dn(t,e,n){var i,a=t.getTicks().length,r=Math.min(e,a-1),o=t.getPixelForTick(r),s=t._startPixel,l=t._endPixel;if(!(n&&(i=1===a?Math.max(o-s,l-o):0===e?(t.getPixelForTick(1)-o)/2:(o-t.getPixelForTick(r-1))/2,(o+=r<e?i:-i)<s-1e-6||o>l+1e-6)))return o}function hn(t,e,n,i){var a,r,o,s,l,u,d,h,c,f,g,p,m,v=n.length,b=[],x=[],y=[];for(a=0;a<v;++a){if(s=n[a].label,l=n[a].major?e.major:e.minor,t.font=u=l.string,d=i[u]=i[u]||{data:{},gc:[]},h=l.lineHeight,c=f=0,sn(s)||on(s)){if(on(s))for(r=0,o=s.length;r<o;++r)g=s[r],sn(g)||on(g)||(c=V.measureText(t,d.data,d.gc,c,g),f+=h)}else c=V.measureText(t,d.data,d.gc,c,s),f=h;b.push(c),x.push(f),y.push(h/2)}function _(t){return{width:b[t]||0,height:x[t]||0,offset:y[t]||0}}return function(t,e){V.each(t,(function(t){var n,i=t.gc,a=i.length/2;if(a>e){for(n=0;n<a;++n)delete t.data[i[n]];i.splice(0,a)}}))}(i,v),p=b.indexOf(Math.max.apply(null,b)),m=x.indexOf(Math.max.apply(null,x)),{first:_(0),last:_(v-1),widest:_(p),highest:_(m)}}function cn(t){return t.drawTicks?t.tickMarkLength:0}function fn(t){var e,n;return t.display?(e=V.options._parseFont(t),n=V.options.toPadding(t.padding),e.lineHeight+n.height):0}function gn(t,e){return V.extend(V.options._parseFont({fontFamily:ln(e.fontFamily,t.fontFamily),fontSize:ln(e.fontSize,t.fontSize),fontStyle:ln(e.fontStyle,t.fontStyle),lineHeight:ln(e.lineHeight,t.lineHeight)}),{color:V.options.resolve([e.fontColor,t.fontColor,z.global.defaultFontColor])})}function pn(t){var e=gn(t,t.minor);return{minor:e,major:t.major.enabled?gn(t,t.major):e}}function mn(t){var e,n,i,a=[];for(n=0,i=t.length;n<i;++n)void 0!==(e=t[n])._index&&a.push(e);return a}function vn(t,e,n,i){var a,r,o,s,l=ln(n,0),u=Math.min(ln(i,t.length),t.length),d=0;for(e=Math.ceil(e),i&&(e=(a=i-n)/Math.floor(a/e)),s=l;s<0;)d++,s=Math.round(l+d*e);for(r=Math.max(l,0);r<u;r++)o=t[r],r===s?(o._index=r,d++,s=Math.round(l+d*e)):delete o.label}z._set("scale",{display:!0,position:"left",offset:!1,gridLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,drawBorder:!0,drawOnChartArea:!0,drawTicks:!0,tickMarkLength:10,zeroLineWidth:1,zeroLineColor:"rgba(0,0,0,0.25)",zeroLineBorderDash:[],zeroLineBorderDashOffset:0,offsetGridLines:!1,borderDash:[],borderDashOffset:0},scaleLabel:{display:!1,labelString:"",padding:{top:4,bottom:4}},ticks:{beginAtZero:!1,minRotation:0,maxRotation:50,mirror:!1,padding:0,reverse:!1,display:!0,autoSkip:!0,autoSkipPadding:0,labelOffset:0,callback:rn.formatters.values,minor:{},major:{}}});var bn=X.extend({zeroLineIndex:0,getPadding:function(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}},getTicks:function(){return this._ticks},_getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]},mergeTicksOptions:function(){},beforeUpdate:function(){V.callback(this.options.beforeUpdate,[this])},update:function(t,e,n){var i,a,r,o,s,l=this,u=l.options.ticks,d=u.sampleSize;if(l.beforeUpdate(),l.maxWidth=t,l.maxHeight=e,l.margins=V.extend({left:0,right:0,top:0,bottom:0},n),l._ticks=null,l.ticks=null,l._labelSizes=null,l._maxLabelLines=0,l.longestLabelWidth=0,l.longestTextCache=l.longestTextCache||{},l._gridLineItems=null,l._labelItems=null,l.beforeSetDimensions(),l.setDimensions(),l.afterSetDimensions(),l.beforeDataLimits(),l.determineDataLimits(),l.afterDataLimits(),l.beforeBuildTicks(),o=l.buildTicks()||[],(!(o=l.afterBuildTicks(o)||o)||!o.length)&&l.ticks)for(o=[],i=0,a=l.ticks.length;i<a;++i)o.push({value:l.ticks[i],major:!1});return l._ticks=o,s=d<o.length,r=l._convertTicksToLabels(s?function(t,e){for(var n=[],i=t.length/e,a=0,r=t.length;a<r;a+=i)n.push(t[Math.floor(a)]);return n}(o,d):o),l._configure(),l.beforeCalculateTickRotation(),l.calculateTickRotation(),l.afterCalculateTickRotation(),l.beforeFit(),l.fit(),l.afterFit(),l._ticksToDraw=u.display&&(u.autoSkip||"auto"===u.source)?l._autoSkip(o):o,s&&(r=l._convertTicksToLabels(l._ticksToDraw)),l.ticks=r,l.afterUpdate(),l.minSize},_configure:function(){var t,e,n=this,i=n.options.ticks.reverse;n.isHorizontal()?(t=n.left,e=n.right):(t=n.top,e=n.bottom,i=!i),n._startPixel=t,n._endPixel=e,n._reversePixels=i,n._length=e-t},afterUpdate:function(){V.callback(this.options.afterUpdate,[this])},beforeSetDimensions:function(){V.callback(this.options.beforeSetDimensions,[this])},setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0},afterSetDimensions:function(){V.callback(this.options.afterSetDimensions,[this])},beforeDataLimits:function(){V.callback(this.options.beforeDataLimits,[this])},determineDataLimits:V.noop,afterDataLimits:function(){V.callback(this.options.afterDataLimits,[this])},beforeBuildTicks:function(){V.callback(this.options.beforeBuildTicks,[this])},buildTicks:V.noop,afterBuildTicks:function(t){var e=this;return on(t)&&t.length?V.callback(e.options.afterBuildTicks,[e,t]):(e.ticks=V.callback(e.options.afterBuildTicks,[e,e.ticks])||e.ticks,t)},beforeTickToLabelConversion:function(){V.callback(this.options.beforeTickToLabelConversion,[this])},convertTicksToLabels:function(){var t=this.options.ticks;this.ticks=this.ticks.map(t.userCallback||t.callback,this)},afterTickToLabelConversion:function(){V.callback(this.options.afterTickToLabelConversion,[this])},beforeCalculateTickRotation:function(){V.callback(this.options.beforeCalculateTickRotation,[this])},calculateTickRotation:function(){var t,e,n,i,a,r,o,s=this,l=s.options,u=l.ticks,d=s.getTicks().length,h=u.minRotation||0,c=u.maxRotation,f=h;!s._isVisible()||!u.display||h>=c||d<=1||!s.isHorizontal()?s.labelRotation=h:(e=(t=s._getLabelSizes()).widest.width,n=t.highest.height-t.highest.offset,i=Math.min(s.maxWidth,s.chart.width-e),e+6>(a=l.offset?s.maxWidth/d:i/(d-1))&&(a=i/(d-(l.offset?.5:1)),r=s.maxHeight-cn(l.gridLines)-u.padding-fn(l.scaleLabel),o=Math.sqrt(e*e+n*n),f=V.toDegrees(Math.min(Math.asin(Math.min((t.highest.height+6)/a,1)),Math.asin(Math.min(r/o,1))-Math.asin(n/o))),f=Math.max(h,Math.min(c,f))),s.labelRotation=f)},afterCalculateTickRotation:function(){V.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){V.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},n=t.chart,i=t.options,a=i.ticks,r=i.scaleLabel,o=i.gridLines,s=t._isVisible(),l="bottom"===i.position,u=t.isHorizontal();if(u?e.width=t.maxWidth:s&&(e.width=cn(o)+fn(r)),u?s&&(e.height=cn(o)+fn(r)):e.height=t.maxHeight,a.display&&s){var d=pn(a),h=t._getLabelSizes(),c=h.first,f=h.last,g=h.widest,p=h.highest,m=.4*d.minor.lineHeight,v=a.padding;if(u){var b=0!==t.labelRotation,x=V.toRadians(t.labelRotation),y=Math.cos(x),_=Math.sin(x),k=_*g.width+y*(p.height-(b?p.offset:0))+(b?0:m);e.height=Math.min(t.maxHeight,e.height+k+v);var w,M,S=t.getPixelForTick(0)-t.left,C=t.right-t.getPixelForTick(t.getTicks().length-1);b?(w=l?y*c.width+_*c.offset:_*(c.height-c.offset),M=l?_*(f.height-f.offset):y*f.width+_*f.offset):(w=c.width/2,M=f.width/2),t.paddingLeft=Math.max((w-S)*t.width/(t.width-S),0)+3,t.paddingRight=Math.max((M-C)*t.width/(t.width-C),0)+3}else{var P=a.mirror?0:g.width+v+m;e.width=Math.min(t.maxWidth,e.width+P),t.paddingTop=c.height/2,t.paddingBottom=f.height/2}}t.handleMargins(),u?(t.width=t._length=n.width-t.margins.left-t.margins.right,t.height=e.height):(t.width=e.width,t.height=t._length=n.height-t.margins.top-t.margins.bottom)},handleMargins:function(){var t=this;t.margins&&(t.margins.left=Math.max(t.paddingLeft,t.margins.left),t.margins.top=Math.max(t.paddingTop,t.margins.top),t.margins.right=Math.max(t.paddingRight,t.margins.right),t.margins.bottom=Math.max(t.paddingBottom,t.margins.bottom))},afterFit:function(){V.callback(this.options.afterFit,[this])},isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(sn(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},_convertTicksToLabels:function(t){var e,n,i,a=this;for(a.ticks=t.map((function(t){return t.value})),a.beforeTickToLabelConversion(),e=a.convertTicksToLabels(t)||a.ticks,a.afterTickToLabelConversion(),n=0,i=t.length;n<i;++n)t[n].label=e[n];return e},_getLabelSizes:function(){var t=this,e=t._labelSizes;return e||(t._labelSizes=e=hn(t.ctx,pn(t.options.ticks),t.getTicks(),t.longestTextCache),t.longestLabelWidth=e.widest.width),e},_parseValue:function(t){var e,n,i,a;return on(t)?(e=+this.getRightValue(t[0]),n=+this.getRightValue(t[1]),i=Math.min(e,n),a=Math.max(e,n)):(e=void 0,n=t=+this.getRightValue(t),i=t,a=t),{min:i,max:a,start:e,end:n}},_getScaleLabel:function(t){var e=this._parseValue(t);return void 0!==e.start?"["+e.start+", "+e.end+"]":+this.getRightValue(t)},getLabelForIndex:V.noop,getPixelForValue:V.noop,getValueForPixel:V.noop,getPixelForTick:function(t){var e=this.options.offset,n=this._ticks.length,i=1/Math.max(n-(e?0:1),1);return t<0||t>n-1?null:this.getPixelForDecimal(t*i+(e?i/2:0))},getPixelForDecimal:function(t){return this._reversePixels&&(t=1-t),this._startPixel+t*this._length},getDecimalForPixel:function(t){var e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,n,i,a,r=this.options.ticks,o=this._length,s=r.maxTicksLimit||o/this._tickSize()+1,l=r.major.enabled?function(t){var e,n,i=[];for(e=0,n=t.length;e<n;e++)t[e].major&&i.push(e);return i}(t):[],u=l.length,d=l[0],h=l[u-1];if(u>s)return function(t,e,n){var i,a,r=0,o=e[0];for(n=Math.ceil(n),i=0;i<t.length;i++)a=t[i],i===o?(a._index=i,o=e[++r*n]):delete a.label}(t,l,u/s),mn(t);if(i=function(t,e,n,i){var a,r,o,s,l=function(t){var e,n,i=t.length;if(i<2)return!1;for(n=t[0],e=1;e<i;++e)if(t[e]-t[e-1]!==n)return!1;return n}(t),u=(e.length-1)/i;if(!l)return Math.max(u,1);for(o=0,s=(a=V.math._factorize(l)).length-1;o<s;o++)if((r=a[o])>u)return r;return Math.max(u,1)}(l,t,0,s),u>0){for(e=0,n=u-1;e<n;e++)vn(t,i,l[e],l[e+1]);return a=u>1?(h-d)/(u-1):null,vn(t,i,V.isNullOrUndef(a)?0:d-a,d),vn(t,i,h,V.isNullOrUndef(a)?t.length:h+a),mn(t)}return vn(t,i),mn(t)},_tickSize:function(){var t=this.options.ticks,e=V.toRadians(this.labelRotation),n=Math.abs(Math.cos(e)),i=Math.abs(Math.sin(e)),a=this._getLabelSizes(),r=t.autoSkipPadding||0,o=a?a.widest.width+r:0,s=a?a.highest.height+r:0;return this.isHorizontal()?s*n>o*i?o/n:s/i:s*i<o*n?s/n:o/i},_isVisible:function(){var t,e,n,i=this.chart,a=this.options.display;if("auto"!==a)return!!a;for(t=0,e=i.data.datasets.length;t<e;++t)if(i.isDatasetVisible(t)&&((n=i.getDatasetMeta(t)).xAxisID===this.id||n.yAxisID===this.id))return!0;return!1},_computeGridLineItems:function(t){var e,n,i,a,r,o,s,l,u,d,h,c,f,g,p,m,v,b=this,x=b.chart,y=b.options,_=y.gridLines,k=y.position,w=_.offsetGridLines,M=b.isHorizontal(),S=b._ticksToDraw,C=S.length+(w?1:0),P=cn(_),A=[],D=_.drawBorder?un(_.lineWidth,0,0):0,T=D/2,I=V._alignPixel,F=function(t){return I(x,t,D)};for("top"===k?(e=F(b.bottom),s=b.bottom-P,u=e-T,h=F(t.top)+T,f=t.bottom):"bottom"===k?(e=F(b.top),h=t.top,f=F(t.bottom)-T,s=e+T,u=b.top+P):"left"===k?(e=F(b.right),o=b.right-P,l=e-T,d=F(t.left)+T,c=t.right):(e=F(b.left),d=t.left,c=F(t.right)-T,o=e+T,l=b.left+P),n=0;n<C;++n)i=S[n]||{},sn(i.label)&&n<S.length||(n===b.zeroLineIndex&&y.offset===w?(g=_.zeroLineWidth,p=_.zeroLineColor,m=_.zeroLineBorderDash||[],v=_.zeroLineBorderDashOffset||0):(g=un(_.lineWidth,n,1),p=un(_.color,n,"rgba(0,0,0,0.1)"),m=_.borderDash||[],v=_.borderDashOffset||0),void 0!==(a=dn(b,i._index||n,w))&&(r=I(x,a,g),M?o=l=d=c=r:s=u=h=f=r,A.push({tx1:o,ty1:s,tx2:l,ty2:u,x1:d,y1:h,x2:c,y2:f,width:g,color:p,borderDash:m,borderDashOffset:v})));return A.ticksLength=C,A.borderValue=e,A},_computeLabelItems:function(){var t,e,n,i,a,r,o,s,l,u,d,h,c=this,f=c.options,g=f.ticks,p=f.position,m=g.mirror,v=c.isHorizontal(),b=c._ticksToDraw,x=pn(g),y=g.padding,_=cn(f.gridLines),k=-V.toRadians(c.labelRotation),w=[];for("top"===p?(r=c.bottom-_-y,o=k?"left":"center"):"bottom"===p?(r=c.top+_+y,o=k?"right":"center"):"left"===p?(a=c.right-(m?0:_)-y,o=m?"left":"right"):(a=c.left+(m?0:_)+y,o=m?"right":"left"),t=0,e=b.length;t<e;++t)i=(n=b[t]).label,sn(i)||(s=c.getPixelForTick(n._index||t)+g.labelOffset,u=(l=n.major?x.major:x.minor).lineHeight,d=on(i)?i.length:1,v?(a=s,h="top"===p?((k?1:.5)-d)*u:(k?0:.5)*u):(r=s,h=(1-d)*u/2),w.push({x:a,y:r,rotation:k,label:i,font:l,textOffset:h,textAlign:o}));return w},_drawGrid:function(t){var e=this,n=e.options.gridLines;if(n.display){var i,a,r,o,s,l=e.ctx,u=e.chart,d=V._alignPixel,h=n.drawBorder?un(n.lineWidth,0,0):0,c=e._gridLineItems||(e._gridLineItems=e._computeGridLineItems(t));for(r=0,o=c.length;r<o;++r)i=(s=c[r]).width,a=s.color,i&&a&&(l.save(),l.lineWidth=i,l.strokeStyle=a,l.setLineDash&&(l.setLineDash(s.borderDash),l.lineDashOffset=s.borderDashOffset),l.beginPath(),n.drawTicks&&(l.moveTo(s.tx1,s.ty1),l.lineTo(s.tx2,s.ty2)),n.drawOnChartArea&&(l.moveTo(s.x1,s.y1),l.lineTo(s.x2,s.y2)),l.stroke(),l.restore());if(h){var f,g,p,m,v=h,b=un(n.lineWidth,c.ticksLength-1,1),x=c.borderValue;e.isHorizontal()?(f=d(u,e.left,v)-v/2,g=d(u,e.right,b)+b/2,p=m=x):(p=d(u,e.top,v)-v/2,m=d(u,e.bottom,b)+b/2,f=g=x),l.lineWidth=h,l.strokeStyle=un(n.color,0),l.beginPath(),l.moveTo(f,p),l.lineTo(g,m),l.stroke()}}},_drawLabels:function(){var t=this;if(t.options.ticks.display){var e,n,i,a,r,o,s,l,u=t.ctx,d=t._labelItems||(t._labelItems=t._computeLabelItems());for(e=0,i=d.length;e<i;++e){if(o=(r=d[e]).font,u.save(),u.translate(r.x,r.y),u.rotate(r.rotation),u.font=o.string,u.fillStyle=o.color,u.textBaseline="middle",u.textAlign=r.textAlign,s=r.label,l=r.textOffset,on(s))for(n=0,a=s.length;n<a;++n)u.fillText(""+s[n],0,l),l+=o.lineHeight;else u.fillText(s,0,l);u.restore()}}},_drawTitle:function(){var t=this,e=t.ctx,n=t.options,i=n.scaleLabel;if(i.display){var a,r,o=ln(i.fontColor,z.global.defaultFontColor),s=V.options._parseFont(i),l=V.options.toPadding(i.padding),u=s.lineHeight/2,d=n.position,h=0;if(t.isHorizontal())a=t.left+t.width/2,r="bottom"===d?t.bottom-u-l.bottom:t.top+u+l.top;else{var c="left"===d;a=c?t.left+u+l.top:t.right-u-l.top,r=t.top+t.height/2,h=c?-.5*Math.PI:.5*Math.PI}e.save(),e.translate(a,r),e.rotate(h),e.textAlign="center",e.textBaseline="middle",e.fillStyle=o,e.font=s.string,e.fillText(i.labelString,0,0),e.restore()}},draw:function(t){this._isVisible()&&(this._drawGrid(t),this._drawTitle(),this._drawLabels())},_layers:function(){var t=this,e=t.options,n=e.ticks&&e.ticks.z||0,i=e.gridLines&&e.gridLines.z||0;return t._isVisible()&&n!==i&&t.draw===t._draw?[{z:i,draw:function(){t._drawGrid.apply(t,arguments),t._drawTitle.apply(t,arguments)}},{z:n,draw:function(){t._drawLabels.apply(t,arguments)}}]:[{z:n,draw:function(){t.draw.apply(t,arguments)}}]},_getMatchingVisibleMetas:function(t){var e=this,n=e.isHorizontal();return e.chart._getSortedVisibleDatasetMetas().filter((function(i){return(!t||i.type===t)&&(n?i.xAxisID===e.id:i.yAxisID===e.id)}))}});bn.prototype._draw=bn.prototype.draw;var xn=bn,yn=V.isNullOrUndef,_n=xn.extend({determineDataLimits:function(){var t,e=this,n=e._getLabels(),i=e.options.ticks,a=i.min,r=i.max,o=0,s=n.length-1;void 0!==a&&(t=n.indexOf(a))>=0&&(o=t),void 0!==r&&(t=n.indexOf(r))>=0&&(s=t),e.minIndex=o,e.maxIndex=s,e.min=n[o],e.max=n[s]},buildTicks:function(){var t=this._getLabels(),e=this.minIndex,n=this.maxIndex;this.ticks=0===e&&n===t.length-1?t:t.slice(e,n+1)},getLabelForIndex:function(t,e){var n=this.chart;return n.getDatasetMeta(e).controller._getValueScaleId()===this.id?this.getRightValue(n.data.datasets[e].data[t]):this._getLabels()[t]},_configure:function(){var t=this,e=t.options.offset,n=t.ticks;xn.prototype._configure.call(t),t.isHorizontal()||(t._reversePixels=!t._reversePixels),n&&(t._startValue=t.minIndex-(e?.5:0),t._valueRange=Math.max(n.length-(e?0:1),1))},getPixelForValue:function(t,e,n){var i,a,r,o=this;return yn(e)||yn(n)||(t=o.chart.data.datasets[n].data[e]),yn(t)||(i=o.isHorizontal()?t.x:t.y),(void 0!==i||void 0!==t&&isNaN(e))&&(a=o._getLabels(),t=V.valueOrDefault(i,t),e=-1!==(r=a.indexOf(t))?r:e,isNaN(e)&&(e=t)),o.getPixelForDecimal((e-o._startValue)/o._valueRange)},getPixelForTick:function(t){var e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t],t+this.minIndex)},getValueForPixel:function(t){var e=Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange);return Math.min(Math.max(e,0),this.ticks.length-1)},getBasePixel:function(){return this.bottom}}),kn={position:"bottom"};_n._defaults=kn;var wn=V.noop,Mn=V.isNullOrUndef;var Sn=xn.extend({getRightValue:function(t){return"string"==typeof t?+t:xn.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=V.sign(t.min),i=V.sign(t.max);n<0&&i<0?t.max=0:n>0&&i>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==r&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this.options.ticks,n=e.stepSize,i=e.maxTicksLimit;return n?t=Math.ceil(this.max/n)-Math.floor(this.min/n)+1:(t=this._computeTickLimit(),i=i||11),i&&(t=Math.min(i,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:wn,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),i={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,precision:e.precision,stepSize:V.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var n,i,a,r,o=[],s=t.stepSize,l=s||1,u=t.maxTicks-1,d=t.min,h=t.max,c=t.precision,f=e.min,g=e.max,p=V.niceNum((g-f)/u/l)*l;if(p<1e-14&&Mn(d)&&Mn(h))return[f,g];(r=Math.ceil(g/p)-Math.floor(f/p))>u&&(p=V.niceNum(r*p/u/l)*l),s||Mn(c)?n=Math.pow(10,V._decimalPlaces(p)):(n=Math.pow(10,c),p=Math.ceil(p*n)/n),i=Math.floor(f/p)*p,a=Math.ceil(g/p)*p,s&&(!Mn(d)&&V.almostWhole(d/p,p/1e3)&&(i=d),!Mn(h)&&V.almostWhole(h/p,p/1e3)&&(a=h)),r=(a-i)/p,r=V.almostEquals(r,Math.round(r),p/1e3)?Math.round(r):Math.ceil(r),i=Math.round(i*n)/n,a=Math.round(a*n)/n,o.push(Mn(d)?i:d);for(var m=1;m<r;++m)o.push(Math.round((i+m*p)*n)/n);return o.push(Mn(h)?a:h),o}(i,t);t.handleDirectionalChanges(),t.max=V.max(a),t.min=V.min(a),e.reverse?(a.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var t=this;t.ticksAsNumbers=t.ticks.slice(),t.zeroLineIndex=t.ticks.indexOf(0),xn.prototype.convertTicksToLabels.call(t)},_configure:function(){var t,e=this,n=e.getTicks(),i=e.min,a=e.max;xn.prototype._configure.call(e),e.options.offset&&n.length&&(i-=t=(a-i)/Math.max(n.length-1,1)/2,a+=t),e._startValue=i,e._endValue=a,e._valueRange=a-i}}),Cn={position:"left",ticks:{callback:rn.formatters.linear}};function Pn(t,e,n,i){var a,r,o=t.options,s=function(t,e,n){var i=[n.type,void 0===e&&void 0===n.stack?n.index:"",n.stack].join(".");return void 0===t[i]&&(t[i]={pos:[],neg:[]}),t[i]}(e,o.stacked,n),l=s.pos,u=s.neg,d=i.length;for(a=0;a<d;++a)r=t._parseValue(i[a]),isNaN(r.min)||isNaN(r.max)||n.data[a].hidden||(l[a]=l[a]||0,u[a]=u[a]||0,o.relativePoints?l[a]=100:r.min<0||r.max<0?u[a]+=r.min:l[a]+=r.max)}function An(t,e,n){var i,a,r=n.length;for(i=0;i<r;++i)a=t._parseValue(n[i]),isNaN(a.min)||isNaN(a.max)||e.data[i].hidden||(t.min=Math.min(t.min,a.min),t.max=Math.max(t.max,a.max))}var Dn=Sn.extend({determineDataLimits:function(){var t,e,n,i,a=this,r=a.options,o=a.chart.data.datasets,s=a._getMatchingVisibleMetas(),l=r.stacked,u={},d=s.length;if(a.min=Number.POSITIVE_INFINITY,a.max=Number.NEGATIVE_INFINITY,void 0===l)for(t=0;!l&&t<d;++t)l=void 0!==(e=s[t]).stack;for(t=0;t<d;++t)n=o[(e=s[t]).index].data,l?Pn(a,u,e,n):An(a,e,n);V.each(u,(function(t){i=t.pos.concat(t.neg),a.min=Math.min(a.min,V.min(i)),a.max=Math.max(a.max,V.max(i))})),a.min=V.isFinite(a.min)&&!isNaN(a.min)?a.min:0,a.max=V.isFinite(a.max)&&!isNaN(a.max)?a.max:1,a.handleTickRangeOptions()},_computeTickLimit:function(){var t;return this.isHorizontal()?Math.ceil(this.width/40):(t=V.options._parseFont(this.options.ticks),Math.ceil(this.height/t.lineHeight))},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){return this.getPixelForDecimal((+this.getRightValue(t)-this._startValue)/this._valueRange)},getValueForPixel:function(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange},getPixelForTick:function(t){var e=this.ticksAsNumbers;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])}}),Tn=Cn;Dn._defaults=Tn;var In=V.valueOrDefault,Fn=V.math.log10;var Ln={position:"left",ticks:{callback:rn.formatters.logarithmic}};function On(t,e){return V.isFinite(t)&&t>=0?t:e}var Rn=xn.extend({determineDataLimits:function(){var t,e,n,i,a,r,o=this,s=o.options,l=o.chart,u=l.data.datasets,d=o.isHorizontal();function h(t){return d?t.xAxisID===o.id:t.yAxisID===o.id}o.min=Number.POSITIVE_INFINITY,o.max=Number.NEGATIVE_INFINITY,o.minNotZero=Number.POSITIVE_INFINITY;var c=s.stacked;if(void 0===c)for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e)&&void 0!==e.stack){c=!0;break}if(s.stacked||c){var f={};for(t=0;t<u.length;t++){var g=[(e=l.getDatasetMeta(t)).type,void 0===s.stacked&&void 0===e.stack?t:"",e.stack].join(".");if(l.isDatasetVisible(t)&&h(e))for(void 0===f[g]&&(f[g]=[]),a=0,r=(i=u[t].data).length;a<r;a++){var p=f[g];n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(p[a]=p[a]||0,p[a]+=n.max)}}V.each(f,(function(t){if(t.length>0){var e=V.min(t),n=V.max(t);o.min=Math.min(o.min,e),o.max=Math.max(o.max,n)}}))}else for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e))for(a=0,r=(i=u[t].data).length;a<r;a++)n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(o.min=Math.min(n.min,o.min),o.max=Math.max(n.max,o.max),0!==n.min&&(o.minNotZero=Math.min(n.min,o.minNotZero)));o.min=V.isFinite(o.min)?o.min:null,o.max=V.isFinite(o.max)?o.max:null,o.minNotZero=V.isFinite(o.minNotZero)?o.minNotZero:null,this.handleTickRangeOptions()},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;t.min=On(e.min,t.min),t.max=On(e.max,t.max),t.min===t.max&&(0!==t.min&&null!==t.min?(t.min=Math.pow(10,Math.floor(Fn(t.min))-1),t.max=Math.pow(10,Math.floor(Fn(t.max))+1)):(t.min=1,t.max=10)),null===t.min&&(t.min=Math.pow(10,Math.floor(Fn(t.max))-1)),null===t.max&&(t.max=0!==t.min?Math.pow(10,Math.floor(Fn(t.min))+1):10),null===t.minNotZero&&(t.min>0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(Fn(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,n=!t.isHorizontal(),i={min:On(e.min),max:On(e.max)},a=t.ticks=function(t,e){var n,i,a=[],r=In(t.min,Math.pow(10,Math.floor(Fn(e.min)))),o=Math.floor(Fn(e.max)),s=Math.ceil(e.max/Math.pow(10,o));0===r?(n=Math.floor(Fn(e.minNotZero)),i=Math.floor(e.minNotZero/Math.pow(10,n)),a.push(r),r=i*Math.pow(10,n)):(n=Math.floor(Fn(r)),i=Math.floor(r/Math.pow(10,n)));var l=n<0?Math.pow(10,Math.abs(n)):1;do{a.push(r),10===++i&&(i=1,l=++n>=0?1:l),r=Math.round(i*Math.pow(10,n)*l)/l}while(n<o||n===o&&i<s);var u=In(t.max,r);return a.push(u),a}(i,t);t.max=V.max(a),t.min=V.min(a),e.reverse?(n=!n,t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max),n&&a.reverse()},convertTicksToLabels:function(){this.tickValues=this.ticks.slice(),xn.prototype.convertTicksToLabels.call(this)},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForTick:function(t){var e=this.tickValues;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])},_getFirstTickValue:function(t){var e=Math.floor(Fn(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},_configure:function(){var t=this,e=t.min,n=0;xn.prototype._configure.call(t),0===e&&(e=t._getFirstTickValue(t.minNotZero),n=In(t.options.ticks.fontSize,z.global.defaultFontSize)/t._length),t._startValue=Fn(e),t._valueOffset=n,t._valueRange=(Fn(t.max)-Fn(e))/(1-n)},getPixelForValue:function(t){var e=this,n=0;return(t=+e.getRightValue(t))>e.min&&t>0&&(n=(Fn(t)-e._startValue)/e._valueRange+e._valueOffset),e.getPixelForDecimal(n)},getValueForPixel:function(t){var e=this,n=e.getDecimalForPixel(t);return 0===n&&0===e.min?0:Math.pow(10,e._startValue+(n-e._valueOffset)*e._valueRange)}}),zn=Ln;Rn._defaults=zn;var Nn=V.valueOrDefault,Bn=V.valueAtIndexOrDefault,En=V.options.resolve,Wn={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:rn.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function Vn(t){var e=t.ticks;return e.display&&t.display?Nn(e.fontSize,z.global.defaultFontSize)+2*e.backdropPaddingY:0}function Hn(t,e,n,i,a){return t===i||t===a?{start:e-n/2,end:e+n/2}:t<i||t>a?{start:e-n,end:e}:{start:e,end:e+n}}function jn(t){return 0===t||180===t?"center":t<180?"left":"right"}function qn(t,e,n,i){var a,r,o=n.y+i/2;if(V.isArray(e))for(a=0,r=e.length;a<r;++a)t.fillText(e[a],n.x,o),o+=i;else t.fillText(e,n.x,o)}function Un(t,e,n){90===t||270===t?n.y-=e.h/2:(t>270||t<90)&&(n.y-=e.h)}function Yn(t){return V.isNumber(t)?t:0}var Gn=Sn.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Vn(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;V.each(e.data.datasets,(function(a,r){if(e.isDatasetVisible(r)){var o=e.getDatasetMeta(r);V.each(a.data,(function(e,a){var r=+t.getRightValue(e);isNaN(r)||o.data[a].hidden||(n=Math.min(r,n),i=Math.max(r,i))}))}})),t.min=n===Number.POSITIVE_INFINITY?0:n,t.max=i===Number.NEGATIVE_INFINITY?0:i,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Vn(this.options))},convertTicksToLabels:function(){var t=this;Sn.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map((function(){var e=V.callback(t.options.pointLabels.callback,arguments,t);return e||0===e?e:""}))},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this.options;t.display&&t.pointLabels.display?function(t){var e,n,i,a=V.options._parseFont(t.options.pointLabels),r={l:0,r:t.width,t:0,b:t.height-t.paddingTop},o={};t.ctx.font=a.string,t._pointLabelSizes=[];var s,l,u,d=t.chart.data.labels.length;for(e=0;e<d;e++){i=t.getPointPosition(e,t.drawingArea+5),s=t.ctx,l=a.lineHeight,u=t.pointLabels[e],n=V.isArray(u)?{w:V.longestText(s,s.font,u),h:u.length*l}:{w:s.measureText(u).width,h:l},t._pointLabelSizes[e]=n;var h=t.getIndexAngle(e),c=V.toDegrees(h)%360,f=Hn(c,i.x,n.w,0,180),g=Hn(c,i.y,n.h,90,270);f.start<r.l&&(r.l=f.start,o.l=h),f.end>r.r&&(r.r=f.end,o.r=h),g.start<r.t&&(r.t=g.start,o.t=h),g.end>r.b&&(r.b=g.end,o.b=h)}t.setReductions(t.drawingArea,r,o)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(t,e,n){var i=this,a=e.l/Math.sin(n.l),r=Math.max(e.r-i.width,0)/Math.sin(n.r),o=-e.t/Math.cos(n.t),s=-Math.max(e.b-(i.height-i.paddingTop),0)/Math.cos(n.b);a=Yn(a),r=Yn(r),o=Yn(o),s=Yn(s),i.drawingArea=Math.min(Math.floor(t-(a+r)/2),Math.floor(t-(o+s)/2)),i.setCenterPoint(a,r,o,s)},setCenterPoint:function(t,e,n,i){var a=this,r=a.width-e-a.drawingArea,o=t+a.drawingArea,s=n+a.drawingArea,l=a.height-a.paddingTop-i-a.drawingArea;a.xCenter=Math.floor((o+r)/2+a.left),a.yCenter=Math.floor((s+l)/2+a.top+a.paddingTop)},getIndexAngle:function(t){var e=this.chart,n=(t*(360/e.data.labels.length)+((e.options||{}).startAngle||0))%360;return(n<0?n+360:n)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(V.isNullOrUndef(t))return NaN;var n=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*n:(t-e.min)*n},getPointPosition:function(t,e){var n=this.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(n)*e+this.xCenter,y:Math.sin(n)*e+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(t){var e=this.min,n=this.max;return this.getPointPositionForValue(t||0,this.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0)},_drawGrid:function(){var t,e,n,i=this,a=i.ctx,r=i.options,o=r.gridLines,s=r.angleLines,l=Nn(s.lineWidth,o.lineWidth),u=Nn(s.color,o.color);if(r.pointLabels.display&&function(t){var e=t.ctx,n=t.options,i=n.pointLabels,a=Vn(n),r=t.getDistanceFromCenterForValue(n.ticks.reverse?t.min:t.max),o=V.options._parseFont(i);e.save(),e.font=o.string,e.textBaseline="middle";for(var s=t.chart.data.labels.length-1;s>=0;s--){var l=0===s?a/2:0,u=t.getPointPosition(s,r+l+5),d=Bn(i.fontColor,s,z.global.defaultFontColor);e.fillStyle=d;var h=t.getIndexAngle(s),c=V.toDegrees(h);e.textAlign=jn(c),Un(c,t._pointLabelSizes[s],u),qn(e,t.pointLabels[s],u,o.lineHeight)}e.restore()}(i),o.display&&V.each(i.ticks,(function(t,n){0!==n&&(e=i.getDistanceFromCenterForValue(i.ticksAsNumbers[n]),function(t,e,n,i){var a,r=t.ctx,o=e.circular,s=t.chart.data.labels.length,l=Bn(e.color,i-1),u=Bn(e.lineWidth,i-1);if((o||s)&&l&&u){if(r.save(),r.strokeStyle=l,r.lineWidth=u,r.setLineDash&&(r.setLineDash(e.borderDash||[]),r.lineDashOffset=e.borderDashOffset||0),r.beginPath(),o)r.arc(t.xCenter,t.yCenter,n,0,2*Math.PI);else{a=t.getPointPosition(0,n),r.moveTo(a.x,a.y);for(var d=1;d<s;d++)a=t.getPointPosition(d,n),r.lineTo(a.x,a.y)}r.closePath(),r.stroke(),r.restore()}}(i,o,e,n))})),s.display&&l&&u){for(a.save(),a.lineWidth=l,a.strokeStyle=u,a.setLineDash&&(a.setLineDash(En([s.borderDash,o.borderDash,[]])),a.lineDashOffset=En([s.borderDashOffset,o.borderDashOffset,0])),t=i.chart.data.labels.length-1;t>=0;t--)e=i.getDistanceFromCenterForValue(r.ticks.reverse?i.min:i.max),n=i.getPointPosition(t,e),a.beginPath(),a.moveTo(i.xCenter,i.yCenter),a.lineTo(n.x,n.y),a.stroke();a.restore()}},_drawLabels:function(){var t=this,e=t.ctx,n=t.options.ticks;if(n.display){var i,a,r=t.getIndexAngle(0),o=V.options._parseFont(n),s=Nn(n.fontColor,z.global.defaultFontColor);e.save(),e.font=o.string,e.translate(t.xCenter,t.yCenter),e.rotate(r),e.textAlign="center",e.textBaseline="middle",V.each(t.ticks,(function(r,l){(0!==l||n.reverse)&&(i=t.getDistanceFromCenterForValue(t.ticksAsNumbers[l]),n.showLabelBackdrop&&(a=e.measureText(r).width,e.fillStyle=n.backdropColor,e.fillRect(-a/2-n.backdropPaddingX,-i-o.size/2-n.backdropPaddingY,a+2*n.backdropPaddingX,o.size+2*n.backdropPaddingY)),e.fillStyle=s,e.fillText(r,0,-i))})),e.restore()}},_drawTitle:V.noop}),Xn=Wn;Gn._defaults=Xn;var Kn=V._deprecated,Zn=V.options.resolve,$n=V.valueOrDefault,Jn=Number.MIN_SAFE_INTEGER||-9007199254740991,Qn=Number.MAX_SAFE_INTEGER||9007199254740991,ti={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}},ei=Object.keys(ti);function ni(t,e){return t-e}function ii(t){return V.valueOrDefault(t.time.min,t.ticks.min)}function ai(t){return V.valueOrDefault(t.time.max,t.ticks.max)}function ri(t,e,n,i){var a=function(t,e,n){for(var i,a,r,o=0,s=t.length-1;o>=0&&o<=s;){if(a=t[(i=o+s>>1)-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]<n)o=i+1;else{if(!(a[e]>n))return{lo:a,hi:r};s=i-1}}return{lo:r,hi:null}}(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],o=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=o[e]-r[e],l=s?(n-r[e])/s:0,u=(o[i]-r[i])*l;return r[i]+u}function oi(t,e){var n=t._adapter,i=t.options.time,a=i.parser,r=a||i.format,o=e;return"function"==typeof a&&(o=a(o)),V.isFinite(o)||(o="string"==typeof r?n.parse(o,r):n.parse(o)),null!==o?+o:(a||"function"!=typeof r||(o=r(e),V.isFinite(o)||(o=n.parse(o))),o)}function si(t,e){if(V.isNullOrUndef(e))return null;var n=t.options.time,i=oi(t,t.getRightValue(e));return null===i?i:(n.round&&(i=+t._adapter.startOf(i,n.round)),i)}function li(t,e,n,i){var a,r,o,s=ei.length;for(a=ei.indexOf(t);a<s-1;++a)if(o=(r=ti[ei[a]]).steps?r.steps:Qn,r.common&&Math.ceil((n-e)/(o*r.size))<=i)return ei[a];return ei[s-1]}function ui(t,e,n){var i,a,r=[],o={},s=e.length;for(i=0;i<s;++i)o[a=e[i]]=i,r.push({value:a,major:!1});return 0!==s&&n?function(t,e,n,i){var a,r,o=t._adapter,s=+o.startOf(e[0].value,i),l=e[e.length-1].value;for(a=s;a<=l;a=+o.add(a,1,i))(r=n[a])>=0&&(e[r].major=!0);return e}(t,r,o,n):r}var di=xn.extend({initialize:function(){this.mergeTicksOptions(),xn.prototype.initialize.call(this)},update:function(){var t=this,e=t.options,n=e.time||(e.time={}),i=t._adapter=new an._date(e.adapters.date);return Kn("time scale",n.format,"time.format","time.parser"),Kn("time scale",n.min,"time.min","ticks.min"),Kn("time scale",n.max,"time.max","ticks.max"),V.mergeIf(n.displayFormats,i.formats()),xn.prototype.update.apply(t,arguments)},getRightValue:function(t){return t&&void 0!==t.t&&(t=t.t),xn.prototype.getRightValue.call(this,t)},determineDataLimits:function(){var t,e,n,i,a,r,o,s=this,l=s.chart,u=s._adapter,d=s.options,h=d.time.unit||"day",c=Qn,f=Jn,g=[],p=[],m=[],v=s._getLabels();for(t=0,n=v.length;t<n;++t)m.push(si(s,v[t]));for(t=0,n=(l.data.datasets||[]).length;t<n;++t)if(l.isDatasetVisible(t))if(a=l.data.datasets[t].data,V.isObject(a[0]))for(p[t]=[],e=0,i=a.length;e<i;++e)r=si(s,a[e]),g.push(r),p[t][e]=r;else p[t]=m.slice(0),o||(g=g.concat(m),o=!0);else p[t]=[];m.length&&(c=Math.min(c,m[0]),f=Math.max(f,m[m.length-1])),g.length&&(g=n>1?function(t){var e,n,i,a={},r=[];for(e=0,n=t.length;e<n;++e)a[i=t[e]]||(a[i]=!0,r.push(i));return r}(g).sort(ni):g.sort(ni),c=Math.min(c,g[0]),f=Math.max(f,g[g.length-1])),c=si(s,ii(d))||c,f=si(s,ai(d))||f,c=c===Qn?+u.startOf(Date.now(),h):c,f=f===Jn?+u.endOf(Date.now(),h)+1:f,s.min=Math.min(c,f),s.max=Math.max(c+1,f),s._table=[],s._timestamps={data:g,datasets:p,labels:m}},buildTicks:function(){var t,e,n,i=this,a=i.min,r=i.max,o=i.options,s=o.ticks,l=o.time,u=i._timestamps,d=[],h=i.getLabelCapacity(a),c=s.source,f=o.distribution;for(u="data"===c||"auto"===c&&"series"===f?u.data:"labels"===c?u.labels:function(t,e,n,i){var a,r=t._adapter,o=t.options,s=o.time,l=s.unit||li(s.minUnit,e,n,i),u=Zn([s.stepSize,s.unitStepSize,1]),d="week"===l&&s.isoWeekday,h=e,c=[];if(d&&(h=+r.startOf(h,"isoWeek",d)),h=+r.startOf(h,d?"day":l),r.diff(n,e,l)>1e5*u)throw e+" and "+n+" are too far apart with stepSize of "+u+" "+l;for(a=h;a<n;a=+r.add(a,u,l))c.push(a);return a!==n&&"ticks"!==o.bounds||c.push(a),c}(i,a,r,h),"ticks"===o.bounds&&u.length&&(a=u[0],r=u[u.length-1]),a=si(i,ii(o))||a,r=si(i,ai(o))||r,t=0,e=u.length;t<e;++t)(n=u[t])>=a&&n<=r&&d.push(n);return i.min=a,i.max=r,i._unit=l.unit||(s.autoSkip?li(l.minUnit,i.min,i.max,h):function(t,e,n,i,a){var r,o;for(r=ei.length-1;r>=ei.indexOf(n);r--)if(o=ei[r],ti[o].common&&t._adapter.diff(a,i,o)>=e-1)return o;return ei[n?ei.indexOf(n):0]}(i,d.length,l.minUnit,i.min,i.max)),i._majorUnit=s.major.enabled&&"year"!==i._unit?function(t){for(var e=ei.indexOf(t)+1,n=ei.length;e<n;++e)if(ti[ei[e]].common)return ei[e]}(i._unit):void 0,i._table=function(t,e,n,i){if("linear"===i||!t.length)return[{time:e,pos:0},{time:n,pos:1}];var a,r,o,s,l,u=[],d=[e];for(a=0,r=t.length;a<r;++a)(s=t[a])>e&&s<n&&d.push(s);for(d.push(n),a=0,r=d.length;a<r;++a)l=d[a+1],o=d[a-1],s=d[a],void 0!==o&&void 0!==l&&Math.round((l+o)/2)===s||u.push({time:s,pos:a/(r-1)});return u}(i._timestamps.data,a,r,f),i._offsets=function(t,e,n,i,a){var r,o,s=0,l=0;return a.offset&&e.length&&(r=ri(t,"time",e[0],"pos"),s=1===e.length?1-r:(ri(t,"time",e[1],"pos")-r)/2,o=ri(t,"time",e[e.length-1],"pos"),l=1===e.length?o:(o-ri(t,"time",e[e.length-2],"pos"))/2),{start:s,end:l,factor:1/(s+1+l)}}(i._table,d,0,0,o),s.reverse&&d.reverse(),ui(i,d,i._majorUnit)},getLabelForIndex:function(t,e){var n=this,i=n._adapter,a=n.chart.data,r=n.options.time,o=a.labels&&t<a.labels.length?a.labels[t]:"",s=a.datasets[e].data[t];return V.isObject(s)&&(o=n.getRightValue(s)),r.tooltipFormat?i.format(oi(n,o),r.tooltipFormat):"string"==typeof o?o:i.format(oi(n,o),r.displayFormats.datetime)},tickFormatFunction:function(t,e,n,i){var a=this._adapter,r=this.options,o=r.time.displayFormats,s=o[this._unit],l=this._majorUnit,u=o[l],d=n[e],h=r.ticks,c=l&&u&&d&&d.major,f=a.format(t,i||(c?u:s)),g=c?h.major:h.minor,p=Zn([g.callback,g.userCallback,h.callback,h.userCallback]);return p?p(f,e,n):f},convertTicksToLabels:function(t){var e,n,i=[];for(e=0,n=t.length;e<n;++e)i.push(this.tickFormatFunction(t[e].value,e,t));return i},getPixelForOffset:function(t){var e=this._offsets,n=ri(this._table,"time",t,"pos");return this.getPixelForDecimal((e.start+n)*e.factor)},getPixelForValue:function(t,e,n){var i=null;if(void 0!==e&&void 0!==n&&(i=this._timestamps.datasets[n][e]),null===i&&(i=si(this,t)),null!==i)return this.getPixelForOffset(i)},getPixelForTick:function(t){var e=this.getTicks();return t>=0&&t<e.length?this.getPixelForOffset(e[t].value):null},getValueForPixel:function(t){var e=this._offsets,n=this.getDecimalForPixel(t)/e.factor-e.end,i=ri(this._table,"pos",n,"time");return this._adapter._create(i)},_getLabelSize:function(t){var e=this.options.ticks,n=this.ctx.measureText(t).width,i=V.toRadians(this.isHorizontal()?e.maxRotation:e.minRotation),a=Math.cos(i),r=Math.sin(i),o=$n(e.fontSize,z.global.defaultFontSize);return{w:n*a+o*r,h:n*r+o*a}},getLabelWidth:function(t){return this._getLabelSize(t).w},getLabelCapacity:function(t){var e=this,n=e.options.time,i=n.displayFormats,a=i[n.unit]||i.millisecond,r=e.tickFormatFunction(t,0,ui(e,[t],e._majorUnit),a),o=e._getLabelSize(r),s=Math.floor(e.isHorizontal()?e.width/o.w:e.height/o.h);return e.options.offset&&s--,s>0?s:1}}),hi={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};di._defaults=hi;var ci={category:_n,linear:Dn,logarithmic:Rn,radialLinear:Gn,time:di},fi={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};an._date.override("function"==typeof t?{_id:"moment",formats:function(){return fi},parse:function(e,n){return"string"==typeof e&&"string"==typeof n?e=t(e,n):e instanceof t||(e=t(e)),e.isValid()?e.valueOf():null},format:function(e,n){return t(e).format(n)},add:function(e,n,i){return t(e).add(n,i).valueOf()},diff:function(e,n,i){return t(e).diff(t(n),i)},startOf:function(e,n,i){return e=t(e),"isoWeek"===n?e.isoWeekday(i).valueOf():e.startOf(n).valueOf()},endOf:function(e,n){return t(e).endOf(n).valueOf()},_create:function(e){return t(e)}}:{}),z._set("global",{plugins:{filler:{propagate:!0}}});var gi={dataset:function(t){var e=t.fill,n=t.chart,i=n.getDatasetMeta(e),a=i&&n.isDatasetVisible(e)&&i.dataset._children||[],r=a.length||0;return r?function(t,e){return e<r&&a[e]._view||null}:null},boundary:function(t){var e=t.boundary,n=e?e.x:null,i=e?e.y:null;return V.isArray(e)?function(t,n){return e[n]}:function(t){return{x:null===n?t.x:n,y:null===i?t.y:i}}}};function pi(t,e,n){var i,a=t._model||{},r=a.fill;if(void 0===r&&(r=!!a.backgroundColor),!1===r||null===r)return!1;if(!0===r)return"origin";if(i=parseFloat(r,10),isFinite(i)&&Math.floor(i)===i)return"-"!==r[0]&&"+"!==r[0]||(i=e+i),!(i===e||i<0||i>=n)&&i;switch(r){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return r;default:return!1}}function mi(t){return(t.el._scale||{}).getPointPositionForValue?function(t){var e,n,i,a,r,o=t.el._scale,s=o.options,l=o.chart.data.labels.length,u=t.fill,d=[];if(!l)return null;for(e=s.ticks.reverse?o.max:o.min,n=s.ticks.reverse?o.min:o.max,i=o.getPointPositionForValue(0,e),a=0;a<l;++a)r="start"===u||"end"===u?o.getPointPositionForValue(a,"start"===u?e:n):o.getBasePosition(a),s.gridLines.circular&&(r.cx=i.x,r.cy=i.y,r.angle=o.getIndexAngle(a)-Math.PI/2),d.push(r);return d}(t):function(t){var e,n=t.el._model||{},i=t.el._scale||{},a=t.fill,r=null;if(isFinite(a))return null;if("start"===a?r=void 0===n.scaleBottom?i.bottom:n.scaleBottom:"end"===a?r=void 0===n.scaleTop?i.top:n.scaleTop:void 0!==n.scaleZero?r=n.scaleZero:i.getBasePixel&&(r=i.getBasePixel()),null!=r){if(void 0!==r.x&&void 0!==r.y)return r;if(V.isFinite(r))return{x:(e=i.isHorizontal())?r:null,y:e?null:r}}return null}(t)}function vi(t,e,n){var i,a=t[e].fill,r=[e];if(!n)return a;for(;!1!==a&&-1===r.indexOf(a);){if(!isFinite(a))return a;if(!(i=t[a]))return!1;if(i.visible)return a;r.push(a),a=i.fill}return!1}function bi(t){var e=t.fill,n="dataset";return!1===e?null:(isFinite(e)||(n="boundary"),gi[n](t))}function xi(t){return t&&!t.skip}function yi(t,e,n,i,a){var r,o,s,l;if(i&&a){for(t.moveTo(e[0].x,e[0].y),r=1;r<i;++r)V.canvas.lineTo(t,e[r-1],e[r]);if(void 0===n[0].angle)for(t.lineTo(n[a-1].x,n[a-1].y),r=a-1;r>0;--r)V.canvas.lineTo(t,n[r],n[r-1],!0);else for(o=n[0].cx,s=n[0].cy,l=Math.sqrt(Math.pow(n[0].x-o,2)+Math.pow(n[0].y-s,2)),r=a-1;r>0;--r)t.arc(o,s,l,n[r].angle,n[r-1].angle,!0)}}function _i(t,e,n,i,a,r){var o,s,l,u,d,h,c,f,g=e.length,p=i.spanGaps,m=[],v=[],b=0,x=0;for(t.beginPath(),o=0,s=g;o<s;++o)d=n(u=e[l=o%g]._view,l,i),h=xi(u),c=xi(d),r&&void 0===f&&h&&(s=g+(f=o+1)),h&&c?(b=m.push(u),x=v.push(d)):b&&x&&(p?(h&&m.push(u),c&&v.push(d)):(yi(t,m,v,b,x),b=x=0,m=[],v=[]));yi(t,m,v,b,x),t.closePath(),t.fillStyle=a,t.fill()}var ki={id:"filler",afterDatasetsUpdate:function(t,e){var n,i,a,r,o=(t.data.datasets||[]).length,s=e.propagate,l=[];for(i=0;i<o;++i)r=null,(a=(n=t.getDatasetMeta(i)).dataset)&&a._model&&a instanceof _t.Line&&(r={visible:t.isDatasetVisible(i),fill:pi(a,i,o),chart:t,el:a}),n.$filler=r,l.push(r);for(i=0;i<o;++i)(r=l[i])&&(r.fill=vi(l,i,s),r.boundary=mi(r),r.mapper=bi(r))},beforeDatasetsDraw:function(t){var e,n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas(),u=t.ctx;for(n=l.length-1;n>=0;--n)(e=l[n].$filler)&&e.visible&&(a=(i=e.el)._view,r=i._children||[],o=e.mapper,s=a.backgroundColor||z.global.defaultColor,o&&s&&r.length&&(V.canvas.clipArea(u,t.chartArea),_i(u,r,o,a,s,i._loop),V.canvas.unclipArea(u)))}},wi=V.rtl.getRtlAdapter,Mi=V.noop,Si=V.valueOrDefault;function Ci(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}z._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var n=e.datasetIndex,i=this.chart,a=i.getDatasetMeta(n);a.hidden=null===a.hidden?!i.data.datasets[n].hidden:null,i.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data.datasets,n=t.options.legend||{},i=n.labels&&n.labels.usePointStyle;return t._getSortedDatasetMetas().map((function(n){var a=n.controller.getStyle(i?0:void 0);return{text:e[n.index].label,fillStyle:a.backgroundColor,hidden:!t.isDatasetVisible(n.index),lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:a.borderWidth,strokeStyle:a.borderColor,pointStyle:a.pointStyle,rotation:a.rotation,datasetIndex:n.index}}),this)}}},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data.datasets;for(a.setAttribute("class",t.id+"-legend"),e=0,n=r.length;e<n;e++)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=r[e].backgroundColor,r[e].label&&i.appendChild(document.createTextNode(r[e].label));return a.outerHTML}});var Pi=X.extend({initialize:function(t){V.extend(this,t),this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1},beforeUpdate:Mi,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Mi,beforeSetDimensions:Mi,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Mi,beforeBuildLabels:Mi,buildLabels:function(){var t=this,e=t.options.labels||{},n=V.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(n=n.filter((function(n){return e.filter(n,t.chart.data)}))),t.options.reverse&&n.reverse(),t.legendItems=n},afterBuildLabels:Mi,beforeFit:Mi,fit:function(){var t=this,e=t.options,n=e.labels,i=e.display,a=t.ctx,r=V.options._parseFont(n),o=r.size,s=t.legendHitBoxes=[],l=t.minSize,u=t.isHorizontal();if(u?(l.width=t.maxWidth,l.height=i?10:0):(l.width=i?10:0,l.height=t.maxHeight),i){if(a.font=r.string,u){var d=t.lineWidths=[0],h=0;a.textAlign="left",a.textBaseline="middle",V.each(t.legendItems,(function(t,e){var i=Ci(n,o)+o/2+a.measureText(t.text).width;(0===e||d[d.length-1]+i+2*n.padding>l.width)&&(h+=o+n.padding,d[d.length-(e>0?0:1)]=0),s[e]={left:0,top:0,width:i,height:o},d[d.length-1]+=i+n.padding})),l.height+=h}else{var c=n.padding,f=t.columnWidths=[],g=t.columnHeights=[],p=n.padding,m=0,v=0;V.each(t.legendItems,(function(t,e){var i=Ci(n,o)+o/2+a.measureText(t.text).width;e>0&&v+o+2*c>l.height&&(p+=m+n.padding,f.push(m),g.push(v),m=0,v=0),m=Math.max(m,i),v+=o+c,s[e]={left:0,top:0,width:i,height:o}})),p+=m,f.push(m),g.push(v),l.width+=p}t.width=l.width,t.height=l.height}else t.width=l.width=t.height=l.height=0},afterFit:Mi,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,n=e.labels,i=z.global,a=i.defaultColor,r=i.elements.line,o=t.height,s=t.columnHeights,l=t.width,u=t.lineWidths;if(e.display){var d,h=wi(e.rtl,t.left,t.minSize.width),c=t.ctx,f=Si(n.fontColor,i.defaultFontColor),g=V.options._parseFont(n),p=g.size;c.textAlign=h.textAlign("left"),c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=g.string;var m=Ci(n,p),v=t.legendHitBoxes,b=function(t,i){switch(e.align){case"start":return n.padding;case"end":return t-i;default:return(t-i+n.padding)/2}},x=t.isHorizontal();d=x?{x:t.left+b(l,u[0]),y:t.top+n.padding,line:0}:{x:t.left+n.padding,y:t.top+b(o,s[0]),line:0},V.rtl.overrideTextDirection(t.ctx,e.textDirection);var y=p+n.padding;V.each(t.legendItems,(function(e,i){var f=c.measureText(e.text).width,g=m+p/2+f,_=d.x,k=d.y;h.setWidth(t.minSize.width),x?i>0&&_+g+n.padding>t.left+t.minSize.width&&(k=d.y+=y,d.line++,_=d.x=t.left+b(l,u[d.line])):i>0&&k+y>t.top+t.minSize.height&&(_=d.x=_+t.columnWidths[d.line]+n.padding,d.line++,k=d.y=t.top+b(o,s[d.line]));var w=h.x(_);!function(t,e,i){if(!(isNaN(m)||m<=0)){c.save();var o=Si(i.lineWidth,r.borderWidth);if(c.fillStyle=Si(i.fillStyle,a),c.lineCap=Si(i.lineCap,r.borderCapStyle),c.lineDashOffset=Si(i.lineDashOffset,r.borderDashOffset),c.lineJoin=Si(i.lineJoin,r.borderJoinStyle),c.lineWidth=o,c.strokeStyle=Si(i.strokeStyle,a),c.setLineDash&&c.setLineDash(Si(i.lineDash,r.borderDash)),n&&n.usePointStyle){var s=m*Math.SQRT2/2,l=h.xPlus(t,m/2),u=e+p/2;V.canvas.drawPoint(c,i.pointStyle,s,l,u,i.rotation)}else c.fillRect(h.leftForLtr(t,m),e,m,p),0!==o&&c.strokeRect(h.leftForLtr(t,m),e,m,p);c.restore()}}(w,k,e),v[i].left=h.leftForLtr(w,v[i].width),v[i].top=k,function(t,e,n,i){var a=p/2,r=h.xPlus(t,m+a),o=e+a;c.fillText(n.text,r,o),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(r,o),c.lineTo(h.xPlus(r,i),o),c.stroke())}(w,k,e,f),x?d.x+=g+n.padding:d.y+=y})),V.rtl.restoreTextDirection(t.ctx,e.textDirection)}},_getLegendItemAt:function(t,e){var n,i,a,r=this;if(t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom)for(a=r.legendHitBoxes,n=0;n<a.length;++n)if(t>=(i=a[n]).left&&t<=i.left+i.width&&e>=i.top&&e<=i.top+i.height)return r.legendItems[n];return null},handleEvent:function(t){var e,n=this,i=n.options,a="mouseup"===t.type?"click":t.type;if("mousemove"===a){if(!i.onHover&&!i.onLeave)return}else{if("click"!==a)return;if(!i.onClick)return}e=n._getLegendItemAt(t.x,t.y),"click"===a?e&&i.onClick&&i.onClick.call(n,t.native,e):(i.onLeave&&e!==n._hoveredItem&&(n._hoveredItem&&i.onLeave.call(n,t.native,n._hoveredItem),n._hoveredItem=e),i.onHover&&e&&i.onHover.call(n,t.native,e))}});function Ai(t,e){var n=new Pi({ctx:t.ctx,options:e,chart:t});ge.configure(t,n,e),ge.addBox(t,n),t.legend=n}var Di={id:"legend",_element:Pi,beforeInit:function(t){var e=t.options.legend;e&&Ai(t,e)},beforeUpdate:function(t){var e=t.options.legend,n=t.legend;e?(V.mergeIf(e,z.global.legend),n?(ge.configure(t,n,e),n.options=e):Ai(t,e)):n&&(ge.removeBox(t,n),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}},Ti=V.noop;z._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var Ii=X.extend({initialize:function(t){V.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:Ti,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Ti,beforeSetDimensions:Ti,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Ti,beforeBuildLabels:Ti,buildLabels:Ti,afterBuildLabels:Ti,beforeFit:Ti,fit:function(){var t,e=this,n=e.options,i=e.minSize={},a=e.isHorizontal();n.display?(t=(V.isArray(n.text)?n.text.length:1)*V.options._parseFont(n).lineHeight+2*n.padding,e.width=i.width=a?e.maxWidth:t,e.height=i.height=a?t:e.maxHeight):e.width=i.width=e.height=i.height=0},afterFit:Ti,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=t.options;if(n.display){var i,a,r,o=V.options._parseFont(n),s=o.lineHeight,l=s/2+n.padding,u=0,d=t.top,h=t.left,c=t.bottom,f=t.right;e.fillStyle=V.valueOrDefault(n.fontColor,z.global.defaultFontColor),e.font=o.string,t.isHorizontal()?(a=h+(f-h)/2,r=d+l,i=f-h):(a="left"===n.position?h+l:f-l,r=d+(c-d)/2,i=c-d,u=Math.PI*("left"===n.position?-.5:.5)),e.save(),e.translate(a,r),e.rotate(u),e.textAlign="center",e.textBaseline="middle";var g=n.text;if(V.isArray(g))for(var p=0,m=0;m<g.length;++m)e.fillText(g[m],0,p,i),p+=s;else e.fillText(g,0,0,i);e.restore()}}});function Fi(t,e){var n=new Ii({ctx:t.ctx,options:e,chart:t});ge.configure(t,n,e),ge.addBox(t,n),t.titleBlock=n}var Li={},Oi=ki,Ri=Di,zi={id:"title",_element:Ii,beforeInit:function(t){var e=t.options.title;e&&Fi(t,e)},beforeUpdate:function(t){var e=t.options.title,n=t.titleBlock;e?(V.mergeIf(e,z.global.title),n?(ge.configure(t,n,e),n.options=e):Fi(t,e)):n&&(ge.removeBox(t,n),delete t.titleBlock)}};for(var Ni in Li.filler=Oi,Li.legend=Ri,Li.title=zi,tn.helpers=V,function(){function t(t,e,n){var i;return"string"==typeof t?(i=parseInt(t,10),-1!==t.indexOf("%")&&(i=i/100*e.parentNode[n])):i=t,i}function e(t){return null!=t&&"none"!==t}function n(n,i,a){var r=document.defaultView,o=V._getParentNode(n),s=r.getComputedStyle(n)[i],l=r.getComputedStyle(o)[i],u=e(s),d=e(l),h=Number.POSITIVE_INFINITY;return u||d?Math.min(u?t(s,n,a):h,d?t(l,o,a):h):"none"}V.where=function(t,e){if(V.isArray(t)&&Array.prototype.filter)return t.filter(e);var n=[];return V.each(t,(function(t){e(t)&&n.push(t)})),n},V.findIndex=Array.prototype.findIndex?function(t,e,n){return t.findIndex(e,n)}:function(t,e,n){n=void 0===n?t:n;for(var i=0,a=t.length;i<a;++i)if(e.call(n,t[i],i,t))return i;return-1},V.findNextWhere=function(t,e,n){V.isNullOrUndef(n)&&(n=-1);for(var i=n+1;i<t.length;i++){var a=t[i];if(e(a))return a}},V.findPreviousWhere=function(t,e,n){V.isNullOrUndef(n)&&(n=t.length);for(var i=n-1;i>=0;i--){var a=t[i];if(e(a))return a}},V.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},V.almostEquals=function(t,e,n){return Math.abs(t-e)<n},V.almostWhole=function(t,e){var n=Math.round(t);return n-e<=t&&n+e>=t},V.max=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.max(t,e)}),Number.NEGATIVE_INFINITY)},V.min=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.min(t,e)}),Number.POSITIVE_INFINITY)},V.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0===(t=+t)||isNaN(t)?t:t>0?1:-1},V.toRadians=function(t){return t*(Math.PI/180)},V.toDegrees=function(t){return t*(180/Math.PI)},V._decimalPlaces=function(t){if(V.isFinite(t)){for(var e=1,n=0;Math.round(t*e)/e!==t;)e*=10,n++;return n}},V.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},V.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},V.aliasPixel=function(t){return t%2==0?0:.5},V._alignPixel=function(t,e,n){var i=t.currentDevicePixelRatio,a=n/2;return Math.round((e-a)*i)/i+a},V.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,s=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),l=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=s/(s+l),d=l/(s+l),h=i*(u=isNaN(u)?0:u),c=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-h*(o.x-a.x),y:r.y-h*(o.y-a.y)},next:{x:r.x+c*(o.x-a.x),y:r.y+c*(o.y-a.y)}}},V.EPSILON=Number.EPSILON||1e-14,V.splineCurveMonotone=function(t){var e,n,i,a,r,o,s,l,u,d=(t||[]).map((function(t){return{model:t._model,deltaK:0,mK:0}})),h=d.length;for(e=0;e<h;++e)if(!(i=d[e]).model.skip){if(n=e>0?d[e-1]:null,(a=e<h-1?d[e+1]:null)&&!a.model.skip){var c=a.model.x-i.model.x;i.deltaK=0!==c?(a.model.y-i.model.y)/c:0}!n||n.model.skip?i.mK=i.deltaK:!a||a.model.skip?i.mK=n.deltaK:this.sign(n.deltaK)!==this.sign(i.deltaK)?i.mK=0:i.mK=(n.deltaK+i.deltaK)/2}for(e=0;e<h-1;++e)i=d[e],a=d[e+1],i.model.skip||a.model.skip||(V.almostEquals(i.deltaK,0,this.EPSILON)?i.mK=a.mK=0:(r=i.mK/i.deltaK,o=a.mK/i.deltaK,(l=Math.pow(r,2)+Math.pow(o,2))<=9||(s=3/Math.sqrt(l),i.mK=r*s*i.deltaK,a.mK=o*s*i.deltaK)));for(e=0;e<h;++e)(i=d[e]).model.skip||(n=e>0?d[e-1]:null,a=e<h-1?d[e+1]:null,n&&!n.model.skip&&(u=(i.model.x-n.model.x)/3,i.model.controlPointPreviousX=i.model.x-u,i.model.controlPointPreviousY=i.model.y-u*i.mK),a&&!a.model.skip&&(u=(a.model.x-i.model.x)/3,i.model.controlPointNextX=i.model.x+u,i.model.controlPointNextY=i.model.y+u*i.mK))},V.nextItem=function(t,e,n){return n?e>=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},V.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},V.niceNum=function(t,e){var n=Math.floor(V.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},V.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},V.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,r=t.target||t.srcElement,o=r.getBoundingClientRect(),s=a.touches;s&&s.length>0?(n=s[0].clientX,i=s[0].clientY):(n=a.clientX,i=a.clientY);var l=parseFloat(V.getStyle(r,"padding-left")),u=parseFloat(V.getStyle(r,"padding-top")),d=parseFloat(V.getStyle(r,"padding-right")),h=parseFloat(V.getStyle(r,"padding-bottom")),c=o.right-o.left-l-d,f=o.bottom-o.top-u-h;return{x:n=Math.round((n-o.left-l)/c*r.width/e.currentDevicePixelRatio),y:i=Math.round((i-o.top-u)/f*r.height/e.currentDevicePixelRatio)}},V.getConstraintWidth=function(t){return n(t,"max-width","clientWidth")},V.getConstraintHeight=function(t){return n(t,"max-height","clientHeight")},V._calculatePadding=function(t,e,n){return(e=V.getStyle(t,e)).indexOf("%")>-1?n*parseInt(e,10)/100:parseInt(e,10)},V._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},V.getMaximumWidth=function(t){var e=V._getParentNode(t);if(!e)return t.clientWidth;var n=e.clientWidth,i=n-V._calculatePadding(e,"padding-left",n)-V._calculatePadding(e,"padding-right",n),a=V.getConstraintWidth(t);return isNaN(a)?i:Math.min(i,a)},V.getMaximumHeight=function(t){var e=V._getParentNode(t);if(!e)return t.clientHeight;var n=e.clientHeight,i=n-V._calculatePadding(e,"padding-top",n)-V._calculatePadding(e,"padding-bottom",n),a=V.getConstraintHeight(t);return isNaN(a)?i:Math.min(i,a)},V.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},V.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height||i.style.width||(i.style.height=a+"px",i.style.width=r+"px")}},V.fontString=function(t,e,n){return e+" "+t+"px "+n},V.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},r=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},r=i.garbageCollect=[],i.font=e),t.font=e;var o,s,l,u,d,h=0,c=n.length;for(o=0;o<c;o++)if(null!=(u=n[o])&&!0!==V.isArray(u))h=V.measureText(t,a,r,h,u);else if(V.isArray(u))for(s=0,l=u.length;s<l;s++)null==(d=u[s])||V.isArray(d)||(h=V.measureText(t,a,r,h,d));var f=r.length/2;if(f>n.length){for(o=0;o<f;o++)delete a[r[o]];r.splice(0,f)}return h},V.measureText=function(t,e,n,i,a){var r=e[a];return r||(r=e[a]=t.measureText(a).width,n.push(a)),r>i&&(i=r),i},V.numberOfLabelLines=function(t){var e=1;return V.each(t,(function(t){V.isArray(t)&&t.length>e&&(e=t.length)})),e},V.color=k?function(t){return t instanceof CanvasGradient&&(t=z.global.defaultColor),k(t)}:function(t){return console.error("Color.js not found!"),t},V.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:V.color(t).saturate(.5).darken(.1).rgbString()}}(),tn._adapters=an,tn.Animation=Z,tn.animationService=$,tn.controllers=$t,tn.DatasetController=nt,tn.defaults=z,tn.Element=X,tn.elements=_t,tn.Interaction=ae,tn.layouts=ge,tn.platform=Fe,tn.plugins=Le,tn.Scale=xn,tn.scaleService=Oe,tn.Ticks=rn,tn.Tooltip=Ue,tn.helpers.each(ci,(function(t,e){tn.scaleService.registerScaleType(e,t,t._defaults)})),Li)Li.hasOwnProperty(Ni)&&tn.plugins.register(Li[Ni]);tn.platform.initialize();var Bi=tn;return"undefined"!=typeof window&&(window.Chart=tn),tn.Chart=tn,tn.Legend=Li.legend._element,tn.Title=Li.title._element,tn.pluginService=tn.plugins,tn.PluginBase=tn.Element.extend({}),tn.canvasHelpers=tn.helpers.canvas,tn.layoutService=tn.layouts,tn.LinearScaleBase=Sn,tn.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],(function(t){tn[t]=function(e,n){return new tn(e,tn.helpers.merge(n||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}})),Bi}));
1
+ /*!
2
+ * Chart.js v2.9.4
3
+ * https://www.chartjs.org
4
+ * (c) 2020 Chart.js Contributors
5
+ * Released under the MIT License
6
+ */
7
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(function(){try{return require("moment")}catch(t){}}()):"function"==typeof define&&define.amd?define(["require"],(function(t){return e(function(){try{return t("moment")}catch(t){}}())})):(t=t||self).Chart=e(t.moment)}(this,(function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},n=function(t,e){return t(e={exports:{}},e.exports),e.exports}((function(t){var n={};for(var i in e)e.hasOwnProperty(i)&&(n[e[i]]=i);var a=t.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var r in a)if(a.hasOwnProperty(r)){if(!("channels"in a[r]))throw new Error("missing channels property: "+r);if(!("labels"in a[r]))throw new Error("missing channel labels property: "+r);if(a[r].labels.length!==a[r].channels)throw new Error("channel and label counts mismatch: "+r);var o=a[r].channels,s=a[r].labels;delete a[r].channels,delete a[r].labels,Object.defineProperty(a[r],"channels",{value:o}),Object.defineProperty(a[r],"labels",{value:s})}a.rgb.hsl=function(t){var e,n,i=t[0]/255,a=t[1]/255,r=t[2]/255,o=Math.min(i,a,r),s=Math.max(i,a,r),l=s-o;return s===o?e=0:i===s?e=(a-r)/l:a===s?e=2+(r-i)/l:r===s&&(e=4+(i-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),n=(o+s)/2,[e,100*(s===o?0:n<=.5?l/(s+o):l/(2-s-o)),100*n]},a.rgb.hsv=function(t){var e,n,i,a,r,o=t[0]/255,s=t[1]/255,l=t[2]/255,u=Math.max(o,s,l),d=u-Math.min(o,s,l),h=function(t){return(u-t)/6/d+.5};return 0===d?a=r=0:(r=d/u,e=h(o),n=h(s),i=h(l),o===u?a=i-n:s===u?a=1/3+e-i:l===u&&(a=2/3+n-e),a<0?a+=1:a>1&&(a-=1)),[360*a,100*r,100*u]},a.rgb.hwb=function(t){var e=t[0],n=t[1],i=t[2];return[a.rgb.hsl(t)[0],100*(1/255*Math.min(e,Math.min(n,i))),100*(i=1-1/255*Math.max(e,Math.max(n,i)))]},a.rgb.cmyk=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255;return[100*((1-n-(e=Math.min(1-n,1-i,1-a)))/(1-e)||0),100*((1-i-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]},a.rgb.keyword=function(t){var i=n[t];if(i)return i;var a,r,o,s=1/0;for(var l in e)if(e.hasOwnProperty(l)){var u=e[l],d=(r=t,o=u,Math.pow(r[0]-o[0],2)+Math.pow(r[1]-o[1],2)+Math.pow(r[2]-o[2],2));d<s&&(s=d,a=l)}return a},a.keyword.rgb=function(t){return e[t]},a.rgb.xyz=function(t){var e=t[0]/255,n=t[1]/255,i=t[2]/255;return[100*(.4124*(e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]},a.rgb.lab=function(t){var e=a.rgb.xyz(t),n=e[0],i=e[1],r=e[2];return i/=100,r/=108.883,n=(n/=95.047)>.008856?Math.pow(n,1/3):7.787*n+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(n-i),200*(i-(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116))]},a.hsl.rgb=function(t){var e,n,i,a,r,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0===s)return[r=255*l,r,r];e=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a},a.hsl.hsv=function(t){var e=t[0],n=t[1]/100,i=t[2]/100,a=n,r=Math.max(i,.01);return n*=(i*=2)<=1?i:2-i,a*=r<=1?r:2-r,[e,100*(0===i?2*a/(r+a):2*n/(i+n)),100*((i+n)/2)]},a.hsv.rgb=function(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),s=255*i*(1-n*r),l=255*i*(1-n*(1-r));switch(i*=255,a){case 0:return[i,l,o];case 1:return[s,i,o];case 2:return[o,i,l];case 3:return[o,s,i];case 4:return[l,o,i];case 5:return[i,o,s]}},a.hsv.hsl=function(t){var e,n,i,a=t[0],r=t[1]/100,o=t[2]/100,s=Math.max(o,.01);return i=(2-r)*o,n=r*s,[a,100*(n=(n/=(e=(2-r)*s)<=1?e:2-e)||0),100*(i/=2)]},a.hwb.rgb=function(t){var e,n,i,a,r,o,s,l=t[0]/360,u=t[1]/100,d=t[2]/100,h=u+d;switch(h>1&&(u/=h,d/=h),i=6*l-(e=Math.floor(6*l)),0!=(1&e)&&(i=1-i),a=u+i*((n=1-d)-u),e){default:case 6:case 0:r=n,o=a,s=u;break;case 1:r=a,o=n,s=u;break;case 2:r=u,o=n,s=a;break;case 3:r=u,o=a,s=n;break;case 4:r=a,o=u,s=n;break;case 5:r=n,o=u,s=a}return[255*r,255*o,255*s]},a.cmyk.rgb=function(t){var e=t[0]/100,n=t[1]/100,i=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a))]},a.xyz.rgb=function(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=(e=3.2406*a+-1.5372*r+-.4986*o)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:12.92*i,[255*(e=Math.min(Math.max(0,e),1)),255*(n=Math.min(Math.max(0,n),1)),255*(i=Math.min(Math.max(0,i),1))]},a.xyz.lab=function(t){var e=t[0],n=t[1],i=t[2];return n/=100,i/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(e-n),200*(n-(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116))]},a.lab.xyz=function(t){var e,n,i,a=t[0];e=t[1]/500+(n=(a+16)/116),i=n-t[2]/200;var r=Math.pow(n,3),o=Math.pow(e,3),s=Math.pow(i,3);return n=r>.008856?r:(n-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,i=s>.008856?s:(i-16/116)/7.787,[e*=95.047,n*=100,i*=108.883]},a.lab.lch=function(t){var e,n=t[0],i=t[1],a=t[2];return(e=360*Math.atan2(a,i)/2/Math.PI)<0&&(e+=360),[n,Math.sqrt(i*i+a*a),e]},a.lch.lab=function(t){var e,n=t[0],i=t[1];return e=t[2]/360*2*Math.PI,[n,i*Math.cos(e),i*Math.sin(e)]},a.rgb.ansi16=function(t){var e=t[0],n=t[1],i=t[2],r=1 in arguments?arguments[1]:a.rgb.hsv(t)[2];if(0===(r=Math.round(r/50)))return 30;var o=30+(Math.round(i/255)<<2|Math.round(n/255)<<1|Math.round(e/255));return 2===r&&(o+=60),o},a.hsv.ansi16=function(t){return a.rgb.ansi16(a.hsv.rgb(t),t[2])},a.rgb.ansi256=function(t){var e=t[0],n=t[1],i=t[2];return e===n&&n===i?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(n/255*5)+Math.round(i/255*5)},a.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var n=.5*(1+~~(t>50));return[(1&e)*n*255,(e>>1&1)*n*255,(e>>2&1)*n*255]},a.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var n;return t-=16,[Math.floor(t/36)/5*255,Math.floor((n=t%36)/6)/5*255,n%6/5*255]},a.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},a.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var n=e[0];3===e[0].length&&(n=n.split("").map((function(t){return t+t})).join(""));var i=parseInt(n,16);return[i>>16&255,i>>8&255,255&i]},a.rgb.hcg=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255,r=Math.max(Math.max(n,i),a),o=Math.min(Math.min(n,i),a),s=r-o;return e=s<=0?0:r===n?(i-a)/s%6:r===i?2+(a-n)/s:4+(n-i)/s+4,e/=6,[360*(e%=1),100*s,100*(s<1?o/(1-s):0)]},a.hsl.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=1,a=0;return(i=n<.5?2*e*n:2*e*(1-n))<1&&(a=(n-.5*i)/(1-i)),[t[0],100*i,100*a]},a.hsv.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=e*n,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.hcg.rgb=function(t){var e=t[0]/360,n=t[1]/100,i=t[2]/100;if(0===n)return[255*i,255*i,255*i];var a,r=[0,0,0],o=e%1*6,s=o%1,l=1-s;switch(Math.floor(o)){case 0:r[0]=1,r[1]=s,r[2]=0;break;case 1:r[0]=l,r[1]=1,r[2]=0;break;case 2:r[0]=0,r[1]=1,r[2]=s;break;case 3:r[0]=0,r[1]=l,r[2]=1;break;case 4:r[0]=s,r[1]=0,r[2]=1;break;default:r[0]=1,r[1]=0,r[2]=l}return a=(1-n)*i,[255*(n*r[0]+a),255*(n*r[1]+a),255*(n*r[2]+a)]},a.hcg.hsv=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e),i=0;return n>0&&(i=e/n),[t[0],100*i,100*n]},a.hcg.hsl=function(t){var e=t[1]/100,n=t[2]/100*(1-e)+.5*e,i=0;return n>0&&n<.5?i=e/(2*n):n>=.5&&n<1&&(i=e/(2*(1-n))),[t[0],100*i,100*n]},a.hcg.hwb=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e);return[t[0],100*(n-e),100*(1-n)]},a.hwb.hcg=function(t){var e=t[1]/100,n=1-t[2]/100,i=n-e,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},a.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},a.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},a.gray.hsl=a.gray.hsv=function(t){return[0,0,t[0]]},a.gray.hwb=function(t){return[0,100,t[0]]},a.gray.cmyk=function(t){return[0,0,0,t[0]]},a.gray.lab=function(t){return[t[0],0,0]},a.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),n=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(n.length)+n},a.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}));n.rgb,n.hsl,n.hsv,n.hwb,n.cmyk,n.xyz,n.lab,n.lch,n.hex,n.keyword,n.ansi16,n.ansi256,n.hcg,n.apple,n.gray;function i(t){var e=function(){for(var t={},e=Object.keys(n),i=e.length,a=0;a<i;a++)t[e[a]]={distance:-1,parent:null};return t}(),i=[t];for(e[t].distance=0;i.length;)for(var a=i.pop(),r=Object.keys(n[a]),o=r.length,s=0;s<o;s++){var l=r[s],u=e[l];-1===u.distance&&(u.distance=e[a].distance+1,u.parent=a,i.unshift(l))}return e}function a(t,e){return function(n){return e(t(n))}}function r(t,e){for(var i=[e[t].parent,t],r=n[e[t].parent][t],o=e[t].parent;e[o].parent;)i.unshift(e[o].parent),r=a(n[e[o].parent][o],r),o=e[o].parent;return r.conversion=i,r}var o={};Object.keys(n).forEach((function(t){o[t]={},Object.defineProperty(o[t],"channels",{value:n[t].channels}),Object.defineProperty(o[t],"labels",{value:n[t].labels});var e=function(t){for(var e=i(t),n={},a=Object.keys(e),o=a.length,s=0;s<o;s++){var l=a[s];null!==e[l].parent&&(n[l]=r(l,e))}return n}(t);Object.keys(e).forEach((function(n){var i=e[n];o[t][n]=function(t){var e=function(e){if(null==e)return e;arguments.length>1&&(e=Array.prototype.slice.call(arguments));var n=t(e);if("object"==typeof n)for(var i=n.length,a=0;a<i;a++)n[a]=Math.round(n[a]);return n};return"conversion"in t&&(e.conversion=t.conversion),e}(i),o[t][n].raw=function(t){var e=function(e){return null==e?e:(arguments.length>1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(i)}))}));var s=o,l={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},u={getRgba:d,getHsla:h,getRgb:function(t){var e=d(t);return e&&e.slice(0,3)},getHsl:function(t){var e=h(t);return e&&e.slice(0,3)},getHwb:c,getAlpha:function(t){var e=d(t);if(e)return e[3];if(e=h(t))return e[3];if(e=c(t))return e[3]},hexString:function(t,e){e=void 0!==e&&3===t.length?e:t[3];return"#"+v(t[0])+v(t[1])+v(t[2])+(e>=0&&e<1?v(Math.round(255*e)):"")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return f(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:f,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return g(t,e);var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+n+"%, "+i+"%, "+a+"%)"},percentaString:g,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return p(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:p,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return b[t.slice(0,3)]}};function d(t){if(t){var e=[0,0,0],n=1,i=t.match(/^#([a-fA-F0-9]{3,4})$/i),a="";if(i){a=(i=i[1])[3];for(var r=0;r<e.length;r++)e[r]=parseInt(i[r]+i[r],16);a&&(n=Math.round(parseInt(a+a,16)/255*100)/100)}else if(i=t.match(/^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i)){a=i[2],i=i[1];for(r=0;r<e.length;r++)e[r]=parseInt(i.slice(2*r,2*r+2),16);a&&(n=Math.round(parseInt(a,16)/255*100)/100)}else if(i=t.match(/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=parseInt(i[r+1]);n=parseFloat(i[4])}else if(i=t.match(/^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=Math.round(2.55*parseFloat(i[r+1]));n=parseFloat(i[4])}else if(i=t.match(/(\w+)/)){if("transparent"==i[1])return[0,0,0,0];if(!(e=l[i[1]]))return}for(r=0;r<e.length;r++)e[r]=m(e[r],0,255);return n=n||0==n?m(n,0,1):1,e[3]=n,e}}function h(t){if(t){var e=t.match(/^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[m(parseInt(e[1]),0,360),m(parseFloat(e[2]),0,100),m(parseFloat(e[3]),0,100),m(isNaN(n)?1:n,0,1)]}}}function c(t){if(t){var e=t.match(/^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[m(parseInt(e[1]),0,360),m(parseFloat(e[2]),0,100),m(parseFloat(e[3]),0,100),m(isNaN(n)?1:n,0,1)]}}}function f(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+e+")"}function g(t,e){return"rgba("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%, "+(e||t[3]||1)+")"}function p(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+e+")"}function m(t,e,n){return Math.min(Math.max(e,t),n)}function v(t){var e=t.toString(16).toUpperCase();return e.length<2?"0"+e:e}var b={};for(var x in l)b[l[x]]=x;var y=function(t){return t instanceof y?t:this instanceof y?(this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1},void("string"==typeof t?(e=u.getRgba(t))?this.setValues("rgb",e):(e=u.getHsla(t))?this.setValues("hsl",e):(e=u.getHwb(t))&&this.setValues("hwb",e):"object"==typeof t&&(void 0!==(e=t).r||void 0!==e.red?this.setValues("rgb",e):void 0!==e.l||void 0!==e.lightness?this.setValues("hsl",e):void 0!==e.v||void 0!==e.value?this.setValues("hsv",e):void 0!==e.w||void 0!==e.whiteness?this.setValues("hwb",e):void 0===e.c&&void 0===e.cyan||this.setValues("cmyk",e)))):new y(t);var e};y.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:function(){var t=this.values;return t.hsl.concat([t.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues("alpha",t),this)},red:function(t){return this.setChannel("rgb",0,t)},green:function(t){return this.setChannel("rgb",1,t)},blue:function(t){return this.setChannel("rgb",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel("hsl",0,t)},saturation:function(t){return this.setChannel("hsl",1,t)},lightness:function(t){return this.setChannel("hsl",2,t)},saturationv:function(t){return this.setChannel("hsv",1,t)},whiteness:function(t){return this.setChannel("hwb",1,t)},blackness:function(t){return this.setChannel("hwb",2,t)},value:function(t){return this.setChannel("hsv",2,t)},cyan:function(t){return this.setChannel("cmyk",0,t)},magenta:function(t){return this.setChannel("cmyk",1,t)},yellow:function(t){return this.setChannel("cmyk",2,t)},black:function(t){return this.setChannel("cmyk",3,t)},hexString:function(){return u.hexString(this.values.rgb)},rgbString:function(){return u.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return u.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return u.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return u.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return u.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return u.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return u.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){var t=this.values.rgb;return t[0]<<16|t[1]<<8|t[2]},luminosity:function(){for(var t=this.values.rgb,e=[],n=0;n<t.length;n++){var i=t[n]/255;e[n]=i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4)}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast:function(t){var e=this.luminosity(),n=t.luminosity();return e>n?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=t,i=void 0===e?.5:e,a=2*i-1,r=this.alpha()-n.alpha(),o=((a*r==-1?a:(a+r)/(1+a*r))+1)/2,s=1-o;return this.rgb(o*this.red()+s*n.red(),o*this.green()+s*n.green(),o*this.blue()+s*n.blue()).alpha(this.alpha()*i+n.alpha()*(1-i))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new y,i=this.values,a=n.values;for(var r in i)i.hasOwnProperty(r)&&(t=i[r],"[object Array]"===(e={}.toString.call(t))?a[r]=t.slice(0):"[object Number]"===e?a[r]=t:console.error("unexpected color value:",t));return n}},y.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},y.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},y.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i<t.length;i++)n[t.charAt(i)]=e[t][i];return 1!==e.alpha&&(n.a=e.alpha),n},y.prototype.setValues=function(t,e){var n,i,a=this.values,r=this.spaces,o=this.maxes,l=1;if(this.valid=!0,"alpha"===t)l=e;else if(e.length)a[t]=e.slice(0,t.length),l=e[t.length];else if(void 0!==e[t.charAt(0)]){for(n=0;n<t.length;n++)a[t][n]=e[t.charAt(n)];l=e.a}else if(void 0!==e[r[t][0]]){var u=r[t];for(n=0;n<t.length;n++)a[t][n]=e[u[n]];l=e.alpha}if(a.alpha=Math.max(0,Math.min(1,void 0===l?a.alpha:l)),"alpha"===t)return!1;for(n=0;n<t.length;n++)i=Math.max(0,Math.min(o[t][n],a[t][n])),a[t][n]=Math.round(i);for(var d in r)d!==t&&(a[d]=s[t][d](a[t]));return!0},y.prototype.setSpace=function(t,e){var n=e[0];return void 0===n?this.getValues(t):("number"==typeof n&&(n=Array.prototype.slice.call(e)),this.setValues(t,n),this)},y.prototype.setChannel=function(t,e,n){var i=this.values[t];return void 0===n?i[e]:n===i[e]?this:(i[e]=n,this.setValues(t,i),this)},"undefined"!=typeof window&&(window.Color=y);var _=y;function k(t){return-1===["__proto__","prototype","constructor"].indexOf(t)}var w,M={noop:function(){},uid:(w=0,function(){return w++}),isNullOrUndef:function(t){return null==t},isArray:function(t){if(Array.isArray&&Array.isArray(t))return!0;var e=Object.prototype.toString.call(t);return"[object"===e.substr(0,7)&&"Array]"===e.substr(-6)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},isFinite:function(t){return("number"==typeof t||t instanceof Number)&&isFinite(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,n){return M.valueOrDefault(M.isArray(t)?t[e]:t,n)},callback:function(t,e,n){if(t&&"function"==typeof t.call)return t.apply(n,e)},each:function(t,e,n,i){var a,r,o;if(M.isArray(t))if(r=t.length,i)for(a=r-1;a>=0;a--)e.call(n,t[a],a);else for(a=0;a<r;a++)e.call(n,t[a],a);else if(M.isObject(t))for(r=(o=Object.keys(t)).length,a=0;a<r;a++)e.call(n,t[o[a]],o[a])},arrayEquals:function(t,e){var n,i,a,r;if(!t||!e||t.length!==e.length)return!1;for(n=0,i=t.length;n<i;++n)if(a=t[n],r=e[n],a instanceof Array&&r instanceof Array){if(!M.arrayEquals(a,r))return!1}else if(a!==r)return!1;return!0},clone:function(t){if(M.isArray(t))return t.map(M.clone);if(M.isObject(t)){for(var e=Object.create(t),n=Object.keys(t),i=n.length,a=0;a<i;++a)e[n[a]]=M.clone(t[n[a]]);return e}return t},_merger:function(t,e,n,i){if(k(t)){var a=e[t],r=n[t];M.isObject(a)&&M.isObject(r)?M.merge(a,r,i):e[t]=M.clone(r)}},_mergerIf:function(t,e,n){if(k(t)){var i=e[t],a=n[t];M.isObject(i)&&M.isObject(a)?M.mergeIf(i,a):e.hasOwnProperty(t)||(e[t]=M.clone(a))}},merge:function(t,e,n){var i,a,r,o,s,l=M.isArray(e)?e:[e],u=l.length;if(!M.isObject(t))return t;for(i=(n=n||{}).merger||M._merger,a=0;a<u;++a)if(e=l[a],M.isObject(e))for(s=0,o=(r=Object.keys(e)).length;s<o;++s)i(r[s],t,e,n);return t},mergeIf:function(t,e){return M.merge(t,e,{merger:M._mergerIf})},extend:Object.assign||function(t){return M.merge(t,[].slice.call(arguments,1),{merger:function(t,e,n){e[t]=n[t]}})},inherits:function(t){var e=this,n=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return e.apply(this,arguments)},i=function(){this.constructor=n};return i.prototype=e.prototype,n.prototype=new i,n.extend=M.inherits,t&&M.extend(n.prototype,t),n.__super__=e.prototype,n},_deprecated:function(t,e,n,i){void 0!==e&&console.warn(t+': "'+n+'" is deprecated. Please use "'+i+'" instead')}},S=M;M.callCallback=M.callback,M.indexOf=function(t,e,n){return Array.prototype.indexOf.call(t,e,n)},M.getValueOrDefault=M.valueOrDefault,M.getValueAtIndexOrDefault=M.valueAtIndexOrDefault;var C={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return(t-=1)*t*t+1},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-((t-=1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return(t-=1)*t*t*t*t+1},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return 1-Math.cos(t*(Math.PI/2))},easeOutSine:function(t){return Math.sin(t*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},easeInExpo:function(t){return 0===t?0:Math.pow(2,10*(t-1))},easeOutExpo:function(t){return 1===t?1:1-Math.pow(2,-10*t)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*--t))},easeInCirc:function(t){return t>=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var 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:function(t){return 1-C.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*C.easeInBounce(2*t):.5*C.easeOutBounce(2*t-1)+.5}},P={effects:C};S.easingEffects=C;var A=Math.PI,D=A/180,T=2*A,I=A/2,F=A/4,O=2*A/3,L={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,a/2,i/2),s=e+o,l=n+o,u=e+i-o,d=n+a-o;t.moveTo(e,l),s<u&&l<d?(t.arc(s,l,o,-A,-I),t.arc(u,l,o,-I,0),t.arc(u,d,o,0,I),t.arc(s,d,o,I,A)):s<u?(t.moveTo(s,n),t.arc(u,l,o,-I,I),t.arc(s,l,o,I,A+I)):l<d?(t.arc(s,l,o,-A,0),t.arc(s,d,o,0,A)):t.arc(s,l,o,-A,A),t.closePath(),t.moveTo(e,n)}else t.rect(e,n,i,a)},drawPoint:function(t,e,n,i,a,r){var o,s,l,u,d,h=(r||0)*D;if(e&&"object"==typeof e&&("[object HTMLImageElement]"===(o=e.toString())||"[object HTMLCanvasElement]"===o))return t.save(),t.translate(i,a),t.rotate(h),t.drawImage(e,-e.width/2,-e.height/2,e.width,e.height),void t.restore();if(!(isNaN(n)||n<=0)){switch(t.beginPath(),e){default:t.arc(i,a,n,0,T),t.closePath();break;case"triangle":t.moveTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=O,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=O,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),t.closePath();break;case"rectRounded":u=n-(d=.516*n),s=Math.cos(h+F)*u,l=Math.sin(h+F)*u,t.arc(i-s,a-l,d,h-A,h-I),t.arc(i+l,a-s,d,h-I,h),t.arc(i+s,a+l,d,h,h+I),t.arc(i-l,a+s,d,h+I,h+A),t.closePath();break;case"rect":if(!r){u=Math.SQRT1_2*n,t.rect(i-u,a-u,2*u,2*u);break}h+=F;case"rectRot":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+l,a-s),t.lineTo(i+s,a+l),t.lineTo(i-l,a+s),t.closePath();break;case"crossRot":h+=F;case"cross":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"star":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s),h+=F,s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"line":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l);break;case"dash":t.moveTo(i,a),t.lineTo(i+Math.cos(h)*n,a+Math.sin(h)*n)}t.fill(),t.stroke()}},_isPointInArea:function(t,e){return t.x>e.left-1e-6&&t.x<e.right+1e-6&&t.y>e.top-1e-6&&t.y<e.bottom+1e-6},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,n,i){var a=n.steppedLine;if(a){if("middle"===a){var r=(e.x+n.x)/2;t.lineTo(r,i?n.y:e.y),t.lineTo(r,i?e.y:n.y)}else"after"===a&&!i||"after"!==a&&i?t.lineTo(e.x,n.y):t.lineTo(n.x,e.y);t.lineTo(n.x,n.y)}else n.tension?t.bezierCurveTo(i?e.controlPointPreviousX:e.controlPointNextX,i?e.controlPointPreviousY:e.controlPointNextY,i?n.controlPointNextX:n.controlPointPreviousX,i?n.controlPointNextY:n.controlPointPreviousY,n.x,n.y):t.lineTo(n.x,n.y)}},R=L;S.clear=L.clear,S.drawRoundedRectangle=function(t){t.beginPath(),L.roundedRect.apply(L,arguments)};var z={_set:function(t,e){return S.merge(this[t]||(this[t]={}),e)}};z._set("global",{defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",defaultLineHeight:1.2,showLines:!0});var N=z,B=S.valueOrDefault;var E={toLineHeight:function(t,e){var n=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!n||"normal"===n[1])return 1.2*e;switch(t=+n[2],n[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,n,i,a;return S.isObject(t)?(e=+t.top||0,n=+t.right||0,i=+t.bottom||0,a=+t.left||0):e=n=i=a=+t||0,{top:e,right:n,bottom:i,left:a,height:e+i,width:a+n}},_parseFont:function(t){var e=N.global,n=B(t.fontSize,e.defaultFontSize),i={family:B(t.fontFamily,e.defaultFontFamily),lineHeight:S.options.toLineHeight(B(t.lineHeight,e.defaultLineHeight),n),size:n,style:B(t.fontStyle,e.defaultFontStyle),weight:null,string:""};return i.string=function(t){return!t||S.isNullOrUndef(t.size)||S.isNullOrUndef(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}(i),i},resolve:function(t,e,n,i){var a,r,o,s=!0;for(a=0,r=t.length;a<r;++a)if(void 0!==(o=t[a])&&(void 0!==e&&"function"==typeof o&&(o=o(e),s=!1),void 0!==n&&S.isArray(o)&&(o=o[n],s=!1),void 0!==o))return i&&!s&&(i.cacheable=!1),o}},W={_factorize:function(t){var e,n=[],i=Math.sqrt(t);for(e=1;e<i;e++)t%e==0&&(n.push(e),n.push(t/e));return i===(0|i)&&n.push(i),n.sort((function(t,e){return t-e})).pop(),n},log10:Math.log10||function(t){var e=Math.log(t)*Math.LOG10E,n=Math.round(e);return t===Math.pow(10,n)?n:e}},V=W;S.log10=W.log10;var H=S,j=P,q=R,U=E,Y=V,G={getRtlAdapter:function(t,e,n){return t?function(t,e){return{x:function(n){return t+t+e-n},setWidth:function(t){e=t},textAlign:function(t){return"center"===t?t:"right"===t?"left":"right"},xPlus:function(t,e){return t-e},leftForLtr:function(t,e){return t-e}}}(e,n):{x:function(t){return t},setWidth:function(t){},textAlign:function(t){return t},xPlus:function(t,e){return t+e},leftForLtr:function(t,e){return t}}},overrideTextDirection:function(t,e){var n,i;"ltr"!==e&&"rtl"!==e||(i=[(n=t.canvas.style).getPropertyValue("direction"),n.getPropertyPriority("direction")],n.setProperty("direction",e,"important"),t.prevTextDirection=i)},restoreTextDirection:function(t){var e=t.prevTextDirection;void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}};H.easing=j,H.canvas=q,H.options=U,H.math=Y,H.rtl=G;var X=function(t){H.extend(this,t),this.initialize.apply(this,arguments)};H.extend(X.prototype,{_type:void 0,initialize:function(){this.hidden=!1},pivot:function(){var t=this;return t._view||(t._view=H.extend({},t._model)),t._start={},t},transition:function(t){var e=this,n=e._model,i=e._start,a=e._view;return n&&1!==t?(a||(a=e._view={}),i||(i=e._start={}),function(t,e,n,i){var a,r,o,s,l,u,d,h,c,f=Object.keys(n);for(a=0,r=f.length;a<r;++a)if(u=n[o=f[a]],e.hasOwnProperty(o)||(e[o]=u),(s=e[o])!==u&&"_"!==o[0]){if(t.hasOwnProperty(o)||(t[o]=s),(d=typeof u)===typeof(l=t[o]))if("string"===d){if((h=_(l)).valid&&(c=_(u)).valid){e[o]=c.mix(h,i).rgbString();continue}}else if(H.isFinite(l)&&H.isFinite(u)){e[o]=l+(u-l)*i;continue}e[o]=u}}(i,a,n,t),e):(e._view=H.extend({},n),e._start=null,e)},tooltipPosition:function(){return{x:this._model.x,y:this._model.y}},hasValue:function(){return H.isNumber(this._model.x)&&H.isNumber(this._model.y)}}),X.extend=H.inherits;var K=X,Z=K.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),$=Z;Object.defineProperty(Z.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(Z.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}}),N._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:H.noop,onComplete:H.noop}});var J={animations:[],request:null,addAnimation:function(t,e,n,i){var a,r,o=this.animations;for(e.chart=t,e.startTime=Date.now(),e.duration=n,i||(t.animating=!0),a=0,r=o.length;a<r;++a)if(o[a].chart===t)return void(o[a]=e);o.push(e),1===o.length&&this.requestAnimationFrame()},cancelAnimation:function(t){var e=H.findIndex(this.animations,(function(e){return e.chart===t}));-1!==e&&(this.animations.splice(e,1),t.animating=!1)},requestAnimationFrame:function(){var t=this;null===t.request&&(t.request=H.requestAnimFrame.call(window,(function(){t.request=null,t.startDigest()})))},startDigest:function(){this.advance(),this.animations.length>0&&this.requestAnimationFrame()},advance:function(){for(var t,e,n,i,a=this.animations,r=0;r<a.length;)e=(t=a[r]).chart,n=t.numSteps,i=Math.floor((Date.now()-t.startTime)/t.duration*n)+1,t.currentStep=Math.min(i,n),H.callback(t.render,[e,t],e),H.callback(t.onAnimationProgress,[t],e),t.currentStep>=n?(H.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(r,1)):++r}},Q=H.options.resolve,tt=["push","pop","shift","splice","unshift"];function et(t,e){var n=t._chartjs;if(n){var i=n.listeners,a=i.indexOf(e);-1!==a&&i.splice(a,1),i.length>0||(tt.forEach((function(e){delete t[e]})),delete t._chartjs)}}var nt=function(t,e){this.initialize(t,e)};H.extend(nt.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth"],_dataElementOptions:["backgroundColor","borderColor","borderWidth","pointStyle"],initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements(),n._type=n.getMeta().type},updateIndex:function(t){this.index=t},linkScales:function(){var t=this.getMeta(),e=this.chart,n=e.scales,i=this.getDataset(),a=e.options.scales;null!==t.xAxisID&&t.xAxisID in n&&!i.xAxisID||(t.xAxisID=i.xAxisID||a.xAxes[0].id),null!==t.yAxisID&&t.yAxisID in n&&!i.yAxisID||(t.yAxisID=i.yAxisID||a.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this._update(!0)},destroy:function(){this._data&&et(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,n=this.getMeta(),i=this.getDataset().data||[],a=n.data;for(t=0,e=i.length;t<e;++t)a[t]=a[t]||this.createMetaData(t);n.dataset=n.dataset||this.createMetaDataset()},addElementAndReset:function(t){var e=this.createMetaData(t);this.getMeta().data.splice(t,0,e),this.updateElement(e,t,!0)},buildOrUpdateElements:function(){var t,e,n=this,i=n.getDataset(),a=i.data||(i.data=[]);n._data!==a&&(n._data&&et(n._data,n),a&&Object.isExtensible(a)&&(e=n,(t=a)._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),tt.forEach((function(e){var n="onData"+e.charAt(0).toUpperCase()+e.slice(1),i=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value:function(){var e=Array.prototype.slice.call(arguments),a=i.apply(this,e);return H.each(t._chartjs.listeners,(function(t){"function"==typeof t[n]&&t[n].apply(t,e)})),a}})})))),n._data=a),n.resyncElements()},_configure:function(){this._config=H.merge(Object.create(null),[this.chart.options.datasets[this._type],this.getDataset()],{merger:function(t,e,n){"_meta"!==t&&"data"!==t&&H._merger(t,e,n)}})},_update:function(t){this._configure(),this._cachedDataOpts=null,this.update(t)},update:H.noop,transition:function(t){for(var e=this.getMeta(),n=e.data||[],i=n.length,a=0;a<i;++a)n[a].transition(t);e.dataset&&e.dataset.transition(t)},draw:function(){var t=this.getMeta(),e=t.data||[],n=e.length,i=0;for(t.dataset&&t.dataset.draw();i<n;++i)e[i].draw()},getStyle:function(t){var e,n=this.getMeta(),i=n.dataset;return this._configure(),i&&void 0===t?e=this._resolveDatasetElementOptions(i||{}):(t=t||0,e=this._resolveDataElementOptions(n.data[t]||{},t)),!1!==e.fill&&null!==e.fill||(e.backgroundColor=e.borderColor),e},_resolveDatasetElementOptions:function(t,e){var n,i,a,r,o=this,s=o.chart,l=o._config,u=t.custom||{},d=s.options.elements[o.datasetElementType.prototype._type]||{},h=o._datasetElementOptions,c={},f={chart:s,dataset:o.getDataset(),datasetIndex:o.index,hover:e};for(n=0,i=h.length;n<i;++n)a=h[n],r=e?"hover"+a.charAt(0).toUpperCase()+a.slice(1):a,c[a]=Q([u[r],l[r],d[r]],f);return c},_resolveDataElementOptions:function(t,e){var n=this,i=t&&t.custom,a=n._cachedDataOpts;if(a&&!i)return a;var r,o,s,l,u=n.chart,d=n._config,h=u.options.elements[n.dataElementType.prototype._type]||{},c=n._dataElementOptions,f={},g={chart:u,dataIndex:e,dataset:n.getDataset(),datasetIndex:n.index},p={cacheable:!i};if(i=i||{},H.isArray(c))for(o=0,s=c.length;o<s;++o)f[l=c[o]]=Q([i[l],d[l],h[l]],g,e,p);else for(o=0,s=(r=Object.keys(c)).length;o<s;++o)f[l=r[o]]=Q([i[l],d[c[l]],d[l],h[l]],g,e,p);return p.cacheable&&(n._cachedDataOpts=Object.freeze(f)),f},removeHoverStyle:function(t){H.merge(t._model,t.$previousStyle||{}),delete t.$previousStyle},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t._index,i=t.custom||{},a=t._model,r=H.getHoverColor;t.$previousStyle={backgroundColor:a.backgroundColor,borderColor:a.borderColor,borderWidth:a.borderWidth},a.backgroundColor=Q([i.hoverBackgroundColor,e.hoverBackgroundColor,r(a.backgroundColor)],void 0,n),a.borderColor=Q([i.hoverBorderColor,e.hoverBorderColor,r(a.borderColor)],void 0,n),a.borderWidth=Q([i.hoverBorderWidth,e.hoverBorderWidth,a.borderWidth],void 0,n)},_removeDatasetHoverStyle:function(){var t=this.getMeta().dataset;t&&this.removeHoverStyle(t)},_setDatasetHoverStyle:function(){var t,e,n,i,a,r,o=this.getMeta().dataset,s={};if(o){for(r=o._model,a=this._resolveDatasetElementOptions(o,!0),t=0,e=(i=Object.keys(a)).length;t<e;++t)s[n=i[t]]=r[n],r[n]=a[n];o.$previousStyle=s}},resyncElements:function(){var t=this.getMeta(),e=this.getDataset().data,n=t.data.length,i=e.length;i<n?t.data.splice(i,n-i):i>n&&this.insertElements(n,i-n)},insertElements:function(t,e){for(var n=0;n<e;++n)this.addElementAndReset(t+n)},onDataPush:function(){var t=arguments.length;this.insertElements(this.getDataset().data.length-t,t)},onDataPop:function(){this.getMeta().data.pop()},onDataShift:function(){this.getMeta().data.shift()},onDataSplice:function(t,e){this.getMeta().data.splice(t,e),this.insertElements(t,arguments.length-2)},onDataUnshift:function(){this.insertElements(0,arguments.length)}}),nt.extend=H.inherits;var it=nt,at=2*Math.PI;function rt(t,e){var n=e.startAngle,i=e.endAngle,a=e.pixelMargin,r=a/e.outerRadius,o=e.x,s=e.y;t.beginPath(),t.arc(o,s,e.outerRadius,n-r,i+r),e.innerRadius>a?(r=a/e.innerRadius,t.arc(o,s,e.innerRadius-a,i+r,n-r,!0)):t.arc(o,s,a,i+Math.PI/2,n-Math.PI/2),t.closePath(),t.clip()}function ot(t,e,n){var i="inner"===e.borderAlign;i?(t.lineWidth=2*e.borderWidth,t.lineJoin="round"):(t.lineWidth=e.borderWidth,t.lineJoin="bevel"),n.fullCircles&&function(t,e,n,i){var a,r=n.endAngle;for(i&&(n.endAngle=n.startAngle+at,rt(t,n),n.endAngle=r,n.endAngle===n.startAngle&&n.fullCircles&&(n.endAngle+=at,n.fullCircles--)),t.beginPath(),t.arc(n.x,n.y,n.innerRadius,n.startAngle+at,n.startAngle,!0),a=0;a<n.fullCircles;++a)t.stroke();for(t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.startAngle+at),a=0;a<n.fullCircles;++a)t.stroke()}(t,e,n,i),i&&rt(t,n),t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.endAngle),t.arc(n.x,n.y,n.innerRadius,n.endAngle,n.startAngle,!0),t.closePath(),t.stroke()}N._set("global",{elements:{arc:{backgroundColor:N.global.defaultColor,borderColor:"#fff",borderWidth:2,borderAlign:"center"}}});var st=K.extend({_type:"arc",inLabelRange:function(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hoverRadius,2)},inRange:function(t,e){var n=this._view;if(n){for(var i=H.getAngleFromPoint(n,{x:t,y:e}),a=i.angle,r=i.distance,o=n.startAngle,s=n.endAngle;s<o;)s+=at;for(;a>s;)a-=at;for(;a<o;)a+=at;var l=a>=o&&a<=s,u=r>=n.innerRadius&&r<=n.outerRadius;return l&&u}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t,e=this._chart.ctx,n=this._view,i="inner"===n.borderAlign?.33:0,a={x:n.x,y:n.y,innerRadius:n.innerRadius,outerRadius:Math.max(n.outerRadius-i,0),pixelMargin:i,startAngle:n.startAngle,endAngle:n.endAngle,fullCircles:Math.floor(n.circumference/at)};if(e.save(),e.fillStyle=n.backgroundColor,e.strokeStyle=n.borderColor,a.fullCircles){for(a.endAngle=a.startAngle+at,e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),t=0;t<a.fullCircles;++t)e.fill();a.endAngle=a.startAngle+n.circumference%at}e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),e.fill(),n.borderWidth&&ot(e,n,a),e.restore()}}),lt=H.valueOrDefault,ut=N.global.defaultColor;N._set("global",{elements:{line:{tension:.4,backgroundColor:ut,borderWidth:3,borderColor:ut,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}});var dt=K.extend({_type:"line",draw:function(){var t,e,n,i=this,a=i._view,r=i._chart.ctx,o=a.spanGaps,s=i._children.slice(),l=N.global,u=l.elements.line,d=-1,h=i._loop;if(s.length){if(i._loop){for(t=0;t<s.length;++t)if(e=H.previousItem(s,t),!s[t]._view.skip&&e._view.skip){s=s.slice(t).concat(s.slice(0,t)),h=o;break}h&&s.push(s[0])}for(r.save(),r.lineCap=a.borderCapStyle||u.borderCapStyle,r.setLineDash&&r.setLineDash(a.borderDash||u.borderDash),r.lineDashOffset=lt(a.borderDashOffset,u.borderDashOffset),r.lineJoin=a.borderJoinStyle||u.borderJoinStyle,r.lineWidth=lt(a.borderWidth,u.borderWidth),r.strokeStyle=a.borderColor||l.defaultColor,r.beginPath(),(n=s[0]._view).skip||(r.moveTo(n.x,n.y),d=0),t=1;t<s.length;++t)n=s[t]._view,e=-1===d?H.previousItem(s,t):s[d],n.skip||(d!==t-1&&!o||-1===d?r.moveTo(n.x,n.y):H.canvas.lineTo(r,e._view,n),d=t);h&&r.closePath(),r.stroke(),r.restore()}}}),ht=H.valueOrDefault,ct=N.global.defaultColor;function ft(t){var e=this._view;return!!e&&Math.abs(t-e.x)<e.radius+e.hitRadius}N._set("global",{elements:{point:{radius:3,pointStyle:"circle",backgroundColor:ct,borderColor:ct,borderWidth:1,hitRadius:1,hoverRadius:4,hoverBorderWidth:1}}});var gt=K.extend({_type:"point",inRange:function(t,e){var n=this._view;return!!n&&Math.pow(t-n.x,2)+Math.pow(e-n.y,2)<Math.pow(n.hitRadius+n.radius,2)},inLabelRange:ft,inXRange:ft,inYRange:function(t){var e=this._view;return!!e&&Math.abs(t-e.y)<e.radius+e.hitRadius},getCenterPoint:function(){var t=this._view;return{x:t.x,y:t.y}},getArea:function(){return Math.PI*Math.pow(this._view.radius,2)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y,padding:t.radius+t.borderWidth}},draw:function(t){var e=this._view,n=this._chart.ctx,i=e.pointStyle,a=e.rotation,r=e.radius,o=e.x,s=e.y,l=N.global,u=l.defaultColor;e.skip||(void 0===t||H.canvas._isPointInArea(e,t))&&(n.strokeStyle=e.borderColor||u,n.lineWidth=ht(e.borderWidth,l.elements.point.borderWidth),n.fillStyle=e.backgroundColor||u,H.canvas.drawPoint(n,i,r,o,s,a))}}),pt=N.global.defaultColor;function mt(t){return t&&void 0!==t.width}function vt(t){var e,n,i,a,r;return mt(t)?(r=t.width/2,e=t.x-r,n=t.x+r,i=Math.min(t.y,t.base),a=Math.max(t.y,t.base)):(r=t.height/2,e=Math.min(t.x,t.base),n=Math.max(t.x,t.base),i=t.y-r,a=t.y+r),{left:e,top:i,right:n,bottom:a}}function bt(t,e,n){return t===e?n:t===n?e:t}function xt(t,e,n){var i,a,r,o,s=t.borderWidth,l=function(t){var e=t.borderSkipped,n={};return e?(t.horizontal?t.base>t.x&&(e=bt(e,"left","right")):t.base<t.y&&(e=bt(e,"bottom","top")),n[e]=!0,n):n}(t);return H.isObject(s)?(i=+s.top||0,a=+s.right||0,r=+s.bottom||0,o=+s.left||0):i=a=r=o=+s||0,{t:l.top||i<0?0:i>n?n:i,r:l.right||a<0?0:a>e?e:a,b:l.bottom||r<0?0:r>n?n:r,l:l.left||o<0?0:o>e?e:o}}function yt(t,e,n){var i=null===e,a=null===n,r=!(!t||i&&a)&&vt(t);return r&&(i||e>=r.left&&e<=r.right)&&(a||n>=r.top&&n<=r.bottom)}N._set("global",{elements:{rectangle:{backgroundColor:pt,borderColor:pt,borderSkipped:"bottom",borderWidth:0}}});var _t=K.extend({_type:"rectangle",draw:function(){var t=this._chart.ctx,e=this._view,n=function(t){var e=vt(t),n=e.right-e.left,i=e.bottom-e.top,a=xt(t,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+a.l,y:e.top+a.t,w:n-a.l-a.r,h:i-a.t-a.b}}}(e),i=n.outer,a=n.inner;t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h),i.w===a.w&&i.h===a.h||(t.save(),t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return yt(this._view,t,e)},inLabelRange:function(t,e){var n=this._view;return mt(n)?yt(n,t,null):yt(n,null,e)},inXRange:function(t){return yt(this._view,t,null)},inYRange:function(t){return yt(this._view,null,t)},getCenterPoint:function(){var t,e,n=this._view;return mt(n)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return mt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),kt={},wt=st,Mt=dt,St=gt,Ct=_t;kt.Arc=wt,kt.Line=Mt,kt.Point=St,kt.Rectangle=Ct;var Pt=H._deprecated,At=H.valueOrDefault;function Dt(t,e,n){var i,a,r=n.barThickness,o=e.stackCount,s=e.pixels[t],l=H.isNullOrUndef(r)?function(t,e){var n,i,a,r,o=t._length;for(a=1,r=e.length;a<r;++a)o=Math.min(o,Math.abs(e[a]-e[a-1]));for(a=0,r=t.getTicks().length;a<r;++a)i=t.getPixelForTick(a),o=a>0?Math.min(o,Math.abs(i-n)):o,n=i;return o}(e.scale,e.pixels):-1;return H.isNullOrUndef(r)?(i=l*n.categoryPercentage,a=n.barPercentage):(i=r*o,a=1),{chunk:i/o,ratio:a,start:s-i/2}}N._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),N._set("global",{datasets:{bar:{categoryPercentage:.8,barPercentage:.9}}});var Tt=it.extend({dataElementType:kt.Rectangle,_dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","barPercentage","barThickness","categoryPercentage","maxBarThickness","minBarLength"],initialize:function(){var t,e,n=this;it.prototype.initialize.apply(n,arguments),(t=n.getMeta()).stack=n.getDataset().stack,t.bar=!0,e=n._getIndexScale().options,Pt("bar chart",e.barPercentage,"scales.[x/y]Axes.barPercentage","dataset.barPercentage"),Pt("bar chart",e.barThickness,"scales.[x/y]Axes.barThickness","dataset.barThickness"),Pt("bar chart",e.categoryPercentage,"scales.[x/y]Axes.categoryPercentage","dataset.categoryPercentage"),Pt("bar chart",n._getValueScale().options.minBarLength,"scales.[x/y]Axes.minBarLength","dataset.minBarLength"),Pt("bar chart",e.maxBarThickness,"scales.[x/y]Axes.maxBarThickness","dataset.maxBarThickness")},update:function(t){var e,n,i=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,n=i.length;e<n;++e)this.updateElement(i[e],e,t)},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=i.getDataset(),o=i._resolveDataElementOptions(t,e);t._xScale=i.getScaleForId(a.xAxisID),t._yScale=i.getScaleForId(a.yAxisID),t._datasetIndex=i.index,t._index=e,t._model={backgroundColor:o.backgroundColor,borderColor:o.borderColor,borderSkipped:o.borderSkipped,borderWidth:o.borderWidth,datasetLabel:r.label,label:i.chart.data.labels[e]},H.isArray(r.data[e])&&(t._model.borderSkipped=null),i._updateElementGeometry(t,e,n,o),t.pivot()},_updateElementGeometry:function(t,e,n,i){var a=this,r=t._model,o=a._getValueScale(),s=o.getBasePixel(),l=o.isHorizontal(),u=a._ruler||a.getRuler(),d=a.calculateBarValuePixels(a.index,e,i),h=a.calculateBarIndexPixels(a.index,e,u,i);r.horizontal=l,r.base=n?s:d.base,r.x=l?n?s:d.head:h.center,r.y=l?h.center:n?s:d.head,r.height=l?h.size:void 0,r.width=l?void 0:h.size},_getStacks:function(t){var e,n,i=this._getIndexScale(),a=i._getMatchingVisibleMetas(this._type),r=i.options.stacked,o=a.length,s=[];for(e=0;e<o&&(n=a[e],(!1===r||-1===s.indexOf(n.stack)||void 0===r&&void 0===n.stack)&&s.push(n.stack),n.index!==t);++e);return s},getStackCount:function(){return this._getStacks().length},getStackIndex:function(t,e){var n=this._getStacks(t),i=void 0!==e?n.indexOf(e):-1;return-1===i?n.length-1:i},getRuler:function(){var t,e,n=this._getIndexScale(),i=[];for(t=0,e=this.getMeta().data.length;t<e;++t)i.push(n.getPixelForValue(null,t,this.index));return{pixels:i,start:n._startPixel,end:n._endPixel,stackCount:this.getStackCount(),scale:n}},calculateBarValuePixels:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._getValueScale(),c=h.isHorizontal(),f=d.data.datasets,g=h._getMatchingVisibleMetas(this._type),p=h._parseValue(f[t].data[e]),m=n.minBarLength,v=h.options.stacked,b=this.getMeta().stack,x=void 0===p.start?0:p.max>=0&&p.min>=0?p.min:p.max,y=void 0===p.start?p.end:p.max>=0&&p.min>=0?p.max-p.min:p.min-p.max,_=g.length;if(v||void 0===v&&void 0!==b)for(i=0;i<_&&(a=g[i]).index!==t;++i)a.stack===b&&(r=void 0===(u=h._parseValue(f[a.index].data[e])).start?u.end:u.min>=0&&u.max>=0?u.max:u.min,(p.min<0&&r<0||p.max>=0&&r>0)&&(x+=r));return o=h.getPixelForValue(x),l=(s=h.getPixelForValue(x+y))-o,void 0!==m&&Math.abs(l)<m&&(l=m,s=y>=0&&!c||y<0&&c?o-m:o+m),{size:l,base:o,head:s,center:s+l/2}},calculateBarIndexPixels:function(t,e,n,i){var a="flex"===i.barThickness?function(t,e,n){var i,a=e.pixels,r=a[t],o=t>0?a[t-1]:null,s=t<a.length-1?a[t+1]:null,l=n.categoryPercentage;return null===o&&(o=r-(null===s?e.end-e.start:s-r)),null===s&&(s=r+r-o),i=r-(r-Math.min(o,s))/2*l,{chunk:Math.abs(s-o)/2*l/e.stackCount,ratio:n.barPercentage,start:i}}(e,n,i):Dt(e,n,i),r=this.getStackIndex(t,this.getMeta().stack),o=a.start+a.chunk*r+a.chunk/2,s=Math.min(At(i.maxBarThickness,1/0),a.chunk*a.ratio);return{base:o-s/2,head:o+s/2,center:o,size:s}},draw:function(){var t=this.chart,e=this._getValueScale(),n=this.getMeta().data,i=this.getDataset(),a=n.length,r=0;for(H.canvas.clipArea(t.ctx,t.chartArea);r<a;++r){var o=e._parseValue(i.data[r]);isNaN(o.min)||isNaN(o.max)||n[r].draw()}H.canvas.unclipArea(t.ctx)},_resolveDataElementOptions:function(){var t=this,e=H.extend({},it.prototype._resolveDataElementOptions.apply(t,arguments)),n=t._getIndexScale().options,i=t._getValueScale().options;return e.barPercentage=At(n.barPercentage,e.barPercentage),e.barThickness=At(n.barThickness,e.barThickness),e.categoryPercentage=At(n.categoryPercentage,e.categoryPercentage),e.maxBarThickness=At(n.maxBarThickness,e.maxBarThickness),e.minBarLength=At(i.minBarLength,e.minBarLength),e}}),It=H.valueOrDefault,Ft=H.options.resolve;N._set("bubble",{hover:{mode:"single"},scales:{xAxes:[{type:"linear",position:"bottom",id:"x-axis-0"}],yAxes:[{type:"linear",position:"left",id:"y-axis-0"}]},tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.datasets[t.datasetIndex].label||"",i=e.datasets[t.datasetIndex].data[t.index];return n+": ("+t.xLabel+", "+t.yLabel+", "+i.r+")"}}}});var Ot=it.extend({dataElementType:kt.Point,_dataElementOptions:["backgroundColor","borderColor","borderWidth","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth","hoverRadius","hitRadius","pointStyle","rotation"],update:function(t){var e=this,n=e.getMeta().data;H.each(n,(function(n,i){e.updateElement(n,i,t)}))},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=t.custom||{},o=i.getScaleForId(a.xAxisID),s=i.getScaleForId(a.yAxisID),l=i._resolveDataElementOptions(t,e),u=i.getDataset().data[e],d=i.index,h=n?o.getPixelForDecimal(.5):o.getPixelForValue("object"==typeof u?u:NaN,e,d),c=n?s.getBasePixel():s.getPixelForValue(u,e,d);t._xScale=o,t._yScale=s,t._options=l,t._datasetIndex=d,t._index=e,t._model={backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,hitRadius:l.hitRadius,pointStyle:l.pointStyle,rotation:l.rotation,radius:n?0:l.radius,skip:r.skip||isNaN(h)||isNaN(c),x:h,y:c},t.pivot()},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=It(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=It(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=It(n.hoverBorderWidth,n.borderWidth),e.radius=n.radius+n.hoverRadius},_resolveDataElementOptions:function(t,e){var n=this,i=n.chart,a=n.getDataset(),r=t.custom||{},o=a.data[e]||{},s=it.prototype._resolveDataElementOptions.apply(n,arguments),l={chart:i,dataIndex:e,dataset:a,datasetIndex:n.index};return n._cachedDataOpts===s&&(s=H.extend({},s)),s.radius=Ft([r.radius,o.r,n._config.radius,i.options.elements.point.radius],l,e),s}}),Lt=H.valueOrDefault,Rt=Math.PI,zt=2*Rt,Nt=Rt/2;N._set("doughnut",{animation:{animateRotate:!0,animateScale:!1},hover:{mode:"single"},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r]&&(a.data[r].hidden=!a.data[r].hidden);o.update()}},cutoutPercentage:50,rotation:-Nt,circumference:zt,tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.labels[t.index],i=": "+e.datasets[t.datasetIndex].data[t.index];return H.isArray(n)?(n=n.slice())[0]+=i:n+=i,n}}}});var Bt=it.extend({dataElementType:kt.Arc,linkScales:H.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],getRingIndex:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&++e;return e},update:function(t){var e,n,i,a,r=this,o=r.chart,s=o.chartArea,l=o.options,u=1,d=1,h=0,c=0,f=r.getMeta(),g=f.data,p=l.cutoutPercentage/100||0,m=l.circumference,v=r._getRingWeight(r.index);if(m<zt){var b=l.rotation%zt,x=(b+=b>=Rt?-zt:b<-Rt?zt:0)+m,y=Math.cos(b),_=Math.sin(b),k=Math.cos(x),w=Math.sin(x),M=b<=0&&x>=0||x>=zt,S=b<=Nt&&x>=Nt||x>=zt+Nt,C=b<=-Nt&&x>=-Nt||x>=Rt+Nt,P=b===-Rt||x>=Rt?-1:Math.min(y,y*p,k,k*p),A=C?-1:Math.min(_,_*p,w,w*p),D=M?1:Math.max(y,y*p,k,k*p),T=S?1:Math.max(_,_*p,w,w*p);u=(D-P)/2,d=(T-A)/2,h=-(D+P)/2,c=-(T+A)/2}for(i=0,a=g.length;i<a;++i)g[i]._options=r._resolveDataElementOptions(g[i],i);for(o.borderWidth=r.getMaxBorderWidth(),e=(s.right-s.left-o.borderWidth)/u,n=(s.bottom-s.top-o.borderWidth)/d,o.outerRadius=Math.max(Math.min(e,n)/2,0),o.innerRadius=Math.max(o.outerRadius*p,0),o.radiusLength=(o.outerRadius-o.innerRadius)/(r._getVisibleDatasetWeightTotal()||1),o.offsetX=h*o.outerRadius,o.offsetY=c*o.outerRadius,f.total=r.calculateTotal(),r.outerRadius=o.outerRadius-o.radiusLength*r._getRingWeightOffset(r.index),r.innerRadius=Math.max(r.outerRadius-o.radiusLength*v,0),i=0,a=g.length;i<a;++i)r.updateElement(g[i],i,t)},updateElement:function(t,e,n){var i=this,a=i.chart,r=a.chartArea,o=a.options,s=o.animation,l=(r.left+r.right)/2,u=(r.top+r.bottom)/2,d=o.rotation,h=o.rotation,c=i.getDataset(),f=n&&s.animateRotate?0:t.hidden?0:i.calculateCircumference(c.data[e])*(o.circumference/zt),g=n&&s.animateScale?0:i.innerRadius,p=n&&s.animateScale?0:i.outerRadius,m=t._options||{};H.extend(t,{_datasetIndex:i.index,_index:e,_model:{backgroundColor:m.backgroundColor,borderColor:m.borderColor,borderWidth:m.borderWidth,borderAlign:m.borderAlign,x:l+a.offsetX,y:u+a.offsetY,startAngle:d,endAngle:h,circumference:f,outerRadius:p,innerRadius:g,label:H.valueAtIndexOrDefault(c.label,e,a.data.labels[e])}});var v=t._model;n&&s.animateRotate||(v.startAngle=0===e?o.rotation:i.getMeta().data[e-1]._model.endAngle,v.endAngle=v.startAngle+v.circumference),t.pivot()},calculateTotal:function(){var t,e=this.getDataset(),n=this.getMeta(),i=0;return H.each(n.data,(function(n,a){t=e.data[a],isNaN(t)||n.hidden||(i+=Math.abs(t))})),i},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?zt*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,n,i,a,r,o,s,l,u=0,d=this.chart;if(!t)for(e=0,n=d.data.datasets.length;e<n;++e)if(d.isDatasetVisible(e)){t=(i=d.getDatasetMeta(e)).data,e!==this.index&&(r=i.controller);break}if(!t)return 0;for(e=0,n=t.length;e<n;++e)a=t[e],r?(r._configure(),o=r._resolveDataElementOptions(a,e)):o=a._options,"inner"!==o.borderAlign&&(s=o.borderWidth,u=(l=o.hoverBorderWidth)>(u=s>u?s:u)?l:u);return u},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Lt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Lt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Lt(n.hoverBorderWidth,n.borderWidth)},_getRingWeightOffset:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&(e+=this._getRingWeight(n));return e},_getRingWeight:function(t){return Math.max(Lt(this.chart.data.datasets[t].weight,1),0)},_getVisibleDatasetWeightTotal:function(){return this._getRingWeightOffset(this.chart.data.datasets.length)}});N._set("horizontalBar",{hover:{mode:"index",axis:"y"},scales:{xAxes:[{type:"linear",position:"bottom"}],yAxes:[{type:"category",position:"left",offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:"left"}},tooltips:{mode:"index",axis:"y"}}),N._set("global",{datasets:{horizontalBar:{categoryPercentage:.8,barPercentage:.9}}});var Et=Tt.extend({_getValueScaleId:function(){return this.getMeta().xAxisID},_getIndexScaleId:function(){return this.getMeta().yAxisID}}),Wt=H.valueOrDefault,Vt=H.options.resolve,Ht=H.canvas._isPointInArea;function jt(t,e){var n=t&&t.options.ticks||{},i=n.reverse,a=void 0===n.min?e:0,r=void 0===n.max?e:0;return{start:i?r:a,end:i?a:r}}function qt(t,e,n){var i=n/2,a=jt(t,i),r=jt(e,i);return{top:r.end,right:a.end,bottom:r.start,left:a.start}}function Ut(t){var e,n,i,a;return H.isObject(t)?(e=t.top,n=t.right,i=t.bottom,a=t.left):e=n=i=a=t,{top:e,right:n,bottom:i,left:a}}N._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}});var Yt=it.extend({datasetElementType:kt.Line,dataElementType:kt.Point,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth","cubicInterpolationMode","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.options,l=i._config,u=i._showLine=Wt(l.showLine,s.showLines);for(i._xScale=i.getScaleForId(a.xAxisID),i._yScale=i.getScaleForId(a.yAxisID),u&&(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=i._yScale,r._datasetIndex=i.index,r._children=o,r._model=i._resolveDatasetElementOptions(r),r.pivot()),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(u&&0!==r._model.tension&&i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i,a,r=this,o=r.getMeta(),s=t.custom||{},l=r.getDataset(),u=r.index,d=l.data[e],h=r._xScale,c=r._yScale,f=o.dataset._model,g=r._resolveDataElementOptions(t,e);i=h.getPixelForValue("object"==typeof d?d:NaN,e,u),a=n?c.getBasePixel():r.calculatePointY(d,e,u),t._xScale=h,t._yScale=c,t._options=g,t._datasetIndex=u,t._index=e,t._model={x:i,y:a,skip:s.skip||isNaN(i)||isNaN(a),radius:g.radius,pointStyle:g.pointStyle,rotation:g.rotation,backgroundColor:g.backgroundColor,borderColor:g.borderColor,borderWidth:g.borderWidth,tension:Wt(s.tension,f?f.tension:0),steppedLine:!!f&&f.steppedLine,hitRadius:g.hitRadius}},_resolveDatasetElementOptions:function(t){var e=this,n=e._config,i=t.custom||{},a=e.chart.options,r=a.elements.line,o=it.prototype._resolveDatasetElementOptions.apply(e,arguments);return o.spanGaps=Wt(n.spanGaps,a.spanGaps),o.tension=Wt(n.lineTension,r.tension),o.steppedLine=Vt([i.steppedLine,n.steppedLine,r.stepped]),o.clip=Ut(Wt(n.clip,qt(e._xScale,e._yScale,o.borderWidth))),o},calculatePointY:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._yScale,c=0,f=0;if(h.options.stacked){for(s=+h.getRightValue(t),u=(l=d._getSortedVisibleDatasetMetas()).length,i=0;i<u&&(r=l[i]).index!==n;++i)a=d.data.datasets[r.index],"line"===r.type&&r.yAxisID===h.id&&((o=+h.getRightValue(a.data[e]))<0?f+=o||0:c+=o||0);return s<0?h.getPixelForValue(f+s):h.getPixelForValue(c+s)}return h.getPixelForValue(t)},updateBezierControlPoints:function(){var t,e,n,i,a=this.chart,r=this.getMeta(),o=r.dataset._model,s=a.chartArea,l=r.data||[];function u(t,e,n){return Math.max(Math.min(t,n),e)}if(o.spanGaps&&(l=l.filter((function(t){return!t._model.skip}))),"monotone"===o.cubicInterpolationMode)H.splineCurveMonotone(l);else for(t=0,e=l.length;t<e;++t)n=l[t]._model,i=H.splineCurve(H.previousItem(l,t)._model,n,H.nextItem(l,t)._model,o.tension),n.controlPointPreviousX=i.previous.x,n.controlPointPreviousY=i.previous.y,n.controlPointNextX=i.next.x,n.controlPointNextY=i.next.y;if(a.options.elements.line.capBezierPoints)for(t=0,e=l.length;t<e;++t)n=l[t]._model,Ht(n,s)&&(t>0&&Ht(l[t-1]._model,s)&&(n.controlPointPreviousX=u(n.controlPointPreviousX,s.left,s.right),n.controlPointPreviousY=u(n.controlPointPreviousY,s.top,s.bottom)),t<l.length-1&&Ht(l[t+1]._model,s)&&(n.controlPointNextX=u(n.controlPointNextX,s.left,s.right),n.controlPointNextY=u(n.controlPointNextY,s.top,s.bottom)))},draw:function(){var t,e=this.chart,n=this.getMeta(),i=n.data||[],a=e.chartArea,r=e.canvas,o=0,s=i.length;for(this._showLine&&(t=n.dataset._model.clip,H.canvas.clipArea(e.ctx,{left:!1===t.left?0:a.left-t.left,right:!1===t.right?r.width:a.right+t.right,top:!1===t.top?0:a.top-t.top,bottom:!1===t.bottom?r.height:a.bottom+t.bottom}),n.dataset.draw(),H.canvas.unclipArea(e.ctx));o<s;++o)i[o].draw(a)},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Wt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Wt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Wt(n.hoverBorderWidth,n.borderWidth),e.radius=Wt(n.hoverRadius,n.radius)}}),Gt=H.options.resolve;N._set("polarArea",{scale:{type:"radialLinear",angleLines:{display:!1},gridLines:{circular:!0},pointLabels:{display:!1},ticks:{beginAtZero:!0}},animation:{animateRotate:!0,animateScale:!0},startAngle:-.5*Math.PI,legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r].hidden=!a.data[r].hidden;o.update()}},tooltips:{callbacks:{title:function(){return""},label:function(t,e){return e.labels[t.index]+": "+t.yLabel}}}});var Xt=it.extend({dataElementType:kt.Arc,linkScales:H.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i,a=this,r=a.getDataset(),o=a.getMeta(),s=a.chart.options.startAngle||0,l=a._starts=[],u=a._angles=[],d=o.data;for(a._updateRadius(),o.count=a.countVisibleElements(),e=0,n=r.data.length;e<n;e++)l[e]=s,i=a._computeAngle(e),u[e]=i,s+=i;for(e=0,n=d.length;e<n;++e)d[e]._options=a._resolveDataElementOptions(d[e],e),a.updateElement(d[e],e,t)},_updateRadius:function(){var t=this,e=t.chart,n=e.chartArea,i=e.options,a=Math.min(n.right-n.left,n.bottom-n.top);e.outerRadius=Math.max(a/2,0),e.innerRadius=Math.max(i.cutoutPercentage?e.outerRadius/100*i.cutoutPercentage:1,0),e.radiusLength=(e.outerRadius-e.innerRadius)/e.getVisibleDatasetCount(),t.outerRadius=e.outerRadius-e.radiusLength*t.index,t.innerRadius=t.outerRadius-e.radiusLength},updateElement:function(t,e,n){var i=this,a=i.chart,r=i.getDataset(),o=a.options,s=o.animation,l=a.scale,u=a.data.labels,d=l.xCenter,h=l.yCenter,c=o.startAngle,f=t.hidden?0:l.getDistanceFromCenterForValue(r.data[e]),g=i._starts[e],p=g+(t.hidden?0:i._angles[e]),m=s.animateScale?0:l.getDistanceFromCenterForValue(r.data[e]),v=t._options||{};H.extend(t,{_datasetIndex:i.index,_index:e,_scale:l,_model:{backgroundColor:v.backgroundColor,borderColor:v.borderColor,borderWidth:v.borderWidth,borderAlign:v.borderAlign,x:d,y:h,innerRadius:0,outerRadius:n?m:f,startAngle:n&&s.animateRotate?c:g,endAngle:n&&s.animateRotate?c:p,label:H.valueAtIndexOrDefault(u,e,u[e])}}),t.pivot()},countVisibleElements:function(){var t=this.getDataset(),e=this.getMeta(),n=0;return H.each(e.data,(function(e,i){isNaN(t.data[i])||e.hidden||n++})),n},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor,a=H.valueOrDefault;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=a(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=a(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=a(n.hoverBorderWidth,n.borderWidth)},_computeAngle:function(t){var e=this,n=this.getMeta().count,i=e.getDataset(),a=e.getMeta();if(isNaN(i.data[t])||a.data[t].hidden)return 0;var r={chart:e.chart,dataIndex:t,dataset:i,datasetIndex:e.index};return Gt([e.chart.options.elements.arc.angle,2*Math.PI/n],r,t)}});N._set("pie",H.clone(N.doughnut)),N._set("pie",{cutoutPercentage:0});var Kt=Bt,Zt=H.valueOrDefault;N._set("radar",{spanGaps:!1,scale:{type:"radialLinear"},elements:{line:{fill:"start",tension:0}}});var $t=it.extend({datasetElementType:kt.Line,dataElementType:kt.Point,linkScales:H.noop,_datasetElementOptions:["backgroundColor","borderWidth","borderColor","borderCapStyle","borderDash","borderDashOffset","borderJoinStyle","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.scale,l=i._config;for(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=s,r._datasetIndex=i.index,r._children=o,r._loop=!0,r._model=i._resolveDatasetElementOptions(r),r.pivot(),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i=this,a=t.custom||{},r=i.getDataset(),o=i.chart.scale,s=o.getPointPositionForValue(e,r.data[e]),l=i._resolveDataElementOptions(t,e),u=i.getMeta().dataset._model,d=n?o.xCenter:s.x,h=n?o.yCenter:s.y;t._scale=o,t._options=l,t._datasetIndex=i.index,t._index=e,t._model={x:d,y:h,skip:a.skip||isNaN(d)||isNaN(h),radius:l.radius,pointStyle:l.pointStyle,rotation:l.rotation,backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,tension:Zt(a.tension,u?u.tension:0),hitRadius:l.hitRadius}},_resolveDatasetElementOptions:function(){var t=this,e=t._config,n=t.chart.options,i=it.prototype._resolveDatasetElementOptions.apply(t,arguments);return i.spanGaps=Zt(e.spanGaps,n.spanGaps),i.tension=Zt(e.lineTension,n.elements.line.tension),i},updateBezierControlPoints:function(){var t,e,n,i,a=this.getMeta(),r=this.chart.chartArea,o=a.data||[];function s(t,e,n){return Math.max(Math.min(t,n),e)}for(a.dataset._model.spanGaps&&(o=o.filter((function(t){return!t._model.skip}))),t=0,e=o.length;t<e;++t)n=o[t]._model,i=H.splineCurve(H.previousItem(o,t,!0)._model,n,H.nextItem(o,t,!0)._model,n.tension),n.controlPointPreviousX=s(i.previous.x,r.left,r.right),n.controlPointPreviousY=s(i.previous.y,r.top,r.bottom),n.controlPointNextX=s(i.next.x,r.left,r.right),n.controlPointNextY=s(i.next.y,r.top,r.bottom)},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Zt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Zt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Zt(n.hoverBorderWidth,n.borderWidth),e.radius=Zt(n.hoverRadius,n.radius)}});N._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}}),N._set("global",{datasets:{scatter:{showLine:!1}}});var Jt={bar:Tt,bubble:Ot,doughnut:Bt,horizontalBar:Et,line:Yt,polarArea:Xt,pie:Kt,radar:$t,scatter:Yt};function Qt(t,e){return t.native?{x:t.x,y:t.y}:H.getRelativePosition(t,e)}function te(t,e){var n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas();for(i=0,r=l.length;i<r;++i)for(a=0,o=(n=l[i].data).length;a<o;++a)(s=n[a])._view.skip||e(s)}function ee(t,e){var n=[];return te(t,(function(t){t.inRange(e.x,e.y)&&n.push(t)})),n}function ne(t,e,n,i){var a=Number.POSITIVE_INFINITY,r=[];return te(t,(function(t){if(!n||t.inRange(e.x,e.y)){var o=t.getCenterPoint(),s=i(e,o);s<a?(r=[t],a=s):s===a&&r.push(t)}})),r}function ie(t){var e=-1!==t.indexOf("x"),n=-1!==t.indexOf("y");return function(t,i){var a=e?Math.abs(t.x-i.x):0,r=n?Math.abs(t.y-i.y):0;return Math.sqrt(Math.pow(a,2)+Math.pow(r,2))}}function ae(t,e,n){var i=Qt(e,t);n.axis=n.axis||"x";var a=ie(n.axis),r=n.intersect?ee(t,i):ne(t,i,!1,a),o=[];return r.length?(t._getSortedVisibleDatasetMetas().forEach((function(t){var e=t.data[r[0]._index];e&&!e._view.skip&&o.push(e)})),o):[]}var re={modes:{single:function(t,e){var n=Qt(e,t),i=[];return te(t,(function(t){if(t.inRange(n.x,n.y))return i.push(t),i})),i.slice(0,1)},label:ae,index:ae,dataset:function(t,e,n){var i=Qt(e,t);n.axis=n.axis||"xy";var a=ie(n.axis),r=n.intersect?ee(t,i):ne(t,i,!1,a);return r.length>0&&(r=t.getDatasetMeta(r[0]._datasetIndex).data),r},"x-axis":function(t,e){return ae(t,e,{intersect:!1})},point:function(t,e){return ee(t,Qt(e,t))},nearest:function(t,e,n){var i=Qt(e,t);n.axis=n.axis||"xy";var a=ie(n.axis);return ne(t,i,n.intersect,a)},x:function(t,e,n){var i=Qt(e,t),a=[],r=!1;return te(t,(function(t){t.inXRange(i.x)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a},y:function(t,e,n){var i=Qt(e,t),a=[],r=!1;return te(t,(function(t){t.inYRange(i.y)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a}}},oe=H.extend;function se(t,e){return H.where(t,(function(t){return t.pos===e}))}function le(t,e){return t.sort((function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i.index-a.index:i.weight-a.weight}))}function ue(t,e,n,i){return Math.max(t[n],e[n])+Math.max(t[i],e[i])}function de(t,e,n){var i,a,r=n.box,o=t.maxPadding;if(n.size&&(t[n.pos]-=n.size),n.size=n.horizontal?r.height:r.width,t[n.pos]+=n.size,r.getPadding){var s=r.getPadding();o.top=Math.max(o.top,s.top),o.left=Math.max(o.left,s.left),o.bottom=Math.max(o.bottom,s.bottom),o.right=Math.max(o.right,s.right)}if(i=e.outerWidth-ue(o,t,"left","right"),a=e.outerHeight-ue(o,t,"top","bottom"),i!==t.w||a!==t.h){t.w=i,t.h=a;var l=n.horizontal?[i,t.w]:[a,t.h];return!(l[0]===l[1]||isNaN(l[0])&&isNaN(l[1]))}}function he(t,e){var n=e.maxPadding;function i(t){var i={left:0,top:0,right:0,bottom:0};return t.forEach((function(t){i[t]=Math.max(e[t],n[t])})),i}return i(t?["left","right"]:["top","bottom"])}function ce(t,e,n){var i,a,r,o,s,l,u=[];for(i=0,a=t.length;i<a;++i)(o=(r=t[i]).box).update(r.width||e.w,r.height||e.h,he(r.horizontal,e)),de(e,n,r)&&(l=!0,u.length&&(s=!0)),o.fullWidth||u.push(r);return s&&ce(u,e,n)||l}function fe(t,e,n){var i,a,r,o,s=n.padding,l=e.x,u=e.y;for(i=0,a=t.length;i<a;++i)o=(r=t[i]).box,r.horizontal?(o.left=o.fullWidth?s.left:e.left,o.right=o.fullWidth?n.outerWidth-s.right:e.left+e.w,o.top=u,o.bottom=u+o.height,o.width=o.right-o.left,u=o.bottom):(o.left=l,o.right=l+o.width,o.top=e.top,o.bottom=e.top+e.h,o.height=o.bottom-o.top,l=o.right);e.x=l,e.y=u}N._set("global",{layout:{padding:{top:0,right:0,bottom:0,left:0}}});var ge,pe={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw:function(){e.draw.apply(e,arguments)}}]},t.boxes.push(e)},removeBox:function(t,e){var n=t.boxes?t.boxes.indexOf(e):-1;-1!==n&&t.boxes.splice(n,1)},configure:function(t,e,n){for(var i,a=["fullWidth","position","weight"],r=a.length,o=0;o<r;++o)i=a[o],n.hasOwnProperty(i)&&(e[i]=n[i])},update:function(t,e,n){if(t){var i=t.options.layout||{},a=H.options.toPadding(i.padding),r=e-a.width,o=n-a.height,s=function(t){var e=function(t){var e,n,i,a=[];for(e=0,n=(t||[]).length;e<n;++e)i=t[e],a.push({index:e,box:i,pos:i.position,horizontal:i.isHorizontal(),weight:i.weight});return a}(t),n=le(se(e,"left"),!0),i=le(se(e,"right")),a=le(se(e,"top"),!0),r=le(se(e,"bottom"));return{leftAndTop:n.concat(a),rightAndBottom:i.concat(r),chartArea:se(e,"chartArea"),vertical:n.concat(i),horizontal:a.concat(r)}}(t.boxes),l=s.vertical,u=s.horizontal,d=Object.freeze({outerWidth:e,outerHeight:n,padding:a,availableWidth:r,vBoxMaxWidth:r/2/l.length,hBoxMaxHeight:o/2}),h=oe({maxPadding:oe({},a),w:r,h:o,x:a.left,y:a.top},a);!function(t,e){var n,i,a;for(n=0,i=t.length;n<i;++n)(a=t[n]).width=a.horizontal?a.box.fullWidth&&e.availableWidth:e.vBoxMaxWidth,a.height=a.horizontal&&e.hBoxMaxHeight}(l.concat(u),d),ce(l,h,d),ce(u,h,d)&&ce(l,h,d),function(t){var e=t.maxPadding;function n(n){var i=Math.max(e[n]-t[n],0);return t[n]+=i,i}t.y+=n("top"),t.x+=n("left"),n("right"),n("bottom")}(h),fe(s.leftAndTop,h,d),h.x+=h.w,h.y+=h.h,fe(s.rightAndBottom,h,d),t.chartArea={left:h.left,top:h.top,right:h.left+h.w,bottom:h.top+h.h},H.each(s.chartArea,(function(e){var n=e.box;oe(n,t.chartArea),n.update(h.w,h.h)}))}}},me=(ge=Object.freeze({__proto__:null,default:"@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}"}))&&ge.default||ge,ve="$chartjs",be="chartjs-size-monitor",xe="chartjs-render-monitor",ye="chartjs-render-animation",_e=["animationstart","webkitAnimationStart"],ke={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function we(t,e){var n=H.getStyle(t,e),i=n&&n.match(/^(\d+)(\.\d+)?px$/);return i?Number(i[1]):void 0}var Me=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function Se(t,e,n){t.addEventListener(e,n,Me)}function Ce(t,e,n){t.removeEventListener(e,n,Me)}function Pe(t,e,n,i,a){return{type:t,chart:e,native:a||null,x:void 0!==n?n:null,y:void 0!==i?i:null}}function Ae(t){var e=document.createElement("div");return e.className=t||"",e}function De(t,e,n){var i,a,r,o,s=t[ve]||(t[ve]={}),l=s.resizer=function(t){var e=Ae(be),n=Ae(be+"-expand"),i=Ae(be+"-shrink");n.appendChild(Ae()),i.appendChild(Ae()),e.appendChild(n),e.appendChild(i),e._reset=function(){n.scrollLeft=1e6,n.scrollTop=1e6,i.scrollLeft=1e6,i.scrollTop=1e6};var a=function(){e._reset(),t()};return Se(n,"scroll",a.bind(n,"expand")),Se(i,"scroll",a.bind(i,"shrink")),e}((i=function(){if(s.resizer){var i=n.options.maintainAspectRatio&&t.parentNode,a=i?i.clientWidth:0;e(Pe("resize",n)),i&&i.clientWidth<a&&n.canvas&&e(Pe("resize",n))}},r=!1,o=[],function(){o=Array.prototype.slice.call(arguments),a=a||this,r||(r=!0,H.requestAnimFrame.call(window,(function(){r=!1,i.apply(a,o)})))}));!function(t,e){var n=t[ve]||(t[ve]={}),i=n.renderProxy=function(t){t.animationName===ye&&e()};H.each(_e,(function(e){Se(t,e,i)})),n.reflow=!!t.offsetParent,t.classList.add(xe)}(t,(function(){if(s.resizer){var e=t.parentNode;e&&e!==l.parentNode&&e.insertBefore(l,e.firstChild),l._reset()}}))}function Te(t){var e=t[ve]||{},n=e.resizer;delete e.resizer,function(t){var e=t[ve]||{},n=e.renderProxy;n&&(H.each(_e,(function(e){Ce(t,e,n)})),delete e.renderProxy),t.classList.remove(xe)}(t),n&&n.parentNode&&n.parentNode.removeChild(n)}var Ie={disableCSSInjection:!1,_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,_ensureLoaded:function(t){if(!this.disableCSSInjection){var e=t.getRootNode?t.getRootNode():document;!function(t,e){var n=t[ve]||(t[ve]={});if(!n.containsStyles){n.containsStyles=!0,e="/* Chart.js */\n"+e;var i=document.createElement("style");i.setAttribute("type","text/css"),i.appendChild(document.createTextNode(e)),t.appendChild(i)}}(e.host?e:document.head,me)}},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var n=t&&t.getContext&&t.getContext("2d");return n&&n.canvas===t?(this._ensureLoaded(t),function(t,e){var n=t.style,i=t.getAttribute("height"),a=t.getAttribute("width");if(t[ve]={initial:{height:i,width:a,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||"block",null===a||""===a){var r=we(t,"width");void 0!==r&&(t.width=r)}if(null===i||""===i)if(""===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var o=we(t,"height");void 0!==r&&(t.height=o)}}(t,e),n):null},releaseContext:function(t){var e=t.canvas;if(e[ve]){var n=e[ve].initial;["height","width"].forEach((function(t){var i=n[t];H.isNullOrUndef(i)?e.removeAttribute(t):e.setAttribute(t,i)})),H.each(n.style||{},(function(t,n){e.style[n]=t})),e.width=e.width,delete e[ve]}},addEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=n[ve]||(n[ve]={});Se(i,e,(a.proxies||(a.proxies={}))[t.id+"_"+e]=function(e){n(function(t,e){var n=ke[t.type]||t.type,i=H.getRelativePosition(t,e);return Pe(n,e,i.x,i.y,t)}(e,t))})}else De(i,n,t)},removeEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=((n[ve]||{}).proxies||{})[t.id+"_"+e];a&&Ce(i,e,a)}else Te(i)}};H.addEvent=Se,H.removeEvent=Ce;var Fe=Ie._enabled?Ie:{acquireContext:function(t){return t&&t.canvas&&(t=t.canvas),t&&t.getContext("2d")||null}},Oe=H.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},Fe);N._set("global",{plugins:{}});var Le={_plugins:[],_cacheId:0,register:function(t){var e=this._plugins;[].concat(t).forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),this._cacheId++},unregister:function(t){var e=this._plugins;[].concat(t).forEach((function(t){var n=e.indexOf(t);-1!==n&&e.splice(n,1)})),this._cacheId++},clear:function(){this._plugins=[],this._cacheId++},count:function(){return this._plugins.length},getAll:function(){return this._plugins},notify:function(t,e,n){var i,a,r,o,s,l=this.descriptors(t),u=l.length;for(i=0;i<u;++i)if("function"==typeof(s=(r=(a=l[i]).plugin)[e])&&((o=[t].concat(n||[])).push(a.options),!1===s.apply(r,o)))return!1;return!0},descriptors:function(t){var e=t.$plugins||(t.$plugins={});if(e.id===this._cacheId)return e.descriptors;var n=[],i=[],a=t&&t.config||{},r=a.options&&a.options.plugins||{};return this._plugins.concat(a.plugins||[]).forEach((function(t){if(-1===n.indexOf(t)){var e=t.id,a=r[e];!1!==a&&(!0===a&&(a=H.clone(N.global.plugins[e])),n.push(t),i.push({plugin:t,options:a||{}}))}})),e.descriptors=i,e.id=this._cacheId,i},_invalidate:function(t){delete t.$plugins}},Re={constructors:{},defaults:{},registerScaleType:function(t,e,n){this.constructors[t]=e,this.defaults[t]=H.clone(n)},getScaleConstructor:function(t){return this.constructors.hasOwnProperty(t)?this.constructors[t]:void 0},getScaleDefaults:function(t){return this.defaults.hasOwnProperty(t)?H.merge(Object.create(null),[N.scale,this.defaults[t]]):{}},updateScaleDefaults:function(t,e){this.defaults.hasOwnProperty(t)&&(this.defaults[t]=H.extend(this.defaults[t],e))},addScalesToLayout:function(t){H.each(t.scales,(function(e){e.fullWidth=e.options.fullWidth,e.position=e.options.position,e.weight=e.options.weight,pe.addBox(t,e)}))}},ze=H.valueOrDefault,Ne=H.rtl.getRtlAdapter;N._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:H.noop,title:function(t,e){var n="",i=e.labels,a=i?i.length:0;if(t.length>0){var r=t[0];r.label?n=r.label:r.xLabel?n=r.xLabel:a>0&&r.index<a&&(n=i[r.index])}return n},afterTitle:H.noop,beforeBody:H.noop,beforeLabel:H.noop,label:function(t,e){var n=e.datasets[t.datasetIndex].label||"";return n&&(n+=": "),H.isNullOrUndef(t.value)?n+=t.yLabel:n+=t.value,n},labelColor:function(t,e){var n=e.getDatasetMeta(t.datasetIndex).data[t.index]._view;return{borderColor:n.borderColor,backgroundColor:n.backgroundColor}},labelTextColor:function(){return this._options.bodyFontColor},afterLabel:H.noop,afterBody:H.noop,beforeFooter:H.noop,footer:H.noop,afterFooter:H.noop}}});var Be={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,r=0;for(e=0,n=t.length;e<n;++e){var o=t[e];if(o&&o.hasValue()){var s=o.tooltipPosition();i+=s.x,a+=s.y,++r}}return{x:i/r,y:a/r}},nearest:function(t,e){var n,i,a,r=e.x,o=e.y,s=Number.POSITIVE_INFINITY;for(n=0,i=t.length;n<i;++n){var l=t[n];if(l&&l.hasValue()){var u=l.getCenterPoint(),d=H.distanceBetweenPoints(e,u);d<s&&(s=d,a=l)}}if(a){var h=a.tooltipPosition();r=h.x,o=h.y}return{x:r,y:o}}};function Ee(t,e){return e&&(H.isArray(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function We(t){return("string"==typeof t||t instanceof String)&&t.indexOf("\n")>-1?t.split("\n"):t}function Ve(t){var e=N.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,rtl:t.rtl,textDirection:t.textDirection,bodyFontColor:t.bodyFontColor,_bodyFontFamily:ze(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:ze(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:ze(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:ze(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:ze(t.titleFontStyle,e.defaultFontStyle),titleFontSize:ze(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:ze(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:ze(t.footerFontStyle,e.defaultFontStyle),footerFontSize:ze(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function He(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function je(t){return Ee([],We(t))}var qe=K.extend({initialize:function(){this._model=Ve(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options,n=e.callbacks,i=n.beforeTitle.apply(t,arguments),a=n.title.apply(t,arguments),r=n.afterTitle.apply(t,arguments),o=[];return o=Ee(o,We(i)),o=Ee(o,We(a)),o=Ee(o,We(r))},getBeforeBody:function(){return je(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var n=this,i=n._options.callbacks,a=[];return H.each(t,(function(t){var r={before:[],lines:[],after:[]};Ee(r.before,We(i.beforeLabel.call(n,t,e))),Ee(r.lines,i.label.call(n,t,e)),Ee(r.after,We(i.afterLabel.call(n,t,e))),a.push(r)})),a},getAfterBody:function(){return je(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this,e=t._options.callbacks,n=e.beforeFooter.apply(t,arguments),i=e.footer.apply(t,arguments),a=e.afterFooter.apply(t,arguments),r=[];return r=Ee(r,We(n)),r=Ee(r,We(i)),r=Ee(r,We(a))},update:function(t){var e,n,i,a,r,o,s,l,u,d,h=this,c=h._options,f=h._model,g=h._model=Ve(c),p=h._active,m=h._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},x={width:f.width,height:f.height},y={x:f.caretX,y:f.caretY};if(p.length){g.opacity=1;var _=[],k=[];y=Be[c.position].call(h,p,h._eventPosition);var w=[];for(e=0,n=p.length;e<n;++e)w.push((i=p[e],a=void 0,r=void 0,o=void 0,s=void 0,l=void 0,u=void 0,d=void 0,a=i._xScale,r=i._yScale||i._scale,o=i._index,s=i._datasetIndex,l=i._chart.getDatasetMeta(s).controller,u=l._getIndexScale(),d=l._getValueScale(),{xLabel:a?a.getLabelForIndex(o,s):"",yLabel:r?r.getLabelForIndex(o,s):"",label:u?""+u.getLabelForIndex(o,s):"",value:d?""+d.getLabelForIndex(o,s):"",index:o,datasetIndex:s,x:i._model.x,y:i._model.y}));c.filter&&(w=w.filter((function(t){return c.filter(t,m)}))),c.itemSort&&(w=w.sort((function(t,e){return c.itemSort(t,e,m)}))),H.each(w,(function(t){_.push(c.callbacks.labelColor.call(h,t,h._chart)),k.push(c.callbacks.labelTextColor.call(h,t,h._chart))})),g.title=h.getTitle(w,m),g.beforeBody=h.getBeforeBody(w,m),g.body=h.getBody(w,m),g.afterBody=h.getAfterBody(w,m),g.footer=h.getFooter(w,m),g.x=y.x,g.y=y.y,g.caretPadding=c.caretPadding,g.labelColors=_,g.labelTextColors=k,g.dataPoints=w,x=function(t,e){var n=t._chart.ctx,i=2*e.yPadding,a=0,r=e.body,o=r.reduce((function(t,e){return t+e.before.length+e.lines.length+e.after.length}),0);o+=e.beforeBody.length+e.afterBody.length;var s=e.title.length,l=e.footer.length,u=e.titleFontSize,d=e.bodyFontSize,h=e.footerFontSize;i+=s*u,i+=s?(s-1)*e.titleSpacing:0,i+=s?e.titleMarginBottom:0,i+=o*d,i+=o?(o-1)*e.bodySpacing:0,i+=l?e.footerMarginTop:0,i+=l*h,i+=l?(l-1)*e.footerSpacing:0;var c=0,f=function(t){a=Math.max(a,n.measureText(t).width+c)};return n.font=H.fontString(u,e._titleFontStyle,e._titleFontFamily),H.each(e.title,f),n.font=H.fontString(d,e._bodyFontStyle,e._bodyFontFamily),H.each(e.beforeBody.concat(e.afterBody),f),c=e.displayColors?d+2:0,H.each(r,(function(t){H.each(t.before,f),H.each(t.lines,f),H.each(t.after,f)})),c=0,n.font=H.fontString(h,e._footerFontStyle,e._footerFontFamily),H.each(e.footer,f),{width:a+=2*e.xPadding,height:i}}(this,g),b=function(t,e,n,i){var a=t.x,r=t.y,o=t.caretSize,s=t.caretPadding,l=t.cornerRadius,u=n.xAlign,d=n.yAlign,h=o+s,c=l+s;return"right"===u?a-=e.width:"center"===u&&((a-=e.width/2)+e.width>i.width&&(a=i.width-e.width),a<0&&(a=0)),"top"===d?r+=h:r-="bottom"===d?e.height+h:e.height/2,"center"===d?"left"===u?a+=h:"right"===u&&(a-=h):"left"===u?a-=c:"right"===u&&(a+=c),{x:a,y:r}}(g,x,v=function(t,e){var n,i,a,r,o,s=t._model,l=t._chart,u=t._chart.chartArea,d="center",h="center";s.y<e.height?h="top":s.y>l.height-e.height&&(h="bottom");var c=(u.left+u.right)/2,f=(u.top+u.bottom)/2;"center"===h?(n=function(t){return t<=c},i=function(t){return t>c}):(n=function(t){return t<=e.width/2},i=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},r=function(t){return t-e.width-s.caretSize-s.caretPadding<0},o=function(t){return t<=f?"top":"bottom"},n(s.x)?(d="left",a(s.x)&&(d="center",h=o(s.y))):i(s.x)&&(d="right",r(s.x)&&(d="center",h=o(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:h}}(this,x),h._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=x.width,g.height=x.height,g.caretX=y.x,g.caretY=y.y,h._model=g,t&&c.custom&&c.custom.call(h,g),h},drawCaret:function(t,e){var n=this._chart.ctx,i=this._view,a=this.getCaretPosition(t,e,i);n.lineTo(a.x1,a.y1),n.lineTo(a.x2,a.y2),n.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,n){var i,a,r,o,s,l,u=n.caretSize,d=n.cornerRadius,h=n.xAlign,c=n.yAlign,f=t.x,g=t.y,p=e.width,m=e.height;if("center"===c)s=g+m/2,"left"===h?(a=(i=f)-u,r=i,o=s+u,l=s-u):(a=(i=f+p)+u,r=i,o=s-u,l=s+u);else if("left"===h?(i=(a=f+d+u)-u,r=a+u):"right"===h?(i=(a=f+p-d-u)-u,r=a+u):(i=(a=n.caretX)-u,r=a+u),"top"===c)s=(o=g)-u,l=o;else{s=(o=g+m)+u,l=o;var v=r;r=i,i=v}return{x1:i,x2:a,x3:r,y1:o,y2:s,y3:l}},drawTitle:function(t,e,n){var i,a,r,o=e.title,s=o.length;if(s){var l=Ne(e.rtl,e.x,e.width);for(t.x=He(e,e._titleAlign),n.textAlign=l.textAlign(e._titleAlign),n.textBaseline="middle",i=e.titleFontSize,a=e.titleSpacing,n.fillStyle=e.titleFontColor,n.font=H.fontString(i,e._titleFontStyle,e._titleFontFamily),r=0;r<s;++r)n.fillText(o[r],l.x(t.x),t.y+i/2),t.y+=i+a,r+1===s&&(t.y+=e.titleMarginBottom-a)}},drawBody:function(t,e,n){var i,a,r,o,s,l,u,d,h=e.bodyFontSize,c=e.bodySpacing,f=e._bodyAlign,g=e.body,p=e.displayColors,m=0,v=p?He(e,"left"):0,b=Ne(e.rtl,e.x,e.width),x=function(e){n.fillText(e,b.x(t.x+m),t.y+h/2),t.y+=h+c},y=b.textAlign(f);for(n.textAlign=f,n.textBaseline="middle",n.font=H.fontString(h,e._bodyFontStyle,e._bodyFontFamily),t.x=He(e,y),n.fillStyle=e.bodyFontColor,H.each(e.beforeBody,x),m=p&&"right"!==y?"center"===f?h/2+1:h+2:0,s=0,u=g.length;s<u;++s){for(i=g[s],a=e.labelTextColors[s],r=e.labelColors[s],n.fillStyle=a,H.each(i.before,x),l=0,d=(o=i.lines).length;l<d;++l){if(p){var _=b.x(v);n.fillStyle=e.legendColorBackground,n.fillRect(b.leftForLtr(_,h),t.y,h,h),n.lineWidth=1,n.strokeStyle=r.borderColor,n.strokeRect(b.leftForLtr(_,h),t.y,h,h),n.fillStyle=r.backgroundColor,n.fillRect(b.leftForLtr(b.xPlus(_,1),h-2),t.y+1,h-2,h-2),n.fillStyle=a}x(o[l])}H.each(i.after,x)}m=0,H.each(e.afterBody,x),t.y-=c},drawFooter:function(t,e,n){var i,a,r=e.footer,o=r.length;if(o){var s=Ne(e.rtl,e.x,e.width);for(t.x=He(e,e._footerAlign),t.y+=e.footerMarginTop,n.textAlign=s.textAlign(e._footerAlign),n.textBaseline="middle",i=e.footerFontSize,n.fillStyle=e.footerFontColor,n.font=H.fontString(i,e._footerFontStyle,e._footerFontFamily),a=0;a<o;++a)n.fillText(r[a],s.x(t.x),t.y+i/2),t.y+=i+e.footerSpacing}},drawBackground:function(t,e,n,i){n.fillStyle=e.backgroundColor,n.strokeStyle=e.borderColor,n.lineWidth=e.borderWidth;var a=e.xAlign,r=e.yAlign,o=t.x,s=t.y,l=i.width,u=i.height,d=e.cornerRadius;n.beginPath(),n.moveTo(o+d,s),"top"===r&&this.drawCaret(t,i),n.lineTo(o+l-d,s),n.quadraticCurveTo(o+l,s,o+l,s+d),"center"===r&&"right"===a&&this.drawCaret(t,i),n.lineTo(o+l,s+u-d),n.quadraticCurveTo(o+l,s+u,o+l-d,s+u),"bottom"===r&&this.drawCaret(t,i),n.lineTo(o+d,s+u),n.quadraticCurveTo(o,s+u,o,s+u-d),"center"===r&&"left"===a&&this.drawCaret(t,i),n.lineTo(o,s+d),n.quadraticCurveTo(o,s,o+d,s),n.closePath(),n.fill(),e.borderWidth>0&&n.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(t.save(),t.globalAlpha=a,this.drawBackground(i,e,t,n),i.y+=e.yPadding,H.rtl.overrideTextDirection(t,e.textDirection),this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),H.rtl.restoreTextDirection(t,e.textDirection),t.restore())}},handleEvent:function(t){var e,n=this,i=n._options;return n._lastActive=n._lastActive||[],"mouseout"===t.type?n._active=[]:(n._active=n._chart.getElementsAtEventForMode(t,i.mode,i),i.reverse&&n._active.reverse()),(e=!H.arrayEquals(n._active,n._lastActive))&&(n._lastActive=n._active,(i.enabled||i.custom)&&(n._eventPosition={x:t.x,y:t.y},n.update(!0),n.pivot())),e}}),Ue=Be,Ye=qe;Ye.positioners=Ue;var Ge=H.valueOrDefault;function Xe(){return H.merge(Object.create(null),[].slice.call(arguments),{merger:function(t,e,n,i){if("xAxes"===t||"yAxes"===t){var a,r,o,s=n[t].length;for(e[t]||(e[t]=[]),a=0;a<s;++a)o=n[t][a],r=Ge(o.type,"xAxes"===t?"category":"linear"),a>=e[t].length&&e[t].push({}),!e[t][a].type||o.type&&o.type!==e[t][a].type?H.merge(e[t][a],[Re.getScaleDefaults(r),o]):H.merge(e[t][a],o)}else H._merger(t,e,n,i)}})}function Ke(){return H.merge(Object.create(null),[].slice.call(arguments),{merger:function(t,e,n,i){var a=e[t]||Object.create(null),r=n[t];"scales"===t?e[t]=Xe(a,r):"scale"===t?e[t]=H.merge(a,[Re.getScaleDefaults(r.type),r]):H._merger(t,e,n,i)}})}function Ze(t){var e=t.options;H.each(t.scales,(function(e){pe.removeBox(t,e)})),e=Ke(N.global,N[t.config.type],e),t.options=t.config.options=e,t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.tooltip._options=e.tooltips,t.tooltip.initialize()}function $e(t,e,n){var i,a=function(t){return t.id===i};do{i=e+n++}while(H.findIndex(t,a)>=0);return i}function Je(t){return"top"===t||"bottom"===t}function Qe(t,e){return function(n,i){return n[t]===i[t]?n[e]-i[e]:n[t]-i[t]}}N._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var tn=function(t,e){return this.construct(t,e),this};H.extend(tn.prototype,{construct:function(t,e){var n=this;e=function(t){var e=(t=t||Object.create(null)).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=Ke(N.global,N[t.type],t.options||{}),t}(e);var i=Oe.acquireContext(t,e),a=i&&i.canvas,r=a&&a.height,o=a&&a.width;n.id=H.uid(),n.ctx=i,n.canvas=a,n.config=e,n.width=o,n.height=r,n.aspectRatio=r?o/r:null,n.options=e.options,n._bufferedRender=!1,n._layers=[],n.chart=n,n.controller=n,tn.instances[n.id]=n,Object.defineProperty(n,"data",{get:function(){return n.config.data},set:function(t){n.config.data=t}}),i&&a?(n.initialize(),n.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return Le.notify(t,"beforeInit"),H.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.initToolTip(),Le.notify(t,"afterInit"),t},clear:function(){return H.canvas.clear(this),this},stop:function(){return J.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,a=n.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(H.getMaximumWidth(i))),o=Math.max(0,Math.floor(a?r/a:H.getMaximumHeight(i)));if((e.width!==r||e.height!==o)&&(i.width=e.width=r,i.height=e.height=o,i.style.width=r+"px",i.style.height=o+"px",H.retinaScale(e,n.devicePixelRatio),!t)){var s={width:r,height:o};Le.notify(e,"resize",[s]),n.onResize&&n.onResize(e,s),e.stop(),e.update({duration:n.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;H.each(e.xAxes,(function(t,n){t.id||(t.id=$e(e.xAxes,"x-axis-",n))})),H.each(e.yAxes,(function(t,n){t.id||(t.id=$e(e.yAxes,"y-axis-",n))})),n&&(n.id=n.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,n=t.scales||{},i=[],a=Object.keys(n).reduce((function(t,e){return t[e]=!1,t}),{});e.scales&&(i=i.concat((e.scales.xAxes||[]).map((function(t){return{options:t,dtype:"category",dposition:"bottom"}})),(e.scales.yAxes||[]).map((function(t){return{options:t,dtype:"linear",dposition:"left"}})))),e.scale&&i.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),H.each(i,(function(e){var i=e.options,r=i.id,o=Ge(i.type,e.dtype);Je(i.position)!==Je(e.dposition)&&(i.position=e.dposition),a[r]=!0;var s=null;if(r in n&&n[r].type===o)(s=n[r]).options=i,s.ctx=t.ctx,s.chart=t;else{var l=Re.getScaleConstructor(o);if(!l)return;s=new l({id:r,type:o,options:i,ctx:t.ctx,chart:t}),n[s.id]=s}s.mergeTicksOptions(),e.isDefault&&(t.scale=s)})),H.each(a,(function(t,e){t||delete n[e]})),t.scales=n,Re.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t,e,n=this,i=[],a=n.data.datasets;for(t=0,e=a.length;t<e;t++){var r=a[t],o=n.getDatasetMeta(t),s=r.type||n.config.type;if(o.type&&o.type!==s&&(n.destroyDatasetMeta(t),o=n.getDatasetMeta(t)),o.type=s,o.order=r.order||0,o.index=t,o.controller)o.controller.updateIndex(t),o.controller.linkScales();else{var l=Jt[o.type];if(void 0===l)throw new Error('"'+o.type+'" is not a chart type.');o.controller=new l(n,t),i.push(o.controller)}}return i},resetElements:function(){var t=this;H.each(t.data.datasets,(function(e,n){t.getDatasetMeta(n).controller.reset()}),t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e,n,i=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),Ze(i),Le._invalidate(i),!1!==Le.notify(i,"beforeUpdate")){i.tooltip._data=i.data;var a=i.buildOrUpdateControllers();for(e=0,n=i.data.datasets.length;e<n;e++)i.getDatasetMeta(e).controller.buildOrUpdateElements();i.updateLayout(),i.options.animation&&i.options.animation.duration&&H.each(a,(function(t){t.reset()})),i.updateDatasets(),i.tooltip.initialize(),i.lastActive=[],Le.notify(i,"afterUpdate"),i._layers.sort(Qe("z","_idx")),i._bufferedRender?i._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:i.render(t)}},updateLayout:function(){var t=this;!1!==Le.notify(t,"beforeLayout")&&(pe.update(this,this.width,this.height),t._layers=[],H.each(t.boxes,(function(e){e._configure&&e._configure(),t._layers.push.apply(t._layers,e._layers())}),t),t._layers.forEach((function(t,e){t._idx=e})),Le.notify(t,"afterScaleUpdate"),Le.notify(t,"afterLayout"))},updateDatasets:function(){if(!1!==Le.notify(this,"beforeDatasetsUpdate")){for(var t=0,e=this.data.datasets.length;t<e;++t)this.updateDataset(t);Le.notify(this,"afterDatasetsUpdate")}},updateDataset:function(t){var e=this.getDatasetMeta(t),n={meta:e,index:t};!1!==Le.notify(this,"beforeDatasetUpdate",[n])&&(e.controller._update(),Le.notify(this,"afterDatasetUpdate",[n]))},render:function(t){var e=this;t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]});var n=e.options.animation,i=Ge(t.duration,n&&n.duration),a=t.lazy;if(!1!==Le.notify(e,"beforeRender")){var r=function(t){Le.notify(e,"afterRender"),H.callback(n&&n.onComplete,[t],e)};if(n&&i){var o=new $({numSteps:i/16.66,easing:t.easing||n.easing,render:function(t,e){var n=H.easing.effects[e.easing],i=e.currentStep,a=i/e.numSteps;t.draw(n(a),a,i)},onAnimationProgress:n.onProgress,onAnimationComplete:r});J.addAnimation(e,o,i,a)}else e.draw(),r(new $({numSteps:0,chart:e}));return e}},draw:function(t){var e,n,i=this;if(i.clear(),H.isNullOrUndef(t)&&(t=1),i.transition(t),!(i.width<=0||i.height<=0)&&!1!==Le.notify(i,"beforeDraw",[t])){for(n=i._layers,e=0;e<n.length&&n[e].z<=0;++e)n[e].draw(i.chartArea);for(i.drawDatasets(t);e<n.length;++e)n[e].draw(i.chartArea);i._drawTooltip(t),Le.notify(i,"afterDraw",[t])}},transition:function(t){for(var e=0,n=(this.data.datasets||[]).length;e<n;++e)this.isDatasetVisible(e)&&this.getDatasetMeta(e).controller.transition(t);this.tooltip.transition(t)},_getSortedDatasetMetas:function(t){var e,n,i=[];for(e=0,n=(this.data.datasets||[]).length;e<n;++e)t&&!this.isDatasetVisible(e)||i.push(this.getDatasetMeta(e));return i.sort(Qe("order","index")),i},_getSortedVisibleDatasetMetas:function(){return this._getSortedDatasetMetas(!0)},drawDatasets:function(t){var e,n;if(!1!==Le.notify(this,"beforeDatasetsDraw",[t])){for(n=(e=this._getSortedVisibleDatasetMetas()).length-1;n>=0;--n)this.drawDataset(e[n],t);Le.notify(this,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n={meta:t,index:t.index,easingValue:e};!1!==Le.notify(this,"beforeDatasetDraw",[n])&&(t.controller.draw(e),Le.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,n={tooltip:e,easingValue:t};!1!==Le.notify(this,"beforeTooltipDraw",[n])&&(e.draw(),Le.notify(this,"afterTooltipDraw",[n]))},getElementAtEvent:function(t){return re.modes.single(this,t)},getElementsAtEvent:function(t){return re.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return re.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=re.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return re.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var n=e._meta[this.id];return n||(n=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e.order||0,index:t}),n},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e<n;++e)this.isDatasetVisible(e)&&t++;return t},isDatasetVisible:function(t){var e=this.getDatasetMeta(t);return"boolean"==typeof e.hidden?!e.hidden:!this.data.datasets[t].hidden},generateLegend:function(){return this.options.legendCallback(this)},destroyDatasetMeta:function(t){var e=this.id,n=this.data.datasets[t],i=n._meta&&n._meta[e];i&&(i.controller.destroy(),delete n._meta[e])},destroy:function(){var t,e,n=this,i=n.canvas;for(n.stop(),t=0,e=n.data.datasets.length;t<e;++t)n.destroyDatasetMeta(t);i&&(n.unbindEvents(),H.canvas.clear(n),Oe.releaseContext(n.ctx),n.canvas=null,n.ctx=null),Le.notify(n,"destroy"),delete tn.instances[n.id]},toBase64Image:function(){return this.canvas.toDataURL.apply(this.canvas,arguments)},initToolTip:function(){var t=this;t.tooltip=new Ye({_chart:t,_chartInstance:t,_data:t.data,_options:t.options.tooltips},t)},bindEvents:function(){var t=this,e=t._listeners={},n=function(){t.eventHandler.apply(t,arguments)};H.each(t.options.events,(function(i){Oe.addEventListener(t,i,n),e[i]=n})),t.options.responsive&&(n=function(){t.resize()},Oe.addEventListener(t,"resize",n),e.resize=n)},unbindEvents:function(){var t=this,e=t._listeners;e&&(delete t._listeners,H.each(e,(function(e,n){Oe.removeEventListener(t,n,e)})))},updateHoverStyle:function(t,e,n){var i,a,r,o=n?"set":"remove";for(a=0,r=t.length;a<r;++a)(i=t[a])&&this.getDatasetMeta(i._datasetIndex).controller[o+"HoverStyle"](i);"dataset"===e&&this.getDatasetMeta(t[0]._datasetIndex).controller["_"+o+"DatasetHoverStyle"]()},eventHandler:function(t){var e=this,n=e.tooltip;if(!1!==Le.notify(e,"beforeEvent",[t])){e._bufferedRender=!0,e._bufferedRequest=null;var i=e.handleEvent(t);n&&(i=n._start?n.handleEvent(t):i|n.handleEvent(t)),Le.notify(e,"afterEvent",[t]);var a=e._bufferedRequest;return a?e.render(a):i&&!e.animating&&(e.stop(),e.render({duration:e.options.hover.animationDuration,lazy:!0})),e._bufferedRender=!1,e._bufferedRequest=null,e}},handleEvent:function(t){var e,n=this,i=n.options||{},a=i.hover;return n.lastActive=n.lastActive||[],"mouseout"===t.type?n.active=[]:n.active=n.getElementsAtEventForMode(t,a.mode,a),H.callback(i.onHover||i.hover.onHover,[t.native,n.active],n),"mouseup"!==t.type&&"click"!==t.type||i.onClick&&i.onClick.call(n,t.native,n.active),n.lastActive.length&&n.updateHoverStyle(n.lastActive,a.mode,!1),n.active.length&&a.mode&&n.updateHoverStyle(n.active,a.mode,!0),e=!H.arrayEquals(n.active,n.lastActive),n.lastActive=n.active,e}}),tn.instances={};var en=tn;tn.Controller=tn,tn.types={},H.configMerge=Ke,H.scaleMerge=Xe;function nn(){throw new Error("This method is not implemented: either no adapter can be found or an incomplete integration was provided.")}function an(t){this.options=t||{}}H.extend(an.prototype,{formats:nn,parse:nn,format:nn,add:nn,diff:nn,startOf:nn,endOf:nn,_create:function(t){return t}}),an.override=function(t){H.extend(an.prototype,t)};var rn={_date:an},on={formatters:{values:function(t){return H.isArray(t)?t:""+t},linear:function(t,e,n){var i=n.length>3?n[2]-n[1]:n[1]-n[0];Math.abs(i)>1&&t!==Math.floor(t)&&(i=t-Math.floor(t));var a=H.log10(Math.abs(i)),r="";if(0!==t)if(Math.max(Math.abs(n[0]),Math.abs(n[n.length-1]))<1e-4){var o=H.log10(Math.abs(t)),s=Math.floor(o)-Math.floor(a);s=Math.max(Math.min(s,20),0),r=t.toExponential(s)}else{var l=-1*Math.floor(a);l=Math.max(Math.min(l,20),0),r=t.toFixed(l)}else r="0";return r},logarithmic:function(t,e,n){var i=t/Math.pow(10,Math.floor(H.log10(t)));return 0===t?"0":1===i||2===i||5===i||0===e||e===n.length-1?t.toExponential():""}}},sn=H.isArray,ln=H.isNullOrUndef,un=H.valueOrDefault,dn=H.valueAtIndexOrDefault;function hn(t,e,n){var i,a=t.getTicks().length,r=Math.min(e,a-1),o=t.getPixelForTick(r),s=t._startPixel,l=t._endPixel;if(!(n&&(i=1===a?Math.max(o-s,l-o):0===e?(t.getPixelForTick(1)-o)/2:(o-t.getPixelForTick(r-1))/2,(o+=r<e?i:-i)<s-1e-6||o>l+1e-6)))return o}function cn(t,e,n,i){var a,r,o,s,l,u,d,h,c,f,g,p,m,v=n.length,b=[],x=[],y=[],_=0,k=0;for(a=0;a<v;++a){if(s=n[a].label,l=n[a].major?e.major:e.minor,t.font=u=l.string,d=i[u]=i[u]||{data:{},gc:[]},h=l.lineHeight,c=f=0,ln(s)||sn(s)){if(sn(s))for(r=0,o=s.length;r<o;++r)g=s[r],ln(g)||sn(g)||(c=H.measureText(t,d.data,d.gc,c,g),f+=h)}else c=H.measureText(t,d.data,d.gc,c,s),f=h;b.push(c),x.push(f),y.push(h/2),_=Math.max(c,_),k=Math.max(f,k)}function w(t){return{width:b[t]||0,height:x[t]||0,offset:y[t]||0}}return function(t,e){H.each(t,(function(t){var n,i=t.gc,a=i.length/2;if(a>e){for(n=0;n<a;++n)delete t.data[i[n]];i.splice(0,a)}}))}(i,v),p=b.indexOf(_),m=x.indexOf(k),{first:w(0),last:w(v-1),widest:w(p),highest:w(m)}}function fn(t){return t.drawTicks?t.tickMarkLength:0}function gn(t){var e,n;return t.display?(e=H.options._parseFont(t),n=H.options.toPadding(t.padding),e.lineHeight+n.height):0}function pn(t,e){return H.extend(H.options._parseFont({fontFamily:un(e.fontFamily,t.fontFamily),fontSize:un(e.fontSize,t.fontSize),fontStyle:un(e.fontStyle,t.fontStyle),lineHeight:un(e.lineHeight,t.lineHeight)}),{color:H.options.resolve([e.fontColor,t.fontColor,N.global.defaultFontColor])})}function mn(t){var e=pn(t,t.minor);return{minor:e,major:t.major.enabled?pn(t,t.major):e}}function vn(t){var e,n,i,a=[];for(n=0,i=t.length;n<i;++n)void 0!==(e=t[n])._index&&a.push(e);return a}function bn(t,e,n,i){var a,r,o,s,l=un(n,0),u=Math.min(un(i,t.length),t.length),d=0;for(e=Math.ceil(e),i&&(e=(a=i-n)/Math.floor(a/e)),s=l;s<0;)d++,s=Math.round(l+d*e);for(r=Math.max(l,0);r<u;r++)o=t[r],r===s?(o._index=r,d++,s=Math.round(l+d*e)):delete o.label}N._set("scale",{display:!0,position:"left",offset:!1,gridLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,drawBorder:!0,drawOnChartArea:!0,drawTicks:!0,tickMarkLength:10,zeroLineWidth:1,zeroLineColor:"rgba(0,0,0,0.25)",zeroLineBorderDash:[],zeroLineBorderDashOffset:0,offsetGridLines:!1,borderDash:[],borderDashOffset:0},scaleLabel:{display:!1,labelString:"",padding:{top:4,bottom:4}},ticks:{beginAtZero:!1,minRotation:0,maxRotation:50,mirror:!1,padding:0,reverse:!1,display:!0,autoSkip:!0,autoSkipPadding:0,labelOffset:0,callback:on.formatters.values,minor:{},major:{}}});var xn=K.extend({zeroLineIndex:0,getPadding:function(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}},getTicks:function(){return this._ticks},_getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]},mergeTicksOptions:function(){},beforeUpdate:function(){H.callback(this.options.beforeUpdate,[this])},update:function(t,e,n){var i,a,r,o,s,l=this,u=l.options.ticks,d=u.sampleSize;if(l.beforeUpdate(),l.maxWidth=t,l.maxHeight=e,l.margins=H.extend({left:0,right:0,top:0,bottom:0},n),l._ticks=null,l.ticks=null,l._labelSizes=null,l._maxLabelLines=0,l.longestLabelWidth=0,l.longestTextCache=l.longestTextCache||{},l._gridLineItems=null,l._labelItems=null,l.beforeSetDimensions(),l.setDimensions(),l.afterSetDimensions(),l.beforeDataLimits(),l.determineDataLimits(),l.afterDataLimits(),l.beforeBuildTicks(),o=l.buildTicks()||[],(!(o=l.afterBuildTicks(o)||o)||!o.length)&&l.ticks)for(o=[],i=0,a=l.ticks.length;i<a;++i)o.push({value:l.ticks[i],major:!1});return l._ticks=o,s=d<o.length,r=l._convertTicksToLabels(s?function(t,e){for(var n=[],i=t.length/e,a=0,r=t.length;a<r;a+=i)n.push(t[Math.floor(a)]);return n}(o,d):o),l._configure(),l.beforeCalculateTickRotation(),l.calculateTickRotation(),l.afterCalculateTickRotation(),l.beforeFit(),l.fit(),l.afterFit(),l._ticksToDraw=u.display&&(u.autoSkip||"auto"===u.source)?l._autoSkip(o):o,s&&(r=l._convertTicksToLabels(l._ticksToDraw)),l.ticks=r,l.afterUpdate(),l.minSize},_configure:function(){var t,e,n=this,i=n.options.ticks.reverse;n.isHorizontal()?(t=n.left,e=n.right):(t=n.top,e=n.bottom,i=!i),n._startPixel=t,n._endPixel=e,n._reversePixels=i,n._length=e-t},afterUpdate:function(){H.callback(this.options.afterUpdate,[this])},beforeSetDimensions:function(){H.callback(this.options.beforeSetDimensions,[this])},setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0},afterSetDimensions:function(){H.callback(this.options.afterSetDimensions,[this])},beforeDataLimits:function(){H.callback(this.options.beforeDataLimits,[this])},determineDataLimits:H.noop,afterDataLimits:function(){H.callback(this.options.afterDataLimits,[this])},beforeBuildTicks:function(){H.callback(this.options.beforeBuildTicks,[this])},buildTicks:H.noop,afterBuildTicks:function(t){var e=this;return sn(t)&&t.length?H.callback(e.options.afterBuildTicks,[e,t]):(e.ticks=H.callback(e.options.afterBuildTicks,[e,e.ticks])||e.ticks,t)},beforeTickToLabelConversion:function(){H.callback(this.options.beforeTickToLabelConversion,[this])},convertTicksToLabels:function(){var t=this.options.ticks;this.ticks=this.ticks.map(t.userCallback||t.callback,this)},afterTickToLabelConversion:function(){H.callback(this.options.afterTickToLabelConversion,[this])},beforeCalculateTickRotation:function(){H.callback(this.options.beforeCalculateTickRotation,[this])},calculateTickRotation:function(){var t,e,n,i,a,r,o,s=this,l=s.options,u=l.ticks,d=s.getTicks().length,h=u.minRotation||0,c=u.maxRotation,f=h;!s._isVisible()||!u.display||h>=c||d<=1||!s.isHorizontal()?s.labelRotation=h:(e=(t=s._getLabelSizes()).widest.width,n=t.highest.height-t.highest.offset,i=Math.min(s.maxWidth,s.chart.width-e),e+6>(a=l.offset?s.maxWidth/d:i/(d-1))&&(a=i/(d-(l.offset?.5:1)),r=s.maxHeight-fn(l.gridLines)-u.padding-gn(l.scaleLabel),o=Math.sqrt(e*e+n*n),f=H.toDegrees(Math.min(Math.asin(Math.min((t.highest.height+6)/a,1)),Math.asin(Math.min(r/o,1))-Math.asin(n/o))),f=Math.max(h,Math.min(c,f))),s.labelRotation=f)},afterCalculateTickRotation:function(){H.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){H.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},n=t.chart,i=t.options,a=i.ticks,r=i.scaleLabel,o=i.gridLines,s=t._isVisible(),l="bottom"===i.position,u=t.isHorizontal();if(u?e.width=t.maxWidth:s&&(e.width=fn(o)+gn(r)),u?s&&(e.height=fn(o)+gn(r)):e.height=t.maxHeight,a.display&&s){var d=mn(a),h=t._getLabelSizes(),c=h.first,f=h.last,g=h.widest,p=h.highest,m=.4*d.minor.lineHeight,v=a.padding;if(u){var b=0!==t.labelRotation,x=H.toRadians(t.labelRotation),y=Math.cos(x),_=Math.sin(x),k=_*g.width+y*(p.height-(b?p.offset:0))+(b?0:m);e.height=Math.min(t.maxHeight,e.height+k+v);var w,M,S=t.getPixelForTick(0)-t.left,C=t.right-t.getPixelForTick(t.getTicks().length-1);b?(w=l?y*c.width+_*c.offset:_*(c.height-c.offset),M=l?_*(f.height-f.offset):y*f.width+_*f.offset):(w=c.width/2,M=f.width/2),t.paddingLeft=Math.max((w-S)*t.width/(t.width-S),0)+3,t.paddingRight=Math.max((M-C)*t.width/(t.width-C),0)+3}else{var P=a.mirror?0:g.width+v+m;e.width=Math.min(t.maxWidth,e.width+P),t.paddingTop=c.height/2,t.paddingBottom=f.height/2}}t.handleMargins(),u?(t.width=t._length=n.width-t.margins.left-t.margins.right,t.height=e.height):(t.width=e.width,t.height=t._length=n.height-t.margins.top-t.margins.bottom)},handleMargins:function(){var t=this;t.margins&&(t.margins.left=Math.max(t.paddingLeft,t.margins.left),t.margins.top=Math.max(t.paddingTop,t.margins.top),t.margins.right=Math.max(t.paddingRight,t.margins.right),t.margins.bottom=Math.max(t.paddingBottom,t.margins.bottom))},afterFit:function(){H.callback(this.options.afterFit,[this])},isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(ln(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},_convertTicksToLabels:function(t){var e,n,i,a=this;for(a.ticks=t.map((function(t){return t.value})),a.beforeTickToLabelConversion(),e=a.convertTicksToLabels(t)||a.ticks,a.afterTickToLabelConversion(),n=0,i=t.length;n<i;++n)t[n].label=e[n];return e},_getLabelSizes:function(){var t=this,e=t._labelSizes;return e||(t._labelSizes=e=cn(t.ctx,mn(t.options.ticks),t.getTicks(),t.longestTextCache),t.longestLabelWidth=e.widest.width),e},_parseValue:function(t){var e,n,i,a;return sn(t)?(e=+this.getRightValue(t[0]),n=+this.getRightValue(t[1]),i=Math.min(e,n),a=Math.max(e,n)):(e=void 0,n=t=+this.getRightValue(t),i=t,a=t),{min:i,max:a,start:e,end:n}},_getScaleLabel:function(t){var e=this._parseValue(t);return void 0!==e.start?"["+e.start+", "+e.end+"]":+this.getRightValue(t)},getLabelForIndex:H.noop,getPixelForValue:H.noop,getValueForPixel:H.noop,getPixelForTick:function(t){var e=this.options.offset,n=this._ticks.length,i=1/Math.max(n-(e?0:1),1);return t<0||t>n-1?null:this.getPixelForDecimal(t*i+(e?i/2:0))},getPixelForDecimal:function(t){return this._reversePixels&&(t=1-t),this._startPixel+t*this._length},getDecimalForPixel:function(t){var e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,n,i,a,r=this.options.ticks,o=this._length,s=r.maxTicksLimit||o/this._tickSize()+1,l=r.major.enabled?function(t){var e,n,i=[];for(e=0,n=t.length;e<n;e++)t[e].major&&i.push(e);return i}(t):[],u=l.length,d=l[0],h=l[u-1];if(u>s)return function(t,e,n){var i,a,r=0,o=e[0];for(n=Math.ceil(n),i=0;i<t.length;i++)a=t[i],i===o?(a._index=i,o=e[++r*n]):delete a.label}(t,l,u/s),vn(t);if(i=function(t,e,n,i){var a,r,o,s,l=function(t){var e,n,i=t.length;if(i<2)return!1;for(n=t[0],e=1;e<i;++e)if(t[e]-t[e-1]!==n)return!1;return n}(t),u=(e.length-1)/i;if(!l)return Math.max(u,1);for(o=0,s=(a=H.math._factorize(l)).length-1;o<s;o++)if((r=a[o])>u)return r;return Math.max(u,1)}(l,t,0,s),u>0){for(e=0,n=u-1;e<n;e++)bn(t,i,l[e],l[e+1]);return a=u>1?(h-d)/(u-1):null,bn(t,i,H.isNullOrUndef(a)?0:d-a,d),bn(t,i,h,H.isNullOrUndef(a)?t.length:h+a),vn(t)}return bn(t,i),vn(t)},_tickSize:function(){var t=this.options.ticks,e=H.toRadians(this.labelRotation),n=Math.abs(Math.cos(e)),i=Math.abs(Math.sin(e)),a=this._getLabelSizes(),r=t.autoSkipPadding||0,o=a?a.widest.width+r:0,s=a?a.highest.height+r:0;return this.isHorizontal()?s*n>o*i?o/n:s/i:s*i<o*n?s/n:o/i},_isVisible:function(){var t,e,n,i=this.chart,a=this.options.display;if("auto"!==a)return!!a;for(t=0,e=i.data.datasets.length;t<e;++t)if(i.isDatasetVisible(t)&&((n=i.getDatasetMeta(t)).xAxisID===this.id||n.yAxisID===this.id))return!0;return!1},_computeGridLineItems:function(t){var e,n,i,a,r,o,s,l,u,d,h,c,f,g,p,m,v,b=this,x=b.chart,y=b.options,_=y.gridLines,k=y.position,w=_.offsetGridLines,M=b.isHorizontal(),S=b._ticksToDraw,C=S.length+(w?1:0),P=fn(_),A=[],D=_.drawBorder?dn(_.lineWidth,0,0):0,T=D/2,I=H._alignPixel,F=function(t){return I(x,t,D)};for("top"===k?(e=F(b.bottom),s=b.bottom-P,u=e-T,h=F(t.top)+T,f=t.bottom):"bottom"===k?(e=F(b.top),h=t.top,f=F(t.bottom)-T,s=e+T,u=b.top+P):"left"===k?(e=F(b.right),o=b.right-P,l=e-T,d=F(t.left)+T,c=t.right):(e=F(b.left),d=t.left,c=F(t.right)-T,o=e+T,l=b.left+P),n=0;n<C;++n)i=S[n]||{},ln(i.label)&&n<S.length||(n===b.zeroLineIndex&&y.offset===w?(g=_.zeroLineWidth,p=_.zeroLineColor,m=_.zeroLineBorderDash||[],v=_.zeroLineBorderDashOffset||0):(g=dn(_.lineWidth,n,1),p=dn(_.color,n,"rgba(0,0,0,0.1)"),m=_.borderDash||[],v=_.borderDashOffset||0),void 0!==(a=hn(b,i._index||n,w))&&(r=I(x,a,g),M?o=l=d=c=r:s=u=h=f=r,A.push({tx1:o,ty1:s,tx2:l,ty2:u,x1:d,y1:h,x2:c,y2:f,width:g,color:p,borderDash:m,borderDashOffset:v})));return A.ticksLength=C,A.borderValue=e,A},_computeLabelItems:function(){var t,e,n,i,a,r,o,s,l,u,d,h,c=this,f=c.options,g=f.ticks,p=f.position,m=g.mirror,v=c.isHorizontal(),b=c._ticksToDraw,x=mn(g),y=g.padding,_=fn(f.gridLines),k=-H.toRadians(c.labelRotation),w=[];for("top"===p?(r=c.bottom-_-y,o=k?"left":"center"):"bottom"===p?(r=c.top+_+y,o=k?"right":"center"):"left"===p?(a=c.right-(m?0:_)-y,o=m?"left":"right"):(a=c.left+(m?0:_)+y,o=m?"right":"left"),t=0,e=b.length;t<e;++t)i=(n=b[t]).label,ln(i)||(s=c.getPixelForTick(n._index||t)+g.labelOffset,u=(l=n.major?x.major:x.minor).lineHeight,d=sn(i)?i.length:1,v?(a=s,h="top"===p?((k?1:.5)-d)*u:(k?0:.5)*u):(r=s,h=(1-d)*u/2),w.push({x:a,y:r,rotation:k,label:i,font:l,textOffset:h,textAlign:o}));return w},_drawGrid:function(t){var e=this,n=e.options.gridLines;if(n.display){var i,a,r,o,s,l=e.ctx,u=e.chart,d=H._alignPixel,h=n.drawBorder?dn(n.lineWidth,0,0):0,c=e._gridLineItems||(e._gridLineItems=e._computeGridLineItems(t));for(r=0,o=c.length;r<o;++r)i=(s=c[r]).width,a=s.color,i&&a&&(l.save(),l.lineWidth=i,l.strokeStyle=a,l.setLineDash&&(l.setLineDash(s.borderDash),l.lineDashOffset=s.borderDashOffset),l.beginPath(),n.drawTicks&&(l.moveTo(s.tx1,s.ty1),l.lineTo(s.tx2,s.ty2)),n.drawOnChartArea&&(l.moveTo(s.x1,s.y1),l.lineTo(s.x2,s.y2)),l.stroke(),l.restore());if(h){var f,g,p,m,v=h,b=dn(n.lineWidth,c.ticksLength-1,1),x=c.borderValue;e.isHorizontal()?(f=d(u,e.left,v)-v/2,g=d(u,e.right,b)+b/2,p=m=x):(p=d(u,e.top,v)-v/2,m=d(u,e.bottom,b)+b/2,f=g=x),l.lineWidth=h,l.strokeStyle=dn(n.color,0),l.beginPath(),l.moveTo(f,p),l.lineTo(g,m),l.stroke()}}},_drawLabels:function(){var t=this;if(t.options.ticks.display){var e,n,i,a,r,o,s,l,u=t.ctx,d=t._labelItems||(t._labelItems=t._computeLabelItems());for(e=0,i=d.length;e<i;++e){if(o=(r=d[e]).font,u.save(),u.translate(r.x,r.y),u.rotate(r.rotation),u.font=o.string,u.fillStyle=o.color,u.textBaseline="middle",u.textAlign=r.textAlign,s=r.label,l=r.textOffset,sn(s))for(n=0,a=s.length;n<a;++n)u.fillText(""+s[n],0,l),l+=o.lineHeight;else u.fillText(s,0,l);u.restore()}}},_drawTitle:function(){var t=this,e=t.ctx,n=t.options,i=n.scaleLabel;if(i.display){var a,r,o=un(i.fontColor,N.global.defaultFontColor),s=H.options._parseFont(i),l=H.options.toPadding(i.padding),u=s.lineHeight/2,d=n.position,h=0;if(t.isHorizontal())a=t.left+t.width/2,r="bottom"===d?t.bottom-u-l.bottom:t.top+u+l.top;else{var c="left"===d;a=c?t.left+u+l.top:t.right-u-l.top,r=t.top+t.height/2,h=c?-.5*Math.PI:.5*Math.PI}e.save(),e.translate(a,r),e.rotate(h),e.textAlign="center",e.textBaseline="middle",e.fillStyle=o,e.font=s.string,e.fillText(i.labelString,0,0),e.restore()}},draw:function(t){this._isVisible()&&(this._drawGrid(t),this._drawTitle(),this._drawLabels())},_layers:function(){var t=this,e=t.options,n=e.ticks&&e.ticks.z||0,i=e.gridLines&&e.gridLines.z||0;return t._isVisible()&&n!==i&&t.draw===t._draw?[{z:i,draw:function(){t._drawGrid.apply(t,arguments),t._drawTitle.apply(t,arguments)}},{z:n,draw:function(){t._drawLabels.apply(t,arguments)}}]:[{z:n,draw:function(){t.draw.apply(t,arguments)}}]},_getMatchingVisibleMetas:function(t){var e=this,n=e.isHorizontal();return e.chart._getSortedVisibleDatasetMetas().filter((function(i){return(!t||i.type===t)&&(n?i.xAxisID===e.id:i.yAxisID===e.id)}))}});xn.prototype._draw=xn.prototype.draw;var yn=xn,_n=H.isNullOrUndef,kn=yn.extend({determineDataLimits:function(){var t,e=this,n=e._getLabels(),i=e.options.ticks,a=i.min,r=i.max,o=0,s=n.length-1;void 0!==a&&(t=n.indexOf(a))>=0&&(o=t),void 0!==r&&(t=n.indexOf(r))>=0&&(s=t),e.minIndex=o,e.maxIndex=s,e.min=n[o],e.max=n[s]},buildTicks:function(){var t=this._getLabels(),e=this.minIndex,n=this.maxIndex;this.ticks=0===e&&n===t.length-1?t:t.slice(e,n+1)},getLabelForIndex:function(t,e){var n=this.chart;return n.getDatasetMeta(e).controller._getValueScaleId()===this.id?this.getRightValue(n.data.datasets[e].data[t]):this._getLabels()[t]},_configure:function(){var t=this,e=t.options.offset,n=t.ticks;yn.prototype._configure.call(t),t.isHorizontal()||(t._reversePixels=!t._reversePixels),n&&(t._startValue=t.minIndex-(e?.5:0),t._valueRange=Math.max(n.length-(e?0:1),1))},getPixelForValue:function(t,e,n){var i,a,r,o=this;return _n(e)||_n(n)||(t=o.chart.data.datasets[n].data[e]),_n(t)||(i=o.isHorizontal()?t.x:t.y),(void 0!==i||void 0!==t&&isNaN(e))&&(a=o._getLabels(),t=H.valueOrDefault(i,t),e=-1!==(r=a.indexOf(t))?r:e,isNaN(e)&&(e=t)),o.getPixelForDecimal((e-o._startValue)/o._valueRange)},getPixelForTick:function(t){var e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t],t+this.minIndex)},getValueForPixel:function(t){var e=Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange);return Math.min(Math.max(e,0),this.ticks.length-1)},getBasePixel:function(){return this.bottom}}),wn={position:"bottom"};kn._defaults=wn;var Mn=H.noop,Sn=H.isNullOrUndef;var Cn=yn.extend({getRightValue:function(t){return"string"==typeof t?+t:yn.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=H.sign(t.min),i=H.sign(t.max);n<0&&i<0?t.max=0:n>0&&i>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==r&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this.options.ticks,n=e.stepSize,i=e.maxTicksLimit;return n?t=Math.ceil(this.max/n)-Math.floor(this.min/n)+1:(t=this._computeTickLimit(),i=i||11),i&&(t=Math.min(i,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:Mn,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),i={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,precision:e.precision,stepSize:H.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var n,i,a,r,o=[],s=t.stepSize,l=s||1,u=t.maxTicks-1,d=t.min,h=t.max,c=t.precision,f=e.min,g=e.max,p=H.niceNum((g-f)/u/l)*l;if(p<1e-14&&Sn(d)&&Sn(h))return[f,g];(r=Math.ceil(g/p)-Math.floor(f/p))>u&&(p=H.niceNum(r*p/u/l)*l),s||Sn(c)?n=Math.pow(10,H._decimalPlaces(p)):(n=Math.pow(10,c),p=Math.ceil(p*n)/n),i=Math.floor(f/p)*p,a=Math.ceil(g/p)*p,s&&(!Sn(d)&&H.almostWhole(d/p,p/1e3)&&(i=d),!Sn(h)&&H.almostWhole(h/p,p/1e3)&&(a=h)),r=(a-i)/p,r=H.almostEquals(r,Math.round(r),p/1e3)?Math.round(r):Math.ceil(r),i=Math.round(i*n)/n,a=Math.round(a*n)/n,o.push(Sn(d)?i:d);for(var m=1;m<r;++m)o.push(Math.round((i+m*p)*n)/n);return o.push(Sn(h)?a:h),o}(i,t);t.handleDirectionalChanges(),t.max=H.max(a),t.min=H.min(a),e.reverse?(a.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var t=this;t.ticksAsNumbers=t.ticks.slice(),t.zeroLineIndex=t.ticks.indexOf(0),yn.prototype.convertTicksToLabels.call(t)},_configure:function(){var t,e=this,n=e.getTicks(),i=e.min,a=e.max;yn.prototype._configure.call(e),e.options.offset&&n.length&&(i-=t=(a-i)/Math.max(n.length-1,1)/2,a+=t),e._startValue=i,e._endValue=a,e._valueRange=a-i}}),Pn={position:"left",ticks:{callback:on.formatters.linear}};function An(t,e,n,i){var a,r,o=t.options,s=function(t,e,n){var i=[n.type,void 0===e&&void 0===n.stack?n.index:"",n.stack].join(".");return void 0===t[i]&&(t[i]={pos:[],neg:[]}),t[i]}(e,o.stacked,n),l=s.pos,u=s.neg,d=i.length;for(a=0;a<d;++a)r=t._parseValue(i[a]),isNaN(r.min)||isNaN(r.max)||n.data[a].hidden||(l[a]=l[a]||0,u[a]=u[a]||0,o.relativePoints?l[a]=100:r.min<0||r.max<0?u[a]+=r.min:l[a]+=r.max)}function Dn(t,e,n){var i,a,r=n.length;for(i=0;i<r;++i)a=t._parseValue(n[i]),isNaN(a.min)||isNaN(a.max)||e.data[i].hidden||(t.min=Math.min(t.min,a.min),t.max=Math.max(t.max,a.max))}var Tn=Cn.extend({determineDataLimits:function(){var t,e,n,i,a=this,r=a.options,o=a.chart.data.datasets,s=a._getMatchingVisibleMetas(),l=r.stacked,u={},d=s.length;if(a.min=Number.POSITIVE_INFINITY,a.max=Number.NEGATIVE_INFINITY,void 0===l)for(t=0;!l&&t<d;++t)l=void 0!==(e=s[t]).stack;for(t=0;t<d;++t)n=o[(e=s[t]).index].data,l?An(a,u,e,n):Dn(a,e,n);H.each(u,(function(t){i=t.pos.concat(t.neg),a.min=Math.min(a.min,H.min(i)),a.max=Math.max(a.max,H.max(i))})),a.min=H.isFinite(a.min)&&!isNaN(a.min)?a.min:0,a.max=H.isFinite(a.max)&&!isNaN(a.max)?a.max:1,a.handleTickRangeOptions()},_computeTickLimit:function(){var t;return this.isHorizontal()?Math.ceil(this.width/40):(t=H.options._parseFont(this.options.ticks),Math.ceil(this.height/t.lineHeight))},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){return this.getPixelForDecimal((+this.getRightValue(t)-this._startValue)/this._valueRange)},getValueForPixel:function(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange},getPixelForTick:function(t){var e=this.ticksAsNumbers;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])}}),In=Pn;Tn._defaults=In;var Fn=H.valueOrDefault,On=H.math.log10;var Ln={position:"left",ticks:{callback:on.formatters.logarithmic}};function Rn(t,e){return H.isFinite(t)&&t>=0?t:e}var zn=yn.extend({determineDataLimits:function(){var t,e,n,i,a,r,o=this,s=o.options,l=o.chart,u=l.data.datasets,d=o.isHorizontal();function h(t){return d?t.xAxisID===o.id:t.yAxisID===o.id}o.min=Number.POSITIVE_INFINITY,o.max=Number.NEGATIVE_INFINITY,o.minNotZero=Number.POSITIVE_INFINITY;var c=s.stacked;if(void 0===c)for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e)&&void 0!==e.stack){c=!0;break}if(s.stacked||c){var f={};for(t=0;t<u.length;t++){var g=[(e=l.getDatasetMeta(t)).type,void 0===s.stacked&&void 0===e.stack?t:"",e.stack].join(".");if(l.isDatasetVisible(t)&&h(e))for(void 0===f[g]&&(f[g]=[]),a=0,r=(i=u[t].data).length;a<r;a++){var p=f[g];n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(p[a]=p[a]||0,p[a]+=n.max)}}H.each(f,(function(t){if(t.length>0){var e=H.min(t),n=H.max(t);o.min=Math.min(o.min,e),o.max=Math.max(o.max,n)}}))}else for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e))for(a=0,r=(i=u[t].data).length;a<r;a++)n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(o.min=Math.min(n.min,o.min),o.max=Math.max(n.max,o.max),0!==n.min&&(o.minNotZero=Math.min(n.min,o.minNotZero)));o.min=H.isFinite(o.min)?o.min:null,o.max=H.isFinite(o.max)?o.max:null,o.minNotZero=H.isFinite(o.minNotZero)?o.minNotZero:null,this.handleTickRangeOptions()},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;t.min=Rn(e.min,t.min),t.max=Rn(e.max,t.max),t.min===t.max&&(0!==t.min&&null!==t.min?(t.min=Math.pow(10,Math.floor(On(t.min))-1),t.max=Math.pow(10,Math.floor(On(t.max))+1)):(t.min=1,t.max=10)),null===t.min&&(t.min=Math.pow(10,Math.floor(On(t.max))-1)),null===t.max&&(t.max=0!==t.min?Math.pow(10,Math.floor(On(t.min))+1):10),null===t.minNotZero&&(t.min>0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(On(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,n=!t.isHorizontal(),i={min:Rn(e.min),max:Rn(e.max)},a=t.ticks=function(t,e){var n,i,a=[],r=Fn(t.min,Math.pow(10,Math.floor(On(e.min)))),o=Math.floor(On(e.max)),s=Math.ceil(e.max/Math.pow(10,o));0===r?(n=Math.floor(On(e.minNotZero)),i=Math.floor(e.minNotZero/Math.pow(10,n)),a.push(r),r=i*Math.pow(10,n)):(n=Math.floor(On(r)),i=Math.floor(r/Math.pow(10,n)));var l=n<0?Math.pow(10,Math.abs(n)):1;do{a.push(r),10===++i&&(i=1,l=++n>=0?1:l),r=Math.round(i*Math.pow(10,n)*l)/l}while(n<o||n===o&&i<s);var u=Fn(t.max,r);return a.push(u),a}(i,t);t.max=H.max(a),t.min=H.min(a),e.reverse?(n=!n,t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max),n&&a.reverse()},convertTicksToLabels:function(){this.tickValues=this.ticks.slice(),yn.prototype.convertTicksToLabels.call(this)},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForTick:function(t){var e=this.tickValues;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])},_getFirstTickValue:function(t){var e=Math.floor(On(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},_configure:function(){var t=this,e=t.min,n=0;yn.prototype._configure.call(t),0===e&&(e=t._getFirstTickValue(t.minNotZero),n=Fn(t.options.ticks.fontSize,N.global.defaultFontSize)/t._length),t._startValue=On(e),t._valueOffset=n,t._valueRange=(On(t.max)-On(e))/(1-n)},getPixelForValue:function(t){var e=this,n=0;return(t=+e.getRightValue(t))>e.min&&t>0&&(n=(On(t)-e._startValue)/e._valueRange+e._valueOffset),e.getPixelForDecimal(n)},getValueForPixel:function(t){var e=this,n=e.getDecimalForPixel(t);return 0===n&&0===e.min?0:Math.pow(10,e._startValue+(n-e._valueOffset)*e._valueRange)}}),Nn=Ln;zn._defaults=Nn;var Bn=H.valueOrDefault,En=H.valueAtIndexOrDefault,Wn=H.options.resolve,Vn={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:on.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function Hn(t){var e=t.ticks;return e.display&&t.display?Bn(e.fontSize,N.global.defaultFontSize)+2*e.backdropPaddingY:0}function jn(t,e,n,i,a){return t===i||t===a?{start:e-n/2,end:e+n/2}:t<i||t>a?{start:e-n,end:e}:{start:e,end:e+n}}function qn(t){return 0===t||180===t?"center":t<180?"left":"right"}function Un(t,e,n,i){var a,r,o=n.y+i/2;if(H.isArray(e))for(a=0,r=e.length;a<r;++a)t.fillText(e[a],n.x,o),o+=i;else t.fillText(e,n.x,o)}function Yn(t,e,n){90===t||270===t?n.y-=e.h/2:(t>270||t<90)&&(n.y-=e.h)}function Gn(t){return H.isNumber(t)?t:0}var Xn=Cn.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Hn(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;H.each(e.data.datasets,(function(a,r){if(e.isDatasetVisible(r)){var o=e.getDatasetMeta(r);H.each(a.data,(function(e,a){var r=+t.getRightValue(e);isNaN(r)||o.data[a].hidden||(n=Math.min(r,n),i=Math.max(r,i))}))}})),t.min=n===Number.POSITIVE_INFINITY?0:n,t.max=i===Number.NEGATIVE_INFINITY?0:i,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Hn(this.options))},convertTicksToLabels:function(){var t=this;Cn.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map((function(){var e=H.callback(t.options.pointLabels.callback,arguments,t);return e||0===e?e:""}))},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this.options;t.display&&t.pointLabels.display?function(t){var e,n,i,a=H.options._parseFont(t.options.pointLabels),r={l:0,r:t.width,t:0,b:t.height-t.paddingTop},o={};t.ctx.font=a.string,t._pointLabelSizes=[];var s,l,u,d=t.chart.data.labels.length;for(e=0;e<d;e++){i=t.getPointPosition(e,t.drawingArea+5),s=t.ctx,l=a.lineHeight,u=t.pointLabels[e],n=H.isArray(u)?{w:H.longestText(s,s.font,u),h:u.length*l}:{w:s.measureText(u).width,h:l},t._pointLabelSizes[e]=n;var h=t.getIndexAngle(e),c=H.toDegrees(h)%360,f=jn(c,i.x,n.w,0,180),g=jn(c,i.y,n.h,90,270);f.start<r.l&&(r.l=f.start,o.l=h),f.end>r.r&&(r.r=f.end,o.r=h),g.start<r.t&&(r.t=g.start,o.t=h),g.end>r.b&&(r.b=g.end,o.b=h)}t.setReductions(t.drawingArea,r,o)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(t,e,n){var i=this,a=e.l/Math.sin(n.l),r=Math.max(e.r-i.width,0)/Math.sin(n.r),o=-e.t/Math.cos(n.t),s=-Math.max(e.b-(i.height-i.paddingTop),0)/Math.cos(n.b);a=Gn(a),r=Gn(r),o=Gn(o),s=Gn(s),i.drawingArea=Math.min(Math.floor(t-(a+r)/2),Math.floor(t-(o+s)/2)),i.setCenterPoint(a,r,o,s)},setCenterPoint:function(t,e,n,i){var a=this,r=a.width-e-a.drawingArea,o=t+a.drawingArea,s=n+a.drawingArea,l=a.height-a.paddingTop-i-a.drawingArea;a.xCenter=Math.floor((o+r)/2+a.left),a.yCenter=Math.floor((s+l)/2+a.top+a.paddingTop)},getIndexAngle:function(t){var e=this.chart,n=(t*(360/e.data.labels.length)+((e.options||{}).startAngle||0))%360;return(n<0?n+360:n)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(H.isNullOrUndef(t))return NaN;var n=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*n:(t-e.min)*n},getPointPosition:function(t,e){var n=this.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(n)*e+this.xCenter,y:Math.sin(n)*e+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(t){var e=this.min,n=this.max;return this.getPointPositionForValue(t||0,this.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0)},_drawGrid:function(){var t,e,n,i=this,a=i.ctx,r=i.options,o=r.gridLines,s=r.angleLines,l=Bn(s.lineWidth,o.lineWidth),u=Bn(s.color,o.color);if(r.pointLabels.display&&function(t){var e=t.ctx,n=t.options,i=n.pointLabels,a=Hn(n),r=t.getDistanceFromCenterForValue(n.ticks.reverse?t.min:t.max),o=H.options._parseFont(i);e.save(),e.font=o.string,e.textBaseline="middle";for(var s=t.chart.data.labels.length-1;s>=0;s--){var l=0===s?a/2:0,u=t.getPointPosition(s,r+l+5),d=En(i.fontColor,s,N.global.defaultFontColor);e.fillStyle=d;var h=t.getIndexAngle(s),c=H.toDegrees(h);e.textAlign=qn(c),Yn(c,t._pointLabelSizes[s],u),Un(e,t.pointLabels[s],u,o.lineHeight)}e.restore()}(i),o.display&&H.each(i.ticks,(function(t,n){0!==n&&(e=i.getDistanceFromCenterForValue(i.ticksAsNumbers[n]),function(t,e,n,i){var a,r=t.ctx,o=e.circular,s=t.chart.data.labels.length,l=En(e.color,i-1),u=En(e.lineWidth,i-1);if((o||s)&&l&&u){if(r.save(),r.strokeStyle=l,r.lineWidth=u,r.setLineDash&&(r.setLineDash(e.borderDash||[]),r.lineDashOffset=e.borderDashOffset||0),r.beginPath(),o)r.arc(t.xCenter,t.yCenter,n,0,2*Math.PI);else{a=t.getPointPosition(0,n),r.moveTo(a.x,a.y);for(var d=1;d<s;d++)a=t.getPointPosition(d,n),r.lineTo(a.x,a.y)}r.closePath(),r.stroke(),r.restore()}}(i,o,e,n))})),s.display&&l&&u){for(a.save(),a.lineWidth=l,a.strokeStyle=u,a.setLineDash&&(a.setLineDash(Wn([s.borderDash,o.borderDash,[]])),a.lineDashOffset=Wn([s.borderDashOffset,o.borderDashOffset,0])),t=i.chart.data.labels.length-1;t>=0;t--)e=i.getDistanceFromCenterForValue(r.ticks.reverse?i.min:i.max),n=i.getPointPosition(t,e),a.beginPath(),a.moveTo(i.xCenter,i.yCenter),a.lineTo(n.x,n.y),a.stroke();a.restore()}},_drawLabels:function(){var t=this,e=t.ctx,n=t.options.ticks;if(n.display){var i,a,r=t.getIndexAngle(0),o=H.options._parseFont(n),s=Bn(n.fontColor,N.global.defaultFontColor);e.save(),e.font=o.string,e.translate(t.xCenter,t.yCenter),e.rotate(r),e.textAlign="center",e.textBaseline="middle",H.each(t.ticks,(function(r,l){(0!==l||n.reverse)&&(i=t.getDistanceFromCenterForValue(t.ticksAsNumbers[l]),n.showLabelBackdrop&&(a=e.measureText(r).width,e.fillStyle=n.backdropColor,e.fillRect(-a/2-n.backdropPaddingX,-i-o.size/2-n.backdropPaddingY,a+2*n.backdropPaddingX,o.size+2*n.backdropPaddingY)),e.fillStyle=s,e.fillText(r,0,-i))})),e.restore()}},_drawTitle:H.noop}),Kn=Vn;Xn._defaults=Kn;var Zn=H._deprecated,$n=H.options.resolve,Jn=H.valueOrDefault,Qn=Number.MIN_SAFE_INTEGER||-9007199254740991,ti=Number.MAX_SAFE_INTEGER||9007199254740991,ei={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}},ni=Object.keys(ei);function ii(t,e){return t-e}function ai(t){return H.valueOrDefault(t.time.min,t.ticks.min)}function ri(t){return H.valueOrDefault(t.time.max,t.ticks.max)}function oi(t,e,n,i){var a=function(t,e,n){for(var i,a,r,o=0,s=t.length-1;o>=0&&o<=s;){if(a=t[(i=o+s>>1)-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]<n)o=i+1;else{if(!(a[e]>n))return{lo:a,hi:r};s=i-1}}return{lo:r,hi:null}}(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],o=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=o[e]-r[e],l=s?(n-r[e])/s:0,u=(o[i]-r[i])*l;return r[i]+u}function si(t,e){var n=t._adapter,i=t.options.time,a=i.parser,r=a||i.format,o=e;return"function"==typeof a&&(o=a(o)),H.isFinite(o)||(o="string"==typeof r?n.parse(o,r):n.parse(o)),null!==o?+o:(a||"function"!=typeof r||(o=r(e),H.isFinite(o)||(o=n.parse(o))),o)}function li(t,e){if(H.isNullOrUndef(e))return null;var n=t.options.time,i=si(t,t.getRightValue(e));return null===i?i:(n.round&&(i=+t._adapter.startOf(i,n.round)),i)}function ui(t,e,n,i){var a,r,o,s=ni.length;for(a=ni.indexOf(t);a<s-1;++a)if(o=(r=ei[ni[a]]).steps?r.steps:ti,r.common&&Math.ceil((n-e)/(o*r.size))<=i)return ni[a];return ni[s-1]}function di(t,e,n){var i,a,r=[],o={},s=e.length;for(i=0;i<s;++i)o[a=e[i]]=i,r.push({value:a,major:!1});return 0!==s&&n?function(t,e,n,i){var a,r,o=t._adapter,s=+o.startOf(e[0].value,i),l=e[e.length-1].value;for(a=s;a<=l;a=+o.add(a,1,i))(r=n[a])>=0&&(e[r].major=!0);return e}(t,r,o,n):r}var hi=yn.extend({initialize:function(){this.mergeTicksOptions(),yn.prototype.initialize.call(this)},update:function(){var t=this,e=t.options,n=e.time||(e.time={}),i=t._adapter=new rn._date(e.adapters.date);return Zn("time scale",n.format,"time.format","time.parser"),Zn("time scale",n.min,"time.min","ticks.min"),Zn("time scale",n.max,"time.max","ticks.max"),H.mergeIf(n.displayFormats,i.formats()),yn.prototype.update.apply(t,arguments)},getRightValue:function(t){return t&&void 0!==t.t&&(t=t.t),yn.prototype.getRightValue.call(this,t)},determineDataLimits:function(){var t,e,n,i,a,r,o,s=this,l=s.chart,u=s._adapter,d=s.options,h=d.time.unit||"day",c=ti,f=Qn,g=[],p=[],m=[],v=s._getLabels();for(t=0,n=v.length;t<n;++t)m.push(li(s,v[t]));for(t=0,n=(l.data.datasets||[]).length;t<n;++t)if(l.isDatasetVisible(t))if(a=l.data.datasets[t].data,H.isObject(a[0]))for(p[t]=[],e=0,i=a.length;e<i;++e)r=li(s,a[e]),g.push(r),p[t][e]=r;else p[t]=m.slice(0),o||(g=g.concat(m),o=!0);else p[t]=[];m.length&&(c=Math.min(c,m[0]),f=Math.max(f,m[m.length-1])),g.length&&(g=n>1?function(t){var e,n,i,a={},r=[];for(e=0,n=t.length;e<n;++e)a[i=t[e]]||(a[i]=!0,r.push(i));return r}(g).sort(ii):g.sort(ii),c=Math.min(c,g[0]),f=Math.max(f,g[g.length-1])),c=li(s,ai(d))||c,f=li(s,ri(d))||f,c=c===ti?+u.startOf(Date.now(),h):c,f=f===Qn?+u.endOf(Date.now(),h)+1:f,s.min=Math.min(c,f),s.max=Math.max(c+1,f),s._table=[],s._timestamps={data:g,datasets:p,labels:m}},buildTicks:function(){var t,e,n,i=this,a=i.min,r=i.max,o=i.options,s=o.ticks,l=o.time,u=i._timestamps,d=[],h=i.getLabelCapacity(a),c=s.source,f=o.distribution;for(u="data"===c||"auto"===c&&"series"===f?u.data:"labels"===c?u.labels:function(t,e,n,i){var a,r=t._adapter,o=t.options,s=o.time,l=s.unit||ui(s.minUnit,e,n,i),u=$n([s.stepSize,s.unitStepSize,1]),d="week"===l&&s.isoWeekday,h=e,c=[];if(d&&(h=+r.startOf(h,"isoWeek",d)),h=+r.startOf(h,d?"day":l),r.diff(n,e,l)>1e5*u)throw e+" and "+n+" are too far apart with stepSize of "+u+" "+l;for(a=h;a<n;a=+r.add(a,u,l))c.push(a);return a!==n&&"ticks"!==o.bounds||c.push(a),c}(i,a,r,h),"ticks"===o.bounds&&u.length&&(a=u[0],r=u[u.length-1]),a=li(i,ai(o))||a,r=li(i,ri(o))||r,t=0,e=u.length;t<e;++t)(n=u[t])>=a&&n<=r&&d.push(n);return i.min=a,i.max=r,i._unit=l.unit||(s.autoSkip?ui(l.minUnit,i.min,i.max,h):function(t,e,n,i,a){var r,o;for(r=ni.length-1;r>=ni.indexOf(n);r--)if(o=ni[r],ei[o].common&&t._adapter.diff(a,i,o)>=e-1)return o;return ni[n?ni.indexOf(n):0]}(i,d.length,l.minUnit,i.min,i.max)),i._majorUnit=s.major.enabled&&"year"!==i._unit?function(t){for(var e=ni.indexOf(t)+1,n=ni.length;e<n;++e)if(ei[ni[e]].common)return ni[e]}(i._unit):void 0,i._table=function(t,e,n,i){if("linear"===i||!t.length)return[{time:e,pos:0},{time:n,pos:1}];var a,r,o,s,l,u=[],d=[e];for(a=0,r=t.length;a<r;++a)(s=t[a])>e&&s<n&&d.push(s);for(d.push(n),a=0,r=d.length;a<r;++a)l=d[a+1],o=d[a-1],s=d[a],void 0!==o&&void 0!==l&&Math.round((l+o)/2)===s||u.push({time:s,pos:a/(r-1)});return u}(i._timestamps.data,a,r,f),i._offsets=function(t,e,n,i,a){var r,o,s=0,l=0;return a.offset&&e.length&&(r=oi(t,"time",e[0],"pos"),s=1===e.length?1-r:(oi(t,"time",e[1],"pos")-r)/2,o=oi(t,"time",e[e.length-1],"pos"),l=1===e.length?o:(o-oi(t,"time",e[e.length-2],"pos"))/2),{start:s,end:l,factor:1/(s+1+l)}}(i._table,d,0,0,o),s.reverse&&d.reverse(),di(i,d,i._majorUnit)},getLabelForIndex:function(t,e){var n=this,i=n._adapter,a=n.chart.data,r=n.options.time,o=a.labels&&t<a.labels.length?a.labels[t]:"",s=a.datasets[e].data[t];return H.isObject(s)&&(o=n.getRightValue(s)),r.tooltipFormat?i.format(si(n,o),r.tooltipFormat):"string"==typeof o?o:i.format(si(n,o),r.displayFormats.datetime)},tickFormatFunction:function(t,e,n,i){var a=this._adapter,r=this.options,o=r.time.displayFormats,s=o[this._unit],l=this._majorUnit,u=o[l],d=n[e],h=r.ticks,c=l&&u&&d&&d.major,f=a.format(t,i||(c?u:s)),g=c?h.major:h.minor,p=$n([g.callback,g.userCallback,h.callback,h.userCallback]);return p?p(f,e,n):f},convertTicksToLabels:function(t){var e,n,i=[];for(e=0,n=t.length;e<n;++e)i.push(this.tickFormatFunction(t[e].value,e,t));return i},getPixelForOffset:function(t){var e=this._offsets,n=oi(this._table,"time",t,"pos");return this.getPixelForDecimal((e.start+n)*e.factor)},getPixelForValue:function(t,e,n){var i=null;if(void 0!==e&&void 0!==n&&(i=this._timestamps.datasets[n][e]),null===i&&(i=li(this,t)),null!==i)return this.getPixelForOffset(i)},getPixelForTick:function(t){var e=this.getTicks();return t>=0&&t<e.length?this.getPixelForOffset(e[t].value):null},getValueForPixel:function(t){var e=this._offsets,n=this.getDecimalForPixel(t)/e.factor-e.end,i=oi(this._table,"pos",n,"time");return this._adapter._create(i)},_getLabelSize:function(t){var e=this.options.ticks,n=this.ctx.measureText(t).width,i=H.toRadians(this.isHorizontal()?e.maxRotation:e.minRotation),a=Math.cos(i),r=Math.sin(i),o=Jn(e.fontSize,N.global.defaultFontSize);return{w:n*a+o*r,h:n*r+o*a}},getLabelWidth:function(t){return this._getLabelSize(t).w},getLabelCapacity:function(t){var e=this,n=e.options.time,i=n.displayFormats,a=i[n.unit]||i.millisecond,r=e.tickFormatFunction(t,0,di(e,[t],e._majorUnit),a),o=e._getLabelSize(r),s=Math.floor(e.isHorizontal()?e.width/o.w:e.height/o.h);return e.options.offset&&s--,s>0?s:1}}),ci={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};hi._defaults=ci;var fi={category:kn,linear:Tn,logarithmic:zn,radialLinear:Xn,time:hi},gi={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};rn._date.override("function"==typeof t?{_id:"moment",formats:function(){return gi},parse:function(e,n){return"string"==typeof e&&"string"==typeof n?e=t(e,n):e instanceof t||(e=t(e)),e.isValid()?e.valueOf():null},format:function(e,n){return t(e).format(n)},add:function(e,n,i){return t(e).add(n,i).valueOf()},diff:function(e,n,i){return t(e).diff(t(n),i)},startOf:function(e,n,i){return e=t(e),"isoWeek"===n?e.isoWeekday(i).valueOf():e.startOf(n).valueOf()},endOf:function(e,n){return t(e).endOf(n).valueOf()},_create:function(e){return t(e)}}:{}),N._set("global",{plugins:{filler:{propagate:!0}}});var pi={dataset:function(t){var e=t.fill,n=t.chart,i=n.getDatasetMeta(e),a=i&&n.isDatasetVisible(e)&&i.dataset._children||[],r=a.length||0;return r?function(t,e){return e<r&&a[e]._view||null}:null},boundary:function(t){var e=t.boundary,n=e?e.x:null,i=e?e.y:null;return H.isArray(e)?function(t,n){return e[n]}:function(t){return{x:null===n?t.x:n,y:null===i?t.y:i}}}};function mi(t,e,n){var i,a=t._model||{},r=a.fill;if(void 0===r&&(r=!!a.backgroundColor),!1===r||null===r)return!1;if(!0===r)return"origin";if(i=parseFloat(r,10),isFinite(i)&&Math.floor(i)===i)return"-"!==r[0]&&"+"!==r[0]||(i=e+i),!(i===e||i<0||i>=n)&&i;switch(r){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return r;default:return!1}}function vi(t){return(t.el._scale||{}).getPointPositionForValue?function(t){var e,n,i,a,r,o=t.el._scale,s=o.options,l=o.chart.data.labels.length,u=t.fill,d=[];if(!l)return null;for(e=s.ticks.reverse?o.max:o.min,n=s.ticks.reverse?o.min:o.max,i=o.getPointPositionForValue(0,e),a=0;a<l;++a)r="start"===u||"end"===u?o.getPointPositionForValue(a,"start"===u?e:n):o.getBasePosition(a),s.gridLines.circular&&(r.cx=i.x,r.cy=i.y,r.angle=o.getIndexAngle(a)-Math.PI/2),d.push(r);return d}(t):function(t){var e,n=t.el._model||{},i=t.el._scale||{},a=t.fill,r=null;if(isFinite(a))return null;if("start"===a?r=void 0===n.scaleBottom?i.bottom:n.scaleBottom:"end"===a?r=void 0===n.scaleTop?i.top:n.scaleTop:void 0!==n.scaleZero?r=n.scaleZero:i.getBasePixel&&(r=i.getBasePixel()),null!=r){if(void 0!==r.x&&void 0!==r.y)return r;if(H.isFinite(r))return{x:(e=i.isHorizontal())?r:null,y:e?null:r}}return null}(t)}function bi(t,e,n){var i,a=t[e].fill,r=[e];if(!n)return a;for(;!1!==a&&-1===r.indexOf(a);){if(!isFinite(a))return a;if(!(i=t[a]))return!1;if(i.visible)return a;r.push(a),a=i.fill}return!1}function xi(t){var e=t.fill,n="dataset";return!1===e?null:(isFinite(e)||(n="boundary"),pi[n](t))}function yi(t){return t&&!t.skip}function _i(t,e,n,i,a){var r,o,s,l;if(i&&a){for(t.moveTo(e[0].x,e[0].y),r=1;r<i;++r)H.canvas.lineTo(t,e[r-1],e[r]);if(void 0===n[0].angle)for(t.lineTo(n[a-1].x,n[a-1].y),r=a-1;r>0;--r)H.canvas.lineTo(t,n[r],n[r-1],!0);else for(o=n[0].cx,s=n[0].cy,l=Math.sqrt(Math.pow(n[0].x-o,2)+Math.pow(n[0].y-s,2)),r=a-1;r>0;--r)t.arc(o,s,l,n[r].angle,n[r-1].angle,!0)}}function ki(t,e,n,i,a,r){var o,s,l,u,d,h,c,f,g=e.length,p=i.spanGaps,m=[],v=[],b=0,x=0;for(t.beginPath(),o=0,s=g;o<s;++o)d=n(u=e[l=o%g]._view,l,i),h=yi(u),c=yi(d),r&&void 0===f&&h&&(s=g+(f=o+1)),h&&c?(b=m.push(u),x=v.push(d)):b&&x&&(p?(h&&m.push(u),c&&v.push(d)):(_i(t,m,v,b,x),b=x=0,m=[],v=[]));_i(t,m,v,b,x),t.closePath(),t.fillStyle=a,t.fill()}var wi={id:"filler",afterDatasetsUpdate:function(t,e){var n,i,a,r,o=(t.data.datasets||[]).length,s=e.propagate,l=[];for(i=0;i<o;++i)r=null,(a=(n=t.getDatasetMeta(i)).dataset)&&a._model&&a instanceof kt.Line&&(r={visible:t.isDatasetVisible(i),fill:mi(a,i,o),chart:t,el:a}),n.$filler=r,l.push(r);for(i=0;i<o;++i)(r=l[i])&&(r.fill=bi(l,i,s),r.boundary=vi(r),r.mapper=xi(r))},beforeDatasetsDraw:function(t){var e,n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas(),u=t.ctx;for(n=l.length-1;n>=0;--n)(e=l[n].$filler)&&e.visible&&(a=(i=e.el)._view,r=i._children||[],o=e.mapper,s=a.backgroundColor||N.global.defaultColor,o&&s&&r.length&&(H.canvas.clipArea(u,t.chartArea),ki(u,r,o,a,s,i._loop),H.canvas.unclipArea(u)))}},Mi=H.rtl.getRtlAdapter,Si=H.noop,Ci=H.valueOrDefault;function Pi(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}N._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var n=e.datasetIndex,i=this.chart,a=i.getDatasetMeta(n);a.hidden=null===a.hidden?!i.data.datasets[n].hidden:null,i.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data.datasets,n=t.options.legend||{},i=n.labels&&n.labels.usePointStyle;return t._getSortedDatasetMetas().map((function(n){var a=n.controller.getStyle(i?0:void 0);return{text:e[n.index].label,fillStyle:a.backgroundColor,hidden:!t.isDatasetVisible(n.index),lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:a.borderWidth,strokeStyle:a.borderColor,pointStyle:a.pointStyle,rotation:a.rotation,datasetIndex:n.index}}),this)}}},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data.datasets;for(a.setAttribute("class",t.id+"-legend"),e=0,n=r.length;e<n;e++)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=r[e].backgroundColor,r[e].label&&i.appendChild(document.createTextNode(r[e].label));return a.outerHTML}});var Ai=K.extend({initialize:function(t){H.extend(this,t),this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1},beforeUpdate:Si,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Si,beforeSetDimensions:Si,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Si,beforeBuildLabels:Si,buildLabels:function(){var t=this,e=t.options.labels||{},n=H.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(n=n.filter((function(n){return e.filter(n,t.chart.data)}))),t.options.reverse&&n.reverse(),t.legendItems=n},afterBuildLabels:Si,beforeFit:Si,fit:function(){var t=this,e=t.options,n=e.labels,i=e.display,a=t.ctx,r=H.options._parseFont(n),o=r.size,s=t.legendHitBoxes=[],l=t.minSize,u=t.isHorizontal();if(u?(l.width=t.maxWidth,l.height=i?10:0):(l.width=i?10:0,l.height=t.maxHeight),i){if(a.font=r.string,u){var d=t.lineWidths=[0],h=0;a.textAlign="left",a.textBaseline="middle",H.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;(0===e||d[d.length-1]+i+2*n.padding>l.width)&&(h+=o+n.padding,d[d.length-(e>0?0:1)]=0),s[e]={left:0,top:0,width:i,height:o},d[d.length-1]+=i+n.padding})),l.height+=h}else{var c=n.padding,f=t.columnWidths=[],g=t.columnHeights=[],p=n.padding,m=0,v=0;H.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;e>0&&v+o+2*c>l.height&&(p+=m+n.padding,f.push(m),g.push(v),m=0,v=0),m=Math.max(m,i),v+=o+c,s[e]={left:0,top:0,width:i,height:o}})),p+=m,f.push(m),g.push(v),l.width+=p}t.width=l.width,t.height=l.height}else t.width=l.width=t.height=l.height=0},afterFit:Si,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,n=e.labels,i=N.global,a=i.defaultColor,r=i.elements.line,o=t.height,s=t.columnHeights,l=t.width,u=t.lineWidths;if(e.display){var d,h=Mi(e.rtl,t.left,t.minSize.width),c=t.ctx,f=Ci(n.fontColor,i.defaultFontColor),g=H.options._parseFont(n),p=g.size;c.textAlign=h.textAlign("left"),c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=g.string;var m=Pi(n,p),v=t.legendHitBoxes,b=function(t,i){switch(e.align){case"start":return n.padding;case"end":return t-i;default:return(t-i+n.padding)/2}},x=t.isHorizontal();d=x?{x:t.left+b(l,u[0]),y:t.top+n.padding,line:0}:{x:t.left+n.padding,y:t.top+b(o,s[0]),line:0},H.rtl.overrideTextDirection(t.ctx,e.textDirection);var y=p+n.padding;H.each(t.legendItems,(function(e,i){var f=c.measureText(e.text).width,g=m+p/2+f,_=d.x,k=d.y;h.setWidth(t.minSize.width),x?i>0&&_+g+n.padding>t.left+t.minSize.width&&(k=d.y+=y,d.line++,_=d.x=t.left+b(l,u[d.line])):i>0&&k+y>t.top+t.minSize.height&&(_=d.x=_+t.columnWidths[d.line]+n.padding,d.line++,k=d.y=t.top+b(o,s[d.line]));var w=h.x(_);!function(t,e,i){if(!(isNaN(m)||m<=0)){c.save();var o=Ci(i.lineWidth,r.borderWidth);if(c.fillStyle=Ci(i.fillStyle,a),c.lineCap=Ci(i.lineCap,r.borderCapStyle),c.lineDashOffset=Ci(i.lineDashOffset,r.borderDashOffset),c.lineJoin=Ci(i.lineJoin,r.borderJoinStyle),c.lineWidth=o,c.strokeStyle=Ci(i.strokeStyle,a),c.setLineDash&&c.setLineDash(Ci(i.lineDash,r.borderDash)),n&&n.usePointStyle){var s=m*Math.SQRT2/2,l=h.xPlus(t,m/2),u=e+p/2;H.canvas.drawPoint(c,i.pointStyle,s,l,u,i.rotation)}else c.fillRect(h.leftForLtr(t,m),e,m,p),0!==o&&c.strokeRect(h.leftForLtr(t,m),e,m,p);c.restore()}}(w,k,e),v[i].left=h.leftForLtr(w,v[i].width),v[i].top=k,function(t,e,n,i){var a=p/2,r=h.xPlus(t,m+a),o=e+a;c.fillText(n.text,r,o),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(r,o),c.lineTo(h.xPlus(r,i),o),c.stroke())}(w,k,e,f),x?d.x+=g+n.padding:d.y+=y})),H.rtl.restoreTextDirection(t.ctx,e.textDirection)}},_getLegendItemAt:function(t,e){var n,i,a,r=this;if(t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom)for(a=r.legendHitBoxes,n=0;n<a.length;++n)if(t>=(i=a[n]).left&&t<=i.left+i.width&&e>=i.top&&e<=i.top+i.height)return r.legendItems[n];return null},handleEvent:function(t){var e,n=this,i=n.options,a="mouseup"===t.type?"click":t.type;if("mousemove"===a){if(!i.onHover&&!i.onLeave)return}else{if("click"!==a)return;if(!i.onClick)return}e=n._getLegendItemAt(t.x,t.y),"click"===a?e&&i.onClick&&i.onClick.call(n,t.native,e):(i.onLeave&&e!==n._hoveredItem&&(n._hoveredItem&&i.onLeave.call(n,t.native,n._hoveredItem),n._hoveredItem=e),i.onHover&&e&&i.onHover.call(n,t.native,e))}});function Di(t,e){var n=new Ai({ctx:t.ctx,options:e,chart:t});pe.configure(t,n,e),pe.addBox(t,n),t.legend=n}var Ti={id:"legend",_element:Ai,beforeInit:function(t){var e=t.options.legend;e&&Di(t,e)},beforeUpdate:function(t){var e=t.options.legend,n=t.legend;e?(H.mergeIf(e,N.global.legend),n?(pe.configure(t,n,e),n.options=e):Di(t,e)):n&&(pe.removeBox(t,n),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}},Ii=H.noop;N._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var Fi=K.extend({initialize:function(t){H.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:Ii,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Ii,beforeSetDimensions:Ii,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Ii,beforeBuildLabels:Ii,buildLabels:Ii,afterBuildLabels:Ii,beforeFit:Ii,fit:function(){var t,e=this,n=e.options,i=e.minSize={},a=e.isHorizontal();n.display?(t=(H.isArray(n.text)?n.text.length:1)*H.options._parseFont(n).lineHeight+2*n.padding,e.width=i.width=a?e.maxWidth:t,e.height=i.height=a?t:e.maxHeight):e.width=i.width=e.height=i.height=0},afterFit:Ii,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=t.options;if(n.display){var i,a,r,o=H.options._parseFont(n),s=o.lineHeight,l=s/2+n.padding,u=0,d=t.top,h=t.left,c=t.bottom,f=t.right;e.fillStyle=H.valueOrDefault(n.fontColor,N.global.defaultFontColor),e.font=o.string,t.isHorizontal()?(a=h+(f-h)/2,r=d+l,i=f-h):(a="left"===n.position?h+l:f-l,r=d+(c-d)/2,i=c-d,u=Math.PI*("left"===n.position?-.5:.5)),e.save(),e.translate(a,r),e.rotate(u),e.textAlign="center",e.textBaseline="middle";var g=n.text;if(H.isArray(g))for(var p=0,m=0;m<g.length;++m)e.fillText(g[m],0,p,i),p+=s;else e.fillText(g,0,0,i);e.restore()}}});function Oi(t,e){var n=new Fi({ctx:t.ctx,options:e,chart:t});pe.configure(t,n,e),pe.addBox(t,n),t.titleBlock=n}var Li={},Ri=wi,zi=Ti,Ni={id:"title",_element:Fi,beforeInit:function(t){var e=t.options.title;e&&Oi(t,e)},beforeUpdate:function(t){var e=t.options.title,n=t.titleBlock;e?(H.mergeIf(e,N.global.title),n?(pe.configure(t,n,e),n.options=e):Oi(t,e)):n&&(pe.removeBox(t,n),delete t.titleBlock)}};for(var Bi in Li.filler=Ri,Li.legend=zi,Li.title=Ni,en.helpers=H,function(){function t(t,e,n){var i;return"string"==typeof t?(i=parseInt(t,10),-1!==t.indexOf("%")&&(i=i/100*e.parentNode[n])):i=t,i}function e(t){return null!=t&&"none"!==t}function n(n,i,a){var r=document.defaultView,o=H._getParentNode(n),s=r.getComputedStyle(n)[i],l=r.getComputedStyle(o)[i],u=e(s),d=e(l),h=Number.POSITIVE_INFINITY;return u||d?Math.min(u?t(s,n,a):h,d?t(l,o,a):h):"none"}H.where=function(t,e){if(H.isArray(t)&&Array.prototype.filter)return t.filter(e);var n=[];return H.each(t,(function(t){e(t)&&n.push(t)})),n},H.findIndex=Array.prototype.findIndex?function(t,e,n){return t.findIndex(e,n)}:function(t,e,n){n=void 0===n?t:n;for(var i=0,a=t.length;i<a;++i)if(e.call(n,t[i],i,t))return i;return-1},H.findNextWhere=function(t,e,n){H.isNullOrUndef(n)&&(n=-1);for(var i=n+1;i<t.length;i++){var a=t[i];if(e(a))return a}},H.findPreviousWhere=function(t,e,n){H.isNullOrUndef(n)&&(n=t.length);for(var i=n-1;i>=0;i--){var a=t[i];if(e(a))return a}},H.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},H.almostEquals=function(t,e,n){return Math.abs(t-e)<n},H.almostWhole=function(t,e){var n=Math.round(t);return n-e<=t&&n+e>=t},H.max=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.max(t,e)}),Number.NEGATIVE_INFINITY)},H.min=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.min(t,e)}),Number.POSITIVE_INFINITY)},H.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0===(t=+t)||isNaN(t)?t:t>0?1:-1},H.toRadians=function(t){return t*(Math.PI/180)},H.toDegrees=function(t){return t*(180/Math.PI)},H._decimalPlaces=function(t){if(H.isFinite(t)){for(var e=1,n=0;Math.round(t*e)/e!==t;)e*=10,n++;return n}},H.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},H.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},H.aliasPixel=function(t){return t%2==0?0:.5},H._alignPixel=function(t,e,n){var i=t.currentDevicePixelRatio,a=n/2;return Math.round((e-a)*i)/i+a},H.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,s=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),l=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=s/(s+l),d=l/(s+l),h=i*(u=isNaN(u)?0:u),c=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-h*(o.x-a.x),y:r.y-h*(o.y-a.y)},next:{x:r.x+c*(o.x-a.x),y:r.y+c*(o.y-a.y)}}},H.EPSILON=Number.EPSILON||1e-14,H.splineCurveMonotone=function(t){var e,n,i,a,r,o,s,l,u,d=(t||[]).map((function(t){return{model:t._model,deltaK:0,mK:0}})),h=d.length;for(e=0;e<h;++e)if(!(i=d[e]).model.skip){if(n=e>0?d[e-1]:null,(a=e<h-1?d[e+1]:null)&&!a.model.skip){var c=a.model.x-i.model.x;i.deltaK=0!==c?(a.model.y-i.model.y)/c:0}!n||n.model.skip?i.mK=i.deltaK:!a||a.model.skip?i.mK=n.deltaK:this.sign(n.deltaK)!==this.sign(i.deltaK)?i.mK=0:i.mK=(n.deltaK+i.deltaK)/2}for(e=0;e<h-1;++e)i=d[e],a=d[e+1],i.model.skip||a.model.skip||(H.almostEquals(i.deltaK,0,this.EPSILON)?i.mK=a.mK=0:(r=i.mK/i.deltaK,o=a.mK/i.deltaK,(l=Math.pow(r,2)+Math.pow(o,2))<=9||(s=3/Math.sqrt(l),i.mK=r*s*i.deltaK,a.mK=o*s*i.deltaK)));for(e=0;e<h;++e)(i=d[e]).model.skip||(n=e>0?d[e-1]:null,a=e<h-1?d[e+1]:null,n&&!n.model.skip&&(u=(i.model.x-n.model.x)/3,i.model.controlPointPreviousX=i.model.x-u,i.model.controlPointPreviousY=i.model.y-u*i.mK),a&&!a.model.skip&&(u=(a.model.x-i.model.x)/3,i.model.controlPointNextX=i.model.x+u,i.model.controlPointNextY=i.model.y+u*i.mK))},H.nextItem=function(t,e,n){return n?e>=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},H.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},H.niceNum=function(t,e){var n=Math.floor(H.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},H.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},H.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,r=t.target||t.srcElement,o=r.getBoundingClientRect(),s=a.touches;s&&s.length>0?(n=s[0].clientX,i=s[0].clientY):(n=a.clientX,i=a.clientY);var l=parseFloat(H.getStyle(r,"padding-left")),u=parseFloat(H.getStyle(r,"padding-top")),d=parseFloat(H.getStyle(r,"padding-right")),h=parseFloat(H.getStyle(r,"padding-bottom")),c=o.right-o.left-l-d,f=o.bottom-o.top-u-h;return{x:n=Math.round((n-o.left-l)/c*r.width/e.currentDevicePixelRatio),y:i=Math.round((i-o.top-u)/f*r.height/e.currentDevicePixelRatio)}},H.getConstraintWidth=function(t){return n(t,"max-width","clientWidth")},H.getConstraintHeight=function(t){return n(t,"max-height","clientHeight")},H._calculatePadding=function(t,e,n){return(e=H.getStyle(t,e)).indexOf("%")>-1?n*parseInt(e,10)/100:parseInt(e,10)},H._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},H.getMaximumWidth=function(t){var e=H._getParentNode(t);if(!e)return t.clientWidth;var n=e.clientWidth,i=n-H._calculatePadding(e,"padding-left",n)-H._calculatePadding(e,"padding-right",n),a=H.getConstraintWidth(t);return isNaN(a)?i:Math.min(i,a)},H.getMaximumHeight=function(t){var e=H._getParentNode(t);if(!e)return t.clientHeight;var n=e.clientHeight,i=n-H._calculatePadding(e,"padding-top",n)-H._calculatePadding(e,"padding-bottom",n),a=H.getConstraintHeight(t);return isNaN(a)?i:Math.min(i,a)},H.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},H.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height||i.style.width||(i.style.height=a+"px",i.style.width=r+"px")}},H.fontString=function(t,e,n){return e+" "+t+"px "+n},H.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},r=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},r=i.garbageCollect=[],i.font=e),t.font=e;var o,s,l,u,d,h=0,c=n.length;for(o=0;o<c;o++)if(null!=(u=n[o])&&!0!==H.isArray(u))h=H.measureText(t,a,r,h,u);else if(H.isArray(u))for(s=0,l=u.length;s<l;s++)null==(d=u[s])||H.isArray(d)||(h=H.measureText(t,a,r,h,d));var f=r.length/2;if(f>n.length){for(o=0;o<f;o++)delete a[r[o]];r.splice(0,f)}return h},H.measureText=function(t,e,n,i,a){var r=e[a];return r||(r=e[a]=t.measureText(a).width,n.push(a)),r>i&&(i=r),i},H.numberOfLabelLines=function(t){var e=1;return H.each(t,(function(t){H.isArray(t)&&t.length>e&&(e=t.length)})),e},H.color=_?function(t){return t instanceof CanvasGradient&&(t=N.global.defaultColor),_(t)}:function(t){return console.error("Color.js not found!"),t},H.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:H.color(t).saturate(.5).darken(.1).rgbString()}}(),en._adapters=rn,en.Animation=$,en.animationService=J,en.controllers=Jt,en.DatasetController=it,en.defaults=N,en.Element=K,en.elements=kt,en.Interaction=re,en.layouts=pe,en.platform=Oe,en.plugins=Le,en.Scale=yn,en.scaleService=Re,en.Ticks=on,en.Tooltip=Ye,en.helpers.each(fi,(function(t,e){en.scaleService.registerScaleType(e,t,t._defaults)})),Li)Li.hasOwnProperty(Bi)&&en.plugins.register(Li[Bi]);en.platform.initialize();var Ei=en;return"undefined"!=typeof window&&(window.Chart=en),en.Chart=en,en.Legend=Li.legend._element,en.Title=Li.title._element,en.pluginService=en.plugins,en.PluginBase=en.Element.extend({}),en.canvasHelpers=en.helpers.canvas,en.layoutService=en.layouts,en.LinearScaleBase=Cn,en.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],(function(t){en[t]=function(e,n){return new en(e,en.helpers.merge(n||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}})),Ei}));
css/block-editor.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .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}
includes/class-admin.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Admin class.
8
+ *
9
+ * @class Post_Views_Counter_Admin
10
+ */
11
+ class Post_Views_Counter_Admin {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @return void
17
+ */
18
+ public function __construct() {
19
+ // actions
20
+ add_action( 'plugins_loaded', [ $this, 'init_block_editor' ] );
21
+ }
22
+
23
+ /**
24
+ * Init block editor actions.
25
+ *
26
+ * @return void
27
+ */
28
+ public function init_block_editor() {
29
+ add_action( 'rest_api_init', [ $this, 'block_editor_rest_api_init' ] );
30
+ add_action( 'enqueue_block_editor_assets', [ $this, 'block_editor_enqueue_scripts' ] );
31
+ }
32
+
33
+ /**
34
+ * Register REST API block editor endpoints.
35
+ *
36
+ * @return void
37
+ */
38
+ public function block_editor_rest_api_init() {
39
+ // get views route
40
+ register_rest_route(
41
+ 'post-views-counter',
42
+ '/update-post-views/',
43
+ [
44
+ 'methods' => [ 'POST' ],
45
+ 'callback' => [ $this, 'block_editor_update_callback' ],
46
+ 'permission_callback' => [ $this, 'check_rest_route_permissions' ],
47
+ 'args' => [
48
+ 'id' => [
49
+ 'sanitize_callback' => 'absint',
50
+ ]
51
+ ]
52
+ ]
53
+ );
54
+ }
55
+
56
+ /**
57
+ * Check whether user has permissions to perform post views update in block editor.
58
+ *
59
+ * @param object $request WP_REST_Request
60
+ * @return bool|WP_Error
61
+ */
62
+ public function check_rest_route_permissions( $request ) {
63
+ // break if current user can't edit this post
64
+ if ( ! current_user_can( 'edit_post', (int) $request->get_param( 'id' ) ) )
65
+ return new WP_Error( 'pvc-user-not-allowed', __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
66
+
67
+ // break if views editing is restricted
68
+ if ( (bool) Post_Views_Counter()->options['general']['restrict_edit_views'] === true && ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) )
69
+ return new WP_Error( 'pvc-user-not-allowed', __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
70
+
71
+ return true;
72
+ }
73
+
74
+ /**
75
+ * REST API callback for block editor endpoint.
76
+ *
77
+ * @param array $data
78
+ * @return string|int
79
+ */
80
+ public function block_editor_update_callback( $data ) {
81
+ // cast post ID
82
+ $post_id = ! empty( $data['id'] ) ? (int) $data['id'] : 0;
83
+
84
+ // cast post views
85
+ $post_views = ! empty( $data['post_views'] ) ? (int) $data['post_views'] : 0;
86
+
87
+ // get countable post types
88
+ $post_types = Post_Views_Counter()->options['general']['post_types_count'];
89
+
90
+ // check if post exists
91
+ $post = get_post( $post_id );
92
+
93
+ // whether to count this post type or not
94
+ if ( empty( $post_types ) || empty( $post ) || ! in_array( $post->post_type, $post_types, true ) )
95
+ return wp_send_json_error( __( 'Invalid post ID.', 'post-views-counter' ) );
96
+
97
+ // break if current user can't edit this post
98
+ if ( ! current_user_can( 'edit_post', $post_id ) )
99
+ return wp_send_json_error( __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
100
+
101
+ // break if views editing is restricted
102
+ if ( (bool) Post_Views_Counter()->options['general']['restrict_edit_views'] === true && ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) )
103
+ return wp_send_json_error( __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
104
+
105
+ // update post views
106
+ pvc_update_post_views( $post_id, $post_views );
107
+
108
+ do_action( 'pvc_after_update_post_views_count', $post_id );
109
+
110
+ return $post_id;
111
+ }
112
+
113
+ /**
114
+ * Enqueue frontend and editor JavaScript and CSS.
115
+ *
116
+ * @return void
117
+ */
118
+ public function block_editor_enqueue_scripts() {
119
+ global $pagenow, $wp_version;
120
+
121
+ // skip widgets and customizer pages
122
+ if ( $pagenow === 'widgets.php' || $pagenow === 'customize.php' )
123
+ return;
124
+
125
+ // enqueue the bundled block JS file
126
+ wp_enqueue_script( 'pvc-block-editor', POST_VIEWS_COUNTER_URL . '/js/block-editor.min.js', [ 'wp-element', 'wp-components', 'wp-edit-post', 'wp-data', 'wp-plugins' ], Post_Views_Counter()->defaults['version'] );
127
+
128
+ // restrict editing
129
+ $restrict = (bool) Post_Views_Counter()->options['general']['restrict_edit_views'];
130
+
131
+ wp_localize_script(
132
+ 'pvc-block-editor',
133
+ 'pvcEditorArgs',
134
+ [
135
+ 'postID' => get_the_ID(),
136
+ 'postViews' => pvc_get_post_views( get_the_ID() ),
137
+ 'canEdit' => ( $restrict === false || ( $restrict === true && current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) ) ),
138
+ 'nonce' => wp_create_nonce( 'wp_rest' ),
139
+ 'wpGreater53' => version_compare( $wp_version, '5.3', '>=' ),
140
+ 'textPostViews' => __( 'Post Views', 'post-views-counter' ),
141
+ 'textHelp' => __( 'Adjust the views count for this post.', 'post-views-counter' ),
142
+ 'textCancel' => __( 'Cancel', 'post-views-counter' )
143
+ ]
144
+ );
145
+
146
+ // enqueue frontend and editor block styles
147
+ wp_enqueue_style( 'pvc-block-editor', POST_VIEWS_COUNTER_URL . '/css/block-editor.min.css', '', Post_Views_Counter()->defaults['version'] );
148
+ }
149
+ }
includes/class-columns.php ADDED
@@ -0,0 +1,499 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Columns class.
8
+ *
9
+ * @class Post_Views_Counter_Columns
10
+ */
11
+ class Post_Views_Counter_Columns {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @return void
17
+ */
18
+ public function __construct() {
19
+ // actions
20
+ add_action( 'admin_init', [ $this, 'register_new_column' ] );
21
+ add_action( 'post_submitbox_misc_actions', [ $this, 'submitbox_views' ] );
22
+ add_action( 'attachment_submitbox_misc_actions', [ $this, 'submitbox_views' ] );
23
+ add_action( 'save_post', [ $this, 'save_post' ], 10, 2 );
24
+ add_action( 'edit_attachment', [ $this, 'save_post' ], 10 );
25
+ add_action( 'bulk_edit_custom_box', [ $this, 'quick_edit_custom_box' ], 10, 2 );
26
+ add_action( 'quick_edit_custom_box', [ $this, 'quick_edit_custom_box' ], 10, 2 );
27
+ add_action( 'wp_ajax_save_bulk_post_views', [ $this, 'save_bulk_post_views' ] );
28
+ add_action( 'admin_bar_menu', [ $this, 'admin_bar_menu' ], 100 );
29
+ add_action( 'wp', [ $this, 'admin_bar_maybe_add_style' ] );
30
+ add_action( 'admin_init', [ $this, 'admin_bar_maybe_add_style' ] );
31
+ }
32
+
33
+ /**
34
+ * Output post views for single post.
35
+ *
36
+ * @global object $post
37
+ * @return void
38
+ */
39
+ public function submitbox_views() {
40
+ global $post;
41
+
42
+ // incorrect post type?
43
+ if ( ! in_array( $post->post_type, (array) Post_Views_Counter()->options['general']['post_types_count'] ) )
44
+ return;
45
+
46
+ // break if current user can't edit this post
47
+ if ( ! current_user_can( 'edit_post', $post->ID ) )
48
+ return;
49
+
50
+ // get total post views
51
+ $count = (int) pvc_get_post_views( $post->ID ); ?>
52
+
53
+ <div class="misc-pub-section" id="post-views">
54
+
55
+ <?php wp_nonce_field( 'post_views_count', 'pvc_nonce' ); ?>
56
+
57
+ <span id="post-views-display">
58
+ <?php echo __( 'Post Views', 'post-views-counter' ) . ': <b>' . number_format_i18n( $count ) . '</b>'; ?>
59
+ </span>
60
+
61
+ <?php
62
+ // restrict editing
63
+ $restrict = (bool) Post_Views_Counter()->options['general']['restrict_edit_views'];
64
+
65
+ if ( $restrict === false || ( $restrict === true && current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) ) ) {
66
+ ?>
67
+ <a href="#post-views" class="edit-post-views hide-if-no-js"><?php _e( 'Edit', 'post-views-counter' ); ?></a>
68
+
69
+ <div id="post-views-input-container" class="hide-if-js">
70
+
71
+ <p><?php _e( 'Adjust the views count for this post.', 'post-views-counter' ); ?></p>
72
+ <input type="hidden" name="current_post_views" id="post-views-current" value="<?php echo $count; ?>" />
73
+ <input type="text" name="post_views" id="post-views-input" value="<?php echo $count; ?>"/><br />
74
+ <p>
75
+ <a href="#post-views" class="save-post-views hide-if-no-js button"><?php _e( 'OK', 'post-views-counter' ); ?></a>
76
+ <a href="#post-views" class="cancel-post-views hide-if-no-js"><?php _e( 'Cancel', 'post-views-counter' ); ?></a>
77
+ </p>
78
+
79
+ </div>
80
+ <?php
81
+ }
82
+ ?>
83
+
84
+ </div>
85
+ <?php
86
+ }
87
+
88
+ /**
89
+ * Save post views data.
90
+ *
91
+ * @param int $post_id
92
+ * @param object $post
93
+ * @return int
94
+ */
95
+ public function save_post( $post_id, $post = null ) {
96
+ // break if doing autosave
97
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
98
+ return $post_id;
99
+
100
+ // break if current user can't edit this post
101
+ if ( ! current_user_can( 'edit_post', $post_id ) )
102
+ return $post_id;
103
+
104
+ // is post views set
105
+ if ( ! isset( $_POST['post_views'] ) )
106
+ return $post_id;
107
+
108
+ // cast numeric post views
109
+ $post_views = (int) $_POST['post_views'];
110
+
111
+ // unchanged post views value?
112
+ if ( isset( $_POST['current_post_views'] ) && $post_views === (int) $_POST['current_post_views'] )
113
+ return $post_id;
114
+
115
+ // break if post views in not one of the selected
116
+ $post_types = (array) Post_Views_Counter()->options['general']['post_types_count'];
117
+
118
+ // get post type
119
+ if ( is_null( $post ) )
120
+ $post_type = get_post_type( $post_id );
121
+ else
122
+ $post_type = $post->post_type;
123
+
124
+ // invalid post type?
125
+ if ( ! in_array( $post_type, $post_types, true ) )
126
+ return $post_id;
127
+
128
+ // break if views editing is restricted
129
+ if ( (bool) Post_Views_Counter()->options['general']['restrict_edit_views'] === true && ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) )
130
+ return $post_id;
131
+
132
+ // validate data
133
+ if ( ! isset( $_POST['pvc_nonce'] ) || ! wp_verify_nonce( $_POST['pvc_nonce'], 'post_views_count' ) )
134
+ return $post_id;
135
+
136
+ // update post views
137
+ pvc_update_post_views( $post_id, $post_views );
138
+
139
+ do_action( 'pvc_after_update_post_views_count', $post_id );
140
+ }
141
+
142
+ /**
143
+ * Register post views column for specific post types.
144
+ *
145
+ * @return void
146
+ */
147
+ public function register_new_column() {
148
+ // get post types
149
+ $post_types = Post_Views_Counter()->options['general']['post_types_count'];
150
+
151
+ // any post types?
152
+ if ( ! empty( $post_types ) ) {
153
+ foreach ( $post_types as $post_type ) {
154
+ if ( $post_type === 'attachment' ) {
155
+ // actions
156
+ add_action( 'manage_media_custom_column', [ $this, 'add_new_column_content' ], 10, 2 );
157
+
158
+ // filters
159
+ add_filter( 'manage_media_columns', [ $this, 'add_new_column' ] );
160
+ add_filter( 'manage_upload_sortable_columns', [ $this, 'register_sortable_custom_column' ] );
161
+ } else {
162
+ // actions
163
+ add_action( 'manage_' . $post_type . '_posts_custom_column', [ $this, 'add_new_column_content' ], 10, 2 );
164
+
165
+ // filters
166
+ add_filter( 'manage_' . $post_type . '_posts_columns', [ $this, 'add_new_column' ] );
167
+ add_filter( 'manage_edit-' . $post_type . '_sortable_columns', [ $this, 'register_sortable_custom_column' ] );
168
+
169
+ // bbPress?
170
+ if ( class_exists( 'bbPress' ) ) {
171
+ if ( $post_type === 'forum' )
172
+ add_filter( 'bbp_admin_forums_column_headers', [ $this, 'add_new_column' ] );
173
+ elseif ( $post_type === 'topic' )
174
+ add_filter( 'bbp_admin_topics_column_headers', [ $this, 'add_new_column' ] );
175
+ }
176
+ }
177
+ }
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Register sortable post views column.
183
+ *
184
+ * @param array $columns
185
+ * @return array
186
+ */
187
+ public function register_sortable_custom_column( $columns ) {
188
+ // add new sortable column
189
+ $columns['post_views'] = 'post_views';
190
+
191
+ return $columns;
192
+ }
193
+
194
+ /**
195
+ * Add post views column.
196
+ *
197
+ * @param array $columns
198
+ * @return array
199
+ */
200
+ public function add_new_column( $columns ) {
201
+ $offset = 0;
202
+
203
+ // date column?
204
+ if ( isset( $columns['date'] ) )
205
+ $offset++;
206
+
207
+ // comments column?
208
+ if ( isset( $columns['comments'] ) )
209
+ $offset++;
210
+
211
+ // any skipped columns?
212
+ if ( $offset > 0 ) {
213
+ $data = array_slice( $columns, -$offset, $offset, true );
214
+
215
+ // unset columns
216
+ foreach ( $data as $column => $name ) {
217
+ unset( $columns[$column] );
218
+ }
219
+
220
+ $columns['post_views'] = '<span class="dash-icon dashicons dashicons-chart-bar" title="' . __( 'Post Views', 'post-views-counter' ) . '"></span><span class="dash-title">' . __( 'Post Views', 'post-views-counter' ) . '</span>';
221
+
222
+ // again add columns
223
+ foreach ( $data as $column => $name ) {
224
+ $columns[$column] = $name;
225
+ }
226
+ } else
227
+ $columns['post_views'] = '<span class="dash-icon dashicons dashicons-chart-bar" title="' . __( 'Post Views', 'post-views-counter' ) . '"></span><span class="dash-title">' . __( 'Post Views', 'post-views-counter' ) . '</span>';
228
+
229
+ return $columns;
230
+ }
231
+
232
+ /**
233
+ * Add post views column content.
234
+ *
235
+ * @param string $column_name
236
+ * @param int $id
237
+ * @return void
238
+ */
239
+ public function add_new_column_content( $column_name, $id ) {
240
+ if ( $column_name === 'post_views' ) {
241
+ // get total post views
242
+ $count = pvc_get_post_views( $id );
243
+
244
+ echo $count;
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Handle quick edit.
250
+ *
251
+ * @global string $pagenow
252
+ * @param string $column_name
253
+ * @param string $post_type
254
+ * @return void
255
+ */
256
+ function quick_edit_custom_box( $column_name, $post_type ) {
257
+ global $pagenow;
258
+
259
+ if ( $pagenow !== 'edit.php' )
260
+ return;
261
+
262
+ if ( $column_name !== 'post_views' )
263
+ return;
264
+
265
+ if ( ! Post_Views_Counter()->options['general']['post_views_column'] || ! in_array( $post_type, Post_Views_Counter()->options['general']['post_types_count'] ) )
266
+ return;
267
+
268
+ // break if views editing is restricted
269
+ $restrict = (bool) Post_Views_Counter()->options['general']['restrict_edit_views'];
270
+
271
+ if ( $restrict === true && ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) )
272
+ return;
273
+
274
+ ?>
275
+ <fieldset class="inline-edit-col-left">
276
+ <div id="inline-edit-post_views" class="inline-edit-col">
277
+ <label class="inline-edit-group">
278
+ <span class="title"><?php _e( 'Post Views', 'post-views-counter' ); ?></span>
279
+ <span class="input-text-wrap"><input type="text" name="post_views" class="title text" value=""></span>
280
+ <input type="hidden" name="current_post_views" value="" />
281
+ <?php wp_nonce_field( 'post_views_count', 'pvc_nonce' ); ?>
282
+ </label>
283
+ </div>
284
+ </fieldset>
285
+ <?php
286
+ }
287
+
288
+ /**
289
+ * Bulk save post views.
290
+ *
291
+ * @global object $wpdb;
292
+ * @return void
293
+ */
294
+ function save_bulk_post_views() {
295
+ $count = null;
296
+
297
+ if ( isset( $_POST['post_views'] ) ) {
298
+ if ( is_numeric( trim( $_POST['post_views'] ) ) ) {
299
+ $count = (int) $_POST['post_views'];
300
+
301
+ if ( $count < 0 )
302
+ $count = 0;
303
+ } else
304
+ $count = null;
305
+ }
306
+
307
+ // check post IDs
308
+ $post_ids = ( ! empty( $_POST['post_ids'] ) && is_array( $_POST['post_ids'] ) ) ? array_map( 'absint', $_POST['post_ids'] ) : [];
309
+
310
+ if ( is_null( $count ) )
311
+ exit;
312
+
313
+ // break if views editing is restricted
314
+ $restrict = (bool) Post_Views_Counter()->options['general']['restrict_edit_views'];
315
+
316
+ if ( $restrict === true && ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) )
317
+ exit;
318
+
319
+ // any post IDs?
320
+ if ( ! empty( $post_ids ) ) {
321
+ foreach ( $post_ids as $post_id ) {
322
+
323
+ // break if current user can't edit this post
324
+ if ( ! current_user_can( 'edit_post', $post_id ) )
325
+ continue;
326
+
327
+ global $wpdb;
328
+
329
+ // insert or update db post views count
330
+ $wpdb->query(
331
+ $wpdb->prepare( "
332
+ INSERT INTO " . $wpdb->prefix . "post_views (id, type, period, count)
333
+ VALUES (%d, %d, %s, %d)
334
+ ON DUPLICATE KEY UPDATE count = %d", $post_id, 4, 'total', $count, $count
335
+ )
336
+ );
337
+ }
338
+ }
339
+
340
+ exit;
341
+ }
342
+
343
+ /**
344
+ * Add admin bar stats to a post.
345
+ *
346
+ * @return void
347
+ */
348
+ public function admin_bar_menu( $admin_bar ) {
349
+ // get main instance
350
+ $pvc = Post_Views_Counter();
351
+
352
+ // statistics enabled?
353
+ if ( ! apply_filters( 'pvc_display_toolbar_statistics', $pvc->options['display']['toolbar_statistics'] ) )
354
+ return;
355
+
356
+ $post = null;
357
+
358
+ if ( is_admin() && ! wp_doing_ajax() ) {
359
+ global $pagenow;
360
+
361
+ $post = $pagenow == 'post.php' && ! empty( $_GET['post'] ) ? get_post( (int) $_GET['post'] ) : $post;
362
+ } elseif ( is_singular() )
363
+ global $post;
364
+
365
+ // get countable post types
366
+ $post_types = Post_Views_Counter()->options['general']['post_types_count'];
367
+
368
+ // whether to count this post type or not
369
+ if ( empty( $post_types ) || empty( $post ) || ! in_array( $post->post_type, $post_types, true ) )
370
+ return;
371
+
372
+ $dt = new DateTime();
373
+
374
+ // get post views
375
+ $views = pvc_get_views(
376
+ [
377
+ 'post_id' => $post->ID,
378
+ 'post_type' => $post->post_type,
379
+ 'fields' => 'date=>views',
380
+ 'views_query' => [
381
+ 'year' => $dt->format( 'Y' ),
382
+ 'month' => $dt->format( 'm' )
383
+ ]
384
+ ]
385
+ );
386
+
387
+ $graph = '';
388
+
389
+ // get highest value
390
+ $views_copy = $views;
391
+
392
+ arsort( $views_copy, SORT_NUMERIC );
393
+
394
+ $highest = reset( $views_copy );
395
+
396
+ // find the multiplier
397
+ $multiplier = $highest * 0.05;
398
+
399
+ // generate ranges
400
+ $ranges = [];
401
+
402
+ for ( $i = 1; $i <= 20; $i ++ ) {
403
+ $ranges[$i] = round( $multiplier * $i );
404
+ }
405
+
406
+ // create graph
407
+ foreach ( $views as $date => $count ) {
408
+ $count_class = 0;
409
+
410
+ if ( $count > 0 ) {
411
+ foreach ( $ranges as $index => $range ) {
412
+ if ( $count <= $range ) {
413
+ $count_class = $index;
414
+ break;
415
+ }
416
+ }
417
+ }
418
+
419
+ $graph .= '<span class="pvc-line-graph pvc-line-graph-' . $count_class . '" title="' . sprintf( _n( '%s post view', '%s post views', $count, 'post-views-counter' ), number_format_i18n( $count ) ) . '"></span>';
420
+ }
421
+
422
+ $admin_bar->add_menu(
423
+ [
424
+ 'id' => 'pvc-post-views',
425
+ 'title' => '<span class="pvc-graph-container">' . $graph . '</span>',
426
+ 'href' => false,
427
+ 'meta' => [
428
+ 'title' => false
429
+ ]
430
+ ]
431
+ );
432
+ }
433
+
434
+ /**
435
+ * Maybe add admin CSS.
436
+ *
437
+ * @return void
438
+ */
439
+ public function admin_bar_maybe_add_style() {
440
+ // get main instance
441
+ $pvc = Post_Views_Counter();
442
+
443
+ // statistics enabled?
444
+ if ( ! $pvc->options['display']['toolbar_statistics'] )
445
+ return;
446
+
447
+ $post = null;
448
+
449
+ if ( is_admin() && ! wp_doing_ajax() ) {
450
+ global $pagenow;
451
+
452
+ $post = ( $pagenow === 'post.php' && ! empty( $_GET['post'] ) ) ? get_post( (int) $_GET['post'] ) : $post;
453
+ } elseif ( is_singular() )
454
+ global $post;
455
+
456
+ // get countable post types
457
+ $post_types = $pvc->options['general']['post_types_count'];
458
+
459
+ // whether to count this post type or not
460
+ if ( empty( $post_types ) || empty( $post ) || ! in_array( $post->post_type, $post_types, true ) )
461
+ return;
462
+
463
+ // on backend area
464
+ add_action( 'admin_head', [ $this, 'admin_bar_css' ] );
465
+
466
+ // on frontend area
467
+ add_action( 'wp_head', [ $this, 'admin_bar_css' ] );
468
+ }
469
+
470
+ /**
471
+ * Add admin CSS.
472
+ *
473
+ * @return void
474
+ */
475
+ public function admin_bar_css() {
476
+ $html = '
477
+ <style type="text/css">
478
+ #wp-admin-bar-pvc-post-views .pvc-graph-container { padding-top: 6px; padding-bottom: 6px; position: relative; display: block; height: 100%; box-sizing: border-box; }
479
+ #wp-admin-bar-pvc-post-views .pvc-line-graph {
480
+ display: inline-block;
481
+ width: 1px;
482
+ margin-right: 1px;
483
+ background-color: #ccc;
484
+ vertical-align: baseline;
485
+ }
486
+ #wp-admin-bar-pvc-post-views .pvc-line-graph:hover { background-color: #eee; }
487
+ #wp-admin-bar-pvc-post-views .pvc-line-graph-0 { height: 1% }';
488
+
489
+ for ( $i = 1; $i <= 20; $i ++ ) {
490
+ $html .= '
491
+ #wp-admin-bar-pvc-post-views .pvc-line-graph-' . $i . ' { height: ' . $i * 5 . '% }';
492
+ }
493
+
494
+ $html .= '
495
+ </style>';
496
+
497
+ echo $html;
498
+ }
499
+ }
includes/class-counter.php ADDED
@@ -0,0 +1,903 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Counter class.
8
+ *
9
+ * @class Post_Views_Counter_Counter
10
+ */
11
+ class Post_Views_Counter_Counter {
12
+
13
+ const GROUP = 'pvc';
14
+ const NAME_ALLKEYS = 'cached_key_names';
15
+ const CACHE_KEY_SEPARATOR = '.';
16
+ const MAX_INSERT_STRING_LENGTH = 25000;
17
+
18
+ private $db_insert_values = '';
19
+ private $cookie = [
20
+ 'exists' => false,
21
+ 'visited_posts' => [],
22
+ 'expiration' => 0
23
+ ];
24
+
25
+ /**
26
+ * Class constructor.
27
+ *
28
+ * @return void
29
+ */
30
+ public function __construct() {
31
+ // actions
32
+ add_action( 'plugins_loaded', [ $this, 'check_cookie' ], 1 );
33
+ add_action( 'init', [ $this, 'init_counter' ] );
34
+ add_action( 'deleted_post', [ $this, 'delete_post_views' ] );
35
+ }
36
+
37
+ /**
38
+ * Initialize counter.
39
+ *
40
+ * @return void
41
+ */
42
+ public function init_counter() {
43
+ // admin?
44
+ if ( is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) )
45
+ return;
46
+
47
+ // get main instance
48
+ $pvc = Post_Views_Counter();
49
+
50
+ // PHP counter
51
+ if ( $pvc->options['general']['counter_mode'] === 'php' )
52
+ add_action( 'wp', [ $this, 'check_post_php' ] );
53
+ // JavaScript (AJAX) counter
54
+ elseif ( $pvc->options['general']['counter_mode'] === 'js' ) {
55
+ add_action( 'wp_ajax_pvc-check-post', [ $this, 'check_post_js' ] );
56
+ add_action( 'wp_ajax_nopriv_pvc-check-post', [ $this, 'check_post_js' ] );
57
+ // REST API?
58
+ } elseif ( $pvc->options['general']['counter_mode'] === 'rest_api' )
59
+ add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
60
+ }
61
+
62
+ /**
63
+ * Check whether to count visit.
64
+ *
65
+ * @param int $id
66
+ * @return void
67
+ */
68
+ public function check_post( $id = 0 ) {
69
+ // get post id
70
+ $id = (int) ( empty( $id ) ? get_the_ID() : $id );
71
+
72
+ // empty id?
73
+ if ( empty( $id ) )
74
+ return;
75
+
76
+ // get user id, from current user or static var in rest api request
77
+ $user_id = get_current_user_id();
78
+
79
+ // get user IP address
80
+ $user_ip = $this->get_user_ip();
81
+
82
+ do_action( 'pvc_before_check_visit', $id, $user_id, $user_ip );
83
+
84
+ // get main instance
85
+ $pvc = Post_Views_Counter();
86
+
87
+ // get ips
88
+ $ips = $pvc->options['general']['exclude_ips'];
89
+
90
+ // whether to count this ip
91
+ if ( ! empty( $ips ) && filter_var( preg_replace( '/[^0-9a-fA-F:., ]/', '', $user_ip ), FILTER_VALIDATE_IP ) ) {
92
+ // check ips
93
+ foreach ( $ips as $ip ) {
94
+ if ( strpos( $ip, '*' ) !== false ) {
95
+ if ( $this->ipv4_in_range( $user_ip, $ip ) )
96
+ return;
97
+ } else {
98
+ if ( $user_ip === $ip )
99
+ return;
100
+ }
101
+ }
102
+ }
103
+
104
+ // strict counts?
105
+ if ( $pvc->options['general']['strict_counts'] ) {
106
+ // get IP cached visits
107
+ $ip_cache = get_transient( 'post_views_counter_ip_cache' );
108
+
109
+ if ( ! $ip_cache )
110
+ $ip_cache = [];
111
+
112
+ // get user IP address
113
+ $user_ip = $this->encrypt_ip( $user_ip );
114
+
115
+ // visit exists in transient?
116
+ if ( isset( $ip_cache[$id][$user_ip] ) ) {
117
+ // get current time
118
+ $current_time = current_time( 'timestamp', true );
119
+
120
+ if ( $current_time < $ip_cache[$id][$user_ip] + $this->get_timestamp( $pvc->options['general']['time_between_counts']['type'], $pvc->options['general']['time_between_counts']['number'], false ) )
121
+ return;
122
+ }
123
+ }
124
+
125
+ // get groups to check them faster
126
+ $groups = $pvc->options['general']['exclude']['groups'];
127
+
128
+ // whether to count this user
129
+ if ( ! empty( $user_id ) ) {
130
+ // exclude logged in users?
131
+ if ( in_array( 'users', $groups, true ) )
132
+ return;
133
+ // exclude specific roles?
134
+ elseif ( in_array( 'roles', $groups, true ) && $this->is_user_role_excluded( $user_id, $pvc->options['general']['exclude']['roles'] ) )
135
+ return;
136
+ // exclude guests?
137
+ } elseif ( in_array( 'guests', $groups, true ) )
138
+ return;
139
+
140
+ // whether to count robots
141
+ if ( in_array( 'robots', $groups, true ) && $pvc->crawler_detect->is_crawler() )
142
+ return;
143
+
144
+ // cookie already existed?
145
+ if ( $this->cookie['exists'] ) {
146
+ // get current time if needed
147
+ if ( ! isset( $current_time ) )
148
+ $current_time = current_time( 'timestamp', true );
149
+
150
+ // post already viewed but not expired?
151
+ if ( in_array( $id, array_keys( $this->cookie['visited_posts'] ), true ) && $current_time < $this->cookie['visited_posts'][$id] ) {
152
+ // update cookie but do not count visit
153
+ $this->save_cookie( $id, $this->cookie, false );
154
+
155
+ return;
156
+ // update cookie
157
+ } else
158
+ $this->save_cookie( $id, $this->cookie );
159
+ } else {
160
+ // set new cookie
161
+ $this->save_cookie( $id );
162
+ }
163
+
164
+ $count_visit = (bool) apply_filters( 'pvc_count_visit', true, $id );
165
+
166
+ // count visit
167
+ if ( $count_visit ) {
168
+ // strict counts?
169
+ if ( $pvc->options['general']['strict_counts'] )
170
+ $this->save_ip( $id );
171
+
172
+ return $this->count_visit( $id );
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Check whether to count visit via PHP request.
178
+ *
179
+ * @return void
180
+ */
181
+ public function check_post_php() {
182
+ // do not count admin entries
183
+ if ( is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) )
184
+ return;
185
+
186
+ // do we use PHP as counter?
187
+ if ( Post_Views_Counter()->options['general']['counter_mode'] !== 'php' )
188
+ return;
189
+
190
+ // get countable post types
191
+ $post_types = Post_Views_Counter()->options['general']['post_types_count'];
192
+
193
+ // whether to count this post type
194
+ if ( empty( $post_types ) || ! is_singular( $post_types ) )
195
+ return;
196
+
197
+ $this->check_post( get_the_ID() );
198
+ }
199
+
200
+ /**
201
+ * Check whether to count visit via JavaScript (AJAX) request.
202
+ *
203
+ * @return void
204
+ */
205
+ public function check_post_js() {
206
+ if ( isset( $_POST['action'], $_POST['id'], $_POST['pvc_nonce'] ) && $_POST['action'] === 'pvc-check-post' && ( $post_id = (int) $_POST['id'] ) > 0 && wp_verify_nonce( $_POST['pvc_nonce'], 'pvc-check-post' ) !== false ) {
207
+ // do we use JavaScript as counter?
208
+ if ( Post_Views_Counter()->options['general']['counter_mode'] !== 'js' )
209
+ exit;
210
+
211
+ // get countable post types
212
+ $post_types = Post_Views_Counter()->options['general']['post_types_count'];
213
+
214
+ // check if post exists
215
+ $post = get_post( $post_id );
216
+
217
+ // whether to count this post type or not
218
+ if ( empty( $post_types ) || empty( $post ) || ! in_array( $post->post_type, $post_types, true ) )
219
+ exit;
220
+
221
+ $this->check_post( $post_id );
222
+ }
223
+
224
+ exit;
225
+ }
226
+
227
+ /**
228
+ * Check whether to count visit via REST API request.
229
+ *
230
+ * @param array $request
231
+ * @return array|int
232
+ */
233
+ public function check_post_rest_api( $request ) {
234
+ $post_id = absint( $request['id'] );
235
+
236
+ // do we use REST API as counter?
237
+ if ( Post_Views_Counter()->options['general']['counter_mode'] !== 'rest_api' )
238
+ return new WP_Error( 'pvc_rest_api_disabled', __( 'REST API method is disabled.', 'post-views-counter' ), [ 'status' => 404 ] );
239
+
240
+ // @todo: get current user id in direct api endpoint calls
241
+
242
+ // check if post exists
243
+ $post = get_post( $post_id );
244
+
245
+ if ( ! $post )
246
+ return new WP_Error( 'pvc_post_invalid_id', __( 'Invalid post ID.', 'post-views-counter' ), [ 'status' => 404 ] );
247
+
248
+ // get countable post types
249
+ $post_types = Post_Views_Counter()->options['general']['post_types_count'];
250
+
251
+ // whether to count this post type
252
+ if ( empty( $post_types ) || ! in_array( $post->post_type, $post_types, true ) )
253
+ return new WP_Error( 'pvc_post_type_excluded', __( 'Post type excluded.', 'post-views-counter' ), [ 'status' => 404 ] );
254
+
255
+ return $this->check_post( $post_id );
256
+ }
257
+
258
+ /**
259
+ * Initialize cookie session.
260
+ *
261
+ * @return void
262
+ */
263
+ public function check_cookie() {
264
+ // do not run in admin except for ajax requests
265
+ if ( is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) )
266
+ return;
267
+
268
+ // assign cookie name
269
+ $cookie_name = 'pvc_visits' . ( is_multisite() ? '_' . get_current_blog_id() : '' );
270
+
271
+ // is cookie set?
272
+ if ( isset( $_COOKIE[$cookie_name] ) && ! empty( $_COOKIE[$cookie_name] ) ) {
273
+ $visited_posts = $expirations = [];
274
+
275
+ foreach ( $_COOKIE[$cookie_name] as $content ) {
276
+ // is cookie valid?
277
+ if ( preg_match( '/^(([0-9]+b[0-9]+a?)+)$/', $content ) === 1 ) {
278
+ // get single id with expiration
279
+ $expiration_ids = explode( 'a', $content );
280
+
281
+ // check every expiration => id pair
282
+ foreach ( $expiration_ids as $pair ) {
283
+ $pair = explode( 'b', $pair );
284
+ $expirations[] = (int) $pair[0];
285
+ $visited_posts[(int) $pair[1]] = (int) $pair[0];
286
+ }
287
+ }
288
+ }
289
+
290
+ // update cookie
291
+ $this->cookie = [
292
+ 'exists' => true,
293
+ 'visited_posts' => $visited_posts,
294
+ 'expiration' => max( $expirations )
295
+ ];
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Save cookie function.
301
+ *
302
+ * @param int $id
303
+ * @param array $cookie
304
+ * @param bool $expired
305
+ * @return void
306
+ */
307
+ private function save_cookie( $id, $cookie = [], $expired = true ) {
308
+ $set_cookie = apply_filters( 'pvc_maybe_set_cookie', true );
309
+
310
+ // Cookie Notice compatibility
311
+ if ( function_exists( 'cn_cookies_accepted' ) && ! cn_cookies_accepted() )
312
+ $set_cookie = false;
313
+
314
+ if ( $set_cookie !== true )
315
+ return;
316
+
317
+ $expiration = $this->get_timestamp( Post_Views_Counter()->options['general']['time_between_counts']['type'], Post_Views_Counter()->options['general']['time_between_counts']['number'] );
318
+
319
+ // assign cookie name
320
+ $cookie_name = 'pvc_visits' . ( is_multisite() ? '_' . get_current_blog_id() : '' );
321
+
322
+ // is this a new cookie?
323
+ if ( empty( $cookie ) ) {
324
+ // set cookie
325
+ setcookie( $cookie_name . '[0]', $expiration . 'b' . $id, $expiration, COOKIEPATH, COOKIE_DOMAIN, (isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ? true : false ), true );
326
+ } else {
327
+ if ( $expired ) {
328
+ // add new id or change expiration date if id already exists
329
+ $cookie['visited_posts'][$id] = $expiration;
330
+ }
331
+
332
+ // create copy for better foreach performance
333
+ $visited_posts_expirations = $cookie['visited_posts'];
334
+
335
+ // get current gmt time
336
+ $time = current_time( 'timestamp', true );
337
+
338
+ // check whether viewed id has expired - no need to keep it in cookie (less size)
339
+ foreach ( $visited_posts_expirations as $post_id => $post_expiration ) {
340
+ if ( $time > $post_expiration )
341
+ unset( $cookie['visited_posts'][$post_id] );
342
+ }
343
+
344
+ // set new last expiration date if needed
345
+ $cookie['expiration'] = max( $cookie['visited_posts'] );
346
+
347
+ $cookies = $imploded = [];
348
+
349
+ // create pairs
350
+ foreach ( $cookie['visited_posts'] as $id => $exp ) {
351
+ $imploded[] = $exp . 'b' . $id;
352
+ }
353
+
354
+ // split cookie into chunks (3980 bytes to make sure it is safe for every browser)
355
+ $chunks = str_split( implode( 'a', $imploded ), 3980 );
356
+
357
+ // more then one chunk?
358
+ if ( count( $chunks ) > 1 ) {
359
+ $last_id = '';
360
+
361
+ foreach ( $chunks as $chunk_id => $chunk ) {
362
+ // new chunk
363
+ $chunk_c = $last_id . $chunk;
364
+
365
+ // is it full-length chunk?
366
+ if ( strlen( $chunk ) === 3980 ) {
367
+ // get last part
368
+ $last_part = strrchr( $chunk_c, 'a' );
369
+
370
+ // get last id
371
+ $last_id = substr( $last_part, 1 );
372
+
373
+ // add new full-lenght chunk
374
+ $cookies[$chunk_id] = substr( $chunk_c, 0, strlen( $chunk_c ) - strlen( $last_part ) );
375
+ } else {
376
+ // add last chunk
377
+ $cookies[$chunk_id] = $chunk_c;
378
+ }
379
+ }
380
+ } else {
381
+ // only one chunk
382
+ $cookies[] = $chunks[0];
383
+ }
384
+
385
+ foreach ( $cookies as $key => $value ) {
386
+ // set cookie
387
+ setcookie( $cookie_name . '[' . $key . ']', $value, $cookie['expiration'], COOKIEPATH, COOKIE_DOMAIN, ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ? true : false ), true );
388
+ }
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Save user IP address.
394
+ *
395
+ * @param int $id
396
+ * @return int|void
397
+ */
398
+ private function save_ip( $id ) {
399
+ $set_cookie = apply_filters( 'pvc_maybe_set_cookie', true );
400
+
401
+ // Cookie Notice compatibility
402
+ if ( function_exists( 'cn_cookies_accepted' ) && ! cn_cookies_accepted() )
403
+ $set_cookie = false;
404
+
405
+ if ( $set_cookie !== true )
406
+ return $id;
407
+
408
+ // get IP cached visits
409
+ $ip_cache = get_transient( 'post_views_counter_ip_cache' );
410
+
411
+ if ( ! $ip_cache )
412
+ $ip_cache = [];
413
+
414
+ // get user IP address
415
+ $user_ip = $this->encrypt_ip( $this->get_user_ip() );
416
+
417
+ // get current time
418
+ $current_time = current_time( 'timestamp', true );
419
+
420
+ // visit exists in transient?
421
+ if ( isset( $ip_cache[$id][$user_ip] ) ) {
422
+ if ( $current_time > $ip_cache[$id][$user_ip] + $this->get_timestamp( Post_Views_Counter()->options['general']['time_between_counts']['type'], Post_Views_Counter()->options['general']['time_between_counts']['number'], false ) )
423
+ $ip_cache[$id][$user_ip] = $current_time;
424
+ else
425
+ return;
426
+ } else
427
+ $ip_cache[$id][$user_ip] = $current_time;
428
+
429
+ // keep it light, only 10 records per post and maximum 100 post records (=> max. 1000 ip entries)
430
+ // also, the data gets deleted after a week if there's no activity during this time...
431
+ if ( count( $ip_cache[$id] ) > 10 )
432
+ $ip_cache[$id] = array_slice( $ip_cache[$id], -10, 10, true );
433
+
434
+ if ( count( $ip_cache ) > 100 )
435
+ $ip_cache = array_slice( $ip_cache, -100, 100, true );
436
+
437
+ set_transient( 'post_views_counter_ip_cache', $ip_cache, WEEK_IN_SECONDS );
438
+ }
439
+
440
+ /**
441
+ * Count visit.
442
+ *
443
+ * @param int $id
444
+ * @return int
445
+ */
446
+ private function count_visit( $id ) {
447
+ $cache_key_names = [];
448
+ $using_object_cache = $this->using_object_cache();
449
+ $increment_amount = (int) apply_filters( 'pvc_views_increment_amount', 1, $id );
450
+
451
+ // get day, week, month and year
452
+ $date = explode( '-', date( 'W-d-m-Y-o', current_time( 'timestamp', true ) ) );
453
+
454
+ foreach ( [
455
+ 0 => $date[3] . $date[2] . $date[1], // day like 20140324
456
+ 1 => $date[4] . $date[0], // week like 201439
457
+ 2 => $date[3] . $date[2], // month like 201405
458
+ 3 => $date[3], // year like 2014
459
+ 4 => 'total' // total views
460
+ ] as $type => $period ) {
461
+ if ( $using_object_cache ) {
462
+ $cache_key = $id . self::CACHE_KEY_SEPARATOR . $type . self::CACHE_KEY_SEPARATOR . $period;
463
+ wp_cache_add( $cache_key, 0, self::GROUP );
464
+ wp_cache_incr( $cache_key, $increment_amount, self::GROUP );
465
+ $cache_key_names[] = $cache_key;
466
+ } else {
467
+ // hit the database directly
468
+ // @TODO: investigate queueing these queries on the 'shutdown' hook instead of running them instantly?
469
+ $this->db_insert( $id, $type, $period, $increment_amount );
470
+ }
471
+ }
472
+
473
+ // update the list of cache keys to be flushed
474
+ if ( $using_object_cache && ! empty( $cache_key_names ) )
475
+ $this->update_cached_keys_list_if_needed( $cache_key_names );
476
+
477
+ do_action( 'pvc_after_count_visit', $id );
478
+
479
+ return $id;
480
+ }
481
+
482
+ /**
483
+ * Remove post views from database when post is deleted.
484
+ *
485
+ * @global $wpdb
486
+ * @param int $post_id
487
+ * @return void
488
+ */
489
+ public function delete_post_views( $post_id ) {
490
+ global $wpdb;
491
+
492
+ $wpdb->delete( $wpdb->prefix . 'post_views', [ 'id' => $post_id ], [ '%d' ] );
493
+ }
494
+
495
+ /**
496
+ * Get timestamp convertion.
497
+ *
498
+ * @param string $type
499
+ * @param int $number
500
+ * @param bool $timestamp
501
+ * @return string
502
+ */
503
+ public function get_timestamp( $type, $number, $timestamp = true ) {
504
+ $converter = [
505
+ 'minutes' => 60,
506
+ 'hours' => 3600,
507
+ 'days' => 86400,
508
+ 'weeks' => 604800,
509
+ 'months' => 2592000,
510
+ 'years' => 946080000
511
+ ];
512
+
513
+ return (int) ( ( $timestamp ? current_time( 'timestamp', true ) : 0 ) + $number * $converter[$type] );
514
+ }
515
+
516
+ /**
517
+ * Check if object cache is in use.
518
+ *
519
+ * @param bool $using
520
+ * @return bool
521
+ */
522
+ public function using_object_cache( $using = null ) {
523
+ $using = wp_using_ext_object_cache( $using );
524
+
525
+ if ( $using ) {
526
+ // check if explicitly disabled by flush_interval setting/option <= 0
527
+ $flush_interval_number = Post_Views_Counter()->options['general']['flush_interval']['number'];
528
+ $using = ( $flush_interval_number <= 0 ) ? false : true;
529
+ }
530
+
531
+ return $using;
532
+ }
533
+
534
+ /**
535
+ * Update the single cache key which holds a list of all the cache keys
536
+ * that need to be flushed to the database.
537
+ *
538
+ * The value of that special cache key is a giant string containing key names separated with the `|` character.
539
+ * Each such key name then consists of 3 elements: $id, $type, $period (separated by a `.` character).
540
+ * Examples:
541
+ * 62053.0.20150327|62053.1.201513|62053.2.201503|62053.3.2015|62053.4.total|62180.0.20150327|62180.1.201513|62180.2.201503|62180.3.2015|62180.4.total
542
+ * A single key is `62053.0.20150327` and that key's data is: $id = 62053, $type = 0, $period = 20150327
543
+ *
544
+ * This data format proved more efficient (avoids the (un)serialization overhead completely + duplicates filtering is a string search now)
545
+ *
546
+ * @param array $key_names
547
+ * @return void
548
+ */
549
+ private function update_cached_keys_list_if_needed( $key_names = [] ) {
550
+ $existing_list = wp_cache_get( self::NAME_ALLKEYS, self::GROUP );
551
+
552
+ if ( ! $existing_list )
553
+ $existing_list = '';
554
+
555
+ $list_modified = false;
556
+
557
+ // modify the list contents if/when needed
558
+ if ( empty( $existing_list ) ) {
559
+ // the simpler case of an empty initial list where we just
560
+ // transform the specified key names into a string
561
+ $existing_list = implode( '|', $key_names );
562
+ $list_modified = true;
563
+ } else {
564
+ // search each specified key name and append it if it's not found
565
+ foreach ( $key_names as $key_name ) {
566
+ if ( false === strpos( $existing_list, $key_name ) ) {
567
+ $existing_list .= '|' . $key_name;
568
+ $list_modified = true;
569
+ }
570
+ }
571
+ }
572
+
573
+ // save modified list back in cache
574
+ if ( $list_modified )
575
+ wp_cache_set( self::NAME_ALLKEYS, $existing_list, self::GROUP );
576
+ }
577
+
578
+ /**
579
+ * Flush views data stored in the persistent object cache into
580
+ * our custom table and clear the object cache keys when done.
581
+ *
582
+ * @return bool
583
+ */
584
+ public function flush_cache_to_db() {
585
+ $key_names = wp_cache_get( self::NAME_ALLKEYS, self::GROUP );
586
+
587
+ if ( ! $key_names )
588
+ $key_names = [];
589
+ else {
590
+ // create an array out of a string that's stored in the cache
591
+ $key_names = explode( '|', $key_names );
592
+ }
593
+
594
+ foreach ( $key_names as $key_name ) {
595
+ // get values stored within the key name itself
596
+ list( $id, $type, $period ) = explode( self::CACHE_KEY_SEPARATOR, $key_name );
597
+
598
+ // get the cached count value
599
+ $count = wp_cache_get( $key_name, self::GROUP );
600
+
601
+ // store cached value in the db
602
+ $this->db_prepare_insert( $id, $type, $period, $count );
603
+
604
+ // clear the cache key we just flushed
605
+ wp_cache_delete( $key_name, self::GROUP );
606
+ }
607
+
608
+ // actually flush values to db (if any left)
609
+ $this->db_commit_insert();
610
+
611
+ // remember last flush to db time
612
+ wp_cache_set( 'last-flush', time(), self::GROUP );
613
+
614
+ // delete the key holding the list itself after we've successfully flushed it
615
+ if ( ! empty( $key_names ) )
616
+ wp_cache_delete( self::NAME_ALLKEYS, self::GROUP );
617
+
618
+ return true;
619
+ }
620
+
621
+ /**
622
+ * Insert or update views count.
623
+ *
624
+ * @global $wpdb
625
+ * @param int $id
626
+ * @param string $type
627
+ * @param string $period
628
+ * @param int $count
629
+ * @return int|bool
630
+ */
631
+ private function db_insert( $id, $type, $period, $count = 1 ) {
632
+ global $wpdb;
633
+
634
+ $count = (int) $count;
635
+
636
+ if ( ! $count )
637
+ $count = 1;
638
+
639
+ return $wpdb->query(
640
+ $wpdb->prepare( "
641
+ INSERT INTO " . $wpdb->prefix . "post_views (id, type, period, count)
642
+ VALUES (%d, %d, %s, %d)
643
+ ON DUPLICATE KEY UPDATE count = count + %d", $id, $type, $period, $count, $count
644
+ )
645
+ );
646
+ }
647
+
648
+ /**
649
+ * Prepare bulk insert or update views count.
650
+ *
651
+ * @param int $id
652
+ * @param string $type
653
+ * @param string $period
654
+ * @param int $count
655
+ * @return void
656
+ */
657
+ private function db_prepare_insert( $id, $type, $period, $count = 1 ) {
658
+ // cast count
659
+ $count = (int) $count;
660
+
661
+ if ( ! $count )
662
+ $count = 1;
663
+
664
+ // any queries?
665
+ if ( ! empty( $this->db_insert_values ) )
666
+ $this->db_insert_values .= ', ';
667
+
668
+ // append insert queries
669
+ $this->db_insert_values .= sprintf( '(%d, %d, "%s", %d)', $id, $type, $period, $count );
670
+
671
+ if ( strlen( $this->db_insert_values ) > self::MAX_INSERT_STRING_LENGTH )
672
+ $this->db_commit_insert();
673
+ }
674
+
675
+ /**
676
+ * Insert accumulated values to database.
677
+ *
678
+ * @global $wpdb
679
+ * @return int|bool
680
+ */
681
+ private function db_commit_insert() {
682
+ if ( empty( $this->db_insert_values ) )
683
+ return false;
684
+
685
+ global $wpdb;
686
+
687
+ $result = $wpdb->query( "
688
+ INSERT INTO " . $wpdb->prefix . "post_views (id, type, period, count)
689
+ VALUES " . $this->db_insert_values . "
690
+ ON DUPLICATE KEY UPDATE count = count + VALUES(count)"
691
+ );
692
+
693
+ $this->db_insert_values = '';
694
+
695
+ return $result;
696
+ }
697
+
698
+ /**
699
+ * Check whether user has excluded roles.
700
+ *
701
+ * @param int $user_id
702
+ * @param string $option
703
+ * @return bool
704
+ */
705
+ public function is_user_role_excluded( $user_id, $option ) {
706
+ // get user by ID
707
+ $user = get_user_by( 'id', $user_id );
708
+
709
+ // no user?
710
+ if ( empty( $user ) )
711
+ return false;
712
+
713
+ // get user roles
714
+ $roles = (array) $user->roles;
715
+
716
+ // any roles?
717
+ if ( ! empty( $roles ) ) {
718
+ foreach ( $roles as $role ) {
719
+ if ( in_array( $role, $option, true ) )
720
+ return true;
721
+ }
722
+ }
723
+
724
+ return false;
725
+ }
726
+
727
+ /**
728
+ * Check if IPv4 is in range.
729
+ *
730
+ * @param string $ip IP address
731
+ * @param string $range IP range
732
+ * @return bool
733
+ */
734
+ public function ipv4_in_range( $ip, $range ) {
735
+ $start = str_replace( '*', '0', $range );
736
+ $end = str_replace( '*', '255', $range );
737
+ $ip = (float) sprintf( "%u", ip2long( $ip ) );
738
+
739
+ return ( $ip >= (float) sprintf( "%u", ip2long( $start ) ) && $ip <= (float) sprintf( "%u", ip2long( $end ) ) );
740
+ }
741
+
742
+ /**
743
+ * Get user real IP address.
744
+ *
745
+ * @return string
746
+ */
747
+ public function get_user_ip() {
748
+ $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
749
+
750
+ foreach ( [ 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ] as $key ) {
751
+ if ( array_key_exists( $key, $_SERVER ) === true ) {
752
+ foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
753
+ // trim for safety measures
754
+ $ip = trim( $ip );
755
+
756
+ // attempt to validate IP
757
+ if ( $this->validate_user_ip( $ip ) )
758
+ continue;
759
+ }
760
+ }
761
+ }
762
+
763
+ return (string) $ip;
764
+ }
765
+
766
+ /**
767
+ * Ensure an ip address is both a valid IP and does not fall within a private network range.
768
+ *
769
+ * @param $ip string IP address
770
+ * @return bool
771
+ */
772
+ public function validate_user_ip( $ip ) {
773
+ if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) === false )
774
+ return false;
775
+
776
+ return true;
777
+ }
778
+
779
+ /**
780
+ * Encrypt user IP.
781
+ *
782
+ * @param int $ip
783
+ * @return string
784
+ */
785
+ public function encrypt_ip( $ip ) {
786
+ $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
787
+ $auth_iv = defined( 'NONCE_KEY' ) ? NONCE_KEY : '';
788
+
789
+ // mcrypt strong encryption
790
+ if ( function_exists( 'mcrypt_encrypt' ) && function_exists( 'mcrypt_get_key_size' ) && function_exists( 'mcrypt_get_iv_size' ) && defined( 'MCRYPT_BLOWFISH' ) ) {
791
+ // get max key size of the mcrypt mode
792
+ $max_key_size = mcrypt_get_key_size( MCRYPT_BLOWFISH, MCRYPT_MODE_CBC );
793
+ $max_iv_size = mcrypt_get_iv_size( MCRYPT_BLOWFISH, MCRYPT_MODE_CBC );
794
+
795
+ $encrypt_key = mb_strimwidth( $auth_key, 0, $max_key_size );
796
+ $encrypt_iv = mb_strimwidth( $auth_iv, 0, $max_iv_size );
797
+
798
+ $encrypted_ip = strtr( base64_encode( mcrypt_encrypt( MCRYPT_BLOWFISH, $encrypt_key, $ip, MCRYPT_MODE_CBC, $encrypt_iv ) ), '+/=', '-_,' );
799
+ // simple encryption
800
+ } elseif ( function_exists( 'gzdeflate' ) )
801
+ $encrypted_ip = base64_encode( convert_uuencode( gzdeflate( $ip ) ) );
802
+ // no encryption
803
+ else
804
+ $encrypted_ip = strtr( base64_encode( convert_uuencode( $ip ) ), '+/=', '-_,' );
805
+
806
+ return $encrypted_ip;
807
+ }
808
+
809
+ /**
810
+ * Decrypt user IP.
811
+ *
812
+ * @param int $encrypted_ip
813
+ * @return string
814
+ */
815
+ public function decrypt_ip( $encrypted_ip ) {
816
+ $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
817
+ $auth_iv = defined( 'NONCE_KEY' ) ? NONCE_KEY : '';
818
+
819
+ // mcrypt strong encryption
820
+ if ( function_exists( 'mcrypt_decrypt' ) && function_exists( 'mcrypt_get_key_size' ) && function_exists( 'mcrypt_get_iv_size' ) && defined( 'MCRYPT_BLOWFISH' ) ) {
821
+ // get max key size of the mcrypt mode
822
+ $max_key_size = mcrypt_get_key_size( MCRYPT_BLOWFISH, MCRYPT_MODE_CBC );
823
+ $max_iv_size = mcrypt_get_iv_size( MCRYPT_BLOWFISH, MCRYPT_MODE_CBC );
824
+
825
+ $encrypt_key = mb_strimwidth( $auth_key, 0, $max_key_size );
826
+ $encrypt_iv = mb_strimwidth( $auth_iv, 0, $max_iv_size );
827
+
828
+ $ip = mcrypt_decrypt( MCRYPT_BLOWFISH, $encrypt_key, base64_decode( strtr( $encrypted_ip, '-_,', '+/=' ) ), MCRYPT_MODE_CBC, $encrypt_iv );
829
+ // simple encryption
830
+ } elseif ( function_exists( 'gzinflate' ) )
831
+ $ip = gzinflate( convert_uudecode( base64_decode( $encrypted_ip ) ) );
832
+ // no encryption
833
+ else
834
+ $ip = convert_uudecode( base64_decode( strtr( $encrypted_ip, '-_,', '+/=' ) ) );
835
+
836
+ return $ip;
837
+ }
838
+
839
+ /**
840
+ * Register REST API endpoints.
841
+ *
842
+ * @return void
843
+ */
844
+ public function rest_api_init() {
845
+ // view post route
846
+ register_rest_route( 'post-views-counter', '/view-post/', [
847
+ 'methods' => [ 'GET', 'POST' ],
848
+ 'callback' => [ $this, 'check_post_rest_api' ],
849
+ 'permission_callback' => [ $this, 'post_views_permissions_check' ],
850
+ 'args' => [
851
+ 'id' => [
852
+ 'default' => 0,
853
+ 'sanitize_callback' => 'absint'
854
+ ]
855
+ ]
856
+ ] );
857
+
858
+ // get views route
859
+ register_rest_route( 'post-views-counter', '/get-post-views/', [
860
+ 'methods' => [ 'GET', 'POST' ],
861
+ 'callback' => [ $this, 'get_post_views_rest_api' ],
862
+ 'permission_callback' => [ $this, 'get_post_views_permissions_check' ],
863
+ 'args' => [
864
+ 'id' => [
865
+ 'default' => 0,
866
+ 'user_id' => get_current_user_id()
867
+ ]
868
+ ]
869
+ ] );
870
+ }
871
+
872
+ /**
873
+ * Get post views via REST API request.
874
+ *
875
+ * @param array $request
876
+ * @return int
877
+ */
878
+ public function get_post_views_rest_api( $request ) {
879
+ $post_id = is_array( $request['id'] ) ? array_map( 'absint', $request['id'] ) : absint( $request['id'] );
880
+
881
+ return pvc_get_post_views( $post_id );
882
+ }
883
+
884
+ /**
885
+ * Check if a given request has access to get views.
886
+ *
887
+ * @param WP_REST_Request $request Full data about the request.
888
+ * @return WP_Error|bool
889
+ */
890
+ public function post_views_permissions_check( $request ) {
891
+ return (bool) apply_filters( 'pvc_rest_api_post_views_check', true, $request );
892
+ }
893
+
894
+ /**
895
+ * Check if a given request has access to get views.
896
+ *
897
+ * @param WP_REST_Request $request Full data about the request.
898
+ * @return WP_Error|bool
899
+ */
900
+ public function get_post_views_permissions_check( $request ) {
901
+ return (bool) apply_filters( 'pvc_rest_api_get_post_views_check', true, $request );
902
+ }
903
+ }
includes/class-crawler-detect.php ADDED
@@ -0,0 +1,969 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Crawler_Detect class.
8
+ *
9
+ * Based on CrawlerDetect php class adjusted to PHP 5.2
10
+ * https://github.com/JayBizzle/Crawler-Detect/blob/master/src/CrawlerDetect.php
11
+ *
12
+ * @since 1.2.4
13
+ * @class Post_Views_Counter_Crawler_Detect
14
+ */
15
+ class Post_Views_Counter_Crawler_Detect {
16
+
17
+ /**
18
+ * The user agent.
19
+ *
20
+ * @var null
21
+ */
22
+ protected $user_agent = null;
23
+
24
+ /**
25
+ * Headers that contain a user agent.
26
+ *
27
+ * @var array
28
+ */
29
+ protected $http_headers = [];
30
+
31
+ /**
32
+ * Store regex matches.
33
+ *
34
+ * @var array
35
+ */
36
+ protected $matches = [];
37
+
38
+ /**
39
+ * Crawlers object.
40
+ *
41
+ * @var object
42
+ */
43
+ protected $crawlers = [];
44
+
45
+ /**
46
+ * Exclusions object.
47
+ *
48
+ * @var object
49
+ */
50
+ protected $exclusions = [];
51
+
52
+ /**
53
+ * Headers object.
54
+ *
55
+ * @var object
56
+ */
57
+ protected $ua_http_headers;
58
+
59
+ /**
60
+ * Class constructor.
61
+ *
62
+ * @return void
63
+ */
64
+ public function __construct() {
65
+ $this->crawlers = $this->get_crawlers_list();
66
+ $this->exclusions = $this->get_exclusions_list();
67
+
68
+ add_action( 'after_setup_theme', [ $this, 'init' ] );
69
+ }
70
+
71
+ /**
72
+ * Initialize class.
73
+ *
74
+ * @return void
75
+ */
76
+ public function init() {
77
+ // break on admin side
78
+ if ( is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) )
79
+ return;
80
+
81
+ $this->ua_http_headers = $this->get_headers_list();
82
+ $this->set_http_headers();
83
+ $this->set_user_agent();
84
+ }
85
+
86
+ /**
87
+ * Set HTTP headers.
88
+ *
89
+ * @param array $http_headers
90
+ * @return void
91
+ */
92
+ public function set_http_headers( $http_headers = null ) {
93
+ // use global _SERVER if $http_headers aren't defined
94
+ if ( ! is_array( $http_headers ) || ! count( $http_headers ) )
95
+ $http_headers = $_SERVER;
96
+
97
+ // clear existing headers
98
+ $this->http_headers = [];
99
+
100
+ // only save HTTP headers - in PHP land, that means only _SERVER vars that start with HTTP_.
101
+ foreach ( $http_headers as $key => $value ) {
102
+ if ( substr( $key, 0, 5 ) === 'HTTP_' )
103
+ $this->http_headers[$key] = $value;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Return user agent headers.
109
+ *
110
+ * @return array
111
+ */
112
+ public function get_ua_http_headers() {
113
+ return $this->ua_http_headers;
114
+ }
115
+
116
+ /**
117
+ * Return the user agent.
118
+ *
119
+ * @return string
120
+ */
121
+ public function get_user_agent() {
122
+ return $this->user_agent;
123
+ }
124
+
125
+ /**
126
+ * Set the user agent.
127
+ *
128
+ * @param string $user_agent
129
+ * @return string
130
+ */
131
+ public function set_user_agent( $user_agent = null ) {
132
+ if ( false === empty( $user_agent ) )
133
+ return $this->user_agent = $user_agent;
134
+ else {
135
+ $this->user_agent = null;
136
+
137
+ foreach ( $this->get_ua_http_headers() as $alt_header ) {
138
+ if ( false === empty( $this->http_headers[$alt_header] ) ) // @todo: should use get_http_header(), but it would be slow.
139
+ $this->user_agent .= $this->http_headers[$alt_header] . ' ';
140
+ }
141
+
142
+ return $this->user_agent = ( ! empty( $this->user_agent ) ? trim( $this->user_agent ) : null);
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Build the user agent regex.
148
+ *
149
+ * @return string
150
+ */
151
+ public function get_regex() {
152
+ return '(' . implode( '|', $this->crawlers ) . ')';
153
+ }
154
+
155
+ /**
156
+ * Build the replacement regex.
157
+ *
158
+ * @return string
159
+ */
160
+ public function get_exclusions() {
161
+ return '(' . implode( '|', $this->exclusions ) . ')';
162
+ }
163
+
164
+ /**
165
+ * Check user agent string against the regex.
166
+ *
167
+ * @param string $user_agent
168
+ * @return bool
169
+ */
170
+ public function is_crawler( $user_agent = null ) {
171
+ $agent = is_null( $user_agent ) ? $this->user_agent : $user_agent;
172
+ $agent = preg_replace( '/' . $this->get_exclusions() . '/i', '', $agent );
173
+
174
+ if ( strlen( trim( $agent ) ) == 0 )
175
+ return false;
176
+ else
177
+ $result = preg_match( '/' . $this->get_regex() . '/i', trim( $agent ), $matches );
178
+
179
+ if ( $matches )
180
+ $this->matches = $matches;
181
+
182
+ return (bool) $result;
183
+ }
184
+
185
+ /**
186
+ * Return the matches.
187
+ *
188
+ * @return string
189
+ */
190
+ public function get_matches() {
191
+ return isset( $this->matches[0] ) ? $this->matches[0] : null;
192
+ }
193
+
194
+ /**
195
+ * Return the regular expressions to match against the user agent.
196
+ *
197
+ * @return array
198
+ */
199
+ protected function get_crawlers_list() {
200
+ return [
201
+ '.*Java.*outbrain',
202
+ '008\/',
203
+ '192.comAgent',
204
+ '2ip\.ru',
205
+ '404checker',
206
+ '^bluefish ',
207
+ '^FDM ',
208
+ '^Goose\/',
209
+ '^Java\/',
210
+ '^Mget',
211
+ '^NG\/[0-9\.]',
212
+ '^NING\/',
213
+ '^PHP\/[0-9]',
214
+ '^RMA\/',
215
+ '^Ruby|Ruby\/[0-9]',
216
+ '^scrutiny\/',
217
+ '^VSE\/[0-9]',
218
+ '^WordPress\.com',
219
+ '^XRL\/[0-9]',
220
+ 'a3logics\.in',
221
+ 'A6-Indexer',
222
+ 'a\.pr-cy\.ru',
223
+ 'Aboundex',
224
+ 'aboutthedomain',
225
+ 'Accoona-AI-Agent',
226
+ 'acoon',
227
+ 'acrylicapps\.com\/pulp',
228
+ 'adbeat',
229
+ 'AddThis',
230
+ 'ADmantX',
231
+ 'adressendeutschland',
232
+ 'Advanced Email Extractor v',
233
+ 'agentslug',
234
+ 'AHC',
235
+ 'aihit',
236
+ 'aiohttp\/',
237
+ 'Airmail',
238
+ 'akula\/',
239
+ 'alertra',
240
+ 'alexa site audit',
241
+ 'alyze\.info',
242
+ 'amagit',
243
+ 'AndroidDownloadManager',
244
+ 'Anemone',
245
+ 'Ant\.com',
246
+ 'Anturis Agent',
247
+ 'AnyEvent-HTTP\/',
248
+ 'Apache-HttpClient\/',
249
+ 'AportWorm\/[0-9]',
250
+ 'AppEngine-Google',
251
+ 'Arachmo',
252
+ 'arachnode',
253
+ 'Arachnophilia',
254
+ 'archive-com',
255
+ 'aria2',
256
+ 'asafaweb.com',
257
+ 'AskQuickly',
258
+ 'Astute',
259
+ 'autocite',
260
+ 'Autonomy',
261
+ 'B-l-i-t-z-B-O-T',
262
+ 'Backlink-Ceck\.de',
263
+ 'Bad-Neighborhood',
264
+ 'baidu\.com',
265
+ 'baypup\/[0-9]',
266
+ 'baypup\/colbert',
267
+ 'BazQux',
268
+ 'BCKLINKS',
269
+ 'BDFetch',
270
+ 'BegunAdvertising\/',
271
+ 'bibnum\.bnf',
272
+ 'BigBozz',
273
+ 'biglotron',
274
+ 'BingLocalSearch',
275
+ 'BingPreview',
276
+ 'binlar',
277
+ 'biz_Directory',
278
+ 'Blackboard Safeassign',
279
+ 'Bloglovin',
280
+ 'BlogPulseLive',
281
+ 'BlogSearch',
282
+ 'Blogtrottr',
283
+ 'boitho\.com-dc',
284
+ 'BPImageWalker',
285
+ 'Braintree-Webhooks',
286
+ 'Branch Metrics API',
287
+ 'Branch-Passthrough',
288
+ 'Browsershots',
289
+ 'BUbiNG',
290
+ 'Butterfly\/',
291
+ 'BuzzSumo',
292
+ 'CakePHP',
293
+ 'CapsuleChecker',
294
+ 'CaretNail',
295
+ 'cb crawl',
296
+ 'CC Metadata Scaper',
297
+ 'Cerberian Drtrs',
298
+ 'CERT\.at-Statistics-Survey',
299
+ 'cg-eye',
300
+ 'changedetection',
301
+ 'Charlotte',
302
+ 'CheckHost',
303
+ 'chkme\.com',
304
+ 'CirrusExplorer\/',
305
+ 'CISPA Vulnerability Notification',
306
+ 'CJNetworkQuality',
307
+ 'clips\.ua\.ac\.be',
308
+ 'Cloud mapping experiment',
309
+ 'CloudFlare-AlwaysOnline',
310
+ 'Cloudinary\/[0-9]',
311
+ 'cmcm\.com',
312
+ 'coccoc',
313
+ 'CommaFeed',
314
+ 'Commons-HttpClient',
315
+ 'Comodo SSL Checker',
316
+ 'contactbigdatafr',
317
+ 'convera',
318
+ 'copyright sheriff',
319
+ 'cosmos\/[0-9]',
320
+ 'Covario-IDS',
321
+ 'CrawlForMe\/[0-9]',
322
+ 'cron-job\.org',
323
+ 'Crowsnest',
324
+ 'curb',
325
+ 'Curious George',
326
+ 'curl',
327
+ 'cuwhois\/[0-9]',
328
+ 'CyberPatrol',
329
+ 'cybo\.com',
330
+ 'DareBoost',
331
+ 'DataparkSearch',
332
+ 'dataprovider',
333
+ 'Daum(oa)?[ \/][0-9]',
334
+ 'DeuSu',
335
+ 'developers\.google\.com\/\+\/web\/snippet\/',
336
+ 'Digg',
337
+ 'Dispatch\/',
338
+ 'dlvr',
339
+ 'DNS-Tools Header-Analyzer',
340
+ 'DNSPod-reporting',
341
+ 'docoloc',
342
+ 'DomainAppender',
343
+ 'dotSemantic',
344
+ 'downforeveryoneorjustme',
345
+ 'downnotifier\.com',
346
+ 'DowntimeDetector',
347
+ 'Dragonfly File Reader',
348
+ 'drupact',
349
+ 'Drupal (\+http:\/\/drupal\.org\/)',
350
+ 'dubaiindex',
351
+ 'EARTHCOM',
352
+ 'Easy-Thumb',
353
+ 'ec2linkfinder',
354
+ 'eCairn-Grabber',
355
+ 'ECCP',
356
+ 'ElectricMonk',
357
+ 'elefent',
358
+ 'EMail Exractor',
359
+ 'EmailWolf',
360
+ 'Embed PHP Library',
361
+ 'Embedly',
362
+ 'europarchive\.org',
363
+ 'evc-batch\/[0-9]',
364
+ 'EventMachine HttpClient',
365
+ 'Evidon',
366
+ 'Evrinid',
367
+ 'ExactSearch',
368
+ 'ExaleadCloudview',
369
+ 'Excel\/',
370
+ 'Exploratodo',
371
+ 'ezooms',
372
+ 'facebookexternalhit',
373
+ 'facebookplatform',
374
+ 'fairshare',
375
+ 'Faraday v',
376
+ 'Faveeo',
377
+ 'Favicon downloader',
378
+ 'FavOrg',
379
+ 'Feed Wrangler',
380
+ 'Feedbin',
381
+ 'FeedBooster',
382
+ 'FeedBucket',
383
+ 'FeedBurner',
384
+ 'FeedChecker',
385
+ 'Feedly',
386
+ 'Feedspot',
387
+ 'feeltiptop',
388
+ 'Fetch API',
389
+ 'Fetch\/[0-9]',
390
+ 'Fever\/[0-9]',
391
+ 'findlink',
392
+ 'findthatfile',
393
+ 'Flamingo_SearchEngine',
394
+ 'FlipboardBrowserProxy',
395
+ 'FlipboardProxy',
396
+ 'FlipboardRSS',
397
+ 'fluffy',
398
+ 'flynxapp',
399
+ 'forensiq',
400
+ 'FoundSeoTool\/[0-9]',
401
+ 'free thumbnails',
402
+ 'FreeWebMonitoring SiteChecker',
403
+ 'Funnelback',
404
+ 'g00g1e\.net',
405
+ 'GAChecker',
406
+ 'geek-tools',
407
+ 'Genderanalyzer',
408
+ 'Genieo',
409
+ 'GentleSource',
410
+ 'GetLinkInfo',
411
+ 'getprismatic\.com',
412
+ 'GetURLInfo\/[0-9]',
413
+ 'GigablastOpenSource',
414
+ 'Go [\d\.]* package http',
415
+ 'Go-http-client',
416
+ 'GomezAgent',
417
+ 'gooblog',
418
+ 'Goodzer\/[0-9]',
419
+ 'Google favicon',
420
+ 'Google Keyword Suggestion',
421
+ 'Google Keyword Tool',
422
+ 'Google Page Speed Insights',
423
+ 'Google PP Default',
424
+ 'Google Search Console',
425
+ 'Google Web Preview',
426
+ 'Google-Adwords',
427
+ 'Google-Apps-Script',
428
+ 'Google-Calendar-Importer',
429
+ 'Google-HTTP-Java-Client',
430
+ 'Google-Publisher-Plugin',
431
+ 'Google-SearchByImage',
432
+ 'Google-Site-Verification',
433
+ 'Google-Structured-Data-Testing-Tool',
434
+ 'google_partner_monitoring',
435
+ 'GoogleDocs',
436
+ 'GoogleHC\/',
437
+ 'GoogleProducer',
438
+ 'GoScraper',
439
+ 'GoSpotCheck',
440
+ 'GoSquared-Status-Checker',
441
+ 'gosquared-thumbnailer',
442
+ 'GotSiteMonitor',
443
+ 'Grammarly',
444
+ 'grouphigh',
445
+ 'grub-client',
446
+ 'GTmetrix',
447
+ 'Hatena',
448
+ 'hawkReader',
449
+ 'HEADMasterSEO',
450
+ 'HeartRails_Capture',
451
+ 'heritrix',
452
+ 'hledejLevne\.cz\/[0-9]',
453
+ 'Holmes',
454
+ 'HootSuite Image proxy',
455
+ 'Hootsuite-WebFeed\/[0-9]',
456
+ 'HostTracker',
457
+ 'ht:\/\/check',
458
+ 'htdig',
459
+ 'HTMLParser\/',
460
+ 'HTTP-Header-Abfrage',
461
+ 'http-kit',
462
+ 'HTTP-Tiny',
463
+ 'HTTP_Compression_Test',
464
+ 'http_request2',
465
+ 'http_requester',
466
+ 'HttpComponents',
467
+ 'httphr',
468
+ 'HTTPMon',
469
+ 'httpscheck',
470
+ 'httpssites_power',
471
+ 'httpunit',
472
+ 'HttpUrlConnection',
473
+ 'httrack',
474
+ 'hosterstats',
475
+ 'huaweisymantec',
476
+ 'HubPages.*crawlingpolicy',
477
+ 'HubSpot Connect',
478
+ 'HubSpot Marketing Grader',
479
+ 'HyperZbozi.cz Feeder',
480
+ 'ichiro',
481
+ 'IdeelaborPlagiaat',
482
+ 'IDG Twitter Links Resolver',
483
+ 'IDwhois\/[0-9]',
484
+ 'Iframely',
485
+ 'igdeSpyder',
486
+ 'IlTrovatore',
487
+ 'ImageEngine\/',
488
+ 'Imagga',
489
+ 'InAGist',
490
+ 'inbound\.li parser',
491
+ 'InDesign%20CC',
492
+ 'infegy',
493
+ 'infohelfer',
494
+ 'InfoWizards Reciprocal Link System PRO',
495
+ 'inpwrd\.com',
496
+ 'Integrity',
497
+ 'integromedb',
498
+ 'internet_archive',
499
+ 'InternetSeer',
500
+ 'internetVista monitor',
501
+ 'IODC',
502
+ 'IOI',
503
+ 'ips-agent',
504
+ 'iqdb\/',
505
+ 'Irokez',
506
+ 'isitup\.org',
507
+ 'iskanie',
508
+ 'iZSearch',
509
+ 'janforman',
510
+ 'Jigsaw',
511
+ 'Jobboerse',
512
+ 'jobo',
513
+ 'Jobrapido',
514
+ 'KeepRight OpenStreetMap Checker',
515
+ 'KimonoLabs\/',
516
+ 'knows\.is',
517
+ 'kouio',
518
+ 'KrOWLer',
519
+ 'kulturarw3',
520
+ 'KumKie',
521
+ 'L\.webis',
522
+ 'Larbin',
523
+ 'LayeredExtractor',
524
+ 'LibVLC',
525
+ 'libwww',
526
+ 'link checker',
527
+ 'Link Valet',
528
+ 'link_thumbnailer',
529
+ 'linkCheck',
530
+ 'linkdex',
531
+ 'LinkExaminer',
532
+ 'linkfluence',
533
+ 'linkpeek',
534
+ 'LinkTiger',
535
+ 'LinkWalker',
536
+ 'Lipperhey',
537
+ 'livedoor ScreenShot',
538
+ 'LoadImpactPageAnalyzer',
539
+ 'LoadImpactRload',
540
+ 'LongURL API',
541
+ 'looksystems\.net',
542
+ 'ltx71',
543
+ 'lwp-trivial',
544
+ 'lycos',
545
+ 'LYT\.SR',
546
+ 'mabontland',
547
+ 'MagpieRSS',
548
+ 'Mail.Ru',
549
+ 'MailChimp\.com',
550
+ 'Mandrill',
551
+ 'marketinggrader',
552
+ 'Mediapartners-Google',
553
+ 'MegaIndex\.ru',
554
+ 'Melvil Rawi\/',
555
+ 'MergeFlow-PageReader',
556
+ 'MetaInspector',
557
+ 'Metaspinner',
558
+ 'MetaURI',
559
+ 'Microsearch',
560
+ 'Microsoft Office ',
561
+ 'Microsoft Windows Network Diagnostics',
562
+ 'Mindjet',
563
+ 'Miniflux',
564
+ 'Mnogosearch',
565
+ 'mogimogi',
566
+ 'Mojolicious (Perl)',
567
+ 'monitis',
568
+ 'Monitority\/[0-9]',
569
+ 'montastic',
570
+ 'MonTools',
571
+ 'Moreover',
572
+ 'Morning Paper',
573
+ 'mowser',
574
+ 'Mrcgiguy',
575
+ 'mShots',
576
+ 'MVAClient',
577
+ 'nagios',
578
+ 'Najdi\.si\/',
579
+ 'NETCRAFT',
580
+ 'NetLyzer FastProbe',
581
+ 'netresearch',
582
+ 'NetShelter ContentScan',
583
+ 'NetTrack',
584
+ 'Netvibes',
585
+ 'Neustar WPM',
586
+ 'NeutrinoAPI',
587
+ 'NewsBlur .*Finder',
588
+ 'NewsGator',
589
+ 'newsme',
590
+ 'newspaper\/',
591
+ 'NG-Search',
592
+ 'nineconnections\.com',
593
+ 'NLNZ_IAHarvester',
594
+ 'Nmap Scripting Engine',
595
+ 'node-superagent',
596
+ 'node\.io',
597
+ 'nominet\.org\.uk',
598
+ 'Norton-Safeweb',
599
+ 'Notifixious',
600
+ 'notifyninja',
601
+ 'nuhk',
602
+ 'nutch',
603
+ 'Nuzzel',
604
+ 'nWormFeedFinder',
605
+ 'Nymesis',
606
+ 'Ocelli\/[0-9]',
607
+ 'oegp',
608
+ 'okhttp',
609
+ 'Omea Reader',
610
+ 'omgili',
611
+ 'Online Domain Tools',
612
+ 'OpenCalaisSemanticProxy',
613
+ 'Openstat\/',
614
+ 'OpenVAS',
615
+ 'Optimizer',
616
+ 'Orbiter',
617
+ 'OrgProbe\/[0-9]',
618
+ 'ow\.ly',
619
+ 'ownCloud News',
620
+ 'Page Analyzer',
621
+ 'Page Valet',
622
+ 'page2rss',
623
+ 'page_verifier',
624
+ 'PagePeeker',
625
+ 'Pagespeed\/[0-9]',
626
+ 'Panopta',
627
+ 'panscient',
628
+ 'parsijoo',
629
+ 'PayPal IPN',
630
+ 'Pcore-HTTP',
631
+ 'Pearltrees',
632
+ 'peerindex',
633
+ 'Peew',
634
+ 'PhantomJS\/',
635
+ 'Photon\/',
636
+ 'phpcrawl',
637
+ 'phpservermon',
638
+ 'Pi-Monster',
639
+ 'Pingdom\.com',
640
+ 'Pingoscope',
641
+ 'PingSpot',
642
+ 'Pinterest',
643
+ 'Pizilla',
644
+ 'Ploetz \+ Zeller',
645
+ 'Plukkie',
646
+ 'PocketParser',
647
+ 'Pompos',
648
+ 'Porkbun',
649
+ 'Port Monitor',
650
+ 'postano',
651
+ 'PostPost',
652
+ 'postrank',
653
+ 'PowerPoint\/',
654
+ 'Priceonomics Analysis Engine',
655
+ 'Prlog',
656
+ 'probethenet',
657
+ 'Project 25499',
658
+ 'Promotion_Tools_www.searchenginepromotionhelp.com',
659
+ 'prospectb2b',
660
+ 'Protopage',
661
+ 'proximic',
662
+ 'PTST ',
663
+ 'PTST\/[0-9]+',
664
+ 'Pulsepoint XT3 web scraper',
665
+ 'Python-httplib2',
666
+ 'python-requests',
667
+ 'Python-urllib',
668
+ 'Qirina Hurdler',
669
+ 'Qseero',
670
+ 'Qualidator.com SiteAnalyzer',
671
+ 'Quora Link Preview',
672
+ 'Qwantify',
673
+ 'Radian6',
674
+ 'RankSonicSiteAuditor',
675
+ 'Readability',
676
+ 'RealPlayer%20Downloader',
677
+ 'RebelMouse',
678
+ 'redback\/',
679
+ 'Redirect Checker Tool',
680
+ 'ReederForMac',
681
+ 'ResponseCodeTest\/[0-9]',
682
+ 'RestSharp',
683
+ 'RetrevoPageAnalyzer',
684
+ 'Riddler',
685
+ 'Rival IQ',
686
+ 'Robosourcer',
687
+ 'Robozilla\/[0-9]',
688
+ 'ROI Hunter',
689
+ 'SalesIntelligent',
690
+ 'SauceNAO',
691
+ 'SBIder',
692
+ 'Scoop',
693
+ 'scooter',
694
+ 'ScoutJet',
695
+ 'ScoutURLMonitor',
696
+ 'Scrapy',
697
+ 'Scrubby',
698
+ 'SearchSight',
699
+ 'semanticdiscovery',
700
+ 'semanticjuice',
701
+ 'SEO Browser',
702
+ 'Seo Servis',
703
+ 'seo-nastroj.cz',
704
+ 'Seobility',
705
+ 'SEOCentro',
706
+ 'SeoCheck',
707
+ 'SeopultContentAnalyzer',
708
+ 'SEOstats',
709
+ 'Server Density Service Monitoring',
710
+ 'servernfo\.com',
711
+ 'Seznam screenshot-generator',
712
+ 'Shelob',
713
+ 'Shoppimon Analyzer',
714
+ 'ShoppimonAgent\/[0-9]',
715
+ 'ShopWiki',
716
+ 'ShortLinkTranslate',
717
+ 'shrinktheweb',
718
+ 'SilverReader',
719
+ 'SimplePie',
720
+ 'SimplyFast',
721
+ 'Site-Shot\/',
722
+ 'Site24x7',
723
+ 'SiteBar',
724
+ 'SiteCondor',
725
+ 'siteexplorer\.info',
726
+ 'SiteGuardian',
727
+ 'Siteimprove\.com',
728
+ 'Sitemap(s)? Generator',
729
+ 'Siteshooter B0t',
730
+ 'SiteTruth',
731
+ 'sitexy\.com',
732
+ 'SkypeUriPreview',
733
+ 'slider\.com',
734
+ 'slurp',
735
+ 'SMRF URL Expander',
736
+ 'Snappy',
737
+ 'SniffRSS',
738
+ 'sniptracker',
739
+ 'Snoopy',
740
+ 'sogou web',
741
+ 'SortSite',
742
+ 'spaziodati',
743
+ 'Specificfeeds',
744
+ 'speedy',
745
+ 'SPEng',
746
+ 'Spinn3r',
747
+ 'spray-can',
748
+ 'Sprinklr ',
749
+ 'spyonweb',
750
+ 'Sqworm',
751
+ 'SSL Labs',
752
+ 'StackRambler',
753
+ 'Statastico\/',
754
+ 'StatusCake',
755
+ 'Stratagems Kumo',
756
+ 'Stroke.cz',
757
+ 'StudioFACA',
758
+ 'suchen',
759
+ 'summify',
760
+ 'Super Monitoring',
761
+ 'Surphace Scout',
762
+ 'SwiteScraper',
763
+ 'Symfony2 BrowserKit',
764
+ 'Sysomos',
765
+ 'T0PHackTeam',
766
+ 'Tarantula\/',
767
+ 'teoma',
768
+ 'terrainformatica\.com',
769
+ 'The Expert HTML Source Viewer',
770
+ 'theinternetrules',
771
+ 'theoldreader\.com',
772
+ 'Thumbshots',
773
+ 'ThumbSniper',
774
+ 'TinEye',
775
+ 'Tiny Tiny RSS',
776
+ 'topster',
777
+ 'touche.com',
778
+ 'Traackr.com',
779
+ 'truwoGPS',
780
+ 'tweetedtimes\.com',
781
+ 'Tweetminster',
782
+ 'Twikle',
783
+ 'Twingly',
784
+ 'Typhoeus',
785
+ 'ubermetrics-technologies',
786
+ 'uclassify',
787
+ 'UdmSearch',
788
+ 'UnwindFetchor',
789
+ 'updated',
790
+ 'Upflow',
791
+ 'URLChecker',
792
+ 'URLitor.com',
793
+ 'urlresolver',
794
+ 'Urlstat',
795
+ 'UrlTrends Ranking Updater',
796
+ 'Vagabondo',
797
+ 'via ggpht\.com GoogleImageProxy',
798
+ 'visionutils',
799
+ 'vkShare',
800
+ 'voltron',
801
+ 'Vortex\/[0-9]',
802
+ 'voyager\/',
803
+ 'VSAgent\/[0-9]',
804
+ 'VSB-TUO\/[0-9]',
805
+ 'VYU2',
806
+ 'w3af\.org',
807
+ 'W3C-checklink',
808
+ 'W3C-mobileOK',
809
+ 'W3C_I18n-Checker',
810
+ 'W3C_Unicorn',
811
+ 'wangling',
812
+ 'Wappalyzer',
813
+ 'WatchMouse',
814
+ 'WbSrch\/',
815
+ 'web-capture\.net',
816
+ 'Web-Monitoring',
817
+ 'Web-sniffer',
818
+ 'Webauskunft',
819
+ 'WebCapture',
820
+ 'webcollage',
821
+ 'WebCookies',
822
+ 'WebCorp',
823
+ 'WebDoc',
824
+ 'WebFetch',
825
+ 'WebImages',
826
+ 'WebIndex',
827
+ 'webkit2png',
828
+ 'webmastercoffee',
829
+ 'webmon ',
830
+ 'webscreenie',
831
+ 'Webshot',
832
+ 'Website Analyzer\/',
833
+ 'websitepulse[+ ]checker',
834
+ 'Websnapr\/',
835
+ 'Websquash\.com',
836
+ 'Webthumb\/[0-9]',
837
+ 'WebThumbnail',
838
+ 'WeCrawlForThePeace',
839
+ 'WeLikeLinks',
840
+ 'WEPA',
841
+ 'WeSEE',
842
+ 'wf84',
843
+ 'wget',
844
+ 'WhatsApp',
845
+ 'WhatsMyIP',
846
+ 'WhatWeb',
847
+ 'Whibse',
848
+ 'Whynder Magnet',
849
+ 'Windows-RSS-Platform',
850
+ 'WinHttpRequest',
851
+ 'wkhtmlto',
852
+ 'wmtips',
853
+ 'Woko',
854
+ 'WomlpeFactory',
855
+ 'Word\/',
856
+ 'WordPress\/',
857
+ 'wotbox',
858
+ 'WP Engine Install Performance API',
859
+ 'WPScan',
860
+ 'wscheck',
861
+ 'WWW-Mechanize',
862
+ 'www\.monitor\.us',
863
+ 'XaxisSemanticsClassifier',
864
+ 'Xenu Link Sleuth',
865
+ 'XING-contenttabreceiver\/[0-9]',
866
+ 'XmlSitemapGenerator',
867
+ 'xpymep([0-9]?)\.exe',
868
+ 'Y!J-(ASR|BSC)',
869
+ 'Yaanb',
870
+ 'yacy',
871
+ 'Yahoo Ad monitoring',
872
+ 'Yahoo Link Preview',
873
+ 'YahooCacheSystem',
874
+ 'YahooSeeker',
875
+ 'YahooYSMcm',
876
+ 'YandeG',
877
+ 'yandex',
878
+ 'yanga',
879
+ 'yeti',
880
+ 'Yo-yo',
881
+ 'Yoleo Consumer',
882
+ 'yoogliFetchAgent',
883
+ 'YottaaMonitor',
884
+ 'yourls\.org',
885
+ 'Zao',
886
+ 'Zemanta Aggregator',
887
+ 'Zend\\\\Http\\\\Client',
888
+ 'Zend_Http_Client',
889
+ 'zgrab',
890
+ 'ZnajdzFoto',
891
+ 'ZyBorg',
892
+ '[a-z0-9\-_]*((?<!cu)bot|crawler|archiver|transcoder|spider|uptime|validator|fetcher)',
893
+ ];
894
+ }
895
+
896
+ /**
897
+ * Return the list of strings to remove from the user agent before running the crawler regex.
898
+ *
899
+ * @return array
900
+ */
901
+ public function get_exclusions_list() {
902
+ return [
903
+ 'Safari.[\d\.]*',
904
+ 'Firefox.[\d\.]*',
905
+ 'Chrome.[\d\.]*',
906
+ 'Chromium.[\d\.]*',
907
+ 'MSIE.[\d\.]',
908
+ 'Opera\/[\d\.]*',
909
+ 'Mozilla.[\d\.]*',
910
+ 'AppleWebKit.[\d\.]*',
911
+ 'Trident.[\d\.]*',
912
+ 'Windows NT.[\d\.]*',
913
+ 'Android [\d\.]*',
914
+ 'Macintosh.',
915
+ 'Ubuntu',
916
+ 'Linux',
917
+ '[ ]Intel',
918
+ 'Mac OS X [\d_]*',
919
+ '(like )?Gecko(.[\d\.]*)?',
920
+ 'KHTML,',
921
+ 'CriOS.[\d\.]*',
922
+ 'CPU iPhone OS ([0-9_])* like Mac OS X',
923
+ 'CPU OS ([0-9_])* like Mac OS X',
924
+ 'iPod',
925
+ 'compatible',
926
+ 'x86_..',
927
+ 'i686',
928
+ 'x64',
929
+ 'X11',
930
+ 'rv:[\d\.]*',
931
+ 'Version.[\d\.]*',
932
+ 'WOW64',
933
+ 'Win64',
934
+ 'Dalvik.[\d\.]*',
935
+ ' \.NET CLR [\d\.]*',
936
+ 'Presto.[\d\.]*',
937
+ 'Media Center PC',
938
+ 'BlackBerry',
939
+ 'Build',
940
+ 'Opera Mini\/\d{1,2}\.\d{1,2}\.[\d\.]*\/\d{1,2}\.',
941
+ 'Opera',
942
+ ' \.NET[\d\.]*',
943
+ '\(|\)|;|,', // remove the following characters ( ) : ,
944
+ ];
945
+ }
946
+
947
+ /**
948
+ * Return all possible HTTP headers that represent the User-Agent string.
949
+ *
950
+ * @return array
951
+ */
952
+ public function get_headers_list() {
953
+ return [
954
+ // the default User-Agent string.
955
+ 'HTTP_USER_AGENT',
956
+ // header can occur on devices using Opera Mini.
957
+ 'HTTP_X_OPERAMINI_PHONE_UA',
958
+ // vodafone specific header: http://www.seoprinciple.com/mobile-web-community-still-angry-at-vodafone/24/
959
+ 'HTTP_X_DEVICE_USER_AGENT',
960
+ 'HTTP_X_ORIGINAL_USER_AGENT',
961
+ 'HTTP_X_SKYFIRE_PHONE',
962
+ 'HTTP_X_BOLT_PHONE_UA',
963
+ 'HTTP_DEVICE_STOCK_UA',
964
+ 'HTTP_X_UCBROWSER_DEVICE_UA',
965
+ // sometimes, bots (especially Google) use a genuine user agent, but fill this header in with their email address
966
+ 'HTTP_FROM',
967
+ ];
968
+ }
969
+ }
includes/class-cron.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Cron class.
8
+ *
9
+ * @class Post_Views_Counter_Cron
10
+ */
11
+ class Post_Views_Counter_Cron {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @return void
17
+ */
18
+ public function __construct() {
19
+ // actions
20
+ add_action( 'init', [ $this, 'check_cron' ] );
21
+ add_action( 'pvc_reset_counts', [ $this, 'reset_counts' ] );
22
+ add_action( 'pvc_flush_cached_counts', [ $this, 'flush_cached_counts' ] );
23
+
24
+ // filters
25
+ add_filter( 'cron_schedules', [ $this, 'cron_time_intervals' ] );
26
+ }
27
+
28
+ /**
29
+ * Reset daily counts.
30
+ *
31
+ * @global $wpdb
32
+ * @return void
33
+ */
34
+ public function reset_counts() {
35
+ global $wpdb;
36
+
37
+ $counter = [
38
+ 'days' => 1,
39
+ 'weeks' => 7,
40
+ 'months' => 30,
41
+ 'years' => 365
42
+ ];
43
+
44
+ // get main instance
45
+ $pvc = Post_Views_Counter();
46
+
47
+ $wpdb->query( 'DELETE FROM ' . $wpdb->prefix . 'post_views WHERE type = 0 AND 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)' );
48
+ }
49
+
50
+ /**
51
+ * Call Post_Views_Counter_Counter::flush_cache_to_db().
52
+ * This is (un)scheduled on plugin activation/deactivation.
53
+ *
54
+ * @return void
55
+ */
56
+ public function flush_cached_counts() {
57
+ // get counter class
58
+ $counter = Post_Views_Counter()->counter;
59
+
60
+ // caching?
61
+ if ( $counter && $counter->using_object_cache() )
62
+ $counter->flush_cache_to_db();
63
+ }
64
+
65
+ /**
66
+ * Add new cron interval from settings.
67
+ *
68
+ * @param array $schedules
69
+ * @return array
70
+ */
71
+ public function cron_time_intervals( $schedules ) {
72
+ // get main instance
73
+ $pvc = Post_Views_Counter();
74
+
75
+ $schedules['post_views_counter_interval'] = [
76
+ 'interval' => 86400,
77
+ 'display' => __( 'Post Views Counter reset daily counts interval', 'post-views-counter' )
78
+ ];
79
+
80
+ $schedules['post_views_counter_flush_interval'] = [
81
+ 'interval' => $pvc->counter->get_timestamp( $pvc->options['general']['flush_interval']['type'], $pvc->options['general']['flush_interval']['number'], false ),
82
+ 'display' => __( 'Post Views Counter cache flush interval', 'post-views-counter' )
83
+ ];
84
+
85
+ return $schedules;
86
+ }
87
+
88
+ /**
89
+ * Check whether WP Cron needs to add new task.
90
+ *
91
+ * @return void
92
+ */
93
+ public function check_cron() {
94
+ // only for backend
95
+ if ( ! is_admin() )
96
+ return;
97
+
98
+ // get main instance
99
+ $pvc = Post_Views_Counter();
100
+
101
+ // set wp cron task
102
+ if ( $pvc->options['general']['cron_run'] ) {
103
+ // not set or need to be updated?
104
+ if ( ! wp_next_scheduled( 'pvc_reset_counts' ) || $pvc->options['general']['cron_update'] ) {
105
+ // task is added but need to be updated
106
+ if ( $pvc->options['general']['cron_update'] ) {
107
+ // remove old schedule
108
+ wp_clear_scheduled_hook( 'pvc_reset_counts' );
109
+
110
+ // set update to false
111
+ $general = $pvc->options['general'];
112
+ $general['cron_update'] = false;
113
+
114
+ // update settings
115
+ update_option( 'post_views_counter_settings_general', $general );
116
+ }
117
+
118
+ // set schedule
119
+ wp_schedule_event( current_time( 'timestamp', true ) + 86400, 'post_views_counter_interval', 'pvc_reset_counts' );
120
+ }
121
+ } else {
122
+ // remove schedule
123
+ wp_clear_scheduled_hook( 'pvc_reset_counts' );
124
+ remove_action( 'pvc_reset_counts', [ $this, 'reset_counts' ] );
125
+ }
126
+ }
127
+ }
includes/class-dashboard.php ADDED
@@ -0,0 +1,628 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Dashboard class.
8
+ *
9
+ * @class Post_Views_Counter_Dashboard
10
+ */
11
+ class Post_Views_Counter_Dashboard {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @return void
17
+ */
18
+ public function __construct() {
19
+ // actions
20
+ add_action( 'wp_dashboard_setup', [ $this, 'wp_dashboard_setup' ], 1 );
21
+ add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts_styles' ] );
22
+ add_action( 'wp_ajax_pvc_dashboard_most_viewed', [ $this, 'dashboard_get_most_viewed' ] );
23
+ add_action( 'wp_ajax_pvc_dashboard_chart', [ $this, 'dashboard_get_chart' ] );
24
+ add_action( 'wp_ajax_pvc_dashboard_user_options', [ $this, 'update_dashboard_user_options' ] );
25
+ }
26
+
27
+ /**
28
+ * Initialize widget.
29
+ *
30
+ * @return void
31
+ */
32
+ public function wp_dashboard_setup() {
33
+ // filter user_can_see_stats
34
+ if ( ! apply_filters( 'pvc_user_can_see_stats', current_user_can( 'publish_posts' ) ) )
35
+ return;
36
+
37
+ // add dashboard post views chart widget
38
+ wp_add_dashboard_widget( 'pvc_dashboard', __( 'Post Views Counter', 'post-views-counter' ), [ $this, 'dashboard_widget' ] );
39
+ }
40
+
41
+ /**
42
+ * Enqueue admin scripts and styles.
43
+ *
44
+ * @param string $pagenow
45
+ * @return void
46
+ */
47
+ public function admin_scripts_styles( $pagenow ) {
48
+ if ( $pagenow !== 'index.php' )
49
+ return;
50
+
51
+ // filter user_can_see_stats
52
+ if ( ! apply_filters( 'pvc_user_can_see_stats', current_user_can( 'publish_posts' ) ) )
53
+ return;
54
+
55
+ // styles
56
+ wp_enqueue_style( 'pvc-admin-dashboard', POST_VIEWS_COUNTER_URL . '/css/admin-dashboard.css', [], Post_Views_Counter()->defaults['version'] );
57
+ wp_enqueue_style( 'pvc-chartjs', POST_VIEWS_COUNTER_URL . '/assets/chartjs/chart.min.css', [], Post_Views_Counter()->defaults['version'] );
58
+ wp_enqueue_style( 'pvc-microtip', POST_VIEWS_COUNTER_URL . '/assets/microtip/microtip.min.css', [], Post_Views_Counter()->defaults['version'] );
59
+
60
+ // scripts
61
+ wp_register_script( 'pvc-chartjs', POST_VIEWS_COUNTER_URL . '/assets/chartjs/chart.min.js', [ 'jquery' ], Post_Views_Counter()->defaults['version'], true );
62
+ wp_enqueue_script( 'pvc-admin-dashboard', POST_VIEWS_COUNTER_URL . '/js/admin-dashboard.js', [ 'jquery', 'pvc-chartjs' ], Post_Views_Counter()->defaults['version'], true );
63
+
64
+ wp_localize_script(
65
+ 'pvc-admin-dashboard',
66
+ 'pvcArgs',
67
+ [
68
+ 'ajaxURL' => admin_url( 'admin-ajax.php' ),
69
+ 'nonce' => wp_create_nonce( 'pvc-dashboard-chart' ),
70
+ 'nonceUser' => wp_create_nonce( 'pvc-dashboard-user-options' )
71
+ ]
72
+ );
73
+ }
74
+
75
+ /**
76
+ * Render dashboard widget.
77
+ *
78
+ * @return void
79
+ */
80
+ public function dashboard_widget() {
81
+ // get user options
82
+ $user_options = get_user_meta( get_current_user_id(), 'pvc_dashboard', true );
83
+
84
+ // empty options?
85
+ if ( empty( $user_options ) || ! is_array( $user_options ) )
86
+ $user_options = [];
87
+
88
+ // sanitize options
89
+ $user_options = map_deep( $user_options, 'sanitize_text_field' );
90
+ $menu_items = ! empty( $user_options['menu_items'] ) ? $user_options['menu_items'] : [];
91
+
92
+ // generate months
93
+ $months_html = $this->generate_months( current_time( 'timestamp', false ) );
94
+
95
+ ?>
96
+ <div id="pvc-dashboard-accordion" class="pvc-accordion">
97
+ <div id="pvc-post-views" class="pvc-accordion-item<?php echo in_array( 'post-views', $menu_items ) ? ' pvc-collapsed' : ''; ?>">
98
+ <div class="pvc-accordion-header">
99
+ <div class="pvc-accordion-toggle"><span class="pvc-accordion-title"><?php _e( 'Post Views', 'post-views-counter' ); ?></span><span class="pvc-tooltip" aria-label="<?php _e( 'Displays the chart of most viewed post types for a selected time period.', 'post-views-counter' ); ?>" data-microtip-position="top" data-microtip-size="large" role="tooltip"><span class="pvc-tooltip-icon"></span></span></div>
100
+ <?php /*
101
+ <div class="pvc-accordion-actions">
102
+ <a href="javascript:void(0);" class="pvc-accordion-action dashicons dashicons-admin-generic"></a>
103
+ </div>
104
+ */ ?>
105
+ </div>
106
+ <div class="pvc-accordion-content">
107
+ <div class="pvc-dashboard-container loading">
108
+ <div id="pvc-chart-container" class="pvc-data-container">
109
+ <canvas id="pvc-chart" height="175"></canvas>
110
+ <span class="spinner"></span>
111
+ </div>
112
+ <div class="pvc-months">
113
+ <?php echo wp_kses_post( $months_html ); ?>
114
+ </div>
115
+ </div>
116
+ </div>
117
+ </div>
118
+ <div id="pvc-most-viewed" class="pvc-accordion-item<?php echo in_array( 'most-viewed', $menu_items ) ? ' pvc-collapsed' : ''; ?>">
119
+ <div class="pvc-accordion-header">
120
+ <div class="pvc-accordion-toggle"><span class="pvc-accordion-title"><?php _e( 'Top Posts', 'post-views-counter' ); ?></span><span class="pvc-tooltip" aria-label="<?php _e( 'Displays the list of most viewed posts and pages on your website.', 'post-views-counter' ); ?>" data-microtip-position="top" data-microtip-size="large" role="tooltip"><span class="pvc-tooltip-icon"></span></span></div>
121
+ </div>
122
+ <div class="pvc-accordion-content">
123
+ <div class="pvc-dashboard-container loading">
124
+ <div class="pvc-data-container">
125
+ <div id="pvc-viewed" class="pvc-table-responsive"></div>
126
+ <span class="spinner"></span>
127
+ </div>
128
+ <div class="pvc-months">
129
+ <?php echo wp_kses_post( $months_html ); ?>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ <?php
136
+ }
137
+
138
+ /**
139
+ * Dashboard widget chart data function.
140
+ *
141
+ * @global $_wp_admin_css_colors
142
+ * @return void
143
+ */
144
+ public function dashboard_get_chart() {
145
+ if ( ! apply_filters( 'pvc_user_can_see_stats', current_user_can( 'publish_posts' ) ) )
146
+ wp_die( _( 'You do not have permission to access this page.', 'post-views-counter' ) );
147
+
148
+ if ( ! check_ajax_referer( 'pvc-dashboard-chart', 'nonce' ) )
149
+ wp_die( __( 'You do not have permission to access this page.', 'post-views-counter' ) );
150
+
151
+ // get period
152
+ $period = isset( $_POST['period'] ) ? sanitize_text_field( $_POST['period'] ) : 'this_month';
153
+
154
+ // get post types
155
+ $post_types = Post_Views_Counter()->options['general']['post_types_count'];
156
+
157
+ // get stats
158
+ $query_args = [
159
+ 'post_type' => $post_types,
160
+ 'posts_per_page' => -1,
161
+ 'paged' => false,
162
+ 'orderby' => 'post_views',
163
+ 'suppress_filters' => false,
164
+ 'no_found_rows' => true
165
+ ];
166
+
167
+ // $now = getdate( current_time( 'timestamp', get_option( 'gmt_offset' ) ) );
168
+ $now = getdate( current_time( 'timestamp', get_option( 'gmt_offset' ) ) - 2592000 );
169
+
170
+ // get color scheme global
171
+ global $_wp_admin_css_colors;
172
+
173
+ // set default color;
174
+ $color = [
175
+ 'r' => 105,
176
+ 'g' => 168,
177
+ 'b' => 187
178
+ ];
179
+
180
+ if ( ! empty( $_wp_admin_css_colors ) ) {
181
+ // get current admin color scheme name
182
+ $current_color_scheme = get_user_option( 'admin_color' );
183
+
184
+ if ( empty( $current_color_scheme ) )
185
+ $current_color_scheme = 'fresh';
186
+
187
+ if ( isset( $_wp_admin_css_colors[$current_color_scheme] ) )
188
+ $color = $this->hex2rgb( $_wp_admin_css_colors[$current_color_scheme]->colors[2] );
189
+ }
190
+
191
+ // set chart labels
192
+ switch ( $period ) {
193
+ case 'this_week':
194
+ $data = [
195
+ 'text' => [
196
+ 'xAxes' => date_i18n( 'F Y' ),
197
+ 'yAxes' => __( 'Post Views', 'post-views-counter' )
198
+ ]
199
+ ];
200
+
201
+ for ( $day = 0; $day <= 6; $day ++ ) {
202
+ $date = strtotime( $now['mday'] . '-' . $now['mon'] . '-' . $now['year'] . ' + ' . $day . ' days - ' . $now['wday'] . ' days' );
203
+ $query = new WP_Query(
204
+ wp_parse_args(
205
+ $query_args,
206
+ [
207
+ 'views_query' => [
208
+ 'year' => date( 'Y', $date ),
209
+ 'month' => date( 'n', $date ),
210
+ 'day' => date( 'd', $date )
211
+ ]
212
+ ]
213
+ )
214
+ );
215
+
216
+ $data['data']['labels'][] = date_i18n( 'j', $date );
217
+ $data['data']['datasets'][$type_name]['label'] = __( 'Post Views', 'post-views-counter' );
218
+ $data['data']['datasets'][0]['data'][] = $query->total_views;
219
+ }
220
+ break;
221
+
222
+ case 'this_year':
223
+ $data = [
224
+ 'text' => [
225
+ 'xAxes' => __( 'Year', 'post-views-counter' ) . date( ' Y' ),
226
+ 'yAxes' => __( 'Post Views', 'post-views-counter' ),
227
+ ],
228
+ 'design' => [
229
+ 'fill' => true,
230
+ 'backgroundColor' => 'rgba(' . $color['r'] . ',' . $color['g'] . ',' . $color['b'] . ', 0.2)',
231
+ 'borderColor' => 'rgba(' . $color['r'] . ',' . $color['g'] . ',' . $color['b'] . ', 1)',
232
+ 'borderWidth' => 1.2,
233
+ 'borderDash' => [],
234
+ 'pointBorderColor' => 'rgba(' . $color['r'] . ',' . $color['g'] . ',' . $color['b'] . ', 1)',
235
+ 'pointBackgroundColor' => 'rgba(255, 255, 255, 1)',
236
+ 'pointBorderWidth' => 1.2
237
+ ]
238
+ ];
239
+
240
+ $data['data']['datasets'][0]['label'] = __( 'Total Views', 'post-views-counter' );
241
+ $data['data']['datasets'][0]['post_type'] = '_pvc_total_views';
242
+
243
+ // reindex post types
244
+ $post_types = array_combine( range( 1, count( $post_types ) ), array_values( $post_types ) );
245
+
246
+ $post_type_data = [];
247
+
248
+ foreach ( $post_types as $id => $post_type ) {
249
+ $post_type_obj = get_post_type_object( $post_type );
250
+
251
+ // unrecognized post type? (mainly from deactivated plugins)
252
+ if ( empty( $post_type_obj ) )
253
+ $label = $post_type;
254
+ else
255
+ $label = $post_type_obj->labels->name;
256
+
257
+ $data['data']['datasets'][$id]['label'] = $label;
258
+ $data['data']['datasets'][$id]['post_type'] = $post_type;
259
+ $data['data']['datasets'][$id]['data'] = [];
260
+
261
+ // get month views
262
+ $post_type_data[$id] = array_values(
263
+ pvc_get_views(
264
+ [
265
+ 'fields' => 'date=>views',
266
+ 'post_type' => $post_type,
267
+ 'views_query' => [
268
+ 'year' => date( 'Y' ),
269
+ 'month' => '',
270
+ 'week' => '',
271
+ 'day' => ''
272
+ ]
273
+ ]
274
+ )
275
+ );
276
+ }
277
+
278
+ $sum = [];
279
+
280
+ foreach ( $post_type_data as $post_type_id => $post_views ) {
281
+ foreach ( $post_views as $id => $views ) {
282
+ // generate chart data for specific post types
283
+ $data['data']['datasets'][$post_type_id]['data'][] = $views;
284
+
285
+ if ( ! array_key_exists( $id, $sum ) )
286
+ $sum[$id] = 0;
287
+
288
+ $sum[$id] += $views;
289
+ }
290
+ }
291
+
292
+ // this month all days
293
+ for ( $i = 1; $i <= 12; $i ++ ) {
294
+ // generate chart data
295
+ $data['data']['labels'][] = $i;
296
+ $data['data']['dates'][] = date_i18n( 'F Y', strtotime( date( 'Y' ) . '-' . str_pad( $i, 2, '0', STR_PAD_LEFT ) . '-01' ) );
297
+ $data['data']['datasets'][0]['data'][] = $sum[$i - 1];
298
+ }
299
+ break;
300
+
301
+ case 'this_month':
302
+ default:
303
+ $user_options = $this->get_dashboard_user_options( get_current_user_id(), 'post_types' );
304
+
305
+ if ( $period !== 'this_month' ) {
306
+ $date = explode( '|', $period, 2 );
307
+ $months = strtotime( (string) $date[1] . '-' . (string) $date[0] . '-13' );
308
+ } else
309
+ $months = current_time( 'timestamp', false );
310
+
311
+ // get date chunks
312
+ $date = explode( ' ', date( "m Y t F", $months ) );
313
+
314
+ $data = [
315
+ 'months' => $this->generate_months( $months ),
316
+ 'text' => [
317
+ 'xAxes' => $date[3] . ' ' . $date[1],
318
+ 'yAxes' => __( 'Post Views', 'post-views-counter' ),
319
+ ],
320
+ 'design' => [
321
+ 'fill' => true,
322
+ 'backgroundColor' => 'rgba(' . $color['r'] . ',' . $color['g'] . ',' . $color['b'] . ', 0.2)',
323
+ 'borderColor' => 'rgba(' . $color['r'] . ',' . $color['g'] . ',' . $color['b'] . ', 1)',
324
+ 'borderWidth' => 1.2,
325
+ 'borderDash' => [],
326
+ 'pointBorderColor' => 'rgba(' . $color['r'] . ',' . $color['g'] . ',' . $color['b'] . ', 1)',
327
+ 'pointBackgroundColor' => 'rgba(255, 255, 255, 1)',
328
+ 'pointBorderWidth' => 1.2
329
+ ]
330
+ ];
331
+
332
+ $data['data']['datasets'][0]['label'] = __( 'Total Views', 'post-views-counter' );
333
+ $data['data']['datasets'][0]['post_type'] = '_pvc_total_views';
334
+ $data['data']['datasets'][0]['hidden'] = in_array( '_pvc_total_views', $user_options, true );
335
+
336
+ // reindex post types
337
+ $post_types = array_combine( range( 1, count( $post_types ) ), array_values( $post_types ) );
338
+
339
+ $post_type_data = [];
340
+
341
+ foreach ( $post_types as $id => $post_type ) {
342
+ $post_type_obj = get_post_type_object( $post_type );
343
+
344
+ // unrecognized post type? (mainly from deactivated plugins)
345
+ if ( empty( $post_type_obj ) )
346
+ $label = $post_type;
347
+ else
348
+ $label = $post_type_obj->labels->name;
349
+
350
+ $data['data']['datasets'][$id]['label'] = $label;
351
+ $data['data']['datasets'][$id]['post_type'] = $post_type;
352
+ $data['data']['datasets'][$id]['hidden'] = in_array( $post_type, $user_options, true );
353
+ $data['data']['datasets'][$id]['data'] = [];
354
+
355
+ // get month views
356
+ $post_type_data[$id] = array_values(
357
+ pvc_get_views(
358
+ [
359
+ 'fields' => 'date=>views',
360
+ 'post_type' => $post_type,
361
+ 'views_query' => [
362
+ 'year' => $date[1],
363
+ 'month' => $date[0],
364
+ 'week' => '',
365
+ 'day' => ''
366
+ ]
367
+ ]
368
+ )
369
+ );
370
+ }
371
+
372
+ $sum = [];
373
+
374
+ foreach ( $post_type_data as $post_type_id => $post_views ) {
375
+ foreach ( $post_views as $id => $views ) {
376
+ // generate chart data for specific post types
377
+ $data['data']['datasets'][$post_type_id]['data'][] = $views;
378
+
379
+ if ( ! array_key_exists( $id, $sum ) )
380
+ $sum[$id] = 0;
381
+
382
+ $sum[$id] += $views;
383
+ }
384
+ }
385
+
386
+ // this month all days
387
+ for ( $i = 1; $i <= $date[2]; $i ++ ) {
388
+ // generate chart data
389
+ $data['data']['labels'][] = ( $i % 2 === 0 ? '' : $i );
390
+ $data['data']['dates'][] = date_i18n( get_option( 'date_format' ), strtotime( $date[1] . '-' . $date[0] . '-' . str_pad( $i, 2, '0', STR_PAD_LEFT ) ) );
391
+ $data['data']['datasets'][0]['data'][] = $sum[$i - 1];
392
+ }
393
+ break;
394
+ }
395
+
396
+ echo json_encode( $data );
397
+
398
+ exit;
399
+ }
400
+
401
+ /**
402
+ * Dashboard widget chart data function.
403
+ *
404
+ * @global $_wp_admin_css_colors
405
+ * @return void
406
+ */
407
+ public function dashboard_get_most_viewed() {
408
+ if ( ! apply_filters( 'pvc_user_can_see_stats', current_user_can( 'publish_posts' ) ) )
409
+ wp_die( _( 'You do not have permission to access this page.', 'post-views-counter' ) );
410
+
411
+ if ( ! check_ajax_referer( 'pvc-dashboard-chart', 'nonce' ) )
412
+ wp_die( __( 'You do not have permission to access this page.', 'post-views-counter' ) );
413
+
414
+ // get period
415
+ $period = isset( $_POST['period'] ) ? sanitize_text_field( $_POST['period'] ) : 'this_month';
416
+
417
+ // get post types
418
+ $post_types = Post_Views_Counter()->options['general']['post_types_count'];
419
+
420
+ if ( $period !== 'this_month' ) {
421
+ $date = explode( '|', $period, 2 );
422
+ $months = strtotime( (string) $date[1] . '-' . (string) $date[0] . '-13' );
423
+ } else
424
+ $months = current_time( 'timestamp', false );
425
+
426
+ // get date chunks
427
+ $date = explode( ' ', date( "m Y t F", $months ) );
428
+
429
+ // get stats
430
+ $query_args = [
431
+ 'post_type' => $post_types,
432
+ 'posts_per_page' => 10,
433
+ 'paged' => false,
434
+ 'suppress_filters' => false,
435
+ 'no_found_rows' => true,
436
+ 'views_query' => [
437
+ 'year' => $date[1],
438
+ 'month' => $date[0],
439
+ ]
440
+ ];
441
+
442
+ $posts = pvc_get_most_viewed_posts( $query_args );
443
+
444
+ $data = [
445
+ 'months' => $this->generate_months( $months ),
446
+ 'html' => '',
447
+ ];
448
+
449
+ $html = '<table id="pvc-most-viewed-table" class="pvc-table pvc-table-hover">';
450
+ $html .= '<thead>';
451
+ $html .= '<tr>';
452
+ $html .= '<th scope="col">#</th>';
453
+ $html .= '<th scope="col">' . __( 'Post', 'post-views-counter' ) . '</th>';
454
+ $html .= '<th scope="col">' . __( 'Post Views', 'post-views-counter' ) . '</th>';
455
+ $html .= '</tr>';
456
+ $html .= '</thead>';
457
+ $html .= '<tbody>';
458
+
459
+ if ( $posts ) {
460
+ foreach ( $posts as $index => $post ) {
461
+ setup_postdata( $post );
462
+
463
+ $html .= '<tr>';
464
+ $html .= '<th scope="col">' . ( $index + 1 ) . '</th>';
465
+
466
+ if ( current_user_can( 'edit_post', $post->ID ) )
467
+ $html .= '<td><a href="' . get_edit_post_link( $post->ID ) . '">' . get_the_title( $post ) . '</a></td>';
468
+ else
469
+ $html .= '<td>' . get_the_title( $post ). '</td>';
470
+
471
+ $html .= '<td>' . number_format_i18n( $post->post_views ) . '</td>';
472
+ $html .= '</tr>';
473
+ }
474
+ } else {
475
+ $html .= '<tr class="no-posts">';
476
+ $html .= '<td colspan="3">' . __( 'No most viewed posts found', 'post-views-counter' ) . '</td>';
477
+ $html .= '</tr>';
478
+ }
479
+
480
+ $html .= '</tbody>';
481
+ $html .= '</table>';
482
+
483
+ $data['html'] = $html;
484
+
485
+ echo json_encode( $data );
486
+
487
+ exit;
488
+ }
489
+
490
+ /**
491
+ * Generate months.
492
+ *
493
+ * @param int $timestamp
494
+ * @return string
495
+ */
496
+ public function generate_months( $timestamp ) {
497
+ $dates = [
498
+ explode( ' ', date( "m F Y", strtotime( "-1 months", $timestamp ) ) ),
499
+ explode( ' ', date( "m F Y", $timestamp ) ),
500
+ explode( ' ', date( "m F Y", strtotime( "+1 months", $timestamp ) ) )
501
+ ];
502
+
503
+ $current = date( "Ym", current_time( 'timestamp', false ) );
504
+
505
+ if ( (int) $current <= (int) ( $dates[1][2] . $dates[1][0] ) )
506
+ $next = '<span class="next">' . $dates[2][1] . ' ' . $dates[2][2] . ' ›</span>';
507
+ else
508
+ $next = '<a class="next" href="#" data-date="' . ( $dates[2][0] . '|' . $dates[2][2] ) . '">' . $dates[2][1] . ' ' . $dates[2][2] . ' ›</a>';
509
+
510
+ $dates = [
511
+ 'prev' => '<a class="prev" href="#" data-date="' . ( $dates[0][0] . '|' . $dates[0][2] ) . '">‹ ' . $dates[0][1] . ' ' . $dates[0][2] . '</a>',
512
+ 'current' => '<span class="current">' . $dates[1][1] . ' ' . $dates[1][2] . '</span>',
513
+ 'next' => $next
514
+ ];
515
+
516
+ return $dates['prev'] . $dates['current'] . $dates['next'];
517
+ }
518
+
519
+ /**
520
+ * Dashboard widget chart user post types.
521
+ *
522
+ * @return void
523
+ */
524
+ public function update_dashboard_user_options() {
525
+ if ( ! check_ajax_referer( 'pvc-dashboard-user-options', 'nonce' ) )
526
+ wp_die( __( 'You do not have permission to access this page.', 'post-views-counter' ) );
527
+
528
+ // get allowed post types
529
+ $allowed_post_types = Post_Views_Counter()->options['general']['post_types_count'];
530
+
531
+ // simulate total views as post type
532
+ $allowed_post_types[] = '_pvc_total_views';
533
+
534
+ // get allowed menu items
535
+ $allowed_menu_items = [ 'post-views', 'most-viewed' ];
536
+
537
+ // valid data?
538
+ if ( isset( $_POST['nonce'], $_POST['options'] ) && ! empty( $_POST['options'] ) ) {
539
+ // get options
540
+ $update = map_deep( $_POST['options'], 'sanitize_text_field' );
541
+
542
+ // get user ID
543
+ $user_id = get_current_user_id();
544
+
545
+ // get user dashboard data
546
+ $user_options = get_user_meta( $user_id, 'pvc_dashboard', true );
547
+
548
+ // empty userdata?
549
+ if ( ! is_array( $user_options ) || empty( $user_options ) )
550
+ $user_options = [];
551
+
552
+ // empty post types?
553
+ if ( ! array_key_exists( 'post_types', $user_options ) || ! is_array( $user_options['post_types'] ) )
554
+ $user_options['post_types'] = [];
555
+
556
+ // hide post type?
557
+ if ( ! empty( $update['post_type'] ) && in_array( $update['post_type'], $allowed_post_types, true ) ) {
558
+ if ( isset( $update['hidden'] ) && $update['hidden'] === 'true' ) {
559
+ if ( ! in_array( $update['post_type'], $user_options['post_types'], true ) )
560
+ $user_options['post_types'][] = $update['post_type'];
561
+ } else {
562
+ if ( ( $key = array_search( $update['post_type'], $user_options['post_types'] ) ) !== false )
563
+ unset( $user_options['post_types'][$key] );
564
+ }
565
+ }
566
+
567
+ // hide menu item?
568
+ $user_options['menu_items'] = [];
569
+
570
+ if ( ! empty( $update['menu_items'] ) && is_array( $update['menu_items'] ) ) {
571
+ $update['menu_items'] = map_deep( $update['menu_items'], 'sanitize_text_field' );
572
+
573
+ foreach ( $update['menu_items'] as $menu_item => $hidden ) {
574
+ if ( in_array( $menu_item, $allowed_menu_items ) && $hidden === 'true' )
575
+ $user_options['menu_items'][] = $menu_item;
576
+ }
577
+ }
578
+
579
+ // update userdata
580
+ update_user_meta( $user_id, 'pvc_dashboard', $user_options );
581
+ }
582
+
583
+ exit;
584
+ }
585
+
586
+ /**
587
+ * Get user dashboard data.
588
+ *
589
+ * @param int $user_id
590
+ * @param string $data_type
591
+ * @return array
592
+ */
593
+ public function get_dashboard_user_options( $user_id, $data_type ) {
594
+ $user_options = get_user_meta( $user_id, 'pvc_dashboard', true );
595
+
596
+ if ( ! is_array( $user_options ) || empty( $user_options ) )
597
+ $user_options = [];
598
+
599
+ if ( ! array_key_exists( $data_type, $user_options ) || ! is_array( $user_options[$data_type] ) )
600
+ $user_options[$data_type] = [];
601
+
602
+ return $user_options[$data_type];
603
+ }
604
+
605
+ /**
606
+ * Convert hex to rgb color.
607
+ *
608
+ * @param string $color
609
+ * @return bool
610
+ */
611
+ public function hex2rgb( $color ) {
612
+ if ( $color[0] == '#' )
613
+ $color = substr( $color, 1 );
614
+
615
+ if ( strlen( $color ) == 6 )
616
+ list( $r, $g, $b ) = [ $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] ];
617
+ elseif ( strlen( $color ) == 3 )
618
+ list( $r, $g, $b ) = [ $color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2] ];
619
+ else
620
+ return false;
621
+
622
+ $r = hexdec( $r );
623
+ $g = hexdec( $g );
624
+ $b = hexdec( $b );
625
+
626
+ return [ 'r' => $r, 'g' => $g, 'b' => $b ];
627
+ }
628
+ }
includes/class-frontend.php ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Frontend class.
8
+ *
9
+ * @class Post_Views_Counter_Frontend
10
+ */
11
+ class Post_Views_Counter_Frontend {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @return void
17
+ */
18
+ public function __construct() {
19
+ // actions
20
+ add_action( 'after_setup_theme', [ $this, 'register_shortcode' ] );
21
+ add_action( 'wp_enqueue_scripts', [ $this, 'wp_enqueue_scripts' ] );
22
+ add_action( 'wp', [ $this, 'run' ] );
23
+ }
24
+
25
+ /**
26
+ * Register post-views shortcode function.
27
+ *
28
+ * @return void
29
+ */
30
+ public function register_shortcode() {
31
+ add_shortcode( 'post-views', [ $this, 'post_views_shortcode' ] );
32
+ }
33
+
34
+ /**
35
+ * Post views shortcode function.
36
+ *
37
+ * @param array $args
38
+ * @return string
39
+ */
40
+ public function post_views_shortcode( $args ) {
41
+ $defaults = [
42
+ 'id' => get_the_ID()
43
+ ];
44
+
45
+ $args = shortcode_atts( $defaults, $args );
46
+
47
+ return pvc_post_views( $args['id'], false );
48
+ }
49
+
50
+ /**
51
+ * Set up plugin hooks.
52
+ *
53
+ * @return void
54
+ */
55
+ public function run() {
56
+ if ( is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) )
57
+ return;
58
+
59
+ $filter = apply_filters( 'pvc_shortcode_filter_hook', Post_Views_Counter()->options['display']['position'] );
60
+
61
+ if ( ! empty( $filter ) && in_array( $filter, [ 'before', 'after' ] ) ) {
62
+ // post content
63
+ add_filter( 'the_content', [ $this, 'add_post_views_count' ] );
64
+
65
+ // bbpress support
66
+ add_action( 'bbp_template_' . $filter . '_single_topic', [ $this, 'display_bbpress_post_views' ] );
67
+ add_action( 'bbp_template_' . $filter . '_single_forum', [ $this, 'display_bbpress_post_views' ] );
68
+ } else {
69
+ // custom
70
+ if ( $filter !== 'manual' && is_string( $filter ) )
71
+ add_filter( $filter, [ $this, 'add_post_views_count' ] );
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Add post views counter to forum/topic of bbPress.
77
+ *
78
+ * @return void
79
+ */
80
+ public function display_bbpress_post_views() {
81
+ $post_id = get_the_ID();
82
+
83
+ // check only for forums and topics
84
+ if ( bbp_is_forum( $post_id ) || bbp_is_topic( $post_id ) )
85
+ echo $this->add_post_views_count( '' );
86
+ }
87
+
88
+ /**
89
+ * Add post views counter to content.
90
+ *
91
+ * @param string $content
92
+ * @return string
93
+ */
94
+ public function add_post_views_count( $content = '' ) {
95
+ // get main instance
96
+ $pvc = Post_Views_Counter();
97
+
98
+ $display = false;
99
+
100
+ // page visibility check
101
+ if ( ! empty( $pvc->options['display']['page_types_display'] ) ) {
102
+ foreach ( $pvc->options['display']['page_types_display'] as $page ) {
103
+ switch ( $page ) {
104
+ case 'singular':
105
+ if ( is_singular( $pvc->options['display']['post_types_display'] ) )
106
+ $display = true;
107
+ break;
108
+
109
+ case 'archive':
110
+ if ( is_archive() )
111
+ $display = true;
112
+ break;
113
+
114
+ case 'search':
115
+ if ( is_search() )
116
+ $display = true;
117
+ break;
118
+
119
+ case 'home':
120
+ if ( is_home() || is_front_page() )
121
+ $display = true;
122
+ break;
123
+ }
124
+ }
125
+ }
126
+
127
+ // get groups to check it faster
128
+ $groups = $pvc->options['display']['restrict_display']['groups'];
129
+
130
+ // whether to display views
131
+ if ( is_user_logged_in() ) {
132
+ // exclude logged in users?
133
+ if ( in_array( 'users', $groups, true ) )
134
+ $display = false;
135
+ // exclude specific roles?
136
+ elseif ( in_array( 'roles', $groups, true ) && $pvc->counter->is_user_role_excluded( get_current_user_id(), $pvc->options['display']['restrict_display']['roles'] ) )
137
+ $display = false;
138
+ // exclude guests?
139
+ } elseif ( in_array( 'guests', $groups, true ) )
140
+ $display = false;
141
+
142
+ // we don't want to mess custom loops
143
+ if ( ! in_the_loop() && ! class_exists( 'bbPress' ) )
144
+ $display = false;
145
+
146
+ if ( (bool) apply_filters( 'pvc_display_views_count', $display ) === true ) {
147
+ $filter = apply_filters( 'pvc_shortcode_filter_hook', $pvc->options['display']['position'] );
148
+
149
+ switch ( $filter ) {
150
+ case 'after':
151
+ $content = $content . do_shortcode( '[post-views]' );
152
+ break;
153
+
154
+ case 'before':
155
+ $content = do_shortcode( '[post-views]' ) . $content;
156
+ break;
157
+
158
+ case 'manual':
159
+ default:
160
+ break;
161
+ }
162
+ }
163
+
164
+ return $content;
165
+ }
166
+
167
+ /**
168
+ * Enqueue frontend scripts and styles.
169
+ *
170
+ * @return void
171
+ */
172
+ public function wp_enqueue_scripts() {
173
+ // get main instance
174
+ $pvc = Post_Views_Counter();
175
+
176
+ // enable styles?
177
+ if ( (bool) apply_filters( 'pvc_enqueue_styles', true ) === true ) {
178
+ // load dashicons
179
+ wp_enqueue_style( 'dashicons' );
180
+
181
+ // load style
182
+ wp_enqueue_style( 'post-views-counter-frontend', POST_VIEWS_COUNTER_URL . '/css/frontend.css', [], $pvc->defaults['version'] );
183
+ }
184
+
185
+ // get countable post types
186
+ $post_types = $pvc->options['general']['post_types_count'];
187
+
188
+ // whether to count this post type or not
189
+ if ( empty( $post_types ) || ! is_singular( $post_types ) )
190
+ return;
191
+
192
+ // get counter mode
193
+ $mode = $pvc->options['general']['counter_mode'];
194
+
195
+ // specific counter mode?
196
+ if ( in_array( $mode, [ 'js', 'rest_api' ], true ) ) {
197
+ wp_enqueue_script( 'post-views-counter-frontend', POST_VIEWS_COUNTER_URL . '/js/frontend.js', [ 'jquery' ], $pvc->defaults['version'], true );
198
+
199
+ $args = [
200
+ 'mode' => $mode,
201
+ 'postID' => get_the_ID(),
202
+ 'nonce' => ( $mode === 'rest_api' ? wp_create_nonce( 'wp_rest' ) : wp_create_nonce( 'pvc-check-post' ) )
203
+ ];
204
+
205
+ switch ( $mode ) {
206
+ case 'rest_api':
207
+ $args['requestURL'] = rest_url( 'post-views-counter/view-post/' );
208
+ break;
209
+
210
+ case 'js':
211
+ default:
212
+ $args['requestURL'] = admin_url( 'admin-ajax.php' );
213
+ break;
214
+ }
215
+
216
+ // make it safe
217
+ $args['requestURL'] = esc_url_raw( $args['requestURL'] );
218
+
219
+ wp_localize_script( 'post-views-counter-frontend', 'pvcArgsFrontend', apply_filters( 'pvc_frontend_script_args', $args ) );
220
+ }
221
+ }
222
+ }
includes/class-functions.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Functions class.
8
+ *
9
+ * @class Post_Views_Counter_Functions
10
+ */
11
+ class Post_Views_Counter_Functions {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @return void
17
+ */
18
+ public function __construct() {}
19
+
20
+ /**
21
+ * Get post types available for counting.
22
+ *
23
+ * @return array
24
+ */
25
+ public function get_post_types() {
26
+ $post_types = [];
27
+
28
+ // built in public post types
29
+ foreach ( get_post_types( [ '_builtin' => true, 'public' => true ], 'objects', 'and' ) as $key => $post_type ) {
30
+ $post_types[$key] = $post_type->labels->name;
31
+ }
32
+
33
+ // public custom post types
34
+ foreach ( get_post_types( [ '_builtin' => false, 'public' => true ], 'objects', 'and' ) as $key => $post_type ) {
35
+ $post_types[$key] = $post_type->labels->name;
36
+ }
37
+
38
+ // remove bbPress replies
39
+ if ( class_exists( 'bbPress' ) && isset( $post_types['reply'] ) )
40
+ unset( $post_types['reply'] );
41
+
42
+ // filter post types
43
+ $post_types = apply_filters( 'pvc_available_post_types', $post_types );
44
+
45
+ // sort post types alphabetically with their keys
46
+ asort( $post_types, SORT_STRING );
47
+
48
+ return $post_types;
49
+ }
50
+
51
+ /**
52
+ * Get all user roles.
53
+ *
54
+ * @global object $wp_roles
55
+ * @return array
56
+ */
57
+ public function get_user_roles() {
58
+ global $wp_roles;
59
+
60
+ $roles = [];
61
+
62
+ foreach ( apply_filters( 'editable_roles', $wp_roles->roles ) as $role => $details ) {
63
+ $roles[$role] = translate_user_role( $details['name'] );
64
+ }
65
+
66
+ // sort user roles alphabetically with their keys
67
+ asort( $roles, SORT_STRING );
68
+
69
+ return $roles;
70
+ }
71
+ }
includes/class-query.php ADDED
@@ -0,0 +1,379 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Query class.
8
+ *
9
+ * @class Post_Views_Counter_Query
10
+ */
11
+ class Post_Views_Counter_Query {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @return void
17
+ */
18
+ public function __construct() {
19
+ // actions
20
+ add_action( 'pre_get_posts', [ $this, 'extend_pre_query' ], 1 );
21
+
22
+ // filters
23
+ add_filter( 'query_vars', [ $this, 'query_vars' ] );
24
+ add_filter( 'posts_join', [ $this, 'posts_join' ], 10, 2 );
25
+ add_filter( 'posts_groupby', [ $this, 'posts_groupby' ], 10, 2 );
26
+ add_filter( 'posts_orderby', [ $this, 'posts_orderby' ], 10, 2 );
27
+ add_filter( 'posts_fields', [ $this, 'posts_fields' ], 10, 2 );
28
+ add_filter( 'the_posts', [ $this, 'the_posts' ], 10, 2 );
29
+ }
30
+
31
+ /**
32
+ * Register views_query var.
33
+ *
34
+ * @param array $query_vars
35
+ * @return array
36
+ */
37
+ public function query_vars( $query_vars ) {
38
+ $query_vars[] = 'views_query';
39
+
40
+ return $query_vars;
41
+ }
42
+
43
+ /**
44
+ * Extend query with post_views orderby parameter.
45
+ *
46
+ * @param object $query
47
+ * @return void
48
+ */
49
+ public function extend_pre_query( $query ) {
50
+ if ( isset( $query->query_vars['orderby'] ) && $query->query_vars['orderby'] === 'post_views' )
51
+ $query->pvc_orderby = true;
52
+ }
53
+
54
+ /**
55
+ * Modify the database query to use post_views parameter.
56
+ *
57
+ * @global object $wpdb
58
+ * @param string $join
59
+ * @param object $query
60
+ * @return string
61
+ */
62
+ public function posts_join( $join, $query ) {
63
+ $sql = '';
64
+ $query_chunks = [];
65
+
66
+ // views query?
67
+ if ( ! empty( $query->query['views_query'] ) ) {
68
+ if ( isset( $query->query['views_query']['inclusive'] ) )
69
+ $query->query['views_query']['inclusive'] = (bool) $query->query['views_query']['inclusive'];
70
+ else
71
+ $query->query['views_query']['inclusive'] = true;
72
+
73
+ // check after and before dates
74
+ foreach ( [ 'after' => '>', 'before' => '<' ] as $date => $type ) {
75
+ $year_ = null;
76
+ $month_ = null;
77
+ $week_ = null;
78
+ $day_ = null;
79
+
80
+ // check views query date
81
+ if ( ! empty( $query->query['views_query'][$date] ) ) {
82
+ // is it a date array?
83
+ if ( is_array( $query->query['views_query'][$date] ) ) {
84
+ // check views query $date date year
85
+ if ( ! empty( $query->query['views_query'][$date]['year'] ) )
86
+ $year_ = str_pad( (int) $query->query['views_query'][$date]['year'], 4, 0, STR_PAD_LEFT );
87
+
88
+ // check views query date month
89
+ if ( ! empty( $query->query['views_query'][$date]['month'] ) )
90
+ $month_ = str_pad( (int) $query->query['views_query'][$date]['month'], 2, 0, STR_PAD_LEFT );
91
+
92
+ // check views query date week
93
+ if ( ! empty( $query->query['views_query'][$date]['week'] ) )
94
+ $week_ = str_pad( (int) $query->query['views_query'][$date]['week'], 2, 0, STR_PAD_LEFT );
95
+
96
+ // check views query date day
97
+ if ( ! empty( $query->query['views_query'][$date]['day'] ) )
98
+ $day_ = str_pad( (int) $query->query['views_query'][$date]['day'], 2, 0, STR_PAD_LEFT );
99
+ // is it a date string?
100
+ } elseif ( is_string( $query->query['views_query'][$date] ) ) {
101
+ $time_ = strtotime( $query->query['views_query'][$date] );
102
+
103
+ // valid datetime?
104
+ if ( $time_ !== false ) {
105
+ // week does not exists here, string dates are always treated as year + month + day
106
+ list( $day_, $month_, $year_ ) = explode( ' ', date( "d m Y", $time_ ) );
107
+ }
108
+ }
109
+
110
+ // valid date?
111
+ if ( ! ( $year_ === null && $month_ === null && $week_ === null && $day_ === null ) ) {
112
+ $query_chunks[] = [
113
+ 'year' => $year_,
114
+ 'month' => $month_,
115
+ 'day' => $day_,
116
+ 'week' => $week_,
117
+ 'type' => $type . ( $query->query['views_query']['inclusive'] ? '=' : '' )
118
+ ];
119
+ }
120
+ }
121
+ }
122
+
123
+ // any after, before query chunks?
124
+ if ( ! empty( $query_chunks ) ) {
125
+ $valid_dates = true;
126
+
127
+ // check only if both dates are in query
128
+ if ( count( $query_chunks ) === 2 ) {
129
+ // before and after dates should be the same
130
+ foreach ( [ 'year', 'month', 'day', 'week' ] as $date_type ) {
131
+ if ( ! ( ( $query_chunks[0][$date_type] !== null && $query_chunks[1][$date_type] !== null ) || ( $query_chunks[0][$date_type] === null && $query_chunks[1][$date_type] === null ) ) )
132
+ $valid_dates = false;
133
+ }
134
+ }
135
+
136
+ // after and before dates should be
137
+ if ( $valid_dates ) {
138
+ foreach ( $query_chunks as $chunk ) {
139
+ // year
140
+ if ( isset( $chunk['year'] ) ) {
141
+ // year, week
142
+ if ( isset( $chunk['week'] ) )
143
+ $sql .= " AND pvc.type = 1 AND pvc.period " . $chunk['type'] . " '" . $chunk['year'] . $chunk['week'] . "'";
144
+ // year, month
145
+ elseif ( isset( $chunk['month'] ) ) {
146
+ // year, month, day
147
+ if ( isset( $chunk['day'] ) )
148
+ $sql .= " AND pvc.type = 0 AND pvc.period " . $chunk['type'] . " '" . $chunk['year'] . $chunk['month'] . $chunk['day'] . "'";
149
+ // year, month
150
+ else
151
+ $sql .= " AND pvc.type = 2 AND pvc.period " . $chunk['type'] . " '" . $chunk['year'] . $chunk['month'] . "'";
152
+ // year
153
+ } else
154
+ $sql .= " AND pvc.type = 3 AND pvc.period " . $chunk['type'] . " '" . $chunk['year'] . "'";
155
+ // month
156
+ } elseif ( isset( $chunk['month'] ) ) {
157
+ // month, day
158
+ if ( isset( $chunk['day'] ) )
159
+ $sql .= " AND pvc.type = 0 AND RIGHT( pvc.period, 4 ) " . $chunk['type'] . " '" . $chunk['month'] . $chunk['day'] . "'";
160
+ // month
161
+ else
162
+ $sql .= " AND pvc.type = 2 AND RIGHT( pvc.period, 2 ) " . $chunk['type'] . " '" . $chunk['month'] . "'";
163
+ // week
164
+ } elseif ( isset( $chunk['week'] ) )
165
+ $sql .= " AND pvc.type = 1 AND RIGHT( pvc.period, 2 ) " . $chunk['type'] . " '" . $chunk['week'] . "'";
166
+ // day
167
+ elseif ( isset( $chunk['day'] ) )
168
+ $sql .= " AND pvc.type = 0 AND RIGHT( pvc.period, 2 ) " . $chunk['type'] . " '" . $chunk['day'] . "'";
169
+ }
170
+ }
171
+ }
172
+
173
+ // standard query
174
+ if ( $sql === '' ) {
175
+ // check year
176
+ if ( isset( $query->query['views_query']['year'] ) )
177
+ $year = (int) $query->query['views_query']['year'];
178
+
179
+ // check month
180
+ if ( isset( $query->query['views_query']['month'] ) )
181
+ $month = (int) $query->query['views_query']['month'];
182
+
183
+ // check week
184
+ if ( isset( $query->query['views_query']['week'] ) )
185
+ $week = (int) $query->query['views_query']['week'];
186
+
187
+ // check day
188
+ if ( isset( $query->query['views_query']['day'] ) )
189
+ $day = (int) $query->query['views_query']['day'];
190
+
191
+ // year
192
+ if ( isset( $year ) ) {
193
+ // year, week
194
+ if ( isset( $week ) && $this->is_date_valid( 'yw', $year, 0, 0, $week ) )
195
+ $sql = " AND pvc.type = 1 AND pvc.period = '" . str_pad( $year, 4, 0, STR_PAD_LEFT ) . str_pad( $week, 2, 0, STR_PAD_LEFT ) . "'";
196
+ // year, month
197
+ elseif ( isset( $month ) ) {
198
+ // year, month, day
199
+ if ( isset( $day ) && $this->is_date_valid( 'ymd', $year, $month, $day ) )
200
+ $sql = " AND pvc.type = 0 AND pvc.period = '" . str_pad( $year, 4, 0, STR_PAD_LEFT ) . str_pad( $month, 2, 0, STR_PAD_LEFT ) . str_pad( $day, 2, 0, STR_PAD_LEFT ) . "'";
201
+ // year, month
202
+ elseif ( $this->is_date_valid( 'ym', $year, $month ) )
203
+ $sql = " AND pvc.type = 2 AND pvc.period = '" . str_pad( $year, 4, 0, STR_PAD_LEFT ) . str_pad( $month, 2, 0, STR_PAD_LEFT ) . "'";
204
+ // year
205
+ } elseif ( $this->is_date_valid( 'y', $year ) )
206
+ $sql = " AND pvc.type = 3 AND pvc.period = '" . str_pad( $year, 4, 0, STR_PAD_LEFT ) . "'";
207
+ // month
208
+ } elseif ( isset( $month ) ) {
209
+ // month, day
210
+ if ( isset( $day ) && $this->is_date_valid( 'md', 0, $month, $day ) ) {
211
+ $sql = " AND pvc.type = 0 AND RIGHT( pvc.period, 4 ) = '" . str_pad( $month, 2, 0, STR_PAD_LEFT ) . str_pad( $day, 2, 0, STR_PAD_LEFT ) . "'";
212
+ // month
213
+ } elseif ( $this->is_date_valid( 'm', 0, $month ) )
214
+ $sql = " AND pvc.type = 2 AND RIGHT( pvc.period, 2 ) = '" . str_pad( $month, 2, 0, STR_PAD_LEFT ) . "'";
215
+ // week
216
+ } elseif ( isset( $week ) && $this->is_date_valid( 'w', 0, 0, 0, $week ) )
217
+ $sql = " AND pvc.type = 1 AND RIGHT( pvc.period, 2 ) = '" . str_pad( $week, 2, 0, STR_PAD_LEFT ) . "'";
218
+ // day
219
+ elseif ( isset( $day ) && $this->is_date_valid( 'd', 0, 0, $day ) )
220
+ $sql = " AND pvc.type = 0 AND RIGHT( pvc.period, 2 ) = '" . str_pad( $day, 2, 0, STR_PAD_LEFT ) . "'";
221
+ }
222
+
223
+ if ( $sql !== '' )
224
+ $query->pvc_query = true;
225
+ }
226
+
227
+ // is it sorted by post views?
228
+ if ( ( $sql === '' && isset( $query->pvc_orderby ) && $query->pvc_orderby ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true )
229
+ $sql = ' AND pvc.type = 4';
230
+
231
+ // add date range
232
+ if ( $sql !== '' ) {
233
+ global $wpdb;
234
+
235
+ $join .= " LEFT JOIN " . $wpdb->prefix . "post_views pvc ON pvc.id = " . $wpdb->prefix . "posts.ID" . $sql;
236
+ }
237
+
238
+ return $join;
239
+ }
240
+
241
+ /**
242
+ * Group posts using the post ID.
243
+ *
244
+ * @global object $wpdb
245
+ * @param string $groupby
246
+ * @param object $query
247
+ * @return string
248
+ */
249
+ public function posts_groupby( $groupby, $query ) {
250
+ // is it sorted by post views or views_query is used?
251
+ if ( ( isset( $query->pvc_orderby ) && $query->pvc_orderby ) || ( isset( $query->pvc_query ) && $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true ) {
252
+ global $pagenow;
253
+
254
+ // needed only for sorting
255
+ if ( $pagenow === 'upload.php' || $pagenow === 'edit.php' )
256
+ $query->query['views_query']['hide_empty'] = false;
257
+
258
+ global $wpdb;
259
+
260
+ $groupby = trim( $groupby );
261
+
262
+ if ( strpos( $groupby, $wpdb->prefix . 'posts.ID' ) === false )
263
+ $groupby = ( $groupby !== '' ? $groupby . ', ' : '') . $wpdb->prefix . 'posts.ID';
264
+
265
+ if ( ! isset( $query->query['views_query']['hide_empty'] ) || $query->query['views_query']['hide_empty'] === true )
266
+ $groupby .= ' HAVING post_views > 0';
267
+ }
268
+
269
+ return $groupby;
270
+ }
271
+
272
+ /**
273
+ * Order posts by post views.
274
+ *
275
+ * @global object $wpdb
276
+ * @param string $orderby
277
+ * @param object $query
278
+ * @return string
279
+ */
280
+ public function posts_orderby( $orderby, $query ) {
281
+ // is it sorted by post views?
282
+ if ( ( isset( $query->pvc_orderby ) && $query->pvc_orderby ) ) {
283
+ global $wpdb;
284
+
285
+ $order = $query->get( 'order' );
286
+ $orderby = ( ! isset( $query->query['views_query']['hide_empty'] ) || $query->query['views_query']['hide_empty'] === true ? 'post_views' : 'pvc.count' ) . ' ' . $order . ', ' . $wpdb->prefix . 'posts.ID ' . $order;
287
+ }
288
+
289
+ return $orderby;
290
+ }
291
+
292
+ /**
293
+ * Return post views in queried post objects.
294
+ *
295
+ * @param string $fields
296
+ * @param object $query
297
+ * @return string
298
+ */
299
+ public function posts_fields( $fields, $query ) {
300
+ if ( ( ! isset( $query->query['fields'] ) || $query->query['fields'] === '' ) && ( ( isset( $query->pvc_orderby ) && $query->pvc_orderby ) || ( isset( $query->pvc_query ) && $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true ) )
301
+ $fields = $fields . ', SUM( IFNULL( pvc.count, 0 ) ) AS post_views';
302
+
303
+ return $fields;
304
+ }
305
+
306
+ /**
307
+ * Extend query object with total post views.
308
+ *
309
+ * @param array $posts
310
+ * @param object $query
311
+ * @return array
312
+ */
313
+ public function the_posts( $posts, $query ) {
314
+ if ( ( isset( $query->pvc_orderby ) && $query->pvc_orderby ) || ( isset( $query->pvc_query ) && $query->pvc_query ) || apply_filters( 'pvc_extend_post_object', false, $query ) === true ) {
315
+ $sum = 0;
316
+
317
+ // any posts found?
318
+ if ( ! empty( $posts ) ) {
319
+ foreach ( $posts as $post ) {
320
+ if ( ! empty( $post->post_views ) )
321
+ $sum += (int) $post->post_views;
322
+ }
323
+ }
324
+
325
+ // pass total views
326
+ $query->total_views = $sum;
327
+ }
328
+
329
+ return $posts;
330
+ }
331
+
332
+ /**
333
+ * Check whether date is valid.
334
+ *
335
+ * @param string $type
336
+ * @param int $year
337
+ * @param int $month
338
+ * @param int $day
339
+ * @param int $week
340
+ * @return bool
341
+ */
342
+ private function is_date_valid( $type, $year = 0, $month = 0, $day = 0, $week = 0 ) {
343
+ switch ( $type ) {
344
+ case 'y':
345
+ $bool = ( $year >= 1 && $year <= 32767 );
346
+ break;
347
+
348
+ case 'yw':
349
+ $bool = ( $year >= 1 && $year <= 32767 && $week >= 0 && $week <= 53 );
350
+ break;
351
+
352
+ case 'ym':
353
+ $bool = ( $year >= 1 && $year <= 32767 && $month >= 1 && $month <= 12 );
354
+ break;
355
+
356
+ case 'ymd':
357
+ $bool = checkdate( $month, $day, $year );
358
+ break;
359
+
360
+ case 'm':
361
+ $bool = ( $month >= 1 && $month <= 12 );
362
+ break;
363
+
364
+ case 'md':
365
+ $bool = ( $month >= 1 && $month <= 12 && $day >= 1 && $day <= 31 );
366
+ break;
367
+
368
+ case 'w':
369
+ $bool = ( $week >= 0 && $week <= 53 );
370
+ break;
371
+
372
+ case 'd':
373
+ $bool = ( $day >= 1 && $day <= 31 );
374
+ break;
375
+ }
376
+
377
+ return $bool;
378
+ }
379
+ }
includes/{settings-api.php → class-settings-api.php} RENAMED
@@ -164,7 +164,7 @@ class Post_Views_Counter_Settings_API {
164
  $first_tab = key( $tabs );
165
 
166
  // get current tab
167
- $tab_key = ! empty( $_GET['tab'] ) && array_key_exists( $_GET['tab'], $tabs ) ? $_GET['tab'] : $first_tab;
168
 
169
  echo '
170
  <h2 class="nav-tab-wrapper">';
@@ -355,6 +355,10 @@ class Post_Views_Counter_Settings_API {
355
  break;
356
 
357
  case 'checkbox':
 
 
 
 
358
  $display_type = ! empty( $args['display_type'] ) && in_array( $args['display_type'], [ 'horizontal', 'vertical' ], true ) ? $args['display_type'] : 'horizontal';
359
 
360
  $html .= '<input type="hidden" name="' . esc_attr( $args['name'] ) . '" value="empty" />';
164
  $first_tab = key( $tabs );
165
 
166
  // get current tab
167
+ $tab_key = ! empty( $_GET['tab'] ) && array_key_exists( sanitize_text_field( $_GET['tab'] ), $tabs ) ? sanitize_text_field( $_GET['tab'] ) : $first_tab;
168
 
169
  echo '
170
  <h2 class="nav-tab-wrapper">';
355
  break;
356
 
357
  case 'checkbox':
358
+ // possible "empty" value
359
+ if ( $args['value'] === 'empty' )
360
+ $args['value'] = [];
361
+
362
  $display_type = ! empty( $args['display_type'] ) && in_array( $args['display_type'], [ 'horizontal', 'vertical' ], true ) ? $args['display_type'] : 'horizontal';
363
 
364
  $html .= '<input type="hidden" name="' . esc_attr( $args['name'] ) . '" value="empty" />';
includes/class-settings.php ADDED
@@ -0,0 +1,901 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Settings class.
8
+ *
9
+ * @class Post_Views_Counter_Settings
10
+ */
11
+ class Post_Views_Counter_Settings {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @return void
17
+ */
18
+ public function __construct() {
19
+ // actions
20
+ add_action( 'admin_init', [ $this, 'update_counter_mode' ] );
21
+
22
+ // filters
23
+ add_filter( 'post_views_counter_settings_data', [ $this, 'settings_data' ] );
24
+ add_filter( 'post_views_counter_settings_pages', [ $this, 'settings_page' ] );
25
+ }
26
+
27
+ /**
28
+ * Update counter mode.
29
+ *
30
+ * @return void
31
+ */
32
+ public function update_counter_mode() {
33
+ // get main instance
34
+ $pvc = Post_Views_Counter();
35
+
36
+ // Fast AJAX as active but not available counter mode?
37
+ if ( $pvc->options['general']['counter_mode'] === 'ajax' && ! in_array( 'ajax', $this->get_counter_modes(), true ) ) {
38
+ // set standard JavaScript AJAX calls
39
+ $pvc->options['general']['counter_mode'] = 'js';
40
+
41
+ // update database options
42
+ update_option( 'post_views_counter_settings_general', $pvc->options['general'] );
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Get available counter modes.
48
+ *
49
+ * @return array
50
+ */
51
+ public function get_counter_modes() {
52
+ // counter modes
53
+ $modes = [
54
+ 'php' => __( 'PHP', 'post-views-counter' ),
55
+ 'js' => __( 'JavaScript', 'post-views-counter' )
56
+ ];
57
+
58
+ // WordPress 4.4+?
59
+ if ( function_exists( 'register_rest_route' ) )
60
+ $modes['rest_api'] = __( 'REST API', 'post-views-counter' );
61
+
62
+ return apply_filters( 'pvc_get_counter_modes', $modes );
63
+ }
64
+
65
+ /**
66
+ * Add settings data.
67
+ *
68
+ * @param array $settings
69
+ * @return array
70
+ */
71
+ public function settings_data( $settings ) {
72
+ // get main instance
73
+ $pvc = Post_Views_Counter();
74
+
75
+ // time types
76
+ $time_types = [
77
+ 'minutes' => __( 'minutes', 'post-views-counter' ),
78
+ 'hours' => __( 'hours', 'post-views-counter' ),
79
+ 'days' => __( 'days', 'post-views-counter' ),
80
+ 'weeks' => __( 'weeks', 'post-views-counter' ),
81
+ 'months' => __( 'months', 'post-views-counter' ),
82
+ 'years' => __( 'years', 'post-views-counter' )
83
+ ];
84
+
85
+ // user groups
86
+ $groups = [
87
+ 'robots' => __( 'robots', 'post-views-counter' ),
88
+ 'users' => __( 'logged in users', 'post-views-counter' ),
89
+ 'guests' => __( 'guests', 'post-views-counter' ),
90
+ 'roles' => __( 'selected user roles', 'post-views-counter' )
91
+ ];
92
+
93
+ // get user roles
94
+ $user_roles = $pvc->functions->get_user_roles();
95
+
96
+ // get post types
97
+ $post_types = $pvc->functions->get_post_types();
98
+
99
+ // add settings
100
+ $settings['post-views-counter'] = [
101
+ 'label' => __( 'Post Views Counter Settings', 'post-views-counter' ),
102
+ 'option_name' => [
103
+ 'general' => 'post_views_counter_settings_general',
104
+ 'display' => 'post_views_counter_settings_display'
105
+ ],
106
+ 'validate' => [ $this, 'validate_settings' ],
107
+ 'sections' => [
108
+ 'post_views_counter_general_settings' => [],
109
+ 'post_views_counter_display_settings' => []
110
+ ],
111
+ 'fields' => [
112
+ 'post_types_count' => [
113
+ 'tab' => 'general',
114
+ 'title' => __( 'Post Types Count', 'post-views-counter' ),
115
+ 'section' => 'post_views_counter_general_settings',
116
+ 'type' => 'checkbox',
117
+ 'display_type' => 'horizontal',
118
+ 'description' => __( 'Select post types for which post views will be counted.', 'post-views-counter' ),
119
+ 'options' => $post_types
120
+ ],
121
+ 'counter_mode' => [
122
+ 'tab' => 'general',
123
+ 'title' => __( 'Counter Mode', 'post-views-counter' ),
124
+ 'section' => 'post_views_counter_general_settings',
125
+ 'type' => 'radio',
126
+ 'description' => __( 'Select the method of collecting post views data. If you are using any of the caching plugins select JavaScript or REST API (if available).', 'post-views-counter' ),
127
+ 'options' => $this->get_counter_modes()
128
+ ],
129
+ 'post_views_column' => [
130
+ 'tab' => 'general',
131
+ 'title' => __( 'Post Views Column', 'post-views-counter' ),
132
+ 'section' => 'post_views_counter_general_settings',
133
+ 'type' => 'boolean',
134
+ 'description' => '',
135
+ 'label' => __( 'Enable to display post views count column for each of the selected post types.', 'post-views-counter' )
136
+ ],
137
+ 'restrict_edit_views' => [
138
+ 'tab' => 'general',
139
+ 'title' => __( 'Restrict Edit', 'post-views-counter' ),
140
+ 'section' => 'post_views_counter_general_settings',
141
+ 'type' => 'boolean',
142
+ 'description' => '',
143
+ 'label' => __( 'Enable to restrict post views editing to admins only.', 'post-views-counter' )
144
+ ],
145
+ 'time_between_counts' => [
146
+ 'tab' => 'general',
147
+ 'title' => __( 'Count Interval', 'post-views-counter' ),
148
+ 'section' => 'post_views_counter_general_settings',
149
+ 'type' => 'custom',
150
+ 'description' => '',
151
+ 'min' => 0,
152
+ 'max' => 999999,
153
+ 'options' => $time_types,
154
+ 'callback' => [ $this, 'setting_time_between_counts' ],
155
+ 'validate' => [ $this, 'validate_time_between_counts' ]
156
+ ],
157
+ 'reset_counts' => [
158
+ 'tab' => 'general',
159
+ 'title' => __( 'Reset Data Interval', 'post-views-counter' ),
160
+ 'section' => 'post_views_counter_general_settings',
161
+ 'type' => 'custom',
162
+ 'description' => '',
163
+ 'min' => 0,
164
+ 'max' => 999999,
165
+ 'options' => $time_types,
166
+ 'callback' => [ $this, 'setting_reset_counts' ],
167
+ 'validate' => [ $this, 'validate_reset_counts' ]
168
+ ],
169
+ 'flush_interval' => [
170
+ 'tab' => 'general',
171
+ 'title' => __( 'Flush Object Cache Interval', 'post-views-counter' ),
172
+ 'section' => 'post_views_counter_general_settings',
173
+ 'type' => 'custom',
174
+ 'description' => '',
175
+ 'min' => 0,
176
+ 'max' => 999999,
177
+ 'options' => $time_types,
178
+ 'callback' => [ $this, 'setting_flush_interval' ],
179
+ 'validate' => [ $this, 'validate_flush_interval' ]
180
+ ],
181
+ 'exclude' => [
182
+ 'tab' => 'general',
183
+ 'title' => __( 'Exclude Visitors', 'post-views-counter' ),
184
+ 'section' => 'post_views_counter_general_settings',
185
+ 'type' => 'custom',
186
+ 'description' => '',
187
+ 'options' => [
188
+ 'groups' => $groups,
189
+ 'roles' => $user_roles
190
+ ],
191
+ 'callback' => [ $this, 'setting_exclude' ],
192
+ 'validate' => [ $this, 'validate_exclude' ]
193
+ ],
194
+ 'exclude_ips' => [
195
+ 'tab' => 'general',
196
+ 'title' => __( 'Exclude IPs', 'post-views-counter' ),
197
+ 'section' => 'post_views_counter_general_settings',
198
+ 'type' => 'custom',
199
+ 'description' => '',
200
+ 'callback' => [ $this, 'setting_exclude_ips' ],
201
+ 'validate' => [ $this, 'validate_exclude_ips' ]
202
+ ],
203
+ 'strict_counts' => [
204
+ 'tab' => 'general',
205
+ 'title' => __( 'Strict counts', 'post-views-counter' ),
206
+ 'section' => 'post_views_counter_general_settings',
207
+ 'type' => 'boolean',
208
+ 'description' => '',
209
+ 'label' => __( 'Enable to prevent bypassing the counts interval (for e.g. using incognito browser window or by clearing cookies).', 'post-views-counter' )
210
+ ],
211
+ 'wp_postviews' => [
212
+ 'tab' => 'general',
213
+ 'title' => __( 'Tools', 'post-views-counter' ),
214
+ 'section' => 'post_views_counter_general_settings',
215
+ 'type' => 'custom',
216
+ 'description' => '',
217
+ 'skip_saving' => true,
218
+ 'callback' => [ $this, 'setting_wp_postviews' ]
219
+ ],
220
+ 'deactivation_delete' => [
221
+ 'tab' => 'general',
222
+ 'title' => __( 'Deactivation', 'post-views-counter' ),
223
+ 'section' => 'post_views_counter_general_settings',
224
+ 'type' => 'boolean',
225
+ 'description' => '',
226
+ 'label' => __( 'Enable to delete all plugin data on deactivation.', 'post-views-counter' )
227
+ ],
228
+ 'label' => [
229
+ 'tab' => 'display',
230
+ 'title' => __( 'Post Views Label', 'post-views-counter' ),
231
+ 'section' => 'post_views_counter_display_settings',
232
+ 'type' => 'input',
233
+ 'description' => __( 'Enter the label for the post views counter field.', 'post-views-counter' ),
234
+ 'subclass' => 'regular-text',
235
+ 'validate' => [ $this, 'validate_label' ],
236
+ 'reset' => [ $this, 'reset_label' ]
237
+ ],
238
+ 'post_types_display' => [
239
+ 'tab' => 'display',
240
+ 'title' => __( 'Post Type', 'post-views-counter' ),
241
+ 'section' => 'post_views_counter_display_settings',
242
+ 'type' => 'checkbox',
243
+ 'display_type' => 'horizontal',
244
+ 'description' => __( 'Select post types for which the views count will be displayed.', 'post-views-counter' ),
245
+ 'options' => $post_types
246
+ ],
247
+ 'page_types_display' => [
248
+ 'tab' => 'display',
249
+ 'title' => __( 'Page Type', 'post-views-counter' ),
250
+ 'section' => 'post_views_counter_display_settings',
251
+ 'type' => 'checkbox',
252
+ 'display_type' => 'horizontal',
253
+ 'description' => __( 'Select page types where the views count will be displayed.', 'post-views-counter' ),
254
+ 'options' => apply_filters(
255
+ 'pvc_page_types_display_options',
256
+ [
257
+ 'home' => __( 'Home', 'post-views-counter' ),
258
+ 'archive' => __( 'Archives', 'post-views-counter' ),
259
+ 'singular' => __( 'Single pages', 'post-views-counter' ),
260
+ 'search' => __( 'Search results', 'post-views-counter' ),
261
+ ]
262
+ )
263
+ ],
264
+ 'restrict_display' => [
265
+ 'tab' => 'display',
266
+ 'title' => __( 'User Type', 'post-views-counter' ),
267
+ 'section' => 'post_views_counter_display_settings',
268
+ 'type' => 'custom',
269
+ 'description' => '',
270
+ 'options' => [
271
+ 'groups' => $groups,
272
+ 'roles' => $user_roles
273
+ ],
274
+ 'callback' => [ $this, 'setting_restrict_display' ],
275
+ 'validate' => [ $this, 'validate_restrict_display' ]
276
+ ],
277
+ 'position' => [
278
+ 'tab' => 'display',
279
+ 'title' => __( 'Position', 'post-views-counter' ),
280
+ 'section' => 'post_views_counter_display_settings',
281
+ 'type' => 'select',
282
+ 'description' => sprintf( __( 'Select where would you like to display the post views counter. Use %s shortcode for manual display.', 'post-views-counter' ), '<code>[post-views]</code>' ),
283
+ 'options' => [
284
+ 'before' => __( 'before the content', 'post-views-counter' ),
285
+ 'after' => __( 'after the content', 'post-views-counter' ),
286
+ 'manual' => __( 'manual', 'post-views-counter' )
287
+ ]
288
+ ],
289
+ 'display_style' => [
290
+ 'tab' => 'display',
291
+ 'title' => __( 'Display Style', 'post-views-counter' ),
292
+ 'section' => 'post_views_counter_display_settings',
293
+ 'type' => 'custom',
294
+ 'description' => __( 'Choose how to display the post views counter.', 'post-views-counter' ),
295
+ 'callback' => [ $this, 'setting_display_style' ],
296
+ 'validate' => [ $this, 'validate_display_style' ],
297
+ 'options' => [
298
+ 'icon' => __( 'icon', 'post-views-counter' ),
299
+ 'text' => __( 'label', 'post-views-counter' )
300
+ ]
301
+ ],
302
+ 'icon_class' => [
303
+ 'tab' => 'display',
304
+ 'title' => __( 'Icon Class', 'post-views-counter' ),
305
+ 'section' => 'post_views_counter_display_settings',
306
+ 'type' => 'input',
307
+ 'description' => sprintf( __( 'Enter the post views icon class. Any of the <a href="%s" target="_blank">Dashicons</a> classes are available.', 'post-views-counter' ), 'https://developer.wordpress.org/resource/dashicons/' ),
308
+ 'subclass' => 'regular-text'
309
+ ],
310
+ 'toolbar_statistics' => [
311
+ 'tab' => 'display',
312
+ 'title' => __( 'Toolbar Chart', 'post-views-counter' ),
313
+ 'section' => 'post_views_counter_display_settings',
314
+ 'type' => 'boolean',
315
+ 'description' => __( 'The post views chart will be displayed for the post types that are being counted.', 'post-views-counter' ),
316
+ 'label' => __( 'Enable to display the post views chart at the toolbar.', 'post-views-counter' )
317
+ ]
318
+ ]
319
+ ];
320
+
321
+ return $settings;
322
+ }
323
+
324
+ /**
325
+ * Add settings page.
326
+ *
327
+ * @param array $pages
328
+ * @return array
329
+ */
330
+ public function settings_page( $pages ) {
331
+ $pages['post-views-counter'] = [
332
+ 'type' => 'settings_page',
333
+ 'menu_slug' => 'post-views-counter',
334
+ 'page_title' => __( 'Post Views Counter Settings', 'post-views-counter' ),
335
+ 'menu_title' => __( 'Post Views Counter', 'post-views-counter' ),
336
+ 'capability' => apply_filters( 'pvc_settings_capability', 'manage_options' ),
337
+ 'callback' => null,
338
+ 'tabs' => [
339
+ 'general' => [
340
+ 'label' => __( 'General', 'post-views-counter' ),
341
+ 'option_name' => 'post_views_counter_settings_general'
342
+ ],
343
+ 'display' => [
344
+ 'label' => __( 'Display', 'post-views-counter' ),
345
+ 'option_name' => 'post_views_counter_settings_display'
346
+ ]
347
+ ]
348
+ ];
349
+
350
+ return $pages;
351
+ }
352
+
353
+ /**
354
+ * Validate options.
355
+ *
356
+ * @param array $input Settings data
357
+ * @return array
358
+ */
359
+ public function validate_settings( $input ) {
360
+ // check capability
361
+ if ( ! current_user_can( 'manage_options' ) )
362
+ return $input;
363
+
364
+ // get main instance
365
+ $pvc = Post_Views_Counter();
366
+
367
+ // use internal settings API to validate settings first
368
+ $input = $pvc->settings_api->validate_settings( $input );
369
+
370
+ // import post views data from another plugin
371
+ if ( isset( $_POST['post_views_counter_import_wp_postviews'] ) ) {
372
+ // make sure we do not change anything in the settings
373
+ $input = $pvc->options['general'];
374
+
375
+ global $wpdb;
376
+
377
+ // get views key
378
+ $meta_key = esc_attr( apply_filters( 'pvc_import_meta_key', 'views' ) );
379
+
380
+ // get views
381
+ $views = $wpdb->get_results( "SELECT post_id, meta_value FROM " . $wpdb->postmeta . " WHERE meta_key = '" . $meta_key . "'", ARRAY_A, 0 );
382
+
383
+ // any views?
384
+ if ( ! empty( $views ) ) {
385
+ $sql = [];
386
+
387
+ foreach ( $views as $view ) {
388
+ $sql[] = "(" . $view['post_id'] . ", 4, 'total', " . ( (int) $view['meta_value'] ) . ")";
389
+ }
390
+
391
+ $wpdb->query( "INSERT INTO " . $wpdb->prefix . "post_views(id, type, period, count) VALUES " . implode( ',', $sql ) . " ON DUPLICATE KEY UPDATE count = " . ( isset( $_POST['post_views_counter_import_wp_postviews_override'] ) ? '' : 'count + ' ) . "VALUES(count)" );
392
+
393
+ add_settings_error( 'wp_postviews_import', 'wp_postviews_import', __( 'Post views data imported succesfully.', 'post-views-counter' ), 'updated' );
394
+ } else
395
+ add_settings_error( 'wp_postviews_import', 'wp_postviews_import', __( 'There was no post views data to import.', 'post-views-counter' ), 'updated' );
396
+ // delete all post views data
397
+ } elseif ( isset( $_POST['post_views_counter_reset_views'] ) ) {
398
+ // make sure we do not change anything in the settings
399
+ $input = $pvc->options['general'];
400
+
401
+ global $wpdb;
402
+
403
+ if ( $wpdb->query( 'TRUNCATE TABLE ' . $wpdb->prefix . 'post_views' ) )
404
+ add_settings_error( 'reset_post_views', 'reset_post_views', __( 'All existing data deleted succesfully.', 'post-views-counter' ), 'updated' );
405
+ else
406
+ add_settings_error( 'reset_post_views', 'reset_post_views', __( 'Error occurred. All existing data were not deleted.', 'post-views-counter' ), 'error' );
407
+ // save general settings
408
+ } elseif ( isset( $_POST['save_post_views_counter_settings_general'] ) ) {
409
+ $input['update_version'] = $pvc->options['general']['update_version'];
410
+ $input['update_notice'] = $pvc->options['general']['update_notice'];
411
+ $input['update_delay_date'] = $pvc->options['general']['update_delay_date'];
412
+ // reset general settings
413
+ } elseif ( isset( $_POST['reset_post_views_counter_settings_general'] ) ) {
414
+ $input['update_version'] = $pvc->options['general']['update_version'];
415
+ $input['update_notice'] = $pvc->options['general']['update_notice'];
416
+ $input['update_delay_date'] = $pvc->options['general']['update_delay_date'];
417
+ }
418
+
419
+ return $input;
420
+ }
421
+
422
+ /**
423
+ * Validate label.
424
+ *
425
+ * @param array $input Input POST data
426
+ * @param array $field Field options
427
+ * @return array
428
+ */
429
+ public function validate_label( $input, $field ) {
430
+ // get main instance
431
+ $pvc = Post_Views_Counter();
432
+
433
+ if ( ! isset( $input ) )
434
+ $input = $pvc->defaults['display']['label'];
435
+
436
+ // use internal settings API to validate settings first
437
+ $input = $pvc->settings_api->validate_field( $input, 'input', $field );
438
+
439
+ if ( function_exists( 'icl_register_string' ) )
440
+ icl_register_string( 'Post Views Counter', 'Post Views Label', $input );
441
+
442
+ return $input;
443
+ }
444
+
445
+ /**
446
+ * Restore post views label to default value.
447
+ *
448
+ * @param array $default Default value
449
+ * @param array $field Field options
450
+ * @return array
451
+ */
452
+ public function reset_label( $default, $field ) {
453
+ if ( function_exists( 'icl_register_string' ) )
454
+ icl_register_string( 'Post Views Counter', 'Post Views Label', $default );
455
+
456
+ return $default;
457
+ }
458
+
459
+ /**
460
+ * Setting: display style.
461
+ *
462
+ * @param array $field Field options
463
+ * @return string
464
+ */
465
+ public function setting_display_style( $field ) {
466
+ // get main instance
467
+ $pvc = Post_Views_Counter();
468
+
469
+ $html = '
470
+ <input type="hidden" name="post_views_counter_settings_display[display_style]" value="empty" />';
471
+
472
+ foreach ( $field['options'] as $key => $label ) {
473
+ $html .= '
474
+ <label><input id="post_views_counter_display_display_style_' . esc_attr( $key ) . '" type="checkbox" name="post_views_counter_settings_display[display_style][]" value="' . esc_attr( $key ) . '" ' . checked( ! empty( $pvc->options['display']['display_style'][$key] ), true, false ) . ' />' . esc_html( $label ) . '</label> ';
475
+ }
476
+
477
+ return $html;
478
+ }
479
+
480
+ /**
481
+ * Validate display style.
482
+ *
483
+ * @param array $input Input POST data
484
+ * @param array $field Field options
485
+ * @return array
486
+ */
487
+ public function validate_display_style( $input, $field ) {
488
+ // get main instance
489
+ $pvc = Post_Views_Counter();
490
+
491
+ $data = [];
492
+
493
+ foreach ( $field['options'] as $value => $label ) {
494
+ $data[$value] = false;
495
+ }
496
+
497
+ // any data?
498
+ if ( ! empty( $input['display_style'] && $input['display_style'] !== 'empty' && is_array( $input['display_style'] ) ) ) {
499
+ foreach ( $input['display_style'] as $value ) {
500
+ if ( array_key_exists( $value, $field['options'] ) )
501
+ $data[$value] = true;
502
+ }
503
+ }
504
+
505
+ $input['display_style'] = $data;
506
+
507
+ return $input;
508
+ }
509
+
510
+ /**
511
+ * Setting: count interval.
512
+ *
513
+ * @param array $field Field options
514
+ * @return string
515
+ */
516
+ public function setting_time_between_counts( $field ) {
517
+ // get main instance
518
+ $pvc = Post_Views_Counter();
519
+
520
+ $html = '
521
+ <input size="6" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="post_views_counter_settings_general[time_between_counts][number]" value="' . esc_attr( $pvc->options['general']['time_between_counts']['number'] ) . '" />
522
+ <select class="pvc-chosen-short" name="post_views_counter_settings_general[time_between_counts][type]">';
523
+
524
+ foreach ( $field['options'] as $type => $type_name ) {
525
+ $html .= '
526
+ <option value="' . esc_attr( $type ) . '" ' . selected( $type, $pvc->options['general']['time_between_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
527
+ }
528
+
529
+ $html .= '
530
+ </select>
531
+ <p class="description">' . __( 'Enter the time between single user visit count.', 'post-views-counter' ) . '</p>';
532
+
533
+ return $html;
534
+ }
535
+
536
+ /**
537
+ * Validate count interval.
538
+ *
539
+ * @param array $input Input POST data
540
+ * @param array $field Field options
541
+ * @return array
542
+ */
543
+ public function validate_time_between_counts( $input, $field ) {
544
+ // get main instance
545
+ $pvc = Post_Views_Counter();
546
+
547
+ // number
548
+ $input['time_between_counts']['number'] = isset( $input['time_between_counts']['number'] ) ? (int) $input['time_between_counts']['number'] : $pvc->defaults['general']['time_between_counts']['number'];
549
+
550
+ if ( $input['time_between_counts']['number'] < $field['min'] || $input['time_between_counts']['number'] > $field['max'] )
551
+ $input['time_between_counts']['number'] = $pvc->defaults['general']['time_between_counts']['number'];
552
+
553
+ // type
554
+ $input['time_between_counts']['type'] = isset( $input['time_between_counts']['type'], $field['options'][$input['time_between_counts']['type']] ) ? $input['time_between_counts']['type'] : $pvc->defaults['general']['time_between_counts']['type'];
555
+
556
+ return $input;
557
+ }
558
+
559
+ /**
560
+ * Setting: reset data interval.
561
+ *
562
+ * @param array $field Field options
563
+ * @return string
564
+ */
565
+ public function setting_reset_counts( $field ) {
566
+ // get main instance
567
+ $pvc = Post_Views_Counter();
568
+
569
+ $html = '
570
+ <input size="6" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="post_views_counter_settings_general[reset_counts][number]" value="' . esc_attr( $pvc->options['general']['reset_counts']['number'] ) . '" />
571
+ <select class="pvc-chosen-short" name="post_views_counter_settings_general[reset_counts][type]">';
572
+
573
+ foreach ( array_slice( $field['options'], 2, null, true ) as $type => $type_name ) {
574
+ $html .= '
575
+ <option value="' . esc_attr( $type ) . '" ' . selected( $type, $pvc->options['general']['reset_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
576
+ }
577
+
578
+ $html .= '
579
+ </select>
580
+ <p class="description">' . sprintf( __( 'Delete single day post views data older than specified above. Enter %s if you want to preserve your data regardless of its age.', 'post-views-counter' ), '<code>0</code>' ) . '</p>';
581
+
582
+ return $html;
583
+ }
584
+
585
+ /**
586
+ * Validate reset data interval.
587
+ *
588
+ * @param array $input Input POST data
589
+ * @param array $field Field options
590
+ * @return array
591
+ */
592
+ public function validate_reset_counts( $input, $field ) {
593
+ // get main instance
594
+ $pvc = Post_Views_Counter();
595
+
596
+ // number
597
+ $input['reset_counts']['number'] = isset( $input['reset_counts']['number'] ) ? (int) $input['reset_counts']['number'] : $pvc->defaults['general']['reset_counts']['number'];
598
+
599
+ if ( $input['reset_counts']['number'] < $field['min'] || $input['reset_counts']['number'] > $field['max'] )
600
+ $input['reset_counts']['number'] = $pvc->defaults['general']['reset_counts']['number'];
601
+
602
+ // type
603
+ $input['reset_counts']['type'] = isset( $input['reset_counts']['type'], $field['options'][$input['reset_counts']['type']] ) ? $input['reset_counts']['type'] : $pvc->defaults['general']['reset_counts']['type'];
604
+
605
+ // run cron on next visit?
606
+ $input['cron_run'] = ( $input['reset_counts']['number'] > 0 );
607
+
608
+ // cron update?
609
+ $input['cron_update'] = ( $input['cron_run'] && ( $pvc->options['general']['reset_counts']['number'] !== $input['reset_counts']['number'] || $pvc->options['general']['reset_counts']['type'] !== $input['reset_counts']['type'] ) );
610
+
611
+ return $input;
612
+ }
613
+
614
+ /**
615
+ * Setting: flush object cache interval.
616
+ *
617
+ * @param array $field Field options
618
+ * @return string
619
+ */
620
+ public function setting_flush_interval( $field ) {
621
+ // get main instance
622
+ $pvc = Post_Views_Counter();
623
+
624
+ $html = '
625
+ <input size="6" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="post_views_counter_settings_general[flush_interval][number]" value="' . esc_attr( $pvc->options['general']['flush_interval']['number'] ) . '" />
626
+ <select class="pvc-chosen-short" name="post_views_counter_settings_general[flush_interval][type]">';
627
+
628
+ foreach ( $field['options'] as $type => $type_name ) {
629
+ $html .= '
630
+ <option value="' . esc_attr( $type ) . '" ' . selected( $type, $pvc->options['general']['flush_interval']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
631
+ }
632
+
633
+ $html .= '
634
+ </select>
635
+ <p class="description">' . sprintf( __( 'How often to flush cached view counts from the object cache into the database. This feature is used only if a persistent object cache 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.<br /><strong>Notice:</strong> Potential data loss may occur if the object cache is cleared/unavailable for the duration of the interval.', 'post-views-counter' ), '<code>0</code>' ) . '</p>';
636
+
637
+ return $html;
638
+ }
639
+
640
+ /**
641
+ * Validate flush object cache interval.
642
+ *
643
+ * @param array $input Input POST data
644
+ * @param array $field Field options
645
+ * @return array
646
+ */
647
+ public function validate_flush_interval( $input, $field ) {
648
+ // get main instance
649
+ $pvc = Post_Views_Counter();
650
+
651
+ // number
652
+ $input['flush_interval']['number'] = isset( $input['flush_interval']['number'] ) ? (int) $input['flush_interval']['number'] : $pvc->defaults['general']['flush_interval']['number'];
653
+
654
+ if ( $input['flush_interval']['number'] < $field['min'] || $input['flush_interval']['number'] > $field['max'] )
655
+ $input['flush_interval']['number'] = $pvc->defaults['general']['flush_interval']['number'];
656
+
657
+ // type
658
+ $input['flush_interval']['type'] = isset( $input['flush_interval']['type'], $field['options'][$input['flush_interval']['type']] ) ? $input['flush_interval']['type'] : $pvc->defaults['general']['flush_interval']['type'];
659
+
660
+ // Since the settings are about to be saved and cache flush interval could've changed,
661
+ // we want to make sure that any changes done on the settings page are in effect immediately
662
+ // (instead of having to wait for the previous schedule to occur).
663
+ // We achieve that by making sure to clear any previous cache flush schedules and
664
+ // schedule the new one if the specified interval is > 0
665
+ $pvc->remove_cache_flush();
666
+
667
+ if ( $input['flush_interval']['number'] > 0 )
668
+ $pvc->schedule_cache_flush();
669
+
670
+ return $input;
671
+ }
672
+
673
+ /**
674
+ * Setting: exclude visitors.
675
+ *
676
+ * @param array $field Field options
677
+ * @return string
678
+ */
679
+ public function setting_exclude( $field ) {
680
+ // get main instance
681
+ $pvc = Post_Views_Counter();
682
+
683
+ $html = '';
684
+
685
+ foreach ( $field['options']['groups'] as $type => $type_name ) {
686
+ $html .= '
687
+ <label><input id="' . esc_attr( 'pvc_exclude-' . $type ) . '" type="checkbox" name="post_views_counter_settings_general[exclude][groups][' . esc_attr( $type ) . ']" value="1" ' . checked( in_array( $type, $pvc->options['general']['exclude']['groups'], true ), true, false ) . ' />' . esc_html( $type_name ) . '</label>';
688
+ }
689
+
690
+ $html .= '
691
+ <p class="description">' . __( 'Use it exclude specific user groups from post views count.', 'post-views-counter' ) . '</p>
692
+ <div class="pvc_user_roles"' . ( in_array( 'roles', $pvc->options['general']['exclude']['groups'], true ) ? '' : ' style="display: none;"' ) . '>';
693
+
694
+ foreach ( $field['options']['roles'] as $role => $role_name ) {
695
+ $html .= '
696
+ <label><input type="checkbox" name="post_views_counter_settings_general[exclude][roles][' . $role . ']" value="1" ' . checked( in_array( $role, $pvc->options['general']['exclude']['roles'], true ), true, false ) . '>' . esc_html( $role_name ) . '</label>';
697
+ }
698
+
699
+ $html .= '
700
+ <p class="description">' . __( 'Use it exclude specific user roles from post views count.', 'post-views-counter' ) . '</p>
701
+ </div>';
702
+
703
+ return $html;
704
+ }
705
+
706
+ /**
707
+ * Validate exclude visitors.
708
+ *
709
+ * @param array $input Input POST data
710
+ * @param array $field Field options
711
+ * @return array
712
+ */
713
+ public function validate_exclude( $input, $field ) {
714
+ // get main instance
715
+ $pvc = Post_Views_Counter();
716
+
717
+ // any groups?
718
+ if ( isset( $input['exclude']['groups'] ) ) {
719
+ $groups = [];
720
+
721
+ foreach ( $input['exclude']['groups'] as $group => $set ) {
722
+ if ( isset( $field['options']['groups'][$group] ) )
723
+ $groups[] = $group;
724
+ }
725
+
726
+ $input['exclude']['groups'] = array_unique( $groups );
727
+ } else
728
+ $input['exclude']['groups'] = [];
729
+
730
+ // any roles?
731
+ if ( in_array( 'roles', $input['exclude']['groups'], true ) && isset( $input['exclude']['roles'] ) ) {
732
+ $roles = [];
733
+
734
+ foreach ( $input['exclude']['roles'] as $role => $set ) {
735
+ if ( isset( $field['options']['roles'][$role] ) )
736
+ $roles[] = $role;
737
+ }
738
+
739
+ $input['exclude']['roles'] = array_unique( $roles );
740
+ } else
741
+ $input['exclude']['roles'] = [];
742
+
743
+ return $input;
744
+ }
745
+
746
+ /**
747
+ * Setting: exclude IP addresses.
748
+ *
749
+ * @return string
750
+ */
751
+ public function setting_exclude_ips() {
752
+ $ips = Post_Views_Counter()->options['general']['exclude_ips'];
753
+
754
+ $html = '';
755
+
756
+ if ( ! empty( $ips ) ) {
757
+ foreach ( $ips as $key => $ip ) {
758
+ $html .= '
759
+ <div class="ip-box">
760
+ <input type="text" name="post_views_counter_settings_general[exclude_ips][]" value="' . esc_attr( $ip ) . '" /> <a href="#" class="remove-exclude-ip" title="' . esc_attr__( 'Remove', 'post-views-counter' ) . '">' . esc_html__( 'Remove', 'post-views-counter' ) . '</a>
761
+ </div>';
762
+ }
763
+ } else {
764
+ $html .= '
765
+ <div class="ip-box">
766
+ <input type="text" name="post_views_counter_settings_general[exclude_ips][]" value="" /> <a href="#" class="remove-exclude-ip" title="' . esc_attr__( 'Remove', 'post-views-counter' ) . '">' . esc_html__( 'Remove', 'post-views-counter' ) . '</a>
767
+ </div>';
768
+ }
769
+
770
+ $html .= '
771
+ <p><input type="button" class="button button-secondary add-exclude-ip" value="' . esc_attr__( 'Add new', 'post-views-counter' ) . '" /> <input type="button" class="button button-secondary add-current-ip" value="' . esc_attr__( 'Add my current IP', 'post-views-counter' ) . '" data-rel="' . esc_attr( $_SERVER['REMOTE_ADDR'] ) . '" /></p>
772
+ <p class="description">' . esc_html__( 'Enter the IP addresses to be excluded from post views count.', 'post-views-counter' ) . '</p>';
773
+
774
+ return $html;
775
+ }
776
+
777
+ /**
778
+ * Validate exclude IP addresses.
779
+ *
780
+ * @param array $input Input POST data
781
+ * @param array $field Field options
782
+ * @return array
783
+ */
784
+ public function validate_exclude_ips( $input, $field ) {
785
+ // get main instance
786
+ $pvc = Post_Views_Counter();
787
+
788
+ // any ip addresses?
789
+ if ( isset( $input['exclude_ips'] ) ) {
790
+ $ips = [];
791
+
792
+ foreach ( $input['exclude_ips'] as $ip ) {
793
+ if ( strpos( $ip, '*' ) !== false ) {
794
+ $new_ip = str_replace( '*', '0', $ip );
795
+
796
+ if ( filter_var( $new_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
797
+ $ips[] = $ip;
798
+ } elseif ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
799
+ $ips[] = $ip;
800
+ }
801
+
802
+ $input['exclude_ips'] = array_unique( $ips );
803
+ }
804
+
805
+ return $input;
806
+ }
807
+
808
+ /**
809
+ * Setting: tools.
810
+ *
811
+ * @return string
812
+ */
813
+ public function setting_wp_postviews() {
814
+ $html = '
815
+ <input type="submit" class="button button-secondary" name="post_views_counter_import_wp_postviews" value="' . __( 'Import views', 'post-views-counter' ) . '"/> <label><input id="pvc-wp-postviews" type="checkbox" name="post_views_counter_import_wp_postviews_override" value="1" />' . __( 'Override existing views data.', 'post-views-counter' ) . '</label>
816
+ <p class="description">' . __( 'Import post views data from WP-PostViews plugin.', 'post-views-counter' ) . '</p>
817
+ <br /><input type="submit" class="button button-secondary" name="post_views_counter_reset_views" value="' . __( 'Delete views', 'post-views-counter' ) . '"/>
818
+ <p class="description">' . __( 'Delete all the existing post views data.', 'post-views-counter' ) . '</p>';
819
+
820
+ return $html;
821
+ }
822
+
823
+ /**
824
+ * Setting: user type.
825
+ *
826
+ * @param array $field Field options
827
+ * @return string
828
+ */
829
+ public function setting_restrict_display( $field ) {
830
+ // get main instance
831
+ $pvc = Post_Views_Counter();
832
+
833
+ $html = '';
834
+
835
+ foreach ( $field['options']['groups'] as $type => $type_name ) {
836
+ if ( $type === 'robots' )
837
+ continue;
838
+
839
+ $html .= '
840
+ <label><input id="pvc_restrict_display-' . esc_attr( $type ) . '" type="checkbox" name="post_views_counter_settings_display[restrict_display][groups][' . esc_html( $type ) . ']" value="1" ' . checked( in_array( $type, $pvc->options['display']['restrict_display']['groups'], true ), true, false ) . ' />' . esc_html( $type_name ) . '</label>';
841
+ }
842
+
843
+ $html .= '
844
+ <p class="description">' . __( 'Use it to hide the post views counter from selected type of visitors.', 'post-views-counter' ) . '</p>
845
+ <div class="pvc_user_roles"' . ( in_array( 'roles', $pvc->options['display']['restrict_display']['groups'], true ) ? '' : ' style="display: none;"' ) . '>';
846
+
847
+ foreach ( $field['options']['roles'] as $role => $role_name ) {
848
+ $html .= '
849
+ <label><input type="checkbox" name="post_views_counter_settings_display[restrict_display][roles][' . esc_attr( $role ) . ']" value="1" ' . checked( in_array( $role, $pvc->options['display']['restrict_display']['roles'], true ), true, false ) . ' />' . esc_html( $role_name ) . '</label>';
850
+ }
851
+
852
+ $html .= '
853
+ <p class="description">' . __( 'Use it to hide the post views counter from selected user roles.', 'post-views-counter' ) . '</p>
854
+ </div>';
855
+
856
+ return $html;
857
+ }
858
+
859
+ /**
860
+ * Validate user type.
861
+ *
862
+ * @param array $input Input POST data
863
+ * @param array $field Field options
864
+ * @return array
865
+ */
866
+ public function validate_restrict_display( $input, $field ) {
867
+ // get main instance
868
+ $pvc = Post_Views_Counter();
869
+
870
+ // any groups?
871
+ if ( isset( $input['restrict_display']['groups'] ) ) {
872
+ $groups = [];
873
+
874
+ foreach ( $input['restrict_display']['groups'] as $group => $set ) {
875
+ if ( $group === 'robots' )
876
+ continue;
877
+
878
+ if ( isset( $field['options']['groups'][$group] ) )
879
+ $groups[] = $group;
880
+ }
881
+
882
+ $input['restrict_display']['groups'] = array_unique( $groups );
883
+ } else
884
+ $input['restrict_display']['groups'] = [];
885
+
886
+ // any roles?
887
+ if ( in_array( 'roles', $input['restrict_display']['groups'], true ) && isset( $input['restrict_display']['roles'] ) ) {
888
+ $roles = [];
889
+
890
+ foreach ( $input['restrict_display']['roles'] as $role => $set ) {
891
+ if ( isset( $field['options']['roles'][$role] ) )
892
+ $roles[] = $role;
893
+ }
894
+
895
+ $input['restrict_display']['roles'] = array_unique( $roles );
896
+ } else
897
+ $input['restrict_display']['roles'] = [];
898
+
899
+ return $input;
900
+ }
901
+ }
includes/class-update.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Update class.
8
+ *
9
+ * @class Post_Views_Counter_Update
10
+ */
11
+ class Post_Views_Counter_Update {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @return void
17
+ */
18
+ public function __construct() {
19
+ // actions
20
+ add_action( 'init', array( $this, 'check_update' ) );
21
+ }
22
+
23
+ /**
24
+ * Check if there's a database update required.
25
+ *
26
+ * @return void
27
+ */
28
+ public function check_update() {
29
+ if ( ! current_user_can( 'manage_options' ) )
30
+ return;
31
+
32
+ // get current database version
33
+ $current_db_version = get_option( 'post_views_counter_version', '1.0.0' );
34
+
35
+ // update 1.2.4+
36
+ if ( version_compare( $current_db_version, '1.2.4', '<=' ) ) {
37
+ $general = Post_Views_Counter()->options['general'];
38
+
39
+ if ( $general['reset_counts']['number'] > 0 ) {
40
+ // unsupported data reset in minutes/hours
41
+ if ( in_array( $general['reset_counts']['type'], array( 'minutes', 'hours' ), true ) ) {
42
+ // set type to date
43
+ $general['reset_counts']['type'] = 'days';
44
+
45
+ // new number of days
46
+ if ( $general['reset_counts']['type'] === 'minutes' )
47
+ $general['reset_counts']['number'] = $general['reset_counts']['number'] * 60;
48
+ else
49
+ $general['reset_counts']['number'] = $general['reset_counts']['number'] * 3600;
50
+
51
+ // how many days?
52
+ $general['reset_counts']['number'] = (int) round( ceil( $general['reset_counts']['number'] / 86400 ) );
53
+
54
+ // force cron to update
55
+ $general['cron_run'] = true;
56
+ $general['cron_update'] = true;
57
+
58
+ // update settings
59
+ update_option( 'post_views_counter_settings_general', $general );
60
+
61
+ // update general options
62
+ Post_Views_Counter()->options['general'] = $general;
63
+ }
64
+
65
+ // update cron job for all users
66
+ Post_Views_Counter()->cron->check_cron();
67
+ }
68
+ }
69
+
70
+ if ( isset( $_POST['post_view_counter_update'], $_POST['post_view_counter_number'] ) ) {
71
+ if ( $_POST['post_view_counter_number'] === 'update_1' ) {
72
+ $this->update_1();
73
+
74
+ // update plugin version
75
+ update_option( 'post_views_counter_version', Post_Views_Counter()->defaults['version'], false );
76
+ }
77
+ }
78
+
79
+ $update_1_html = '
80
+ <form action="" method="post">
81
+ <input type="hidden" name="post_view_counter_number" value="update_1"/>
82
+ <p>' . __( '<strong>Post Views Counter</strong> - this version requires a database update. Make sure to back up your database first.', 'post-views-counter' ) . '</p>
83
+ <p><input type="submit" class="button button-primary" name="post_view_counter_update" value="' . __( 'Run the Update', 'post-views-counter' ) . '"/></p>
84
+ </form>';
85
+
86
+ // get current database version
87
+ $current_db_version = get_option( 'post_views_counter_version', '1.0.0' );
88
+
89
+ // new version?
90
+ if ( version_compare( $current_db_version, Post_Views_Counter()->defaults['version'], '<' ) ) {
91
+ // is update 1 required?
92
+ if ( version_compare( $current_db_version, '1.2.4', '<=' ) )
93
+ Post_Views_Counter()->add_notice( $update_1_html, 'notice notice-info' );
94
+ else
95
+ // update plugin version
96
+ update_option( 'post_views_counter_version', Post_Views_Counter()->defaults['version'], false );
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Database update for 1.2.4 and below.
102
+ *
103
+ * @return void
104
+ */
105
+ public function update_1() {
106
+ global $wpdb;
107
+
108
+ // get index
109
+ $old_index = $wpdb->query( "SHOW INDEX FROM `" . $wpdb->prefix . "post_views` WHERE Key_name = 'id_period'" );
110
+
111
+ // check whether index already exists
112
+ if ( $old_index > 0 ) {
113
+ // drop unwanted index which prevented saving views with indentical weeks and months
114
+ $wpdb->query( "ALTER TABLE `" . $wpdb->prefix . "post_views` DROP INDEX id_period" );
115
+ }
116
+
117
+ // get index
118
+ $new_index = $wpdb->query( "SHOW INDEX FROM `" . $wpdb->prefix . "post_views` WHERE Key_name = 'id_type_period_count'" );
119
+
120
+ // check whether index already exists
121
+ if ( $new_index === 0 ) {
122
+ // create new index for better performance of SQL queries
123
+ $wpdb->query( 'ALTER TABLE `' . $wpdb->prefix . 'post_views` ADD UNIQUE INDEX `id_type_period_count` (`id`, `type`, `period`, `count`) USING BTREE' );
124
+ }
125
+
126
+ Post_Views_Counter()->add_notice( __( 'Thank you! Datebase was succesfully updated.', 'post-views-counter' ), 'updated', true );
127
+ }
128
+ }
includes/class-widgets.php ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ // exit if accessed directly
3
+ if ( ! defined( 'ABSPATH' ) )
4
+ exit;
5
+
6
+ /**
7
+ * Post_Views_Counter_Widgets class.
8
+ *
9
+ * @class Post_Views_Counter_Widgets
10
+ */
11
+ class Post_Views_Counter_Widgets {
12
+
13
+ /**
14
+ * Class constructor.
15
+ *
16
+ * @return void
17
+ */
18
+ public function __construct() {
19
+ // actions
20
+ add_action( 'widgets_init', [ $this, 'register_widgets' ] );
21
+ }
22
+
23
+ /**
24
+ * Register widgets.
25
+ *
26
+ * @return void
27
+ */
28
+ public function register_widgets() {
29
+ register_widget( 'Post_Views_Counter_List_Widget' );
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Post_Views_Counter_List_Widget class.
35
+ *
36
+ * @class Post_Views_Counter_List_Widget
37
+ */
38
+ class Post_Views_Counter_List_Widget extends WP_Widget {
39
+
40
+ private $pvc_defaults;
41
+ private $pvc_order_types;
42
+ private $pvc_list_types;
43
+ private $pvc_image_sizes;
44
+
45
+ /**
46
+ * Class constructor.
47
+ *
48
+ * @return void
49
+ */
50
+ public function __construct() {
51
+ // call parent
52
+ parent::__construct(
53
+ 'Post_Views_Counter_List_Widget',
54
+ __( 'Most Viewed Posts', 'post-views-counter' ),
55
+ [
56
+ 'description' => __( 'Displays a list of the most viewed posts', 'post-views-counter' )
57
+ ]
58
+ );
59
+
60
+ // default settings
61
+ $this->pvc_defaults = [
62
+ 'title' => __( 'Most Viewed Posts', 'post-views-counter' ),
63
+ 'number_of_posts' => 5,
64
+ 'thumbnail_size' => 'thumbnail',
65
+ 'post_type' => [],
66
+ 'order' => 'desc',
67
+ 'list_type' => 'unordered',
68
+ 'show_post_views' => true,
69
+ 'show_post_thumbnail' => false,
70
+ 'show_post_excerpt' => false,
71
+ 'show_post_author' => false,
72
+ 'no_posts_message' => __( 'No most viewed posts found', 'post-views-counter' )
73
+ ];
74
+
75
+ // order types
76
+ $this->pvc_order_types = [
77
+ 'asc' => __( 'Ascending', 'post-views-counter' ),
78
+ 'desc' => __( 'Descending', 'post-views-counter' )
79
+ ];
80
+
81
+ // list types
82
+ $this->pvc_list_types = [
83
+ 'unordered' => __( 'Unordered list', 'post-views-counter' ),
84
+ 'ordered' => __( 'Ordered list', 'post-views-counter' )
85
+ ];
86
+
87
+ // image sizes
88
+ $this->pvc_image_sizes = array_merge( [ 'full' ], get_intermediate_image_sizes() );
89
+
90
+ // sort image sizes by name, ascending
91
+ sort( $this->pvc_image_sizes, SORT_STRING );
92
+ }
93
+
94
+ /**
95
+ * Display widget.
96
+ *
97
+ * @param array $args
98
+ * @param array $instance
99
+ * @return void
100
+ */
101
+ public function widget( $args, $instance ) {
102
+ // empty title?
103
+ if ( empty( $instance['title'] ) )
104
+ $instance['title'] = $this->pvc_defaults['title'];
105
+
106
+ // filter title
107
+ $instance['title'] = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
108
+
109
+ $html = $args['before_widget'] . ( ! empty( $instance['title'] ) ? $args['before_title'] . esc_html( $instance['title'] ) . $args['after_title'] : '' );
110
+ $html .= pvc_most_viewed_posts( $instance, false );
111
+ $html .= $args['after_widget'];
112
+
113
+ echo $html;
114
+ }
115
+
116
+ /** Render widget form.
117
+ *
118
+ * @param array $instance
119
+ * @return void
120
+ */
121
+ public function form( $instance ) {
122
+ $html = '
123
+ <p>
124
+ <label for="' . $this->get_field_id( 'title' ) . '">' . __( 'Title', 'post-views-counter' ) . ':</label>
125
+ <input id="' . $this->get_field_id( 'title' ) . '" class="widefat" name="' . $this->get_field_name( 'title' ) . '" type="text" value="' . esc_attr( isset( $instance['title'] ) ? $instance['title'] : $this->pvc_defaults['title'] ) . '" />
126
+ </p>
127
+ <p>
128
+ <label>' . __( 'Post Types', 'post-views-counter' ) . ':</label><br />';
129
+
130
+ // post types
131
+ foreach ( Post_Views_Counter()->functions->get_post_types() as $post_type => $post_type_name ) {
132
+ $html .= '
133
+ <input id="' . $this->get_field_id( 'post_type' ) . '-' . $post_type . '" type="checkbox" name="' . $this->get_field_name( 'post_type' ) . '[]" value="' . $post_type . '" ' . checked( ( ! isset( $instance['post_type'] ) ? true : in_array( $post_type, $instance['post_type'], true ) ), true, false ) . '><label for="' . $this->get_field_id( 'post_type' ) . '-' . $post_type . '">' . esc_html( $post_type_name ) . '</label>';
134
+ }
135
+
136
+ $show_post_thumbnail = isset( $instance['show_post_thumbnail'] ) ? $instance['show_post_thumbnail'] : $this->pvc_defaults['show_post_thumbnail'];
137
+
138
+ $html .= '
139
+ </p>
140
+ <p>
141
+ <label for="' . $this->get_field_id( 'number_of_posts' ) . '">' . __( 'Number of posts to show', 'post-views-counter' ) . ':</label>
142
+ <input id="' . $this->get_field_id( 'number_of_posts' ) . '" name="' . $this->get_field_name( 'number_of_posts' ) . '" type="number" size="3" min="0" max="100" value="' . esc_attr( isset( $instance['number_of_posts'] ) ? $instance['number_of_posts'] : $this->pvc_defaults['number_of_posts'] ) . '" />
143
+ </p>
144
+ <p>
145
+ <label for="' . $this->get_field_id( 'no_posts_message' ) . '">' . __( 'No posts message', 'post-views-counter' ) . ':</label>
146
+ <input id="' . $this->get_field_id( 'no_posts_message' ) . '" class="widefat" type="text" name="' . $this->get_field_name( 'no_posts_message' ) . '" value="' . esc_attr( isset( $instance['no_posts_message'] ) ? $instance['no_posts_message'] : $this->pvc_defaults['no_posts_message'] ) . '" />
147
+ </p>
148
+ <p>
149
+ <label for="' . $this->get_field_id( 'order' ) . '">' . __( 'Order', 'post-views-counter' ) . ':</label>
150
+ <select id="' . $this->get_field_id( 'order' ) . '" name="' . $this->get_field_name( 'order' ) . '">';
151
+
152
+ // order types
153
+ foreach ( $this->pvc_order_types as $id => $order ) {
154
+ $html .= '
155
+ <option value="' . esc_attr( $id ) . '" ' . selected( $id, ( isset( $instance['order'] ) ? $instance['order'] : $this->pvc_defaults['order'] ), false ) . '>' . esc_html( $order ) . '</option>';
156
+ }
157
+
158
+ $html .= '
159
+ </select>
160
+ </p>
161
+ <p>
162
+ <label for="' . $this->get_field_id( 'list_type' ) . '">' . __( 'Display Style', 'post-views-counter' ) . ':</label>
163
+ <select id="' . $this->get_field_id( 'list_type' ) . '" name="' . $this->get_field_name( 'list_type' ) . '">';
164
+
165
+ // list types
166
+ foreach ( $this->pvc_list_types as $id => $list_type ) {
167
+ $html .= '
168
+ <option value="' . esc_attr( $id ) . '" ' . selected( $id, ( isset( $instance['list_type'] ) ? $instance['list_type'] : $this->pvc_defaults['list_type'] ), false ) . '>' . esc_html( $list_type ) . '</option>';
169
+ }
170
+
171
+ $html .= '
172
+ </select>
173
+ </p>
174
+ <p>
175
+ <input id="' . $this->get_field_id( 'show_post_views' ) . '" type="checkbox" name="' . $this->get_field_name( 'show_post_views' ) . '" value="show_post_views" ' . checked( true, ( isset( $instance['show_post_views'] ) ? $instance['show_post_views'] : $this->pvc_defaults['show_post_views'] ), false ) . ' /> <label for="' . $this->get_field_id( 'show_post_views' ) . '">' . __( 'Display post views?', 'post-views-counter' ) . '</label>
176
+ <br />
177
+ <input id="' . $this->get_field_id( 'show_post_excerpt' ) . '" type="checkbox" name="' . $this->get_field_name( 'show_post_excerpt' ) . '" value="show_post_excerpt" ' . checked( true, ( isset( $instance['show_post_excerpt'] ) ? $instance['show_post_excerpt'] : $this->pvc_defaults['show_post_excerpt'] ), false ) . ' /> <label for="' . $this->get_field_id( 'show_post_excerpt' ) . '">' . __( 'Display post excerpt?', 'post-views-counter' ) . '</label>
178
+ <br />
179
+ <input id="' . $this->get_field_id( 'show_post_author' ) . '" type="checkbox" name="' . $this->get_field_name( 'show_post_author' ) . '" value="show_post_author" ' . checked( true, ( isset( $instance['show_post_author'] ) ? $instance['show_post_author'] : $this->pvc_defaults['show_post_author'] ), false ) . ' /> <label for="' . $this->get_field_id( 'show_post_author' ) . '">' . __( 'Display post author?', 'post-views-counter' ) . '</label>
180
+ <br />
181
+ <input id="' . $this->get_field_id( 'show_post_thumbnail' ) . '" class="pvc-show-post-thumbnail" type="checkbox" name="' . $this->get_field_name( 'show_post_thumbnail' ) . '" value="show_post_thumbnail" ' . checked( true, $show_post_thumbnail, false ) . ' /> <label for="' . $this->get_field_id( 'show_post_thumbnail' ) . '">' . __( 'Display post thumbnail?', 'post-views-counter' ) . '</label>
182
+ </p>
183
+ <p class="pvc-post-thumbnail-size"' . ( $show_post_thumbnail ? '' : ' style="display: none;"' ) . '>
184
+ <label for="' . $this->get_field_id( 'thumbnail_size' ) . '">' . __( 'Thumbnail size', 'post-views-counter' ) . ':</label>
185
+ <select id="' . $this->get_field_id( 'thumbnail_size' ) . '" name="' . $this->get_field_name( 'thumbnail_size' ) . '">';
186
+
187
+ $size_type = isset( $instance['thumbnail_size'] ) ? $instance['thumbnail_size'] : $this->pvc_defaults['thumbnail_size'];
188
+
189
+ // image sizes
190
+ foreach ( $this->pvc_image_sizes as $size ) {
191
+ $html .= '
192
+ <option value="' . esc_attr( $size ) . '" ' . selected( $size, $size_type, false ) . '>' . esc_html( $size ) . '</option>';
193
+ }
194
+
195
+ $html .= '
196
+ </select>
197
+ </p>';
198
+
199
+ echo $html;
200
+ }
201
+
202
+ /**
203
+ * Save widget form.
204
+ *
205
+ * @param array $new_instance
206
+ * @param array $old_instance
207
+ * @return array
208
+ */
209
+ public function update( $new_instance, $old_instance ) {
210
+ // number of posts
211
+ $old_instance['number_of_posts'] = (int) (isset( $new_instance['number_of_posts'] ) ? $new_instance['number_of_posts'] : $this->pvc_defaults['number_of_posts']);
212
+
213
+ // order
214
+ $old_instance['order'] = isset( $new_instance['order'] ) && in_array( $new_instance['order'], array_keys( $this->pvc_order_types ), true ) ? $new_instance['order'] : $this->pvc_defaults['order'];
215
+
216
+ // list type
217
+ $old_instance['list_type'] = isset( $new_instance['list_type'] ) && in_array( $new_instance['list_type'], array_keys( $this->pvc_list_types ), true ) ? $new_instance['list_type'] : $this->pvc_defaults['list_type'];
218
+
219
+ // thumbnail size
220
+ $old_instance['thumbnail_size'] = isset( $new_instance['thumbnail_size'] ) && in_array( $new_instance['thumbnail_size'], $this->pvc_image_sizes, true ) ? $new_instance['thumbnail_size'] : $this->pvc_defaults['thumbnail_size'];
221
+
222
+ // booleans
223
+ $old_instance['show_post_views'] = ! empty( $new_instance['show_post_views'] );
224
+ $old_instance['show_post_thumbnail'] = ! empty( $new_instance['show_post_thumbnail'] );
225
+ $old_instance['show_post_excerpt'] = ! empty( $new_instance['show_post_excerpt'] );
226
+ $old_instance['show_post_author'] = ! empty( $new_instance['show_post_author'] );
227
+
228
+ // texts
229
+ $old_instance['title'] = sanitize_text_field( isset( $new_instance['title'] ) ? $new_instance['title'] : $this->pvc_defaults['title'] );
230
+ $old_instance['no_posts_message'] = sanitize_text_field( isset( $new_instance['no_posts_message'] ) ? $new_instance['no_posts_message'] : $this->pvc_defaults['no_posts_message'] );
231
+
232
+ // post types
233
+ if ( isset( $new_instance['post_type'] ) ) {
234
+ $post_types = [];
235
+
236
+ // get post types
237
+ $_post_types = Post_Views_Counter()->functions->get_post_types();
238
+
239
+ foreach ( $new_instance['post_type'] as $post_type ) {
240
+ if ( isset( $_post_types[$post_type] ) )
241
+ $post_types[] = $post_type;
242
+ }
243
+
244
+ $old_instance['post_type'] = array_unique( $post_types );
245
+ } else
246
+ $old_instance['post_type'] = [ 'post' ];
247
+
248
+ return $old_instance;
249
+ }
250
+ }
includes/columns.php CHANGED
@@ -20,18 +20,15 @@ class Post_Views_Counter_Columns {
20
  add_action( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
21
  add_action( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
22
  add_action( 'wp_ajax_save_bulk_post_views', array( $this, 'save_bulk_post_views' ) );
23
- add_action( 'admin_bar_menu', array( $this, 'admin_bar_menu' ), 100 );
24
- add_action( 'wp', array( $this, 'admin_bar_maybe_add_style' ) );
25
- add_action( 'admin_init', array( $this, 'admin_bar_maybe_add_style' ) );
26
 
27
  // gutenberg
28
- add_action( 'plugins_loaded', array( $this, 'init_gutenberg' ) );
29
  }
30
-
31
  /**
32
  * Init Gutenberg
33
  */
34
- public function init_gutenberg() {
35
  $block_editor = has_action( 'enqueue_block_assets' );
36
  $gutenberg = function_exists( 'gutenberg_can_edit_post_type' );
37
 
@@ -53,7 +50,7 @@ class Post_Views_Counter_Columns {
53
  '__block_editor_compatible_meta_box' => true
54
  ) );
55
  }
56
-
57
  /**
58
  * Register REST API Gutenberg endpoints.
59
  */
@@ -63,10 +60,9 @@ class Post_Views_Counter_Columns {
63
  'post-views-counter',
64
  '/update-post-views/',
65
  array(
66
- 'methods' => array( 'POST' ),
67
- 'callback' => array( $this, 'gutenberg_update_callback' ),
68
- 'permission_callback' => array( $this, 'check_rest_route_permissions' ),
69
- 'args' => array(
70
  'id' => array(
71
  'sanitize_callback' => 'absint',
72
  )
@@ -74,25 +70,7 @@ class Post_Views_Counter_Columns {
74
  )
75
  );
76
  }
77
-
78
- /**
79
- * Check whether user has permissions to perform post views update in Gutenberg editor.
80
- *
81
- * @param object $request WP_REST_Request
82
- * @return bool|WP_Error
83
- */
84
- public function check_rest_route_permissions( $request ) {
85
- // break if current user can't edit this post
86
- if ( ! current_user_can( 'edit_post', (int) $request->get_param( 'id' ) ) )
87
- return new WP_Error( 'pvc-user-not-allowed', __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
88
-
89
- // break if views editing is restricted
90
- if ( (bool) Post_Views_Counter()->options['general']['restrict_edit_views'] === true && ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) )
91
- return new WP_Error( 'pvc-user-not-allowed', __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
92
-
93
- return true;
94
- }
95
-
96
  /**
97
  * REST API Callback for Gutenberg endpoint.
98
  *
@@ -118,7 +96,9 @@ class Post_Views_Counter_Columns {
118
  return wp_send_json_error( __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
119
 
120
  // break if views editing is restricted
121
- if ( (bool) Post_Views_Counter()->options['general']['restrict_edit_views'] === true && ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) )
 
 
122
  return wp_send_json_error( __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
123
 
124
  global $wpdb;
@@ -465,153 +445,4 @@ class Post_Views_Counter_Columns {
465
 
466
  exit;
467
  }
468
-
469
- /**
470
- * Add admin bar stats to a post.
471
- */
472
- public function admin_bar_menu( $admin_bar ) {
473
- // get main instance
474
- $pvc = Post_Views_Counter();
475
-
476
- // statistics enabled?
477
- if ( ! apply_filters( 'pvc_display_toolbar_statistics', $pvc->options['display']['toolbar_statistics'] ) )
478
- return;
479
-
480
- $post = null;
481
-
482
- if ( is_admin() && ! wp_doing_ajax() ) {
483
- global $pagenow;
484
-
485
- $post = $pagenow == 'post.php' && ! empty( $_GET['post'] ) ? get_post( (int) $_GET['post'] ) : $post;
486
- } elseif ( is_singular() )
487
- global $post;
488
-
489
- // get countable post types
490
- $post_types = Post_Views_Counter()->options['general']['post_types_count'];
491
-
492
- // whether to count this post type or not
493
- if ( empty( $post_types ) || empty( $post ) || ! in_array( $post->post_type, $post_types, true ) )
494
- return;
495
-
496
- $dt = new DateTime();
497
-
498
- // get post views
499
- $views = pvc_get_views( array(
500
- 'post_id' => $post->ID,
501
- 'post_type' => $post->post_type,
502
- 'fields' => 'date=>views',
503
- 'views_query' => array(
504
- 'year' => $dt->format( 'Y' ),
505
- 'month' => $dt->format( 'm' )
506
- )
507
- ) );
508
-
509
- $graph = '';
510
-
511
- // get highest value
512
- $views_copy = $views;
513
-
514
- arsort( $views_copy, SORT_NUMERIC );
515
-
516
- $highest = reset( $views_copy );
517
-
518
- // find the multiplier
519
- $multiplier = $highest * 0.05;
520
-
521
- // generate ranges
522
- $ranges = array();
523
-
524
- for ( $i = 1; $i <= 20; $i ++ ) {
525
- $ranges[$i] = round( $multiplier * $i );
526
- }
527
-
528
- // create graph
529
- foreach ( $views as $date => $count ) {
530
- $count_class = 0;
531
-
532
- if ( $count > 0 ) {
533
- foreach ( $ranges as $index => $range ) {
534
- if ( $count <= $range ) {
535
- $count_class = $index;
536
- break;
537
- }
538
- }
539
- }
540
-
541
- $graph .= '<span class="pvc-line-graph pvc-line-graph-' . $count_class . '" title="' . sprintf( _n( '%s post view', '%s post views', $count, 'post-views-counter' ), number_format_i18n( $count ) ) . '"></span>';
542
- }
543
-
544
- $admin_bar->add_menu(
545
- [
546
- 'id' => 'pvc-post-views',
547
- 'title' => '<span class="pvc-graph-container">' . $graph . '</span>',
548
- 'href' => false,
549
- 'meta' => [
550
- 'title' => false
551
- ]
552
- ]
553
- );
554
- }
555
-
556
- /**
557
- * Maybe add admin CSS.
558
- */
559
- public function admin_bar_maybe_add_style() {
560
- // get main instance
561
- $pvc = Post_Views_Counter();
562
-
563
- // statistics enabled?
564
- if ( ! $pvc->options['display']['toolbar_statistics'] )
565
- return;
566
-
567
- $post = null;
568
-
569
- if ( is_admin() && ! wp_doing_ajax() ) {
570
- global $pagenow;
571
-
572
- $post = ( $pagenow === 'post.php' && ! empty( $_GET['post'] ) ) ? get_post( (int) $_GET['post'] ) : $post;
573
- } elseif ( is_singular() )
574
- global $post;
575
-
576
- // get countable post types
577
- $post_types = $pvc->options['general']['post_types_count'];
578
-
579
- // whether to count this post type or not
580
- if ( empty( $post_types ) || empty( $post ) || ! in_array( $post->post_type, $post_types, true ) )
581
- return;
582
-
583
- // on backend area
584
- add_action( 'admin_head', array( $this, 'admin_bar_css' ) );
585
-
586
- // on frontend area
587
- add_action( 'wp_head', array( $this, 'admin_bar_css' ) );
588
- }
589
-
590
- /**
591
- * Add admin CSS.
592
- */
593
- public function admin_bar_css() {
594
- $html = '
595
- <style type="text/css">
596
- #wp-admin-bar-pvc-post-views .pvc-graph-container { padding-top: 6px; padding-bottom: 6px; position: relative; display: block; height: 100%; box-sizing: border-box; }
597
- #wp-admin-bar-pvc-post-views .pvc-line-graph {
598
- display: inline-block;
599
- width: 1px;
600
- margin-right: 1px;
601
- background-color: #ccc;
602
- vertical-align: baseline;
603
- }
604
- #wp-admin-bar-pvc-post-views .pvc-line-graph:hover { background-color: #eee; }
605
- #wp-admin-bar-pvc-post-views .pvc-line-graph-0 { height: 1% }';
606
-
607
- for ( $i = 1; $i <= 20; $i ++ ) {
608
- $html .= '
609
- #wp-admin-bar-pvc-post-views .pvc-line-graph-' . $i . ' { height: ' . $i * 5 . '% }';
610
- }
611
-
612
- $html .= '
613
- </style>';
614
-
615
- echo $html;
616
- }
617
- }
20
  add_action( 'bulk_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
21
  add_action( 'quick_edit_custom_box', array( $this, 'quick_edit_custom_box' ), 10, 2 );
22
  add_action( 'wp_ajax_save_bulk_post_views', array( $this, 'save_bulk_post_views' ) );
 
 
 
23
 
24
  // gutenberg
25
+ add_action( 'plugins_loaded', array( $this, 'init_gutemberg' ) );
26
  }
27
+
28
  /**
29
  * Init Gutenberg
30
  */
31
+ public function init_gutemberg() {
32
  $block_editor = has_action( 'enqueue_block_assets' );
33
  $gutenberg = function_exists( 'gutenberg_can_edit_post_type' );
34
 
50
  '__block_editor_compatible_meta_box' => true
51
  ) );
52
  }
53
+
54
  /**
55
  * Register REST API Gutenberg endpoints.
56
  */
60
  'post-views-counter',
61
  '/update-post-views/',
62
  array(
63
+ 'methods' => array( 'POST' ),
64
+ 'callback' => array( $this, 'gutenberg_update_callback' ),
65
+ 'args' => array(
 
66
  'id' => array(
67
  'sanitize_callback' => 'absint',
68
  )
70
  )
71
  );
72
  }
73
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  /**
75
  * REST API Callback for Gutenberg endpoint.
76
  *
96
  return wp_send_json_error( __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
97
 
98
  // break if views editing is restricted
99
+ $restrict = (bool) Post_Views_Counter()->options['general']['restrict_edit_views'];
100
+
101
+ if ( $restrict === true && ! current_user_can( apply_filters( 'pvc_restrict_edit_capability', 'manage_options' ) ) )
102
  return wp_send_json_error( __( 'You are not allowed to edit this item.', 'post-views-counter' ) );
103
 
104
  global $wpdb;
445
 
446
  exit;
447
  }
448
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/counter.php CHANGED
@@ -74,27 +74,6 @@ class Post_Views_Counter_Counter {
74
  }
75
  }
76
  }
77
-
78
- // strict counts?
79
- if ( Post_Views_Counter()->options['general']['strict_counts'] ) {
80
- // get IP cached visits
81
- $ip_cache = get_transient( 'post_views_counter_ip_cache' );
82
-
83
- if ( ! $ip_cache )
84
- $ip_cache = array();
85
-
86
- // get user IP address
87
- $user_ip = $this->encrypt_ip( $this->get_user_ip() );
88
-
89
- // get current time
90
- $current_time = current_time( 'timestamp', true );
91
-
92
- // visit exists in transient?
93
- if ( isset( $ip_cache[$id][$user_ip] ) ) {
94
- if ( $current_time < $ip_cache[$id][$user_ip] + $this->get_timestamp( Post_Views_Counter()->options['general']['time_between_counts']['type'], Post_Views_Counter()->options['general']['time_between_counts']['number'], false ) )
95
- return;
96
- }
97
- }
98
 
99
  // get groups to check them faster
100
  $groups = Post_Views_Counter()->options['general']['exclude']['groups'];
@@ -386,7 +365,7 @@ class Post_Views_Counter_Counter {
386
  */
387
  private function save_ip( $id ) {
388
  $set_cookie = apply_filters( 'pvc_maybe_set_cookie', true );
389
-
390
  // Cookie Notice compatibility
391
  if ( function_exists( 'cn_cookies_accepted' ) && ! cn_cookies_accepted() )
392
  $set_cookie = false;
@@ -401,7 +380,7 @@ class Post_Views_Counter_Counter {
401
  $ip_cache = array();
402
 
403
  // get user IP address
404
- $user_ip = $this->encrypt_ip( $this->get_user_ip() );
405
 
406
  // get current time
407
  $current_time = current_time( 'timestamp', true );
@@ -674,15 +653,15 @@ class Post_Views_Counter_Counter {
674
 
675
  global $wpdb;
676
 
677
- $result = $wpdb->query( "
678
- INSERT INTO " . $wpdb->prefix . "post_views (id, type, period, count)
679
- VALUES " . $this->db_insert_values . "
680
- ON DUPLICATE KEY UPDATE count = count + VALUES(count)"
681
- );
682
 
683
  $this->db_insert_values = '';
684
 
685
- return $result;
686
  }
687
 
688
  /**
@@ -730,8 +709,6 @@ class Post_Views_Counter_Counter {
730
  * @return string
731
  */
732
  public function get_user_ip() {
733
- $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
734
-
735
  foreach ( array( 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ) as $key ) {
736
  if ( array_key_exists( $key, $_SERVER ) === true ) {
737
  foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
@@ -740,18 +717,18 @@ class Post_Views_Counter_Counter {
740
 
741
  // attempt to validate IP
742
  if ( $this->validate_user_ip( $ip ) )
743
- continue;
744
  }
745
  }
746
  }
747
 
748
- return $ip;
749
  }
750
 
751
  /**
752
  * Ensure an ip address is both a valid IP and does not fall within a private network range.
753
- *
754
- * @param $ip string IP address
755
  * @return bool
756
  */
757
  public function validate_user_ip( $ip ) {
@@ -761,78 +738,17 @@ class Post_Views_Counter_Counter {
761
  return true;
762
  }
763
 
764
- /**
765
- * Encrypt user IP.
766
- *
767
- * @param int $ip
768
- * @return string $encrypted_ip
769
- */
770
- public function encrypt_ip( $ip ) {
771
- $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
772
- $auth_iv = defined( 'NONCE_KEY' ) ? NONCE_KEY : '';
773
-
774
- // mcrypt strong encryption
775
- if ( function_exists( 'mcrypt_encrypt' ) && function_exists( 'mcrypt_get_key_size' ) && function_exists( 'mcrypt_get_iv_size' ) && defined( 'MCRYPT_BLOWFISH' ) ) {
776
- // get max key size of the mcrypt mode
777
- $max_key_size = mcrypt_get_key_size( MCRYPT_BLOWFISH, MCRYPT_MODE_CBC );
778
- $max_iv_size = mcrypt_get_iv_size( MCRYPT_BLOWFISH, MCRYPT_MODE_CBC );
779
-
780
- $encrypt_key = mb_strimwidth( $auth_key, 0, $max_key_size );
781
- $encrypt_iv = mb_strimwidth( $auth_iv, 0, $max_iv_size );
782
-
783
- $encrypted_ip = strtr( base64_encode( mcrypt_encrypt( MCRYPT_BLOWFISH, $encrypt_key, $ip, MCRYPT_MODE_CBC, $encrypt_iv ) ), '+/=', '-_,' );
784
- // simple encryption
785
- } elseif ( function_exists( 'gzdeflate' ) )
786
- $encrypted_ip = base64_encode( convert_uuencode( gzdeflate( $ip ) ) );
787
- // no encryption
788
- else
789
- $encrypted_ip = strtr( base64_encode( convert_uuencode( $ip ) ), '+/=', '-_,' );
790
-
791
- return $encrypted_ip;
792
- }
793
-
794
- /**
795
- * Decrypt user IP.
796
- *
797
- * @param int $encrypted_ip
798
- * @return string $ip
799
- */
800
- public function decrypt_ip( $encrypted_ip ) {
801
- $auth_key = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
802
- $auth_iv = defined( 'NONCE_KEY' ) ? NONCE_KEY : '';
803
-
804
- // mcrypt strong encryption
805
- if ( function_exists( 'mcrypt_decrypt' ) && function_exists( 'mcrypt_get_key_size' ) && function_exists( 'mcrypt_get_iv_size' ) && defined( 'MCRYPT_BLOWFISH' ) ) {
806
- // get max key size of the mcrypt mode
807
- $max_key_size = mcrypt_get_key_size( MCRYPT_BLOWFISH, MCRYPT_MODE_CBC );
808
- $max_iv_size = mcrypt_get_iv_size( MCRYPT_BLOWFISH, MCRYPT_MODE_CBC );
809
-
810
- $encrypt_key = mb_strimwidth( $auth_key, 0, $max_key_size );
811
- $encrypt_iv = mb_strimwidth( $auth_iv, 0, $max_iv_size );
812
-
813
- $ip = mcrypt_decrypt( MCRYPT_BLOWFISH, $encrypt_key, base64_decode( strtr( $encrypted_ip, '-_,', '+/=' ) ), MCRYPT_MODE_CBC, $encrypt_iv );
814
- // simple encryption
815
- } elseif ( function_exists( 'gzinflate' ) )
816
- $ip = gzinflate( convert_uudecode( base64_decode( $encrypted_ip ) ) );
817
- // no encryption
818
- else
819
- $ip = convert_uudecode( base64_decode( strtr( $encrypted_ip, '-_,', '+/=' ) ) );
820
-
821
- return $ip;
822
- }
823
-
824
  /**
825
  * Register REST API endpoints.
826
- *
827
  * @return void
828
  */
829
  public function rest_api_init() {
830
  // view post route
831
  register_rest_route( 'post-views-counter', '/view-post/', array(
832
- 'methods' => array( 'GET', 'POST' ),
833
- 'callback' => array( $this, 'check_post_rest_api' ),
834
- 'permission_callback' => array( $this, 'post_views_permissions_check' ),
835
- 'args' => array(
836
  'id' => array(
837
  'default' => 0,
838
  'sanitize_callback' => 'absint'
@@ -856,7 +772,7 @@ class Post_Views_Counter_Counter {
856
 
857
  /**
858
  * Get post views via REST API request.
859
- *
860
  * @param array $request
861
  * @return int
862
  */
@@ -867,17 +783,7 @@ class Post_Views_Counter_Counter {
867
  }
868
 
869
  /**
870
- * Check if a given request has access to get views.
871
- *
872
- * @param WP_REST_Request $request Full data about the request.
873
- * @return WP_Error|bool
874
- */
875
- public function post_views_permissions_check( $request ) {
876
- return (bool) apply_filters( 'pvc_rest_api_post_views_check', true, $request );
877
- }
878
-
879
- /**
880
- * Check if a given request has access to get views.
881
  *
882
  * @param WP_REST_Request $request Full data about the request.
883
  * @return WP_Error|bool
@@ -885,4 +791,5 @@ class Post_Views_Counter_Counter {
885
  public function get_post_views_permissions_check( $request ) {
886
  return (bool) apply_filters( 'pvc_rest_api_get_post_views_check', true, $request );
887
  }
 
888
  }
74
  }
75
  }
76
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
  // get groups to check them faster
79
  $groups = Post_Views_Counter()->options['general']['exclude']['groups'];
365
  */
366
  private function save_ip( $id ) {
367
  $set_cookie = apply_filters( 'pvc_maybe_set_cookie', true );
368
+
369
  // Cookie Notice compatibility
370
  if ( function_exists( 'cn_cookies_accepted' ) && ! cn_cookies_accepted() )
371
  $set_cookie = false;
380
  $ip_cache = array();
381
 
382
  // get user IP address
383
+ $user_ip = (string) $this->get_user_ip();
384
 
385
  // get current time
386
  $current_time = current_time( 'timestamp', true );
653
 
654
  global $wpdb;
655
 
656
+ // $result = $wpdb->query( "
657
+ // INSERT INTO " . $wpdb->prefix . "post_views (id, type, period, count)
658
+ // VALUES " . $this->db_insert_values . "
659
+ // ON DUPLICATE KEY UPDATE count = count + VALUES(count)"
660
+ // );
661
 
662
  $this->db_insert_values = '';
663
 
664
+ // return $result;
665
  }
666
 
667
  /**
709
  * @return string
710
  */
711
  public function get_user_ip() {
 
 
712
  foreach ( array( 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR' ) as $key ) {
713
  if ( array_key_exists( $key, $_SERVER ) === true ) {
714
  foreach ( explode( ',', $_SERVER[$key] ) as $ip ) {
717
 
718
  // attempt to validate IP
719
  if ( $this->validate_user_ip( $ip ) )
720
+ return $ip;
721
  }
722
  }
723
  }
724
 
725
+ return isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
726
  }
727
 
728
  /**
729
  * Ensure an ip address is both a valid IP and does not fall within a private network range.
730
+ *
731
+ * @param $ip
732
  * @return bool
733
  */
734
  public function validate_user_ip( $ip ) {
738
  return true;
739
  }
740
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
741
  /**
742
  * Register REST API endpoints.
743
+ *
744
  * @return void
745
  */
746
  public function rest_api_init() {
747
  // view post route
748
  register_rest_route( 'post-views-counter', '/view-post/', array(
749
+ 'methods' => array( 'GET', 'POST' ),
750
+ 'callback' => array( $this, 'check_post_rest_api' ),
751
+ 'args' => array(
 
752
  'id' => array(
753
  'default' => 0,
754
  'sanitize_callback' => 'absint'
772
 
773
  /**
774
  * Get post views via REST API request.
775
+ *
776
  * @param array $request
777
  * @return int
778
  */
783
  }
784
 
785
  /**
786
+ * Check if a given request has access to get views
 
 
 
 
 
 
 
 
 
 
787
  *
788
  * @param WP_REST_Request $request Full data about the request.
789
  * @return WP_Error|bool
791
  public function get_post_views_permissions_check( $request ) {
792
  return (bool) apply_filters( 'pvc_rest_api_get_post_views_check', true, $request );
793
  }
794
+
795
  }
includes/dashboard.php CHANGED
@@ -12,11 +12,10 @@ class Post_Views_Counter_Dashboard {
12
 
13
  public function __construct() {
14
  // actions
15
- add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ), 1 );
16
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts_styles' ) );
17
- add_action( 'wp_ajax_pvc_dashboard_most_viewed', array( $this, 'dashboard_get_most_viewed' ) );
18
- add_action( 'wp_ajax_pvc_dashboard_chart', array( $this, 'dashboard_get_chart' ) );
19
- add_action( 'wp_ajax_pvc_dashboard_user_options', array( $this, 'update_dashboard_user_options' ) );
20
  }
21
 
22
  /**
@@ -28,112 +27,102 @@ class Post_Views_Counter_Dashboard {
28
  return;
29
  }
30
 
31
- // add dashboard post views chart widget
32
- wp_add_dashboard_widget( 'pvc_dashboard', __( 'Post Views Counter', 'post-views-counter' ), array( $this, 'dashboard_widget' ) );
33
  }
34
-
35
  /**
36
- * Enqueue admin scripts and styles.
37
- *
38
- * @param string $pagenow
39
  */
40
- public function admin_scripts_styles( $pagenow ) {
41
- if ( $pagenow != 'index.php' )
42
- return;
 
 
43
 
44
- // filter user_can_see_stats
45
- if ( ! apply_filters( 'pvc_user_can_see_stats', current_user_can( 'publish_posts' ) ) )
46
- return;
47
 
48
- wp_register_style( 'pvc-admin-dashboard', POST_VIEWS_COUNTER_URL . '/css/admin-dashboard.css', array(), Post_Views_Counter()->defaults['version'] );
49
- wp_enqueue_style( 'pvc-admin-dashboard' );
50
- wp_enqueue_style( 'pvc-chartjs', POST_VIEWS_COUNTER_URL . '/assets/chartjs/chart.min.css', array(), Post_Views_Counter()->defaults['version'] );
51
- wp_enqueue_style( 'pvc-microtip', POST_VIEWS_COUNTER_URL . '/assets/microtip/microtip.min.css', array(), Post_Views_Counter()->defaults['version'] );
52
 
53
- wp_register_script( 'pvc-chartjs', POST_VIEWS_COUNTER_URL . '/assets/chartjs/chart.min.js', array( 'jquery' ), Post_Views_Counter()->defaults['version'], true );
54
- wp_register_script( 'pvc-admin-dashboard', POST_VIEWS_COUNTER_URL . '/js/admin-dashboard.js', array( 'jquery', 'pvc-chartjs' ), Post_Views_Counter()->defaults['version'], true );
 
 
 
 
 
 
 
 
 
 
55
 
56
- wp_enqueue_script( 'pvc-admin-dashboard' );
57
 
58
- wp_localize_script(
59
- 'pvc-admin-dashboard',
60
- 'pvcArgs',
61
- array(
62
- 'ajaxURL' => admin_url( 'admin-ajax.php' ),
63
- 'nonce' => wp_create_nonce( 'pvc-dashboard-chart' ),
64
- 'nonceUser' => wp_create_nonce( 'pvc-dashboard-user-options' )
65
- )
 
66
  );
 
 
67
  }
68
 
69
  /**
70
- * Render dashboard widget.
71
- *
72
- * @return mixed
73
  */
74
- public function dashboard_widget() {
75
- // get user ID
76
- $user_id = get_current_user_id();
77
-
78
- // get user options
79
- $user_options = get_user_meta( $user_id, 'pvc_dashboard', true );
80
-
81
- // empty options?
82
- if ( ! is_array( $user_options ) || empty( $user_options ) )
83
- $user_options = array();
84
-
85
- // sanitize options
86
- $user_options = map_deep( $user_options, 'sanitize_text_field' );
87
- $menu_items = ! empty( $user_options['menu_items'] ) ? $user_options['menu_items'] : array();
88
-
89
- $months_html = $this->generate_months( current_time( 'timestamp', false ) );
90
- ?>
91
- <div id="pvc-dashboard-accordion" class="pvc-accordion">
92
- <div id="pvc-post-views" class="pvc-accordion-item<?php echo in_array( 'post-views', $menu_items ) ? ' pvc-collapsed' : ''; ?>">
93
- <div class="pvc-accordion-header">
94
- <div class="pvc-accordion-toggle"><span class="pvc-accordion-title"><?php _e( 'Post Views', 'post-views-counter' ); ?></span><span class="pvc-tooltip" aria-label="<?php _e( 'Displays the chart of most viewed post types for a selected time period.', 'post-views-counter' ); ?>" data-microtip-position="top" data-microtip-size="large" role="tooltip"><span class="pvc-tooltip-icon"></span></span></div>
95
- <?php /*
96
- <div class="pvc-accordion-actions">
97
- <a href="javascript:void(0);" class="pvc-accordion-action dashicons dashicons-admin-generic"></a>
98
- </div>
99
- */ ?>
100
- </div>
101
- <div class="pvc-accordion-content">
102
- <div class="pvc-dashboard-container loading">
103
-
104
- <div id="pvc-chart-container" class="pvc-data-container">
105
- <canvas id="pvc-chart" height="175"></canvas>
106
- <span class="spinner"></span>
107
- </div>
108
-
109
- <div class="pvc-months">
110
- <?php echo $months_html; ?>
111
- </div>
112
-
113
- </div>
114
- </div>
115
- </div>
116
- <div id="pvc-most-viewed" class="pvc-accordion-item<?php echo in_array( 'most-viewed', $menu_items ) ? ' pvc-collapsed' : ''; ?>">
117
- <div class="pvc-accordion-header">
118
- <div class="pvc-accordion-toggle"><span class="pvc-accordion-title"><?php _e( 'Top Posts', 'post-views-counter' ); ?></span><span class="pvc-tooltip" aria-label="<?php _e( 'Displays the list of most viewed posts and pages on your website.', 'post-views-counter' ); ?>" data-microtip-position="top" data-microtip-size="large" role="tooltip"><span class="pvc-tooltip-icon"></span></span></div>
119
- </div>
120
- <div class="pvc-accordion-content">
121
- <div class="pvc-dashboard-container loading">
122
-
123
- <div class="pvc-data-container">
124
- <div id="pvc-viewed" class="pvc-table-responsive"></div>
125
- <span class="spinner"></span>
126
- </div>
127
-
128
- <div class="pvc-months">
129
- <?php echo $months_html; ?>
130
- </div>
131
-
132
- </div>
133
- </div>
134
- </div>
135
- </div>
136
- <?php
137
  }
138
 
139
  /**
@@ -142,11 +131,11 @@ class Post_Views_Counter_Dashboard {
142
  * @global $_wp_admin_css_colors
143
  * @return void
144
  */
145
- public function dashboard_get_chart() {
146
  if ( ! apply_filters( 'pvc_user_can_see_stats', current_user_can( 'publish_posts' ) ) )
147
  wp_die( _( 'You do not have permission to access this page.', 'post-views-counter' ) );
148
 
149
- if ( ! check_ajax_referer( 'pvc-dashboard-chart', 'nonce' ) )
150
  wp_die( __( 'You do not have permission to access this page.', 'post-views-counter' ) );
151
 
152
  // get period
@@ -168,26 +157,12 @@ class Post_Views_Counter_Dashboard {
168
  // $now = getdate( current_time( 'timestamp', get_option( 'gmt_offset' ) ) );
169
  $now = getdate( current_time( 'timestamp', get_option( 'gmt_offset' ) ) - 2592000 );
170
 
171
- // get color scheme global
172
  global $_wp_admin_css_colors;
173
 
174
- // set default color;
175
- $color = array(
176
- 'r' => 105,
177
- 'g' => 168,
178
- 'b' => 187
179
- );
180
-
181
- if ( ! empty( $_wp_admin_css_colors ) ) {
182
- // get current admin color scheme name
183
- $current_color_scheme = get_user_option( 'admin_color' );
184
-
185
- if ( empty( $current_color_scheme ) )
186
- $current_color_scheme = 'fresh';
187
-
188
- if ( isset( $_wp_admin_css_colors[$current_color_scheme] ) )
189
- $color = $this->hex2rgb( $_wp_admin_css_colors[$current_color_scheme]->colors[2] );
190
- }
191
 
192
  // set chart labels
193
  switch ( $period ) {
@@ -238,14 +213,8 @@ class Post_Views_Counter_Dashboard {
238
  foreach ( $post_types as $id => $post_type ) {
239
  $post_type_obj = get_post_type_object( $post_type );
240
 
241
- // unrecognized post type? (mainly from deactivated plugins)
242
- if ( empty( $post_type_obj ) )
243
- $label = __( 'Unknown', 'post-views-counter' ) . ' #' . $id;
244
- else
245
- $label = $post_type_obj->labels->name;
246
-
247
- $data['data']['datasets'][$id]['label'] = $label;
248
- $data['data']['datasets'][$id]['post_type'] = $post_type;
249
  $data['data']['datasets'][$id]['data'] = array();
250
 
251
  // get month views
@@ -290,7 +259,7 @@ class Post_Views_Counter_Dashboard {
290
 
291
  case 'this_month':
292
  default:
293
- $user_options = $this->get_dashboard_user_options( get_current_user_id(), 'post_types' );
294
 
295
  if ( $period !== 'this_month' ) {
296
  $date = explode( '|', $period, 2 );
@@ -321,7 +290,7 @@ class Post_Views_Counter_Dashboard {
321
 
322
  $data['data']['datasets'][0]['label'] = __( 'Total Views', 'post-views-counter' );
323
  $data['data']['datasets'][0]['post_type'] = '_pvc_total_views';
324
- $data['data']['datasets'][0]['hidden'] = in_array( '_pvc_total_views', $user_options, true );
325
 
326
  // reindex post types
327
  $post_types = array_combine( range( 1, count( $post_types ) ), array_values( $post_types ) );
@@ -331,15 +300,9 @@ class Post_Views_Counter_Dashboard {
331
  foreach ( $post_types as $id => $post_type ) {
332
  $post_type_obj = get_post_type_object( $post_type );
333
 
334
- // unrecognized post type? (mainly from deactivated plugins)
335
- if ( empty( $post_type_obj ) )
336
- $label = __( 'Unknown', 'post-views-counter' ) . ' #' . $id;
337
- else
338
- $label = $post_type_obj->labels->name;
339
-
340
- $data['data']['datasets'][$id]['label'] = $label;
341
- $data['data']['datasets'][$id]['post_type'] = $post_type;
342
- $data['data']['datasets'][$id]['hidden'] = in_array( $post_type, $user_options, true );
343
  $data['data']['datasets'][$id]['data'] = array();
344
 
345
  // get month views
@@ -387,208 +350,56 @@ class Post_Views_Counter_Dashboard {
387
 
388
  exit;
389
  }
390
-
391
- /**
392
- * Dashboard widget chart data function.
393
- *
394
- * @global $_wp_admin_css_colors
395
- * @return void
396
- */
397
- public function dashboard_get_most_viewed() {
398
- if ( ! apply_filters( 'pvc_user_can_see_stats', current_user_can( 'publish_posts' ) ) )
399
- wp_die( _( 'You do not have permission to access this page.', 'post-views-counter' ) );
400
-
401
- if ( ! check_ajax_referer( 'pvc-dashboard-chart', 'nonce' ) )
402
- wp_die( __( 'You do not have permission to access this page.', 'post-views-counter' ) );
403
 
404
- // get period
405
- $period = isset( $_POST['period'] ) ? esc_attr( $_POST['period'] ) : 'this_month';
406
-
407
- // get post types
408
- $post_types = Post_Views_Counter()->options['general']['post_types_count'];
409
-
410
- if ( $period !== 'this_month' ) {
411
- $date = explode( '|', $period, 2 );
412
- $months = strtotime( (string) $date[1] . '-' . (string) $date[0] . '-13' );
413
- } else
414
- $months = current_time( 'timestamp', false );
415
-
416
- // get date chunks
417
- $date = explode( ' ', date( "m Y t F", $months ) );
418
-
419
- // get stats
420
- $query_args = array(
421
- 'post_type' => $post_types,
422
- 'posts_per_page' => 10,
423
- 'paged' => false,
424
- 'suppress_filters' => false,
425
- 'no_found_rows' => true,
426
- 'views_query' => array(
427
- 'year' => $date[1],
428
- 'month' => $date[0],
429
- )
430
- );
431
-
432
- $posts = pvc_get_most_viewed_posts( $query_args );
433
-
434
- $data = array(
435
- 'months' => $this->generate_months( $months ),
436
- 'html' => '',
437
- );
438
-
439
- $html = '<table id="pvc-most-viewed-table" class="pvc-table pvc-table-hover">';
440
- $html .= '<thead>';
441
- $html .= '<tr>';
442
- $html .= '<th scope="col">#</th>';
443
- $html .= '<th scope="col">' . __( 'Post', 'post-views-counter' ) . '</th>';
444
- $html .= '<th scope="col">' . __( 'Post Views', 'post-views-counter' ) . '</th>';
445
- $html .= '</tr>';
446
- $html .= '</thead>';
447
- $html .= '<tbody>';
448
-
449
- if ( $posts ) {
450
- foreach ( $posts as $index => $post ) {
451
- setup_postdata( $post );
452
-
453
- $html .= '<tr>';
454
- $html .= '<th scope="col">' . ( $index + 1 ) . '</th>';
455
-
456
- if ( current_user_can( 'edit_post', $post->ID ) ) {
457
- $html .= '<td><a href="' . get_edit_post_link( $post->ID ) . '">' . get_the_title( $post ) . '</a></td>';
458
- } else {
459
- $html .= '<td>' . get_the_title( $post ). '</td>';
460
- }
461
-
462
- $html .= '<td>' . number_format_i18n( $post->post_views ) . '</td>';
463
- $html .= '</tr>';
464
- }
465
- } else {
466
- $html .= '<tr class="no-posts">';
467
- $html .= '<td colspan="3">' . __( 'No most viewed posts found', 'post-views-counter' ) . '</td>';
468
- $html .= '</tr>';
469
- }
470
-
471
- $html .= '</tbody>';
472
- $html .='</table>';
473
-
474
- $data['html'] = $html;
475
-
476
- echo json_encode( $data );
477
- exit;
478
- }
479
-
480
  /**
481
- * Generate months.
482
  *
483
- * @param string $timestamp
484
- * @return string
485
  */
486
- public function generate_months( $timestamp ) {
487
- $dates = array(
488
- explode( ' ', date( "m F Y", strtotime( "-1 months", $timestamp ) ) ),
489
- explode( ' ', date( "m F Y", $timestamp ) ),
490
- explode( ' ', date( "m F Y", strtotime( "+1 months", $timestamp ) ) )
491
- );
492
 
493
- $current = date( "Ym", current_time( 'timestamp', false ) );
 
494
 
495
- if ( (int) $current <= (int) ( $dates[1][2] . $dates[1][0] ) )
496
- $next = '<span class="next">' . $dates[2][1] . ' ' . $dates[2][2] . ' ›</span>';
497
- else
498
- $next = '<a class="next" href="#" data-date="' . ( $dates[2][0] . '|' . $dates[2][2] ) . '">' . $dates[2][1] . ' ' . $dates[2][2] . ' ›</a>';
499
-
500
- $dates = array(
501
- 'prev' => '<a class="prev" href="#" data-date="' . ( $dates[0][0] . '|' . $dates[0][2] ) . '">‹ ' . $dates[0][1] . ' ' . $dates[0][2] . '</a>',
502
- 'current' => '<span class="current">' . $dates[1][1] . ' ' . $dates[1][2] . '</span>',
503
- 'next' => $next
504
- );
505
 
506
- return $dates['prev'] . $dates['current'] . $dates['next'];
507
  }
508
 
509
  /**
510
- * Dashboard widget chart user post types.
511
  *
512
- * @return void
513
  */
514
- public function update_dashboard_user_options() {
515
- if ( ! check_ajax_referer( 'pvc-dashboard-user-options', 'nonce' ) )
516
- wp_die( __( 'You do not have permission to access this page.', 'post-views-counter' ) );
517
-
518
- // get allowed post types
519
- $allowed_post_types = Post_Views_Counter()->options['general']['post_types_count'];
520
-
521
- // simulate total views as post type
522
- $allowed_post_types[] = '_pvc_total_views';
523
-
524
- // get allowed menu items
525
- $allowed_menu_items = array( 'post-views', 'most-viewed' );
526
-
527
- // valid data?
528
- if ( isset( $_POST['nonce'], $_POST['options'] ) && ! empty( $_POST['options'] ) ) {
529
- // get options
530
- $update = map_deep( $_POST['options'], 'sanitize_text_field' );
531
-
532
- // get user ID
533
- $user_id = get_current_user_id();
534
-
535
- // get user dashboard data
536
- $user_options = get_user_meta( $user_id, 'pvc_dashboard', true );
537
-
538
- // empty userdata?
539
- if ( ! is_array( $user_options ) || empty( $user_options ) )
540
- $user_options = array();
541
-
542
- // empty post types?
543
- if ( ! array_key_exists( 'post_types', $user_options ) || ! is_array( $user_options['post_types'] ) )
544
- $user_options['post_types'] = array();
545
-
546
- // hide post type?
547
- if ( ! empty( $update['post_type'] ) && in_array( $update['post_type'], $allowed_post_types, true ) ) {
548
- if ( isset( $update['hidden'] ) && $update['hidden'] === 'true' ) {
549
- if ( ! in_array( $update['post_type'], $user_options['post_types'], true ) )
550
- $user_options['post_types'][] = $update['post_type'];
551
- } else {
552
- if ( ( $key = array_search( $update['post_type'], $user_options['post_types'] ) ) !== false )
553
- unset( $user_options['post_types'][$key] );
554
- }
555
- }
556
-
557
- // hide menu item?
558
- $user_options['menu_items'] = array();
559
-
560
- if ( ! empty( $update['menu_items'] ) && is_array( $update['menu_items'] ) ) {
561
- $update['menu_items'] = map_deep( $update['menu_items'], 'sanitize_text_field' );
562
-
563
- foreach ( $update['menu_items'] as $menu_item => $hidden ) {
564
- if ( in_array( $menu_item, $allowed_menu_items ) && $hidden === 'true' )
565
- $user_options['menu_items'][] = $menu_item;
566
- }
567
- }
568
-
569
- // update userdata
570
- update_user_meta( $user_id, 'pvc_dashboard', $user_options );
571
- }
572
 
573
- exit;
574
- }
 
575
 
576
- /**
577
- * Get user dashboard data.
578
- *
579
- * @param string $data_type
580
- * @return array
581
- */
582
- public function get_dashboard_user_options( $user_id, $data_type ) {
583
- $user_options = get_user_meta( $user_id, 'pvc_dashboard', true );
584
 
585
- if ( ! is_array( $user_options ) || empty( $user_options ) )
586
- $user_options = array();
587
 
588
- if ( ! array_key_exists( $data_type, $user_options ) || ! is_array( $user_options[$data_type] ) )
589
- $user_options[$data_type] = array();
590
 
591
- return $user_options[$data_type];
 
 
 
 
 
 
 
 
592
  }
593
 
594
  /**
12
 
13
  public function __construct() {
14
  // actions
15
+ add_action( 'wp_dashboard_setup', array( $this, 'wp_dashboard_setup' ) );
16
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts_styles' ) );
17
+ add_action( 'wp_ajax_pvc_dashboard_chart', array( $this, 'dashboard_widget_chart' ) );
18
+ add_action( 'wp_ajax_pvc_dashboard_chart_user_post_types', array( $this, 'dashboard_widget_chart_user_post_types' ) );
 
19
  }
20
 
21
  /**
27
  return;
28
  }
29
 
30
+ // add dashboard widget
31
+ wp_add_dashboard_widget( 'pvc_dashboard', __( 'Post Views', 'post-views-counter' ), array( $this, 'dashboard_widget' ) );
32
  }
33
+
34
  /**
35
+ * Render dashboard widget.
36
+ *
37
+ * @return mixed
38
  */
39
+ public function dashboard_widget() {
40
+ ?>
41
+ <div id="pvc_dashboard_container">
42
+ <canvas id="pvc_chart" height="175"></canvas>
43
+ <div class="pvc_months">
44
 
45
+ <?php echo $this->generate_months( current_time( 'timestamp', false ) ); ?>
 
 
46
 
47
+ </div>
48
+ </div>
49
+ <?php
50
+ }
51
 
52
+ /**
53
+ * Generate months.
54
+ *
55
+ * @param string $timestamp
56
+ * @return string
57
+ */
58
+ public function generate_months( $timestamp ) {
59
+ $dates = array(
60
+ explode( ' ', date( "m F Y", strtotime( "-1 months", $timestamp ) ) ),
61
+ explode( ' ', date( "m F Y", $timestamp ) ),
62
+ explode( ' ', date( "m F Y", strtotime( "+1 months", $timestamp ) ) )
63
+ );
64
 
65
+ $current = date( "Ym", current_time( 'timestamp', false ) );
66
 
67
+ if ( (int) $current <= (int) ( $dates[1][2] . $dates[1][0] ) )
68
+ $next = '<span class="next">' . $dates[2][1] . ' ' . $dates[2][2] . ' ›</span>';
69
+ else
70
+ $next = '<a class="next" href="#" data-date="' . ( $dates[2][0] . '|' . $dates[2][2] ) . '">' . $dates[2][1] . ' ' . $dates[2][2] . ' ›</a>';
71
+
72
+ $dates = array(
73
+ 'prev' => '<a class="prev" href="#" data-date="' . ( $dates[0][0] . '|' . $dates[0][2] ) . '">‹ ' . $dates[0][1] . ' ' . $dates[0][2] . '</a>',
74
+ 'current' => '<span class="current">' . $dates[1][1] . ' ' . $dates[1][2] . '</span>',
75
+ 'next' => $next
76
  );
77
+
78
+ return $dates['prev'] . $dates['current'] . $dates['next'];
79
  }
80
 
81
  /**
82
+ * Dashboard widget chart user post types.
83
+ *
84
+ * @return void
85
  */
86
+ public function dashboard_widget_chart_user_post_types() {
87
+ if ( ! check_ajax_referer( 'dashboard-chart-user-post-types', 'nonce' ) )
88
+ wp_die( __( 'You do not have permission to access this page.', 'post-views-counter' ) );
89
+
90
+ // get post types
91
+ $post_types = Post_Views_Counter()->options['general']['post_types_count'];
92
+
93
+ // simulate total views as post type
94
+ $post_types[] = '_pvc_total_views';
95
+
96
+ // valid data?
97
+ if ( isset( $_POST['nonce'], $_POST['hidden'], $_POST['post_type'] ) && ( in_array( $_POST['post_type'], $post_types, true ) ) ) {
98
+ // get user ID
99
+ $user_id = get_current_user_id();
100
+
101
+ // get user dashboard data
102
+ $userdata = get_user_meta( $user_id, 'pvc_dashboard', true );
103
+
104
+ // empty userdata?
105
+ if ( ! is_array( $userdata ) || empty( $userdata ) )
106
+ $userdata = array();
107
+
108
+ // empty post types?
109
+ if ( ! array_key_exists( 'post_types', $userdata ) || ! is_array( $userdata['post_types'] ) )
110
+ $userdata['post_types'] = array();
111
+
112
+ // hide post type?
113
+ if ( $_POST['hidden'] === 'true' ) {
114
+ if ( ! in_array( $_POST['post_type'], $userdata['post_types'], true ) )
115
+ $userdata['post_types'][] = $_POST['post_type'];
116
+ } else {
117
+ if ( ( $key = array_search( $_POST['post_type'], $userdata['post_types'] ) ) !== false )
118
+ unset( $userdata['post_types'][$key] );
119
+ }
120
+
121
+ // update userdata
122
+ update_user_meta( $user_id, 'pvc_dashboard', $userdata );
123
+ }
124
+
125
+ exit;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  }
127
 
128
  /**
131
  * @global $_wp_admin_css_colors
132
  * @return void
133
  */
134
+ public function dashboard_widget_chart() {
135
  if ( ! apply_filters( 'pvc_user_can_see_stats', current_user_can( 'publish_posts' ) ) )
136
  wp_die( _( 'You do not have permission to access this page.', 'post-views-counter' ) );
137
 
138
+ if ( ! check_ajax_referer( 'dashboard-chart', 'nonce' ) )
139
  wp_die( __( 'You do not have permission to access this page.', 'post-views-counter' ) );
140
 
141
  // get period
157
  // $now = getdate( current_time( 'timestamp', get_option( 'gmt_offset' ) ) );
158
  $now = getdate( current_time( 'timestamp', get_option( 'gmt_offset' ) ) - 2592000 );
159
 
160
+ // get admin color scheme
161
  global $_wp_admin_css_colors;
162
 
163
+ $admin_color = get_user_option( 'admin_color' );
164
+ $colors = $_wp_admin_css_colors[$admin_color]->colors;
165
+ $color = $this->hex2rgb( $colors[2] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
  // set chart labels
168
  switch ( $period ) {
213
  foreach ( $post_types as $id => $post_type ) {
214
  $post_type_obj = get_post_type_object( $post_type );
215
 
216
+ $data['data']['datasets'][$id]['label'] = $post_type_obj->labels->name;
217
+ $data['data']['datasets'][$id]['post_type'] = $post_type_obj->name;
 
 
 
 
 
 
218
  $data['data']['datasets'][$id]['data'] = array();
219
 
220
  // get month views
259
 
260
  case 'this_month':
261
  default:
262
+ $userdata = $this->get_dashboard_user_data( get_current_user_id(), 'post_types' );
263
 
264
  if ( $period !== 'this_month' ) {
265
  $date = explode( '|', $period, 2 );
290
 
291
  $data['data']['datasets'][0]['label'] = __( 'Total Views', 'post-views-counter' );
292
  $data['data']['datasets'][0]['post_type'] = '_pvc_total_views';
293
+ $data['data']['datasets'][0]['hidden'] = in_array( '_pvc_total_views', $userdata, true );
294
 
295
  // reindex post types
296
  $post_types = array_combine( range( 1, count( $post_types ) ), array_values( $post_types ) );
300
  foreach ( $post_types as $id => $post_type ) {
301
  $post_type_obj = get_post_type_object( $post_type );
302
 
303
+ $data['data']['datasets'][$id]['label'] = $post_type_obj->labels->name;
304
+ $data['data']['datasets'][$id]['post_type'] = $post_type_obj->name;
305
+ $data['data']['datasets'][$id]['hidden'] = in_array( $post_type_obj->name, $userdata, true );
 
 
 
 
 
 
306
  $data['data']['datasets'][$id]['data'] = array();
307
 
308
  // get month views
350
 
351
  exit;
352
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
  /**
355
+ * Get user dashboard data.
356
  *
357
+ * @param string $data_type
358
+ * @return array
359
  */
360
+ public function get_dashboard_user_data( $user_id, $data_type ) {
361
+ $userdata = get_user_meta( $user_id, 'pvc_dashboard', true );
 
 
 
 
362
 
363
+ if ( ! is_array( $userdata ) || empty( $userdata ) )
364
+ $userdata = array();
365
 
366
+ if ( ! array_key_exists( $data_type, $userdata ) || ! is_array( $userdata[$data_type] ) )
367
+ $userdata[$data_type] = array();
 
 
 
 
 
 
 
 
368
 
369
+ return $userdata[$data_type];
370
  }
371
 
372
  /**
373
+ * Enqueue admin scripts and styles.
374
  *
375
+ * @param string $pagenow
376
  */
377
+ public function admin_scripts_styles( $pagenow ) {
378
+ if ( $pagenow != 'index.php' )
379
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
 
381
+ // filter user_can_see_stats
382
+ if ( ! apply_filters( 'pvc_user_can_see_stats', current_user_can( 'publish_posts' ) ) )
383
+ return;
384
 
385
+ wp_register_style( 'pvc-admin-dashboard', POST_VIEWS_COUNTER_URL . '/css/admin-dashboard.css' );
386
+ wp_enqueue_style( 'pvc-admin-dashboard' );
387
+ wp_enqueue_style( 'pvc-chart-css', POST_VIEWS_COUNTER_URL . '/assets/chartjs/chart.min.css' );
 
 
 
 
 
388
 
389
+ wp_register_script( 'pvc-chart', POST_VIEWS_COUNTER_URL . '/assets/chartjs/chart.min.js', array( 'jquery' ), Post_Views_Counter()->defaults['version'], true );
390
+ wp_register_script( 'pvc-admin-dashboard', POST_VIEWS_COUNTER_URL . '/js/admin-dashboard.js', array( 'jquery', 'pvc-chart' ), Post_Views_Counter()->defaults['version'], true );
391
 
392
+ wp_enqueue_script( 'pvc-admin-dashboard' );
 
393
 
394
+ wp_localize_script(
395
+ 'pvc-admin-dashboard',
396
+ 'pvcArgs',
397
+ array(
398
+ 'ajaxURL' => admin_url( 'admin-ajax.php' ),
399
+ 'nonce' => wp_create_nonce( 'dashboard-chart' ),
400
+ 'nonceUser' => wp_create_nonce( 'dashboard-chart-user-post-types' )
401
+ )
402
+ );
403
  }
404
 
405
  /**
includes/functions.php CHANGED
@@ -63,13 +63,13 @@ if ( ! function_exists( 'pvc_get_post_views' ) ) {
63
  */
64
  if ( ! function_exists( 'pvc_get_views' ) ) {
65
 
66
- function pvc_get_views( $args = array() ) {
67
- $range = array();
68
- $defaults = array(
69
  'fields' => 'views',
70
  'post_id' => '',
71
  'post_type' => '',
72
- 'views_query' => array(
73
  'year' => '',
74
  'month' => '',
75
  'week' => '',
@@ -77,8 +77,8 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
77
  'after' => '', // string or array
78
  'before' => '', // string or array
79
  'inclusive' => true
80
- )
81
- );
82
 
83
  // merge default options with new arguments
84
  $args = array_merge( $defaults, $args );
@@ -94,7 +94,7 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
94
 
95
  // check post types
96
  if ( is_array( $args['post_type'] ) && ! empty( $args['post_type'] ) ) {
97
- $post_types = array();
98
 
99
  foreach( $args['post_type'] as $post_type ) {
100
  $post_types[] = "'" . $post_type . "'";
@@ -113,22 +113,22 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
113
  $args['post_id'] = (int) $args['post_id'];
114
 
115
  // check fields
116
- if ( ! in_array( $args['fields'], array( 'views', 'date=>views' ), true ) )
117
  $args['fields'] = $defaults['fields'];
118
 
119
- $query_chunks = array();
120
  $views_query = '';
121
 
122
  // views query after/before parameters work only when fields == views
123
  if ( $args['fields'] === 'views' ) {
124
  // check views query inclusive
125
- if ( ! isset( $args['views_query']['inclusive'] ) ) {
126
  $args['views_query']['inclusive'] = $defaults['views_query']['inclusive'];
127
- } else
128
  $args['views_query']['inclusive'] = (bool) $args['views_query']['inclusive'];
129
 
130
  // check after and before dates
131
- foreach ( array( 'after' => '>', 'before' => '<' ) as $date => $type ) {
132
  $year_ = null;
133
  $month_ = null;
134
  $week_ = null;
@@ -166,13 +166,13 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
166
 
167
  // valid date?
168
  if ( ! ( $year_ === null && $month_ === null && $week_ === null && $day_ === null ) ) {
169
- $query_chunks[] = array(
170
  'year' => $year_,
171
  'month' => $month_,
172
  'day' => $day_,
173
  'week' => $week_,
174
  'type' => $type . ( $args['views_query']['inclusive'] ? '=' : '' )
175
- );
176
  }
177
  }
178
  }
@@ -183,7 +183,7 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
183
  // after and before?
184
  if ( count( $query_chunks ) === 2 ) {
185
  // before and after dates should be the same
186
- foreach ( array( 'year', 'month', 'day', 'week' ) as $date_type ) {
187
  if ( ! ( ( $query_chunks[0][$date_type] !== null && $query_chunks[1][$date_type] !== null ) || ( $query_chunks[0][$date_type] === null && $query_chunks[1][$date_type] === null ) ) )
188
  $valid_dates = false;
189
  }
@@ -374,10 +374,10 @@ if ( ! function_exists( 'pvc_get_views' ) ) {
374
 
375
  /**
376
  * Display post views for a given post.
377
- *
378
- * @param int|array $post_id
379
  * @param bool $display
380
- * @return mixed
381
  */
382
  if ( ! function_exists( 'pvc_post_views' ) ) {
383
 
@@ -428,13 +428,14 @@ if ( ! function_exists( 'pvc_post_views' ) ) {
428
  */
429
  if ( ! function_exists( 'pvc_get_most_viewed_posts' ) ) {
430
 
431
- function pvc_get_most_viewed_posts( $args = array() ) {
432
  $args = array_merge(
433
- array(
434
  'posts_per_page' => 10,
435
  'order' => 'desc',
436
  'post_type' => 'post'
437
- ), $args
 
438
  );
439
 
440
  $args = apply_filters( 'pvc_get_most_viewed_posts_args', $args );
@@ -462,10 +463,10 @@ if ( ! function_exists( 'pvc_get_most_viewed_posts' ) ) {
462
  */
463
  if ( ! function_exists( 'pvc_most_viewed_posts' ) ) {
464
 
465
- function pvc_most_viewed_posts( $args = array(), $display = true ) {
466
- $defaults = array(
467
  'number_of_posts' => 5,
468
- 'post_type' => array( 'post' ),
469
  'order' => 'desc',
470
  'thumbnail_size' => 'thumbnail',
471
  'list_type' => 'unordered',
@@ -476,7 +477,7 @@ if ( ! function_exists( 'pvc_most_viewed_posts' ) ) {
476
  'no_posts_message' => __( 'No Posts', 'post-views-counter' ),
477
  'item_before' => '',
478
  'item_after' => ''
479
- );
480
 
481
  $args = apply_filters( 'pvc_most_viewed_posts_args', wp_parse_args( $args, $defaults ) );
482
 
@@ -486,11 +487,11 @@ if ( ! function_exists( 'pvc_most_viewed_posts' ) ) {
486
  $args['show_post_excerpt'] = (bool) $args['show_post_excerpt'];
487
 
488
  $posts = pvc_get_most_viewed_posts(
489
- array(
490
  'posts_per_page' => ( isset( $args['number_of_posts'] ) ? (int) $args['number_of_posts'] : $defaults['number_of_posts'] ),
491
  'order' => ( isset( $args['order'] ) ? $args['order'] : $defaults['order'] ),
492
  'post_type' => ( isset( $args['post_type'] ) ? $args['post_type'] : $defaults['post_type'] )
493
- )
494
  );
495
 
496
  if ( ! empty( $posts ) ) {
@@ -559,7 +560,7 @@ if ( ! function_exists( 'pvc_most_viewed_posts' ) ) {
559
  * @global $wpdb
560
  * @param int $post_id Post ID
561
  * @param int $post_views Number of post views
562
- * @return true|string True on success, error string otherwise
563
  */
564
  function pvc_update_post_views( $post_id = 0, $post_views = 0 ) {
565
  // cast post ID
@@ -578,11 +579,20 @@ function pvc_update_post_views( $post_id = 0, $post_views = 0 ) {
578
 
579
  global $wpdb;
580
 
581
- // chnage post views?
582
  $post_views = apply_filters( 'pvc_update_post_views_count', $post_views, $post_id );
583
 
584
- // insert or update db post views count
585
- $wpdb->query( $wpdb->prepare( "INSERT INTO " . $wpdb->prefix . "post_views (id, type, period, count) VALUES (%d, %d, %s, %d) ON DUPLICATE KEY UPDATE count = %d", $post_id, 4, 'total', $post_views, $post_views ) );
 
 
 
 
 
 
 
 
 
586
 
587
  // query fails only if it returns false
588
  return apply_filters( 'pvc_update_post_views', $post_id );
@@ -604,4 +614,4 @@ function pvc_view_post( $post_id = 0 ) {
604
  Post_Views_Counter()->counter->check_post( $post_id );
605
 
606
  return true;
607
- }
63
  */
64
  if ( ! function_exists( 'pvc_get_views' ) ) {
65
 
66
+ function pvc_get_views( $args = [] ) {
67
+ $range = [];
68
+ $defaults = [
69
  'fields' => 'views',
70
  'post_id' => '',
71
  'post_type' => '',
72
+ 'views_query' => [
73
  'year' => '',
74
  'month' => '',
75
  'week' => '',
77
  'after' => '', // string or array
78
  'before' => '', // string or array
79
  'inclusive' => true
80
+ ]
81
+ ];
82
 
83
  // merge default options with new arguments
84
  $args = array_merge( $defaults, $args );
94
 
95
  // check post types
96
  if ( is_array( $args['post_type'] ) && ! empty( $args['post_type'] ) ) {
97
+ $post_types = [];
98
 
99
  foreach( $args['post_type'] as $post_type ) {
100
  $post_types[] = "'" . $post_type . "'";
113
  $args['post_id'] = (int) $args['post_id'];
114
 
115
  // check fields
116
+ if ( ! in_array( $args['fields'], [ 'views', 'date=>views' ], true ) )
117
  $args['fields'] = $defaults['fields'];
118
 
119
+ $query_chunks = [];
120
  $views_query = '';
121
 
122
  // views query after/before parameters work only when fields == views
123
  if ( $args['fields'] === 'views' ) {
124
  // check views query inclusive
125
+ if ( ! isset( $args['views_query']['inclusive'] ) )
126
  $args['views_query']['inclusive'] = $defaults['views_query']['inclusive'];
127
+ else
128
  $args['views_query']['inclusive'] = (bool) $args['views_query']['inclusive'];
129
 
130
  // check after and before dates
131
+ foreach ( [ 'after' => '>', 'before' => '<' ] as $date => $type ) {
132
  $year_ = null;
133
  $month_ = null;
134
  $week_ = null;
166
 
167
  // valid date?
168
  if ( ! ( $year_ === null && $month_ === null && $week_ === null && $day_ === null ) ) {
169
+ $query_chunks[] = [
170
  'year' => $year_,
171
  'month' => $month_,
172
  'day' => $day_,
173
  'week' => $week_,
174
  'type' => $type . ( $args['views_query']['inclusive'] ? '=' : '' )
175
+ ];
176
  }
177
  }
178
  }
183
  // after and before?
184
  if ( count( $query_chunks ) === 2 ) {
185
  // before and after dates should be the same
186
+ foreach ( [ 'year', 'month', 'day', 'week' ] as $date_type ) {
187
  if ( ! ( ( $query_chunks[0][$date_type] !== null && $query_chunks[1][$date_type] !== null ) || ( $query_chunks[0][$date_type] === null && $query_chunks[1][$date_type] === null ) ) )
188
  $valid_dates = false;
189
  }
374
 
375
  /**
376
  * Display post views for a given post.
377
+ *
378
+ * @param int|array $post_id
379
  * @param bool $display
380
+ * @return string|void
381
  */
382
  if ( ! function_exists( 'pvc_post_views' ) ) {
383
 
428
  */
429
  if ( ! function_exists( 'pvc_get_most_viewed_posts' ) ) {
430
 
431
+ function pvc_get_most_viewed_posts( $args = [] ) {
432
  $args = array_merge(
433
+ [
434
  'posts_per_page' => 10,
435
  'order' => 'desc',
436
  'post_type' => 'post'
437
+ ],
438
+ $args
439
  );
440
 
441
  $args = apply_filters( 'pvc_get_most_viewed_posts_args', $args );
463
  */
464
  if ( ! function_exists( 'pvc_most_viewed_posts' ) ) {
465
 
466
+ function pvc_most_viewed_posts( $args = [], $display = true ) {
467
+ $defaults = [
468
  'number_of_posts' => 5,
469
+ 'post_type' => [ 'post' ],
470
  'order' => 'desc',
471
  'thumbnail_size' => 'thumbnail',
472
  'list_type' => 'unordered',
477
  'no_posts_message' => __( 'No Posts', 'post-views-counter' ),
478
  'item_before' => '',
479
  'item_after' => ''
480
+ ];
481
 
482
  $args = apply_filters( 'pvc_most_viewed_posts_args', wp_parse_args( $args, $defaults ) );
483
 
487
  $args['show_post_excerpt'] = (bool) $args['show_post_excerpt'];
488
 
489
  $posts = pvc_get_most_viewed_posts(
490
+ [
491
  'posts_per_page' => ( isset( $args['number_of_posts'] ) ? (int) $args['number_of_posts'] : $defaults['number_of_posts'] ),
492
  'order' => ( isset( $args['order'] ) ? $args['order'] : $defaults['order'] ),
493
  'post_type' => ( isset( $args['post_type'] ) ? $args['post_type'] : $defaults['post_type'] )
494
+ ]
495
  );
496
 
497
  if ( ! empty( $posts ) ) {
560
  * @global $wpdb
561
  * @param int $post_id Post ID
562
  * @param int $post_views Number of post views
563
+ * @return true|int
564
  */
565
  function pvc_update_post_views( $post_id = 0, $post_views = 0 ) {
566
  // cast post ID
579
 
580
  global $wpdb;
581
 
582
+ // change post views?
583
  $post_views = apply_filters( 'pvc_update_post_views_count', $post_views, $post_id );
584
 
585
+ // insert or update database post views count
586
+ $wpdb->query(
587
+ $wpdb->prepare(
588
+ "INSERT INTO " . $wpdb->prefix . "post_views (id, type, period, count) VALUES (%d, %d, %s, %d) ON DUPLICATE KEY UPDATE count = %d",
589
+ $post_id,
590
+ 4,
591
+ 'total',
592
+ $post_views,
593
+ $post_views
594
+ )
595
+ );
596
 
597
  // query fails only if it returns false
598
  return apply_filters( 'pvc_update_post_views', $post_id );
614
  Post_Views_Counter()->counter->check_post( $post_id );
615
 
616
  return true;
617
+ }
includes/settings.php CHANGED
@@ -10,78 +10,112 @@ if ( ! defined( 'ABSPATH' ) )
10
  */
11
  class Post_Views_Counter_Settings {
12
 
 
 
 
13
  private $time_types;
14
  private $groups;
15
  private $user_roles;
 
 
16
  public $post_types;
17
  public $page_types;
18
 
19
  public function __construct() {
20
  // actions
21
- add_action( 'after_setup_theme', [ $this, 'load_defaults' ] );
22
- add_action( 'wp_loaded', [ $this, 'load_post_types' ] );
23
-
24
- // filters
25
- add_filter( 'post_views_counter_settings_data', [ $this, 'settings_data' ] );
26
- add_filter( 'post_views_counter_settings_pages', [ $this, 'settings_page' ] );
27
  }
28
 
29
  /**
30
  * Load default settings.
31
- *
32
- * @return void
33
  */
34
  public function load_defaults() {
35
  if ( ! is_admin() )
36
  return;
37
 
38
- $this->time_types = [
39
- 'minutes' => __( 'minutes', 'post-views-counter' ),
40
- 'hours' => __( 'hours', 'post-views-counter' ),
41
- 'days' => __( 'days', 'post-views-counter' ),
42
- 'weeks' => __( 'weeks', 'post-views-counter' ),
43
- 'months' => __( 'months', 'post-views-counter' ),
44
- 'years' => __( 'years', 'post-views-counter' )
45
- ];
46
-
47
- $this->groups = [
48
- 'robots' => __( 'robots', 'post-views-counter' ),
49
- 'users' => __( 'logged in users', 'post-views-counter' ),
50
- 'guests' => __( 'guests', 'post-views-counter' ),
51
- 'roles' => __( 'selected user roles', 'post-views-counter' )
52
- ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  $this->user_roles = $this->get_user_roles();
55
 
56
  $this->page_types = apply_filters(
57
  'pvc_page_types_display_options',
58
- [
59
- 'home' => __( 'Home', 'post-views-counter' ),
60
- 'archive' => __( 'Archives', 'post-views-counter' ),
61
- 'singular' => __( 'Single pages', 'post-views-counter' ),
62
- 'search' => __( 'Search results', 'post-views-counter' ),
63
- ]
64
  );
65
  }
66
 
67
  /**
68
  * Get post types avaiable for counting.
69
- *
70
- * @return void
71
  */
72
  public function load_post_types() {
73
  if ( ! is_admin() )
74
  return;
75
 
76
- $post_types = [];
77
 
78
  // built in public post types
79
- foreach ( get_post_types( [ '_builtin' => true, 'public' => true ], 'objects', 'and' ) as $key => $post_type ) {
80
  $post_types[$key] = $post_type->labels->name;
81
  }
82
 
83
  // public custom post types
84
- foreach ( get_post_types( [ '_builtin' => false, 'public' => true ], 'objects', 'and' ) as $key => $post_type ) {
85
  $post_types[$key] = $post_type->labels->name;
86
  }
87
 
@@ -89,7 +123,7 @@ class Post_Views_Counter_Settings {
89
  if ( class_exists( 'bbPress' ) && isset( $post_types['reply'] ) )
90
  unset( $post_types['reply'] );
91
 
92
- $post_types = apply_filters( 'pvc_available_post_types', $post_types );
93
 
94
  // sort post types alphabetically with their keys
95
  asort( $post_types, SORT_STRING );
@@ -106,7 +140,7 @@ class Post_Views_Counter_Settings {
106
  public function get_user_roles() {
107
  global $wp_roles;
108
 
109
- $roles = [];
110
 
111
  foreach ( apply_filters( 'editable_roles', $wp_roles->roles ) as $role => $details ) {
112
  $roles[$role] = translate_user_role( $details['name'] );
@@ -118,799 +152,664 @@ class Post_Views_Counter_Settings {
118
  }
119
 
120
  /**
121
- * Add settings data.
122
  *
123
- * @param array $settings
124
- * @return array
125
- */
126
- public function settings_data( $settings ) {
127
- $modes = [
128
- 'php' => __( 'PHP', 'post-views-counter' ),
129
- 'js' => __( 'JavaScript', 'post-views-counter' ),
130
- 'ajax' => __( 'Fast AJAX', 'post-views-counter' )
131
- ];
132
-
133
- // WordPress 4.4+?
134
- if ( function_exists( 'register_rest_route' ) )
135
- $modes['rest_api'] = __( 'REST API', 'post-views-counter' );
136
-
137
- $settings['post-views-counter'] = [
138
- 'label' => __( 'Post Views Counter Settings', 'post-views-counter' ),
139
- 'option_name' => [
140
- 'general' => 'post_views_counter_settings_general',
141
- 'display' => 'post_views_counter_settings_display'
142
- ],
143
- 'validate' => [ $this, 'validate_settings' ],
144
- 'sections' => [
145
- 'post_views_counter_general_settings' => [],
146
- 'post_views_counter_display_settings' => []
147
- ],
148
- 'fields' => [
149
- 'post_types_count' => [
150
- 'tab' => 'general',
151
- 'title' => __( 'Post Types Count', 'post-views-counter' ),
152
- 'section' => 'post_views_counter_general_settings',
153
- 'type' => 'checkbox',
154
- 'display_type' => 'horizontal',
155
- 'description' => __( 'Select post types for which post views will be counted.', 'post-views-counter' ),
156
- 'options' => $this->post_types
157
- ],
158
- 'counter_mode' => [
159
- 'tab' => 'general',
160
- 'title' => __( 'Counter Mode', 'post-views-counter' ),
161
- 'section' => 'post_views_counter_general_settings',
162
- 'type' => 'radio',
163
- 'description' => __( 'Select the method of collecting post views data. If you are using any of the caching plugins select Javascript or REST API (if available).', 'post-views-counter' ) . '<br />' . __( 'Optionally try the Fast AJAX experimental method, usually 10+ times faster than Javascript or REST API.', 'post-views-counter' ),
164
- 'options' => $modes
165
- ],
166
- 'post_views_column' => [
167
- 'tab' => 'general',
168
- 'title' => __( 'Post Views Column', 'post-views-counter' ),
169
- 'section' => 'post_views_counter_general_settings',
170
- 'type' => 'boolean',
171
- 'description' => '',
172
- 'label' => __( 'Enable to display post views count column for each of the selected post types.', 'post-views-counter' )
173
- ],
174
- 'restrict_edit_views' => [
175
- 'tab' => 'general',
176
- 'title' => __( 'Restrict Edit', 'post-views-counter' ),
177
- 'section' => 'post_views_counter_general_settings',
178
- 'type' => 'boolean',
179
- 'description' => '',
180
- 'label' => __( 'Enable to restrict post views editing to admins only.', 'post-views-counter' )
181
- ],
182
- 'time_between_counts' => [
183
- 'tab' => 'general',
184
- 'title' => __( 'Count Interval', 'post-views-counter' ),
185
- 'section' => 'post_views_counter_general_settings',
186
- 'type' => 'custom',
187
- 'description' => '',
188
- 'min' => 0,
189
- 'max' => 999999,
190
- 'callback' => [ $this, 'setting_time_between_counts' ],
191
- 'validate' => [ $this, 'validate_time_between_counts' ]
192
- ],
193
- 'reset_counts' => [
194
- 'tab' => 'general',
195
- 'title' => __( 'Reset Data Interval', 'post-views-counter' ),
196
- 'section' => 'post_views_counter_general_settings',
197
- 'type' => 'custom',
198
- 'description' => '',
199
- 'min' => 0,
200
- 'max' => 999999,
201
- 'callback' => [ $this, 'setting_reset_counts' ],
202
- 'validate' => [ $this, 'validate_reset_counts' ]
203
- ],
204
- 'flush_interval' => [
205
- 'tab' => 'general',
206
- 'title' => __( 'Flush Object Cache Interval', 'post-views-counter' ),
207
- 'section' => 'post_views_counter_general_settings',
208
- 'type' => 'custom',
209
- 'description' => '',
210
- 'min' => 0,
211
- 'max' => 999999,
212
- 'callback' => [ $this, 'setting_flush_interval' ],
213
- 'validate' => [ $this, 'validate_flush_interval' ]
214
- ],
215
- 'exclude' => [
216
- 'tab' => 'general',
217
- 'title' => __( 'Exclude Visitors', 'post-views-counter' ),
218
- 'section' => 'post_views_counter_general_settings',
219
- 'type' => 'custom',
220
- 'description' => '',
221
- 'callback' => [ $this, 'setting_exclude' ],
222
- 'validate' => [ $this, 'validate_exclude' ]
223
- ],
224
- 'exclude_ips' => [
225
- 'tab' => 'general',
226
- 'title' => __( 'Exclude IPs', 'post-views-counter' ),
227
- 'section' => 'post_views_counter_general_settings',
228
- 'type' => 'custom',
229
- 'description' => '',
230
- 'callback' => [ $this, 'setting_exclude_ips' ],
231
- 'validate' => [ $this, 'validate_exclude_ips' ]
232
- ],
233
- 'strict_counts' => [
234
- 'tab' => 'general',
235
- 'title' => __( 'Strict counts', 'post-views-counter' ),
236
- 'section' => 'post_views_counter_general_settings',
237
- 'type' => 'boolean',
238
- 'description' => '',
239
- 'label' => __( 'Enable to prevent bypassing the counts interval (for e.g. using incognito browser window or by clearing cookies).', 'post-views-counter' )
240
- ],
241
- 'wp_postviews' => [
242
- 'tab' => 'general',
243
- 'title' => __( 'Tools', 'post-views-counter' ),
244
- 'section' => 'post_views_counter_general_settings',
245
- 'type' => 'custom',
246
- 'description' => '',
247
- 'skip_saving' => true,
248
- 'callback' => [ $this, 'setting_wp_postviews' ]
249
- ],
250
- 'deactivation_delete' => [
251
- 'tab' => 'general',
252
- 'title' => __( 'Deactivation', 'post-views-counter' ),
253
- 'section' => 'post_views_counter_general_settings',
254
- 'type' => 'boolean',
255
- 'description' => '',
256
- 'label' => __( 'Enable to delete all plugin data on deactivation.', 'post-views-counter' )
257
- ],
258
- 'label' => [
259
- 'tab' => 'display',
260
- 'title' => __( 'Post Views Label', 'post-views-counter' ),
261
- 'section' => 'post_views_counter_display_settings',
262
- 'type' => 'input',
263
- 'description' => __( 'Enter the label for the post views counter field.', 'post-views-counter' ),
264
- 'subclass' => 'regular-text',
265
- 'validate' => [ $this, 'validate_label' ],
266
- 'reset' => [ $this, 'reset_label' ]
267
- ],
268
- 'post_types_display' => [
269
- 'tab' => 'display',
270
- 'title' => __( 'Post Type', 'post-views-counter' ),
271
- 'section' => 'post_views_counter_display_settings',
272
- 'type' => 'checkbox',
273
- 'display_type' => 'horizontal',
274
- 'description' => __( 'Select post types for which the views count will be displayed.', 'post-views-counter' ),
275
- 'options' => $this->post_types
276
- ],
277
- 'page_types_display' => [
278
- 'tab' => 'display',
279
- 'title' => __( 'Page Type', 'post-views-counter' ),
280
- 'section' => 'post_views_counter_display_settings',
281
- 'type' => 'checkbox',
282
- 'display_type' => 'horizontal',
283
- 'description' => __( 'Select page types where the views count will be displayed.', 'post-views-counter' ),
284
- 'options' => $this->page_types
285
- ],
286
- 'restrict_display' => [
287
- 'tab' => 'display',
288
- 'title' => __( 'User Type', 'post-views-counter' ),
289
- 'section' => 'post_views_counter_display_settings',
290
- 'type' => 'custom',
291
- 'description' => '',
292
- 'callback' => [ $this, 'setting_restrict_display' ],
293
- 'validate' => [ $this, 'validate_restrict_display' ]
294
- ],
295
- 'position' => [
296
- 'tab' => 'display',
297
- 'title' => __( 'Position', 'post-views-counter' ),
298
- 'section' => 'post_views_counter_display_settings',
299
- 'type' => 'select',
300
- 'description' => __( 'Select where would you like to display the post views counter. Use [post-views] shortcode for manual display.', 'post-views-counter' ),
301
- 'options' => [
302
- 'before' => __( 'before the content', 'post-views-counter' ),
303
- 'after' => __( 'after the content', 'post-views-counter' ),
304
- 'manual' => __( 'manual', 'post-views-counter' )
305
- ]
306
- ],
307
- 'display_style' => [
308
- 'tab' => 'display',
309
- 'title' => __( 'Display Style', 'post-views-counter' ),
310
- 'section' => 'post_views_counter_display_settings',
311
- 'type' => 'custom',
312
- 'description' => __( 'Choose how to display the post views counter.', 'post-views-counter' ),
313
- 'callback' => [ $this, 'setting_display_style' ],
314
- 'validate' => [ $this, 'validate_display_style' ],
315
- 'options' => [
316
- 'icon' => __( 'icon', 'post-views-counter' ),
317
- 'text' => __( 'label', 'post-views-counter' )
318
- ]
319
- ],
320
- 'icon_class' => [
321
- 'tab' => 'display',
322
- 'title' => __( 'Icon Class', 'post-views-counter' ),
323
- 'section' => 'post_views_counter_display_settings',
324
- 'type' => 'input',
325
- 'description' => sprintf( __( 'Enter the post views icon class. Any of the <a href="%s" target="_blank">Dashicons</a> classes are available.', 'post-views-counter' ), 'https://developer.wordpress.org/resource/dashicons/' ),
326
- 'subclass' => 'regular-text'
327
- ],
328
- 'toolbar_statistics' => [
329
- 'tab' => 'display',
330
- 'title' => __( 'Toolbar Chart', 'post-views-counter' ),
331
- 'section' => 'post_views_counter_display_settings',
332
- 'type' => 'boolean',
333
- 'description' => __( 'The post views chart will be displayed for the post types that are being counted.', 'post-views-counter' ),
334
- 'label' => __( 'Enable to display the post views chart at the toolbar.', 'post-views-counter' )
335
- ]
336
- ]
337
- ];
338
-
339
- return $settings;
340
- }
341
-
342
- /**
343
- * Add settings page.
344
- *
345
- * @param array $pages
346
- * @return array
347
  */
348
- public function settings_page( $pages ) {
349
- $pages['post-views-counter'] = [
350
- 'type' => 'settings_page',
351
- 'menu_slug' => 'post-views-counter',
352
- 'page_title' => __( 'Post Views Counter Settings', 'post-views-counter' ),
353
- 'menu_title' => __( 'Post Views Counter', 'post-views-counter' ),
354
- 'capability' => apply_filters( 'pvc_settings_capability', 'manage_options' ),
355
- 'callback' => null,
356
- 'tabs' => [
357
- 'general' => [
358
- 'label' => __( 'General', 'post-views-counter' ),
359
- 'option_name' => 'post_views_counter_settings_general'
360
- ],
361
- 'display' => [
362
- 'label' => __( 'Display', 'post-views-counter' ),
363
- 'option_name' => 'post_views_counter_settings_display'
364
- ]
365
- ]
366
- ];
367
-
368
- return $pages;
369
  }
370
 
371
  /**
372
- * Validate options.
373
  *
374
- * @param array $input Settings data
375
- * @return array
376
  */
377
- public function validate_settings( $input ) {
378
- // check capability
379
- if ( ! current_user_can( 'manage_options' ) )
380
- return $input;
381
 
382
- // get main instance
383
- $pvc = Post_Views_Counter();
 
 
384
 
385
- // use internal settings API to validate settings first
386
- $input = $pvc->settings_api->validate_settings( $input );
387
-
388
- // import post views data from another plugin
389
- if ( isset( $_POST['post_views_counter_import_wp_postviews'] ) ) {
390
- // make sure we do not change anything in the settings
391
- $input = $pvc->options['general'];
392
-
393
- global $wpdb;
394
 
395
- // get views key
396
- $meta_key = esc_attr( apply_filters( 'pvc_import_meta_key', 'views' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
 
398
- // get views
399
- $views = $wpdb->get_results( "SELECT post_id, meta_value FROM " . $wpdb->postmeta . " WHERE meta_key = '" . $meta_key . "'", ARRAY_A, 0 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
- // any views?
402
- if ( ! empty( $views ) ) {
403
- $sql = [];
 
 
 
 
 
 
 
 
 
404
 
405
- foreach ( $views as $view ) {
406
- $sql[] = "(" . $view['post_id'] . ", 4, 'total', " . ( (int) $view['meta_value'] ) . ")";
407
- }
 
 
 
408
 
409
- $wpdb->query( "INSERT INTO " . $wpdb->prefix . "post_views(id, type, period, count) VALUES " . implode( ',', $sql ) . " ON DUPLICATE KEY UPDATE count = " . ( isset( $_POST['post_views_counter_import_wp_postviews_override'] ) ? '' : 'count + ' ) . "VALUES(count)" );
 
 
 
410
 
411
- add_settings_error( 'wp_postviews_import', 'wp_postviews_import', __( 'Post views data imported succesfully.', 'post-views-counter' ), 'updated' );
412
- } else
413
- add_settings_error( 'wp_postviews_import', 'wp_postviews_import', __( 'There was no post views data to import.', 'post-views-counter' ), 'updated' );
414
- // delete all post views data
415
- } elseif ( isset( $_POST['post_views_counter_reset_views'] ) ) {
416
- // make sure we do not change anything in the settings
417
- $input = $pvc->options['general'];
418
 
419
- global $wpdb;
 
 
 
 
 
420
 
421
- if ( $wpdb->query( 'TRUNCATE TABLE ' . $wpdb->prefix . 'post_views' ) )
422
- add_settings_error( 'reset_post_views', 'reset_post_views', __( 'All existing data deleted succesfully.', 'post-views-counter' ), 'updated' );
423
- else
424
- add_settings_error( 'reset_post_views', 'reset_post_views', __( 'Error occurred. All existing data were not deleted.', 'post-views-counter' ), 'error' );
425
- // save general settings
426
- } elseif ( isset( $_POST['save_post_views_counter_settings_general'] ) ) {
427
- $input['update_version'] = $pvc->options['general']['update_version'];
428
- $input['update_notice'] = $pvc->options['general']['update_notice'];
429
- $input['update_delay_date'] = $pvc->options['general']['update_delay_date'];
430
- // reset general settings
431
- } elseif ( isset( $_POST['reset_post_views_counter_settings_general'] ) ) {
432
- $input['update_version'] = $pvc->options['general']['update_version'];
433
- $input['update_notice'] = $pvc->options['general']['update_notice'];
434
- $input['update_delay_date'] = $pvc->options['general']['update_delay_date'];
435
  }
436
 
437
- return $input;
 
 
438
  }
439
 
440
  /**
441
- * Validate label.
442
- *
443
- * @param array $input Input POST data
444
- * @param array $field Field options
445
- * @return array
446
  */
447
- public function validate_label( $input, $field ) {
448
- // get main instance
449
- $pvc = Post_Views_Counter();
450
 
451
- if ( ! isset( $input ) )
452
- $input = $pvc->defaults['display']['label'];
453
 
454
- // use internal settings API to validate settings first
455
- $input = $pvc->settings_api->validate_field( $input, 'input', $field );
456
-
457
- if ( function_exists( 'icl_register_string' ) )
458
- icl_register_string( 'Post Views Counter', 'Post Views Label', $input );
459
 
460
- return $input;
 
 
461
  }
462
 
463
  /**
464
- * Restore post views label to default value.
465
- *
466
- * @param array $default Default value
467
- * @param array $field Field options
468
- * @return array
469
  */
470
- public function reset_label( $default, $field ) {
471
- if ( function_exists( 'icl_register_string' ) )
472
- icl_register_string( 'Post Views Counter', 'Post Views Label', $default );
473
-
474
- return $default;
475
  }
476
 
477
  /**
478
- * Setting: display style.
479
- *
480
- * @param array $field Field options
481
- * @return string
482
  */
483
- public function setting_display_style( $field ) {
484
- // get main instance
485
- $pvc = Post_Views_Counter();
486
-
487
- $html = '
488
- <input type="hidden" name="post_views_counter_settings_display[display_style]" value="empty" />';
489
 
490
- foreach ( $field['options'] as $key => $label ) {
491
- $html .= '
492
- <label><input id="post_views_counter_display_display_style_' . esc_attr( $key ) . '" type="checkbox" name="post_views_counter_settings_display[display_style][]" value="' . esc_attr( $key ) . '" ' . checked( ! empty( $pvc->options['display']['display_style'][$key] ), true, false ) . ' />' . esc_html( $label ) . '</label> ';
493
  }
494
 
495
- return $html;
 
 
 
496
  }
497
 
498
  /**
499
- * Validate display style.
500
- *
501
- * @param array $input Input POST data
502
- * @param array $field Field options
503
- * @return array
504
  */
505
- public function validate_display_style( $input, $field ) {
506
- // get main instance
507
- $pvc = Post_Views_Counter();
508
-
509
- $data = [];
510
-
511
- foreach ( $field['options'] as $value => $label ) {
512
- $data[$value] = false;
513
- }
514
 
515
- // any data?
516
- if ( ! empty( $input['display_style'] && $input['display_style'] !== 'empty' && is_array( $input['display_style'] ) ) ) {
517
- foreach ( $input['display_style'] as $value ) {
518
- if ( array_key_exists( $value, $field['options'] ) )
519
- $data[$value] = true;
520
- }
521
  }
522
 
523
- $input['display_style'] = $data;
524
-
525
- return $input;
 
526
  }
527
 
528
  /**
529
- * Setting: count interval.
530
- *
531
- * @param array $field Field options
532
- * @return string
533
  */
534
- public function setting_time_between_counts( $field ) {
535
- // get main instance
536
- $pvc = Post_Views_Counter();
537
-
538
- $html = '
539
- <input size="6" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="post_views_counter_settings_general[time_between_counts][number]" value="' . esc_attr( $pvc->options['general']['time_between_counts']['number'] ) . '" />
540
- <select class="pvc-chosen-short" name="post_views_counter_settings_general[time_between_counts][type]">';
541
 
542
  foreach ( $this->time_types as $type => $type_name ) {
543
- $html .= '
544
- <option value="' . esc_attr( $type ) . '" ' . selected( $type, $pvc->options['general']['time_between_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
545
  }
546
 
547
- $html .= '
548
- </select>
549
- <p class="description">' . __( 'Enter the time between single user visit count.', 'post-views-counter' ) . '</p>';
550
-
551
- return $html;
552
  }
553
 
554
  /**
555
- * Validate count interval.
556
- *
557
- * @param array $input Input POST data
558
- * @param array $field Field options
559
- * @return array
560
  */
561
- public function validate_time_between_counts( $input, $field ) {
562
- // get main instance
563
- $pvc = Post_Views_Counter();
 
564
 
565
- // number
566
- $input['time_between_counts']['number'] = isset( $input['time_between_counts']['number'] ) ? (int) $input['time_between_counts']['number'] : $pvc->defaults['general']['time_between_counts']['number'];
 
 
567
 
568
- if ( $input['time_between_counts']['number'] < $field['min'] || $input['time_between_counts']['number'] > $field['max'] )
569
- $input['time_between_counts']['number'] = $pvc->defaults['general']['time_between_counts']['number'];
 
570
 
571
- // type
572
- $input['time_between_counts']['type'] = isset( $input['time_between_counts']['type'], $this->time_types[$input['time_between_counts']['type']] ) ? $input['time_between_counts']['type'] : $pvc->defaults['general']['time_between_counts']['type'];
 
 
573
 
574
- return $input;
 
 
 
575
  }
576
 
577
  /**
578
- * Setting: reset data interval.
579
- *
580
- * @param array $field Field options
581
- * @return string
582
  */
583
- public function setting_reset_counts( $field ) {
584
- // get main instance
585
- $pvc = Post_Views_Counter();
586
 
587
- $html = '
588
- <input size="6" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="post_views_counter_settings_general[reset_counts][number]" value="' . esc_attr( $pvc->options['general']['reset_counts']['number'] ) . '" />
589
- <select class="pvc-chosen-short" name="post_views_counter_settings_general[reset_counts][type]">';
590
 
591
- foreach ( array_slice( $this->time_types, 2, null, true ) as $type => $type_name ) {
592
- $html .= '
593
- <option value="' . esc_attr( $type ) . '" ' . selected( $type, $pvc->options['general']['reset_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
 
 
 
 
 
 
 
 
 
594
  }
595
 
596
- $html .= '
597
- </select>
598
- <p class="description">' . __( 'Delete single day post views data older than specified above. Enter 0 (number zero) if you want to preserve your data regardless of its age.', 'post-views-counter' ) . '</p>';
599
-
600
- return $html;
601
  }
602
 
603
  /**
604
- * Validate reset data interval.
605
- *
606
- * @param array $input Input POST data
607
- * @param array $field Field options
608
- * @return array
609
  */
610
- public function validate_reset_counts( $input, $field ) {
611
- // get main instance
612
- $pvc = Post_Views_Counter();
613
-
614
- // number
615
- $input['reset_counts']['number'] = isset( $input['reset_counts']['number'] ) ? (int) $input['reset_counts']['number'] : $pvc->defaults['general']['reset_counts']['number'];
616
-
617
- if ( $input['reset_counts']['number'] < $field['min'] || $input['reset_counts']['number'] > $field['max'] )
618
- $input['reset_counts']['number'] = $pvc->defaults['general']['reset_counts']['number'];
619
-
620
- // type
621
- $input['reset_counts']['type'] = isset( $input['reset_counts']['type'], $this->time_types[$input['reset_counts']['type']] ) ? $input['reset_counts']['type'] : $pvc->defaults['general']['reset_counts']['type'];
622
-
623
- // run cron on next visit?
624
- $input['cron_run'] = ( $input['reset_counts']['number'] > 0 );
625
 
626
- // cron update?
627
- $input['cron_update'] = ( $input['cron_run'] && ( $pvc->options['general']['reset_counts']['number'] !== $input['reset_counts']['number'] || $pvc->options['general']['reset_counts']['type'] !== $input['reset_counts']['type'] ) );
 
 
 
 
 
 
 
 
 
 
 
 
628
 
629
- return $input;
 
 
 
 
 
 
 
630
  }
631
 
632
  /**
633
- * Setting: flush object cache interval.
634
- *
635
- * @param array $field Field options
636
- * @return string
637
  */
638
- public function setting_flush_interval( $field ) {
639
- // get main instance
640
- $pvc = Post_Views_Counter();
 
 
 
641
 
642
- $html = '
643
- <input size="6" type="number" min="' . ( (int) $field['min'] ) . '" max="' . ( (int) $field['max'] ) . '" name="post_views_counter_settings_general[flush_interval][number]" value="' . esc_attr( $pvc->options['general']['flush_interval']['number'] ) . '" />
644
- <select class="pvc-chosen-short" name="post_views_counter_settings_general[flush_interval][type]">';
 
 
 
645
 
646
- foreach ( $this->time_types as $type => $type_name ) {
647
- $html .= '
648
- <option value="' . esc_attr( $type ) . '" ' . selected( $type, $pvc->options['general']['flush_interval']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
649
  }
650
 
651
- $html .= '
652
- </select>
653
- <p class="description">' . __( 'How often to flush cached view counts from the object cache into the database. This feature is used only if a persistent object cache is detected and the interval is greater than 0 (number zero). 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.<br /><strong>Notice:</strong> Potential data loss may occur if the object cache is cleared/unavailable for the duration of the interval.', 'post-views-counter' ) . '</p>';
654
-
655
- return $html;
656
  }
657
 
658
  /**
659
- * Validate flush object cache interval.
660
- *
661
- * @param array $input Input POST data
662
- * @param array $field Field options
663
- * @return array
664
  */
665
- public function validate_flush_interval( $input, $field ) {
666
- // get main instance
667
- $pvc = Post_Views_Counter();
668
-
669
- // number
670
- $input['flush_interval']['number'] = isset( $input['flush_interval']['number'] ) ? (int) $input['flush_interval']['number'] : $pvc->defaults['general']['flush_interval']['number'];
 
 
 
671
 
672
- if ( $input['flush_interval']['number'] < $field['min'] || $input['flush_interval']['number'] > $field['max'] )
673
- $input['flush_interval']['number'] = $pvc->defaults['general']['flush_interval']['number'];
 
 
 
674
 
675
- // type
676
- $input['flush_interval']['type'] = isset( $input['flush_interval']['type'], $this->time_types[$input['flush_interval']['type']] ) ? $input['flush_interval']['type'] : $pvc->defaults['general']['flush_interval']['type'];
 
 
 
 
677
 
678
- // Since the settings are about to be saved and cache flush interval could've changed,
679
- // we want to make sure that any changes done on the settings page are in effect immediately
680
- // (instead of having to wait for the previous schedule to occur).
681
- // We achieve that by making sure to clear any previous cache flush schedules and
682
- // schedule the new one if the specified interval is > 0
683
- $pvc->remove_cache_flush();
684
 
685
- if ( $input['flush_interval']['number'] > 0 )
686
- $pvc->schedule_cache_flush();
 
687
 
688
- return $input;
 
 
689
  }
690
 
691
  /**
692
- * Setting: exclude visitors.
693
- *
694
- * @return string
695
  */
696
- public function setting_exclude() {
697
- // get main instance
698
- $pvc = Post_Views_Counter();
 
 
 
 
699
 
700
- $html = '';
 
 
 
 
 
 
701
 
702
  foreach ( $this->groups as $type => $type_name ) {
703
- $html .= '
704
- <label><input id="' . esc_attr( 'pvc_exclude-' . $type ) . '" type="checkbox" name="post_views_counter_settings_general[exclude][groups][' . esc_attr( $type ) . ']" value="1" ' . checked( in_array( $type, $pvc->options['general']['exclude']['groups'], true ), true, false ) . ' />' . esc_html( $type_name ) . '</label>';
 
 
 
 
705
  }
706
 
707
- $html .= '
708
- <p class="description">' . __( 'Use it exclude specific user groups from post views count.', 'post-views-counter' ) . '</p>
709
- <div class="pvc_user_roles"' . ( in_array( 'roles', $pvc->options['general']['exclude']['groups'], true ) ? '' : ' style="display: none;"' ) . '>';
710
 
711
  foreach ( $this->user_roles as $role => $role_name ) {
712
- $html .= '
713
- <label><input type="checkbox" name="post_views_counter_settings_general[exclude][roles][' . $role . ']" value="1" ' . checked( in_array( $role, $pvc->options['general']['exclude']['roles'], true ), true, false ) . '>' . esc_html( $role_name ) . '</label>';
714
  }
715
 
716
- $html .= '
717
- <p class="description">' . __( 'Use it exclude specific user roles from post views count.', 'post-views-counter' ) . '</p>
718
- </div>';
719
-
720
- return $html;
721
  }
722
 
723
  /**
724
- * Validate exclude visitors.
725
- *
726
- * @param array $input Input POST data
727
- * @param array $field Field options
728
- * @return array
729
  */
730
- public function validate_exclude( $input, $field ) {
731
  // get main instance
732
  $pvc = Post_Views_Counter();
733
 
734
- // any groups?
735
- if ( isset( $input['exclude']['groups'] ) ) {
736
- $groups = [];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
737
 
738
- foreach ( $input['exclude']['groups'] as $group => $set ) {
739
- if ( isset( $this->groups[$group] ) )
740
- $groups[] = $group;
741
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
742
 
743
- $input['exclude']['groups'] = array_unique( $groups );
744
- } else
745
- $input['exclude']['groups'] = [];
746
 
747
- // any roles?
748
- if ( in_array( 'roles', $input['exclude']['groups'], true ) && isset( $input['exclude']['roles'] ) ) {
749
- $roles = [];
750
 
751
- foreach ( $input['exclude']['roles'] as $role => $set ) {
752
- if ( isset( $this->user_roles[$role] ) )
753
- $roles[] = $role;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754
  }
755
 
756
- $input['exclude']['roles'] = array_unique( $roles );
757
- } else
758
- $input['exclude']['roles'] = [];
759
 
760
- return $input;
761
- }
 
762
 
763
- /**
764
- * Setting: exclude IP addresses.
765
- *
766
- * @return string
767
- */
768
- public function setting_exclude_ips() {
769
- $ips = Post_Views_Counter()->options['general']['exclude_ips'];
770
 
771
- $html = '';
 
 
 
772
 
773
- if ( ! empty( $ips ) ) {
774
- foreach ( $ips as $key => $ip ) {
775
- $html .= '
776
- <div class="ip-box">
777
- <input type="text" name="post_views_counter_settings_general[exclude_ips][]" value="' . esc_attr( $ip ) . '" /> <a href="#" class="remove-exclude-ip" title="' . esc_attr__( 'Remove', 'post-views-counter' ) . '">' . esc_html__( 'Remove', 'post-views-counter' ) . '</a>
778
- </div>';
779
  }
780
- } else {
781
- $html .= '
782
- <div class="ip-box">
783
- <input type="text" name="post_views_counter_settings_general[exclude_ips][]" value="" /> <a href="#" class="remove-exclude-ip" title="' . esc_attr__( 'Remove', 'post-views-counter' ) . '">' . esc_html__( 'Remove', 'post-views-counter' ) . '</a>
784
- </div>';
785
- }
786
 
787
- $html .= '
788
- <p><input type="button" class="button button-secondary add-exclude-ip" value="' . esc_attr__( 'Add new', 'post-views-counter' ) . '" /> <input type="button" class="button button-secondary add-current-ip" value="' . esc_attr__( 'Add my current IP', 'post-views-counter' ) . '" data-rel="' . esc_attr( $_SERVER['REMOTE_ADDR'] ) . '" /></p>
789
- <p class="description">' . esc_html__( 'Enter the IP addresses to be excluded from post views count.', 'post-views-counter' ) . '</p>';
790
 
791
- return $html;
792
- }
 
 
793
 
794
- /**
795
- * Validate exclude IP addresses.
796
- *
797
- * @param array $input Input POST data
798
- * @param array $field Field options
799
- * @return array
800
- */
801
- public function validate_exclude_ips( $input, $field ) {
802
- // get main instance
803
- $pvc = Post_Views_Counter();
804
 
805
- // any ip addresses?
806
- if ( isset( $input['exclude_ips'] ) ) {
807
- $ips = [];
808
 
809
- foreach ( $input['exclude_ips'] as $ip ) {
810
- if ( strpos( $ip, '*' ) !== false ) {
811
- $new_ip = str_replace( '*', '0', $ip );
812
 
813
- if ( filter_var( $new_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
 
 
814
  $ips[] = $ip;
815
- } elseif ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
816
- $ips[] = $ip;
 
817
  }
818
 
819
- $input['exclude_ips'] = array_unique( $ips );
820
- }
821
 
822
- return $input;
823
- }
824
 
825
- /**
826
- * Setting: tools.
827
- *
828
- * @return string
829
- */
830
- public function setting_wp_postviews() {
831
- $html = '
832
- <input type="submit" class="button button-secondary" name="post_views_counter_import_wp_postviews" value="' . __( 'Import views', 'post-views-counter' ) . '"/> <label><input id="pvc-wp-postviews" type="checkbox" name="post_views_counter_import_wp_postviews_override" value="1" />' . __( 'Override existing views data.', 'post-views-counter' ) . '</label>
833
- <p class="description">' . __( 'Import post views data from WP-PostViews plugin.', 'post-views-counter' ) . '</p>
834
- <br /><input type="submit" class="button button-secondary" name="post_views_counter_reset_views" value="' . __( 'Delete views', 'post-views-counter' ) . '"/>
835
- <p class="description">' . __( 'Delete all the existing post views data.', 'post-views-counter' ) . '</p>';
836
-
837
- return $html;
838
- }
839
 
840
- /**
841
- * Setting: user type.
842
- *
843
- * @return string
844
- */
845
- public function setting_restrict_display() {
846
- // get main instance
847
- $pvc = Post_Views_Counter();
848
 
849
- $html = '';
 
850
 
851
- foreach ( $this->groups as $type => $type_name ) {
852
- if ( $type === 'robots' )
853
- continue;
854
 
855
- $html .= '
856
- <label><input id="pvc_restrict_display-' . esc_attr( $type ) . '" type="checkbox" name="post_views_counter_settings_display[restrict_display][groups][' . esc_html( $type ) . ']" value="1" ' . checked( in_array( $type, $pvc->options['display']['restrict_display']['groups'], true ), true, false ) . ' />' . esc_html( $type_name ) . '</label>';
857
- }
858
 
859
- $html .= '
860
- <p class="description">' . __( 'Use it to hide the post views counter from selected type of visitors.', 'post-views-counter' ) . '</p>
861
- <div class="pvc_user_roles"' . ( in_array( 'roles', $pvc->options['display']['restrict_display']['groups'], true ) ? '' : ' style="display: none;"' ) . '>';
862
 
863
- foreach ( $this->user_roles as $role => $role_name ) {
864
- $html .= '
865
- <label><input type="checkbox" name="post_views_counter_settings_display[restrict_display][roles][' . esc_attr( $role ) . ']" value="1" ' . checked( in_array( $role, $pvc->options['display']['restrict_display']['roles'], true ), true, false ) . ' />' . esc_html( $role_name ) . '</label>';
866
- }
867
 
868
- $html .= '
869
- <p class="description">' . __( 'Use it to hide the post views counter from selected user roles.', 'post-views-counter' ) . '</p>
870
- </div>';
871
 
872
- return $html;
873
- }
 
874
 
875
- /**
876
- * Validate user type.
877
- *
878
- * @param array $input Input POST data
879
- * @param array $field Field options
880
- * @return array
881
- */
882
- public function validate_restrict_display( $input, $field ) {
883
- // get main instance
884
- $pvc = Post_Views_Counter();
885
 
886
- // any groups?
887
- if ( isset( $input['restrict_display']['groups'] ) ) {
888
- $groups = [];
889
 
890
- foreach ( $input['restrict_display']['groups'] as $group => $set ) {
891
- if ( $group === 'robots' )
892
- continue;
893
 
894
- if ( isset( $this->groups[$group] ) )
895
- $groups[] = $group;
896
- }
 
 
 
 
 
897
 
898
- $input['restrict_display']['groups'] = array_unique( $groups );
899
- } else
900
- $input['restrict_display']['groups'] = [];
901
 
902
- // any roles?
903
- if ( in_array( 'roles', $input['restrict_display']['groups'], true ) && isset( $input['restrict_display']['roles'] ) ) {
904
- $roles = [];
905
 
906
- foreach ( $input['restrict_display']['roles'] as $role => $set ) {
907
- if ( isset( $this->user_roles[$role] ) )
908
- $roles[] = $role;
909
- }
 
 
 
 
 
 
 
 
 
 
 
910
 
911
- $input['restrict_display']['roles'] = array_unique( $roles );
912
- } else
913
- $input['restrict_display']['roles'] = [];
 
 
 
 
 
 
 
 
 
914
 
915
  return $input;
916
  }
10
  */
11
  class Post_Views_Counter_Settings {
12
 
13
+ private $tabs;
14
+ private $choices;
15
+ private $modes;
16
  private $time_types;
17
  private $groups;
18
  private $user_roles;
19
+ private $positions;
20
+ private $display_styles;
21
  public $post_types;
22
  public $page_types;
23
 
24
  public function __construct() {
25
  // actions
26
+ add_action( 'admin_init', array( $this, 'register_settings' ) );
27
+ add_action( 'admin_menu', array( $this, 'admin_menu_options' ) );
28
+ add_action( 'after_setup_theme', array( $this, 'load_defaults' ) );
29
+ add_action( 'wp_loaded', array( $this, 'load_post_types' ) );
 
 
30
  }
31
 
32
  /**
33
  * Load default settings.
 
 
34
  */
35
  public function load_defaults() {
36
  if ( ! is_admin() )
37
  return;
38
 
39
+ $this->modes = array(
40
+ 'php' => __( 'PHP', 'post-views-counter' ),
41
+ 'js' => __( 'JavaScript', 'post-views-counter' ),
42
+ 'ajax' => __( 'Fast AJAX', 'post-views-counter' )
43
+ );
44
+
45
+ if ( function_exists( 'register_rest_route' ) )
46
+ $this->modes['rest_api'] = __( 'REST API', 'post-views-counter' );
47
+
48
+ $this->time_types = array(
49
+ 'minutes' => __( 'minutes', 'post-views-counter' ),
50
+ 'hours' => __( 'hours', 'post-views-counter' ),
51
+ 'days' => __( 'days', 'post-views-counter' ),
52
+ 'weeks' => __( 'weeks', 'post-views-counter' ),
53
+ 'months' => __( 'months', 'post-views-counter' ),
54
+ 'years' => __( 'years', 'post-views-counter' )
55
+ );
56
+
57
+ $this->groups = array(
58
+ 'robots' => __( 'robots', 'post-views-counter' ),
59
+ 'users' => __( 'logged in users', 'post-views-counter' ),
60
+ 'guests' => __( 'guests', 'post-views-counter' ),
61
+ 'roles' => __( 'selected user roles', 'post-views-counter' )
62
+ );
63
+
64
+ $this->positions = array(
65
+ 'before' => __( 'before the content', 'post-views-counter' ),
66
+ 'after' => __( 'after the content', 'post-views-counter' ),
67
+ 'manual' => __( 'manual', 'post-views-counter' )
68
+ );
69
+
70
+ $this->display_styles = array(
71
+ 'icon' => __( 'icon', 'post-views-counter' ),
72
+ 'text' => __( 'label', 'post-views-counter' )
73
+ );
74
+
75
+ $this->tabs = array(
76
+ 'general' => array(
77
+ 'name' => __( 'General', 'post-views-counter' ),
78
+ 'key' => 'post_views_counter_settings_general',
79
+ 'submit' => 'save_pvc_general',
80
+ 'reset' => 'reset_pvc_general'
81
+ ),
82
+ 'display' => array(
83
+ 'name' => __( 'Display', 'post-views-counter' ),
84
+ 'key' => 'post_views_counter_settings_display',
85
+ 'submit' => 'save_pvc_display',
86
+ 'reset' => 'reset_pvc_display'
87
+ )
88
+ );
89
 
90
  $this->user_roles = $this->get_user_roles();
91
 
92
  $this->page_types = apply_filters(
93
  'pvc_page_types_display_options',
94
+ array(
95
+ 'home' => __( 'Home', 'post-views-counter' ),
96
+ 'archive' => __( 'Archives', 'post-views-counter' ),
97
+ 'singular' => __( 'Single pages', 'post-views-counter' ),
98
+ 'search' => __( 'Search results', 'post-views-counter' ),
99
+ )
100
  );
101
  }
102
 
103
  /**
104
  * Get post types avaiable for counting.
 
 
105
  */
106
  public function load_post_types() {
107
  if ( ! is_admin() )
108
  return;
109
 
110
+ $post_types = array();
111
 
112
  // built in public post types
113
+ foreach ( get_post_types( array( '_builtin' => true, 'public' => true ), 'objects', 'and' ) as $key => $post_type ) {
114
  $post_types[$key] = $post_type->labels->name;
115
  }
116
 
117
  // public custom post types
118
+ foreach ( get_post_types( array( '_builtin' => false, 'public' => true ), 'objects', 'and' ) as $key => $post_type ) {
119
  $post_types[$key] = $post_type->labels->name;
120
  }
121
 
123
  if ( class_exists( 'bbPress' ) && isset( $post_types['reply'] ) )
124
  unset( $post_types['reply'] );
125
 
126
+ $post_types = apply_filters( 'pvc_available_post_types', $post_types );
127
 
128
  // sort post types alphabetically with their keys
129
  asort( $post_types, SORT_STRING );
140
  public function get_user_roles() {
141
  global $wp_roles;
142
 
143
+ $roles = array();
144
 
145
  foreach ( apply_filters( 'editable_roles', $wp_roles->roles ) as $role => $details ) {
146
  $roles[$role] = translate_user_role( $details['name'] );
152
  }
153
 
154
  /**
155
+ * Add options page.
156
  *
157
+ * @return void
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  */
159
+ public function admin_menu_options() {
160
+ add_options_page( __( 'Post Views Counter', 'post-views-counter' ), __( 'Post Views Counter', 'post-views-counter' ), 'manage_options', 'post-views-counter', array( $this, 'options_page' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  }
162
 
163
  /**
164
+ * Options page callback.
165
  *
166
+ * @return void
 
167
  */
168
+ public function options_page() {
169
+ $tab_key = (isset( $_GET['tab'] ) ? esc_attr( $_GET['tab'] ) : 'general');
 
 
170
 
171
+ echo '
172
+ <div class="wrap">
173
+ <h2>' . __( 'Post Views Counter', 'post-views-counter' ) . '</h2>
174
+ <h2 class="nav-tab-wrapper">';
175
 
176
+ foreach ( $this->tabs as $key => $name ) {
177
+ echo '
178
+ <a class="nav-tab ' . ($tab_key == $key ? 'nav-tab-active' : '') . '" href="' . esc_url( admin_url( 'options-general.php?page=post-views-counter&tab=' . $key ) ) . '">' . $name['name'] . '</a>';
179
+ }
 
 
 
 
 
180
 
181
+ echo '
182
+ </h2>
183
+ <div class="post-views-counter-settings">
184
+ <div class="df-credits">
185
+ <h3 class="hndle">' . __( 'Post Views Counter', 'post-views-counter' ) . ' ' . Post_Views_Counter()->defaults['version'] . '</h3>
186
+ <div class="inside">
187
+ <h4 class="inner">' . __( 'Need support?', 'post-views-counter' ) . '</h4>
188
+ <p class="inner">' . sprintf( __( 'If you are having problems with this plugin, please browse it\'s <a href="%s" target="_blank">Documentation</a> or talk about them in the <a href="%s" target="_blank">Support forum</a>', 'post-views-counter' ), 'https://www.dfactory.eu/docs/post-views-counter/?utm_source=post-views-counter-settings&utm_medium=link&utm_campaign=docs', 'https://www.dfactory.eu/support/?utm_source=post-views-counter-settings&utm_medium=link&utm_campaign=support' ) . '</p>
189
+ <hr />
190
+ <h4 class="inner">' . __( 'Do you like this plugin?', 'post-views-counter' ) . '</h4>
191
+ <p class="inner">' . sprintf( __( '<a href="%s" target="_blank">Rate it 5</a> on WordPress.org', 'post-views-counter' ), 'https://wordpress.org/support/plugin/post-views-counter/reviews/?filter=5' ) . '<br />' .
192
+ sprintf( __( 'Blog about it & link to the <a href="%s" target="_blank">plugin page</a>.', 'post-views-counter' ), 'https://dfactory.eu/plugins/post-views-counter/?utm_source=post-views-counter-settings&utm_medium=link&utm_campaign=blog-about' ) . '<br />' .
193
+ sprintf( __( 'Check out our other <a href="%s" target="_blank">WordPress plugins</a>.', 'post-views-counter' ), 'https://dfactory.eu/plugins/?utm_source=post-views-counter-settings&utm_medium=link&utm_campaign=other-plugins' ) . '
194
+ </p>
195
+ <hr />
196
+ <p class="df-link inner"><a href="http://www.dfactory.eu/?utm_source=post-views-counter-settings&utm_medium=link&utm_campaign=created-by" target="_blank" title="Digital Factory"><img src="//pvc-53eb.kxcdn.com/df-black-sm.png' . '" alt="Digital Factory" /></a></p>
197
+ </div>
198
+ </div>
199
+ <form action="options.php" method="post">';
200
+
201
+ wp_nonce_field( 'update-options' );
202
+ settings_fields( $this->tabs[$tab_key]['key'] );
203
+ do_settings_sections( $this->tabs[$tab_key]['key'] );
204
+
205
+ echo '
206
+ <p class="submit">';
207
+
208
+ submit_button( '', 'primary', $this->tabs[$tab_key]['submit'], false );
209
+
210
+ echo ' ';
211
+
212
+ submit_button( __( 'Reset to defaults', 'post-views-counter' ), 'secondary reset_pvc_settings', $this->tabs[$tab_key]['reset'], false );
213
+
214
+ echo '
215
+ </p>
216
+ </form>
217
+ </div>
218
+ <div class="clear"></div>
219
+ </div>';
220
+ }
221
 
222
+ /**
223
+ * Register settings callback.
224
+ */
225
+ public function register_settings() {
226
+ // general options
227
+ register_setting( 'post_views_counter_settings_general', 'post_views_counter_settings_general', array( $this, 'validate_settings' ) );
228
+ add_settings_section( 'post_views_counter_settings_general', __( 'General settings', 'post-views-counter' ), '', 'post_views_counter_settings_general' );
229
+ add_settings_field( 'pvc_post_types_count', __( 'Post Types Count', 'post-views-counter' ), array( $this, 'post_types_count' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
230
+ add_settings_field( 'pvc_counter_mode', __( 'Counter Mode', 'post-views-counter' ), array( $this, 'counter_mode' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
231
+ add_settings_field( 'pvc_post_views_column', __( 'Post Views Column', 'post-views-counter' ), array( $this, 'post_views_column' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
232
+ add_settings_field( 'pvc_restrict_edit_views', __( 'Restrict Edit', 'post-views-counter' ), array( $this, 'restrict_edit_views' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
233
+ add_settings_field( 'pvc_time_between_counts', __( 'Count Interval', 'post-views-counter' ), array( $this, 'time_between_counts' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
234
+ add_settings_field( 'pvc_reset_counts', __( 'Reset Data Interval', 'post-views-counter' ), array( $this, 'reset_counts' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
235
+ add_settings_field( 'pvc_flush_interval', __( 'Flush Object Cache Interval', 'post-views-counter' ), array( $this, 'flush_interval' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
236
+ add_settings_field( 'pvc_exclude', __( 'Exclude Visitors', 'post-views-counter' ), array( $this, 'exclude' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
237
+ add_settings_field( 'pvc_exclude_ips', __( 'Exclude IPs', 'post-views-counter' ), array( $this, 'exclude_ips' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
238
+ add_settings_field( 'pvc_strict_counts', __( 'Strict counts', 'post-views-counter' ), array( $this, 'strict_counts' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
239
+ add_settings_field( 'pvc_wp_postviews', __( 'Tools', 'post-views-counter' ), array( $this, 'wp_postviews' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
240
+ add_settings_field( 'pvc_deactivation_delete', __( 'Deactivation', 'post-views-counter' ), array( $this, 'deactivation_delete' ), 'post_views_counter_settings_general', 'post_views_counter_settings_general' );
241
+
242
+ // display options
243
+ register_setting( 'post_views_counter_settings_display', 'post_views_counter_settings_display', array( $this, 'validate_settings' ) );
244
+ add_settings_section( 'post_views_counter_settings_display', __( 'Display settings', 'post-views-counter' ), '', 'post_views_counter_settings_display' );
245
+ add_settings_field( 'pvc_post_views_label', __( 'Post Views Label', 'post-views-counter' ), array( $this, 'post_views_label' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
246
+ add_settings_field( 'pvc_post_types_display', __( 'Post Type', 'post-views-counter' ), array( $this, 'post_types_display' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
247
+ add_settings_field( 'pvc_page_types_display', __( 'Page Type', 'post-views-counter' ), array( $this, 'page_types_display' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
248
+ add_settings_field( 'pvc_restrict_display', __( 'User Type', 'post-views-counter' ), array( $this, 'restrict_display' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
249
+ add_settings_field( 'pvc_position', __( 'Position', 'post-views-counter' ), array( $this, 'position' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
250
+ add_settings_field( 'pvc_display_style', __( 'Display Style', 'post-views-counter' ), array( $this, 'display_style' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
251
+ add_settings_field( 'pvc_icon_class', __( 'Icon Class', 'post-views-counter' ), array( $this, 'icon_class' ), 'post_views_counter_settings_display', 'post_views_counter_settings_display' );
252
+ }
253
 
254
+ /**
255
+ * Post views label option.
256
+ */
257
+ public function post_views_label() {
258
+ echo '
259
+ <div id="pvc_post_views_label">
260
+ <fieldset>
261
+ <input type="text" class="large-text" name="post_views_counter_settings_display[label]" value="' . esc_attr( Post_Views_Counter()->options['display']['label'] ) . '" />
262
+ <p class="description">' . __( 'Enter the label for the post views counter field.', 'post-views-counter' ) . '</p>
263
+ </fieldset>
264
+ </div>';
265
+ }
266
 
267
+ /**
268
+ * Post types to count option.
269
+ */
270
+ public function post_types_count() {
271
+ echo '
272
+ <div id="pvc_post_types_count">';
273
 
274
+ foreach ( $this->post_types as $post_type => $post_type_name ) {
275
+ echo '
276
+ <label class="cb-checkbox"><input id="pvc_post_types_count-' . esc_attr( $post_type ) . '" type="checkbox" name="post_views_counter_settings_general[post_types_count][' . esc_attr( $post_type ) . ']" value="1" ' . checked( in_array( $post_type, Post_Views_Counter()->options['general']['post_types_count'], true ), true, false ) . ' />' . esc_html( $post_type_name ) . ' </label>';
277
+ }
278
 
279
+ echo '
280
+ <p class="description">' . __( 'Select post types for which post views will be counted.', 'post-views-counter' ) . '</p>
281
+ </div>';
282
+ }
 
 
 
283
 
284
+ /**
285
+ * Post types to display option.
286
+ */
287
+ public function post_types_display() {
288
+ echo '
289
+ <div id="pvc_post_types_display">';
290
 
291
+ foreach ( $this->post_types as $post_type => $post_type_name ) {
292
+ echo '
293
+ <label class="cb-checkbox"><input id="pvc_post_types_display-' . esc_attr( $post_type ) . '" type="checkbox" name="post_views_counter_settings_display[post_types_display][' . esc_attr( $post_type ) . ']" value="1" ' . checked( in_array( $post_type, Post_Views_Counter()->options['display']['post_types_display'], true ), true, false ) . ' />' . esc_html( $post_type_name ) . '</label>';
 
 
 
 
 
 
 
 
 
 
 
294
  }
295
 
296
+ echo '
297
+ <p class="description">' . __( 'Select post types for which the views count will be displayed.', 'post-views-counter' ) . '</p>
298
+ </div>';
299
  }
300
 
301
  /**
302
+ * Counter mode option.
 
 
 
 
303
  */
304
+ public function counter_mode() {
305
+ echo '
306
+ <div id="pvc_counter_mode">';
307
 
308
+ foreach ( $this->modes as $key => $value ) {
309
+ $key = esc_attr( $key );
310
 
311
+ echo '
312
+ <label class="cb-radio"><input type="radio" name="post_views_counter_settings_general[counter_mode]" value="' . $key . '" ' . checked( $key, Post_Views_Counter()->options['general']['counter_mode'], false ) . ' />' . esc_html( $value ) . '</label>';
313
+ }
 
 
314
 
315
+ echo '
316
+ <p class="description">' . __( 'Select the method of collecting post views data. If you are using any of the caching plugins select Javascript or REST API (if available).', 'post-views-counter' ) . '<br />' . __( 'Optionally try the Fast AJAX experimental method, usually 10+ times faster than Javascript or REST API.', 'post-views-counter' ) . '</p>
317
+ </div>';
318
  }
319
 
320
  /**
321
+ * Post views column option.
 
 
 
 
322
  */
323
+ public function post_views_column() {
324
+ echo '
325
+ <div id="pvc_post_views_column">
326
+ <label class="cb-checkbox"><input id="pvc-post-views-column-enable" type="checkbox" name="post_views_counter_settings_general[post_views_column]" value="1" ' . checked( true, Post_Views_Counter()->options['general']['post_views_column'], false ) . ' />' . __( 'Enable to display post views count column for each of the selected post types.', 'post-views-counter' ) . '</label>
327
+ </div>';
328
  }
329
 
330
  /**
331
+ * Time between counts option.
 
 
 
332
  */
333
+ public function time_between_counts() {
334
+ echo '
335
+ <div id="pvc_time_between_counts">
336
+ <input size="4" type="text" name="post_views_counter_settings_general[time_between_counts][number]" value="' . esc_attr( Post_Views_Counter()->options['general']['time_between_counts']['number'] ) . '" />
337
+ <select class="pvc-chosen-short" name="post_views_counter_settings_general[time_between_counts][type]">';
 
338
 
339
+ foreach ( $this->time_types as $type => $type_name ) {
340
+ echo '
341
+ <option value="' . esc_attr( $type ) . '" ' . selected( $type, Post_Views_Counter()->options['general']['time_between_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
342
  }
343
 
344
+ echo '
345
+ </select>
346
+ <p class="description">' . __( 'Enter the time between single user visit count.', 'post-views-counter' ) . '</p>
347
+ </div>';
348
  }
349
 
350
  /**
351
+ * Reset counts option.
 
 
 
 
352
  */
353
+ public function reset_counts() {
354
+ echo '
355
+ <div id="pvc_reset_counts">
356
+ <input size="4" type="text" name="post_views_counter_settings_general[reset_counts][number]" value="' . esc_attr( Post_Views_Counter()->options['general']['reset_counts']['number'] ) . '" />
357
+ <select class="pvc-chosen-short" name="post_views_counter_settings_general[reset_counts][type]">';
 
 
 
 
358
 
359
+ foreach ( array_slice( $this->time_types, 2, null, true ) as $type => $type_name ) {
360
+ echo '
361
+ <option value="' . esc_attr( $type ) . '" ' . selected( $type, Post_Views_Counter()->options['general']['reset_counts']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
 
 
 
362
  }
363
 
364
+ echo '
365
+ </select>
366
+ <p class="description">' . __( 'Delete single day post views data older than specified above. Enter 0 (number zero) if you want to preserve your data regardless of its age.', 'post-views-counter' ) . '</p>
367
+ </div>';
368
  }
369
 
370
  /**
371
+ * Flush interval option.
 
 
 
372
  */
373
+ public function flush_interval() {
374
+ echo '
375
+ <div id="pvc_flush_interval">
376
+ <input size="4" type="text" name="post_views_counter_settings_general[flush_interval][number]" value="' . esc_attr( Post_Views_Counter()->options['general']['flush_interval']['number'] ) . '" />
377
+ <select class="pvc-chosen-short" name="post_views_counter_settings_general[flush_interval][type]">';
 
 
378
 
379
  foreach ( $this->time_types as $type => $type_name ) {
380
+ echo '
381
+ <option value="' . esc_attr( $type ) . '" ' . selected( $type, Post_Views_Counter()->options['general']['flush_interval']['type'], false ) . '>' . esc_html( $type_name ) . '</option>';
382
  }
383
 
384
+ echo '
385
+ </select>
386
+ <p class="description">' . __( 'How often to flush cached view counts from the object cache into the database. This feature is used only if a persistent object cache is detected and the interval is greater than 0 (number zero). 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.<br /><strong>Notice:</strong> Potential data loss may occur if the object cache is cleared/unavailable for the duration of the interval.', 'post-views-counter' ) . '</p>
387
+ </div>';
 
388
  }
389
 
390
  /**
391
+ * Exlude user groups option.
 
 
 
 
392
  */
393
+ public function exclude() {
394
+ echo '
395
+ <div id="pvc_exclude">
396
+ <fieldset>';
397
 
398
+ foreach ( $this->groups as $type => $type_name ) {
399
+ echo '
400
+ <label class="cb-checkbox"><input id="pvc_exclude-' . $type . '" type="checkbox" name="post_views_counter_settings_general[exclude][groups][' . $type . ']" value="1" ' . checked( in_array( $type, Post_Views_Counter()->options['general']['exclude']['groups'], true ), true, false ) . ' />' . esc_html( $type_name ) . '</label>';
401
+ }
402
 
403
+ echo '
404
+ <p class="description">' . __( 'Use it to hide the post views counter from selected type of visitors.', 'post-views-counter' ) . '</p>
405
+ <div class="pvc_user_roles"' . (in_array( 'roles', Post_Views_Counter()->options['general']['exclude']['groups'], true ) ? '' : ' style="display: none;"') . '>';
406
 
407
+ foreach ( $this->user_roles as $role => $role_name ) {
408
+ echo '
409
+ <label class="cb-checkbox"><input type="checkbox" name="post_views_counter_settings_general[exclude][roles][' . $role . ']" value="1" ' . checked( in_array( $role, Post_Views_Counter()->options['general']['exclude']['roles'], true ), true, false ) . '>' . esc_html( $role_name ) . '</label>';
410
+ }
411
 
412
+ echo ' <p class="description">' . __( 'Use it to hide the post views counter from selected user roles.', 'post-views-counter' ) . '</p>
413
+ </div>
414
+ </fieldset>
415
+ </div>';
416
  }
417
 
418
  /**
419
+ * Exclude IPs option.
 
 
 
420
  */
421
+ public function exclude_ips() {
422
+ // lovely php 5.2 limitations
423
+ $ips = Post_Views_Counter()->options['general']['exclude_ips'];
424
 
425
+ echo '
426
+ <div id="pvc_exclude_ips">';
 
427
 
428
+ if ( ! empty( $ips ) ) {
429
+ foreach ( $ips as $key => $ip ) {
430
+ echo '
431
+ <div class="ip-box">
432
+ <input type="text" name="post_views_counter_settings_general[exclude_ips][]" value="' . esc_attr( $ip ) . '" /> <a href="#" class="remove-exclude-ip" title="' . esc_attr__( 'Remove', 'post-views-counter' ) . '">' . esc_attr__( 'Remove', 'post-views-counter' ) . '</a>
433
+ </div>';
434
+ }
435
+ } else {
436
+ echo '
437
+ <div class="ip-box">
438
+ <input type="text" name="post_views_counter_settings_general[exclude_ips][]" value="" /> <a href="#" class="remove-exclude-ip" title="' . esc_attr__( 'Remove', 'post-views-counter' ) . '">' . esc_attr__( 'Remove', 'post-views-counter' ) . '</a>
439
+ </div>';
440
  }
441
 
442
+ echo '
443
+ <p><input type="button" class="button button-secondary add-exclude-ip" value="' . esc_attr__( 'Add new', 'post-views-counter' ) . '" /> <input type="button" class="button button-secondary add-current-ip" value="' . esc_attr__( 'Add my current IP', 'post-views-counter' ) . '" data-rel="' . esc_attr( $_SERVER['REMOTE_ADDR'] ) . '" /></p>
444
+ <p class="description">' . __( 'Enter the IP addresses to be excluded from post views count.', 'post-views-counter' ) . '</p>
445
+ </div>';
 
446
  }
447
 
448
  /**
449
+ * Strict counts option.
 
 
 
 
450
  */
451
+ public function strict_counts() {
452
+ echo '
453
+ <div id="pvc_strict_counts">
454
+ <label class="cb-checkbox"><input id="pvc-strict-counts" type="checkbox" name="post_views_counter_settings_general[strict_counts]" value="1" ' . checked( true, Post_Views_Counter()->options['general']['strict_counts'], false ) . ' />' . __( 'Enable to prevent bypassing the counts interval (for e.g. using incognito browser window or by clearing cookies).', 'post-views-counter' ) . '</label>
455
+ </div>';
456
+ }
 
 
 
 
 
 
 
 
 
457
 
458
+ /**
459
+ * WP-PostViews import option.
460
+ */
461
+ public function wp_postviews() {
462
+ echo '
463
+ <div id="pvc_wp_postviews">
464
+ <fieldset>
465
+ <input type="submit" class="button button-secondary" name="post_views_counter_import_wp_postviews" value="' . __( 'Import views', 'post-views-counter' ) . '"/> <label class="cb-checkbox"><input id="pvc-wp-postviews" type="checkbox" name="post_views_counter_import_wp_postviews_override" value="1" />' . __( 'Override existing views data.', 'post-views-counter' ) . '</label>
466
+ <p class="description">' . __( 'Import post views data from WP-PostViews plugin.', 'post-views-counter' ) . '</p>
467
+ <input type="submit" class="button button-secondary" name="post_views_counter_reset_views" value="' . __( 'Delete views', 'post-views-counter' ) . '"/>
468
+ <p class="description">' . __( 'Delete ALL the existing post views data.', 'post-views-counter' ) . '</p>
469
+ </fieldset>
470
+ </div>';
471
+ }
472
 
473
+ /**
474
+ * Limit views edit to admins.
475
+ */
476
+ public function restrict_edit_views() {
477
+ echo '
478
+ <div id="pvc_restrict_edit_views">
479
+ <label class="cb-checkbox"><input type="checkbox" name="post_views_counter_settings_general[restrict_edit_views]" value="1" ' . checked( true, Post_Views_Counter()->options['general']['restrict_edit_views'], false ) . ' />' . __( 'Enable to restrict post views editing to admins only.', 'post-views-counter' ) . '</label>
480
+ </div>';
481
  }
482
 
483
  /**
484
+ * Plugin deactivation option.
 
 
 
485
  */
486
+ public function deactivation_delete() {
487
+ echo '
488
+ <div id="pvc_deactivation_delete">
489
+ <label class="cb-checkbox"><input type="checkbox" name="post_views_counter_settings_general[deactivation_delete]" value="1" ' . checked( true, Post_Views_Counter()->options['general']['deactivation_delete'], false ) . ' />' . __( 'Enable to delete all plugin data on deactivation.', 'post-views-counter' ) . '</label>
490
+ </div>';
491
+ }
492
 
493
+ /**
494
+ * Visibility option.
495
+ */
496
+ public function page_types_display() {
497
+ echo '
498
+ <div id="pvc_post_types_display">';
499
 
500
+ foreach ( $this->page_types as $key => $label ) {
501
+ echo '
502
+ <label class="cb-checkbox"><input id="pvc_page_types_display-' . esc_attr( $key ) . '" type="checkbox" name="post_views_counter_settings_display[page_types_display][' . esc_attr( $key ) . ']" value="1" ' . checked( in_array( $key, Post_Views_Counter()->options['display']['page_types_display'], true ), true, false ) . ' />' . esc_html( $label ) . '</label>';
503
  }
504
 
505
+ echo '
506
+ <p class="description">' . __( 'Select page types where the views count will be displayed.', 'post-views-counter' ) . '</p>
507
+ </div>';
 
 
508
  }
509
 
510
  /**
511
+ * Counter position option.
 
 
 
 
512
  */
513
+ public function position() {
514
+ echo '
515
+ <div id="pvc_position">
516
+ <select class="pvc-chosen-short" name="post_views_counter_settings_display[position]">';
517
+
518
+ foreach ( $this->positions as $position => $position_name ) {
519
+ echo '
520
+ <option value="' . esc_attr( $position ) . '" ' . selected( $position, Post_Views_Counter()->options['display']['position'], false ) . '>' . esc_html( $position_name ) . '</option>';
521
+ }
522
 
523
+ echo '
524
+ </select>
525
+ <p class="description">' . __( 'Select where would you like to display the post views counter. Use [post-views] shortcode for manual display.', 'post-views-counter' ) . '</p>
526
+ </div>';
527
+ }
528
 
529
+ /**
530
+ * Counter style option.
531
+ */
532
+ public function display_style() {
533
+ echo '
534
+ <div id="pvc_display_style">';
535
 
536
+ foreach ( $this->display_styles as $display => $style ) {
537
+ $display = esc_attr( $display );
 
 
 
 
538
 
539
+ echo '
540
+ <label class="cb-checkbox"><input type="checkbox" name="post_views_counter_settings_display[display_style][' . $display . ']" value="1" ' . checked( true, Post_Views_Counter()->options['display']['display_style'][$display], false ) . ' />' . esc_html( $style ) . '</label>';
541
+ }
542
 
543
+ echo '
544
+ <p class="description">' . __( 'Choose how to display the post views counter.', 'post-views-counter' ) . '</p>
545
+ </div>';
546
  }
547
 
548
  /**
549
+ * Counter icon class option.
 
 
550
  */
551
+ public function icon_class() {
552
+ echo '
553
+ <div id="pvc_icon_class">
554
+ <input type="text" name="post_views_counter_settings_display[icon_class]" class="large-text" value="' . esc_attr( Post_Views_Counter()->options['display']['icon_class'] ) . '" />
555
+ <p class="description">' . sprintf( __( 'Enter the post views icon class. Any of the <a href="%s" target="_blank">Dashicons</a> classes are available.', 'post-views-counter' ), 'https://developer.wordpress.org/resource/dashicons/' ) . '</p>
556
+ </div>';
557
+ }
558
 
559
+ /**
560
+ * Restrict display option.
561
+ */
562
+ public function restrict_display() {
563
+ echo '
564
+ <div id="pvc_restrict_display">
565
+ <fieldset>';
566
 
567
  foreach ( $this->groups as $type => $type_name ) {
568
+
569
+ if ( $type === 'robots' )
570
+ continue;
571
+
572
+ echo '
573
+ <label class="cb-checkbox"><input id="pvc_restrict_display-' . $type . '" type="checkbox" name="post_views_counter_settings_display[restrict_display][groups][' . esc_html( $type ) . ']" value="1" ' . checked( in_array( $type, Post_Views_Counter()->options['display']['restrict_display']['groups'], true ), true, false ) . ' />' . esc_html( $type_name ) . '</label>';
574
  }
575
 
576
+ echo '
577
+ <p class="description">' . __( 'Use it to hide the post views counter from selected type of visitors.', 'post-views-counter' ) . '</p>
578
+ <div class="pvc_user_roles"' . (in_array( 'roles', Post_Views_Counter()->options['display']['restrict_display']['groups'], true ) ? '' : ' style="display: none;"') . '>';
579
 
580
  foreach ( $this->user_roles as $role => $role_name ) {
581
+ echo '
582
+ <label class="cb-checkbox"><input type="checkbox" name="post_views_counter_settings_display[restrict_display][roles][' . $role . ']" value="1" ' . checked( in_array( $role, Post_Views_Counter()->options['display']['restrict_display']['roles'], true ), true, false ) . ' />' . esc_html( $role_name ) . '</label>';
583
  }
584
 
585
+ echo '
586
+ <p class="description">' . __( 'Use it to hide the post views counter from selected user roles.', 'post-views-counter' ) . '</p>
587
+ </div>
588
+ </fieldset>
589
+ </div>';
590
  }
591
 
592
  /**
593
+ * Validate general settings.
 
 
 
 
594
  */
595
+ public function validate_settings( $input ) {
596
  // get main instance
597
  $pvc = Post_Views_Counter();
598
 
599
+ if ( isset( $_POST['post_views_counter_import_wp_postviews'] ) ) {
600
+ global $wpdb;
601
+
602
+ $meta_key = esc_attr( apply_filters( 'pvc_import_meta_key', 'views' ) );
603
+
604
+ $views = $wpdb->get_results( "SELECT post_id, meta_value FROM " . $wpdb->postmeta . " WHERE meta_key = '" . $meta_key . "'", ARRAY_A, 0 );
605
+
606
+ if ( ! empty( $views ) ) {
607
+ $input = $pvc->defaults['general'];
608
+ $input['wp_postviews_import'] = true;
609
+
610
+ $sql = array();
611
+
612
+ foreach ( $views as $view ) {
613
+ $sql[] = "(" . $view['post_id'] . ", 4, 'total', " . $view['meta_value'] . ")";
614
+ }
615
+
616
+ $wpdb->query( "INSERT INTO " . $wpdb->prefix . "post_views(id, type, period, count) VALUES " . implode( ',', $sql ) . " ON DUPLICATE KEY UPDATE count = " . (isset( $_POST['post_views_counter_import_wp_postviews_override'] ) ? '' : 'count + ') . "VALUES(count)" );
617
 
618
+ add_settings_error( 'wp_postviews_import', 'wp_postviews_import', __( 'Post views data imported succesfully.', 'post-views-counter' ), 'updated' );
619
+ } else {
620
+ add_settings_error( 'wp_postviews_import', 'wp_postviews_import', __( 'There was no post views data to import.', 'post-views-counter' ), 'updated' );
621
  }
622
+ } elseif ( isset( $_POST['post_views_counter_reset_views'] ) ) {
623
+ global $wpdb;
624
+
625
+ if ( $wpdb->query( 'TRUNCATE TABLE ' . $wpdb->prefix . 'post_views' ) )
626
+ add_settings_error( 'reset_post_views', 'reset_post_views', __( 'All existing data deleted succesfully.', 'post-views-counter' ), 'updated' );
627
+ else
628
+ add_settings_error( 'reset_post_views', 'reset_post_views', __( 'Error occurred. All existing data were not deleted.', 'post-views-counter' ), 'error' );
629
+ } elseif ( isset( $_POST['save_pvc_general'] ) ) {
630
+ // post types count
631
+ if ( isset( $input['post_types_count'] ) ) {
632
+ $post_types = array();
633
+
634
+ foreach ( $input['post_types_count'] as $post_type => $set ) {
635
+ if ( isset( $this->post_types[$post_type] ) )
636
+ $post_types[] = $post_type;
637
+ }
638
+
639
+ $input['post_types_count'] = array_unique( $post_types );
640
+ } else
641
+ $input['post_types_count'] = array();
642
 
643
+ // counter mode
644
+ $input['counter_mode'] = isset( $input['counter_mode'], $this->modes[$input['counter_mode']] ) ? $input['counter_mode'] : $pvc->defaults['general']['counter_mode'];
 
645
 
646
+ // post views column
647
+ $input['post_views_column'] = isset( $input['post_views_column'] );
 
648
 
649
+ // time between counts
650
+ $input['time_between_counts']['number'] = (int) ( isset( $input['time_between_counts']['number'] ) ? $input['time_between_counts']['number'] : $pvc->defaults['general']['time_between_counts']['number'] );
651
+ $input['time_between_counts']['type'] = isset( $input['time_between_counts']['type'], $this->time_types[$input['time_between_counts']['type']] ) ? $input['time_between_counts']['type'] : $pvc->defaults['general']['time_between_counts']['type'];
652
+
653
+ // flush interval
654
+ $input['flush_interval']['number'] = (int) ( isset( $input['flush_interval']['number'] ) ? $input['flush_interval']['number'] : $pvc->defaults['general']['flush_interval']['number'] );
655
+ $input['flush_interval']['type'] = isset( $input['flush_interval']['type'], $this->time_types[$input['flush_interval']['type']] ) ? $input['flush_interval']['type'] : $pvc->defaults['general']['flush_interval']['type'];
656
+
657
+ // Since the settings are about to be saved and cache flush interval could've changed,
658
+ // we want to make sure that any changes done on the settings page are in effect immediately
659
+ // (instead of having to wait for the previous schedule to occur).
660
+ // We achieve that by making sure to clear any previous cache flush schedules and
661
+ // schedule the new one if the specified interval is > 0
662
+ $pvc->remove_cache_flush();
663
+
664
+ if ( $input['flush_interval']['number'] > 0 ) {
665
+ $pvc->schedule_cache_flush();
666
  }
667
 
668
+ // reset counts
669
+ $input['reset_counts']['number'] = (int) ( isset( $input['reset_counts']['number'] ) ? $input['reset_counts']['number'] : $pvc->defaults['general']['reset_counts']['number'] );
670
+ $input['reset_counts']['type'] = isset( $input['reset_counts']['type'], $this->time_types[$input['reset_counts']['type']] ) ? $input['reset_counts']['type'] : $pvc->defaults['general']['reset_counts']['type'];
671
 
672
+ // run cron on next visit?
673
+ $input['cron_run'] = ($input['reset_counts']['number'] > 0 ? true : false);
674
+ $input['cron_update'] = ($input['cron_run'] && ( $pvc->options['general']['reset_counts']['number'] !== $input['reset_counts']['number'] || $pvc->options['general']['reset_counts']['type'] !== $input['reset_counts']['type'] ) ? true : false);
675
 
676
+ // exclude
677
+ if ( isset( $input['exclude']['groups'] ) ) {
678
+ $groups = array();
 
 
 
 
679
 
680
+ foreach ( $input['exclude']['groups'] as $group => $set ) {
681
+ if ( isset( $this->groups[$group] ) )
682
+ $groups[] = $group;
683
+ }
684
 
685
+ $input['exclude']['groups'] = array_unique( $groups );
686
+ } else {
687
+ $input['exclude']['groups'] = array();
 
 
 
688
  }
 
 
 
 
 
 
689
 
690
+ if ( in_array( 'roles', $input['exclude']['groups'], true ) && isset( $input['exclude']['roles'] ) ) {
691
+ $roles = array();
 
692
 
693
+ foreach ( $input['exclude']['roles'] as $role => $set ) {
694
+ if ( isset( $this->user_roles[$role] ) )
695
+ $roles[] = $role;
696
+ }
697
 
698
+ $input['exclude']['roles'] = array_unique( $roles );
699
+ } else
700
+ $input['exclude']['roles'] = array();
 
 
 
 
 
 
 
701
 
702
+ // exclude ips
703
+ if ( isset( $input['exclude_ips'] ) ) {
704
+ $ips = array();
705
 
706
+ foreach ( $input['exclude_ips'] as $ip ) {
707
+ if ( strpos( $ip, '*' ) !== false ) {
708
+ $new_ip = str_replace( '*', '0', $ip );
709
 
710
+ if ( filter_var( $new_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
711
+ $ips[] = $ip;
712
+ } elseif ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) )
713
  $ips[] = $ip;
714
+ }
715
+
716
+ $input['exclude_ips'] = array_unique( $ips );
717
  }
718
 
719
+ // restrict edit views
720
+ $input['restrict_edit_views'] = isset( $input['restrict_edit_views'] );
721
 
722
+ // strict counts
723
+ $input['strict_counts'] = isset( $input['strict_counts'] );
724
 
725
+ // deactivation delete
726
+ $input['deactivation_delete'] = isset( $input['deactivation_delete'] );
 
 
 
 
 
 
 
 
 
 
 
 
727
 
728
+ $input['update_version'] = $pvc->options['general']['update_version'];
729
+ $input['update_notice'] = $pvc->options['general']['update_notice'];
730
+ } elseif ( isset( $_POST['save_pvc_display'] ) ) {
 
 
 
 
 
731
 
732
+ // post views label
733
+ $input['label'] = isset( $input['label'] ) ? $input['label'] : $pvc->defaults['general']['label'];
734
 
735
+ if ( function_exists( 'icl_register_string' ) )
736
+ icl_register_string( 'Post Views Counter', 'Post Views Label', $input['label'] );
 
737
 
738
+ // position
739
+ $input['position'] = isset( $input['position'], $this->positions[$input['position']] ) ? $input['position'] : $pvc->defaults['general']['position'];
 
740
 
741
+ // display style
742
+ $input['display_style']['icon'] = isset( $input['display_style']['icon'] );
743
+ $input['display_style']['text'] = isset( $input['display_style']['text'] );
744
 
745
+ // link to post
746
+ $input['link_to_post'] = isset( $input['link_to_post'] ) ? $input['link_to_post'] : $pvc->defaults['display']['link_to_post'];
 
 
747
 
748
+ // icon class
749
+ $input['icon_class'] = isset( $input['icon_class'] ) ? trim( $input['icon_class'] ) : $pvc->defaults['general']['icon_class'];
 
750
 
751
+ // post types display
752
+ if ( isset( $input['post_types_display'] ) ) {
753
+ $post_types = array();
754
 
755
+ foreach ( $input['post_types_display'] as $post_type => $set ) {
756
+ if ( isset( $this->post_types[$post_type] ) )
757
+ $post_types[] = $post_type;
758
+ }
 
 
 
 
 
 
759
 
760
+ $input['post_types_display'] = array_unique( $post_types );
761
+ } else
762
+ $input['post_types_display'] = array();
763
 
764
+ // page types display
765
+ if ( isset( $input['page_types_display'] ) ) {
766
+ $page_types = array();
767
 
768
+ foreach ( $input['page_types_display'] as $page_type => $set ) {
769
+ if ( isset( $this->page_types[$page_type] ) )
770
+ $page_types[] = $page_type;
771
+ }
772
+
773
+ $input['page_types_display'] = array_unique( $page_types );
774
+ } else
775
+ $input['page_types_display'] = array();
776
 
777
+ // restrict display
778
+ if ( isset( $input['restrict_display']['groups'] ) ) {
779
+ $groups = array();
780
 
781
+ foreach ( $input['restrict_display']['groups'] as $group => $set ) {
782
+ if ( $group === 'robots' )
783
+ continue;
784
 
785
+ if ( isset( $this->groups[$group] ) )
786
+ $groups[] = $group;
787
+ }
788
+
789
+ $input['restrict_display']['groups'] = array_unique( $groups );
790
+ } else
791
+ $input['restrict_display']['groups'] = array();
792
+
793
+ if ( in_array( 'roles', $input['restrict_display']['groups'], true ) && isset( $input['restrict_display']['roles'] ) ) {
794
+ $roles = array();
795
+
796
+ foreach ( $input['restrict_display']['roles'] as $role => $set ) {
797
+ if ( isset( $this->user_roles[$role] ) )
798
+ $roles[] = $role;
799
+ }
800
 
801
+ $input['restrict_display']['roles'] = array_unique( $roles );
802
+ } else
803
+ $input['restrict_display']['roles'] = array();
804
+ } elseif ( isset( $_POST['reset_pvc_general'] ) ) {
805
+ $input = $pvc->defaults['general'];
806
+
807
+ add_settings_error( 'reset_general_settings', 'settings_reset', __( 'General settings restored to defaults.', 'post-views-counter' ), 'updated' );
808
+ } elseif ( isset( $_POST['reset_pvc_display'] ) ) {
809
+ $input = $pvc->defaults['display'];
810
+
811
+ add_settings_error( 'reset_general_settings', 'settings_reset', __( 'Display settings restored to defaults.', 'post-views-counter' ), 'updated' );
812
+ }
813
 
814
  return $input;
815
  }
includes/widgets.php CHANGED
@@ -55,7 +55,7 @@ class Post_Views_Counter_List_Widget extends WP_Widget {
55
  'show_post_thumbnail' => false,
56
  'show_post_excerpt' => false,
57
  'show_post_author' => false,
58
- 'no_posts_message' => __( 'No most viewed posts found', 'post-views-counter' )
59
  );
60
 
61
  $this->pvc_order_types = array(
55
  'show_post_thumbnail' => false,
56
  'show_post_excerpt' => false,
57
  'show_post_author' => false,
58
+ 'no_posts_message' => __( 'No Posts found', 'post-views-counter' )
59
  );
60
 
61
  $this->pvc_order_types = array(
js/admin-settings.js CHANGED
@@ -1,65 +1,65 @@
1
- ( function( $ ) {
2
-
3
- // ready event
4
- $( function() {
5
- var ip_boxes = $( '#post_views_counter_general_exclude_ips_setting' ).find( '.ip-box' ).length;
6
-
7
- $( '#post_views_counter_general_exclude_ips_setting .ip-box:first' ).find( '.remove-exclude-ip' ).hide();
8
-
9
- // ask whether to reset options to defaults
10
- $( document ).on( 'click', '.reset_pvc_settings', function() {
11
- return confirm( pvcArgsSettings.resetToDefaults );
12
- } );
13
-
14
- // ask whether to reset views
15
- $( document ).on( 'click', 'input[name="post_views_counter_reset_views"]', function() {
16
- return confirm( pvcArgsSettings.resetViews );
17
- } );
18
-
19
- // remove ip box
20
- $( document ).on( 'click', '.remove-exclude-ip', function( e ) {
21
- e.preventDefault();
22
-
23
- ip_boxes--;
24
-
25
- var parent = $( this ).parent();
26
-
27
- // remove ip box
28
- parent.slideUp( 'fast', function() {
29
- $( this ).remove();
30
- } );
31
- } );
32
-
33
- // add ip box
34
- $( document ).on( 'click', '.add-exclude-ip', function() {
35
- ip_boxes++;
36
-
37
- var parent = $( this ).parents( '#post_views_counter_general_exclude_ips_setting' ),
38
- new_ip_box = parent.find( '.ip-box:last' ).clone().hide();
39
-
40
- // clear value
41
- new_ip_box.find( 'input' ).val( '' );
42
-
43
- if ( ip_boxes > 1 )
44
- new_ip_box.find( '.remove-exclude-ip' ).show();
45
-
46
- // add and display new ip box
47
- parent.find( '.ip-box:last' ).after( new_ip_box ).next().slideDown( 'fast' );
48
- } );
49
-
50
- // add current ip
51
- $( document ).on( 'click', '.add-current-ip', function() {
52
- // fill input with user's current ip
53
- $( this ).parents( '#post_views_counter_general_exclude_ips_setting' ).find( '.ip-box' ).last().find( 'input' ).val( $( this ).attr( 'data-rel' ) );
54
- } );
55
-
56
- // toggle user roles
57
- $( '#pvc_exclude-roles, #pvc_restrict_display-roles' ).on( 'change', function() {
58
- if ( $( this ).is( ':checked' ) )
59
- $( '.pvc_user_roles' ).slideDown( 'fast' );
60
- else
61
- $( '.pvc_user_roles' ).slideUp( 'fast' );
62
- } );
63
- } );
64
-
65
  } )( jQuery );
1
+ ( function( $ ) {
2
+
3
+ // ready event
4
+ $( function() {
5
+ var ip_boxes = $( '#post_views_counter_general_exclude_ips_setting' ).find( '.ip-box' ).length;
6
+
7
+ $( '#post_views_counter_general_exclude_ips_setting .ip-box:first' ).find( '.remove-exclude-ip' ).hide();
8
+
9
+ // ask whether to reset options to defaults
10
+ $( document ).on( 'click', '.reset_pvc_settings', function() {
11
+ return confirm( pvcArgsSettings.resetToDefaults );
12
+ } );
13
+
14
+ // ask whether to reset views
15
+ $( document ).on( 'click', 'input[name="post_views_counter_reset_views"]', function() {
16
+ return confirm( pvcArgsSettings.resetViews );
17
+ } );
18
+
19
+ // remove ip box
20
+ $( document ).on( 'click', '.remove-exclude-ip', function( e ) {
21
+ e.preventDefault();
22
+
23
+ ip_boxes--;
24
+
25
+ var parent = $( this ).parent();
26
+
27
+ // remove ip box
28
+ parent.slideUp( 'fast', function() {
29
+ $( this ).remove();
30
+ } );
31
+ } );
32
+
33
+ // add ip box
34
+ $( document ).on( 'click', '.add-exclude-ip', function() {
35
+ ip_boxes++;
36
+
37
+ var parent = $( this ).parents( '#post_views_counter_general_exclude_ips_setting' ),
38
+ new_ip_box = parent.find( '.ip-box:last' ).clone().hide();
39
+
40
+ // clear value
41
+ new_ip_box.find( 'input' ).val( '' );
42
+
43
+ if ( ip_boxes > 1 )
44
+ new_ip_box.find( '.remove-exclude-ip' ).show();
45
+
46
+ // add and display new ip box
47
+ parent.find( '.ip-box:last' ).after( new_ip_box ).next().slideDown( 'fast' );
48
+ } );
49
+
50
+ // add current ip
51
+ $( document ).on( 'click', '.add-current-ip', function() {
52
+ // fill input with user's current ip
53
+ $( this ).parents( '#post_views_counter_general_exclude_ips_setting' ).find( '.ip-box' ).last().find( 'input' ).val( $( this ).attr( 'data-rel' ) );
54
+ } );
55
+
56
+ // toggle user roles
57
+ $( '#pvc_exclude-roles, #pvc_restrict_display-roles' ).on( 'change', function() {
58
+ if ( $( this ).is( ':checked' ) )
59
+ $( '.pvc_user_roles' ).slideDown( 'fast' );
60
+ else
61
+ $( '.pvc_user_roles' ).slideUp( 'fast' );
62
+ } );
63
+ } );
64
+
65
  } )( jQuery );
js/block-editor.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(n){var i={};function o(e){if(i[e])return i[e].exports;var t=i[e]={i:e,l:!1,exports:{}};return n[e].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.m=n,o.c=i,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},o.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)o.d(n,i,function(e){return t[e]}.bind(null,i));return n},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},o.p="",o(o.s=0)}([function(e,t){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})(e)}function r(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}function s(e,t){return(s=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function l(n){var i=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(e){return!1}}();return function(){var e,t=a(n);return function(e,t){{if(t&&("object"===o(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined")}return c(e)}(this,i?(e=a(this).constructor,Reflect.construct(t,arguments,e)):t.apply(this,arguments))}}function c(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function a(e){return(a=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}var n=wp.element,i=n.Fragment,u=n.Component,p=wp.data.withSelect,d=wp.plugins.registerPlugin,n=wp.components,f=n.TextControl,w=n.Button,m=n.Popover,v=wp.editPost.PluginPostStatusInfo,n=function(){!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}}),t&&s(e,t)}(o,u);var e,t,n,i=l(o);function o(){var e;return function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,o),(e=i.apply(this,arguments)).state={postViews:pvcEditorArgs.postViews,isVisible:!1},e.handleClick=e.handleClick.bind(c(e)),e.handleClickOutside=e.handleClickOutside.bind(c(e)),e.handleCancel=e.handleCancel.bind(c(e)),e.handleSetViews=e.handleSetViews.bind(c(e)),e}return e=o,n=[{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})}}],(t=[{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(h,{postViews:this.state.postViews,isVisible:this.state.isVisible,handleClick:this.handleClick,handleClickOutside:this.handleClickOutside,handleCancel:this.handleCancel,handleSetViews:this.handleSetViews})}}])&&r(e.prototype,t),n&&r(e,n),o}(),h=function(e){return wp.element.createElement(i,null,wp.element.createElement(v,{className:"edit-post-post-views"},wp.element.createElement("span",null,pvcEditorArgs.textPostViews),!pvcEditorArgs.canEdit&&wp.element.createElement("span",null,e.postViews),pvcEditorArgs.canEdit&&wp.element.createElement(w,{isLink:!0,className:"edit-post-post-views-toggle-link",onClick:e.handleClick},e.postViews,e.isVisible&&(pvcEditorArgs.wpGreater53?wp.element.createElement(m,{position:"bottom right",className:"edit-post-post-views-popover",onFocusOutside:e.handleClickOutside},wp.element.createElement("legend",null,pvcEditorArgs.textPostViews),wp.element.createElement(f,{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(w,{isLink:!0,className:"edit-post-post-views-cancel-link",onClick:e.handleCancel},pvcEditorArgs.textCancel)):wp.element.createElement(m,{position:"bottom right",className:"edit-post-post-views-popover",onClickOutside:e.handleClickOutside},wp.element.createElement("legend",null,pvcEditorArgs.textPostViews),wp.element.createElement(f,{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(w,{isLink:!0,className:"edit-post-post-views-cancel-link",onClick:e.handleCancel},pvcEditorArgs.textCancel))))))};d("post-views-counter",{icon:"",render:p(function(e,t){var n=t.forceIsSaving,i=e("core/editor"),o=i.getCurrentPostId,t=i.isSavingPost,e=i.isPublishingPost,i=i.isAutosavingPost;return{postId:o(),isSaving:n||t(),isAutoSaving:i(),isPublishing:e()}})(n)})}]);
js/frontend.js CHANGED
@@ -3,7 +3,7 @@
3
  // ready event
4
  $( function() {
5
  // rest api request
6
- if ( pvcArgsFrontend.mode == 'rest_api' ) {
7
  var request = {
8
  id: pvcArgsFrontend.postID
9
  };
@@ -24,7 +24,7 @@
24
  detail: response
25
  } );
26
  } );
27
- // admin ajax or fast ajax request
28
  } else {
29
  var request = {
30
  action: 'pvc-check-post',
3
  // ready event
4
  $( function() {
5
  // rest api request
6
+ if ( pvcArgsFrontend.mode === 'rest_api' ) {
7
  var request = {
8
  id: pvcArgsFrontend.postID
9
  };
24
  detail: response
25
  } );
26
  } );
27
+ // admin ajax request
28
  } else {
29
  var request = {
30
  action: 'pvc-check-post',
post-views-counter.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: Post Views Counter
4
  Description: Post Views Counter allows you to display how many times a post, page or custom post type had been viewed in a simple, fast and reliable way.
5
- Version: 1.3.7
6
  Author: Digital Factory
7
  Author URI: http://www.dfactory.eu/
8
  Plugin URI: http://www.dfactory.eu/plugins/post-views-counter/
@@ -25,13 +25,13 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
25
  if ( ! defined( 'ABSPATH' ) )
26
  exit;
27
 
28
- if ( ! class_exists( 'Post_Views_Counter' ) ) :
29
 
30
  /**
31
  * Post Views Counter final class.
32
  *
33
  * @class Post_Views_Counter
34
- * @version 1.3.7
35
  */
36
  final class Post_Views_Counter {
37
 
@@ -84,7 +84,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
84
  'icon_class' => 'dashicons-chart-bar',
85
  'toolbar_statistics' => true
86
  ],
87
- 'version' => '1.3.7'
88
  ];
89
 
90
  /**
@@ -102,8 +102,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
102
  public function __wakeup() {}
103
 
104
  /**
105
- * Main plugin instance,
106
- * Insures that only one instance of the plugin exists in memory at one time.
107
  *
108
  * @return object
109
  */
@@ -112,43 +111,34 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
112
  self::$instance = new Post_Views_Counter;
113
  self::$instance->define_constants();
114
 
115
- // minimal setup for Fast AJAX
116
- if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
117
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/counter.php' );
118
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/crawler-detect.php' );
119
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/functions.php' );
120
-
121
- self::$instance->counter = new Post_Views_Counter_Counter();
122
- self::$instance->crawler_detect = new Post_Views_Counter_Crawler_Detect();
123
- // regular setup
124
- } else {
125
- add_action( 'plugins_loaded', [ self::$instance, 'load_textdomain' ] );
126
-
127
- self::$instance->includes();
128
-
129
- // create settings API
130
- self::$instance->settings_api = new Post_Views_Counter_Settings_API(
131
- [
132
- 'object' => self::$instance,
133
- 'prefix' => 'post_views_counter',
134
- 'slug' => 'post-views-counter',
135
- 'domain' => 'post-views-counter',
136
- 'plugin' => 'Post Views Counter',
137
- 'plugin_url' => 'POST_VIEWS_COUNTER_URL'
138
- ]
139
- );
140
-
141
- self::$instance->update = new Post_Views_Counter_Update();
142
- self::$instance->settings = new Post_Views_Counter_Settings();
143
- self::$instance->query = new Post_Views_Counter_Query();
144
- self::$instance->cron = new Post_Views_Counter_Cron();
145
- self::$instance->counter = new Post_Views_Counter_Counter();
146
- self::$instance->columns = new Post_Views_Counter_Columns();
147
- self::$instance->crawler_detect = new Post_Views_Counter_Crawler_Detect();
148
- self::$instance->frontend = new Post_Views_Counter_Frontend();
149
- self::$instance->dashboard = new Post_Views_Counter_Dashboard();
150
- self::$instance->widgets = new Post_Views_Counter_Widgets();
151
- }
152
  }
153
 
154
  return self::$instance;
@@ -160,12 +150,8 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
160
  * @return void
161
  */
162
  private function define_constants() {
163
- // fix plugin_basename empty $wp_plugin_paths var
164
- if ( ! ( defined( 'SHORTINIT' ) && SHORTINIT ) ) {
165
- define( 'POST_VIEWS_COUNTER_URL', plugins_url( '', __FILE__ ) );
166
- define( 'POST_VIEWS_COUNTER_REL_PATH', dirname( plugin_basename( __FILE__ ) ) . '/' );
167
- }
168
-
169
  define( 'POST_VIEWS_COUNTER_PATH', plugin_dir_path( __FILE__ ) );
170
  }
171
 
@@ -175,51 +161,45 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
175
  * @return void
176
  */
177
  private function includes() {
178
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/update.php' );
179
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/settings-api.php' );
180
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/settings.php' );
181
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/columns.php' );
182
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/query.php' );
183
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/cron.php' );
184
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/counter.php' );
185
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/crawler-detect.php' );
186
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/frontend.php' );
187
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/dashboard.php' );
188
- include_once( POST_VIEWS_COUNTER_PATH . 'includes/widgets.php' );
 
 
189
  }
190
 
191
  /**
192
  * Class constructor.
193
- *
194
  * @return void
195
  */
196
  public function __construct() {
197
- if ( defined( 'SHORTINIT' ) && SHORTINIT ) {
198
- $this->options = [
199
- 'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
200
- 'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) )
201
- ];
202
- } else {
203
- register_activation_hook( __FILE__, [ $this, 'multisite_activation' ] );
204
- register_deactivation_hook( __FILE__, [ $this, 'multisite_deactivation' ] );
205
-
206
- // settings
207
- $this->options = [
208
- 'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
209
- 'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) )
210
- ];
211
-
212
- // actions
213
- add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
214
- add_action( 'wp_loaded', [ $this, 'load_pluggable_functions' ], 10 );
215
- // add_action( 'init', [ $this, 'gutenberg_blocks' ] );
216
- add_action( 'admin_init', [ $this, 'update_notice' ] );
217
- add_action( 'wp_ajax_pvc_dismiss_notice', [ $this, 'dismiss_notice' ] );
218
-
219
- // filters
220
- add_filter( 'plugin_row_meta', [ $this, 'plugin_row_meta' ], 10, 2 );
221
- add_filter( 'plugin_action_links', [ $this, 'plugin_action_links' ], 10, 2 );
222
- }
223
  }
224
 
225
  /**
@@ -279,7 +259,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
279
 
280
  /**
281
  * Add admin notices.
282
- *
283
  * @param string $html
284
  * @param string $status
285
  * @param bool $paragraph
@@ -395,7 +375,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
395
 
396
  /**
397
  * Multisite activation.
398
- *
399
  * @global object $wpdb
400
  * @param bool $networkwide
401
  * @return void
@@ -422,7 +402,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
422
 
423
  /**
424
  * Single site activation.
425
- *
426
  * @global array $wp_roles
427
  * @return void
428
  */
@@ -435,10 +415,10 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
435
  // create post views table
436
  dbDelta( '
437
  CREATE TABLE IF NOT EXISTS ' . $wpdb->prefix . 'post_views (
438
- id bigint unsigned NOT NULL,
439
- type tinyint(1) unsigned NOT NULL,
440
- period varchar(8) NOT NULL,
441
- count bigint unsigned NOT NULL,
442
  PRIMARY KEY (type, period, id),
443
  UNIQUE INDEX id_type_period_count (id, type, period, count) USING BTREE,
444
  INDEX type_period_count (type, period, count) USING BTREE
@@ -456,8 +436,8 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
456
 
457
  /**
458
  * Multisite deactivation.
459
- *
460
- * @global array $wpdb
461
  * @param bool $networkwide
462
  * @return void
463
  */
@@ -487,8 +467,8 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
487
 
488
  /**
489
  * Single site deactivation.
490
- *
491
- * @global array $wp_roles
492
  * @param bool $multi
493
  * @return void
494
  */
@@ -519,7 +499,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
519
 
520
  /**
521
  * Schedule cache flushing if it's not already scheduled.
522
- *
523
  * @param bool $forced
524
  * @return void
525
  */
@@ -539,7 +519,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
539
  }
540
 
541
  /**
542
- * Load text domain..
543
  *
544
  * @return void
545
  */
@@ -548,7 +528,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
548
  }
549
 
550
  /**
551
- * Load pluggable template functions..
552
  *
553
  * @return void
554
  */
@@ -556,20 +536,9 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
556
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/functions.php' );
557
  }
558
 
559
- /**
560
- * Add Gutenberg blocks..
561
- *
562
- * @return void
563
- */
564
- public function gutenberg_blocks() {
565
- wp_register_script( 'pvc-admin-block-views', POST_VIEWS_COUNTER_URL . '/js/admin-block.js', [ 'wp-blocks', 'wp-element', 'wp-i18n' ] );
566
-
567
- register_block_type( 'post-views-counter/views', [ 'editor_script' => 'pvc-admin-block-views' ] );
568
- }
569
-
570
  /**
571
  * Enqueue admin scripts and styles.
572
- *
573
  * @global string $post_type
574
  * @param string $page.
575
  * @return void
@@ -629,7 +598,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
629
 
630
  /**
631
  * Add links to plugin support forum.
632
- *
633
  * @param array $links
634
  * @param string $file
635
  * @return array
@@ -654,7 +623,7 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
654
 
655
  /**
656
  * Add link to settings page.
657
- *
658
  * @staticvar string $plugin
659
  * @param array $links
660
  * @param string $file
@@ -677,12 +646,11 @@ if ( ! class_exists( 'Post_Views_Counter' ) ) :
677
  return $links;
678
  }
679
  }
680
-
681
- endif; // end if class_exists check
682
 
683
  /**
684
  * Initialise Post Views Counter.
685
- *
686
  * @return object
687
  */
688
  function Post_Views_Counter() {
@@ -695,4 +663,4 @@ function Post_Views_Counter() {
695
  return $instance;
696
  }
697
 
698
- Post_Views_Counter();
2
  /*
3
  Plugin Name: Post Views Counter
4
  Description: Post Views Counter allows you to display how many times a post, page or custom post type had been viewed in a simple, fast and reliable way.
5
+ Version: 1.3.8
6
  Author: Digital Factory
7
  Author URI: http://www.dfactory.eu/
8
  Plugin URI: http://www.dfactory.eu/plugins/post-views-counter/
25
  if ( ! defined( 'ABSPATH' ) )
26
  exit;
27
 
28
+ if ( ! class_exists( 'Post_Views_Counter' ) ) {
29
 
30
  /**
31
  * Post Views Counter final class.
32
  *
33
  * @class Post_Views_Counter
34
+ * @version 1.3.8
35
  */
36
  final class Post_Views_Counter {
37
 
84
  'icon_class' => 'dashicons-chart-bar',
85
  'toolbar_statistics' => true
86
  ],
87
+ 'version' => '1.3.8'
88
  ];
89
 
90
  /**
102
  public function __wakeup() {}
103
 
104
  /**
105
+ * Main plugin instance, insures that only one instance of the plugin exists in memory at one time.
 
106
  *
107
  * @return object
108
  */
111
  self::$instance = new Post_Views_Counter;
112
  self::$instance->define_constants();
113
 
114
+ add_action( 'plugins_loaded', [ self::$instance, 'load_textdomain' ] );
115
+
116
+ self::$instance->includes();
117
+
118
+ // create settings API
119
+ self::$instance->settings_api = new Post_Views_Counter_Settings_API(
120
+ [
121
+ 'object' => self::$instance,
122
+ 'prefix' => 'post_views_counter',
123
+ 'slug' => 'post-views-counter',
124
+ 'domain' => 'post-views-counter',
125
+ 'plugin' => 'Post Views Counter',
126
+ 'plugin_url' => 'POST_VIEWS_COUNTER_URL'
127
+ ]
128
+ );
129
+
130
+ self::$instance->functions = new Post_Views_Counter_Functions();
131
+ self::$instance->update = new Post_Views_Counter_Update();
132
+ self::$instance->settings = new Post_Views_Counter_Settings();
133
+ self::$instance->admin = new Post_Views_Counter_Admin();
134
+ self::$instance->query = new Post_Views_Counter_Query();
135
+ self::$instance->cron = new Post_Views_Counter_Cron();
136
+ self::$instance->counter = new Post_Views_Counter_Counter();
137
+ self::$instance->columns = new Post_Views_Counter_Columns();
138
+ self::$instance->crawler_detect = new Post_Views_Counter_Crawler_Detect();
139
+ self::$instance->frontend = new Post_Views_Counter_Frontend();
140
+ self::$instance->dashboard = new Post_Views_Counter_Dashboard();
141
+ self::$instance->widgets = new Post_Views_Counter_Widgets();
 
 
 
 
 
 
 
 
 
142
  }
143
 
144
  return self::$instance;
150
  * @return void
151
  */
152
  private function define_constants() {
153
+ define( 'POST_VIEWS_COUNTER_URL', plugins_url( '', __FILE__ ) );
154
+ define( 'POST_VIEWS_COUNTER_REL_PATH', dirname( plugin_basename( __FILE__ ) ) . '/' );
 
 
 
 
155
  define( 'POST_VIEWS_COUNTER_PATH', plugin_dir_path( __FILE__ ) );
156
  }
157
 
161
  * @return void
162
  */
163
  private function includes() {
164
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-functions.php' );
165
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-update.php' );
166
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings-api.php' );
167
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-settings.php' );
168
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-admin.php' );
169
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-columns.php' );
170
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-query.php' );
171
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-cron.php' );
172
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-counter.php' );
173
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-crawler-detect.php' );
174
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-frontend.php' );
175
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-dashboard.php' );
176
+ include_once( POST_VIEWS_COUNTER_PATH . 'includes/class-widgets.php' );
177
  }
178
 
179
  /**
180
  * Class constructor.
181
+ *
182
  * @return void
183
  */
184
  public function __construct() {
185
+ register_activation_hook( __FILE__, [ $this, 'multisite_activation' ] );
186
+ register_deactivation_hook( __FILE__, [ $this, 'multisite_deactivation' ] );
187
+
188
+ // settings
189
+ $this->options = [
190
+ 'general' => array_merge( $this->defaults['general'], get_option( 'post_views_counter_settings_general', $this->defaults['general'] ) ),
191
+ 'display' => array_merge( $this->defaults['display'], get_option( 'post_views_counter_settings_display', $this->defaults['display'] ) )
192
+ ];
193
+
194
+ // actions
195
+ add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
196
+ add_action( 'wp_loaded', [ $this, 'load_pluggable_functions' ], 10 );
197
+ add_action( 'admin_init', [ $this, 'update_notice' ] );
198
+ add_action( 'wp_ajax_pvc_dismiss_notice', [ $this, 'dismiss_notice' ] );
199
+
200
+ // filters
201
+ add_filter( 'plugin_row_meta', [ $this, 'plugin_row_meta' ], 10, 2 );
202
+ add_filter( 'plugin_action_links', [ $this, 'plugin_action_links' ], 10, 2 );
 
 
 
 
 
 
 
 
203
  }
204
 
205
  /**
259
 
260
  /**
261
  * Add admin notices.
262
+ *
263
  * @param string $html
264
  * @param string $status
265
  * @param bool $paragraph
375
 
376
  /**
377
  * Multisite activation.
378
+ *
379
  * @global object $wpdb
380
  * @param bool $networkwide
381
  * @return void
402
 
403
  /**
404
  * Single site activation.
405
+ *
406
  * @global array $wp_roles
407
  * @return void
408
  */
415
  // create post views table
416
  dbDelta( '
417
  CREATE TABLE IF NOT EXISTS ' . $wpdb->prefix . 'post_views (
418
+ `id` bigint unsigned NOT NULL,
419
+ `type` tinyint(1) unsigned NOT NULL,
420
+ `period` varchar(8) NOT NULL,
421
+ `count` bigint unsigned NOT NULL,
422
  PRIMARY KEY (type, period, id),
423
  UNIQUE INDEX id_type_period_count (id, type, period, count) USING BTREE,
424
  INDEX type_period_count (type, period, count) USING BTREE
436
 
437
  /**
438
  * Multisite deactivation.
439
+ *
440
+ * @global $wpdb
441
  * @param bool $networkwide
442
  * @return void
443
  */
467
 
468
  /**
469
  * Single site deactivation.
470
+ *
471
+ * @global $wpdb
472
  * @param bool $multi
473
  * @return void
474
  */
499
 
500
  /**
501
  * Schedule cache flushing if it's not already scheduled.
502
+ *
503
  * @param bool $forced
504
  * @return void
505
  */
519
  }
520
 
521
  /**
522
+ * Load text domain.
523
  *
524
  * @return void
525
  */
528
  }
529
 
530
  /**
531
+ * Load pluggable template functions.
532
  *
533
  * @return void
534
  */
536
  include_once( POST_VIEWS_COUNTER_PATH . 'includes/functions.php' );
537
  }
538
 
 
 
 
 
 
 
 
 
 
 
 
539
  /**
540
  * Enqueue admin scripts and styles.
541
+ *
542
  * @global string $post_type
543
  * @param string $page.
544
  * @return void
598
 
599
  /**
600
  * Add links to plugin support forum.
601
+ *
602
  * @param array $links
603
  * @param string $file
604
  * @return array
623
 
624
  /**
625
  * Add link to settings page.
626
+ *
627
  * @staticvar string $plugin
628
  * @param array $links
629
  * @param string $file
646
  return $links;
647
  }
648
  }
649
+ }
 
650
 
651
  /**
652
  * Initialise Post Views Counter.
653
+ *
654
  * @return object
655
  */
656
  function Post_Views_Counter() {
663
  return $instance;
664
  }
665
 
666
+ Post_Views_Counter();
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: counter, hits, posts, postviews, post views, views, count, statistics, sta
5
  Requires at least: 5.0
6
  Requires PHP: 5.2.4
7
  Tested up to: 5.8.1
8
- Stable tag: 1.3.7
9
  License: MIT License
10
  License URI: http://opensource.org/licenses/MIT
11
 
@@ -20,7 +20,7 @@ For more information, check out plugin page at [dFactory](http://dfactory.eu/) o
20
  = Features include: =
21
 
22
  * Option to select post types for which post views will be counted and displayed.
23
- * 4 methods of collecting post views data: PHP, Javascript, Fast AJAX and REST API for greater flexibility
24
  * Compatible with data privacy regulations
25
  * Possibility to manually set views count for each post
26
  * Dashboard post views stats widget
@@ -62,6 +62,9 @@ No questions yet.
62
 
63
  == Changelog ==
64
 
 
 
 
65
  = 1.3.7 =
66
  * Tweak: Implemented internal settings API
67
 
@@ -222,5 +225,5 @@ Initial release
222
 
223
  == Upgrade Notice ==
224
 
225
- = 1.3.7 =
226
- * Tweak: Implemented internal settings API
5
  Requires at least: 5.0
6
  Requires PHP: 5.2.4
7
  Tested up to: 5.8.1
8
+ Stable tag: 1.3.8
9
  License: MIT License
10
  License URI: http://opensource.org/licenses/MIT
11
 
20
  = Features include: =
21
 
22
  * Option to select post types for which post views will be counted and displayed.
23
+ * 3 methods of collecting post views data: PHP, Javascript and REST API for greater flexibility
24
  * Compatible with data privacy regulations
25
  * Possibility to manually set views count for each post
26
  * Dashboard post views stats widget
62
 
63
  == Changelog ==
64
 
65
+ = 1.3.8 =
66
+ * Tweak: Improved user input escaping
67
+
68
  = 1.3.7 =
69
  * Tweak: Implemented internal settings API
70
 
225
 
226
  == Upgrade Notice ==
227
 
228
+ = 1.3.8 =
229
+ * Tweak: Improved user input escaping