Version Description
- Tweak: Improved user input escaping
Download this release
Release Info
Developer | dfactory |
Plugin | 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 +7 -7
- css/block-editor.min.css +1 -0
- includes/class-admin.php +149 -0
- includes/class-columns.php +499 -0
- includes/class-counter.php +903 -0
- includes/class-crawler-detect.php +969 -0
- includes/class-cron.php +127 -0
- includes/class-dashboard.php +628 -0
- includes/class-frontend.php +222 -0
- includes/class-functions.php +71 -0
- includes/class-query.php +379 -0
- includes/{settings-api.php → class-settings-api.php} +5 -1
- includes/class-settings.php +901 -0
- includes/class-update.php +128 -0
- includes/class-widgets.php +250 -0
- includes/columns.php +12 -181
- includes/counter.php +19 -112
- includes/dashboard.php +132 -321
- includes/functions.php +42 -32
- includes/settings.php +578 -679
- includes/widgets.php +1 -1
- js/admin-settings.js +64 -64
- js/block-editor.min.js +1 -0
- js/frontend.js +2 -2
- post-views-counter.php +87 -119
- readme.txt +7 -4
assets/chartjs/chart.min.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
-
/*!
|
2 |
-
* Chart.js v2.9.
|
3 |
-
* https://www.chartjs.org
|
4 |
-
* (c)
|
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, '
|
29 |
}
|
30 |
-
|
31 |
/**
|
32 |
* Init Gutenberg
|
33 |
*/
|
34 |
-
public function
|
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'
|
67 |
-
'callback'
|
68 |
-
'
|
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 |
-
|
|
|
|
|
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 =
|
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 |
-
|
744 |
}
|
745 |
}
|
746 |
}
|
747 |
|
748 |
-
return $
|
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
|
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'
|
833 |
-
'callback'
|
834 |
-
'
|
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' )
|
16 |
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts_styles' ) );
|
17 |
-
add_action( '
|
18 |
-
add_action( '
|
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
|
32 |
-
wp_add_dashboard_widget( 'pvc_dashboard', __( 'Post Views
|
33 |
}
|
34 |
-
|
35 |
/**
|
36 |
-
*
|
37 |
-
*
|
38 |
-
* @
|
39 |
*/
|
40 |
-
public function
|
41 |
-
|
42 |
-
|
|
|
|
|
43 |
|
44 |
-
|
45 |
-
if ( ! apply_filters( 'pvc_user_can_see_stats', current_user_can( 'publish_posts' ) ) )
|
46 |
-
return;
|
47 |
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
|
56 |
-
|
57 |
|
58 |
-
|
59 |
-
'
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
|
|
66 |
);
|
|
|
|
|
67 |
}
|
68 |
|
69 |
/**
|
70 |
-
*
|
71 |
-
*
|
72 |
-
* @return
|
73 |
*/
|
74 |
-
public function
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
// get
|
79 |
-
$
|
80 |
-
|
81 |
-
//
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
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
|
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( '
|
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
|
172 |
global $_wp_admin_css_colors;
|
173 |
|
174 |
-
|
175 |
-
$
|
176 |
-
|
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 |
-
|
242 |
-
|
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 |
-
$
|
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', $
|
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 |
-
|
335 |
-
|
336 |
-
|
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 |
-
*
|
482 |
*
|
483 |
-
* @param string $
|
484 |
-
* @return
|
485 |
*/
|
486 |
-
public function
|
487 |
-
$
|
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 |
-
|
|
|
494 |
|
495 |
-
if ( (
|
496 |
-
$
|
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 $
|
507 |
}
|
508 |
|
509 |
/**
|
510 |
-
*
|
511 |
*
|
512 |
-
* @
|
513 |
*/
|
514 |
-
public function
|
515 |
-
if (
|
516 |
-
|
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 |
-
|
574 |
-
|
|
|
575 |
|
576 |
-
|
577 |
-
|
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 |
-
|
586 |
-
|
587 |
|
588 |
-
|
589 |
-
$user_options[$data_type] = array();
|
590 |
|
591 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 =
|
67 |
-
$range =
|
68 |
-
$defaults =
|
69 |
'fields' => 'views',
|
70 |
'post_id' => '',
|
71 |
'post_type' => '',
|
72 |
-
'views_query' =>
|
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 =
|
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'],
|
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 |
-
|
128 |
$args['views_query']['inclusive'] = (bool) $args['views_query']['inclusive'];
|
129 |
|
130 |
// check after and before dates
|
131 |
-
foreach (
|
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[] =
|
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 (
|
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
|
379 |
* @param bool $display
|
380 |
-
* @return
|
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 =
|
432 |
$args = array_merge(
|
433 |
-
|
434 |
'posts_per_page' => 10,
|
435 |
'order' => 'desc',
|
436 |
'post_type' => 'post'
|
437 |
-
|
|
|
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 =
|
466 |
-
$defaults =
|
467 |
'number_of_posts' => 5,
|
468 |
-
'post_type' =>
|
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 |
-
|
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|
|
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 |
-
//
|
582 |
$post_views = apply_filters( 'pvc_update_post_views_count', $post_views, $post_id );
|
583 |
|
584 |
-
// insert or update
|
585 |
-
$wpdb->query(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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( '
|
22 |
-
add_action( '
|
23 |
-
|
24 |
-
|
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->
|
39 |
-
'
|
40 |
-
'
|
41 |
-
'
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
$this->
|
48 |
-
'
|
49 |
-
'
|
50 |
-
'
|
51 |
-
'
|
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'
|
60 |
-
'archive'
|
61 |
-
'singular'
|
62 |
-
'search'
|
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(
|
80 |
$post_types[$key] = $post_type->labels->name;
|
81 |
}
|
82 |
|
83 |
// public custom post types
|
84 |
-
foreach ( get_post_types(
|
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 |
-
|
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
|
122 |
*
|
123 |
-
* @
|
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
|
349 |
-
|
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 |
-
*
|
373 |
*
|
374 |
-
* @
|
375 |
-
* @return array
|
376 |
*/
|
377 |
-
public function
|
378 |
-
|
379 |
-
if ( ! current_user_can( 'manage_options' ) )
|
380 |
-
return $input;
|
381 |
|
382 |
-
|
383 |
-
|
|
|
|
|
384 |
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
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 |
-
|
396 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
397 |
|
398 |
-
|
399 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
400 |
|
401 |
-
|
402 |
-
|
403 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
404 |
|
405 |
-
|
406 |
-
|
407 |
-
|
|
|
|
|
|
|
408 |
|
409 |
-
|
|
|
|
|
|
|
410 |
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
420 |
|
421 |
-
|
422 |
-
|
423 |
-
|
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 |
-
|
|
|
|
|
438 |
}
|
439 |
|
440 |
/**
|
441 |
-
*
|
442 |
-
*
|
443 |
-
* @param array $input Input POST data
|
444 |
-
* @param array $field Field options
|
445 |
-
* @return array
|
446 |
*/
|
447 |
-
public function
|
448 |
-
|
449 |
-
|
450 |
|
451 |
-
|
452 |
-
$
|
453 |
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
if ( function_exists( 'icl_register_string' ) )
|
458 |
-
icl_register_string( 'Post Views Counter', 'Post Views Label', $input );
|
459 |
|
460 |
-
|
|
|
|
|
461 |
}
|
462 |
|
463 |
/**
|
464 |
-
*
|
465 |
-
*
|
466 |
-
* @param array $default Default value
|
467 |
-
* @param array $field Field options
|
468 |
-
* @return array
|
469 |
*/
|
470 |
-
public function
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
}
|
476 |
|
477 |
/**
|
478 |
-
*
|
479 |
-
*
|
480 |
-
* @param array $field Field options
|
481 |
-
* @return string
|
482 |
*/
|
483 |
-
public function
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
<input type="hidden" name="post_views_counter_settings_display[display_style]" value="empty" />';
|
489 |
|
490 |
-
foreach ( $
|
491 |
-
|
492 |
-
|
493 |
}
|
494 |
|
495 |
-
|
|
|
|
|
|
|
496 |
}
|
497 |
|
498 |
/**
|
499 |
-
*
|
500 |
-
*
|
501 |
-
* @param array $input Input POST data
|
502 |
-
* @param array $field Field options
|
503 |
-
* @return array
|
504 |
*/
|
505 |
-
public function
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
foreach ( $field['options'] as $value => $label ) {
|
512 |
-
$data[$value] = false;
|
513 |
-
}
|
514 |
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
if ( array_key_exists( $value, $field['options'] ) )
|
519 |
-
$data[$value] = true;
|
520 |
-
}
|
521 |
}
|
522 |
|
523 |
-
|
524 |
-
|
525 |
-
|
|
|
526 |
}
|
527 |
|
528 |
/**
|
529 |
-
*
|
530 |
-
*
|
531 |
-
* @param array $field Field options
|
532 |
-
* @return string
|
533 |
*/
|
534 |
-
public function
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
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 |
-
|
544 |
-
|
545 |
}
|
546 |
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
return $html;
|
552 |
}
|
553 |
|
554 |
/**
|
555 |
-
*
|
556 |
-
*
|
557 |
-
* @param array $input Input POST data
|
558 |
-
* @param array $field Field options
|
559 |
-
* @return array
|
560 |
*/
|
561 |
-
public function
|
562 |
-
|
563 |
-
|
|
|
564 |
|
565 |
-
|
566 |
-
|
|
|
|
|
567 |
|
568 |
-
|
569 |
-
|
|
|
570 |
|
571 |
-
|
572 |
-
|
|
|
|
|
573 |
|
574 |
-
|
|
|
|
|
|
|
575 |
}
|
576 |
|
577 |
/**
|
578 |
-
*
|
579 |
-
*
|
580 |
-
* @param array $field Field options
|
581 |
-
* @return string
|
582 |
*/
|
583 |
-
public function
|
584 |
-
//
|
585 |
-
$
|
586 |
|
587 |
-
|
588 |
-
|
589 |
-
<select class="pvc-chosen-short" name="post_views_counter_settings_general[reset_counts][type]">';
|
590 |
|
591 |
-
|
592 |
-
$
|
593 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
594 |
}
|
595 |
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
return $html;
|
601 |
}
|
602 |
|
603 |
/**
|
604 |
-
*
|
605 |
-
*
|
606 |
-
* @param array $input Input POST data
|
607 |
-
* @param array $field Field options
|
608 |
-
* @return array
|
609 |
*/
|
610 |
-
public function
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
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 |
-
|
627 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
628 |
|
629 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
630 |
}
|
631 |
|
632 |
/**
|
633 |
-
*
|
634 |
-
*
|
635 |
-
* @param array $field Field options
|
636 |
-
* @return string
|
637 |
*/
|
638 |
-
public function
|
639 |
-
|
640 |
-
|
|
|
|
|
|
|
641 |
|
642 |
-
|
643 |
-
|
644 |
-
|
|
|
|
|
|
|
645 |
|
646 |
-
foreach ( $this->
|
647 |
-
|
648 |
-
|
649 |
}
|
650 |
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
return $html;
|
656 |
}
|
657 |
|
658 |
/**
|
659 |
-
*
|
660 |
-
*
|
661 |
-
* @param array $input Input POST data
|
662 |
-
* @param array $field Field options
|
663 |
-
* @return array
|
664 |
*/
|
665 |
-
public function
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
|
|
|
|
|
|
671 |
|
672 |
-
|
673 |
-
|
|
|
|
|
|
|
674 |
|
675 |
-
|
676 |
-
|
|
|
|
|
|
|
|
|
677 |
|
678 |
-
|
679 |
-
|
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 |
-
|
686 |
-
|
|
|
687 |
|
688 |
-
|
|
|
|
|
689 |
}
|
690 |
|
691 |
/**
|
692 |
-
*
|
693 |
-
*
|
694 |
-
* @return string
|
695 |
*/
|
696 |
-
public function
|
697 |
-
|
698 |
-
|
|
|
|
|
|
|
|
|
699 |
|
700 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
701 |
|
702 |
foreach ( $this->groups as $type => $type_name ) {
|
703 |
-
|
704 |
-
|
|
|
|
|
|
|
|
|
705 |
}
|
706 |
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
|
711 |
foreach ( $this->user_roles as $role => $role_name ) {
|
712 |
-
|
713 |
-
|
714 |
}
|
715 |
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
}
|
722 |
|
723 |
/**
|
724 |
-
* Validate
|
725 |
-
*
|
726 |
-
* @param array $input Input POST data
|
727 |
-
* @param array $field Field options
|
728 |
-
* @return array
|
729 |
*/
|
730 |
-
public function
|
731 |
// get main instance
|
732 |
$pvc = Post_Views_Counter();
|
733 |
|
734 |
-
|
735 |
-
|
736 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
737 |
|
738 |
-
|
739 |
-
|
740 |
-
|
741 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
742 |
|
743 |
-
|
744 |
-
|
745 |
-
$input['exclude']['groups'] = [];
|
746 |
|
747 |
-
|
748 |
-
|
749 |
-
$roles = [];
|
750 |
|
751 |
-
|
752 |
-
|
753 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
754 |
}
|
755 |
|
756 |
-
|
757 |
-
|
758 |
-
$input['
|
759 |
|
760 |
-
|
761 |
-
|
|
|
762 |
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
* @return string
|
767 |
-
*/
|
768 |
-
public function setting_exclude_ips() {
|
769 |
-
$ips = Post_Views_Counter()->options['general']['exclude_ips'];
|
770 |
|
771 |
-
|
|
|
|
|
|
|
772 |
|
773 |
-
|
774 |
-
|
775 |
-
$
|
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 |
-
|
788 |
-
|
789 |
-
<p class="description">' . esc_html__( 'Enter the IP addresses to be excluded from post views count.', 'post-views-counter' ) . '</p>';
|
790 |
|
791 |
-
|
792 |
-
|
|
|
|
|
793 |
|
794 |
-
|
795 |
-
|
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 |
-
|
806 |
-
|
807 |
-
|
808 |
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
|
813 |
-
|
|
|
|
|
814 |
$ips[] = $ip;
|
815 |
-
}
|
816 |
-
|
|
|
817 |
}
|
818 |
|
819 |
-
|
820 |
-
|
821 |
|
822 |
-
|
823 |
-
|
824 |
|
825 |
-
|
826 |
-
|
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 |
-
|
842 |
-
|
843 |
-
* @return string
|
844 |
-
*/
|
845 |
-
public function setting_restrict_display() {
|
846 |
-
// get main instance
|
847 |
-
$pvc = Post_Views_Counter();
|
848 |
|
849 |
-
|
|
|
850 |
|
851 |
-
|
852 |
-
|
853 |
-
continue;
|
854 |
|
855 |
-
|
856 |
-
|
857 |
-
}
|
858 |
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
|
863 |
-
|
864 |
-
$
|
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 |
-
|
869 |
-
|
870 |
-
</div>';
|
871 |
|
872 |
-
|
873 |
-
|
|
|
874 |
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
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 |
-
|
887 |
-
|
888 |
-
|
889 |
|
890 |
-
|
891 |
-
|
892 |
-
|
893 |
|
894 |
-
|
895 |
-
$
|
896 |
-
|
|
|
|
|
|
|
|
|
|
|
897 |
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
|
902 |
-
|
903 |
-
|
904 |
-
|
905 |
|
906 |
-
|
907 |
-
|
908 |
-
|
909 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
910 |
|
911 |
-
|
912 |
-
|
913 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
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
|
7 |
var request = {
|
8 |
id: pvcArgsFrontend.postID
|
9 |
};
|
@@ -24,7 +24,7 @@
|
|
24 |
detail: response
|
25 |
} );
|
26 |
} );
|
27 |
-
// admin ajax
|
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.
|
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.
|
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.
|
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 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
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 |
-
|
164 |
-
|
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/
|
179 |
-
include_once( POST_VIEWS_COUNTER_PATH . 'includes/
|
180 |
-
include_once( POST_VIEWS_COUNTER_PATH . 'includes/settings.php' );
|
181 |
-
include_once( POST_VIEWS_COUNTER_PATH . 'includes/
|
182 |
-
include_once( POST_VIEWS_COUNTER_PATH . 'includes/
|
183 |
-
include_once( POST_VIEWS_COUNTER_PATH . 'includes/
|
184 |
-
include_once( POST_VIEWS_COUNTER_PATH . 'includes/
|
185 |
-
include_once( POST_VIEWS_COUNTER_PATH . 'includes/
|
186 |
-
include_once( POST_VIEWS_COUNTER_PATH . 'includes/
|
187 |
-
include_once( POST_VIEWS_COUNTER_PATH . 'includes/
|
188 |
-
include_once( POST_VIEWS_COUNTER_PATH . 'includes/
|
|
|
|
|
189 |
}
|
190 |
|
191 |
/**
|
192 |
* Class constructor.
|
193 |
-
*
|
194 |
* @return void
|
195 |
*/
|
196 |
public function __construct() {
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
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
|
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
|
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.
|
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 |
-
*
|
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.
|
226 |
-
* Tweak:
|
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
|