From c55d8d4073113c174f92888425cad42f7fabc70b Mon Sep 17 00:00:00 2001 From: wess Date: Wed, 22 Sep 2021 11:46:10 -0400 Subject: [PATCH 01/21] Adding Stripe OAuth for connected accountns --- app/config/providers.php | 10 ++ src/Appwrite/Auth/OAuth2/Stripe.php | 157 ++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 src/Appwrite/Auth/OAuth2/Stripe.php diff --git a/app/config/providers.php b/app/config/providers.php index 8525a4216a..b6e21d83ca 100644 --- a/app/config/providers.php +++ b/app/config/providers.php @@ -267,6 +267,16 @@ return [ // Ordered by ABC. 'beta' => false, 'mock' => false ], + 'stripe' => [ + 'name' => 'Stripe', + 'developers' => 'https://stripe.com/docs/api', + 'icon' => 'icon-stripe', + 'enabled' => true, + 'sandbox' => false, + 'form' => false, + 'beta' => false, + 'mock' => false + ], // Keep Last 'mock' => [ 'name' => 'Mock', diff --git a/src/Appwrite/Auth/OAuth2/Stripe.php b/src/Appwrite/Auth/OAuth2/Stripe.php new file mode 100644 index 0000000000..ddf028bb42 --- /dev/null +++ b/src/Appwrite/Auth/OAuth2/Stripe.php @@ -0,0 +1,157 @@ + 'authorization_code', + 'refresh' => 'refresh_token' + ]; + + /** + * @return string + */ + public function getName():string + { + return 'stripe'; + } + + /** + * @return string + */ + public function getLoginURL():string + { + return 'https://connect.stripe.com/oauth/authorize?'. \http_build_query([ + 'response_type' => 'code', // The only option at the moment is "code." + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'scope' => \implode(' ', $this->getScopes()), + 'state' => \json_encode($this->state) + ]); + } + + /** + * @param string $code + * + * @return string + */ + public function getAccessToken(string $code):string + { + $response = $this->request( + 'POST', + 'https://connect.stripe.com/oauth/token', + [], + \http_build_query([ + 'grant_type' => $this->grantType['authorize'], + 'code' => $code + ]) + ); + + $response = \json_decode($response, true); + + if (isset($response['stripe_user_id'])) { + $this->stripeAccountId = $response['stripe_user_id']; + } + + if (isset($response['access_token'])) { + return $response['access_token']; + } + + return ''; + } + + /** + * @param $accessToken + * + * @return string + */ + public function getUserID(string $accessToken):string + { + $user = $this->getUser($accessToken); + + if (isset($user['id'])) { + return $user['id']; + } + + return ''; + } + + /** + * @param $accessToken + * + * @return string + */ + public function getUserEmail(string $accessToken):string + { + $user = $this->getUser($accessToken); + + if(empty($user)) { + return ''; + } + + return $user['email'] ?? ''; + } + + /** + * @param $accessToken + * + * @return string + */ + public function getUserName(string $accessToken):string + { + $user = $this->getUser($accessToken); + + if (isset($user['name'])) { + return $user['name']; + } + + return ''; + } + + /** + * @param string $accessToken + * + * @return array + */ + protected function getUser(string $accessToken) + { + if (empty($this->user) && !empty($this->stripeAccountId)) { + $this->user = \json_decode( + $this->request( + 'GET', + 'https://api.stripe.com/v1/accounts/' . $this->stripeAccountId, + ['Authorization: Bearer '.\urlencode($accessToken)] + ), + true + ); + + + } + + return $this->user; + } +} From b288632a909d417a5b4d31119a6b81b2161efd93 Mon Sep 17 00:00:00 2001 From: wess Date: Mon, 13 Dec 2021 12:28:44 -0500 Subject: [PATCH 02/21] Add stripe logo --- public/images/users/stripe.png | Bin 0 -> 88532 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/images/users/stripe.png diff --git a/public/images/users/stripe.png b/public/images/users/stripe.png new file mode 100644 index 0000000000000000000000000000000000000000..b89c151a2f0ac38e01891074eac8879b01fc73ba GIT binary patch literal 88532 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 ljaowA7Bb(e*@ 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 Date: Mon, 17 Jan 2022 00:46:12 +0100 Subject: [PATCH 03/21] feat: :sparkles: Makes Microsoft OAuth Provider configureable --- app/config/providers.php | 2 +- app/views/console/users/index.phtml | 11 +- app/views/console/users/oauth/apple.phtml | 7 + app/views/console/users/oauth/microsoft.phtml | 12 + gulpfile.js | 1 + public/dist/scripts/app-all.js | 930 +++++++++--------- public/dist/scripts/app-dep.js | 926 ++++++++--------- public/dist/scripts/app.js | 4 + public/scripts/views/forms/oauth-microsoft.js | 50 + src/Appwrite/Auth/OAuth2/Microsoft.php | 41 +- 10 files changed, 1083 insertions(+), 901 deletions(-) create mode 100644 app/views/console/users/oauth/apple.phtml create mode 100644 app/views/console/users/oauth/microsoft.phtml create mode 100644 public/scripts/views/forms/oauth-microsoft.js diff --git a/app/config/providers.php b/app/config/providers.php index bb823c9ab0..c415405b6d 100644 --- a/app/config/providers.php +++ b/app/config/providers.php @@ -127,7 +127,7 @@ return [ // Ordered by ABC. 'icon' => 'icon-windows', 'enabled' => true, 'sandbox' => false, - 'form' => false, + 'form' => 'microsoft.phtml', 'beta' => false, 'mock' => false, ], diff --git a/app/views/console/users/index.phtml b/app/views/console/users/index.phtml index a290b6d68a..a5e0b939b4 100644 --- a/app/views/console/users/index.phtml +++ b/app/views/console/users/index.phtml @@ -1,4 +1,6 @@ getParam('providers', []); $auth = $this->getParam('auth', []); $smtpEnabled = $this->getParam('smtpEnabled', false); @@ -475,9 +477,12 @@ $smtpEnabled = $this->getParam('smtpEnabled', false); - - - + escape($form)); + echo $form_view + ->setParam("provider",$provider) + ->render(); + ?>
diff --git a/app/views/console/users/oauth/apple.phtml b/app/views/console/users/oauth/apple.phtml new file mode 100644 index 0000000000..d18f851ff8 --- /dev/null +++ b/app/views/console/users/oauth/apple.phtml @@ -0,0 +1,7 @@ +getParam('provider', ''); +?> + + + + \ No newline at end of file diff --git a/app/views/console/users/oauth/microsoft.phtml b/app/views/console/users/oauth/microsoft.phtml new file mode 100644 index 0000000000..201953c91a --- /dev/null +++ b/app/views/console/users/oauth/microsoft.phtml @@ -0,0 +1,12 @@ +getParam('provider', ''); +?> + + + + + + + + + \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index fd44420cb9..bd91cbe633 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -58,6 +58,7 @@ const configApp = { 'public/scripts/views/forms/move-up.js', 'public/scripts/views/forms/nav.js', 'public/scripts/views/forms/oauth-apple.js', + 'public/scripts/views/forms/oauth-microsoft.js', 'public/scripts/views/forms/password-meter.js', 'public/scripts/views/forms/pell.js', 'public/scripts/views/forms/required.js', diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index 8ff598d801..f9d8722eea 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -593,18 +593,18 @@ return output;}} exports.Appwrite=Appwrite;Object.defineProperty(exports,'__esModule',{value:true});}(this.window=this.window||{},null,window));(function(global,factory){typeof exports==='object'&&typeof module!=='undefined'?module.exports=factory():typeof define==='function'&&define.amd?define(factory):(global=typeof globalThis!=='undefined'?globalThis:global||self,global.Chart=factory());}(this,(function(){'use strict';function fontString(pixelSize,fontStyle,fontFamily){return fontStyle+' '+pixelSize+'px '+fontFamily;} const requestAnimFrame=(function(){if(typeof window==='undefined'){return function(callback){return callback();};} return window.requestAnimationFrame;}());function throttled(fn,thisArg,updateFn){const updateArgs=updateFn||((args)=>Array.prototype.slice.call(args));let ticking=false;let args=[];return function(...rest){args=updateArgs(rest);if(!ticking){ticking=true;requestAnimFrame.call(window,()=>{ticking=false;fn.apply(thisArg,args);});}};} -function debounce(fn,delay){let timeout;return function(){if(delay){clearTimeout(timeout);timeout=setTimeout(fn,delay);}else{fn();} +function debounce(fn,delay){let timeout;return function(...args){if(delay){clearTimeout(timeout);timeout=setTimeout(fn,delay,args);}else{fn.apply(this,args);} return delay;};} const _toLeftRightCenter=(align)=>align==='start'?'left':align==='end'?'right':'center';const _alignStartEnd=(align,start,end)=>align==='start'?start:align==='end'?end:(start+end)/2;const _textX=(align,left,right,rtl)=>{const check=rtl?'left':'right';return align===check?right:align==='center'?(left+right)/2:left;};class Animator{constructor(){this._request=null;this._charts=new Map();this._running=false;this._lastDate=undefined;} _notify(chart,anims,date,type){const callbacks=anims.listeners[type];const numSteps=anims.duration;callbacks.forEach(fn=>fn({chart,initial:anims.initial,numSteps,currentStep:Math.min(date-anims.start,numSteps)}));} -_refresh(){const me=this;if(me._request){return;} -me._running=true;me._request=requestAnimFrame.call(window,()=>{me._update();me._request=null;if(me._running){me._refresh();}});} -_update(date=Date.now()){const me=this;let remaining=0;me._charts.forEach((anims,chart)=>{if(!anims.running||!anims.items.length){return;} +_refresh(){if(this._request){return;} +this._running=true;this._request=requestAnimFrame.call(window,()=>{this._update();this._request=null;if(this._running){this._refresh();}});} +_update(date=Date.now()){let remaining=0;this._charts.forEach((anims,chart)=>{if(!anims.running||!anims.items.length){return;} const items=anims.items;let i=items.length-1;let draw=false;let item;for(;i>=0;--i){item=items[i];if(item._active){if(item._total>anims.duration){anims.duration=item._total;} item.tick(date);draw=true;}else{items[i]=items[items.length-1];items.pop();}} -if(draw){chart.draw();me._notify(chart,anims,date,'progress');} -if(!items.length){anims.running=false;me._notify(chart,anims,date,'complete');anims.initial=false;} -remaining+=items.length;});me._lastDate=date;if(remaining===0){me._running=false;}} +if(draw){chart.draw();this._notify(chart,anims,date,'progress');} +if(!items.length){anims.running=false;this._notify(chart,anims,date,'complete');anims.initial=false;} +remaining+=items.length;});this._lastDate=date;if(remaining===0){this._running=false;}} _getAnims(chart){const charts=this._charts;let anims=charts.get(chart);if(!anims){anims={running:false,initial:true,items:[],listeners:{complete:[],progress:[]}};charts.set(chart,anims);} return anims;} listen(chart,event,cb){this._getAnims(chart).listeners[event].push(cb);} @@ -757,6 +757,7 @@ function _normalizeAngle(a){return(a%TAU+TAU)%TAU;} function _angleBetween(angle,start,end,sameAngleIsFullCircle){const a=_normalizeAngle(angle);const s=_normalizeAngle(start);const e=_normalizeAngle(end);const angleToStart=_normalizeAngle(s-a);const angleToEnd=_normalizeAngle(e-a);const startToAngle=_normalizeAngle(a-s);const endToAngle=_normalizeAngle(a-e);return a===s||a===e||(sameAngleIsFullCircle&&s===e)||(angleToStart>angleToEnd&&startToAngle=Math.min(start,end)-epsilon&&value<=Math.max(start,end)+epsilon;} function toFontString(font){if(!font||isNullOrUndef(font.size)||isNullOrUndef(font.family)){return null;} return(font.style?font.style+' ':'') +(font.weight?font.weight+' ':'') @@ -851,7 +852,7 @@ if(element.inRange(position.x,position.y,useFinalPosition)){intersectsItem=true; return items;} var Interaction={modes:{index(chart,e,options,useFinalPosition){const position=getRelativePosition(e,chart);const axis=options.axis||'x';const items=options.intersect?getIntersectItems(chart,position,axis,useFinalPosition):getNearestItems(chart,position,axis,false,useFinalPosition);const elements=[];if(!items.length){return[];} chart.getSortedVisibleDatasetMetas().forEach((meta)=>{const index=items[0].index;const element=meta.data[index];if(element&&!element.skip){elements.push({element,datasetIndex:meta.index,index});}});return elements;},dataset(chart,e,options,useFinalPosition){const position=getRelativePosition(e,chart);const axis=options.axis||'xy';let items=options.intersect?getIntersectItems(chart,position,axis,useFinalPosition):getNearestItems(chart,position,axis,false,useFinalPosition);if(items.length>0){const datasetIndex=items[0].datasetIndex;const data=chart.getDatasetMeta(datasetIndex).data;items=[];for(let i=0;i+v||0;function _readValueToProps(value,props){const ret={};const objProps=isObject(props);const keys=objProps?Object.keys(props):props;const read=isObject(value)?objProps?prop=>valueOrDefault(value[prop],value[props[prop]]):prop=>value[prop]:()=>value;for(const prop of keys){ret[prop]=numberOrZero$1(read(prop));} @@ -867,7 +868,8 @@ if(context!==undefined&&typeof value==='function'){value=value(context);cacheabl if(index!==undefined&&isArray(value)){value=value[index%value.length];cacheable=false;} if(value!==undefined){if(info&&!cacheable){info.cacheable=false;} return value;}}} -function _addGrace(minmax,grace){const{min,max}=minmax;return{min:min-Math.abs(toDimension(grace,min)),max:max+toDimension(grace,max)};} +function _addGrace(minmax,grace,beginAtZero){const{min,max}=minmax;const change=toDimension(grace,(max-min)/2);const keepZero=(value,add)=>beginAtZero&&value===0?0:value+add;return{min:keepZero(min,-Math.abs(change)),max:keepZero(max,change)};} +function createContext(parentContext,context){return Object.assign(Object.create(parentContext),context);} const STATIC_POSITIONS=['left','top','right','bottom'];function filterByPosition(array,position){return array.filter(v=>v.pos===position);} function filterDynamicPositionByAxis(array,axis){return array.filter(v=>STATIC_POSITIONS.indexOf(v.pos)===-1&&v.box.axis===axis);} function sortByWeight(array,reverse){return array.sort((a,b)=>{const v0=reverse?b:a;const v1=reverse?a:b;return v0.weight===v1.weight?v0.index-v1.index:v0.weight-v1.weight;});} @@ -898,22 +900,21 @@ stack.start=y;stack.placed+=width;y=box.bottom;}else{const height=chartArea.h*we if(box.fullSize){setBoxDims(box,x,userPadding.top,width,params.outerHeight-userPadding.bottom-userPadding.top);}else{setBoxDims(box,x,chartArea.top+stack.placed,width,height);} stack.start=x;stack.placed+=height;x=box.right;}} chartArea.x=x;chartArea.y=y;} -defaults.set('layout',{padding:{top:0,right:0,bottom:0,left:0}});var layouts={addBox(chart,item){if(!chart.boxes){chart.boxes=[];} +defaults.set('layout',{autoPadding:true,padding:{top:0,right:0,bottom:0,left:0}});var layouts={addBox(chart,item){if(!chart.boxes){chart.boxes=[];} item.fullSize=item.fullSize||false;item.position=item.position||'top';item.weight=item.weight||0;item._layers=item._layers||function(){return[{z:0,draw(chartArea){item.draw(chartArea);}}];};chart.boxes.push(item);},removeBox(chart,layoutItem){const index=chart.boxes?chart.boxes.indexOf(layoutItem):-1;if(index!==-1){chart.boxes.splice(index,1);}},configure(chart,item,options){item.fullSize=options.fullSize;item.position=options.position;item.weight=options.weight;},update(chart,width,height,minPadding){if(!chart){return;} const padding=toPadding(chart.options.layout.padding);const availableWidth=Math.max(width-padding.width,0);const availableHeight=Math.max(height-padding.height,0);const boxes=buildLayoutBoxes(chart.boxes);const verticalBoxes=boxes.vertical;const horizontalBoxes=boxes.horizontal;each(chart.boxes,box=>{if(typeof box.beforeLayout==='function'){box.beforeLayout();}});const visibleVerticalBoxCount=verticalBoxes.reduce((total,wrap)=>wrap.box.options&&wrap.box.options.display===false?total:total+1,0)||1;const params=Object.freeze({outerWidth:width,outerHeight:height,padding,availableWidth,availableHeight,vBoxMaxWidth:availableWidth/2/visibleVerticalBoxCount,hBoxMaxHeight:availableHeight/2});const maxPadding=Object.assign({},padding);updateMaxPadding(maxPadding,toPadding(minPadding));const chartArea=Object.assign({maxPadding,w:availableWidth,h:availableHeight,x:padding.left,y:padding.top},padding);const stacks=setLayoutDims(verticalBoxes.concat(horizontalBoxes),params);fitBoxes(boxes.fullSize,chartArea,params,stacks);fitBoxes(verticalBoxes,chartArea,params,stacks);if(fitBoxes(horizontalBoxes,chartArea,params,stacks)){fitBoxes(verticalBoxes,chartArea,params,stacks);} -handleMaxPadding(chartArea);placeBoxes(boxes.leftAndTop,chartArea,params,stacks);chartArea.x+=chartArea.w;chartArea.y+=chartArea.h;placeBoxes(boxes.rightAndBottom,chartArea,params,stacks);chart.chartArea={left:chartArea.left,top:chartArea.top,right:chartArea.left+chartArea.w,bottom:chartArea.top+chartArea.h,height:chartArea.h,width:chartArea.w,};each(boxes.chartArea,(layout)=>{const box=layout.box;Object.assign(box,chart.chartArea);box.update(chartArea.w,chartArea.h);});}};function _createResolver(scopes,prefixes=[''],rootScopes=scopes,fallback,getTarget=()=>scopes[0]){if(!defined(fallback)){fallback=_resolve('_fallback',scopes);} -const cache={[Symbol.toStringTag]:'Object',_cacheable:true,_scopes:scopes,_rootScopes:rootScopes,_fallback:fallback,_getTarget:getTarget,override:(scope)=>_createResolver([scope,...scopes],prefixes,rootScopes,fallback),};return new Proxy(cache,{deleteProperty(target,prop){delete target[prop];delete target._keys;delete scopes[0][prop];return true;},get(target,prop){return _cached(target,prop,()=>_resolveWithPrefixes(prop,prefixes,scopes,target));},getOwnPropertyDescriptor(target,prop){return Reflect.getOwnPropertyDescriptor(target._scopes[0],prop);},getPrototypeOf(){return Reflect.getPrototypeOf(scopes[0]);},has(target,prop){return getKeysFromAllScopes(target).includes(prop);},ownKeys(target){return getKeysFromAllScopes(target);},set(target,prop,value){const storage=target._storage||(target._storage=getTarget());storage[prop]=value;delete target[prop];delete target._keys;return true;}});} +handleMaxPadding(chartArea);placeBoxes(boxes.leftAndTop,chartArea,params,stacks);chartArea.x+=chartArea.w;chartArea.y+=chartArea.h;placeBoxes(boxes.rightAndBottom,chartArea,params,stacks);chart.chartArea={left:chartArea.left,top:chartArea.top,right:chartArea.left+chartArea.w,bottom:chartArea.top+chartArea.h,height:chartArea.h,width:chartArea.w,};each(boxes.chartArea,(layout)=>{const box=layout.box;Object.assign(box,chart.chartArea);box.update(chartArea.w,chartArea.h,{left:0,top:0,right:0,bottom:0});});}};function _createResolver(scopes,prefixes=[''],rootScopes=scopes,fallback,getTarget=()=>scopes[0]){if(!defined(fallback)){fallback=_resolve('_fallback',scopes);} +const cache={[Symbol.toStringTag]:'Object',_cacheable:true,_scopes:scopes,_rootScopes:rootScopes,_fallback:fallback,_getTarget:getTarget,override:(scope)=>_createResolver([scope,...scopes],prefixes,rootScopes,fallback),};return new Proxy(cache,{deleteProperty(target,prop){delete target[prop];delete target._keys;delete scopes[0][prop];return true;},get(target,prop){return _cached(target,prop,()=>_resolveWithPrefixes(prop,prefixes,scopes,target));},getOwnPropertyDescriptor(target,prop){return Reflect.getOwnPropertyDescriptor(target._scopes[0],prop);},getPrototypeOf(){return Reflect.getPrototypeOf(scopes[0]);},has(target,prop){return getKeysFromAllScopes(target).includes(prop);},ownKeys(target){return getKeysFromAllScopes(target);},set(target,prop,value){const storage=target._storage||(target._storage=getTarget());target[prop]=storage[prop]=value;delete target._keys;return true;}});} function _attachContext(proxy,context,subProxy,descriptorDefaults){const cache={_cacheable:false,_proxy:proxy,_context:context,_subProxy:subProxy,_stack:new Set(),_descriptors:_descriptors(proxy,descriptorDefaults),setContext:(ctx)=>_attachContext(proxy,ctx,subProxy,descriptorDefaults),override:(scope)=>_attachContext(proxy.override(scope),context,subProxy,descriptorDefaults)};return new Proxy(cache,{deleteProperty(target,prop){delete target[prop];delete proxy[prop];return true;},get(target,prop,receiver){return _cached(target,prop,()=>_resolveWithContext(target,prop,receiver));},getOwnPropertyDescriptor(target,prop){return target._descriptors.allKeys?Reflect.has(proxy,prop)?{enumerable:true,configurable:true}:undefined:Reflect.getOwnPropertyDescriptor(proxy,prop);},getPrototypeOf(){return Reflect.getPrototypeOf(proxy);},has(target,prop){return Reflect.has(proxy,prop);},ownKeys(){return Reflect.ownKeys(proxy);},set(target,prop,value){proxy[prop]=value;delete target[prop];return true;}});} function _descriptors(proxy,defaults={scriptable:true,indexable:true}){const{_scriptable=defaults.scriptable,_indexable=defaults.indexable,_allKeys=defaults.allKeys}=proxy;return{allKeys:_allKeys,scriptable:_scriptable,indexable:_indexable,isScriptable:isFunction(_scriptable)?_scriptable:()=>_scriptable,isIndexable:isFunction(_indexable)?_indexable:()=>_indexable};} -const readKey=(prefix,name)=>prefix?prefix+_capitalize(name):name;const needsSubResolver=(prop,value)=>isObject(value)&&prop!=='adapters';function _cached(target,prop,resolve){let value=target[prop];if(defined(value)){return value;} -value=resolve();if(defined(value)){target[prop]=value;} -return value;} +const readKey=(prefix,name)=>prefix?prefix+_capitalize(name):name;const needsSubResolver=(prop,value)=>isObject(value)&&prop!=='adapters'&&(Object.getPrototypeOf(value)===null||value.constructor===Object);function _cached(target,prop,resolve){if(Object.prototype.hasOwnProperty.call(target,prop)){return target[prop];} +const value=resolve();target[prop]=value;return value;} function _resolveWithContext(target,prop,receiver){const{_proxy,_context,_subProxy,_descriptors:descriptors}=target;let value=_proxy[prop];if(isFunction(value)&&descriptors.isScriptable(prop)){value=_resolveScriptable(prop,value,target,receiver);} if(isArray(value)&&value.length){value=_resolveArray(prop,value,target,descriptors.isIndexable);} if(needsSubResolver(prop,value)){value=_attachContext(value,_context,_subProxy&&_subProxy[prop],descriptors);} return value;} function _resolveScriptable(prop,value,target,receiver){const{_proxy,_context,_subProxy,_stack}=target;if(_stack.has(prop)){throw new Error('Recursion detected: '+Array.from(_stack).join('->')+'->'+prop);} -_stack.add(prop);value=value(_context,_subProxy||receiver);_stack.delete(prop);if(isObject(value)){value=createSubResolver(_proxy._scopes,_proxy,prop,value);} +_stack.add(prop);value=value(_context,_subProxy||receiver);_stack.delete(prop);if(needsSubResolver(prop,value)){value=createSubResolver(_proxy._scopes,_proxy,prop,value);} return value;} function _resolveArray(prop,value,target,isIndexable){const{_proxy,_context,_subProxy,_descriptors:descriptors}=target;if(defined(_context.index)&&isIndexable(prop)){value=value[_context.index%value.length];}else if(isObject(value[0])){const arr=value;const scopes=_proxy._scopes.filter(s=>s!==arr);value=[];for(const item of arr){const resolver=createSubResolver(scopes,_proxy,prop,item);value.push(_attachContext(resolver,_context,_subProxy&&_subProxy[prop],descriptors));}} return value;} @@ -969,7 +970,7 @@ return align==='right'?'left':'right';},xPlus(x,value){return x-value;},leftForL function overrideTextDirection(ctx,direction){let style,original;if(direction==='ltr'||direction==='rtl'){style=ctx.canvas.style;original=[style.getPropertyValue('direction'),style.getPropertyPriority('direction'),];style.setProperty('direction',direction,'important');ctx.prevTextDirection=original;}} function restoreTextDirection(ctx,original){if(original!==undefined){delete ctx.prevTextDirection;ctx.canvas.style.setProperty('direction',original[0],original[1]);}} function propertyFn(property){if(property==='angle'){return{between:_angleBetween,compare:_angleDiff,normalize:_normalizeAngle,};} -return{between:(n,s,e)=>n>=Math.min(s,e)&&n<=Math.max(e,s),compare:(a,b)=>a-b,normalize:x=>x};} +return{between:_isBetween,compare:(a,b)=>a-b,normalize:x=>x};} function normalizeSegment({start,end,count,loop,style}){return{start:start%count,end:end%count,loop:loop&&(end-start+1)%count===0,style};} function getSegment(segment,points,bounds){const{property,start:startBound,end:endBound}=bounds;const{between,normalize}=propertyFn(property);const count=points.length;let{start,end,loop}=segment;let i,ilen;if(loop){start+=count;end+=count;for(i=0,ilen=count;ivalue===null||value==='';function initCanvas(canvas,aspectRatio){const style=canvas.style;const renderHeight=canvas.getAttribute('height');const renderWidth=canvas.getAttribute('width');canvas[EXPANDO_KEY]={initial:{height:renderHeight,width:renderWidth,style:{display:style.display,height:style.height,width:style.width}}};style.display=style.display||'block';style.boxSizing=style.boxSizing||'border-box';if(isNullOrEmpty(renderWidth)){const displayWidth=readUsedSize(canvas,'width');if(displayWidth!==undefined){canvas.width=displayWidth;}} if(isNullOrEmpty(renderHeight)){if(canvas.style.height===''){canvas.height=canvas.width/(aspectRatio||2);}else{const displayHeight=readUsedSize(canvas,'height');if(displayHeight!==undefined){canvas.height=displayHeight;}}} return canvas;} const eventListenerOptions=supportsEventListenerOptions?{passive:true}:false;function addListener(node,type,listener){node.addEventListener(type,listener,eventListenerOptions);} function removeListener(chart,type,listener){chart.canvas.removeEventListener(type,listener,eventListenerOptions);} function fromNativeEvent(event,chart){const type=EVENT_TYPES[event.type]||event.type;const{x,y}=getRelativePosition$1(event,chart);return{type,chart,native:event,x:x!==undefined?x:null,y:y!==undefined?y:null,};} -function createAttachObserver(chart,type,listener){const canvas=chart.canvas;const container=canvas&&_getParentNode(canvas);const element=container||canvas;const observer=new MutationObserver(entries=>{const parent=_getParentNode(element);entries.forEach(entry=>{for(let i=0;i{entries.forEach(entry=>{for(let i=0;i{let trigger=false;for(const entry of entries){trigger=trigger||nodeListContains(entry.addedNodes,canvas);trigger=trigger&&!nodeListContains(entry.removedNodes,canvas);} +if(trigger){listener();}});observer.observe(document,{childList:true,subtree:true});return observer;} +function createDetachObserver(chart,type,listener){const canvas=chart.canvas;const observer=new MutationObserver(entries=>{let trigger=false;for(const entry of entries){trigger=trigger||nodeListContains(entry.removedNodes,canvas);trigger=trigger&&!nodeListContains(entry.addedNodes,canvas);} +if(trigger){listener();}});observer.observe(document,{childList:true,subtree:true});return observer;} const drpListeningCharts=new Map();let oldDevicePixelRatio=0;function onWindowResize(){const dpr=window.devicePixelRatio;if(dpr===oldDevicePixelRatio){return;} oldDevicePixelRatio=dpr;drpListeningCharts.forEach((resize,chart)=>{if(chart.currentDevicePixelRatio!==dpr){resize();}});} function listenDevicePixelRatioChanges(chart,resize){if(!drpListeningCharts.size){window.addEventListener('resize',onWindowResize);} @@ -1048,11 +1057,11 @@ function _detectPlatform(canvas){if(!_isDomSupported()||(typeof OffscreenCanvas! return DomPlatform;} var platforms=Object.freeze({__proto__:null,_detectPlatform:_detectPlatform,BasePlatform:BasePlatform,BasicPlatform:BasicPlatform,DomPlatform:DomPlatform});const transparent='transparent';const interpolators={boolean(from,to,factor){return factor>0.5?to:from;},color(from,to,factor){const c0=color(from||transparent);const c1=c0.valid&&color(to||transparent);return c1&&c1.valid?c1.mix(c0,factor).hexString():to;},number(from,to,factor){return from+(to-from)*factor;}};class Animation{constructor(cfg,target,prop,to){const currentValue=target[prop];to=resolve([cfg.to,to,currentValue,cfg.from]);const from=resolve([cfg.from,currentValue,to]);this._active=true;this._fn=cfg.fn||interpolators[cfg.type||typeof from];this._easing=effects[cfg.easing]||effects.linear;this._start=Math.floor(Date.now()+(cfg.delay||0));this._duration=this._total=Math.floor(cfg.duration);this._loop=!!cfg.loop;this._target=target;this._prop=prop;this._from=from;this._to=to;this._promises=undefined;} active(){return this._active;} -update(cfg,to,date){const me=this;if(me._active){me._notify(false);const currentValue=me._target[me._prop];const elapsed=date-me._start;const remain=me._duration-elapsed;me._start=date;me._duration=Math.floor(Math.max(remain,cfg.duration));me._total+=elapsed;me._loop=!!cfg.loop;me._to=resolve([cfg.to,to,currentValue,cfg.from]);me._from=resolve([cfg.from,currentValue,to]);}} -cancel(){const me=this;if(me._active){me.tick(Date.now());me._active=false;me._notify(false);}} -tick(date){const me=this;const elapsed=date-me._start;const duration=me._duration;const prop=me._prop;const from=me._from;const loop=me._loop;const to=me._to;let factor;me._active=from!==to&&(loop||(elapsed1?2-factor:factor;factor=me._easing(Math.min(1,Math.max(0,factor)));me._target[prop]=me._fn(from,to,factor);} +update(cfg,to,date){if(this._active){this._notify(false);const currentValue=this._target[this._prop];const elapsed=date-this._start;const remain=this._duration-elapsed;this._start=date;this._duration=Math.floor(Math.max(remain,cfg.duration));this._total+=elapsed;this._loop=!!cfg.loop;this._to=resolve([cfg.to,to,currentValue,cfg.from]);this._from=resolve([cfg.from,currentValue,to]);}} +cancel(){if(this._active){this.tick(Date.now());this._active=false;this._notify(false);}} +tick(date){const elapsed=date-this._start;const duration=this._duration;const prop=this._prop;const from=this._from;const loop=this._loop;const to=this._to;let factor;this._active=from!==to&&(loop||(elapsed1?2-factor:factor;factor=this._easing(Math.min(1,Math.max(0,factor)));this._target[prop]=this._fn(from,to,factor);} wait(){const promises=this._promises||(this._promises=[]);return new Promise((res,rej)=>{promises.push({res,rej});});} _notify(resolved){const method=resolved?'res':'rej';const promises=this._promises||[];for(let i=0;iname!=='onProgress'&&name!=='onComplete'&&name!=='fn',});defaults.set('animations',{colors:{type:'color',properties:colors},numbers:{type:'number',properties:numbers},});defaults.describe('animations',{_fallback:'animation',});defaults.set('transitions',{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:'transparent'},visible:{type:'boolean',duration:0},}},hide:{animations:{colors:{to:'transparent'},visible:{type:'boolean',easing:'linear',fn:v=>v|0},}}});class Animations{constructor(chart,config){this._chart=chart;this._properties=new Map();this.configure(config);} @@ -1084,7 +1093,7 @@ function toClip(value){let t,r,b,l;if(isObject(value)){t=value.top;r=value.right return{top:t,right:r,bottom:b,left:l,disabled:value===false};} function getSortedDatasetIndices(chart,filterVisible){const keys=[];const metasets=chart._getSortedDatasetMetas(filterVisible);let i,ilen;for(i=0,ilen=metasets.length;i0)||(!positive&&value<0)){return meta.index;}} +function getLastIndexInStack(stack,vScale,positive,type){for(const meta of vScale.getMatchingVisibleMetas(type).reverse()){const value=stack[meta.index];if((positive&&value>0)||(!positive&&value<0)){return meta.index;}} return null;} -function updateStacks(controller,parsed){const{chart,_cachedMeta:meta}=controller;const stacks=chart._stacks||(chart._stacks={});const{iScale,vScale,index:datasetIndex}=meta;const iAxis=iScale.axis;const vAxis=vScale.axis;const key=getStackKey(iScale,vScale,meta);const ilen=parsed.length;let stack;for(let i=0;iscales[key].axis===axis).shift();} -function createDatasetContext(parent,index){return Object.assign(Object.create(parent),{active:false,dataset:undefined,datasetIndex:index,index,mode:'default',type:'dataset'});} -function createDataContext(parent,index,element){return Object.assign(Object.create(parent),{active:false,dataIndex:index,parsed:undefined,raw:undefined,element,index,mode:'default',type:'data'});} +function createDatasetContext(parent,index){return createContext(parent,{active:false,dataset:undefined,datasetIndex:index,index,mode:'default',type:'dataset'});} +function createDataContext(parent,index,element){return createContext(parent,{active:false,dataIndex:index,parsed:undefined,raw:undefined,element,index,mode:'default',type:'data'});} function clearStacks(meta,items){const datasetIndex=meta.controller.index;const axis=meta.vScale&&meta.vScale.axis;if(!axis){return;} items=items||meta._parsed;for(const parsed of items){const stacks=parsed._stacks;if(!stacks||stacks[axis]===undefined||stacks[axis][datasetIndex]===undefined){return;} delete stacks[axis][datasetIndex];}} -const isDirectUpdateMode=(mode)=>mode==='reset'||mode==='none';const cloneIfNotShared=(cached,shared)=>shared?cached:Object.assign({},cached);class DatasetController{constructor(chart,datasetIndex){this.chart=chart;this._ctx=chart.ctx;this.index=datasetIndex;this._cachedDataOpts={};this._cachedMeta=this.getMeta();this._type=this._cachedMeta.type;this.options=undefined;this._parsing=false;this._data=undefined;this._objectData=undefined;this._sharedOptions=undefined;this._drawStart=undefined;this._drawCount=undefined;this.enableOptionSharing=false;this.$context=undefined;this._syncList=[];this.initialize();} -initialize(){const me=this;const meta=me._cachedMeta;me.configure();me.linkScales();meta._stacked=isStacked(meta.vScale,meta);me.addElements();} +const isDirectUpdateMode=(mode)=>mode==='reset'||mode==='none';const cloneIfNotShared=(cached,shared)=>shared?cached:Object.assign({},cached);const createStack=(canStack,meta,chart)=>canStack&&!meta.hidden&&meta._stacked&&{keys:getSortedDatasetIndices(chart,true),values:null};class DatasetController{constructor(chart,datasetIndex){this.chart=chart;this._ctx=chart.ctx;this.index=datasetIndex;this._cachedDataOpts={};this._cachedMeta=this.getMeta();this._type=this._cachedMeta.type;this.options=undefined;this._parsing=false;this._data=undefined;this._objectData=undefined;this._sharedOptions=undefined;this._drawStart=undefined;this._drawCount=undefined;this.enableOptionSharing=false;this.$context=undefined;this._syncList=[];this.initialize();} +initialize(){const meta=this._cachedMeta;this.configure();this.linkScales();meta._stacked=isStacked(meta.vScale,meta);this.addElements();} updateIndex(datasetIndex){if(this.index!==datasetIndex){clearStacks(this._cachedMeta);} this.index=datasetIndex;} -linkScales(){const me=this;const chart=me.chart;const meta=me._cachedMeta;const dataset=me.getDataset();const chooseId=(axis,x,y,r)=>axis==='x'?x:axis==='r'?r:y;const xid=meta.xAxisID=valueOrDefault(dataset.xAxisID,getFirstScaleId(chart,'x'));const yid=meta.yAxisID=valueOrDefault(dataset.yAxisID,getFirstScaleId(chart,'y'));const rid=meta.rAxisID=valueOrDefault(dataset.rAxisID,getFirstScaleId(chart,'r'));const indexAxis=meta.indexAxis;const iid=meta.iAxisID=chooseId(indexAxis,xid,yid,rid);const vid=meta.vAxisID=chooseId(indexAxis,yid,xid,rid);meta.xScale=me.getScaleForId(xid);meta.yScale=me.getScaleForId(yid);meta.rScale=me.getScaleForId(rid);meta.iScale=me.getScaleForId(iid);meta.vScale=me.getScaleForId(vid);} +linkScales(){const chart=this.chart;const meta=this._cachedMeta;const dataset=this.getDataset();const chooseId=(axis,x,y,r)=>axis==='x'?x:axis==='r'?r:y;const xid=meta.xAxisID=valueOrDefault(dataset.xAxisID,getFirstScaleId(chart,'x'));const yid=meta.yAxisID=valueOrDefault(dataset.yAxisID,getFirstScaleId(chart,'y'));const rid=meta.rAxisID=valueOrDefault(dataset.rAxisID,getFirstScaleId(chart,'r'));const indexAxis=meta.indexAxis;const iid=meta.iAxisID=chooseId(indexAxis,xid,yid,rid);const vid=meta.vAxisID=chooseId(indexAxis,yid,xid,rid);meta.xScale=this.getScaleForId(xid);meta.yScale=this.getScaleForId(yid);meta.rScale=this.getScaleForId(rid);meta.iScale=this.getScaleForId(iid);meta.vScale=this.getScaleForId(vid);} getDataset(){return this.chart.data.datasets[this.index];} getMeta(){return this.chart.getDatasetMeta(this.index);} getScaleForId(scaleID){return this.chart.scales[scaleID];} @@ -1116,18 +1125,18 @@ _getOtherScale(scale){const meta=this._cachedMeta;return scale===meta.iScale?met reset(){this._update('reset');} _destroy(){const meta=this._cachedMeta;if(this._data){unlistenArrayEvents(this._data,this);} if(meta._stacked){clearStacks(meta);}} -_dataCheck(){const me=this;const dataset=me.getDataset();const data=dataset.data||(dataset.data=[]);const _data=me._data;if(isObject(data)){me._data=convertObjectDataToArray(data);}else if(_data!==data){if(_data){unlistenArrayEvents(_data,me);const meta=me._cachedMeta;clearStacks(meta);meta._parsed=[];} -if(data&&Object.isExtensible(data)){listenArrayEvents(data,me);} -me._syncList=[];me._data=data;}} -addElements(){const me=this;const meta=me._cachedMeta;me._dataCheck();if(me.datasetElementType){meta.dataset=new me.datasetElementType();}} -buildOrUpdateElements(resetNewElements){const me=this;const meta=me._cachedMeta;const dataset=me.getDataset();let stackChanged=false;me._dataCheck();const oldStacked=meta._stacked;meta._stacked=isStacked(meta.vScale,meta);if(meta.stack!==dataset.stack){stackChanged=true;clearStacks(meta);meta.stack=dataset.stack;} -me._resyncElements(resetNewElements);if(stackChanged||oldStacked!==meta._stacked){updateStacks(me,meta._parsed);}} -configure(){const me=this;const config=me.chart.config;const scopeKeys=config.datasetScopeKeys(me._type);const scopes=config.getOptionScopes(me.getDataset(),scopeKeys,true);me.options=config.createResolver(scopes,me.getContext());me._parsing=me.options.parsing;} -parse(start,count){const me=this;const{_cachedMeta:meta,_data:data}=me;const{iScale,_stacked}=meta;const iAxis=iScale.axis;let sorted=start===0&&count===data.length?true:meta._sorted;let prev=start>0&&meta._parsed[start-1];let i,cur,parsed;if(me._parsing===false){meta._parsed=data;meta._sorted=true;parsed=data;}else{if(isArray(data[start])){parsed=me.parseArrayData(meta,data,start,count);}else if(isObject(data[start])){parsed=me.parseObjectData(meta,data,start,count);}else{parsed=me.parsePrimitiveData(meta,data,start,count);} +_dataCheck(){const dataset=this.getDataset();const data=dataset.data||(dataset.data=[]);const _data=this._data;if(isObject(data)){this._data=convertObjectDataToArray(data);}else if(_data!==data){if(_data){unlistenArrayEvents(_data,this);const meta=this._cachedMeta;clearStacks(meta);meta._parsed=[];} +if(data&&Object.isExtensible(data)){listenArrayEvents(data,this);} +this._syncList=[];this._data=data;}} +addElements(){const meta=this._cachedMeta;this._dataCheck();if(this.datasetElementType){meta.dataset=new this.datasetElementType();}} +buildOrUpdateElements(resetNewElements){const meta=this._cachedMeta;const dataset=this.getDataset();let stackChanged=false;this._dataCheck();const oldStacked=meta._stacked;meta._stacked=isStacked(meta.vScale,meta);if(meta.stack!==dataset.stack){stackChanged=true;clearStacks(meta);meta.stack=dataset.stack;} +this._resyncElements(resetNewElements);if(stackChanged||oldStacked!==meta._stacked){updateStacks(this,meta._parsed);}} +configure(){const config=this.chart.config;const scopeKeys=config.datasetScopeKeys(this._type);const scopes=config.getOptionScopes(this.getDataset(),scopeKeys,true);this.options=config.createResolver(scopes,this.getContext());this._parsing=this.options.parsing;this._cachedDataOpts={};} +parse(start,count){const{_cachedMeta:meta,_data:data}=this;const{iScale,_stacked}=meta;const iAxis=iScale.axis;let sorted=start===0&&count===data.length?true:meta._sorted;let prev=start>0&&meta._parsed[start-1];let i,cur,parsed;if(this._parsing===false){meta._parsed=data;meta._sorted=true;parsed=data;}else{if(isArray(data[start])){parsed=this.parseArrayData(meta,data,start,count);}else if(isObject(data[start])){parsed=this.parseObjectData(meta,data,start,count);}else{parsed=this.parsePrimitiveData(meta,data,start,count);} const isNotInOrderComparedToPrev=()=>cur[iAxis]===null||(prev&&cur[iAxis]otherValue||otherMaxotherValue||otherMax=0;--i){if(_skip()){continue;} -me.updateRangeFromParsed(range,scale,parsed,stack);break;}} +this.updateRangeFromParsed(range,scale,parsed,stack);break;}} return range;} getAllParsedValues(scale){const parsed=this._cachedMeta._parsed;const values=[];let i,ilen,value;for(i=0,ilen=parsed.length;i=0&&index=0&&indexme.getContext(index,active);const values=config.resolveNamedOptions(scopes,names,context,prefixes);if(values.$shared){values.$shared=sharing;cache[cacheKey]=Object.freeze(cloneIfNotShared(values,sharing));} +_resolveElementOptions(elementType,mode='default',index){const active=mode==='active';const cache=this._cachedDataOpts;const cacheKey=elementType+'-'+mode;const cached=cache[cacheKey];const sharing=this.enableOptionSharing&&defined(index);if(cached){return cloneIfNotShared(cached,sharing);} +const config=this.chart.config;const scopeKeys=config.datasetElementScopeKeys(this._type,elementType);const prefixes=active?[`${elementType}Hover`,'hover',elementType,'']:[elementType,''];const scopes=config.getOptionScopes(this.getDataset(),scopeKeys);const names=Object.keys(defaults.elements[elementType]);const context=()=>this.getContext(index,active);const values=config.resolveNamedOptions(scopes,names,context,prefixes);if(values.$shared){values.$shared=sharing;cache[cacheKey]=Object.freeze(cloneIfNotShared(values,sharing));} return values;} -_resolveAnimations(index,transition,active){const me=this;const chart=me.chart;const cache=me._cachedDataOpts;const cacheKey=`animation-${transition}`;const cached=cache[cacheKey];if(cached){return cached;} -let options;if(chart.options.animation!==false){const config=me.chart.config;const scopeKeys=config.datasetAnimationScopeKeys(me._type,transition);const scopes=config.getOptionScopes(me.getDataset(),scopeKeys);options=config.createResolver(scopes,me.getContext(index,active,transition));} +_resolveAnimations(index,transition,active){const chart=this.chart;const cache=this._cachedDataOpts;const cacheKey=`animation-${transition}`;const cached=cache[cacheKey];if(cached){return cached;} +let options;if(chart.options.animation!==false){const config=this.chart.config;const scopeKeys=config.datasetAnimationScopeKeys(this._type,transition);const scopes=config.getOptionScopes(this.getDataset(),scopeKeys);options=config.createResolver(scopes,this.getContext(index,active,transition));} const animations=new Animations(chart,options&&options.animations);if(options&&options._cacheable){cache[cacheKey]=Object.freeze(animations);} return animations;} getSharedOptions(options){if(!options.$shared){return;} @@ -1177,26 +1186,28 @@ removeHoverStyle(element,datasetIndex,index){this._setStyle(element,index,'activ setHoverStyle(element,datasetIndex,index){this._setStyle(element,index,'active',true);} _removeDatasetHoverStyle(){const element=this._cachedMeta.dataset;if(element){this._setStyle(element,undefined,'active',false);}} _setDatasetHoverStyle(){const element=this._cachedMeta.dataset;if(element){this._setStyle(element,undefined,'active',true);}} -_resyncElements(resetNewElements){const me=this;const data=me._data;const elements=me._cachedMeta.data;for(const[method,arg1,arg2]of me._syncList){me[method](arg1,arg2);} -me._syncList=[];const numMeta=elements.length;const numData=data.length;const count=Math.min(numData,numMeta);if(count){me.parse(0,count);} -if(numData>numMeta){me._insertElements(numMeta,numData-numMeta,resetNewElements);}else if(numData{arr.length+=count;for(i=arr.length-1;i>=end;i--){arr[i]=arr[i-count];}};move(data);for(i=start;inumMeta){this._insertElements(numMeta,numData-numMeta,resetNewElements);}else if(numData{arr.length+=count;for(i=arr.length-1;i>=end;i--){arr[i]=arr[i-count];}};move(data);for(i=start;i{ret[prop]=anims[prop]&&anims[prop].active()?anims[prop]._to:me[prop];});return ret;}} +getProps(props,final){const anims=this.$animations;if(!final||!anims){return this;} +const ret={};props.forEach(prop=>{ret[prop]=anims[prop]&&anims[prop].active()?anims[prop]._to:this[prop];});return ret;}} Element.defaults={};Element.defaultRoutes=undefined;const formatters={values(value){return isArray(value)?value:''+value;},numeric(tickValue,index,ticks){if(tickValue===0){return'0';} const locale=this.chart.options.locale;let notation;let delta=tickValue;if(ticks.length>1){const maxTick=Math.max(Math.abs(ticks[0].value),Math.abs(ticks[ticks.length-1].value));if(maxTick<1e-4||maxTick>1e+15){notation='scientific';} delta=calculateDelta(tickValue,ticks);} @@ -1231,8 +1242,8 @@ gc.splice(0,gcLen);}});} function getTickMarkLength(options){return options.drawTicks?options.tickLength:0;} function getTitleHeight(options,fallback){if(!options.display){return 0;} const font=toFont(options.font,fallback);const padding=toPadding(options.padding);const lines=isArray(options.text)?options.text.length:1;return(lines*font.lineHeight)+padding.height;} -function createScaleContext(parent,scale){return Object.assign(Object.create(parent),{scale,type:'scale'});} -function createTickContext(parent,index,tick){return Object.assign(Object.create(parent),{tick,index,type:'tick'});} +function createScaleContext(parent,scale){return createContext(parent,{scale,type:'scale'});} +function createTickContext(parent,index,tick){return createContext(parent,{tick,index,type:'tick'});} function titleAlign(align,position,reverse){let ret=_toLeftRightCenter(align);if((reverse&&position!=='right')||(!reverse&&position==='right')){ret=reverseAlign(ret);} return ret;} function titleArgs(scale,offset,position,align){const{top,left,bottom,right,chart}=scale;const{chartArea,scales}=chart;let rotation=0;let maxWidth,titleX,titleY;const height=bottom-top;const width=right-left;if(scale.isHorizontal()){titleX=_alignStartEnd(align,left,right);if(isObject(position)){const positionAxisID=Object.keys(position)[0];const value=position[positionAxisID];titleY=scales[positionAxisID].getPixelForValue(value)+height-offset;}else if(position==='center'){titleY=(chartArea.bottom+chartArea.top)/2+height-offset;}else{titleY=offsetFromEdge(scale,position,offset);} @@ -1240,30 +1251,30 @@ maxWidth=right-left;}else{if(isObject(position)){const positionAxisID=Object.key titleY=_alignStartEnd(align,bottom,top);rotation=position==='left'?-HALF_PI:HALF_PI;} return{titleX,titleY,maxWidth,rotation};} class Scale extends Element{constructor(cfg){super();this.id=cfg.id;this.type=cfg.type;this.options=undefined;this.ctx=cfg.ctx;this.chart=cfg.chart;this.top=undefined;this.bottom=undefined;this.left=undefined;this.right=undefined;this.width=undefined;this.height=undefined;this._margins={left:0,right:0,top:0,bottom:0};this.maxWidth=undefined;this.maxHeight=undefined;this.paddingTop=undefined;this.paddingBottom=undefined;this.paddingLeft=undefined;this.paddingRight=undefined;this.axis=undefined;this.labelRotation=undefined;this.min=undefined;this.max=undefined;this._range=undefined;this.ticks=[];this._gridLineItems=null;this._labelItems=null;this._labelSizes=null;this._length=0;this._maxLength=0;this._longestTextCache={};this._startPixel=undefined;this._endPixel=undefined;this._reversePixels=false;this._userMax=undefined;this._userMin=undefined;this._suggestedMax=undefined;this._suggestedMin=undefined;this._ticksLength=0;this._borderValue=0;this._cache={};this._dataLimitsCached=false;this.$context=undefined;} -init(options){const me=this;me.options=options.setContext(me.getContext());me.axis=options.axis;me._userMin=me.parse(options.min);me._userMax=me.parse(options.max);me._suggestedMin=me.parse(options.suggestedMin);me._suggestedMax=me.parse(options.suggestedMax);} +init(options){this.options=options.setContext(this.getContext());this.axis=options.axis;this._userMin=this.parse(options.min);this._userMax=this.parse(options.max);this._suggestedMin=this.parse(options.suggestedMin);this._suggestedMax=this.parse(options.suggestedMax);} parse(raw,index){return raw;} getUserBounds(){let{_userMin,_userMax,_suggestedMin,_suggestedMax}=this;_userMin=finiteOrDefault(_userMin,Number.POSITIVE_INFINITY);_userMax=finiteOrDefault(_userMax,Number.NEGATIVE_INFINITY);_suggestedMin=finiteOrDefault(_suggestedMin,Number.POSITIVE_INFINITY);_suggestedMax=finiteOrDefault(_suggestedMax,Number.NEGATIVE_INFINITY);return{min:finiteOrDefault(_userMin,_suggestedMin),max:finiteOrDefault(_userMax,_suggestedMax),minDefined:isNumberFinite(_userMin),maxDefined:isNumberFinite(_userMax)};} -getMinMax(canStack){const me=this;let{min,max,minDefined,maxDefined}=me.getUserBounds();let range;if(minDefined&&maxDefined){return{min,max};} -const metas=me.getMatchingVisibleMetas();for(let i=0,ilen=metas.length;imax?max:min;max=minDefined&&min>max?min:max;return{min:finiteOrDefault(min,finiteOrDefault(max,min)),max:finiteOrDefault(max,finiteOrDefault(min,max))};} +getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0};} getTicks(){return this.ticks;} getLabels(){const data=this.chart.data;return this.options.labels||(this.isHorizontal()?data.xLabels:data.yLabels)||data.labels||[];} beforeLayout(){this._cache={};this._dataLimitsCached=false;} beforeUpdate(){callback(this.options.beforeUpdate,[this]);} -update(maxWidth,maxHeight,margins){const me=this;const tickOpts=me.options.ticks;const sampleSize=tickOpts.sampleSize;me.beforeUpdate();me.maxWidth=maxWidth;me.maxHeight=maxHeight;me._margins=margins=Object.assign({left:0,right:0,top:0,bottom:0},margins);me.ticks=null;me._labelSizes=null;me._gridLineItems=null;me._labelItems=null;me.beforeSetDimensions();me.setDimensions();me.afterSetDimensions();me._maxLength=me.isHorizontal()?me.width+margins.left+margins.right:me.height+margins.top+margins.bottom;if(!me._dataLimitsCached){me.beforeDataLimits();me.determineDataLimits();me.afterDataLimits();me._range=_addGrace(me,me.options.grace);me._dataLimitsCached=true;} -me.beforeBuildTicks();me.ticks=me.buildTicks()||[];me.afterBuildTicks();const samplingEnabled=sampleSize=maxRotation||numTicks<=1||!me.isHorizontal()){me.labelRotation=minRotation;return;} -const labelSizes=me._getLabelSizes();const maxLabelWidth=labelSizes.widest.width;const maxLabelHeight=labelSizes.highest.height;const maxWidth=_limitValue(me.chart.width-maxLabelWidth,0,me.maxWidth);tickWidth=options.offset?me.maxWidth/numTicks:maxWidth/(numTicks-1);if(maxLabelWidth+6>tickWidth){tickWidth=maxWidth/(numTicks-(options.offset?0.5:1));maxHeight=me.maxHeight-getTickMarkLength(options.grid) --tickOpts.padding-getTitleHeight(options.title,me.chart.options.font);maxLabelDiagonal=Math.sqrt(maxLabelWidth*maxLabelWidth+maxLabelHeight*maxLabelHeight);labelRotation=toDegrees(Math.min(Math.asin(_limitValue((labelSizes.highest.height+6)/tickWidth,-1,1)),Math.asin(_limitValue(maxHeight/maxLabelDiagonal,-1,1))-Math.asin(_limitValue(maxLabelHeight/maxLabelDiagonal,-1,1))));labelRotation=Math.max(minRotation,Math.min(maxRotation,labelRotation));} -me.labelRotation=labelRotation;} +calculateLabelRotation(){const options=this.options;const tickOpts=options.ticks;const numTicks=this.ticks.length;const minRotation=tickOpts.minRotation||0;const maxRotation=tickOpts.maxRotation;let labelRotation=minRotation;let tickWidth,maxHeight,maxLabelDiagonal;if(!this._isVisible()||!tickOpts.display||minRotation>=maxRotation||numTicks<=1||!this.isHorizontal()){this.labelRotation=minRotation;return;} +const labelSizes=this._getLabelSizes();const maxLabelWidth=labelSizes.widest.width;const maxLabelHeight=labelSizes.highest.height;const maxWidth=_limitValue(this.chart.width-maxLabelWidth,0,this.maxWidth);tickWidth=options.offset?this.maxWidth/numTicks:maxWidth/(numTicks-1);if(maxLabelWidth+6>tickWidth){tickWidth=maxWidth/(numTicks-(options.offset?0.5:1));maxHeight=this.maxHeight-getTickMarkLength(options.grid) +-tickOpts.padding-getTitleHeight(options.title,this.chart.options.font);maxLabelDiagonal=Math.sqrt(maxLabelWidth*maxLabelWidth+maxLabelHeight*maxLabelHeight);labelRotation=toDegrees(Math.min(Math.asin(_limitValue((labelSizes.highest.height+6)/tickWidth,-1,1)),Math.asin(_limitValue(maxHeight/maxLabelDiagonal,-1,1))-Math.asin(_limitValue(maxLabelHeight/maxLabelDiagonal,-1,1))));labelRotation=Math.max(minRotation,Math.min(maxRotation,labelRotation));} +this.labelRotation=labelRotation;} afterCalculateLabelRotation(){callback(this.options.afterCalculateLabelRotation,[this]);} beforeFit(){callback(this.options.beforeFit,[this]);} -fit(){const me=this;const minSize={width:0,height:0};const{chart,options:{ticks:tickOpts,title:titleOpts,grid:gridOpts}}=me;const display=me._isVisible();const isHorizontal=me.isHorizontal();if(display){const titleHeight=getTitleHeight(titleOpts,chart.options.font);if(isHorizontal){minSize.width=me.maxWidth;minSize.height=getTickMarkLength(gridOpts)+titleHeight;}else{minSize.height=me.maxHeight;minSize.width=getTickMarkLength(gridOpts)+titleHeight;} -if(tickOpts.display&&me.ticks.length){const{first,last,widest,highest}=me._getLabelSizes();const tickPadding=tickOpts.padding*2;const angleRadians=toRadians(me.labelRotation);const cos=Math.cos(angleRadians);const sin=Math.sin(angleRadians);if(isHorizontal){const labelHeight=tickOpts.mirror?0:sin*widest.width+cos*highest.height;minSize.height=Math.min(me.maxHeight,minSize.height+labelHeight+tickPadding);}else{const labelWidth=tickOpts.mirror?0:cos*widest.width+sin*highest.height;minSize.width=Math.min(me.maxWidth,minSize.width+labelWidth+tickPadding);} -me._calculatePadding(first,last,sin,cos);}} -me._handleMargins();if(isHorizontal){me.width=me._length=chart.width-me._margins.left-me._margins.right;me.height=minSize.height;}else{me.width=minSize.width;me.height=me._length=chart.height-me._margins.top-me._margins.bottom;}} -_calculatePadding(first,last,sin,cos){const me=this;const{ticks:{align,padding},position}=me.options;const isRotated=me.labelRotation!==0;const labelsBelowTicks=position!=='top'&&me.axis==='x';if(me.isHorizontal()){const offsetLeft=me.getPixelForTick(0)-me.left;const offsetRight=me.right-me.getPixelForTick(me.ticks.length-1);let paddingLeft=0;let paddingRight=0;if(isRotated){if(labelsBelowTicks){paddingLeft=cos*first.width;paddingRight=sin*last.height;}else{paddingLeft=sin*first.height;paddingRight=cos*last.width;}}else if(align==='start'){paddingRight=last.width;}else if(align==='end'){paddingLeft=first.width;}else{paddingLeft=first.width/2;paddingRight=last.width/2;} -me.paddingLeft=Math.max((paddingLeft-offsetLeft+padding)*me.width/(me.width-offsetLeft),0);me.paddingRight=Math.max((paddingRight-offsetRight+padding)*me.width/(me.width-offsetRight),0);}else{let paddingTop=last.height/2;let paddingBottom=first.height/2;if(align==='start'){paddingTop=0;paddingBottom=first.height;}else if(align==='end'){paddingTop=last.height;paddingBottom=0;} -me.paddingTop=paddingTop+padding;me.paddingBottom=paddingBottom+padding;}} -_handleMargins(){const me=this;if(me._margins){me._margins.left=Math.max(me.paddingLeft,me._margins.left);me._margins.top=Math.max(me.paddingTop,me._margins.top);me._margins.right=Math.max(me.paddingRight,me._margins.right);me._margins.bottom=Math.max(me.paddingBottom,me._margins.bottom);}} +fit(){const minSize={width:0,height:0};const{chart,options:{ticks:tickOpts,title:titleOpts,grid:gridOpts}}=this;const display=this._isVisible();const isHorizontal=this.isHorizontal();if(display){const titleHeight=getTitleHeight(titleOpts,chart.options.font);if(isHorizontal){minSize.width=this.maxWidth;minSize.height=getTickMarkLength(gridOpts)+titleHeight;}else{minSize.height=this.maxHeight;minSize.width=getTickMarkLength(gridOpts)+titleHeight;} +if(tickOpts.display&&this.ticks.length){const{first,last,widest,highest}=this._getLabelSizes();const tickPadding=tickOpts.padding*2;const angleRadians=toRadians(this.labelRotation);const cos=Math.cos(angleRadians);const sin=Math.sin(angleRadians);if(isHorizontal){const labelHeight=tickOpts.mirror?0:sin*widest.width+cos*highest.height;minSize.height=Math.min(this.maxHeight,minSize.height+labelHeight+tickPadding);}else{const labelWidth=tickOpts.mirror?0:cos*widest.width+sin*highest.height;minSize.width=Math.min(this.maxWidth,minSize.width+labelWidth+tickPadding);} +this._calculatePadding(first,last,sin,cos);}} +this._handleMargins();if(isHorizontal){this.width=this._length=chart.width-this._margins.left-this._margins.right;this.height=minSize.height;}else{this.width=minSize.width;this.height=this._length=chart.height-this._margins.top-this._margins.bottom;}} +_calculatePadding(first,last,sin,cos){const{ticks:{align,padding},position}=this.options;const isRotated=this.labelRotation!==0;const labelsBelowTicks=position!=='top'&&this.axis==='x';if(this.isHorizontal()){const offsetLeft=this.getPixelForTick(0)-this.left;const offsetRight=this.right-this.getPixelForTick(this.ticks.length-1);let paddingLeft=0;let paddingRight=0;if(isRotated){if(labelsBelowTicks){paddingLeft=cos*first.width;paddingRight=sin*last.height;}else{paddingLeft=sin*first.height;paddingRight=cos*last.width;}}else if(align==='start'){paddingRight=last.width;}else if(align==='end'){paddingLeft=first.width;}else{paddingLeft=first.width/2;paddingRight=last.width/2;} +this.paddingLeft=Math.max((paddingLeft-offsetLeft+padding)*this.width/(this.width-offsetLeft),0);this.paddingRight=Math.max((paddingRight-offsetRight+padding)*this.width/(this.width-offsetRight),0);}else{let paddingTop=last.height/2;let paddingBottom=first.height/2;if(align==='start'){paddingTop=0;paddingBottom=first.height;}else if(align==='end'){paddingTop=last.height;paddingBottom=0;} +this.paddingTop=paddingTop+padding;this.paddingBottom=paddingBottom+padding;}} +_handleMargins(){if(this._margins){this._margins.left=Math.max(this.paddingLeft,this._margins.left);this._margins.top=Math.max(this.paddingTop,this._margins.top);this._margins.right=Math.max(this.paddingRight,this._margins.right);this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom);}} afterFit(){callback(this.options.afterFit,[this]);} isHorizontal(){const{axis,position}=this.options;return position==='top'||position==='bottom'||axis==='x';} isFullSize(){return this.options.fullSize;} -_convertTicksToLabels(ticks){const me=this;me.beforeTickToLabelConversion();me.generateTickLabels(ticks);let i,ilen;for(i=0,ilen=ticks.length;iticks.length-1){return null;} return this.getPixelForValue(ticks[index].value);} -getPixelForDecimal(decimal){const me=this;if(me._reversePixels){decimal=1-decimal;} -const pixel=me._startPixel+decimal*me._length;return _int16Range(me._alignToPixels?_alignPixel(me.chart,pixel,0):pixel);} +getPixelForDecimal(decimal){if(this._reversePixels){decimal=1-decimal;} +const pixel=this._startPixel+decimal*this._length;return _int16Range(this._alignToPixels?_alignPixel(this.chart,pixel,0):pixel);} getDecimalForPixel(pixel){const decimal=(pixel-this._startPixel)/this._length;return this._reversePixels?1-decimal:decimal;} getBasePixel(){return this.getPixelForValue(this.getBaseValue());} getBaseValue(){const{min,max}=this;return min<0&&max<0?max:min>0&&max>0?min:0;} -getContext(index){const me=this;const ticks=me.ticks||[];if(index>=0&&indexw*sin?w/cos:h/sin:h*sin=0&&indexw*sin?w/cos:h/sin:h*sin0;} -_computeGridLineItems(chartArea){const me=this;const axis=me.axis;const chart=me.chart;const options=me.options;const{grid,position}=options;const offset=grid.offset;const isHorizontal=me.isHorizontal();const ticks=me.ticks;const ticksLength=ticks.length+(offset?1:0);const tl=getTickMarkLength(grid);const items=[];const borderOpts=grid.setContext(me.getContext());const axisWidth=borderOpts.drawBorder?borderOpts.borderWidth:0;const axisHalfWidth=axisWidth/2;const alignBorderValue=function(pixel){return _alignPixel(chart,pixel,axisWidth);};let borderValue,i,lineValue,alignedLineValue;let tx1,ty1,tx2,ty2,x1,y1,x2,y2;if(position==='top'){borderValue=alignBorderValue(me.bottom);ty1=me.bottom-tl;ty2=borderValue-axisHalfWidth;y1=alignBorderValue(chartArea.top)+axisHalfWidth;y2=chartArea.bottom;}else if(position==='bottom'){borderValue=alignBorderValue(me.top);y1=chartArea.top;y2=alignBorderValue(chartArea.bottom)-axisHalfWidth;ty1=borderValue+axisHalfWidth;ty2=me.top+tl;}else if(position==='left'){borderValue=alignBorderValue(me.right);tx1=me.right-tl;tx2=borderValue-axisHalfWidth;x1=alignBorderValue(chartArea.left)+axisHalfWidth;x2=chartArea.right;}else if(position==='right'){borderValue=alignBorderValue(me.left);x1=chartArea.left;x2=alignBorderValue(chartArea.right)-axisHalfWidth;tx1=borderValue+axisHalfWidth;tx2=me.left+tl;}else if(axis==='x'){if(position==='center'){borderValue=alignBorderValue((chartArea.top+chartArea.bottom)/2+0.5);}else if(isObject(position)){const positionAxisID=Object.keys(position)[0];const value=position[positionAxisID];borderValue=alignBorderValue(me.chart.scales[positionAxisID].getPixelForValue(value));} -y1=chartArea.top;y2=chartArea.bottom;ty1=borderValue+axisHalfWidth;ty2=ty1+tl;}else if(axis==='y'){if(position==='center'){borderValue=alignBorderValue((chartArea.left+chartArea.right)/2);}else if(isObject(position)){const positionAxisID=Object.keys(position)[0];const value=position[positionAxisID];borderValue=alignBorderValue(me.chart.scales[positionAxisID].getPixelForValue(value));} +_computeGridLineItems(chartArea){const axis=this.axis;const chart=this.chart;const options=this.options;const{grid,position}=options;const offset=grid.offset;const isHorizontal=this.isHorizontal();const ticks=this.ticks;const ticksLength=ticks.length+(offset?1:0);const tl=getTickMarkLength(grid);const items=[];const borderOpts=grid.setContext(this.getContext());const axisWidth=borderOpts.drawBorder?borderOpts.borderWidth:0;const axisHalfWidth=axisWidth/2;const alignBorderValue=function(pixel){return _alignPixel(chart,pixel,axisWidth);};let borderValue,i,lineValue,alignedLineValue;let tx1,ty1,tx2,ty2,x1,y1,x2,y2;if(position==='top'){borderValue=alignBorderValue(this.bottom);ty1=this.bottom-tl;ty2=borderValue-axisHalfWidth;y1=alignBorderValue(chartArea.top)+axisHalfWidth;y2=chartArea.bottom;}else if(position==='bottom'){borderValue=alignBorderValue(this.top);y1=chartArea.top;y2=alignBorderValue(chartArea.bottom)-axisHalfWidth;ty1=borderValue+axisHalfWidth;ty2=this.top+tl;}else if(position==='left'){borderValue=alignBorderValue(this.right);tx1=this.right-tl;tx2=borderValue-axisHalfWidth;x1=alignBorderValue(chartArea.left)+axisHalfWidth;x2=chartArea.right;}else if(position==='right'){borderValue=alignBorderValue(this.left);x1=chartArea.left;x2=alignBorderValue(chartArea.right)-axisHalfWidth;tx1=borderValue+axisHalfWidth;tx2=this.left+tl;}else if(axis==='x'){if(position==='center'){borderValue=alignBorderValue((chartArea.top+chartArea.bottom)/2+0.5);}else if(isObject(position)){const positionAxisID=Object.keys(position)[0];const value=position[positionAxisID];borderValue=alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));} +y1=chartArea.top;y2=chartArea.bottom;ty1=borderValue+axisHalfWidth;ty2=ty1+tl;}else if(axis==='y'){if(position==='center'){borderValue=alignBorderValue((chartArea.left+chartArea.right)/2);}else if(isObject(position)){const positionAxisID=Object.keys(position)[0];const value=position[positionAxisID];borderValue=alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));} tx1=borderValue-axisHalfWidth;tx2=tx1-tl;x1=chartArea.left;x2=chartArea.right;} -const limit=valueOrDefault(options.ticks.maxTicksLimit,ticksLength);const step=Math.max(1,Math.ceil(ticksLength/limit));for(i=0;it.value===value);if(index>=0){const opts=grid.setContext(me.getContext(index));return opts.lineWidth;} +getLineWidthForValue(value){const grid=this.options.grid;if(!this._isVisible()||!grid.display){return 0;} +const ticks=this.ticks;const index=ticks.findIndex(t=>t.value===value);if(index>=0){const opts=grid.setContext(this.getContext(index));return opts.lineWidth;} return 0;} -drawGrid(chartArea){const me=this;const grid=me.options.grid;const ctx=me.ctx;const items=me._gridLineItems||(me._gridLineItems=me._computeGridLineItems(chartArea));let i,ilen;const drawLine=(p1,p2,style)=>{if(!style.width||!style.color){return;} +drawGrid(chartArea){const grid=this.options.grid;const ctx=this.ctx;const items=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(chartArea));let i,ilen;const drawLine=(p1,p2,style)=>{if(!style.width||!style.color){return;} ctx.save();ctx.lineWidth=style.width;ctx.strokeStyle=style.color;ctx.setLineDash(style.borderDash||[]);ctx.lineDashOffset=style.borderDashOffset;ctx.beginPath();ctx.moveTo(p1.x,p1.y);ctx.lineTo(p2.x,p2.y);ctx.stroke();ctx.restore();};if(grid.display){for(i=0,ilen=items.length;i{this.draw(chartArea);}}];} +return[{z:gz,draw:(chartArea)=>{this.drawBackground();this.drawGrid(chartArea);this.drawTitle();}},{z:gz+1,draw:()=>{this.drawBorder();}},{z:tz,draw:(chartArea)=>{this.drawLabels(chartArea);}}];} +getMatchingVisibleMetas(type){const metas=this.chart.getSortedVisibleDatasetMetas();const axisID=this.axis+'AxisID';const result=[];let i,ilen;for(i=0,ilen=metas.length;i{const reg=typedRegistry||me._getRegistryForType(arg);if(typedRegistry||reg.isForType(arg)||(reg===me.plugins&&arg.id)){me._exec(method,reg,arg);}else{each(arg,item=>{const itemReg=typedRegistry||me._getRegistryForType(item);me._exec(method,itemReg,item);});}});} +_each(method,args,typedRegistry){[...args].forEach(arg=>{const reg=typedRegistry||this._getRegistryForType(arg);if(typedRegistry||reg.isForType(arg)||(reg===this.plugins&&arg.id)){this._exec(method,reg,arg);}else{each(arg,item=>{const itemReg=typedRegistry||this._getRegistryForType(item);this._exec(method,itemReg,item);});}});} _exec(method,registry,component){const camelMethod=_capitalize(method);callback(component['before'+camelMethod],[],component);registry[method](component);callback(component['after'+camelMethod],[],component);} _getRegistryForType(type){for(let i=0;i{const scaleConf=configScales[id];const axis=determineAxis(id,scaleConf);const defaultId=getDefaultScaleIDFromAxis(axis,chartIndexAxis);const defaultScaleOptions=chartDefaults.scales||{};firstIDs[axis]=firstIDs[axis]||id;scales[id]=mergeIf(Object.create(null),[{axis},scaleConf,defaultScaleOptions[axis],defaultScaleOptions[defaultId]]);});config.data.datasets.forEach(dataset=>{const type=dataset.type||config.type;const indexAxis=dataset.indexAxis||getIndexAxis(type,options);const datasetDefaults=overrides[type]||{};const defaultScaleOptions=datasetDefaults.scales||{};Object.keys(defaultScaleOptions).forEach(defaultID=>{const axis=getAxisFromDefaultScaleID(defaultID,indexAxis);const id=dataset[axis+'AxisID']||firstIDs[axis]||axis;scales[id]=scales[id]||Object.create(null);mergeIf(scales[id],[{axis},configScales[id],defaultScaleOptions[defaultID]]);});});Object.keys(scales).forEach(key=>{const scale=scales[key];mergeIf(scale,[defaults.scales[scale.type],defaults.scale]);});return scales;} +function mergeScaleConfig(config,options){const chartDefaults=overrides[config.type]||{scales:{}};const configScales=options.scales||{};const chartIndexAxis=getIndexAxis(config.type,options);const firstIDs=Object.create(null);const scales=Object.create(null);Object.keys(configScales).forEach(id=>{const scaleConf=configScales[id];if(!isObject(scaleConf)){return console.error(`Invalid scale configuration for scale: ${id}`);} +if(scaleConf._proxy){return console.warn(`Ignoring resolver passed as options for scale: ${id}`);} +const axis=determineAxis(id,scaleConf);const defaultId=getDefaultScaleIDFromAxis(axis,chartIndexAxis);const defaultScaleOptions=chartDefaults.scales||{};firstIDs[axis]=firstIDs[axis]||id;scales[id]=mergeIf(Object.create(null),[{axis},scaleConf,defaultScaleOptions[axis],defaultScaleOptions[defaultId]]);});config.data.datasets.forEach(dataset=>{const type=dataset.type||config.type;const indexAxis=dataset.indexAxis||getIndexAxis(type,options);const datasetDefaults=overrides[type]||{};const defaultScaleOptions=datasetDefaults.scales||{};Object.keys(defaultScaleOptions).forEach(defaultID=>{const axis=getAxisFromDefaultScaleID(defaultID,indexAxis);const id=dataset[axis+'AxisID']||firstIDs[axis]||axis;scales[id]=scales[id]||Object.create(null);mergeIf(scales[id],[{axis},configScales[id],defaultScaleOptions[defaultID]]);});});Object.keys(scales).forEach(key=>{const scale=scales[key];mergeIf(scale,[defaults.scales[scale.type],defaults.scale]);});return scales;} function initOptions(config){const options=config.options||(config.options={});options.plugins=valueOrDefault(options.plugins,{});options.scales=mergeScaleConfig(config,options);} function initData(data){data=data||{};data.datasets=data.datasets||[];data.labels=data.labels||[];return data;} function initConfig(config){config=config||{};config.data=initData(config.data);initOptions(config);return config;} @@ -1465,18 +1478,19 @@ createResolver(scopes,context,prefixes=[''],descriptorDefaults){const{resolver}= function getResolver(resolverCache,scopes,prefixes){let cache=resolverCache.get(scopes);if(!cache){cache=new Map();resolverCache.set(scopes,cache);} const cacheKey=prefixes.join();let cached=cache.get(cacheKey);if(!cached){const resolver=_createResolver(scopes,prefixes);cached={resolver,subPrefixes:prefixes.filter(p=>!p.toLowerCase().includes('hover'))};cache.set(cacheKey,cached);} return cached;} -function needContext(proxy,names){const{isScriptable,isIndexable}=_descriptors(proxy);for(const prop of names){if((isScriptable(prop)&&isFunction(proxy[prop]))||(isIndexable(prop)&&isArray(proxy[prop]))){return true;}} +const hasFunction=value=>isObject(value)&&Object.getOwnPropertyNames(value).reduce((acc,key)=>acc||isFunction(value[key]),false);function needContext(proxy,names){const{isScriptable,isIndexable}=_descriptors(proxy);for(const prop of names){const scriptable=isScriptable(prop);const indexable=isIndexable(prop);const value=(indexable||scriptable)&&proxy[prop];if((scriptable&&(isFunction(value)||hasFunction(value)))||(indexable&&isArray(value))){return true;}} return false;} -var version="3.5.1";const KNOWN_POSITIONS=['top','bottom','left','right','chartArea'];function positionIsHorizontal(position,axis){return position==='top'||position==='bottom'||(KNOWN_POSITIONS.indexOf(position)===-1&&axis==='x');} +var version="3.6.2";const KNOWN_POSITIONS=['top','bottom','left','right','chartArea'];function positionIsHorizontal(position,axis){return position==='top'||position==='bottom'||(KNOWN_POSITIONS.indexOf(position)===-1&&axis==='x');} function compare2Level(l1,l2){return function(a,b){return a[l1]===b[l1]?a[l2]-b[l2]:a[l1]-b[l1];};} function onAnimationsComplete(context){const chart=context.chart;const animationOptions=chart.options.animation;chart.notifyPlugins('afterRender');callback(animationOptions&&animationOptions.onComplete,[context],chart);} function onAnimationProgress(context){const chart=context.chart;const animationOptions=chart.options.animation;callback(animationOptions&&animationOptions.onProgress,[context],chart);} function getCanvas(item){if(_isDomSupported()&&typeof item==='string'){item=document.getElementById(item);}else if(item&&item.length){item=item[0];} if(item&&item.canvas){item=item.canvas;} return item;} -const instances={};const getChart=(key)=>{const canvas=getCanvas(key);return Object.values(instances).filter((c)=>c.canvas===canvas).pop();};class Chart{constructor(item,userConfig){const me=this;const config=this.config=new Config(userConfig);const initialCanvas=getCanvas(item);const existingChart=getChart(initialCanvas);if(existingChart){throw new Error('Canvas is already in use. Chart with ID \''+existingChart.id+'\''+' must be destroyed before the canvas can be reused.');} -const options=config.createResolver(config.chartOptionScopes(),me.getContext());this.platform=new(config.platform||_detectPlatform(initialCanvas))();const context=me.platform.acquireContext(initialCanvas,options.aspectRatio);const canvas=context&&context.canvas;const height=canvas&&canvas.height;const width=canvas&&canvas.width;this.id=uid();this.ctx=context;this.canvas=canvas;this.width=width;this.height=height;this._options=options;this._aspectRatio=this.aspectRatio;this._layers=[];this._metasets=[];this._stacks=undefined;this.boxes=[];this.currentDevicePixelRatio=undefined;this.chartArea=undefined;this._active=[];this._lastEvent=undefined;this._listeners={};this._responsiveListeners=undefined;this._sortedMetasets=[];this.scales={};this._plugins=new PluginService();this.$proxies={};this._hiddenIndices={};this.attached=false;this._animationsDisabled=undefined;this.$context=undefined;this._doResize=debounce(()=>this.update('resize'),options.resizeDelay||0);instances[me.id]=me;if(!context||!canvas){console.error("Failed to create chart: can't acquire context from the given item");return;} -animator.listen(me,'complete',onAnimationsComplete);animator.listen(me,'progress',onAnimationProgress);me._initialize();if(me.attached){me.update();}} +const instances={};const getChart=(key)=>{const canvas=getCanvas(key);return Object.values(instances).filter((c)=>c.canvas===canvas).pop();};function moveNumericKeys(obj,start,move){const keys=Object.keys(obj);for(const key of keys){const intKey=+key;if(intKey>=start){const value=obj[key];delete obj[key];if(move>0||intKey>start){obj[intKey+move]=value;}}}} +class Chart{constructor(item,userConfig){const config=this.config=new Config(userConfig);const initialCanvas=getCanvas(item);const existingChart=getChart(initialCanvas);if(existingChart){throw new Error('Canvas is already in use. Chart with ID \''+existingChart.id+'\''+' must be destroyed before the canvas can be reused.');} +const options=config.createResolver(config.chartOptionScopes(),this.getContext());this.platform=new(config.platform||_detectPlatform(initialCanvas))();this.platform.updateConfig(config);const context=this.platform.acquireContext(initialCanvas,options.aspectRatio);const canvas=context&&context.canvas;const height=canvas&&canvas.height;const width=canvas&&canvas.width;this.id=uid();this.ctx=context;this.canvas=canvas;this.width=width;this.height=height;this._options=options;this._aspectRatio=this.aspectRatio;this._layers=[];this._metasets=[];this._stacks=undefined;this.boxes=[];this.currentDevicePixelRatio=undefined;this.chartArea=undefined;this._active=[];this._lastEvent=undefined;this._listeners={};this._responsiveListeners=undefined;this._sortedMetasets=[];this.scales={};this._plugins=new PluginService();this.$proxies={};this._hiddenIndices={};this.attached=false;this._animationsDisabled=undefined;this.$context=undefined;this._doResize=debounce(mode=>this.update(mode),options.resizeDelay||0);this._dataChanges=[];instances[this.id]=this;if(!context||!canvas){console.error("Failed to create chart: can't acquire context from the given item");return;} +animator.listen(this,'complete',onAnimationsComplete);animator.listen(this,'progress',onAnimationProgress);this._initialize();if(this.attached){this.update();}} get aspectRatio(){const{options:{aspectRatio,maintainAspectRatio},width,height,_aspectRatio}=this;if(!isNullOrUndef(aspectRatio)){return aspectRatio;} if(maintainAspectRatio&&_aspectRatio){return _aspectRatio;} return height?width/height:null;} @@ -1484,100 +1498,107 @@ get data(){return this.config.data;} set data(data){this.config.data=data;} get options(){return this._options;} set options(options){this.config.options=options;} -_initialize(){const me=this;me.notifyPlugins('beforeInit');if(me.options.responsive){me.resize();}else{retinaScale(me,me.options.devicePixelRatio);} -me.bindEvents();me.notifyPlugins('afterInit');return me;} +_initialize(){this.notifyPlugins('beforeInit');if(this.options.responsive){this.resize();}else{retinaScale(this,this.options.devicePixelRatio);} +this.bindEvents();this.notifyPlugins('afterInit');return this;} clear(){clearCanvas(this.canvas,this.ctx);return this;} stop(){animator.stop(this);return this;} resize(width,height){if(!animator.running(this)){this._resize(width,height);}else{this._resizeBeforeDraw={width,height};}} -_resize(width,height){const me=this;const options=me.options;const canvas=me.canvas;const aspectRatio=options.maintainAspectRatio&&me.aspectRatio;const newSize=me.platform.getMaximumSize(canvas,width,height,aspectRatio);const newRatio=options.devicePixelRatio||me.platform.getDevicePixelRatio();me.width=newSize.width;me.height=newSize.height;me._aspectRatio=me.aspectRatio;if(!retinaScale(me,newRatio,true)){return;} -me.notifyPlugins('resize',{size:newSize});callback(options.onResize,[me,newSize],me);if(me.attached){if(me._doResize()){me.render();}}} +_resize(width,height){const options=this.options;const canvas=this.canvas;const aspectRatio=options.maintainAspectRatio&&this.aspectRatio;const newSize=this.platform.getMaximumSize(canvas,width,height,aspectRatio);const newRatio=options.devicePixelRatio||this.platform.getDevicePixelRatio();const mode=this.width?'resize':'attach';this.width=newSize.width;this.height=newSize.height;this._aspectRatio=this.aspectRatio;if(!retinaScale(this,newRatio,true)){return;} +this.notifyPlugins('resize',{size:newSize});callback(options.onResize,[this,newSize],this);if(this.attached){if(this._doResize(mode)){this.render();}}} ensureScalesHaveIDs(){const options=this.options;const scalesOptions=options.scales||{};each(scalesOptions,(axisOptions,axisID)=>{axisOptions.id=axisID;});} -buildOrUpdateScales(){const me=this;const options=me.options;const scaleOpts=options.scales;const scales=me.scales;const updated=Object.keys(scales).reduce((obj,id)=>{obj[id]=false;return obj;},{});let items=[];if(scaleOpts){items=items.concat(Object.keys(scaleOpts).map((id)=>{const scaleOptions=scaleOpts[id];const axis=determineAxis(id,scaleOptions);const isRadial=axis==='r';const isHorizontal=axis==='x';return{options:scaleOptions,dposition:isRadial?'chartArea':isHorizontal?'bottom':'left',dtype:isRadial?'radialLinear':isHorizontal?'category':'linear'};}));} +buildOrUpdateScales(){const options=this.options;const scaleOpts=options.scales;const scales=this.scales;const updated=Object.keys(scales).reduce((obj,id)=>{obj[id]=false;return obj;},{});let items=[];if(scaleOpts){items=items.concat(Object.keys(scaleOpts).map((id)=>{const scaleOptions=scaleOpts[id];const axis=determineAxis(id,scaleOptions);const isRadial=axis==='r';const isHorizontal=axis==='x';return{options:scaleOptions,dposition:isRadial?'chartArea':isHorizontal?'bottom':'left',dtype:isRadial?'radialLinear':isHorizontal?'category':'linear'};}));} each(items,(item)=>{const scaleOptions=item.options;const id=scaleOptions.id;const axis=determineAxis(id,scaleOptions);const scaleType=valueOrDefault(scaleOptions.type,item.dtype);if(scaleOptions.position===undefined||positionIsHorizontal(scaleOptions.position,axis)!==positionIsHorizontal(item.dposition)){scaleOptions.position=item.dposition;} -updated[id]=true;let scale=null;if(id in scales&&scales[id].type===scaleType){scale=scales[id];}else{const scaleClass=registry.getScale(scaleType);scale=new scaleClass({id,type:scaleType,ctx:me.ctx,chart:me});scales[scale.id]=scale;} -scale.init(scaleOptions,options);});each(updated,(hasUpdated,id)=>{if(!hasUpdated){delete scales[id];}});each(scales,(scale)=>{layouts.configure(me,scale,scale.options);layouts.addBox(me,scale);});} -_updateMetasets(){const me=this;const metasets=me._metasets;const numData=me.data.datasets.length;const numMeta=metasets.length;metasets.sort((a,b)=>a.index-b.index);if(numMeta>numData){for(let i=numData;i{if(!hasUpdated){delete scales[id];}});each(scales,(scale)=>{layouts.configure(this,scale,scale.options);layouts.addBox(this,scale);});} +_updateMetasets(){const metasets=this._metasets;const numData=this.data.datasets.length;const numMeta=metasets.length;metasets.sort((a,b)=>a.index-b.index);if(numMeta>numData){for(let i=numData;idatasets.length){delete me._stacks;} -metasets.forEach((meta,index)=>{if(datasets.filter(x=>x===meta._dataset).length===0){me._destroyDatasetMeta(index);}});} -buildOrUpdateControllers(){const me=this;const newControllers=[];const datasets=me.data.datasets;let i,ilen;me._removeUnreferencedMetasets();for(i=0,ilen=datasets.length;i{me.getDatasetMeta(datasetIndex).controller.reset();},me);} +this._sortedMetasets=metasets.slice(0).sort(compare2Level('order','index'));} +_removeUnreferencedMetasets(){const{_metasets:metasets,data:{datasets}}=this;if(metasets.length>datasets.length){delete this._stacks;} +metasets.forEach((meta,index)=>{if(datasets.filter(x=>x===meta._dataset).length===0){this._destroyDatasetMeta(index);}});} +buildOrUpdateControllers(){const newControllers=[];const datasets=this.data.datasets;let i,ilen;this._removeUnreferencedMetasets();for(i=0,ilen=datasets.length;i{this.getDatasetMeta(datasetIndex).controller.reset();},this);} reset(){this._resetElements();this.notifyPlugins('reset');} -update(mode){const me=this;const config=me.config;config.update();me._options=config.createResolver(config.chartOptionScopes(),me.getContext());each(me.scales,(scale)=>{layouts.removeBox(me,scale);});const animsDisabled=me._animationsDisabled=!me.options.animation;me.ensureScalesHaveIDs();me.buildOrUpdateScales();const existingEvents=new Set(Object.keys(me._listeners));const newEvents=new Set(me.options.events);if(!setsEqual(existingEvents,newEvents)||!!this._responsiveListeners!==me.options.responsive){me.unbindEvents();me.bindEvents();} -me._plugins.invalidate();if(me.notifyPlugins('beforeUpdate',{mode,cancelable:true})===false){return;} -const newControllers=me.buildOrUpdateControllers();me.notifyPlugins('beforeElementsUpdate');let minPadding=0;for(let i=0,ilen=me.data.datasets.length;i{controller.reset();});} -me._updateDatasets(mode);me.notifyPlugins('afterUpdate',{mode});me._layers.sort(compare2Level('z','_idx'));if(me._lastEvent){me._eventHandler(me._lastEvent,true);} -me.render();} -_updateLayout(minPadding){const me=this;if(me.notifyPlugins('beforeLayout',{cancelable:true})===false){return;} -layouts.update(me,me.width,me.height,minPadding);const area=me.chartArea;const noArea=area.width<=0||area.height<=0;me._layers=[];each(me.boxes,(box)=>{if(noArea&&box.position==='chartArea'){return;} +update(mode){const config=this.config;config.update();const options=this._options=config.createResolver(config.chartOptionScopes(),this.getContext());const animsDisabled=this._animationsDisabled=!options.animation;this._updateScales();this._checkEventBindings();this._updateHiddenIndices();this._plugins.invalidate();if(this.notifyPlugins('beforeUpdate',{mode,cancelable:true})===false){return;} +const newControllers=this.buildOrUpdateControllers();this.notifyPlugins('beforeElementsUpdate');let minPadding=0;for(let i=0,ilen=this.data.datasets.length;i{controller.reset();});} +this._updateDatasets(mode);this.notifyPlugins('afterUpdate',{mode});this._layers.sort(compare2Level('z','_idx'));if(this._lastEvent){this._eventHandler(this._lastEvent,true);} +this.render();} +_updateScales(){each(this.scales,(scale)=>{layouts.removeBox(this,scale);});this.ensureScalesHaveIDs();this.buildOrUpdateScales();} +_checkEventBindings(){const options=this.options;const existingEvents=new Set(Object.keys(this._listeners));const newEvents=new Set(options.events);if(!setsEqual(existingEvents,newEvents)||!!this._responsiveListeners!==options.responsive){this.unbindEvents();this.bindEvents();}} +_updateHiddenIndices(){const{_hiddenIndices}=this;const changes=this._getUniformDataChanges()||[];for(const{method,start,count}of changes){const move=method==='_removeElements'?-count:count;moveNumericKeys(_hiddenIndices,start,move);}} +_getUniformDataChanges(){const _dataChanges=this._dataChanges;if(!_dataChanges||!_dataChanges.length){return;} +this._dataChanges=[];const datasetCount=this.data.datasets.length;const makeSet=(idx)=>new Set(_dataChanges.filter(c=>c[0]===idx).map((c,i)=>i+','+c.splice(1).join(',')));const changeSet=makeSet(0);for(let i=1;ic.split(',')).map(a=>({method:a[1],start:+a[2],count:+a[3]}));} +_updateLayout(minPadding){if(this.notifyPlugins('beforeLayout',{cancelable:true})===false){return;} +layouts.update(this,this.width,this.height,minPadding);const area=this.chartArea;const noArea=area.width<=0||area.height<=0;this._layers=[];each(this.boxes,(box)=>{if(noArea&&box.position==='chartArea'){return;} if(box.configure){box.configure();} -me._layers.push(...box._layers());},me);me._layers.forEach((item,index)=>{item._idx=index;});me.notifyPlugins('afterLayout');} -_updateDatasets(mode){const me=this;const isFunction=typeof mode==='function';if(me.notifyPlugins('beforeDatasetsUpdate',{mode,cancelable:true})===false){return;} -for(let i=0,ilen=me.data.datasets.length;i{item._idx=index;});this.notifyPlugins('afterLayout');} +_updateDatasets(mode){if(this.notifyPlugins('beforeDatasetsUpdate',{mode,cancelable:true})===false){return;} +for(let i=0,ilen=this.data.datasets.length;i=0;--i){me._drawDataset(metasets[i]);} -me.notifyPlugins('afterDatasetsDraw');} -_drawDataset(meta){const me=this;const ctx=me.ctx;const clip=meta._clip;const useClip=!clip.disabled;const area=me.chartArea;const args={meta,index:meta.index,cancelable:true};if(me.notifyPlugins('beforeDatasetDraw',args)===false){return;} -if(useClip){clipArea(ctx,{left:clip.left===false?0:area.left-clip.left,right:clip.right===false?me.width:area.right+clip.right,top:clip.top===false?0:area.top-clip.top,bottom:clip.bottom===false?me.height:area.bottom+clip.bottom});} +_drawDatasets(){if(this.notifyPlugins('beforeDatasetsDraw',{cancelable:true})===false){return;} +const metasets=this.getSortedVisibleDatasetMetas();for(let i=metasets.length-1;i>=0;--i){this._drawDataset(metasets[i]);} +this.notifyPlugins('afterDatasetsDraw');} +_drawDataset(meta){const ctx=this.ctx;const clip=meta._clip;const useClip=!clip.disabled;const area=this.chartArea;const args={meta,index:meta.index,cancelable:true};if(this.notifyPlugins('beforeDatasetDraw',args)===false){return;} +if(useClip){clipArea(ctx,{left:clip.left===false?0:area.left-clip.left,right:clip.right===false?this.width:area.right+clip.right,top:clip.top===false?0:area.top-clip.top,bottom:clip.bottom===false?this.height:area.bottom+clip.bottom});} meta.controller.draw();if(useClip){unclipArea(ctx);} -args.cancelable=false;me.notifyPlugins('afterDatasetDraw',args);} +args.cancelable=false;this.notifyPlugins('afterDatasetDraw',args);} getElementsAtEventForMode(e,mode,options,useFinalPosition){const method=Interaction.modes[mode];if(typeof method==='function'){return method(this,e,options,useFinalPosition);} return[];} -getDatasetMeta(datasetIndex){const me=this;const dataset=me.data.datasets[datasetIndex];const metasets=me._metasets;let meta=metasets.filter(x=>x&&x._dataset===dataset).pop();if(!meta){meta={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:dataset&&dataset.order||0,index:datasetIndex,_dataset:dataset,_parsed:[],_sorted:false};metasets.push(meta);} +getDatasetMeta(datasetIndex){const dataset=this.data.datasets[datasetIndex];const metasets=this._metasets;let meta=metasets.filter(x=>x&&x._dataset===dataset).pop();if(!meta){meta={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:dataset&&dataset.order||0,index:datasetIndex,_dataset:dataset,_parsed:[],_sorted:false};metasets.push(meta);} return meta;} -getContext(){return this.$context||(this.$context={chart:this,type:'chart'});} +getContext(){return this.$context||(this.$context=createContext(null,{chart:this,type:'chart'}));} getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length;} isDatasetVisible(datasetIndex){const dataset=this.data.datasets[datasetIndex];if(!dataset){return false;} const meta=this.getDatasetMeta(datasetIndex);return typeof meta.hidden==='boolean'?!meta.hidden:!dataset.hidden;} setDatasetVisibility(datasetIndex,visible){const meta=this.getDatasetMeta(datasetIndex);meta.hidden=!visible;} toggleDataVisibility(index){this._hiddenIndices[index]=!this._hiddenIndices[index];} getDataVisibility(index){return!this._hiddenIndices[index];} -_updateVisibility(datasetIndex,dataIndex,visible){const me=this;const mode=visible?'show':'hide';const meta=me.getDatasetMeta(datasetIndex);const anims=meta.controller._resolveAnimations(undefined,mode);if(defined(dataIndex)){meta.data[dataIndex].hidden=!visible;me.update();}else{me.setDatasetVisibility(datasetIndex,visible);anims.update(meta,{visible});me.update((ctx)=>ctx.datasetIndex===datasetIndex?mode:undefined);}} +_updateVisibility(datasetIndex,dataIndex,visible){const mode=visible?'show':'hide';const meta=this.getDatasetMeta(datasetIndex);const anims=meta.controller._resolveAnimations(undefined,mode);if(defined(dataIndex)){meta.data[dataIndex].hidden=!visible;this.update();}else{this.setDatasetVisibility(datasetIndex,visible);anims.update(meta,{visible});this.update((ctx)=>ctx.datasetIndex===datasetIndex?mode:undefined);}} hide(datasetIndex,dataIndex){this._updateVisibility(datasetIndex,dataIndex,false);} show(datasetIndex,dataIndex){this._updateVisibility(datasetIndex,dataIndex,true);} -_destroyDatasetMeta(datasetIndex){const me=this;const meta=me._metasets&&me._metasets[datasetIndex];if(meta&&meta.controller){meta.controller._destroy();delete me._metasets[datasetIndex];}} -destroy(){const me=this;const{canvas,ctx}=me;let i,ilen;me.stop();animator.remove(me);for(i=0,ilen=me.data.datasets.length;i{platform.addEventListener(me,type,listener);listeners[type]=listener;};const listener=function(e,x,y){e.offsetX=x;e.offsetY=y;me._eventHandler(e);};each(me.options.events,(type)=>_add(type,listener));} -bindResponsiveEvents(){const me=this;if(!me._responsiveListeners){me._responsiveListeners={};} -const listeners=me._responsiveListeners;const platform=me.platform;const _add=(type,listener)=>{platform.addEventListener(me,type,listener);listeners[type]=listener;};const _remove=(type,listener)=>{if(listeners[type]){platform.removeEventListener(me,type,listener);delete listeners[type];}};const listener=(width,height)=>{if(me.canvas){me.resize(width,height);}};let detached;const attached=()=>{_remove('attach',attached);me.attached=true;me.resize();_add('resize',listener);_add('detach',detached);};detached=()=>{me.attached=false;_remove('resize',listener);_add('attach',attached);};if(platform.isAttached(me.canvas)){attached();}else{detached();}} -unbindEvents(){const me=this;each(me._listeners,(listener,type)=>{me.platform.removeEventListener(me,type,listener);});me._listeners={};each(me._responsiveListeners,(listener,type)=>{me.platform.removeEventListener(me,type,listener);});me._responsiveListeners=undefined;} +bindUserEvents(){const listeners=this._listeners;const platform=this.platform;const _add=(type,listener)=>{platform.addEventListener(this,type,listener);listeners[type]=listener;};const listener=(e,x,y)=>{e.offsetX=x;e.offsetY=y;this._eventHandler(e);};each(this.options.events,(type)=>_add(type,listener));} +bindResponsiveEvents(){if(!this._responsiveListeners){this._responsiveListeners={};} +const listeners=this._responsiveListeners;const platform=this.platform;const _add=(type,listener)=>{platform.addEventListener(this,type,listener);listeners[type]=listener;};const _remove=(type,listener)=>{if(listeners[type]){platform.removeEventListener(this,type,listener);delete listeners[type];}};const listener=(width,height)=>{if(this.canvas){this.resize(width,height);}};let detached;const attached=()=>{_remove('attach',attached);this.attached=true;this.resize();_add('resize',listener);_add('detach',detached);};detached=()=>{this.attached=false;_remove('resize',listener);this._stop();this._resize(0,0);_add('attach',attached);};if(platform.isAttached(this.canvas)){attached();}else{detached();}} +unbindEvents(){each(this._listeners,(listener,type)=>{this.platform.removeEventListener(this,type,listener);});this._listeners={};each(this._responsiveListeners,(listener,type)=>{this.platform.removeEventListener(this,type,listener);});this._responsiveListeners=undefined;} updateHoverStyle(items,mode,enabled){const prefix=enabled?'set':'remove';let meta,item,i,ilen;if(mode==='dataset'){meta=this.getDatasetMeta(items[0].datasetIndex);meta.controller['_'+prefix+'DatasetHoverStyle']();} for(i=0,ilen=items.length;i{const meta=me.getDatasetMeta(datasetIndex);if(!meta){throw new Error('No dataset found at index '+datasetIndex);} -return{datasetIndex,element:meta.data[index],index,};});const changed=!_elementsEqual(active,lastActive);if(changed){me._active=active;me._updateHoverStyles(active,lastActive);}} +setActiveElements(activeElements){const lastActive=this._active||[];const active=activeElements.map(({datasetIndex,index})=>{const meta=this.getDatasetMeta(datasetIndex);if(!meta){throw new Error('No dataset found at index '+datasetIndex);} +return{datasetIndex,element:meta.data[index],index,};});const changed=!_elementsEqual(active,lastActive);if(changed){this._active=active;this._updateHoverStyles(active,lastActive);}} notifyPlugins(hook,args,filter){return this._plugins.notify(this,hook,args,filter);} -_updateHoverStyles(active,lastActive,replay){const me=this;const hoverOptions=me.options.hover;const diff=(a,b)=>a.filter(x=>!b.some(y=>x.datasetIndex===y.datasetIndex&&x.index===y.index));const deactivated=diff(lastActive,active);const activated=replay?active:diff(active,lastActive);if(deactivated.length){me.updateHoverStyle(deactivated,hoverOptions.mode,false);} -if(activated.length&&hoverOptions.mode){me.updateHoverStyle(activated,hoverOptions.mode,true);}} -_eventHandler(e,replay){const me=this;const args={event:e,replay,cancelable:true};const eventFilter=(plugin)=>(plugin.options.events||this.options.events).includes(e.type);if(me.notifyPlugins('beforeEvent',args,eventFilter)===false){return;} -const changed=me._handleEvent(e,replay);args.cancelable=false;me.notifyPlugins('afterEvent',args,eventFilter);if(changed||args.changed){me.render();} -return me;} -_handleEvent(e,replay){const me=this;const{_active:lastActive=[],options}=me;const hoverOptions=options.hover;const useFinalPosition=replay;let active=[];let changed=false;let lastEvent=null;if(e.type!=='mouseout'){active=me.getElementsAtEventForMode(e,hoverOptions.mode,hoverOptions,useFinalPosition);lastEvent=e.type==='click'?me._lastEvent:e;} -me._lastEvent=null;if(_isPointInArea(e,me.chartArea,me._minPadding)){callback(options.onHover,[e,active,me],me);if(e.type==='mouseup'||e.type==='click'||e.type==='contextmenu'){callback(options.onClick,[e,active,me],me);}} -changed=!_elementsEqual(active,lastActive);if(changed||replay){me._active=active;me._updateHoverStyles(active,lastActive,replay);} -me._lastEvent=lastEvent;return changed;}} +_updateHoverStyles(active,lastActive,replay){const hoverOptions=this.options.hover;const diff=(a,b)=>a.filter(x=>!b.some(y=>x.datasetIndex===y.datasetIndex&&x.index===y.index));const deactivated=diff(lastActive,active);const activated=replay?active:diff(active,lastActive);if(deactivated.length){this.updateHoverStyle(deactivated,hoverOptions.mode,false);} +if(activated.length&&hoverOptions.mode){this.updateHoverStyle(activated,hoverOptions.mode,true);}} +_eventHandler(e,replay){const args={event:e,replay,cancelable:true};const eventFilter=(plugin)=>(plugin.options.events||this.options.events).includes(e.native.type);if(this.notifyPlugins('beforeEvent',args,eventFilter)===false){return;} +const changed=this._handleEvent(e,replay);args.cancelable=false;this.notifyPlugins('afterEvent',args,eventFilter);if(changed||args.changed){this.render();} +return this;} +_handleEvent(e,replay){const{_active:lastActive=[],options}=this;const hoverOptions=options.hover;const useFinalPosition=replay;let active=[];let changed=false;let lastEvent=null;if(e.type!=='mouseout'){active=this.getElementsAtEventForMode(e,hoverOptions.mode,hoverOptions,useFinalPosition);lastEvent=e.type==='click'?this._lastEvent:e;} +this._lastEvent=null;if(_isPointInArea(e,this.chartArea,this._minPadding)){callback(options.onHover,[e,active,this],this);if(e.type==='mouseup'||e.type==='click'||e.type==='contextmenu'){callback(options.onClick,[e,active,this],this);}} +changed=!_elementsEqual(active,lastActive);if(changed||replay){this._active=active;this._updateHoverStyles(active,lastActive,replay);} +this._lastEvent=lastEvent;return changed;}} const invalidatePlugins=()=>each(Chart.instances,(chart)=>chart._plugins.invalidate());const enumerable=true;Object.defineProperties(Chart,{defaults:{enumerable,value:defaults},instances:{enumerable,value:instances},overrides:{enumerable,value:overrides},registry:{enumerable,value:registry},version:{enumerable,value:version},getChart:{enumerable,value:getChart},register:{enumerable,value:(...items)=>{registry.add(...items);invalidatePlugins();}},unregister:{enumerable,value:(...items)=>{registry.remove(...items);invalidatePlugins();}}});function abstract(){throw new Error('This method is not implemented: Check that a complete date adapter is provided.');} class DateAdapter{constructor(options){this.options=options||{};} formats(){return abstract();} @@ -1587,10 +1608,10 @@ add(timestamp,amount,unit){return abstract();} diff(a,b,unit){return abstract();} startOf(timestamp,unit,weekday){return abstract();} endOf(timestamp,unit){return abstract();}} -DateAdapter.override=function(members){Object.assign(DateAdapter.prototype,members);};var _adapters={_date:DateAdapter};function getAllScaleValues(scale){if(!scale._cache.$bar){const metas=scale.getMatchingVisibleMetas('bar');let values=[];for(let i=0,ilen=metas.length;ia-b));} return scale._cache.$bar;} -function computeMinSampleSize(scale){const values=getAllScaleValues(scale);let min=scale._length;let i,ilen,curr,prev;const updateMinAndPrev=()=>{if(curr===32767||curr===-32768){return;} +function computeMinSampleSize(meta){const scale=meta.iScale;const values=getAllScaleValues(scale,meta.type);let min=scale._length;let i,ilen,curr,prev;const updateMinAndPrev=()=>{if(curr===32767||curr===-32768){return;} if(defined(prev)){min=Math.min(min,Math.abs(curr-prev)||min);} prev=curr;};for(i=0,ilen=values.length;i=0;--i){max=Math.max(max,data[i].size()/2,_parsed[i]._custom);} +parseArrayData(meta,data,start,count){const parsed=super.parseArrayData(meta,data,start,count);for(let i=0;i=0;--i){max=Math.max(max,data[i].size(this.resolveDataElementOptions(i))/2);} return max>0&&max;} -getLabelAndValue(index){const me=this;const meta=me._cachedMeta;const{xScale,yScale}=meta;const parsed=me.getParsed(index);const x=xScale.getLabelForValue(parsed.x);const y=yScale.getLabelForValue(parsed.y);const r=parsed._custom;return{label:meta.label,value:'('+x+', '+y+(r?', '+r:'')+')'};} -update(mode){const me=this;const points=me._cachedMeta.data;me.updateElements(points,0,points.length,mode);} -updateElements(points,start,count,mode){const me=this;const reset=mode==='reset';const{iScale,vScale}=me._cachedMeta;const firstOpts=me.resolveDataElementOptions(start,mode);const sharedOptions=me.getSharedOptions(firstOpts);const includeOptions=me.includeOptions(mode,sharedOptions);const iAxis=iScale.axis;const vAxis=vScale.axis;for(let i=start;i+data[i];if(isObject(data[start])){const{key='value'}=this._parsing;getter=(i)=>+resolveObjectKey(data[i],key);} +let i,ilen;for(i=start,ilen=start+count;i0&&!isNaN(value)){return TAU*(Math.abs(value)/total);} return 0;} -getLabelAndValue(index){const me=this;const meta=me._cachedMeta;const chart=me.chart;const labels=chart.data.labels||[];const value=formatNumber(meta._parsed[index],chart.options.locale);return{label:labels[index]||'',value,};} -getMaxBorderWidth(arcs){const me=this;let max=0;const chart=me.chart;let i,ilen,meta,controller,options;if(!arcs){for(i=0,ilen=chart.data.datasets.length;iname!=='spacing',_indexable:(name)=>name!=='spacing',};DoughnutController.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(chart){const data=chart.data;if(data.labels.length&&data.datasets.length){const{labels:{pointStyle}}=chart.legend.options;return data.labels.map((label,i)=>{const meta=chart.getDatasetMeta(0);const style=meta.controller.getStyle(i);return{text:label,fillStyle:style.backgroundColor,strokeStyle:style.borderColor,lineWidth:style.borderWidth,pointStyle:pointStyle,hidden:!chart.getDataVisibility(i),index:i};});} return[];}},onClick(e,legendItem,legend){legend.chart.toggleDataVisibility(legendItem.index);legend.chart.update();}},tooltip:{callbacks:{title(){return'';},label(tooltipItem){let dataLabel=tooltipItem.label;const value=': '+tooltipItem.formattedValue;if(isArray(dataLabel)){dataLabel=dataLabel.slice();dataLabel[0]+=value;}else{dataLabel+=value;} return dataLabel;}}}}};class LineController extends DatasetController{initialize(){this.enableOptionSharing=true;super.initialize();} -update(mode){const me=this;const meta=me._cachedMeta;const{dataset:line,data:points=[],_dataset}=meta;const animationsDisabled=me.chart._animationsDisabled;let{start,count}=getStartAndCountOfVisiblePoints(meta,points,animationsDisabled);me._drawStart=start;me._drawCount=count;if(scaleRangesChanged(meta)){start=0;count=points.length;} -line._datasetIndex=me.index;line._decimated=!!_dataset._decimated;line.points=points;const options=me.resolveDatasetElementOptions(mode);if(!me.options.showLine){options.borderWidth=0;} -options.segment=me.options.segment;me.updateElement(line,undefined,{animated:!animationsDisabled,options},mode);me.updateElements(points,start,count,mode);} -updateElements(points,start,count,mode){const me=this;const reset=mode==='reset';const{iScale,vScale,_stacked}=me._cachedMeta;const firstOpts=me.resolveDataElementOptions(start,mode);const sharedOptions=me.getSharedOptions(firstOpts);const includeOptions=me.includeOptions(mode,sharedOptions);const iAxis=iScale.axis;const vAxis=vScale.axis;const spanGaps=me.options.spanGaps;const maxGapLength=isNumber(spanGaps)?spanGaps:Number.POSITIVE_INFINITY;const directUpdate=me.chart._animationsDisabled||reset||mode==='none';let prevParsed=start>0&&me.getParsed(start-1);for(let i=start;i0&&(parsed[iAxis]-prevParsed[iAxis])>maxGapLength;properties.parsed=parsed;if(includeOptions){properties.options=sharedOptions||me.resolveDataElementOptions(i,point.active?'active':mode);} -if(!directUpdate){me.updateElement(point,i,properties,mode);} +update(mode){const meta=this._cachedMeta;const{dataset:line,data:points=[],_dataset}=meta;const animationsDisabled=this.chart._animationsDisabled;let{start,count}=getStartAndCountOfVisiblePoints(meta,points,animationsDisabled);this._drawStart=start;this._drawCount=count;if(scaleRangesChanged(meta)){start=0;count=points.length;} +line._chart=this.chart;line._datasetIndex=this.index;line._decimated=!!_dataset._decimated;line.points=points;const options=this.resolveDatasetElementOptions(mode);if(!this.options.showLine){options.borderWidth=0;} +options.segment=this.options.segment;this.updateElement(line,undefined,{animated:!animationsDisabled,options},mode);this.updateElements(points,start,count,mode);} +updateElements(points,start,count,mode){const reset=mode==='reset';const{iScale,vScale,_stacked,_dataset}=this._cachedMeta;const firstOpts=this.resolveDataElementOptions(start,mode);const sharedOptions=this.getSharedOptions(firstOpts);const includeOptions=this.includeOptions(mode,sharedOptions);const iAxis=iScale.axis;const vAxis=vScale.axis;const{spanGaps,segment}=this.options;const maxGapLength=isNumber(spanGaps)?spanGaps:Number.POSITIVE_INFINITY;const directUpdate=this.chart._animationsDisabled||reset||mode==='none';let prevParsed=start>0&&this.getParsed(start-1);for(let i=start;i0&&(parsed[iAxis]-prevParsed[iAxis])>maxGapLength;if(segment){properties.parsed=parsed;properties.raw=_dataset.data[i];} +if(includeOptions){properties.options=sharedOptions||this.resolveDataElementOptions(i,point.active?'active':mode);} +if(!directUpdate){this.updateElement(point,i,properties,mode);} prevParsed=parsed;} -me.updateSharedOptions(sharedOptions,mode,firstOpts);} -getMaxOverflow(){const me=this;const meta=me._cachedMeta;const dataset=meta.dataset;const border=dataset.options&&dataset.options.borderWidth||0;const data=meta.data||[];if(!data.length){return border;} -const firstPoint=data[0].size(me.resolveDataElementOptions(0));const lastPoint=data[data.length-1].size(me.resolveDataElementOptions(data.length-1));return Math.max(border,firstPoint,lastPoint)/2;} +this.updateSharedOptions(sharedOptions,mode,firstOpts);} +getMaxOverflow(){const meta=this._cachedMeta;const dataset=meta.dataset;const border=dataset.options&&dataset.options.borderWidth||0;const data=meta.data||[];if(!data.length){return border;} +const firstPoint=data[0].size(this.resolveDataElementOptions(0));const lastPoint=data[data.length-1].size(this.resolveDataElementOptions(data.length-1));return Math.max(border,firstPoint,lastPoint)/2;} draw(){const meta=this._cachedMeta;meta.dataset.updateControlPoints(this.chart.chartArea,meta.iScale.axis);super.draw();}} LineController.id='line';LineController.defaults={datasetElementType:'line',dataElementType:'point',showLine:true,spanGaps:false,};LineController.overrides={scales:{_index_:{type:'category',},_value_:{type:'linear',},}};function getStartAndCountOfVisiblePoints(meta,points,animationsDisabled){const pointCount=points.length;let start=0;let count=pointCount;if(meta._sorted){const{iScale,_parsed}=meta;const axis=iScale.axis;const{min,max,minDefined,maxDefined}=iScale.getUserBounds();if(minDefined){start=_limitValue(Math.min(_lookupByKey(_parsed,iScale.axis,min).lo,animationsDisabled?pointCount:_lookupByKey(points,axis,iScale.getPixelForValue(min)).lo),0,pointCount-1);} if(maxDefined){count=_limitValue(Math.max(_lookupByKey(_parsed,iScale.axis,max).hi+1,animationsDisabled?0:_lookupByKey(points,axis,iScale.getPixelForValue(max)).hi+1),start,pointCount)-start;}else{count=pointCount-start;}} @@ -1715,22 +1742,22 @@ return{start,count};} function scaleRangesChanged(meta){const{xScale,yScale,_scaleRanges}=meta;const newRanges={xmin:xScale.min,xmax:xScale.max,ymin:yScale.min,ymax:yScale.max};if(!_scaleRanges){meta._scaleRanges=newRanges;return true;} const changed=_scaleRanges.xmin!==xScale.min||_scaleRanges.xmax!==xScale.max||_scaleRanges.ymin!==yScale.min||_scaleRanges.ymax!==yScale.max;Object.assign(_scaleRanges,newRanges);return changed;} class PolarAreaController extends DatasetController{constructor(chart,datasetIndex){super(chart,datasetIndex);this.innerRadius=undefined;this.outerRadius=undefined;} -getLabelAndValue(index){const me=this;const meta=me._cachedMeta;const chart=me.chart;const labels=chart.data.labels||[];const value=formatNumber(meta._parsed[index].r,chart.options.locale);return{label:labels[index]||'',value,};} +getLabelAndValue(index){const meta=this._cachedMeta;const chart=this.chart;const labels=chart.data.labels||[];const value=formatNumber(meta._parsed[index].r,chart.options.locale);return{label:labels[index]||'',value,};} update(mode){const arcs=this._cachedMeta.data;this._updateRadius();this.updateElements(arcs,0,arcs.length,mode);} -_updateRadius(){const me=this;const chart=me.chart;const chartArea=chart.chartArea;const opts=chart.options;const minSize=Math.min(chartArea.right-chartArea.left,chartArea.bottom-chartArea.top);const outerRadius=Math.max(minSize/2,0);const innerRadius=Math.max(opts.cutoutPercentage?(outerRadius/100)*(opts.cutoutPercentage):1,0);const radiusLength=(outerRadius-innerRadius)/chart.getVisibleDatasetCount();me.outerRadius=outerRadius-(radiusLength*me.index);me.innerRadius=me.outerRadius-radiusLength;} -updateElements(arcs,start,count,mode){const me=this;const reset=mode==='reset';const chart=me.chart;const dataset=me.getDataset();const opts=chart.options;const animationOpts=opts.animation;const scale=me._cachedMeta.rScale;const centerX=scale.xCenter;const centerY=scale.yCenter;const datasetStartAngle=scale.getIndexAngle(0)-0.5*PI;let angle=datasetStartAngle;let i;const defaultAngle=360/me.countVisibleElements();for(i=0;i{if(!isNaN(dataset.data[index])&&this.chart.getDataVisibility(index)){count++;}});return count;} _computeAngle(index,mode,defaultAngle){return this.chart.getDataVisibility(index)?toRadians(this.resolveDataElementOptions(index,mode).angle||defaultAngle):0;}} PolarAreaController.id='polarArea';PolarAreaController.defaults={dataElementType:'arc',animation:{animateRotate:true,animateScale:true},animations:{numbers:{type:'number',properties:['x','y','startAngle','endAngle','innerRadius','outerRadius']},},indexAxis:'r',startAngle:0,};PolarAreaController.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(chart){const data=chart.data;if(data.labels.length&&data.datasets.length){const{labels:{pointStyle}}=chart.legend.options;return data.labels.map((label,i)=>{const meta=chart.getDatasetMeta(0);const style=meta.controller.getStyle(i);return{text:label,fillStyle:style.backgroundColor,strokeStyle:style.borderColor,lineWidth:style.borderWidth,pointStyle:pointStyle,hidden:!chart.getDataVisibility(i),index:i};});} return[];}},onClick(e,legendItem,legend){legend.chart.toggleDataVisibility(legendItem.index);legend.chart.update();}},tooltip:{callbacks:{title(){return'';},label(context){return context.chart.data.labels[context.dataIndex]+': '+context.formattedValue;}}}},scales:{r:{type:'radialLinear',angleLines:{display:false},beginAtZero:true,grid:{circular:true},pointLabels:{display:false},startAngle:0}}};class PieController extends DoughnutController{} -PieController.id='pie';PieController.defaults={cutout:0,rotation:0,circumference:360,radius:'100%'};class RadarController extends DatasetController{getLabelAndValue(index){const me=this;const vScale=me._cachedMeta.vScale;const parsed=me.getParsed(index);return{label:vScale.getLabels()[index],value:''+vScale.getLabelForValue(parsed[vScale.axis])};} -update(mode){const me=this;const meta=me._cachedMeta;const line=meta.dataset;const points=meta.data||[];const labels=meta.iScale.getLabels();line.points=points;if(mode!=='resize'){const options=me.resolveDatasetElementOptions(mode);if(!me.options.showLine){options.borderWidth=0;} -const properties={_loop:true,_fullLoop:labels.length===points.length,options};me.updateElement(line,undefined,properties,mode);} -me.updateElements(points,0,points.length,mode);} -updateElements(points,start,count,mode){const me=this;const dataset=me.getDataset();const scale=me._cachedMeta.rScale;const reset=mode==='reset';for(let i=start;ipixelMargin){angleMargin=pixelMargin/innerRadius;ctx.arc(x,y,innerRadius,endAngle+angleMargin,startAngle-angleMargin,true);}else{ctx.arc(x,y,pixelMargin,endAngle+HALF_PI,startAngle-HALF_PI);} ctx.closePath();ctx.clip();} @@ -1755,12 +1782,12 @@ if(element.fullCircles){drawFullCircleBorders(ctx,element,inner);} if(inner){clipArc(ctx,element,endAngle);} pathArc(ctx,element,offset,spacing,endAngle);ctx.stroke();} class ArcElement extends Element{constructor(cfg){super();this.options=undefined;this.circumference=undefined;this.startAngle=undefined;this.endAngle=undefined;this.innerRadius=undefined;this.outerRadius=undefined;this.pixelMargin=0;this.fullCircles=0;if(cfg){Object.assign(this,cfg);}} -inRange(chartX,chartY,useFinalPosition){const point=this.getProps(['x','y'],useFinalPosition);const{angle,distance}=getAngleFromPoint(point,{x:chartX,y:chartY});const{startAngle,endAngle,innerRadius,outerRadius,circumference}=this.getProps(['startAngle','endAngle','innerRadius','outerRadius','circumference'],useFinalPosition);const rAdjust=this.options.spacing/2;const betweenAngles=circumference>=TAU||_angleBetween(angle,startAngle,endAngle);const withinRadius=(distance>=innerRadius+rAdjust&&distance<=outerRadius+rAdjust);return(betweenAngles&&withinRadius);} +inRange(chartX,chartY,useFinalPosition){const point=this.getProps(['x','y'],useFinalPosition);const{angle,distance}=getAngleFromPoint(point,{x:chartX,y:chartY});const{startAngle,endAngle,innerRadius,outerRadius,circumference}=this.getProps(['startAngle','endAngle','innerRadius','outerRadius','circumference'],useFinalPosition);const rAdjust=this.options.spacing/2;const _circumference=valueOrDefault(circumference,endAngle-startAngle);const betweenAngles=_circumference>=TAU||_angleBetween(angle,startAngle,endAngle);const withinRadius=_isBetween(distance,innerRadius+rAdjust,outerRadius+rAdjust);return(betweenAngles&&withinRadius);} getCenterPoint(useFinalPosition){const{x,y,startAngle,endAngle,innerRadius,outerRadius}=this.getProps(['x','y','startAngle','endAngle','innerRadius','outerRadius','circumference',],useFinalPosition);const{offset,spacing}=this.options;const halfAngle=(startAngle+endAngle)/2;const halfRadius=(innerRadius+outerRadius+spacing+offset)/2;return{x:x+Math.cos(halfAngle)*halfRadius,y:y+Math.sin(halfAngle)*halfRadius};} tooltipPosition(useFinalPosition){return this.getCenterPoint(useFinalPosition);} -draw(ctx){const me=this;const{options,circumference}=me;const offset=(options.offset||0)/2;const spacing=(options.spacing||0)/2;me.pixelMargin=(options.borderAlign==='inner')?0.33:0;me.fullCircles=circumference>TAU?Math.floor(circumference/TAU):0;if(circumference===0||me.innerRadius<0||me.outerRadius<0){return;} -ctx.save();let radiusOffset=0;if(offset){radiusOffset=offset/2;const halfAngle=(me.startAngle+me.endAngle)/2;ctx.translate(Math.cos(halfAngle)*radiusOffset,Math.sin(halfAngle)*radiusOffset);if(me.circumference>=PI){radiusOffset=offset;}} -ctx.fillStyle=options.backgroundColor;ctx.strokeStyle=options.borderColor;const endAngle=drawArc(ctx,me,radiusOffset,spacing);drawBorder(ctx,me,radiusOffset,spacing,endAngle);ctx.restore();}} +draw(ctx){const{options,circumference}=this;const offset=(options.offset||0)/2;const spacing=(options.spacing||0)/2;this.pixelMargin=(options.borderAlign==='inner')?0.33:0;this.fullCircles=circumference>TAU?Math.floor(circumference/TAU):0;if(circumference===0||this.innerRadius<0||this.outerRadius<0){return;} +ctx.save();let radiusOffset=0;if(offset){radiusOffset=offset/2;const halfAngle=(this.startAngle+this.endAngle)/2;ctx.translate(Math.cos(halfAngle)*radiusOffset,Math.sin(halfAngle)*radiusOffset);if(this.circumference>=PI){radiusOffset=offset;}} +ctx.fillStyle=options.backgroundColor;ctx.strokeStyle=options.borderColor;const endAngle=drawArc(ctx,this,radiusOffset,spacing);drawBorder(ctx,this,radiusOffset,spacing,endAngle);ctx.restore();}} ArcElement.id='arc';ArcElement.defaults={borderAlign:'center',borderColor:'#fff',borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:undefined,};ArcElement.defaultRoutes={backgroundColor:'backgroundColor'};function setStyle(ctx,options,style=options){ctx.lineCap=valueOrDefault(style.borderCapStyle,options.borderCapStyle);ctx.setLineDash(valueOrDefault(style.borderDash,options.borderDash));ctx.lineDashOffset=valueOrDefault(style.borderDashOffset,options.borderDashOffset);ctx.lineJoin=valueOrDefault(style.borderJoinStyle,options.borderJoinStyle);ctx.lineWidth=valueOrDefault(style.borderWidth,options.borderWidth);ctx.strokeStyle=valueOrDefault(style.borderColor,options.borderColor);} function lineTo(ctx,previous,target){ctx.lineTo(target.x,target.y);} function getLineMethod(options){if(options.stepped){return _steppedLineTo;} @@ -1785,23 +1812,23 @@ function strokePathWithCache(ctx,line,start,count){let path=line._path;if(!path) setStyle(ctx,line.options);ctx.stroke(path);} function strokePathDirect(ctx,line,start,count){const{segments,options}=line;const segmentMethod=_getSegmentMethod(line);for(const segment of segments){setStyle(ctx,options,segment.style);ctx.beginPath();if(segmentMethod(ctx,line,segment,{start,end:start+count-1})){ctx.closePath();} ctx.stroke();}} -const usePath2D=typeof Path2D==='function';function draw(ctx,line,start,count){if(usePath2D&&line.segments.length===1){strokePathWithCache(ctx,line,start,count);}else{strokePathDirect(ctx,line,start,count);}} -class LineElement extends Element{constructor(cfg){super();this.animated=true;this.options=undefined;this._loop=undefined;this._fullLoop=undefined;this._path=undefined;this._points=undefined;this._segments=undefined;this._decimated=false;this._pointsUpdated=false;this._datasetIndex=undefined;if(cfg){Object.assign(this,cfg);}} -updateControlPoints(chartArea,indexAxis){const me=this;const options=me.options;if((options.tension||options.cubicInterpolationMode==='monotone')&&!options.stepped&&!me._pointsUpdated){const loop=options.spanGaps?me._loop:me._fullLoop;_updateBezierControlPoints(me._points,options,chartArea,loop,indexAxis);me._pointsUpdated=true;}} -set points(points){const me=this;me._points=points;delete me._segments;delete me._path;me._pointsUpdated=false;} +const usePath2D=typeof Path2D==='function';function draw(ctx,line,start,count){if(usePath2D&&!line.options.segment){strokePathWithCache(ctx,line,start,count);}else{strokePathDirect(ctx,line,start,count);}} +class LineElement extends Element{constructor(cfg){super();this.animated=true;this.options=undefined;this._chart=undefined;this._loop=undefined;this._fullLoop=undefined;this._path=undefined;this._points=undefined;this._segments=undefined;this._decimated=false;this._pointsUpdated=false;this._datasetIndex=undefined;if(cfg){Object.assign(this,cfg);}} +updateControlPoints(chartArea,indexAxis){const options=this.options;if((options.tension||options.cubicInterpolationMode==='monotone')&&!options.stepped&&!this._pointsUpdated){const loop=options.spanGaps?this._loop:this._fullLoop;_updateBezierControlPoints(this._points,options,chartArea,loop,indexAxis);this._pointsUpdated=true;}} +set points(points){this._points=points;delete this._segments;delete this._path;this._pointsUpdated=false;} get points(){return this._points;} get segments(){return this._segments||(this._segments=_computeSegments(this,this.options.segment));} first(){const segments=this.segments;const points=this.points;return segments.length&&points[segments[0].start];} last(){const segments=this.segments;const points=this.points;const count=segments.length;return count&&points[segments[count-1].end];} -interpolate(point,property){const me=this;const options=me.options;const value=point[property];const points=me.points;const segments=_boundSegments(me,{property,start:value,end:value});if(!segments.length){return;} +interpolate(point,property){const options=this.options;const value=point[property];const points=this.points;const segments=_boundSegments(this,{property,start:value,end:value});if(!segments.length){return;} const result=[];const _interpolate=_getInterpolationMethod(options);let i,ilen;for(i=0,ilen=segments.length;iname!=='borderDash'&&name!=='fill',};function inRange$1(el,pos,axis,useFinalPosition){const options=el.options;const{[axis]:value}=el.getProps([axis],useFinalPosition);return(Math.abs(pos-value)=bounds.left&&x<=bounds.right)&&(skipY||y>=bounds.top&&y<=bounds.bottom);} +function inRange(bar,x,y,useFinalPosition){const skipX=x===null;const skipY=y===null;const skipBoth=skipX&&skipY;const bounds=bar&&!skipBoth&&getBarBounds(bar,useFinalPosition);return bounds&&(skipX||_isBetween(x,bounds.left,bounds.right))&&(skipY||_isBetween(y,bounds.top,bounds.bottom));} function hasRadius(radius){return radius.topLeft||radius.topRight||radius.bottomLeft||radius.bottomRight;} function addNormalRectPath(ctx,rect){ctx.rect(rect.x,rect.y,rect.w,rect.h);} function inflateRect(rect,amount,refRect={}){const x=rect.x!==refRect.x?-amount:0;const y=rect.y!==refRect.y?-amount:0;const w=(rect.x+rect.w!==refRect.x+refRect.w?amount:0)-x;const h=(rect.y+rect.h!==refRect.y+refRect.h?amount:0)-y;return{x:rect.x+x,y:rect.y+y,w:rect.w+w,h:rect.h+h,radius:rect.radius};} -class BarElement extends Element{constructor(cfg){super();this.options=undefined;this.horizontal=undefined;this.base=undefined;this.width=undefined;this.height=undefined;if(cfg){Object.assign(this,cfg);}} -draw(ctx){const options=this.options;const{inner,outer}=boundingRects(this);const addRectPath=hasRadius(outer.radius)?addRoundedRectPath:addNormalRectPath;const inflateAmount=0.33;ctx.save();if(outer.w!==inner.w||outer.h!==inner.h){ctx.beginPath();addRectPath(ctx,inflateRect(outer,inflateAmount,inner));ctx.clip();addRectPath(ctx,inflateRect(inner,-inflateAmount,outer));ctx.fillStyle=options.borderColor;ctx.fill('evenodd');} -ctx.beginPath();addRectPath(ctx,inflateRect(inner,inflateAmount,outer));ctx.fillStyle=options.backgroundColor;ctx.fill();ctx.restore();} +class BarElement extends Element{constructor(cfg){super();this.options=undefined;this.horizontal=undefined;this.base=undefined;this.width=undefined;this.height=undefined;this.inflateAmount=undefined;if(cfg){Object.assign(this,cfg);}} +draw(ctx){const{inflateAmount,options:{borderColor,backgroundColor}}=this;const{inner,outer}=boundingRects(this);const addRectPath=hasRadius(outer.radius)?addRoundedRectPath:addNormalRectPath;ctx.save();if(outer.w!==inner.w||outer.h!==inner.h){ctx.beginPath();addRectPath(ctx,inflateRect(outer,inflateAmount,inner));ctx.clip();addRectPath(ctx,inflateRect(inner,-inflateAmount,outer));ctx.fillStyle=borderColor;ctx.fill('evenodd');} +ctx.beginPath();addRectPath(ctx,inflateRect(inner,inflateAmount));ctx.fillStyle=backgroundColor;ctx.fill();ctx.restore();} inRange(mouseX,mouseY,useFinalPosition){return inRange(this,mouseX,mouseY,useFinalPosition);} inXRange(mouseX,useFinalPosition){return inRange(this,mouseX,null,useFinalPosition);} inYRange(mouseY,useFinalPosition){return inRange(this,null,mouseY,useFinalPosition);} getCenterPoint(useFinalPosition){const{x,y,base,horizontal}=this.getProps(['x','y','base','horizontal'],useFinalPosition);return{x:horizontal?(x+base)/2:x,y:horizontal?y:(y+base)/2};} getRange(axis){return axis==='x'?this.width/2:this.height/2;}} -BarElement.id='bar';BarElement.defaults={borderSkipped:'start',borderWidth:0,borderRadius:0,enableBorderRadius:true,pointStyle:undefined};BarElement.defaultRoutes={backgroundColor:'backgroundColor',borderColor:'borderColor'};var elements=Object.freeze({__proto__:null,ArcElement:ArcElement,LineElement:LineElement,PointElement:PointElement,BarElement:BarElement});function lttbDecimation(data,start,count,availableWidth,options){const samples=options.samples||availableWidth;if(samples>=count){return data.slice(start,start+count);} +BarElement.id='bar';BarElement.defaults={borderSkipped:'start',borderWidth:0,borderRadius:0,inflateAmount:'auto',pointStyle:undefined};BarElement.defaultRoutes={backgroundColor:'backgroundColor',borderColor:'borderColor'};var elements=Object.freeze({__proto__:null,ArcElement:ArcElement,LineElement:LineElement,PointElement:PointElement,BarElement:BarElement});function lttbDecimation(data,start,count,availableWidth,options){const samples=options.samples||availableWidth;if(samples>=count){return data.slice(start,start+count);} const decimated=[];const bucketWidth=(count-2)/(samples-2);let sampledIndex=0;const endIndex=start+count-1;let a=start;let i,maxAreaPoint,maxArea,area,nextA;decimated[sampledIndex++]=data[a];for(i=0;imaxArea){maxArea=area;maxAreaPoint=data[j];nextA=j;}} @@ -1880,16 +1907,16 @@ return computeLinearBoundary(source);} function findSegmentEnd(start,end,points){for(;end>start;end--){const point=points[end];if(!isNaN(point.x)&&!isNaN(point.y)){break;}} return end;} function pointsFromSegments(boundary,line){const{x=null,y=null}=boundary||{};const linePoints=line.points;const points=[];line.segments.forEach(({start,end})=>{end=findSegmentEnd(start,end,linePoints);const first=linePoints[start];const last=linePoints[end];if(y!==null){points.push({x:first.x,y});points.push({x:last.x,y});}else if(x!==null){points.push({x,y:first.y});points.push({x,y:last.y});}});return points;} -function buildStackLine(source){const{chart,scale,index,line}=source;const points=[];const segments=line.segments;const sourcePoints=line.points;const linesBelow=getLinesBelow(chart,index);linesBelow.push(createBoundaryLine({x:null,y:scale.bottom},line));for(let i=0;imeta.type==='line'&&!meta.hidden;function getLinesBelow(chart,index){const below=[];const metas=chart.getSortedVisibleDatasetMetas();for(let i=0;i=firstValue&&pointValue<=lastValue){first=pointValue===firstValue;last=pointValue===lastValue;break;}} +const pointValue=point[property];const segments=line.segments;const linePoints=line.points;let first=false;let last=false;for(let i=0;i=0;--i){const source=metasets[i].$filler;if(source){drawfill(chart.ctx,source,chart.chartArea);}}},beforeDatasetDraw(chart,args,options){const source=args.meta.$filler;if(!source||source.fill===false||options.drawTime!=='beforeDatasetDraw'){return;} drawfill(chart.ctx,source,chart.chartArea);},defaults:{propagate:true,drawTime:'beforeDatasetDraw'}};const getBoxSize=(labelOpts,fontSize)=>{let{boxHeight=fontSize,boxWidth=fontSize}=labelOpts;if(labelOpts.usePointStyle){boxHeight=Math.min(boxHeight,fontSize);boxWidth=Math.min(boxWidth,fontSize);} return{boxWidth,boxHeight,itemHeight:Math.max(fontSize,boxHeight)};};const itemsEqual=(a,b)=>a!==null&&b!==null&&a.datasetIndex===b.datasetIndex&&a.index===b.index;class Legend extends Element{constructor(config){super();this._added=false;this.legendHitBoxes=[];this._hoveredItem=null;this.doughnutMode=false;this.chart=config.chart;this.options=config.options;this.ctx=config.ctx;this.legendItems=undefined;this.columnSizes=undefined;this.lineWidths=undefined;this.maxHeight=undefined;this.maxWidth=undefined;this.top=undefined;this.bottom=undefined;this.left=undefined;this.right=undefined;this.height=undefined;this.width=undefined;this._margins=undefined;this.position=undefined;this.weight=undefined;this.fullSize=undefined;} -update(maxWidth,maxHeight,margins){const me=this;me.maxWidth=maxWidth;me.maxHeight=maxHeight;me._margins=margins;me.setDimensions();me.buildLabels();me.fit();} -setDimensions(){const me=this;if(me.isHorizontal()){me.width=me.maxWidth;me.left=me._margins.left;me.right=me.width;}else{me.height=me.maxHeight;me.top=me._margins.top;me.bottom=me.height;}} -buildLabels(){const me=this;const labelOpts=me.options.labels||{};let legendItems=callback(labelOpts.generateLabels,[me.chart],me)||[];if(labelOpts.filter){legendItems=legendItems.filter((item)=>labelOpts.filter(item,me.chart.data));} -if(labelOpts.sort){legendItems=legendItems.sort((a,b)=>labelOpts.sort(a,b,me.chart.data));} -if(me.options.reverse){legendItems.reverse();} -me.legendItems=legendItems;} -fit(){const me=this;const{options,ctx}=me;if(!options.display){me.width=me.height=0;return;} -const labelOpts=options.labels;const labelFont=toFont(labelOpts.font);const fontSize=labelFont.size;const titleHeight=me._computeTitleHeight();const{boxWidth,itemHeight}=getBoxSize(labelOpts,fontSize);let width,height;ctx.font=labelFont.string;if(me.isHorizontal()){width=me.maxWidth;height=me._fitRows(titleHeight,fontSize,boxWidth,itemHeight)+10;}else{height=me.maxHeight;width=me._fitCols(titleHeight,fontSize,boxWidth,itemHeight)+10;} -me.width=Math.min(width,options.maxWidth||me.maxWidth);me.height=Math.min(height,options.maxHeight||me.maxHeight);} -_fitRows(titleHeight,fontSize,boxWidth,itemHeight){const me=this;const{ctx,maxWidth,options:{labels:{padding}}}=me;const hitboxes=me.legendHitBoxes=[];const lineWidths=me.lineWidths=[0];const lineHeight=itemHeight+padding;let totalHeight=titleHeight;ctx.textAlign='left';ctx.textBaseline='middle';let row=-1;let top=-lineHeight;me.legendItems.forEach((legendItem,i)=>{const itemWidth=boxWidth+(fontSize/2)+ctx.measureText(legendItem.text).width;if(i===0||lineWidths[lineWidths.length-1]+itemWidth+2*padding>maxWidth){totalHeight+=lineHeight;lineWidths[lineWidths.length-(i>0?0:1)]=0;top+=lineHeight;row++;} +update(maxWidth,maxHeight,margins){this.maxWidth=maxWidth;this.maxHeight=maxHeight;this._margins=margins;this.setDimensions();this.buildLabels();this.fit();} +setDimensions(){if(this.isHorizontal()){this.width=this.maxWidth;this.left=this._margins.left;this.right=this.width;}else{this.height=this.maxHeight;this.top=this._margins.top;this.bottom=this.height;}} +buildLabels(){const labelOpts=this.options.labels||{};let legendItems=callback(labelOpts.generateLabels,[this.chart],this)||[];if(labelOpts.filter){legendItems=legendItems.filter((item)=>labelOpts.filter(item,this.chart.data));} +if(labelOpts.sort){legendItems=legendItems.sort((a,b)=>labelOpts.sort(a,b,this.chart.data));} +if(this.options.reverse){legendItems.reverse();} +this.legendItems=legendItems;} +fit(){const{options,ctx}=this;if(!options.display){this.width=this.height=0;return;} +const labelOpts=options.labels;const labelFont=toFont(labelOpts.font);const fontSize=labelFont.size;const titleHeight=this._computeTitleHeight();const{boxWidth,itemHeight}=getBoxSize(labelOpts,fontSize);let width,height;ctx.font=labelFont.string;if(this.isHorizontal()){width=this.maxWidth;height=this._fitRows(titleHeight,fontSize,boxWidth,itemHeight)+10;}else{height=this.maxHeight;width=this._fitCols(titleHeight,fontSize,boxWidth,itemHeight)+10;} +this.width=Math.min(width,options.maxWidth||this.maxWidth);this.height=Math.min(height,options.maxHeight||this.maxHeight);} +_fitRows(titleHeight,fontSize,boxWidth,itemHeight){const{ctx,maxWidth,options:{labels:{padding}}}=this;const hitboxes=this.legendHitBoxes=[];const lineWidths=this.lineWidths=[0];const lineHeight=itemHeight+padding;let totalHeight=titleHeight;ctx.textAlign='left';ctx.textBaseline='middle';let row=-1;let top=-lineHeight;this.legendItems.forEach((legendItem,i)=>{const itemWidth=boxWidth+(fontSize/2)+ctx.measureText(legendItem.text).width;if(i===0||lineWidths[lineWidths.length-1]+itemWidth+2*padding>maxWidth){totalHeight+=lineHeight;lineWidths[lineWidths.length-(i>0?0:1)]=0;top+=lineHeight;row++;} hitboxes[i]={left:0,top,row,width:itemWidth,height:itemHeight};lineWidths[lineWidths.length-1]+=itemWidth+padding;});return totalHeight;} -_fitCols(titleHeight,fontSize,boxWidth,itemHeight){const me=this;const{ctx,maxHeight,options:{labels:{padding}}}=me;const hitboxes=me.legendHitBoxes=[];const columnSizes=me.columnSizes=[];const heightLimit=maxHeight-titleHeight;let totalWidth=padding;let currentColWidth=0;let currentColHeight=0;let left=0;let col=0;me.legendItems.forEach((legendItem,i)=>{const itemWidth=boxWidth+(fontSize/2)+ctx.measureText(legendItem.text).width;if(i>0&¤tColHeight+itemHeight+2*padding>heightLimit){totalWidth+=currentColWidth+padding;columnSizes.push({width:currentColWidth,height:currentColHeight});left+=currentColWidth+padding;col++;currentColWidth=currentColHeight=0;} +_fitCols(titleHeight,fontSize,boxWidth,itemHeight){const{ctx,maxHeight,options:{labels:{padding}}}=this;const hitboxes=this.legendHitBoxes=[];const columnSizes=this.columnSizes=[];const heightLimit=maxHeight-titleHeight;let totalWidth=padding;let currentColWidth=0;let currentColHeight=0;let left=0;let col=0;this.legendItems.forEach((legendItem,i)=>{const itemWidth=boxWidth+(fontSize/2)+ctx.measureText(legendItem.text).width;if(i>0&¤tColHeight+itemHeight+2*padding>heightLimit){totalWidth+=currentColWidth+padding;columnSizes.push({width:currentColWidth,height:currentColHeight});left+=currentColWidth+padding;col++;currentColWidth=currentColHeight=0;} hitboxes[i]={left,top:currentColHeight,col,width:itemWidth,height:itemHeight};currentColWidth=Math.max(currentColWidth,itemWidth);currentColHeight+=itemHeight+padding;});totalWidth+=currentColWidth;columnSizes.push({width:currentColWidth,height:currentColHeight});return totalWidth;} -adjustHitBoxes(){const me=this;if(!me.options.display){return;} -const titleHeight=me._computeTitleHeight();const{legendHitBoxes:hitboxes,options:{align,labels:{padding},rtl}}=me;const rtlHelper=getRtlAdapter(rtl,me.left,me.width);if(this.isHorizontal()){let row=0;let left=_alignStartEnd(align,me.left+padding,me.right-me.lineWidths[row]);for(const hitbox of hitboxes){if(row!==hitbox.row){row=hitbox.row;left=_alignStartEnd(align,me.left+padding,me.right-me.lineWidths[row]);} -hitbox.top+=me.top+titleHeight+padding;hitbox.left=rtlHelper.leftForLtr(rtlHelper.x(left),hitbox.width);left+=hitbox.width+padding;}}else{let col=0;let top=_alignStartEnd(align,me.top+titleHeight+padding,me.bottom-me.columnSizes[col].height);for(const hitbox of hitboxes){if(hitbox.col!==col){col=hitbox.col;top=_alignStartEnd(align,me.top+titleHeight+padding,me.bottom-me.columnSizes[col].height);} -hitbox.top=top;hitbox.left+=me.left+padding;hitbox.left=rtlHelper.leftForLtr(rtlHelper.x(hitbox.left),hitbox.width);top+=hitbox.height+padding;}}} +adjustHitBoxes(){if(!this.options.display){return;} +const titleHeight=this._computeTitleHeight();const{legendHitBoxes:hitboxes,options:{align,labels:{padding},rtl}}=this;const rtlHelper=getRtlAdapter(rtl,this.left,this.width);if(this.isHorizontal()){let row=0;let left=_alignStartEnd(align,this.left+padding,this.right-this.lineWidths[row]);for(const hitbox of hitboxes){if(row!==hitbox.row){row=hitbox.row;left=_alignStartEnd(align,this.left+padding,this.right-this.lineWidths[row]);} +hitbox.top+=this.top+titleHeight+padding;hitbox.left=rtlHelper.leftForLtr(rtlHelper.x(left),hitbox.width);left+=hitbox.width+padding;}}else{let col=0;let top=_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-this.columnSizes[col].height);for(const hitbox of hitboxes){if(hitbox.col!==col){col=hitbox.col;top=_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-this.columnSizes[col].height);} +hitbox.top=top;hitbox.left+=this.left+padding;hitbox.left=rtlHelper.leftForLtr(rtlHelper.x(hitbox.left),hitbox.width);top+=hitbox.height+padding;}}} isHorizontal(){return this.options.position==='top'||this.options.position==='bottom';} -draw(){const me=this;if(me.options.display){const ctx=me.ctx;clipArea(ctx,me);me._draw();unclipArea(ctx);}} -_draw(){const me=this;const{options:opts,columnSizes,lineWidths,ctx}=me;const{align,labels:labelOpts}=opts;const defaultColor=defaults.color;const rtlHelper=getRtlAdapter(opts.rtl,me.left,me.width);const labelFont=toFont(labelOpts.font);const{color:fontColor,padding}=labelOpts;const fontSize=labelFont.size;const halfFontSize=fontSize/2;let cursor;me.drawTitle();ctx.textAlign=rtlHelper.textAlign('left');ctx.textBaseline='middle';ctx.lineWidth=0.5;ctx.font=labelFont.string;const{boxWidth,boxHeight,itemHeight}=getBoxSize(labelOpts,fontSize);const drawLegendBox=function(x,y,legendItem){if(isNaN(boxWidth)||boxWidth<=0||isNaN(boxHeight)||boxHeight<0){return;} +draw(){if(this.options.display){const ctx=this.ctx;clipArea(ctx,this);this._draw();unclipArea(ctx);}} +_draw(){const{options:opts,columnSizes,lineWidths,ctx}=this;const{align,labels:labelOpts}=opts;const defaultColor=defaults.color;const rtlHelper=getRtlAdapter(opts.rtl,this.left,this.width);const labelFont=toFont(labelOpts.font);const{color:fontColor,padding}=labelOpts;const fontSize=labelFont.size;const halfFontSize=fontSize/2;let cursor;this.drawTitle();ctx.textAlign=rtlHelper.textAlign('left');ctx.textBaseline='middle';ctx.lineWidth=0.5;ctx.font=labelFont.string;const{boxWidth,boxHeight,itemHeight}=getBoxSize(labelOpts,fontSize);const drawLegendBox=function(x,y,legendItem){if(isNaN(boxWidth)||boxWidth<=0||isNaN(boxHeight)||boxHeight<0){return;} ctx.save();const lineWidth=valueOrDefault(legendItem.lineWidth,1);ctx.fillStyle=valueOrDefault(legendItem.fillStyle,defaultColor);ctx.lineCap=valueOrDefault(legendItem.lineCap,'butt');ctx.lineDashOffset=valueOrDefault(legendItem.lineDashOffset,0);ctx.lineJoin=valueOrDefault(legendItem.lineJoin,'miter');ctx.lineWidth=lineWidth;ctx.strokeStyle=valueOrDefault(legendItem.strokeStyle,defaultColor);ctx.setLineDash(valueOrDefault(legendItem.lineDash,[]));if(labelOpts.usePointStyle){const drawOptions={radius:boxWidth*Math.SQRT2/2,pointStyle:legendItem.pointStyle,rotation:legendItem.rotation,borderWidth:lineWidth};const centerX=rtlHelper.xPlus(x,boxWidth/2);const centerY=y+halfFontSize;drawPoint(ctx,drawOptions,centerX,centerY);}else{const yBoxTop=y+Math.max((fontSize-boxHeight)/2,0);const xBoxLeft=rtlHelper.leftForLtr(x,boxWidth);const borderRadius=toTRBLCorners(legendItem.borderRadius);ctx.beginPath();if(Object.values(borderRadius).some(v=>v!==0)){addRoundedRectPath(ctx,{x:xBoxLeft,y:yBoxTop,w:boxWidth,h:boxHeight,radius:borderRadius,});}else{ctx.rect(xBoxLeft,yBoxTop,boxWidth,boxHeight);} ctx.fill();if(lineWidth!==0){ctx.stroke();}} -ctx.restore();};const fillText=function(x,y,legendItem){renderText(ctx,legendItem.text,x,y+(itemHeight/2),labelFont,{strikethrough:legendItem.hidden,textAlign:rtlHelper.textAlign(legendItem.textAlign)});};const isHorizontal=me.isHorizontal();const titleHeight=this._computeTitleHeight();if(isHorizontal){cursor={x:_alignStartEnd(align,me.left+padding,me.right-lineWidths[0]),y:me.top+padding+titleHeight,line:0};}else{cursor={x:me.left+padding,y:_alignStartEnd(align,me.top+titleHeight+padding,me.bottom-columnSizes[0].height),line:0};} -overrideTextDirection(me.ctx,opts.textDirection);const lineHeight=itemHeight+padding;me.legendItems.forEach((legendItem,i)=>{ctx.strokeStyle=legendItem.fontColor||fontColor;ctx.fillStyle=legendItem.fontColor||fontColor;const textWidth=ctx.measureText(legendItem.text).width;const textAlign=rtlHelper.textAlign(legendItem.textAlign||(legendItem.textAlign=labelOpts.textAlign));const width=boxWidth+halfFontSize+textWidth;let x=cursor.x;let y=cursor.y;rtlHelper.setWidth(me.width);if(isHorizontal){if(i>0&&x+width+padding>me.right){y=cursor.y+=lineHeight;cursor.line++;x=cursor.x=_alignStartEnd(align,me.left+padding,me.right-lineWidths[cursor.line]);}}else if(i>0&&y+lineHeight>me.bottom){x=cursor.x=x+columnSizes[cursor.line].width+padding;cursor.line++;y=cursor.y=_alignStartEnd(align,me.top+titleHeight+padding,me.bottom-columnSizes[cursor.line].height);} -const realX=rtlHelper.x(x);drawLegendBox(realX,y,legendItem);x=_textX(textAlign,x+boxWidth+halfFontSize,isHorizontal?x+width:me.right,opts.rtl);fillText(rtlHelper.x(x),y,legendItem);if(isHorizontal){cursor.x+=width+padding;}else{cursor.y+=lineHeight;}});restoreTextDirection(me.ctx,opts.textDirection);} -drawTitle(){const me=this;const opts=me.options;const titleOpts=opts.title;const titleFont=toFont(titleOpts.font);const titlePadding=toPadding(titleOpts.padding);if(!titleOpts.display){return;} -const rtlHelper=getRtlAdapter(opts.rtl,me.left,me.width);const ctx=me.ctx;const position=titleOpts.position;const halfFontSize=titleFont.size/2;const topPaddingPlusHalfFontSize=titlePadding.top+halfFontSize;let y;let left=me.left;let maxWidth=me.width;if(this.isHorizontal()){maxWidth=Math.max(...me.lineWidths);y=me.top+topPaddingPlusHalfFontSize;left=_alignStartEnd(opts.align,left,me.right-maxWidth);}else{const maxHeight=me.columnSizes.reduce((acc,size)=>Math.max(acc,size.height),0);y=topPaddingPlusHalfFontSize+_alignStartEnd(opts.align,me.top,me.bottom-maxHeight-opts.labels.padding-me._computeTitleHeight());} +ctx.restore();};const fillText=function(x,y,legendItem){renderText(ctx,legendItem.text,x,y+(itemHeight/2),labelFont,{strikethrough:legendItem.hidden,textAlign:rtlHelper.textAlign(legendItem.textAlign)});};const isHorizontal=this.isHorizontal();const titleHeight=this._computeTitleHeight();if(isHorizontal){cursor={x:_alignStartEnd(align,this.left+padding,this.right-lineWidths[0]),y:this.top+padding+titleHeight,line:0};}else{cursor={x:this.left+padding,y:_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-columnSizes[0].height),line:0};} +overrideTextDirection(this.ctx,opts.textDirection);const lineHeight=itemHeight+padding;this.legendItems.forEach((legendItem,i)=>{ctx.strokeStyle=legendItem.fontColor||fontColor;ctx.fillStyle=legendItem.fontColor||fontColor;const textWidth=ctx.measureText(legendItem.text).width;const textAlign=rtlHelper.textAlign(legendItem.textAlign||(legendItem.textAlign=labelOpts.textAlign));const width=boxWidth+halfFontSize+textWidth;let x=cursor.x;let y=cursor.y;rtlHelper.setWidth(this.width);if(isHorizontal){if(i>0&&x+width+padding>this.right){y=cursor.y+=lineHeight;cursor.line++;x=cursor.x=_alignStartEnd(align,this.left+padding,this.right-lineWidths[cursor.line]);}}else if(i>0&&y+lineHeight>this.bottom){x=cursor.x=x+columnSizes[cursor.line].width+padding;cursor.line++;y=cursor.y=_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-columnSizes[cursor.line].height);} +const realX=rtlHelper.x(x);drawLegendBox(realX,y,legendItem);x=_textX(textAlign,x+boxWidth+halfFontSize,isHorizontal?x+width:this.right,opts.rtl);fillText(rtlHelper.x(x),y,legendItem);if(isHorizontal){cursor.x+=width+padding;}else{cursor.y+=lineHeight;}});restoreTextDirection(this.ctx,opts.textDirection);} +drawTitle(){const opts=this.options;const titleOpts=opts.title;const titleFont=toFont(titleOpts.font);const titlePadding=toPadding(titleOpts.padding);if(!titleOpts.display){return;} +const rtlHelper=getRtlAdapter(opts.rtl,this.left,this.width);const ctx=this.ctx;const position=titleOpts.position;const halfFontSize=titleFont.size/2;const topPaddingPlusHalfFontSize=titlePadding.top+halfFontSize;let y;let left=this.left;let maxWidth=this.width;if(this.isHorizontal()){maxWidth=Math.max(...this.lineWidths);y=this.top+topPaddingPlusHalfFontSize;left=_alignStartEnd(opts.align,left,this.right-maxWidth);}else{const maxHeight=this.columnSizes.reduce((acc,size)=>Math.max(acc,size.height),0);y=topPaddingPlusHalfFontSize+_alignStartEnd(opts.align,this.top,this.bottom-maxHeight-opts.labels.padding-this._computeTitleHeight());} const x=_alignStartEnd(position,left,left+maxWidth);ctx.textAlign=rtlHelper.textAlign(_toLeftRightCenter(position));ctx.textBaseline='middle';ctx.strokeStyle=titleOpts.color;ctx.fillStyle=titleOpts.color;ctx.font=titleFont.string;renderText(ctx,titleOpts.text,x,y,titleFont);} _computeTitleHeight(){const titleOpts=this.options.title;const titleFont=toFont(titleOpts.font);const titlePadding=toPadding(titleOpts.padding);return titleOpts.display?titleFont.lineHeight+titlePadding.height:0;} -_getLegendItemAt(x,y){const me=this;let i,hitBox,lh;if(x>=me.left&&x<=me.right&&y>=me.top&&y<=me.bottom){lh=me.legendHitBoxes;for(i=0;i=hitBox.left&&x<=hitBox.left+hitBox.width&&y>=hitBox.top&&y<=hitBox.top+hitBox.height){return me.legendItems[i];}}} +_getLegendItemAt(x,y){let i,hitBox,lh;if(_isBetween(x,this.left,this.right)&&_isBetween(y,this.top,this.bottom)){lh=this.legendHitBoxes;for(i=0;ictx.chart.options.color,boxWidth:40,padding:10,generateLabels(chart){const datasets=chart.data.datasets;const{labels:{usePointStyle,pointStyle,textAlign,color}}=chart.legend.options;return chart._getSortedDatasetMetas().map((meta)=>{const style=meta.controller.getStyle(usePointStyle?0:undefined);const borderWidth=toPadding(style.borderWidth);return{text:datasets[meta.index].label,fillStyle:style.backgroundColor,fontColor:color,hidden:!meta.visible,lineCap:style.borderCapStyle,lineDash:style.borderDash,lineDashOffset:style.borderDashOffset,lineJoin:style.borderJoinStyle,lineWidth:(borderWidth.width+borderWidth.height)/4,strokeStyle:style.borderColor,pointStyle:pointStyle||style.pointStyle,rotation:style.rotation,textAlign:textAlign||style.textAlign,borderRadius:0,datasetIndex:meta.index};},this);}},title:{color:(ctx)=>ctx.chart.options.color,display:false,position:'center',text:'',}},descriptors:{_scriptable:(name)=>!name.startsWith('on'),labels:{_scriptable:(name)=>!['generateLabels','filter','sort'].includes(name),}},};class Title extends Element{constructor(config){super();this.chart=config.chart;this.options=config.options;this.ctx=config.ctx;this._padding=undefined;this.top=undefined;this.bottom=undefined;this.left=undefined;this.right=undefined;this.width=undefined;this.height=undefined;this.position=undefined;this.weight=undefined;this.fullSize=undefined;} -update(maxWidth,maxHeight){const me=this;const opts=me.options;me.left=0;me.top=0;if(!opts.display){me.width=me.height=me.right=me.bottom=0;return;} -me.width=me.right=maxWidth;me.height=me.bottom=maxHeight;const lineCount=isArray(opts.text)?opts.text.length:1;me._padding=toPadding(opts.padding);const textSize=lineCount*toFont(opts.font).lineHeight+me._padding.height;if(me.isHorizontal()){me.height=textSize;}else{me.width=textSize;}} +update(maxWidth,maxHeight){const opts=this.options;this.left=0;this.top=0;if(!opts.display){this.width=this.height=this.right=this.bottom=0;return;} +this.width=this.right=maxWidth;this.height=this.bottom=maxHeight;const lineCount=isArray(opts.text)?opts.text.length:1;this._padding=toPadding(opts.padding);const textSize=lineCount*toFont(opts.font).lineHeight+this._padding.height;if(this.isHorizontal()){this.height=textSize;}else{this.width=textSize;}} isHorizontal(){const pos=this.options.position;return pos==='top'||pos==='bottom';} _drawArgs(offset){const{top,left,bottom,right,options}=this;const align=options.align;let rotation=0;let maxWidth,titleX,titleY;if(this.isHorizontal()){titleX=_alignStartEnd(align,left,right);titleY=top+offset;maxWidth=right-left;}else{if(options.position==='left'){titleX=left+offset;titleY=_alignStartEnd(align,bottom,top);rotation=PI*-0.5;}else{titleX=right-offset;titleY=_alignStartEnd(align,top,bottom);rotation=PI*0.5;} maxWidth=bottom-top;} return{titleX,titleY,maxWidth,rotation};} -draw(){const me=this;const ctx=me.ctx;const opts=me.options;if(!opts.display){return;} -const fontOpts=toFont(opts.font);const lineHeight=fontOpts.lineHeight;const offset=lineHeight/2+me._padding.top;const{titleX,titleY,maxWidth,rotation}=me._drawArgs(offset);renderText(ctx,opts.text,0,0,fontOpts,{color:opts.color,maxWidth,rotation,textAlign:_toLeftRightCenter(opts.align),textBaseline:'middle',translation:[titleX,titleY],});}} +draw(){const ctx=this.ctx;const opts=this.options;if(!opts.display){return;} +const fontOpts=toFont(opts.font);const lineHeight=fontOpts.lineHeight;const offset=lineHeight/2+this._padding.top;const{titleX,titleY,maxWidth,rotation}=this._drawArgs(offset);renderText(ctx,opts.text,0,0,fontOpts,{color:opts.color,maxWidth,rotation,textAlign:_toLeftRightCenter(opts.align),textBaseline:'middle',translation:[titleX,titleY],});}} function createTitle(chart,titleOpts){const title=new Title({ctx:chart.ctx,options:titleOpts,chart});layouts.configure(chart,title,titleOpts);layouts.addBox(chart,title);chart.titleBlock=title;} var plugin_title={id:'title',_element:Title,start(chart,_args,options){createTitle(chart,options);},stop(chart){const titleBlock=chart.titleBlock;layouts.removeBox(chart,titleBlock);delete chart.titleBlock;},beforeUpdate(chart,_args,options){const title=chart.titleBlock;layouts.configure(chart,title,options);title.options=options;},defaults:{align:'center',display:false,font:{weight:'bold',},fullSize:true,padding:10,position:'top',text:'',weight:2000},defaultRoutes:{color:'color'},descriptors:{_scriptable:true,_indexable:false,},};const map=new WeakMap();var plugin_subtitle={id:'subtitle',start(chart,_args,options){const title=new Title({ctx:chart.ctx,options,chart});layouts.configure(chart,title,options);layouts.addBox(chart,title);map.set(chart,title);},stop(chart){layouts.removeBox(chart,map.get(chart));map.delete(chart);},beforeUpdate(chart,_args,options){const title=map.get(chart);layouts.configure(chart,title,options);title.options=options;},defaults:{align:'center',display:false,font:{weight:'normal',},fullSize:true,padding:0,position:'top',text:'',weight:1500},defaultRoutes:{color:'color'},descriptors:{_scriptable:true,_indexable:false,},};const positioners={average(items){if(!items.length){return false;} let i,len;let x=0;let y=0;let count=0;for(i=0,len=items.length;i{each(bodyItem.before,maxLineWidth);each(bodyItem.lines,maxLineWidth);each(bodyItem.after,maxLineWidth);});widthPadding=0;ctx.font=footerFont.string;each(tooltip.footer,maxLineWidth);ctx.restore();width+=padding.width;return{width,height};} +let widthPadding=0;const maxLineWidth=function(line){width=Math.max(width,ctx.measureText(line).width+widthPadding);};ctx.save();ctx.font=titleFont.string;each(tooltip.title,maxLineWidth);ctx.font=bodyFont.string;each(tooltip.beforeBody.concat(tooltip.afterBody),maxLineWidth);widthPadding=options.displayColors?(boxWidth+2+options.boxPadding):0;each(body,(bodyItem)=>{each(bodyItem.before,maxLineWidth);each(bodyItem.lines,maxLineWidth);each(bodyItem.after,maxLineWidth);});widthPadding=0;ctx.font=footerFont.string;each(tooltip.footer,maxLineWidth);ctx.restore();width+=padding.width;return{width,height};} function determineYAlign(chart,size){const{y,height}=size;if(y(chart.height-height/2)){return'bottom';} return'center';} function doesNotFitWithAlign(xAlign,chart,options,size){const{x,width}=size;const caret=options.caretSize+options.caretPadding;if(xAlign==='left'&&x+width+caret>chart.width){return true;} @@ -2008,84 +2035,88 @@ function alignX(size,xAlign){let{x,width}=size;if(xAlign==='right'){x-=width;}el return x;} function alignY(size,yAlign,paddingAndSize){let{y,height}=size;if(yAlign==='top'){y+=paddingAndSize;}else if(yAlign==='bottom'){y-=height+paddingAndSize;}else{y-=(height/2);} return y;} -function getBackgroundPoint(options,size,alignment,chart){const{caretSize,caretPadding,cornerRadius}=options;const{xAlign,yAlign}=alignment;const paddingAndSize=caretSize+caretPadding;const radiusAndPadding=cornerRadius+caretPadding;let x=alignX(size,xAlign);const y=alignY(size,yAlign,paddingAndSize);if(yAlign==='center'){if(xAlign==='left'){x+=paddingAndSize;}else if(xAlign==='right'){x-=paddingAndSize;}}else if(xAlign==='left'){x-=radiusAndPadding;}else if(xAlign==='right'){x+=radiusAndPadding;} +function getBackgroundPoint(options,size,alignment,chart){const{caretSize,caretPadding,cornerRadius}=options;const{xAlign,yAlign}=alignment;const paddingAndSize=caretSize+caretPadding;const{topLeft,topRight,bottomLeft,bottomRight}=toTRBLCorners(cornerRadius);let x=alignX(size,xAlign);const y=alignY(size,yAlign,paddingAndSize);if(yAlign==='center'){if(xAlign==='left'){x+=paddingAndSize;}else if(xAlign==='right'){x-=paddingAndSize;}}else if(xAlign==='left'){x-=Math.max(topLeft,bottomLeft)+caretSize;}else if(xAlign==='right'){x+=Math.max(topRight,bottomRight)+caretSize;} return{x:_limitValue(x,0,chart.width-size.width),y:_limitValue(y,0,chart.height-size.height)};} function getAlignedX(tooltip,align,options){const padding=toPadding(options.padding);return align==='center'?tooltip.x+tooltip.width/2:align==='right'?tooltip.x+tooltip.width-padding.right:tooltip.x+padding.left;} function getBeforeAfterBodyLines(callback){return pushOrConcat([],splitNewlines(callback));} -function createTooltipContext(parent,tooltip,tooltipItems){return Object.assign(Object.create(parent),{tooltip,tooltipItems,type:'tooltip'});} +function createTooltipContext(parent,tooltip,tooltipItems){return createContext(parent,{tooltip,tooltipItems,type:'tooltip'});} function overrideCallbacks(callbacks,context){const override=context&&context.dataset&&context.dataset.tooltip&&context.dataset.tooltip.callbacks;return override?callbacks.override(override):callbacks;} class Tooltip extends Element{constructor(config){super();this.opacity=0;this._active=[];this._chart=config._chart;this._eventPosition=undefined;this._size=undefined;this._cachedAnimations=undefined;this._tooltipItems=[];this.$animations=undefined;this.$context=undefined;this.options=config.options;this.dataPoints=undefined;this.title=undefined;this.beforeBody=undefined;this.body=undefined;this.afterBody=undefined;this.footer=undefined;this.xAlign=undefined;this.yAlign=undefined;this.x=undefined;this.y=undefined;this.height=undefined;this.width=undefined;this.caretX=undefined;this.caretY=undefined;this.labelColors=undefined;this.labelPointStyles=undefined;this.labelTextColors=undefined;} initialize(options){this.options=options;this._cachedAnimations=undefined;this.$context=undefined;} -_resolveAnimations(){const me=this;const cached=me._cachedAnimations;if(cached){return cached;} -const chart=me._chart;const options=me.options.setContext(me.getContext());const opts=options.enabled&&chart.options.animation&&options.animations;const animations=new Animations(me._chart,opts);if(opts._cacheable){me._cachedAnimations=Object.freeze(animations);} +_resolveAnimations(){const cached=this._cachedAnimations;if(cached){return cached;} +const chart=this._chart;const options=this.options.setContext(this.getContext());const opts=options.enabled&&chart.options.animation&&options.animations;const animations=new Animations(this._chart,opts);if(opts._cacheable){this._cachedAnimations=Object.freeze(animations);} return animations;} -getContext(){const me=this;return me.$context||(me.$context=createTooltipContext(me._chart.getContext(),me,me._tooltipItems));} -getTitle(context,options){const me=this;const{callbacks}=options;const beforeTitle=callbacks.beforeTitle.apply(me,[context]);const title=callbacks.title.apply(me,[context]);const afterTitle=callbacks.afterTitle.apply(me,[context]);let lines=[];lines=pushOrConcat(lines,splitNewlines(beforeTitle));lines=pushOrConcat(lines,splitNewlines(title));lines=pushOrConcat(lines,splitNewlines(afterTitle));return lines;} +getContext(){return this.$context||(this.$context=createTooltipContext(this._chart.getContext(),this,this._tooltipItems));} +getTitle(context,options){const{callbacks}=options;const beforeTitle=callbacks.beforeTitle.apply(this,[context]);const title=callbacks.title.apply(this,[context]);const afterTitle=callbacks.afterTitle.apply(this,[context]);let lines=[];lines=pushOrConcat(lines,splitNewlines(beforeTitle));lines=pushOrConcat(lines,splitNewlines(title));lines=pushOrConcat(lines,splitNewlines(afterTitle));return lines;} getBeforeBody(tooltipItems,options){return getBeforeAfterBodyLines(options.callbacks.beforeBody.apply(this,[tooltipItems]));} -getBody(tooltipItems,options){const me=this;const{callbacks}=options;const bodyItems=[];each(tooltipItems,(context)=>{const bodyItem={before:[],lines:[],after:[]};const scoped=overrideCallbacks(callbacks,context);pushOrConcat(bodyItem.before,splitNewlines(scoped.beforeLabel.call(me,context)));pushOrConcat(bodyItem.lines,scoped.label.call(me,context));pushOrConcat(bodyItem.after,splitNewlines(scoped.afterLabel.call(me,context)));bodyItems.push(bodyItem);});return bodyItems;} +getBody(tooltipItems,options){const{callbacks}=options;const bodyItems=[];each(tooltipItems,(context)=>{const bodyItem={before:[],lines:[],after:[]};const scoped=overrideCallbacks(callbacks,context);pushOrConcat(bodyItem.before,splitNewlines(scoped.beforeLabel.call(this,context)));pushOrConcat(bodyItem.lines,scoped.label.call(this,context));pushOrConcat(bodyItem.after,splitNewlines(scoped.afterLabel.call(this,context)));bodyItems.push(bodyItem);});return bodyItems;} getAfterBody(tooltipItems,options){return getBeforeAfterBodyLines(options.callbacks.afterBody.apply(this,[tooltipItems]));} -getFooter(tooltipItems,options){const me=this;const{callbacks}=options;const beforeFooter=callbacks.beforeFooter.apply(me,[tooltipItems]);const footer=callbacks.footer.apply(me,[tooltipItems]);const afterFooter=callbacks.afterFooter.apply(me,[tooltipItems]);let lines=[];lines=pushOrConcat(lines,splitNewlines(beforeFooter));lines=pushOrConcat(lines,splitNewlines(footer));lines=pushOrConcat(lines,splitNewlines(afterFooter));return lines;} -_createItems(options){const me=this;const active=me._active;const data=me._chart.data;const labelColors=[];const labelPointStyles=[];const labelTextColors=[];let tooltipItems=[];let i,len;for(i=0,len=active.length;ioptions.filter(element,index,array,data));} if(options.itemSort){tooltipItems=tooltipItems.sort((a,b)=>options.itemSort(a,b,data));} -each(tooltipItems,(context)=>{const scoped=overrideCallbacks(options.callbacks,context);labelColors.push(scoped.labelColor.call(me,context));labelPointStyles.push(scoped.labelPointStyle.call(me,context));labelTextColors.push(scoped.labelTextColor.call(me,context));});me.labelColors=labelColors;me.labelPointStyles=labelPointStyles;me.labelTextColors=labelTextColors;me.dataPoints=tooltipItems;return tooltipItems;} -update(changed,replay){const me=this;const options=me.options.setContext(me.getContext());const active=me._active;let properties;let tooltipItems=[];if(!active.length){if(me.opacity!==0){properties={opacity:0};}}else{const position=positioners[options.position].call(me,active,me._eventPosition);tooltipItems=me._createItems(options);me.title=me.getTitle(tooltipItems,options);me.beforeBody=me.getBeforeBody(tooltipItems,options);me.body=me.getBody(tooltipItems,options);me.afterBody=me.getAfterBody(tooltipItems,options);me.footer=me.getFooter(tooltipItems,options);const size=me._size=getTooltipSize(me,options);const positionAndSize=Object.assign({},position,size);const alignment=determineAlignment(me._chart,options,positionAndSize);const backgroundPoint=getBackgroundPoint(options,positionAndSize,alignment,me._chart);me.xAlign=alignment.xAlign;me.yAlign=alignment.yAlign;properties={opacity:1,x:backgroundPoint.x,y:backgroundPoint.y,width:size.width,height:size.height,caretX:position.x,caretY:position.y};} -me._tooltipItems=tooltipItems;me.$context=undefined;if(properties){me._resolveAnimations().update(me,properties);} -if(changed&&options.external){options.external.call(me,{chart:me._chart,tooltip:me,replay});}} +each(tooltipItems,(context)=>{const scoped=overrideCallbacks(options.callbacks,context);labelColors.push(scoped.labelColor.call(this,context));labelPointStyles.push(scoped.labelPointStyle.call(this,context));labelTextColors.push(scoped.labelTextColor.call(this,context));});this.labelColors=labelColors;this.labelPointStyles=labelPointStyles;this.labelTextColors=labelTextColors;this.dataPoints=tooltipItems;return tooltipItems;} +update(changed,replay){const options=this.options.setContext(this.getContext());const active=this._active;let properties;let tooltipItems=[];if(!active.length){if(this.opacity!==0){properties={opacity:0};}}else{const position=positioners[options.position].call(this,active,this._eventPosition);tooltipItems=this._createItems(options);this.title=this.getTitle(tooltipItems,options);this.beforeBody=this.getBeforeBody(tooltipItems,options);this.body=this.getBody(tooltipItems,options);this.afterBody=this.getAfterBody(tooltipItems,options);this.footer=this.getFooter(tooltipItems,options);const size=this._size=getTooltipSize(this,options);const positionAndSize=Object.assign({},position,size);const alignment=determineAlignment(this._chart,options,positionAndSize);const backgroundPoint=getBackgroundPoint(options,positionAndSize,alignment,this._chart);this.xAlign=alignment.xAlign;this.yAlign=alignment.yAlign;properties={opacity:1,x:backgroundPoint.x,y:backgroundPoint.y,width:size.width,height:size.height,caretX:position.x,caretY:position.y};} +this._tooltipItems=tooltipItems;this.$context=undefined;if(properties){this._resolveAnimations().update(this,properties);} +if(changed&&options.external){options.external.call(this,{chart:this._chart,tooltip:this,replay});}} drawCaret(tooltipPoint,ctx,size,options){const caretPosition=this.getCaretPosition(tooltipPoint,size,options);ctx.lineTo(caretPosition.x1,caretPosition.y1);ctx.lineTo(caretPosition.x2,caretPosition.y2);ctx.lineTo(caretPosition.x3,caretPosition.y3);} -getCaretPosition(tooltipPoint,size,options){const{xAlign,yAlign}=this;const{cornerRadius,caretSize}=options;const{x:ptX,y:ptY}=tooltipPoint;const{width,height}=size;let x1,x2,x3,y1,y2,y3;if(yAlign==='center'){y2=ptY+(height/2);if(xAlign==='left'){x1=ptX;x2=x1-caretSize;y1=y2+caretSize;y3=y2-caretSize;}else{x1=ptX+width;x2=x1+caretSize;y1=y2-caretSize;y3=y2+caretSize;} -x3=x1;}else{if(xAlign==='left'){x2=ptX+cornerRadius+(caretSize);}else if(xAlign==='right'){x2=ptX+width-cornerRadius-caretSize;}else{x2=this.caretX;} +getCaretPosition(tooltipPoint,size,options){const{xAlign,yAlign}=this;const{caretSize,cornerRadius}=options;const{topLeft,topRight,bottomLeft,bottomRight}=toTRBLCorners(cornerRadius);const{x:ptX,y:ptY}=tooltipPoint;const{width,height}=size;let x1,x2,x3,y1,y2,y3;if(yAlign==='center'){y2=ptY+(height/2);if(xAlign==='left'){x1=ptX;x2=x1-caretSize;y1=y2+caretSize;y3=y2-caretSize;}else{x1=ptX+width;x2=x1+caretSize;y1=y2-caretSize;y3=y2+caretSize;} +x3=x1;}else{if(xAlign==='left'){x2=ptX+Math.max(topLeft,bottomLeft)+(caretSize);}else if(xAlign==='right'){x2=ptX+width-Math.max(topRight,bottomRight)-caretSize;}else{x2=this.caretX;} if(yAlign==='top'){y1=ptY;y2=y1-caretSize;x1=x2-caretSize;x3=x2+caretSize;}else{y1=ptY+height;y2=y1+caretSize;x1=x2+caretSize;x3=x2-caretSize;} y3=y1;} return{x1,x2,x3,y1,y2,y3};} -drawTitle(pt,ctx,options){const me=this;const title=me.title;const length=title.length;let titleFont,titleSpacing,i;if(length){const rtlHelper=getRtlAdapter(options.rtl,me.x,me.width);pt.x=getAlignedX(me,options.titleAlign,options);ctx.textAlign=rtlHelper.textAlign(options.titleAlign);ctx.textBaseline='middle';titleFont=toFont(options.titleFont);titleSpacing=options.titleSpacing;ctx.fillStyle=options.titleColor;ctx.font=titleFont.string;for(i=0;iv!==0)){ctx.beginPath();ctx.fillStyle=options.multiKeyBackground;addRoundedRectPath(ctx,{x:outerX,y:colorY,w:boxWidth,h:boxHeight,radius:borderRadius,});ctx.fill();ctx.stroke();ctx.fillStyle=labelColors.backgroundColor;ctx.beginPath();addRoundedRectPath(ctx,{x:innerX,y:colorY+1,w:boxWidth-2,h:boxHeight-2,radius:borderRadius,});ctx.fill();}else{ctx.fillStyle=options.multiKeyBackground;ctx.fillRect(outerX,colorY,boxWidth,boxHeight);ctx.strokeRect(outerX,colorY,boxWidth,boxHeight);ctx.fillStyle=labelColors.backgroundColor;ctx.fillRect(innerX,colorY+1,boxWidth-2,boxHeight-2);}} -ctx.fillStyle=me.labelTextColors[i];} -drawBody(pt,ctx,options){const me=this;const{body}=me;const{bodySpacing,bodyAlign,displayColors,boxHeight,boxWidth}=options;const bodyFont=toFont(options.bodyFont);let bodyLineHeight=bodyFont.lineHeight;let xLinePadding=0;const rtlHelper=getRtlAdapter(options.rtl,me.x,me.width);const fillLineOfText=function(line){ctx.fillText(line,rtlHelper.x(pt.x+xLinePadding),pt.y+bodyLineHeight/2);pt.y+=bodyLineHeight+bodySpacing;};const bodyAlignForCalculation=rtlHelper.textAlign(bodyAlign);let bodyItem,textColor,lines,i,j,ilen,jlen;ctx.textAlign=bodyAlign;ctx.textBaseline='middle';ctx.font=bodyFont.string;pt.x=getAlignedX(me,bodyAlignForCalculation,options);ctx.fillStyle=options.bodyColor;each(me.beforeBody,fillLineOfText);xLinePadding=displayColors&&bodyAlignForCalculation!=='right'?bodyAlign==='center'?(boxWidth/2+1):(boxWidth+2):0;for(i=0,ilen=body.length;iv!==0)){ctx.beginPath();ctx.fillStyle=options.multiKeyBackground;addRoundedRectPath(ctx,{x:outerX,y:colorY,w:boxWidth,h:boxHeight,radius:borderRadius,});ctx.fill();ctx.stroke();ctx.fillStyle=labelColors.backgroundColor;ctx.beginPath();addRoundedRectPath(ctx,{x:innerX,y:colorY+1,w:boxWidth-2,h:boxHeight-2,radius:borderRadius,});ctx.fill();}else{ctx.fillStyle=options.multiKeyBackground;ctx.fillRect(outerX,colorY,boxWidth,boxHeight);ctx.strokeRect(outerX,colorY,boxWidth,boxHeight);ctx.fillStyle=labelColors.backgroundColor;ctx.fillRect(innerX,colorY+1,boxWidth-2,boxHeight-2);}} +ctx.fillStyle=this.labelTextColors[i];} +drawBody(pt,ctx,options){const{body}=this;const{bodySpacing,bodyAlign,displayColors,boxHeight,boxWidth,boxPadding}=options;const bodyFont=toFont(options.bodyFont);let bodyLineHeight=bodyFont.lineHeight;let xLinePadding=0;const rtlHelper=getRtlAdapter(options.rtl,this.x,this.width);const fillLineOfText=function(line){ctx.fillText(line,rtlHelper.x(pt.x+xLinePadding),pt.y+bodyLineHeight/2);pt.y+=bodyLineHeight+bodySpacing;};const bodyAlignForCalculation=rtlHelper.textAlign(bodyAlign);let bodyItem,textColor,lines,i,j,ilen,jlen;ctx.textAlign=bodyAlign;ctx.textBaseline='middle';ctx.font=bodyFont.string;pt.x=getAlignedX(this,bodyAlignForCalculation,options);ctx.fillStyle=options.bodyColor;each(this.beforeBody,fillLineOfText);xLinePadding=displayColors&&bodyAlignForCalculation!=='right'?bodyAlign==='center'?(boxWidth/2+boxPadding):(boxWidth+2+boxPadding):0;for(i=0,ilen=body.length;i0){ctx.stroke();}} -_updateAnimationTarget(options){const me=this;const chart=me._chart;const anims=me.$animations;const animX=anims&&anims.x;const animY=anims&&anims.y;if(animX||animY){const position=positioners[options.position].call(me,me._active,me._eventPosition);if(!position){return;} -const size=me._size=getTooltipSize(me,options);const positionAndSize=Object.assign({},position,me._size);const alignment=determineAlignment(chart,options,positionAndSize);const point=getBackgroundPoint(options,positionAndSize,alignment,chart);if(animX._to!==point.x||animY._to!==point.y){me.xAlign=alignment.xAlign;me.yAlign=alignment.yAlign;me.width=size.width;me.height=size.height;me.caretX=position.x;me.caretY=position.y;me._resolveAnimations().update(me,point);}}} -draw(ctx){const me=this;const options=me.options.setContext(me.getContext());let opacity=me.opacity;if(!opacity){return;} -me._updateAnimationTarget(options);const tooltipSize={width:me.width,height:me.height};const pt={x:me.x,y:me.y};opacity=Math.abs(opacity)<1e-3?0:opacity;const padding=toPadding(options.padding);const hasTooltipContent=me.title.length||me.beforeBody.length||me.body.length||me.afterBody.length||me.footer.length;if(options.enabled&&hasTooltipContent){ctx.save();ctx.globalAlpha=opacity;me.drawBackground(pt,ctx,tooltipSize,options);overrideTextDirection(ctx,options.textDirection);pt.y+=padding.top;me.drawTitle(pt,ctx,options);me.drawBody(pt,ctx,options);me.drawFooter(pt,ctx,options);restoreTextDirection(ctx,options.textDirection);ctx.restore();}} +xLinePadding=0;bodyLineHeight=bodyFont.lineHeight;each(this.afterBody,fillLineOfText);pt.y-=bodySpacing;} +drawFooter(pt,ctx,options){const footer=this.footer;const length=footer.length;let footerFont,i;if(length){const rtlHelper=getRtlAdapter(options.rtl,this.x,this.width);pt.x=getAlignedX(this,options.footerAlign,options);pt.y+=options.footerMarginTop;ctx.textAlign=rtlHelper.textAlign(options.footerAlign);ctx.textBaseline='middle';footerFont=toFont(options.footerFont);ctx.fillStyle=options.footerColor;ctx.font=footerFont.string;for(i=0;i0){ctx.stroke();}} +_updateAnimationTarget(options){const chart=this._chart;const anims=this.$animations;const animX=anims&&anims.x;const animY=anims&&anims.y;if(animX||animY){const position=positioners[options.position].call(this,this._active,this._eventPosition);if(!position){return;} +const size=this._size=getTooltipSize(this,options);const positionAndSize=Object.assign({},position,this._size);const alignment=determineAlignment(chart,options,positionAndSize);const point=getBackgroundPoint(options,positionAndSize,alignment,chart);if(animX._to!==point.x||animY._to!==point.y){this.xAlign=alignment.xAlign;this.yAlign=alignment.yAlign;this.width=size.width;this.height=size.height;this.caretX=position.x;this.caretY=position.y;this._resolveAnimations().update(this,point);}}} +draw(ctx){const options=this.options.setContext(this.getContext());let opacity=this.opacity;if(!opacity){return;} +this._updateAnimationTarget(options);const tooltipSize={width:this.width,height:this.height};const pt={x:this.x,y:this.y};opacity=Math.abs(opacity)<1e-3?0:opacity;const padding=toPadding(options.padding);const hasTooltipContent=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;if(options.enabled&&hasTooltipContent){ctx.save();ctx.globalAlpha=opacity;this.drawBackground(pt,ctx,tooltipSize,options);overrideTextDirection(ctx,options.textDirection);pt.y+=padding.top;this.drawTitle(pt,ctx,options);this.drawBody(pt,ctx,options);this.drawFooter(pt,ctx,options);restoreTextDirection(ctx,options.textDirection);ctx.restore();}} getActiveElements(){return this._active||[];} -setActiveElements(activeElements,eventPosition){const me=this;const lastActive=me._active;const active=activeElements.map(({datasetIndex,index})=>{const meta=me._chart.getDatasetMeta(datasetIndex);if(!meta){throw new Error('Cannot find a dataset at index '+datasetIndex);} -return{datasetIndex,element:meta.data[index],index,};});const changed=!_elementsEqual(lastActive,active);const positionChanged=me._positionChanged(active,eventPosition);if(changed||positionChanged){me._active=active;me._eventPosition=eventPosition;me.update(true);}} -handleEvent(e,replay){const me=this;const options=me.options;const lastActive=me._active||[];let changed=false;let active=[];if(e.type!=='mouseout'){active=me._chart.getElementsAtEventForMode(e,options.mode,options,replay);if(options.reverse){active.reverse();}} -const positionChanged=me._positionChanged(active,e);changed=replay||!_elementsEqual(active,lastActive)||positionChanged;if(changed){me._active=active;if(options.enabled||options.external){me._eventPosition={x:e.x,y:e.y};me.update(true,replay);}} +setActiveElements(activeElements,eventPosition){const lastActive=this._active;const active=activeElements.map(({datasetIndex,index})=>{const meta=this._chart.getDatasetMeta(datasetIndex);if(!meta){throw new Error('Cannot find a dataset at index '+datasetIndex);} +return{datasetIndex,element:meta.data[index],index,};});const changed=!_elementsEqual(lastActive,active);const positionChanged=this._positionChanged(active,eventPosition);if(changed||positionChanged){this._active=active;this._eventPosition=eventPosition;this.update(true);}} +handleEvent(e,replay){const options=this.options;const lastActive=this._active||[];let changed=false;let active=[];if(e.type!=='mouseout'){active=this._chart.getElementsAtEventForMode(e,options.mode,options,replay);if(options.reverse){active.reverse();}} +const positionChanged=this._positionChanged(active,e);changed=replay||!_elementsEqual(active,lastActive)||positionChanged;if(changed){this._active=active;if(options.enabled||options.external){this._eventPosition={x:e.x,y:e.y};this.update(true,replay);}} return changed;} _positionChanged(active,e){const{caretX,caretY,options}=this;const position=positioners[options.position].call(this,active,e);return position!==false&&(caretX!==position.x||caretY!==position.y);}} Tooltip.positioners=positioners;var plugin_tooltip={id:'tooltip',_element:Tooltip,positioners,afterInit(chart,_args,options){if(options){chart.tooltip=new Tooltip({_chart:chart,options});}},beforeUpdate(chart,_args,options){if(chart.tooltip){chart.tooltip.initialize(options);}},reset(chart,_args,options){if(chart.tooltip){chart.tooltip.initialize(options);}},afterDraw(chart){const tooltip=chart.tooltip;const args={tooltip};if(chart.notifyPlugins('beforeTooltipDraw',args)===false){return;} if(tooltip){tooltip.draw(chart.ctx);} -chart.notifyPlugins('afterTooltipDraw',args);},afterEvent(chart,args){if(chart.tooltip){const useFinalPosition=args.replay;if(chart.tooltip.handleEvent(args.event,useFinalPosition)){args.changed=true;}}},defaults:{enabled:true,external:null,position:'average',backgroundColor:'rgba(0,0,0,0.8)',titleColor:'#fff',titleFont:{weight:'bold',},titleSpacing:2,titleMarginBottom:6,titleAlign:'left',bodyColor:'#fff',bodySpacing:2,bodyFont:{},bodyAlign:'left',footerColor:'#fff',footerSpacing:2,footerMarginTop:6,footerFont:{weight:'bold',},footerAlign:'left',padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(ctx,opts)=>opts.bodyFont.size,boxWidth:(ctx,opts)=>opts.bodyFont.size,multiKeyBackground:'#fff',displayColors:true,borderColor:'rgba(0,0,0,0)',borderWidth:0,animation:{duration:400,easing:'easeOutQuart',},animations:{numbers:{type:'number',properties:['x','y','width','height','caretX','caretY'],},opacity:{easing:'linear',duration:200}},callbacks:{beforeTitle:noop,title(tooltipItems){if(tooltipItems.length>0){const item=tooltipItems[0];const labels=item.chart.data.labels;const labelCount=labels?labels.length:0;if(this&&this.options&&this.options.mode==='dataset'){return item.dataset.label||'';}else if(item.label){return item.label;}else if(labelCount>0&&item.dataIndexopts.bodyFont.size,boxWidth:(ctx,opts)=>opts.bodyFont.size,multiKeyBackground:'#fff',displayColors:true,boxPadding:0,borderColor:'rgba(0,0,0,0)',borderWidth:0,animation:{duration:400,easing:'easeOutQuart',},animations:{numbers:{type:'number',properties:['x','y','width','height','caretX','caretY'],},opacity:{easing:'linear',duration:200}},callbacks:{beforeTitle:noop,title(tooltipItems){if(tooltipItems.length>0){const item=tooltipItems[0];const labels=item.chart.data.labels;const labelCount=labels?labels.length:0;if(this&&this.options&&this.options.mode==='dataset'){return item.dataset.label||'';}else if(item.label){return item.label;}else if(labelCount>0&&item.dataIndexname!=='filter'&&name!=='itemSort'&&name!=='external',_indexable:false,callbacks:{_scriptable:false,_indexable:false,},animation:{_fallback:false},animations:{_fallback:'animation'}},additionalOptionScopes:['interaction']};var plugins=Object.freeze({__proto__:null,Decimation:plugin_decimation,Filler:plugin_filler,Legend:plugin_legend,SubTitle:plugin_subtitle,Title:plugin_title,Tooltip:plugin_tooltip});const addIfString=(labels,raw,index)=>typeof raw==='string'?labels.push(raw)-1:isNaN(raw)?null:index;function findOrAddLabel(labels,raw,index){const first=labels.indexOf(raw);if(first===-1){return addIfString(labels,raw,index);} +return label;},labelColor(tooltipItem){const meta=tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);const options=meta.controller.getStyle(tooltipItem.dataIndex);return{borderColor:options.borderColor,backgroundColor:options.backgroundColor,borderWidth:options.borderWidth,borderDash:options.borderDash,borderDashOffset:options.borderDashOffset,borderRadius:0,};},labelTextColor(){return this.options.bodyColor;},labelPointStyle(tooltipItem){const meta=tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);const options=meta.controller.getStyle(tooltipItem.dataIndex);return{pointStyle:options.pointStyle,rotation:options.rotation,};},afterLabel:noop,afterBody:noop,beforeFooter:noop,footer:noop,afterFooter:noop}},defaultRoutes:{bodyFont:'font',footerFont:'font',titleFont:'font'},descriptors:{_scriptable:(name)=>name!=='filter'&&name!=='itemSort'&&name!=='external',_indexable:false,callbacks:{_scriptable:false,_indexable:false,},animation:{_fallback:false},animations:{_fallback:'animation'}},additionalOptionScopes:['interaction']};var plugins=Object.freeze({__proto__:null,Decimation:plugin_decimation,Filler:plugin_filler,Legend:plugin_legend,SubTitle:plugin_subtitle,Title:plugin_title,Tooltip:plugin_tooltip});const addIfString=(labels,raw,index,addedLabels)=>{if(typeof raw==='string'){index=labels.push(raw)-1;addedLabels.unshift({index,label:raw});}else if(isNaN(raw)){index=null;} +return index;};function findOrAddLabel(labels,raw,index,addedLabels){const first=labels.indexOf(raw);if(first===-1){return addIfString(labels,raw,index,addedLabels);} const last=labels.lastIndexOf(raw);return first!==last?index:first;} -const validIndex=(index,max)=>index===null?null:_limitValue(Math.round(index),0,max);class CategoryScale extends Scale{constructor(cfg){super(cfg);this._startValue=undefined;this._valueRange=0;} +const validIndex=(index,max)=>index===null?null:_limitValue(Math.round(index),0,max);class CategoryScale extends Scale{constructor(cfg){super(cfg);this._startValue=undefined;this._valueRange=0;this._addedLabels=[];} +init(scaleOptions){const added=this._addedLabels;if(added.length){const labels=this.getLabels();for(const{index,label}of added){if(labels[index]===label){labels.splice(index,1);}} +this._addedLabels=[];} +super.init(scaleOptions);} parse(raw,index){if(isNullOrUndef(raw)){return null;} -const labels=this.getLabels();index=isFinite(index)&&labels[index]===raw?index:findOrAddLabel(labels,raw,valueOrDefault(index,raw));return validIndex(index,labels.length-1);} -determineDataLimits(){const me=this;const{minDefined,maxDefined}=me.getUserBounds();let{min,max}=me.getMinMax(true);if(me.options.bounds==='ticks'){if(!minDefined){min=0;} -if(!maxDefined){max=me.getLabels().length-1;}} -me.min=min;me.max=max;} -buildTicks(){const me=this;const min=me.min;const max=me.max;const offset=me.options.offset;const ticks=[];let labels=me.getLabels();labels=(min===0&&max===labels.length-1)?labels:labels.slice(min,max+1);me._valueRange=Math.max(labels.length-(offset?0:1),1);me._startValue=me.min-(offset?0.5:0);for(let value=min;value<=max;value++){ticks.push({value});} +const labels=this.getLabels();index=isFinite(index)&&labels[index]===raw?index:findOrAddLabel(labels,raw,valueOrDefault(index,raw),this._addedLabels);return validIndex(index,labels.length-1);} +determineDataLimits(){const{minDefined,maxDefined}=this.getUserBounds();let{min,max}=this.getMinMax(true);if(this.options.bounds==='ticks'){if(!minDefined){min=0;} +if(!maxDefined){max=this.getLabels().length-1;}} +this.min=min;this.max=max;} +buildTicks(){const min=this.min;const max=this.max;const offset=this.options.offset;const ticks=[];let labels=this.getLabels();labels=(min===0&&max===labels.length-1)?labels:labels.slice(min,max+1);this._valueRange=Math.max(labels.length-(offset?0:1),1);this._startValue=this.min-(offset?0.5:0);for(let value=min;value<=max;value++){ticks.push({value});} return ticks;} -getLabelForValue(value){const me=this;const labels=me.getLabels();if(value>=0&&value=0&&valueticks.length-1){return null;} -return me.getPixelForValue(ticks[index].value);} -getValueForPixel(pixel){const me=this;return Math.round(me._startValue+me.getDecimalForPixel(pixel)*me._valueRange);} +configure(){super.configure();if(!this.isHorizontal()){this._reversePixels=!this._reversePixels;}} +getPixelForValue(value){if(typeof value!=='number'){value=this.parse(value);} +return value===null?NaN:this.getPixelForDecimal((value-this._startValue)/this._valueRange);} +getPixelForTick(index){const ticks=this.ticks;if(index<0||index>ticks.length-1){return null;} +return this.getPixelForValue(ticks[index].value);} +getValueForPixel(pixel){return Math.round(this._startValue+this.getDecimalForPixel(pixel)*this._valueRange);} getBasePixel(){return this.bottom;}} CategoryScale.id='category';CategoryScale.defaults={ticks:{callback:CategoryScale.prototype.getLabelForValue}};function generateTicks$1(generationOptions,dataRange){const ticks=[];const MIN_SPACING=1e-14;const{bounds,step,min,max,precision,count,maxTicks,maxDigits,includeBounds}=generationOptions;const unit=step||1;const maxSpaces=maxTicks-1;const{min:rmin,max:rmax}=dataRange;const minDefined=!isNullOrUndef(min);const maxDefined=!isNullOrUndef(max);const countDefined=!isNullOrUndef(count);const minSpacing=(rmax-rmin)/(maxDigits+1);let spacing=niceNum((rmax-rmin)/maxSpaces/unit)*unit;let factor,niceMin,niceMax,numSpaces;if(spacingmaxSpaces){spacing=niceNum(numSpaces*spacing/maxSpaces/unit)*unit;} @@ -2095,29 +2126,29 @@ if(minDefined&&maxDefined&&step&&almostWhole((max-min)/step,spacing/1000)){numSp const decimalPlaces=Math.max(_decimalPlaces(spacing),_decimalPlaces(niceMin));factor=Math.pow(10,isNullOrUndef(precision)?decimalPlaces:precision);niceMin=Math.round(niceMin*factor)/factor;niceMax=Math.round(niceMax*factor)/factor;let j=0;if(minDefined){if(includeBounds&&niceMin!==min){ticks.push({value:min});if(niceMin(min=minDefined?min:v);const setMax=v=>(max=maxDefined?max:v);if(beginAtZero){const minSign=sign(min);const maxSign=sign(max);if(minSign<0&&maxSign<0){setMax(0);}else if(minSign>0&&maxSign>0){setMin(0);}} +handleTickRangeOptions(){const{beginAtZero}=this.options;const{minDefined,maxDefined}=this.getUserBounds();let{min,max}=this;const setMin=v=>(min=minDefined?min:v);const setMax=v=>(max=maxDefined?max:v);if(beginAtZero){const minSign=sign(min);const maxSign=sign(max);if(minSign<0&&maxSign<0){setMax(0);}else if(minSign>0&&maxSign>0){setMin(0);}} if(min===max){let offset=1;if(max>=Number.MAX_SAFE_INTEGER||min<=Number.MIN_SAFE_INTEGER){offset=Math.abs(max*0.05);} setMax(max+offset);if(!beginAtZero){setMin(min-offset);}} -me.min=min;me.max=max;} -getTickLimit(){const me=this;const tickOpts=me.options.ticks;let{maxTicksLimit,stepSize}=tickOpts;let maxTicks;if(stepSize){maxTicks=Math.ceil(me.max/stepSize)-Math.floor(me.min/stepSize)+1;}else{maxTicks=me.computeTickLimit();maxTicksLimit=maxTicksLimit||11;} +this.min=min;this.max=max;} +getTickLimit(){const tickOpts=this.options.ticks;let{maxTicksLimit,stepSize}=tickOpts;let maxTicks;if(stepSize){maxTicks=Math.ceil(this.max/stepSize)-Math.floor(this.min/stepSize)+1;if(maxTicks>1000){console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`);maxTicks=1000;}}else{maxTicks=this.computeTickLimit();maxTicksLimit=maxTicksLimit||11;} if(maxTicksLimit){maxTicks=Math.min(maxTicksLimit,maxTicks);} return maxTicks;} computeTickLimit(){return Number.POSITIVE_INFINITY;} -buildTicks(){const me=this;const opts=me.options;const tickOpts=opts.ticks;let maxTicks=me.getTickLimit();maxTicks=Math.max(2,maxTicks);const numericGeneratorOptions={maxTicks,bounds:opts.bounds,min:opts.min,max:opts.max,precision:tickOpts.precision,step:tickOpts.stepSize,count:tickOpts.count,maxDigits:me._maxDigits(),horizontal:me.isHorizontal(),minRotation:tickOpts.minRotation||0,includeBounds:tickOpts.includeBounds!==false};const dataRange=me._range||me;const ticks=generateTicks$1(numericGeneratorOptions,dataRange);if(opts.bounds==='ticks'){_setMinAndMaxByKey(ticks,me,'value');} -if(opts.reverse){ticks.reverse();me.start=me.max;me.end=me.min;}else{me.start=me.min;me.end=me.max;} +buildTicks(){const opts=this.options;const tickOpts=opts.ticks;let maxTicks=this.getTickLimit();maxTicks=Math.max(2,maxTicks);const numericGeneratorOptions={maxTicks,bounds:opts.bounds,min:opts.min,max:opts.max,precision:tickOpts.precision,step:tickOpts.stepSize,count:tickOpts.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:tickOpts.minRotation||0,includeBounds:tickOpts.includeBounds!==false};const dataRange=this._range||this;const ticks=generateTicks$1(numericGeneratorOptions,dataRange);if(opts.bounds==='ticks'){_setMinAndMaxByKey(ticks,this,'value');} +if(opts.reverse){ticks.reverse();this.start=this.max;this.end=this.min;}else{this.start=this.min;this.end=this.max;} return ticks;} -configure(){const me=this;const ticks=me.ticks;let start=me.min;let end=me.max;super.configure();if(me.options.offset&&ticks.length){const offset=(end-start)/Math.max(ticks.length-1,1)/2;start-=offset;end+=offset;} -me._startValue=start;me._endValue=end;me._valueRange=end-start;} -getLabelForValue(value){return formatNumber(value,this.chart.options.locale);}} -class LinearScale extends LinearScaleBase{determineDataLimits(){const me=this;const{min,max}=me.getMinMax(true);me.min=isNumberFinite(min)?min:0;me.max=isNumberFinite(max)?max:1;me.handleTickRangeOptions();} -computeTickLimit(){const me=this;const horizontal=me.isHorizontal();const length=horizontal?me.width:me.height;const minRotation=toRadians(me.options.ticks.minRotation);const ratio=(horizontal?Math.sin(minRotation):Math.cos(minRotation))||0.001;const tickFont=me._resolveTickFontOptions(0);return Math.ceil(length/Math.min(40,tickFont.lineHeight/ratio));} +configure(){const ticks=this.ticks;let start=this.min;let end=this.max;super.configure();if(this.options.offset&&ticks.length){const offset=(end-start)/Math.max(ticks.length-1,1)/2;start-=offset;end+=offset;} +this._startValue=start;this._endValue=end;this._valueRange=end-start;} +getLabelForValue(value){return formatNumber(value,this.chart.options.locale,this.options.ticks.format);}} +class LinearScale extends LinearScaleBase{determineDataLimits(){const{min,max}=this.getMinMax(true);this.min=isNumberFinite(min)?min:0;this.max=isNumberFinite(max)?max:1;this.handleTickRangeOptions();} +computeTickLimit(){const horizontal=this.isHorizontal();const length=horizontal?this.width:this.height;const minRotation=toRadians(this.options.ticks.minRotation);const ratio=(horizontal?Math.sin(minRotation):Math.cos(minRotation))||0.001;const tickFont=this._resolveTickFontOptions(0);return Math.ceil(length/Math.min(40,tickFont.lineHeight/ratio));} getPixelForValue(value){return value===null?NaN:this.getPixelForDecimal((value-this._startValue)/this._valueRange);} getValueForPixel(pixel){return this._startValue+this.getDecimalForPixel(pixel)*this._valueRange;}} LinearScale.id='linear';LinearScale.defaults={ticks:{callback:Ticks.formatters.numeric}};function isMajor(tickVal){const remain=tickVal/(Math.pow(10,Math.floor(log10(tickVal))));return remain===1;} @@ -2126,22 +2157,22 @@ tickVal=Math.round(significand*Math.pow(10,exp)*precision)/precision;}while(exp< class LogarithmicScale extends Scale{constructor(cfg){super(cfg);this.start=undefined;this.end=undefined;this._startValue=undefined;this._valueRange=0;} parse(raw,index){const value=LinearScaleBase.prototype.parse.apply(this,[raw,index]);if(value===0){this._zero=true;return undefined;} return isNumberFinite(value)&&value>0?value:null;} -determineDataLimits(){const me=this;const{min,max}=me.getMinMax(true);me.min=isNumberFinite(min)?Math.max(0,min):null;me.max=isNumberFinite(max)?Math.max(0,max):null;if(me.options.beginAtZero){me._zero=true;} -me.handleTickRangeOptions();} -handleTickRangeOptions(){const me=this;const{minDefined,maxDefined}=me.getUserBounds();let min=me.min;let max=me.max;const setMin=v=>(min=minDefined?min:v);const setMax=v=>(max=maxDefined?max:v);const exp=(v,m)=>Math.pow(10,Math.floor(log10(v))+m);if(min===max){if(min<=0){setMin(1);setMax(10);}else{setMin(exp(min,-1));setMax(exp(max,+1));}} +determineDataLimits(){const{min,max}=this.getMinMax(true);this.min=isNumberFinite(min)?Math.max(0,min):null;this.max=isNumberFinite(max)?Math.max(0,max):null;if(this.options.beginAtZero){this._zero=true;} +this.handleTickRangeOptions();} +handleTickRangeOptions(){const{minDefined,maxDefined}=this.getUserBounds();let min=this.min;let max=this.max;const setMin=v=>(min=minDefined?min:v);const setMax=v=>(max=maxDefined?max:v);const exp=(v,m)=>Math.pow(10,Math.floor(log10(v))+m);if(min===max){if(min<=0){setMin(1);setMax(10);}else{setMin(exp(min,-1));setMax(exp(max,+1));}} if(min<=0){setMin(exp(max,-1));} if(max<=0){setMax(exp(min,+1));} -if(me._zero&&me.min!==me._suggestedMin&&min===exp(me.min,0)){setMin(exp(min,-1));} -me.min=min;me.max=max;} -buildTicks(){const me=this;const opts=me.options;const generationOptions={min:me._userMin,max:me._userMax};const ticks=generateTicks(generationOptions,me);if(opts.bounds==='ticks'){_setMinAndMaxByKey(ticks,me,'value');} -if(opts.reverse){ticks.reverse();me.start=me.max;me.end=me.min;}else{me.start=me.min;me.end=me.max;} +if(this._zero&&this.min!==this._suggestedMin&&min===exp(this.min,0)){setMin(exp(min,-1));} +this.min=min;this.max=max;} +buildTicks(){const opts=this.options;const generationOptions={min:this._userMin,max:this._userMax};const ticks=generateTicks(generationOptions,this);if(opts.bounds==='ticks'){_setMinAndMaxByKey(ticks,this,'value');} +if(opts.reverse){ticks.reverse();this.start=this.max;this.end=this.min;}else{this.start=this.min;this.end=this.max;} return ticks;} -getLabelForValue(value){return value===undefined?'0':formatNumber(value,this.chart.options.locale);} -configure(){const me=this;const start=me.min;super.configure();me._startValue=log10(start);me._valueRange=log10(me.max)-log10(start);} -getPixelForValue(value){const me=this;if(value===undefined||value===0){value=me.min;} +getLabelForValue(value){return value===undefined?'0':formatNumber(value,this.chart.options.locale,this.options.ticks.format);} +configure(){const start=this.min;super.configure();this._startValue=log10(start);this._valueRange=log10(this.max)-log10(start);} +getPixelForValue(value){if(value===undefined||value===0){value=this.min;} if(value===null||isNaN(value)){return NaN;} -return me.getPixelForDecimal(value===me.min?0:(log10(value)-me._startValue)/me._valueRange);} -getValueForPixel(pixel){const me=this;const decimal=me.getDecimalForPixel(pixel);return Math.pow(10,me._startValue+decimal*me._valueRange);}} +return this.getPixelForDecimal(value===this.min?0:(log10(value)-this._startValue)/this._valueRange);} +getValueForPixel(pixel){const decimal=this.getDecimalForPixel(pixel);return Math.pow(10,this._startValue+decimal*this._valueRange);}} LogarithmicScale.id='logarithmic';LogarithmicScale.defaults={ticks:{callback:Ticks.formatters.logarithmic,major:{enabled:true}}};function getTickBackdropHeight(opts){const tickOpts=opts.ticks;if(tickOpts.display&&opts.display){const padding=toPadding(tickOpts.backdropPadding);return valueOrDefault(tickOpts.font&&tickOpts.font.size,defaults.font.size)+padding.height;} return 0;} function measureLabelSize(ctx,font,label){label=isArray(label)?label:[label];return{w:_longestText(ctx,font.string,label),h:label.length*font.lineHeight};} @@ -2166,36 +2197,36 @@ function pathRadiusLine(scale,radius,circular,labelCount){const{ctx}=scale;if(ci function drawRadiusLine(scale,gridLineOpts,radius,labelCount){const ctx=scale.ctx;const circular=gridLineOpts.circular;const{color,lineWidth}=gridLineOpts;if((!circular&&!labelCount)||!color||!lineWidth||radius<0){return;} ctx.save();ctx.strokeStyle=color;ctx.lineWidth=lineWidth;ctx.setLineDash(gridLineOpts.borderDash);ctx.lineDashOffset=gridLineOpts.borderDashOffset;ctx.beginPath();pathRadiusLine(scale,radius,circular,labelCount);ctx.closePath();ctx.stroke();ctx.restore();} function numberOrZero(param){return isNumber(param)?param:0;} -function createPointLabelContext(parent,index,label){return Object.assign(Object.create(parent),{label,index,type:'pointLabel'});} +function createPointLabelContext(parent,index,label){return createContext(parent,{label,index,type:'pointLabel'});} class RadialLinearScale extends LinearScaleBase{constructor(cfg){super(cfg);this.xCenter=undefined;this.yCenter=undefined;this.drawingArea=undefined;this._pointLabels=[];this._pointLabelItems=[];} -setDimensions(){const me=this;me.width=me.maxWidth;me.height=me.maxHeight;me.paddingTop=getTickBackdropHeight(me.options)/2;me.xCenter=Math.floor(me.width/2);me.yCenter=Math.floor((me.height-me.paddingTop)/2);me.drawingArea=Math.min(me.height-me.paddingTop,me.width)/2;} -determineDataLimits(){const me=this;const{min,max}=me.getMinMax(false);me.min=isNumberFinite(min)&&!isNaN(min)?min:0;me.max=isNumberFinite(max)&&!isNaN(max)?max:0;me.handleTickRangeOptions();} +setDimensions(){this.width=this.maxWidth;this.height=this.maxHeight;this.paddingTop=getTickBackdropHeight(this.options)/2;this.xCenter=Math.floor(this.width/2);this.yCenter=Math.floor((this.height-this.paddingTop)/2);this.drawingArea=Math.min(this.height-this.paddingTop,this.width)/2;} +determineDataLimits(){const{min,max}=this.getMinMax(false);this.min=isNumberFinite(min)&&!isNaN(min)?min:0;this.max=isNumberFinite(max)&&!isNaN(max)?max:0;this.handleTickRangeOptions();} computeTickLimit(){return Math.ceil(this.drawingArea/getTickBackdropHeight(this.options));} -generateTickLabels(ticks){const me=this;LinearScaleBase.prototype.generateTickLabels.call(me,ticks);me._pointLabels=me.getLabels().map((value,index)=>{const label=callback(me.options.pointLabels.callback,[value,index],me);return label||label===0?label:'';});} -fit(){const me=this;const opts=me.options;if(opts.display&&opts.pointLabels.display){fitWithPointLabels(me);}else{me.setCenterPoint(0,0,0,0);}} -_setReductions(largestPossibleRadius,furthestLimits,furthestAngles){const me=this;let radiusReductionLeft=furthestLimits.l/Math.sin(furthestAngles.l);let radiusReductionRight=Math.max(furthestLimits.r-me.width,0)/Math.sin(furthestAngles.r);let radiusReductionTop=-furthestLimits.t/Math.cos(furthestAngles.t);let radiusReductionBottom=-Math.max(furthestLimits.b-(me.height-me.paddingTop),0)/Math.cos(furthestAngles.b);radiusReductionLeft=numberOrZero(radiusReductionLeft);radiusReductionRight=numberOrZero(radiusReductionRight);radiusReductionTop=numberOrZero(radiusReductionTop);radiusReductionBottom=numberOrZero(radiusReductionBottom);me.drawingArea=Math.max(largestPossibleRadius/2,Math.min(Math.floor(largestPossibleRadius-(radiusReductionLeft+radiusReductionRight)/2),Math.floor(largestPossibleRadius-(radiusReductionTop+radiusReductionBottom)/2)));me.setCenterPoint(radiusReductionLeft,radiusReductionRight,radiusReductionTop,radiusReductionBottom);} -setCenterPoint(leftMovement,rightMovement,topMovement,bottomMovement){const me=this;const maxRight=me.width-rightMovement-me.drawingArea;const maxLeft=leftMovement+me.drawingArea;const maxTop=topMovement+me.drawingArea;const maxBottom=(me.height-me.paddingTop)-bottomMovement-me.drawingArea;me.xCenter=Math.floor(((maxLeft+maxRight)/2)+me.left);me.yCenter=Math.floor(((maxTop+maxBottom)/2)+me.top+me.paddingTop);} +generateTickLabels(ticks){LinearScaleBase.prototype.generateTickLabels.call(this,ticks);this._pointLabels=this.getLabels().map((value,index)=>{const label=callback(this.options.pointLabels.callback,[value,index],this);return label||label===0?label:'';});} +fit(){const opts=this.options;if(opts.display&&opts.pointLabels.display){fitWithPointLabels(this);}else{this.setCenterPoint(0,0,0,0);}} +_setReductions(largestPossibleRadius,furthestLimits,furthestAngles){let radiusReductionLeft=furthestLimits.l/Math.sin(furthestAngles.l);let radiusReductionRight=Math.max(furthestLimits.r-this.width,0)/Math.sin(furthestAngles.r);let radiusReductionTop=-furthestLimits.t/Math.cos(furthestAngles.t);let radiusReductionBottom=-Math.max(furthestLimits.b-(this.height-this.paddingTop),0)/Math.cos(furthestAngles.b);radiusReductionLeft=numberOrZero(radiusReductionLeft);radiusReductionRight=numberOrZero(radiusReductionRight);radiusReductionTop=numberOrZero(radiusReductionTop);radiusReductionBottom=numberOrZero(radiusReductionBottom);this.drawingArea=Math.max(largestPossibleRadius/2,Math.min(Math.floor(largestPossibleRadius-(radiusReductionLeft+radiusReductionRight)/2),Math.floor(largestPossibleRadius-(radiusReductionTop+radiusReductionBottom)/2)));this.setCenterPoint(radiusReductionLeft,radiusReductionRight,radiusReductionTop,radiusReductionBottom);} +setCenterPoint(leftMovement,rightMovement,topMovement,bottomMovement){const maxRight=this.width-rightMovement-this.drawingArea;const maxLeft=leftMovement+this.drawingArea;const maxTop=topMovement+this.drawingArea;const maxBottom=(this.height-this.paddingTop)-bottomMovement-this.drawingArea;this.xCenter=Math.floor(((maxLeft+maxRight)/2)+this.left);this.yCenter=Math.floor(((maxTop+maxBottom)/2)+this.top+this.paddingTop);} getIndexAngle(index){const angleMultiplier=TAU/this.getLabels().length;const startAngle=this.options.startAngle||0;return _normalizeAngle(index*angleMultiplier+toRadians(startAngle));} -getDistanceFromCenterForValue(value){const me=this;if(isNullOrUndef(value)){return NaN;} -const scalingFactor=me.drawingArea/(me.max-me.min);if(me.options.reverse){return(me.max-value)*scalingFactor;} -return(value-me.min)*scalingFactor;} +getDistanceFromCenterForValue(value){if(isNullOrUndef(value)){return NaN;} +const scalingFactor=this.drawingArea/(this.max-this.min);if(this.options.reverse){return(this.max-value)*scalingFactor;} +return(value-this.min)*scalingFactor;} getValueForDistanceFromCenter(distance){if(isNullOrUndef(distance)){return NaN;} -const me=this;const scaledDistance=distance/(me.drawingArea/(me.max-me.min));return me.options.reverse?me.max-scaledDistance:me.min+scaledDistance;} -getPointLabelContext(index){const me=this;const pointLabels=me._pointLabels||[];if(index>=0&&index=0&&index{if(index!==0){offset=me.getDistanceFromCenterForValue(tick.value);const optsAtIndex=grid.setContext(me.getContext(index-1));drawRadiusLine(me,optsAtIndex,offset,labelCount);}});} -if(angleLines.display){ctx.save();for(i=me.getLabels().length-1;i>=0;i--){const optsAtIndex=angleLines.setContext(me.getPointLabelContext(i));const{color,lineWidth}=optsAtIndex;if(!lineWidth||!color){continue;} -ctx.lineWidth=lineWidth;ctx.strokeStyle=color;ctx.setLineDash(optsAtIndex.borderDash);ctx.lineDashOffset=optsAtIndex.borderDashOffset;offset=me.getDistanceFromCenterForValue(opts.ticks.reverse?me.min:me.max);position=me.getPointPosition(i,offset);ctx.beginPath();ctx.moveTo(me.xCenter,me.yCenter);ctx.lineTo(position.x,position.y);ctx.stroke();} +drawBackground(){const{backgroundColor,grid:{circular}}=this.options;if(backgroundColor){const ctx=this.ctx;ctx.save();ctx.beginPath();pathRadiusLine(this,this.getDistanceFromCenterForValue(this._endValue),circular,this.getLabels().length);ctx.closePath();ctx.fillStyle=backgroundColor;ctx.fill();ctx.restore();}} +drawGrid(){const ctx=this.ctx;const opts=this.options;const{angleLines,grid}=opts;const labelCount=this.getLabels().length;let i,offset,position;if(opts.pointLabels.display){drawPointLabels(this,labelCount);} +if(grid.display){this.ticks.forEach((tick,index)=>{if(index!==0){offset=this.getDistanceFromCenterForValue(tick.value);const optsAtIndex=grid.setContext(this.getContext(index-1));drawRadiusLine(this,optsAtIndex,offset,labelCount);}});} +if(angleLines.display){ctx.save();for(i=this.getLabels().length-1;i>=0;i--){const optsAtIndex=angleLines.setContext(this.getPointLabelContext(i));const{color,lineWidth}=optsAtIndex;if(!lineWidth||!color){continue;} +ctx.lineWidth=lineWidth;ctx.strokeStyle=color;ctx.setLineDash(optsAtIndex.borderDash);ctx.lineDashOffset=optsAtIndex.borderDashOffset;offset=this.getDistanceFromCenterForValue(opts.ticks.reverse?this.min:this.max);position=this.getPointPosition(i,offset);ctx.beginPath();ctx.moveTo(this.xCenter,this.yCenter);ctx.lineTo(position.x,position.y);ctx.stroke();} ctx.restore();}} drawBorder(){} -drawLabels(){const me=this;const ctx=me.ctx;const opts=me.options;const tickOpts=opts.ticks;if(!tickOpts.display){return;} -const startAngle=me.getIndexAngle(0);let offset,width;ctx.save();ctx.translate(me.xCenter,me.yCenter);ctx.rotate(startAngle);ctx.textAlign='center';ctx.textBaseline='middle';me.ticks.forEach((tick,index)=>{if(index===0&&!opts.reverse){return;} -const optsAtIndex=tickOpts.setContext(me.getContext(index));const tickFont=toFont(optsAtIndex.font);offset=me.getDistanceFromCenterForValue(me.ticks[index].value);if(optsAtIndex.showLabelBackdrop){ctx.font=tickFont.string;width=ctx.measureText(tick.label).width;ctx.fillStyle=optsAtIndex.backdropColor;const padding=toPadding(optsAtIndex.backdropPadding);ctx.fillRect(-width/2-padding.left,-offset-tickFont.size/2-padding.top,width+padding.width,tickFont.size+padding.height);} +drawLabels(){const ctx=this.ctx;const opts=this.options;const tickOpts=opts.ticks;if(!tickOpts.display){return;} +const startAngle=this.getIndexAngle(0);let offset,width;ctx.save();ctx.translate(this.xCenter,this.yCenter);ctx.rotate(startAngle);ctx.textAlign='center';ctx.textBaseline='middle';this.ticks.forEach((tick,index)=>{if(index===0&&!opts.reverse){return;} +const optsAtIndex=tickOpts.setContext(this.getContext(index));const tickFont=toFont(optsAtIndex.font);offset=this.getDistanceFromCenterForValue(this.ticks[index].value);if(optsAtIndex.showLabelBackdrop){ctx.font=tickFont.string;width=ctx.measureText(tick.label).width;ctx.fillStyle=optsAtIndex.backdropColor;const padding=toPadding(optsAtIndex.backdropPadding);ctx.fillRect(-width/2-padding.left,-offset-tickFont.size/2-padding.top,width+padding.width,tickFont.size+padding.height);} renderText(ctx,tick.label,0,-offset,tickFont,{color:optsAtIndex.color,});});ctx.restore();} drawTitle(){}} RadialLinearScale.id='radialLinear';RadialLinearScale.defaults={display:true,animate:true,position:'chartArea',angleLines:{display:true,lineWidth:1,borderDash:[],borderDashOffset:0.0},grid:{circular:false},startAngle:0,ticks:{showLabelBackdrop:true,callback:Ticks.formatters.numeric},pointLabels:{backdropColor:undefined,backdropPadding:2,display:true,font:{size:10},callback(label){return label;},padding:5}};RadialLinearScale.defaultRoutes={'angleLines.color':'borderColor','pointLabels.color':'color','ticks.color':'color'};RadialLinearScale.descriptors={angleLines:{_fallback:'grid'}};const INTERVALS={millisecond:{common:true,size:1,steps:1000},second:{common:true,size:1000,steps:60},minute:{common:true,size:60000,steps:60},hour:{common:true,size:3600000,steps:24},day:{common:true,size:86400000,steps:30},week:{common:false,size:604800000,steps:4},month:{common:true,size:2.628e9,steps:12},quarter:{common:false,size:7.884e9,steps:4},year:{common:true,size:3.154e10}};const UNITS=(Object.keys(INTERVALS));function sorter(a,b){return a-b;} @@ -2220,55 +2251,55 @@ init(scaleOpts,opts){const time=scaleOpts.time||(scaleOpts.time={});const adapte parse(raw,index){if(raw===undefined){return null;} return parse(this,raw);} beforeLayout(){super.beforeLayout();this._cache={data:[],labels:[],all:[]};} -determineDataLimits(){const me=this;const options=me.options;const adapter=me._adapter;const unit=options.time.unit||'day';let{min,max,minDefined,maxDefined}=me.getUserBounds();function _applyBounds(bounds){if(!minDefined&&!isNaN(bounds.min)){min=Math.min(min,bounds.min);} +determineDataLimits(){const options=this.options;const adapter=this._adapter;const unit=options.time.unit||'day';let{min,max,minDefined,maxDefined}=this.getUserBounds();function _applyBounds(bounds){if(!minDefined&&!isNaN(bounds.min)){min=Math.min(min,bounds.min);} if(!maxDefined&&!isNaN(bounds.max)){max=Math.max(max,bounds.max);}} -if(!minDefined||!maxDefined){_applyBounds(me._getLabelBounds());if(options.bounds!=='ticks'||options.ticks.source!=='labels'){_applyBounds(me.getMinMax(false));}} -min=isNumberFinite(min)&&!isNaN(min)?min:+adapter.startOf(Date.now(),unit);max=isNumberFinite(max)&&!isNaN(max)?max:+adapter.endOf(Date.now(),unit)+1;me.min=Math.min(min,max-1);me.max=Math.max(min+1,max);} +if(!minDefined||!maxDefined){_applyBounds(this._getLabelBounds());if(options.bounds!=='ticks'||options.ticks.source!=='labels'){_applyBounds(this.getMinMax(false));}} +min=isNumberFinite(min)&&!isNaN(min)?min:+adapter.startOf(Date.now(),unit);max=isNumberFinite(max)&&!isNaN(max)?max:+adapter.endOf(Date.now(),unit)+1;this.min=Math.min(min,max-1);this.max=Math.max(min+1,max);} _getLabelBounds(){const arr=this.getLabelTimestamps();let min=Number.POSITIVE_INFINITY;let max=Number.NEGATIVE_INFINITY;if(arr.length){min=arr[0];max=arr[arr.length-1];} return{min,max};} -buildTicks(){const me=this;const options=me.options;const timeOpts=options.time;const tickOpts=options.ticks;const timestamps=tickOpts.source==='labels'?me.getLabelTimestamps():me._generate();if(options.bounds==='ticks'&×tamps.length){me.min=me._userMin||timestamps[0];me.max=me._userMax||timestamps[timestamps.length-1];} -const min=me.min;const max=me.max;const ticks=_filterBetween(timestamps,min,max);me._unit=timeOpts.unit||(tickOpts.autoSkip?determineUnitForAutoTicks(timeOpts.minUnit,me.min,me.max,me._getLabelCapacity(min)):determineUnitForFormatting(me,ticks.length,timeOpts.minUnit,me.min,me.max));me._majorUnit=!tickOpts.major.enabled||me._unit==='year'?undefined:determineMajorUnit(me._unit);me.initOffsets(timestamps);if(options.reverse){ticks.reverse();} -return ticksFromTimestamps(me,ticks,me._majorUnit);} -initOffsets(timestamps){const me=this;let start=0;let end=0;let first,last;if(me.options.offset&×tamps.length){first=me.getDecimalForValue(timestamps[0]);if(timestamps.length===1){start=1-first;}else{start=(me.getDecimalForValue(timestamps[1])-first)/2;} -last=me.getDecimalForValue(timestamps[timestamps.length-1]);if(timestamps.length===1){end=last;}else{end=(last-me.getDecimalForValue(timestamps[timestamps.length-2]))/2;}} -const limit=timestamps.length<3?0.5:0.25;start=_limitValue(start,0,limit);end=_limitValue(end,0,limit);me._offsets={start,end,factor:1/(start+1+end)};} -_generate(){const me=this;const adapter=me._adapter;const min=me.min;const max=me.max;const options=me.options;const timeOpts=options.time;const minor=timeOpts.unit||determineUnitForAutoTicks(timeOpts.minUnit,min,max,me._getLabelCapacity(min));const stepSize=valueOrDefault(timeOpts.stepSize,1);const weekday=minor==='week'?timeOpts.isoWeekday:false;const hasWeekday=isNumber(weekday)||weekday===true;const ticks={};let first=min;let time,count;if(hasWeekday){first=+adapter.startOf(first,'isoWeek',weekday);} +buildTicks(){const options=this.options;const timeOpts=options.time;const tickOpts=options.ticks;const timestamps=tickOpts.source==='labels'?this.getLabelTimestamps():this._generate();if(options.bounds==='ticks'&×tamps.length){this.min=this._userMin||timestamps[0];this.max=this._userMax||timestamps[timestamps.length-1];} +const min=this.min;const max=this.max;const ticks=_filterBetween(timestamps,min,max);this._unit=timeOpts.unit||(tickOpts.autoSkip?determineUnitForAutoTicks(timeOpts.minUnit,this.min,this.max,this._getLabelCapacity(min)):determineUnitForFormatting(this,ticks.length,timeOpts.minUnit,this.min,this.max));this._majorUnit=!tickOpts.major.enabled||this._unit==='year'?undefined:determineMajorUnit(this._unit);this.initOffsets(timestamps);if(options.reverse){ticks.reverse();} +return ticksFromTimestamps(this,ticks,this._majorUnit);} +initOffsets(timestamps){let start=0;let end=0;let first,last;if(this.options.offset&×tamps.length){first=this.getDecimalForValue(timestamps[0]);if(timestamps.length===1){start=1-first;}else{start=(this.getDecimalForValue(timestamps[1])-first)/2;} +last=this.getDecimalForValue(timestamps[timestamps.length-1]);if(timestamps.length===1){end=last;}else{end=(last-this.getDecimalForValue(timestamps[timestamps.length-2]))/2;}} +const limit=timestamps.length<3?0.5:0.25;start=_limitValue(start,0,limit);end=_limitValue(end,0,limit);this._offsets={start,end,factor:1/(start+1+end)};} +_generate(){const adapter=this._adapter;const min=this.min;const max=this.max;const options=this.options;const timeOpts=options.time;const minor=timeOpts.unit||determineUnitForAutoTicks(timeOpts.minUnit,min,max,this._getLabelCapacity(min));const stepSize=valueOrDefault(timeOpts.stepSize,1);const weekday=minor==='week'?timeOpts.isoWeekday:false;const hasWeekday=isNumber(weekday)||weekday===true;const ticks={};let first=min;let time,count;if(hasWeekday){first=+adapter.startOf(first,'isoWeek',weekday);} first=+adapter.startOf(first,hasWeekday?'day':minor);if(adapter.diff(max,min,minor)>100000*stepSize){throw new Error(min+' and '+max+' are too far apart with stepSize of '+stepSize+' '+minor);} -const timestamps=options.ticks.source==='data'&&me.getDataTimestamps();for(time=first,count=0;timea-b).map(x=>+x);} -getLabelForValue(value){const me=this;const adapter=me._adapter;const timeOpts=me.options.time;if(timeOpts.tooltipFormat){return adapter.format(value,timeOpts.tooltipFormat);} +getLabelForValue(value){const adapter=this._adapter;const timeOpts=this.options.time;if(timeOpts.tooltipFormat){return adapter.format(value,timeOpts.tooltipFormat);} return adapter.format(value,timeOpts.displayFormats.datetime);} -_tickFormatFunction(time,index,ticks,format){const me=this;const options=me.options;const formats=options.time.displayFormats;const unit=me._unit;const majorUnit=me._majorUnit;const minorFormat=unit&&formats[unit];const majorFormat=majorUnit&&formats[majorUnit];const tick=ticks[index];const major=majorUnit&&majorFormat&&tick&&tick.major;const label=me._adapter.format(time,format||(major?majorFormat:minorFormat));const formatter=options.ticks.callback;return formatter?callback(formatter,[label,index,ticks],me):label;} +_tickFormatFunction(time,index,ticks,format){const options=this.options;const formats=options.time.displayFormats;const unit=this._unit;const majorUnit=this._majorUnit;const minorFormat=unit&&formats[unit];const majorFormat=majorUnit&&formats[majorUnit];const tick=ticks[index];const major=majorUnit&&majorFormat&&tick&&tick.major;const label=this._adapter.format(time,format||(major?majorFormat:minorFormat));const formatter=options.ticks.callback;return formatter?callback(formatter,[label,index,ticks],this):label;} generateTickLabels(ticks){let i,ilen,tick;for(i=0,ilen=ticks.length;i0?capacity:1;} -getDataTimestamps(){const me=this;let timestamps=me._cache.data||[];let i,ilen;if(timestamps.length){return timestamps;} -const metas=me.getMatchingVisibleMetas();if(me._normalized&&metas.length){return(me._cache.data=metas[0].controller.getAllParsedValues(me));} -for(i=0,ilen=metas.length;i0?capacity:1;} +getDataTimestamps(){let timestamps=this._cache.data||[];let i,ilen;if(timestamps.length){return timestamps;} +const metas=this.getMatchingVisibleMetas();if(this._normalized&&metas.length){return(this._cache.data=metas[0].controller.getAllParsedValues(this));} +for(i=0,ilen=metas.length;i=table[lo].pos&&val<=table[hi].pos){({lo,hi}=_lookupByKey(table,'pos',val));} ({pos:prevSource,time:prevTarget}=table[lo]);({pos:nextSource,time:nextTarget}=table[hi]);}else{if(val>=table[lo].time&&val<=table[hi].time){({lo,hi}=_lookupByKey(table,'time',val));} ({time:prevSource,pos:prevTarget}=table[lo]);({time:nextSource,pos:nextTarget}=table[hi]);} const span=nextSource-prevSource;return span?prevTarget+(nextTarget-prevTarget)*(val-prevSource)/span:prevTarget;} class TimeSeriesScale extends TimeScale{constructor(props){super(props);this._table=[];this._minPos=undefined;this._tableRange=undefined;} -initOffsets(){const me=this;const timestamps=me._getTimestampsForTable();const table=me._table=me.buildLookupTable(timestamps);me._minPos=interpolate(table,me.min);me._tableRange=interpolate(table,me.max)-me._minPos;super.initOffsets(timestamps);} +initOffsets(){const timestamps=this._getTimestampsForTable();const table=this._table=this.buildLookupTable(timestamps);this._minPos=interpolate(table,this.min);this._tableRange=interpolate(table,this.max)-this._minPos;super.initOffsets(timestamps);} buildLookupTable(timestamps){const{min,max}=this;const items=[];const table=[];let i,ilen,prev,curr,next;for(i=0,ilen=timestamps.length;i=min&&curr<=max){items.push(curr);}} if(items.length<2){return[{time:min,pos:0},{time:max,pos:1}];} for(i=0,ilen=items.length;i=0;i--){startDelim=delimiters[i];if(startDelim.marker!==95&&startDelim.marker!==42){continue;} if(startDelim.end===-1){continue;} -endDelim=delimiters[startDelim.end];isStrong=i>0&&delimiters[i-1].end===startDelim.end+1&&delimiters[i-1].token===startDelim.token-1&&delimiters[startDelim.end+1].token===endDelim.token+1&&delimiters[i-1].marker===startDelim.marker;ch=String.fromCharCode(startDelim.marker);token=state.tokens[startDelim.token];token.type=isStrong?"strong_open":"em_open";token.tag=isStrong?"strong":"em";token.nesting=1;token.markup=isStrong?ch+ch:ch;token.content="";token=state.tokens[endDelim.token];token.type=isStrong?"strong_close":"em_close";token.tag=isStrong?"strong":"em";token.nesting=-1;token.markup=isStrong?ch+ch:ch;token.content="";if(isStrong){state.tokens[delimiters[i-1].token].content="";state.tokens[delimiters[startDelim.end+1].token].content="";i--;}}} +endDelim=delimiters[startDelim.end];isStrong=i>0&&delimiters[i-1].end===startDelim.end+1&&delimiters[i-1].marker===startDelim.marker&&delimiters[i-1].token===startDelim.token-1&&delimiters[startDelim.end+1].token===endDelim.token+1;ch=String.fromCharCode(startDelim.marker);token=state.tokens[startDelim.token];token.type=isStrong?"strong_open":"em_open";token.tag=isStrong?"strong":"em";token.nesting=1;token.markup=isStrong?ch+ch:ch;token.content="";token=state.tokens[endDelim.token];token.type=isStrong?"strong_close":"em_close";token.tag=isStrong?"strong":"em";token.nesting=-1;token.markup=isStrong?ch+ch:ch;token.content="";if(isStrong){state.tokens[delimiters[i-1].token].content="";state.tokens[delimiters[startDelim.end+1].token].content="";i--;}}} var postProcess_1=function emphasis(state){var curr,tokens_meta=state.tokens_meta,max=state.tokens_meta.length;postProcess(state,state.delimiters);for(curr=0;currminOpenerIdx;openerIdx-=opener.jump+1){opener=delimiters[openerIdx];if(opener.marker!==closer.marker)continue;if(opener.open&&opener.end<0){isOddMatch=false;if(opener.close||closer.open){if((opener.length+closer.length)%3===0){if(opener.length%3!==0||closer.length%3!==0){isOddMatch=true;}}} -if(!isOddMatch){lastJump=openerIdx>0&&!delimiters[openerIdx-1].open?delimiters[openerIdx-1].jump+1:0;closer.jump=closerIdx-openerIdx+lastJump;closer.open=false;opener.end=closerIdx;opener.jump=lastJump;opener.close=false;newMinOpenerIdx=-1;break;}}} +state.pos++;return true;};function processDelimiters(state,delimiters){var closerIdx,openerIdx,closer,opener,minOpenerIdx,newMinOpenerIdx,isOddMatch,lastJump,openersBottom={},max=delimiters.length;if(!max)return;var headerIdx=0;var lastTokenIdx=-2;var jumps=[];for(closerIdx=0;closerIdxminOpenerIdx;openerIdx-=jumps[openerIdx]+1){opener=delimiters[openerIdx];if(opener.marker!==closer.marker)continue;if(opener.open&&opener.end<0){isOddMatch=false;if(opener.close||closer.open){if((opener.length+closer.length)%3===0){if(opener.length%3!==0||closer.length%3!==0){isOddMatch=true;}}} +if(!isOddMatch){lastJump=openerIdx>0&&!delimiters[openerIdx-1].open?jumps[openerIdx-1]+1:0;jumps[closerIdx]=closerIdx-openerIdx+lastJump;jumps[openerIdx]=lastJump;closer.open=false;opener.end=closerIdx;opener.close=false;newMinOpenerIdx=-1;lastTokenIdx=-2;break;}}} if(newMinOpenerIdx!==-1){openersBottom[closer.marker][(closer.open?3:0)+(closer.length||0)%3]=newMinOpenerIdx;}}} var balance_pairs=function link_pairs(state){var curr,tokens_meta=state.tokens_meta,max=state.tokens_meta.length;processDelimiters(state,state.delimiters);for(curr=0;curr0)level++;if(tokens[curr].type==="text"&&curr+1=end){break;} continue;} state.pending+=state.src[state.pos++];} -if(state.pending){state.pushPending();}};ParserInline.prototype.parse=function(str,md,env,outTokens){var i,rules,len;var state=new this.State(str,md,env,outTokens);this.tokenize(state);rules=this.ruler2.getRules("");len=rules.length;for(i=0;i|$))";re.tpl_email_fuzzy="(^|"+text_separators+'|"|\\(|'+re.src_ZCc+")"+"("+re.src_email_name+"@"+re.tpl_host_fuzzy_strict+")";re.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+re.src_ZPCc+"))"+"((?![$+<=>^`|\uff5c])"+re.tpl_host_port_fuzzy_strict+re.src_path+")";re.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+re.src_ZPCc+"))"+"((?![$+<=>^`|\uff5c])"+re.tpl_host_port_no_ip_fuzzy_strict+re.src_path+")";return re;};function assign(obj){var sources=Array.prototype.slice.call(arguments,1);sources.forEach((function(source){if(!source){return;} +if(state.pending){state.pushPending();}};ParserInline.prototype.parse=function(str,md,env,outTokens){var i,rules,len;var state=new this.State(str,md,env,outTokens);this.tokenize(state);rules=this.ruler2.getRules("");len=rules.length;for(i=0;i|$))";re.tpl_email_fuzzy="(^|"+text_separators+'|"|\\(|'+re.src_ZCc+")"+"("+re.src_email_name+"@"+re.tpl_host_fuzzy_strict+")";re.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+re.src_ZPCc+"))"+"((?![$+<=>^`|\uff5c])"+re.tpl_host_port_fuzzy_strict+re.src_path+")";re.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+re.src_ZPCc+"))"+"((?![$+<=>^`|\uff5c])"+re.tpl_host_port_no_ip_fuzzy_strict+re.src_path+")";return re;};function assign(obj){var sources=Array.prototype.slice.call(arguments,1);sources.forEach((function(source){if(!source){return;} Object.keys(source).forEach((function(key){obj[key]=source[key];}));}));return obj;} function _class(obj){return Object.prototype.toString.call(obj);} function isString(obj){return _class(obj)==="[object String]";} @@ -3729,6 +3761,10 @@ console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i function sync(){if(!element.value){return;} let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');} teamID.value=json.teamID||'';keyID.value=json.keyID||'';p8.value=json.p8||'';} +sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-microsoft",controller:function(element){let clientSecret=document.getElementById("oauth2MicrosoftClientSecret");let tenantId=document.getElementById("oauth2MicrosoftTenantId");element.addEventListener('change',sync);clientSecret.addEventListener('change',update);tenantId.addEventListener('change',update);function update(){let json={};json.clientSecret=clientSecret.value;json.tenantId=tenantId.value;element.value=JSON.stringify(json);} +function sync(){if(!element.value){return;} +let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');} +clientSecret.value=json.clientSecret||'';tenantId.value=json.tenantId||'';} sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown,rtl){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){alignText();element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:''},{name:"underline",icon:''},{name:"italic",icon:''},{name:"olist",icon:''},{name:"ulist",icon:''},{name:"link",icon:''}]});var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;console.log(clipboardData.getData("Text"));window.pell.exec("insertText",clipboardData.getData("Text"));return true;};var alignText=function(){let paragraphs=editor.content.querySelectorAll('p,li');let last='';for(let paragraph of paragraphs){var content=paragraph.textContent;if(content.trim()===''){content=last.textContent;} diff --git a/public/dist/scripts/app-dep.js b/public/dist/scripts/app-dep.js index 8cf262d4a2..f79f347f43 100644 --- a/public/dist/scripts/app-dep.js +++ b/public/dist/scripts/app-dep.js @@ -593,18 +593,18 @@ return output;}} exports.Appwrite=Appwrite;Object.defineProperty(exports,'__esModule',{value:true});}(this.window=this.window||{},null,window));(function(global,factory){typeof exports==='object'&&typeof module!=='undefined'?module.exports=factory():typeof define==='function'&&define.amd?define(factory):(global=typeof globalThis!=='undefined'?globalThis:global||self,global.Chart=factory());}(this,(function(){'use strict';function fontString(pixelSize,fontStyle,fontFamily){return fontStyle+' '+pixelSize+'px '+fontFamily;} const requestAnimFrame=(function(){if(typeof window==='undefined'){return function(callback){return callback();};} return window.requestAnimationFrame;}());function throttled(fn,thisArg,updateFn){const updateArgs=updateFn||((args)=>Array.prototype.slice.call(args));let ticking=false;let args=[];return function(...rest){args=updateArgs(rest);if(!ticking){ticking=true;requestAnimFrame.call(window,()=>{ticking=false;fn.apply(thisArg,args);});}};} -function debounce(fn,delay){let timeout;return function(){if(delay){clearTimeout(timeout);timeout=setTimeout(fn,delay);}else{fn();} +function debounce(fn,delay){let timeout;return function(...args){if(delay){clearTimeout(timeout);timeout=setTimeout(fn,delay,args);}else{fn.apply(this,args);} return delay;};} const _toLeftRightCenter=(align)=>align==='start'?'left':align==='end'?'right':'center';const _alignStartEnd=(align,start,end)=>align==='start'?start:align==='end'?end:(start+end)/2;const _textX=(align,left,right,rtl)=>{const check=rtl?'left':'right';return align===check?right:align==='center'?(left+right)/2:left;};class Animator{constructor(){this._request=null;this._charts=new Map();this._running=false;this._lastDate=undefined;} _notify(chart,anims,date,type){const callbacks=anims.listeners[type];const numSteps=anims.duration;callbacks.forEach(fn=>fn({chart,initial:anims.initial,numSteps,currentStep:Math.min(date-anims.start,numSteps)}));} -_refresh(){const me=this;if(me._request){return;} -me._running=true;me._request=requestAnimFrame.call(window,()=>{me._update();me._request=null;if(me._running){me._refresh();}});} -_update(date=Date.now()){const me=this;let remaining=0;me._charts.forEach((anims,chart)=>{if(!anims.running||!anims.items.length){return;} +_refresh(){if(this._request){return;} +this._running=true;this._request=requestAnimFrame.call(window,()=>{this._update();this._request=null;if(this._running){this._refresh();}});} +_update(date=Date.now()){let remaining=0;this._charts.forEach((anims,chart)=>{if(!anims.running||!anims.items.length){return;} const items=anims.items;let i=items.length-1;let draw=false;let item;for(;i>=0;--i){item=items[i];if(item._active){if(item._total>anims.duration){anims.duration=item._total;} item.tick(date);draw=true;}else{items[i]=items[items.length-1];items.pop();}} -if(draw){chart.draw();me._notify(chart,anims,date,'progress');} -if(!items.length){anims.running=false;me._notify(chart,anims,date,'complete');anims.initial=false;} -remaining+=items.length;});me._lastDate=date;if(remaining===0){me._running=false;}} +if(draw){chart.draw();this._notify(chart,anims,date,'progress');} +if(!items.length){anims.running=false;this._notify(chart,anims,date,'complete');anims.initial=false;} +remaining+=items.length;});this._lastDate=date;if(remaining===0){this._running=false;}} _getAnims(chart){const charts=this._charts;let anims=charts.get(chart);if(!anims){anims={running:false,initial:true,items:[],listeners:{complete:[],progress:[]}};charts.set(chart,anims);} return anims;} listen(chart,event,cb){this._getAnims(chart).listeners[event].push(cb);} @@ -757,6 +757,7 @@ function _normalizeAngle(a){return(a%TAU+TAU)%TAU;} function _angleBetween(angle,start,end,sameAngleIsFullCircle){const a=_normalizeAngle(angle);const s=_normalizeAngle(start);const e=_normalizeAngle(end);const angleToStart=_normalizeAngle(s-a);const angleToEnd=_normalizeAngle(e-a);const startToAngle=_normalizeAngle(a-s);const endToAngle=_normalizeAngle(a-e);return a===s||a===e||(sameAngleIsFullCircle&&s===e)||(angleToStart>angleToEnd&&startToAngle=Math.min(start,end)-epsilon&&value<=Math.max(start,end)+epsilon;} function toFontString(font){if(!font||isNullOrUndef(font.size)||isNullOrUndef(font.family)){return null;} return(font.style?font.style+' ':'') +(font.weight?font.weight+' ':'') @@ -851,7 +852,7 @@ if(element.inRange(position.x,position.y,useFinalPosition)){intersectsItem=true; return items;} var Interaction={modes:{index(chart,e,options,useFinalPosition){const position=getRelativePosition(e,chart);const axis=options.axis||'x';const items=options.intersect?getIntersectItems(chart,position,axis,useFinalPosition):getNearestItems(chart,position,axis,false,useFinalPosition);const elements=[];if(!items.length){return[];} chart.getSortedVisibleDatasetMetas().forEach((meta)=>{const index=items[0].index;const element=meta.data[index];if(element&&!element.skip){elements.push({element,datasetIndex:meta.index,index});}});return elements;},dataset(chart,e,options,useFinalPosition){const position=getRelativePosition(e,chart);const axis=options.axis||'xy';let items=options.intersect?getIntersectItems(chart,position,axis,useFinalPosition):getNearestItems(chart,position,axis,false,useFinalPosition);if(items.length>0){const datasetIndex=items[0].datasetIndex;const data=chart.getDatasetMeta(datasetIndex).data;items=[];for(let i=0;i+v||0;function _readValueToProps(value,props){const ret={};const objProps=isObject(props);const keys=objProps?Object.keys(props):props;const read=isObject(value)?objProps?prop=>valueOrDefault(value[prop],value[props[prop]]):prop=>value[prop]:()=>value;for(const prop of keys){ret[prop]=numberOrZero$1(read(prop));} @@ -867,7 +868,8 @@ if(context!==undefined&&typeof value==='function'){value=value(context);cacheabl if(index!==undefined&&isArray(value)){value=value[index%value.length];cacheable=false;} if(value!==undefined){if(info&&!cacheable){info.cacheable=false;} return value;}}} -function _addGrace(minmax,grace){const{min,max}=minmax;return{min:min-Math.abs(toDimension(grace,min)),max:max+toDimension(grace,max)};} +function _addGrace(minmax,grace,beginAtZero){const{min,max}=minmax;const change=toDimension(grace,(max-min)/2);const keepZero=(value,add)=>beginAtZero&&value===0?0:value+add;return{min:keepZero(min,-Math.abs(change)),max:keepZero(max,change)};} +function createContext(parentContext,context){return Object.assign(Object.create(parentContext),context);} const STATIC_POSITIONS=['left','top','right','bottom'];function filterByPosition(array,position){return array.filter(v=>v.pos===position);} function filterDynamicPositionByAxis(array,axis){return array.filter(v=>STATIC_POSITIONS.indexOf(v.pos)===-1&&v.box.axis===axis);} function sortByWeight(array,reverse){return array.sort((a,b)=>{const v0=reverse?b:a;const v1=reverse?a:b;return v0.weight===v1.weight?v0.index-v1.index:v0.weight-v1.weight;});} @@ -898,22 +900,21 @@ stack.start=y;stack.placed+=width;y=box.bottom;}else{const height=chartArea.h*we if(box.fullSize){setBoxDims(box,x,userPadding.top,width,params.outerHeight-userPadding.bottom-userPadding.top);}else{setBoxDims(box,x,chartArea.top+stack.placed,width,height);} stack.start=x;stack.placed+=height;x=box.right;}} chartArea.x=x;chartArea.y=y;} -defaults.set('layout',{padding:{top:0,right:0,bottom:0,left:0}});var layouts={addBox(chart,item){if(!chart.boxes){chart.boxes=[];} +defaults.set('layout',{autoPadding:true,padding:{top:0,right:0,bottom:0,left:0}});var layouts={addBox(chart,item){if(!chart.boxes){chart.boxes=[];} item.fullSize=item.fullSize||false;item.position=item.position||'top';item.weight=item.weight||0;item._layers=item._layers||function(){return[{z:0,draw(chartArea){item.draw(chartArea);}}];};chart.boxes.push(item);},removeBox(chart,layoutItem){const index=chart.boxes?chart.boxes.indexOf(layoutItem):-1;if(index!==-1){chart.boxes.splice(index,1);}},configure(chart,item,options){item.fullSize=options.fullSize;item.position=options.position;item.weight=options.weight;},update(chart,width,height,minPadding){if(!chart){return;} const padding=toPadding(chart.options.layout.padding);const availableWidth=Math.max(width-padding.width,0);const availableHeight=Math.max(height-padding.height,0);const boxes=buildLayoutBoxes(chart.boxes);const verticalBoxes=boxes.vertical;const horizontalBoxes=boxes.horizontal;each(chart.boxes,box=>{if(typeof box.beforeLayout==='function'){box.beforeLayout();}});const visibleVerticalBoxCount=verticalBoxes.reduce((total,wrap)=>wrap.box.options&&wrap.box.options.display===false?total:total+1,0)||1;const params=Object.freeze({outerWidth:width,outerHeight:height,padding,availableWidth,availableHeight,vBoxMaxWidth:availableWidth/2/visibleVerticalBoxCount,hBoxMaxHeight:availableHeight/2});const maxPadding=Object.assign({},padding);updateMaxPadding(maxPadding,toPadding(minPadding));const chartArea=Object.assign({maxPadding,w:availableWidth,h:availableHeight,x:padding.left,y:padding.top},padding);const stacks=setLayoutDims(verticalBoxes.concat(horizontalBoxes),params);fitBoxes(boxes.fullSize,chartArea,params,stacks);fitBoxes(verticalBoxes,chartArea,params,stacks);if(fitBoxes(horizontalBoxes,chartArea,params,stacks)){fitBoxes(verticalBoxes,chartArea,params,stacks);} -handleMaxPadding(chartArea);placeBoxes(boxes.leftAndTop,chartArea,params,stacks);chartArea.x+=chartArea.w;chartArea.y+=chartArea.h;placeBoxes(boxes.rightAndBottom,chartArea,params,stacks);chart.chartArea={left:chartArea.left,top:chartArea.top,right:chartArea.left+chartArea.w,bottom:chartArea.top+chartArea.h,height:chartArea.h,width:chartArea.w,};each(boxes.chartArea,(layout)=>{const box=layout.box;Object.assign(box,chart.chartArea);box.update(chartArea.w,chartArea.h);});}};function _createResolver(scopes,prefixes=[''],rootScopes=scopes,fallback,getTarget=()=>scopes[0]){if(!defined(fallback)){fallback=_resolve('_fallback',scopes);} -const cache={[Symbol.toStringTag]:'Object',_cacheable:true,_scopes:scopes,_rootScopes:rootScopes,_fallback:fallback,_getTarget:getTarget,override:(scope)=>_createResolver([scope,...scopes],prefixes,rootScopes,fallback),};return new Proxy(cache,{deleteProperty(target,prop){delete target[prop];delete target._keys;delete scopes[0][prop];return true;},get(target,prop){return _cached(target,prop,()=>_resolveWithPrefixes(prop,prefixes,scopes,target));},getOwnPropertyDescriptor(target,prop){return Reflect.getOwnPropertyDescriptor(target._scopes[0],prop);},getPrototypeOf(){return Reflect.getPrototypeOf(scopes[0]);},has(target,prop){return getKeysFromAllScopes(target).includes(prop);},ownKeys(target){return getKeysFromAllScopes(target);},set(target,prop,value){const storage=target._storage||(target._storage=getTarget());storage[prop]=value;delete target[prop];delete target._keys;return true;}});} +handleMaxPadding(chartArea);placeBoxes(boxes.leftAndTop,chartArea,params,stacks);chartArea.x+=chartArea.w;chartArea.y+=chartArea.h;placeBoxes(boxes.rightAndBottom,chartArea,params,stacks);chart.chartArea={left:chartArea.left,top:chartArea.top,right:chartArea.left+chartArea.w,bottom:chartArea.top+chartArea.h,height:chartArea.h,width:chartArea.w,};each(boxes.chartArea,(layout)=>{const box=layout.box;Object.assign(box,chart.chartArea);box.update(chartArea.w,chartArea.h,{left:0,top:0,right:0,bottom:0});});}};function _createResolver(scopes,prefixes=[''],rootScopes=scopes,fallback,getTarget=()=>scopes[0]){if(!defined(fallback)){fallback=_resolve('_fallback',scopes);} +const cache={[Symbol.toStringTag]:'Object',_cacheable:true,_scopes:scopes,_rootScopes:rootScopes,_fallback:fallback,_getTarget:getTarget,override:(scope)=>_createResolver([scope,...scopes],prefixes,rootScopes,fallback),};return new Proxy(cache,{deleteProperty(target,prop){delete target[prop];delete target._keys;delete scopes[0][prop];return true;},get(target,prop){return _cached(target,prop,()=>_resolveWithPrefixes(prop,prefixes,scopes,target));},getOwnPropertyDescriptor(target,prop){return Reflect.getOwnPropertyDescriptor(target._scopes[0],prop);},getPrototypeOf(){return Reflect.getPrototypeOf(scopes[0]);},has(target,prop){return getKeysFromAllScopes(target).includes(prop);},ownKeys(target){return getKeysFromAllScopes(target);},set(target,prop,value){const storage=target._storage||(target._storage=getTarget());target[prop]=storage[prop]=value;delete target._keys;return true;}});} function _attachContext(proxy,context,subProxy,descriptorDefaults){const cache={_cacheable:false,_proxy:proxy,_context:context,_subProxy:subProxy,_stack:new Set(),_descriptors:_descriptors(proxy,descriptorDefaults),setContext:(ctx)=>_attachContext(proxy,ctx,subProxy,descriptorDefaults),override:(scope)=>_attachContext(proxy.override(scope),context,subProxy,descriptorDefaults)};return new Proxy(cache,{deleteProperty(target,prop){delete target[prop];delete proxy[prop];return true;},get(target,prop,receiver){return _cached(target,prop,()=>_resolveWithContext(target,prop,receiver));},getOwnPropertyDescriptor(target,prop){return target._descriptors.allKeys?Reflect.has(proxy,prop)?{enumerable:true,configurable:true}:undefined:Reflect.getOwnPropertyDescriptor(proxy,prop);},getPrototypeOf(){return Reflect.getPrototypeOf(proxy);},has(target,prop){return Reflect.has(proxy,prop);},ownKeys(){return Reflect.ownKeys(proxy);},set(target,prop,value){proxy[prop]=value;delete target[prop];return true;}});} function _descriptors(proxy,defaults={scriptable:true,indexable:true}){const{_scriptable=defaults.scriptable,_indexable=defaults.indexable,_allKeys=defaults.allKeys}=proxy;return{allKeys:_allKeys,scriptable:_scriptable,indexable:_indexable,isScriptable:isFunction(_scriptable)?_scriptable:()=>_scriptable,isIndexable:isFunction(_indexable)?_indexable:()=>_indexable};} -const readKey=(prefix,name)=>prefix?prefix+_capitalize(name):name;const needsSubResolver=(prop,value)=>isObject(value)&&prop!=='adapters';function _cached(target,prop,resolve){let value=target[prop];if(defined(value)){return value;} -value=resolve();if(defined(value)){target[prop]=value;} -return value;} +const readKey=(prefix,name)=>prefix?prefix+_capitalize(name):name;const needsSubResolver=(prop,value)=>isObject(value)&&prop!=='adapters'&&(Object.getPrototypeOf(value)===null||value.constructor===Object);function _cached(target,prop,resolve){if(Object.prototype.hasOwnProperty.call(target,prop)){return target[prop];} +const value=resolve();target[prop]=value;return value;} function _resolveWithContext(target,prop,receiver){const{_proxy,_context,_subProxy,_descriptors:descriptors}=target;let value=_proxy[prop];if(isFunction(value)&&descriptors.isScriptable(prop)){value=_resolveScriptable(prop,value,target,receiver);} if(isArray(value)&&value.length){value=_resolveArray(prop,value,target,descriptors.isIndexable);} if(needsSubResolver(prop,value)){value=_attachContext(value,_context,_subProxy&&_subProxy[prop],descriptors);} return value;} function _resolveScriptable(prop,value,target,receiver){const{_proxy,_context,_subProxy,_stack}=target;if(_stack.has(prop)){throw new Error('Recursion detected: '+Array.from(_stack).join('->')+'->'+prop);} -_stack.add(prop);value=value(_context,_subProxy||receiver);_stack.delete(prop);if(isObject(value)){value=createSubResolver(_proxy._scopes,_proxy,prop,value);} +_stack.add(prop);value=value(_context,_subProxy||receiver);_stack.delete(prop);if(needsSubResolver(prop,value)){value=createSubResolver(_proxy._scopes,_proxy,prop,value);} return value;} function _resolveArray(prop,value,target,isIndexable){const{_proxy,_context,_subProxy,_descriptors:descriptors}=target;if(defined(_context.index)&&isIndexable(prop)){value=value[_context.index%value.length];}else if(isObject(value[0])){const arr=value;const scopes=_proxy._scopes.filter(s=>s!==arr);value=[];for(const item of arr){const resolver=createSubResolver(scopes,_proxy,prop,item);value.push(_attachContext(resolver,_context,_subProxy&&_subProxy[prop],descriptors));}} return value;} @@ -969,7 +970,7 @@ return align==='right'?'left':'right';},xPlus(x,value){return x-value;},leftForL function overrideTextDirection(ctx,direction){let style,original;if(direction==='ltr'||direction==='rtl'){style=ctx.canvas.style;original=[style.getPropertyValue('direction'),style.getPropertyPriority('direction'),];style.setProperty('direction',direction,'important');ctx.prevTextDirection=original;}} function restoreTextDirection(ctx,original){if(original!==undefined){delete ctx.prevTextDirection;ctx.canvas.style.setProperty('direction',original[0],original[1]);}} function propertyFn(property){if(property==='angle'){return{between:_angleBetween,compare:_angleDiff,normalize:_normalizeAngle,};} -return{between:(n,s,e)=>n>=Math.min(s,e)&&n<=Math.max(e,s),compare:(a,b)=>a-b,normalize:x=>x};} +return{between:_isBetween,compare:(a,b)=>a-b,normalize:x=>x};} function normalizeSegment({start,end,count,loop,style}){return{start:start%count,end:end%count,loop:loop&&(end-start+1)%count===0,style};} function getSegment(segment,points,bounds){const{property,start:startBound,end:endBound}=bounds;const{between,normalize}=propertyFn(property);const count=points.length;let{start,end,loop}=segment;let i,ilen;if(loop){start+=count;end+=count;for(i=0,ilen=count;ivalue===null||value==='';function initCanvas(canvas,aspectRatio){const style=canvas.style;const renderHeight=canvas.getAttribute('height');const renderWidth=canvas.getAttribute('width');canvas[EXPANDO_KEY]={initial:{height:renderHeight,width:renderWidth,style:{display:style.display,height:style.height,width:style.width}}};style.display=style.display||'block';style.boxSizing=style.boxSizing||'border-box';if(isNullOrEmpty(renderWidth)){const displayWidth=readUsedSize(canvas,'width');if(displayWidth!==undefined){canvas.width=displayWidth;}} if(isNullOrEmpty(renderHeight)){if(canvas.style.height===''){canvas.height=canvas.width/(aspectRatio||2);}else{const displayHeight=readUsedSize(canvas,'height');if(displayHeight!==undefined){canvas.height=displayHeight;}}} return canvas;} const eventListenerOptions=supportsEventListenerOptions?{passive:true}:false;function addListener(node,type,listener){node.addEventListener(type,listener,eventListenerOptions);} function removeListener(chart,type,listener){chart.canvas.removeEventListener(type,listener,eventListenerOptions);} function fromNativeEvent(event,chart){const type=EVENT_TYPES[event.type]||event.type;const{x,y}=getRelativePosition$1(event,chart);return{type,chart,native:event,x:x!==undefined?x:null,y:y!==undefined?y:null,};} -function createAttachObserver(chart,type,listener){const canvas=chart.canvas;const container=canvas&&_getParentNode(canvas);const element=container||canvas;const observer=new MutationObserver(entries=>{const parent=_getParentNode(element);entries.forEach(entry=>{for(let i=0;i{entries.forEach(entry=>{for(let i=0;i{let trigger=false;for(const entry of entries){trigger=trigger||nodeListContains(entry.addedNodes,canvas);trigger=trigger&&!nodeListContains(entry.removedNodes,canvas);} +if(trigger){listener();}});observer.observe(document,{childList:true,subtree:true});return observer;} +function createDetachObserver(chart,type,listener){const canvas=chart.canvas;const observer=new MutationObserver(entries=>{let trigger=false;for(const entry of entries){trigger=trigger||nodeListContains(entry.removedNodes,canvas);trigger=trigger&&!nodeListContains(entry.addedNodes,canvas);} +if(trigger){listener();}});observer.observe(document,{childList:true,subtree:true});return observer;} const drpListeningCharts=new Map();let oldDevicePixelRatio=0;function onWindowResize(){const dpr=window.devicePixelRatio;if(dpr===oldDevicePixelRatio){return;} oldDevicePixelRatio=dpr;drpListeningCharts.forEach((resize,chart)=>{if(chart.currentDevicePixelRatio!==dpr){resize();}});} function listenDevicePixelRatioChanges(chart,resize){if(!drpListeningCharts.size){window.addEventListener('resize',onWindowResize);} @@ -1048,11 +1057,11 @@ function _detectPlatform(canvas){if(!_isDomSupported()||(typeof OffscreenCanvas! return DomPlatform;} var platforms=Object.freeze({__proto__:null,_detectPlatform:_detectPlatform,BasePlatform:BasePlatform,BasicPlatform:BasicPlatform,DomPlatform:DomPlatform});const transparent='transparent';const interpolators={boolean(from,to,factor){return factor>0.5?to:from;},color(from,to,factor){const c0=color(from||transparent);const c1=c0.valid&&color(to||transparent);return c1&&c1.valid?c1.mix(c0,factor).hexString():to;},number(from,to,factor){return from+(to-from)*factor;}};class Animation{constructor(cfg,target,prop,to){const currentValue=target[prop];to=resolve([cfg.to,to,currentValue,cfg.from]);const from=resolve([cfg.from,currentValue,to]);this._active=true;this._fn=cfg.fn||interpolators[cfg.type||typeof from];this._easing=effects[cfg.easing]||effects.linear;this._start=Math.floor(Date.now()+(cfg.delay||0));this._duration=this._total=Math.floor(cfg.duration);this._loop=!!cfg.loop;this._target=target;this._prop=prop;this._from=from;this._to=to;this._promises=undefined;} active(){return this._active;} -update(cfg,to,date){const me=this;if(me._active){me._notify(false);const currentValue=me._target[me._prop];const elapsed=date-me._start;const remain=me._duration-elapsed;me._start=date;me._duration=Math.floor(Math.max(remain,cfg.duration));me._total+=elapsed;me._loop=!!cfg.loop;me._to=resolve([cfg.to,to,currentValue,cfg.from]);me._from=resolve([cfg.from,currentValue,to]);}} -cancel(){const me=this;if(me._active){me.tick(Date.now());me._active=false;me._notify(false);}} -tick(date){const me=this;const elapsed=date-me._start;const duration=me._duration;const prop=me._prop;const from=me._from;const loop=me._loop;const to=me._to;let factor;me._active=from!==to&&(loop||(elapsed1?2-factor:factor;factor=me._easing(Math.min(1,Math.max(0,factor)));me._target[prop]=me._fn(from,to,factor);} +update(cfg,to,date){if(this._active){this._notify(false);const currentValue=this._target[this._prop];const elapsed=date-this._start;const remain=this._duration-elapsed;this._start=date;this._duration=Math.floor(Math.max(remain,cfg.duration));this._total+=elapsed;this._loop=!!cfg.loop;this._to=resolve([cfg.to,to,currentValue,cfg.from]);this._from=resolve([cfg.from,currentValue,to]);}} +cancel(){if(this._active){this.tick(Date.now());this._active=false;this._notify(false);}} +tick(date){const elapsed=date-this._start;const duration=this._duration;const prop=this._prop;const from=this._from;const loop=this._loop;const to=this._to;let factor;this._active=from!==to&&(loop||(elapsed1?2-factor:factor;factor=this._easing(Math.min(1,Math.max(0,factor)));this._target[prop]=this._fn(from,to,factor);} wait(){const promises=this._promises||(this._promises=[]);return new Promise((res,rej)=>{promises.push({res,rej});});} _notify(resolved){const method=resolved?'res':'rej';const promises=this._promises||[];for(let i=0;iname!=='onProgress'&&name!=='onComplete'&&name!=='fn',});defaults.set('animations',{colors:{type:'color',properties:colors},numbers:{type:'number',properties:numbers},});defaults.describe('animations',{_fallback:'animation',});defaults.set('transitions',{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:'transparent'},visible:{type:'boolean',duration:0},}},hide:{animations:{colors:{to:'transparent'},visible:{type:'boolean',easing:'linear',fn:v=>v|0},}}});class Animations{constructor(chart,config){this._chart=chart;this._properties=new Map();this.configure(config);} @@ -1084,7 +1093,7 @@ function toClip(value){let t,r,b,l;if(isObject(value)){t=value.top;r=value.right return{top:t,right:r,bottom:b,left:l,disabled:value===false};} function getSortedDatasetIndices(chart,filterVisible){const keys=[];const metasets=chart._getSortedDatasetMetas(filterVisible);let i,ilen;for(i=0,ilen=metasets.length;i0)||(!positive&&value<0)){return meta.index;}} +function getLastIndexInStack(stack,vScale,positive,type){for(const meta of vScale.getMatchingVisibleMetas(type).reverse()){const value=stack[meta.index];if((positive&&value>0)||(!positive&&value<0)){return meta.index;}} return null;} -function updateStacks(controller,parsed){const{chart,_cachedMeta:meta}=controller;const stacks=chart._stacks||(chart._stacks={});const{iScale,vScale,index:datasetIndex}=meta;const iAxis=iScale.axis;const vAxis=vScale.axis;const key=getStackKey(iScale,vScale,meta);const ilen=parsed.length;let stack;for(let i=0;iscales[key].axis===axis).shift();} -function createDatasetContext(parent,index){return Object.assign(Object.create(parent),{active:false,dataset:undefined,datasetIndex:index,index,mode:'default',type:'dataset'});} -function createDataContext(parent,index,element){return Object.assign(Object.create(parent),{active:false,dataIndex:index,parsed:undefined,raw:undefined,element,index,mode:'default',type:'data'});} +function createDatasetContext(parent,index){return createContext(parent,{active:false,dataset:undefined,datasetIndex:index,index,mode:'default',type:'dataset'});} +function createDataContext(parent,index,element){return createContext(parent,{active:false,dataIndex:index,parsed:undefined,raw:undefined,element,index,mode:'default',type:'data'});} function clearStacks(meta,items){const datasetIndex=meta.controller.index;const axis=meta.vScale&&meta.vScale.axis;if(!axis){return;} items=items||meta._parsed;for(const parsed of items){const stacks=parsed._stacks;if(!stacks||stacks[axis]===undefined||stacks[axis][datasetIndex]===undefined){return;} delete stacks[axis][datasetIndex];}} -const isDirectUpdateMode=(mode)=>mode==='reset'||mode==='none';const cloneIfNotShared=(cached,shared)=>shared?cached:Object.assign({},cached);class DatasetController{constructor(chart,datasetIndex){this.chart=chart;this._ctx=chart.ctx;this.index=datasetIndex;this._cachedDataOpts={};this._cachedMeta=this.getMeta();this._type=this._cachedMeta.type;this.options=undefined;this._parsing=false;this._data=undefined;this._objectData=undefined;this._sharedOptions=undefined;this._drawStart=undefined;this._drawCount=undefined;this.enableOptionSharing=false;this.$context=undefined;this._syncList=[];this.initialize();} -initialize(){const me=this;const meta=me._cachedMeta;me.configure();me.linkScales();meta._stacked=isStacked(meta.vScale,meta);me.addElements();} +const isDirectUpdateMode=(mode)=>mode==='reset'||mode==='none';const cloneIfNotShared=(cached,shared)=>shared?cached:Object.assign({},cached);const createStack=(canStack,meta,chart)=>canStack&&!meta.hidden&&meta._stacked&&{keys:getSortedDatasetIndices(chart,true),values:null};class DatasetController{constructor(chart,datasetIndex){this.chart=chart;this._ctx=chart.ctx;this.index=datasetIndex;this._cachedDataOpts={};this._cachedMeta=this.getMeta();this._type=this._cachedMeta.type;this.options=undefined;this._parsing=false;this._data=undefined;this._objectData=undefined;this._sharedOptions=undefined;this._drawStart=undefined;this._drawCount=undefined;this.enableOptionSharing=false;this.$context=undefined;this._syncList=[];this.initialize();} +initialize(){const meta=this._cachedMeta;this.configure();this.linkScales();meta._stacked=isStacked(meta.vScale,meta);this.addElements();} updateIndex(datasetIndex){if(this.index!==datasetIndex){clearStacks(this._cachedMeta);} this.index=datasetIndex;} -linkScales(){const me=this;const chart=me.chart;const meta=me._cachedMeta;const dataset=me.getDataset();const chooseId=(axis,x,y,r)=>axis==='x'?x:axis==='r'?r:y;const xid=meta.xAxisID=valueOrDefault(dataset.xAxisID,getFirstScaleId(chart,'x'));const yid=meta.yAxisID=valueOrDefault(dataset.yAxisID,getFirstScaleId(chart,'y'));const rid=meta.rAxisID=valueOrDefault(dataset.rAxisID,getFirstScaleId(chart,'r'));const indexAxis=meta.indexAxis;const iid=meta.iAxisID=chooseId(indexAxis,xid,yid,rid);const vid=meta.vAxisID=chooseId(indexAxis,yid,xid,rid);meta.xScale=me.getScaleForId(xid);meta.yScale=me.getScaleForId(yid);meta.rScale=me.getScaleForId(rid);meta.iScale=me.getScaleForId(iid);meta.vScale=me.getScaleForId(vid);} +linkScales(){const chart=this.chart;const meta=this._cachedMeta;const dataset=this.getDataset();const chooseId=(axis,x,y,r)=>axis==='x'?x:axis==='r'?r:y;const xid=meta.xAxisID=valueOrDefault(dataset.xAxisID,getFirstScaleId(chart,'x'));const yid=meta.yAxisID=valueOrDefault(dataset.yAxisID,getFirstScaleId(chart,'y'));const rid=meta.rAxisID=valueOrDefault(dataset.rAxisID,getFirstScaleId(chart,'r'));const indexAxis=meta.indexAxis;const iid=meta.iAxisID=chooseId(indexAxis,xid,yid,rid);const vid=meta.vAxisID=chooseId(indexAxis,yid,xid,rid);meta.xScale=this.getScaleForId(xid);meta.yScale=this.getScaleForId(yid);meta.rScale=this.getScaleForId(rid);meta.iScale=this.getScaleForId(iid);meta.vScale=this.getScaleForId(vid);} getDataset(){return this.chart.data.datasets[this.index];} getMeta(){return this.chart.getDatasetMeta(this.index);} getScaleForId(scaleID){return this.chart.scales[scaleID];} @@ -1116,18 +1125,18 @@ _getOtherScale(scale){const meta=this._cachedMeta;return scale===meta.iScale?met reset(){this._update('reset');} _destroy(){const meta=this._cachedMeta;if(this._data){unlistenArrayEvents(this._data,this);} if(meta._stacked){clearStacks(meta);}} -_dataCheck(){const me=this;const dataset=me.getDataset();const data=dataset.data||(dataset.data=[]);const _data=me._data;if(isObject(data)){me._data=convertObjectDataToArray(data);}else if(_data!==data){if(_data){unlistenArrayEvents(_data,me);const meta=me._cachedMeta;clearStacks(meta);meta._parsed=[];} -if(data&&Object.isExtensible(data)){listenArrayEvents(data,me);} -me._syncList=[];me._data=data;}} -addElements(){const me=this;const meta=me._cachedMeta;me._dataCheck();if(me.datasetElementType){meta.dataset=new me.datasetElementType();}} -buildOrUpdateElements(resetNewElements){const me=this;const meta=me._cachedMeta;const dataset=me.getDataset();let stackChanged=false;me._dataCheck();const oldStacked=meta._stacked;meta._stacked=isStacked(meta.vScale,meta);if(meta.stack!==dataset.stack){stackChanged=true;clearStacks(meta);meta.stack=dataset.stack;} -me._resyncElements(resetNewElements);if(stackChanged||oldStacked!==meta._stacked){updateStacks(me,meta._parsed);}} -configure(){const me=this;const config=me.chart.config;const scopeKeys=config.datasetScopeKeys(me._type);const scopes=config.getOptionScopes(me.getDataset(),scopeKeys,true);me.options=config.createResolver(scopes,me.getContext());me._parsing=me.options.parsing;} -parse(start,count){const me=this;const{_cachedMeta:meta,_data:data}=me;const{iScale,_stacked}=meta;const iAxis=iScale.axis;let sorted=start===0&&count===data.length?true:meta._sorted;let prev=start>0&&meta._parsed[start-1];let i,cur,parsed;if(me._parsing===false){meta._parsed=data;meta._sorted=true;parsed=data;}else{if(isArray(data[start])){parsed=me.parseArrayData(meta,data,start,count);}else if(isObject(data[start])){parsed=me.parseObjectData(meta,data,start,count);}else{parsed=me.parsePrimitiveData(meta,data,start,count);} +_dataCheck(){const dataset=this.getDataset();const data=dataset.data||(dataset.data=[]);const _data=this._data;if(isObject(data)){this._data=convertObjectDataToArray(data);}else if(_data!==data){if(_data){unlistenArrayEvents(_data,this);const meta=this._cachedMeta;clearStacks(meta);meta._parsed=[];} +if(data&&Object.isExtensible(data)){listenArrayEvents(data,this);} +this._syncList=[];this._data=data;}} +addElements(){const meta=this._cachedMeta;this._dataCheck();if(this.datasetElementType){meta.dataset=new this.datasetElementType();}} +buildOrUpdateElements(resetNewElements){const meta=this._cachedMeta;const dataset=this.getDataset();let stackChanged=false;this._dataCheck();const oldStacked=meta._stacked;meta._stacked=isStacked(meta.vScale,meta);if(meta.stack!==dataset.stack){stackChanged=true;clearStacks(meta);meta.stack=dataset.stack;} +this._resyncElements(resetNewElements);if(stackChanged||oldStacked!==meta._stacked){updateStacks(this,meta._parsed);}} +configure(){const config=this.chart.config;const scopeKeys=config.datasetScopeKeys(this._type);const scopes=config.getOptionScopes(this.getDataset(),scopeKeys,true);this.options=config.createResolver(scopes,this.getContext());this._parsing=this.options.parsing;this._cachedDataOpts={};} +parse(start,count){const{_cachedMeta:meta,_data:data}=this;const{iScale,_stacked}=meta;const iAxis=iScale.axis;let sorted=start===0&&count===data.length?true:meta._sorted;let prev=start>0&&meta._parsed[start-1];let i,cur,parsed;if(this._parsing===false){meta._parsed=data;meta._sorted=true;parsed=data;}else{if(isArray(data[start])){parsed=this.parseArrayData(meta,data,start,count);}else if(isObject(data[start])){parsed=this.parseObjectData(meta,data,start,count);}else{parsed=this.parsePrimitiveData(meta,data,start,count);} const isNotInOrderComparedToPrev=()=>cur[iAxis]===null||(prev&&cur[iAxis]otherValue||otherMaxotherValue||otherMax=0;--i){if(_skip()){continue;} -me.updateRangeFromParsed(range,scale,parsed,stack);break;}} +this.updateRangeFromParsed(range,scale,parsed,stack);break;}} return range;} getAllParsedValues(scale){const parsed=this._cachedMeta._parsed;const values=[];let i,ilen,value;for(i=0,ilen=parsed.length;i=0&&index=0&&indexme.getContext(index,active);const values=config.resolveNamedOptions(scopes,names,context,prefixes);if(values.$shared){values.$shared=sharing;cache[cacheKey]=Object.freeze(cloneIfNotShared(values,sharing));} +_resolveElementOptions(elementType,mode='default',index){const active=mode==='active';const cache=this._cachedDataOpts;const cacheKey=elementType+'-'+mode;const cached=cache[cacheKey];const sharing=this.enableOptionSharing&&defined(index);if(cached){return cloneIfNotShared(cached,sharing);} +const config=this.chart.config;const scopeKeys=config.datasetElementScopeKeys(this._type,elementType);const prefixes=active?[`${elementType}Hover`,'hover',elementType,'']:[elementType,''];const scopes=config.getOptionScopes(this.getDataset(),scopeKeys);const names=Object.keys(defaults.elements[elementType]);const context=()=>this.getContext(index,active);const values=config.resolveNamedOptions(scopes,names,context,prefixes);if(values.$shared){values.$shared=sharing;cache[cacheKey]=Object.freeze(cloneIfNotShared(values,sharing));} return values;} -_resolveAnimations(index,transition,active){const me=this;const chart=me.chart;const cache=me._cachedDataOpts;const cacheKey=`animation-${transition}`;const cached=cache[cacheKey];if(cached){return cached;} -let options;if(chart.options.animation!==false){const config=me.chart.config;const scopeKeys=config.datasetAnimationScopeKeys(me._type,transition);const scopes=config.getOptionScopes(me.getDataset(),scopeKeys);options=config.createResolver(scopes,me.getContext(index,active,transition));} +_resolveAnimations(index,transition,active){const chart=this.chart;const cache=this._cachedDataOpts;const cacheKey=`animation-${transition}`;const cached=cache[cacheKey];if(cached){return cached;} +let options;if(chart.options.animation!==false){const config=this.chart.config;const scopeKeys=config.datasetAnimationScopeKeys(this._type,transition);const scopes=config.getOptionScopes(this.getDataset(),scopeKeys);options=config.createResolver(scopes,this.getContext(index,active,transition));} const animations=new Animations(chart,options&&options.animations);if(options&&options._cacheable){cache[cacheKey]=Object.freeze(animations);} return animations;} getSharedOptions(options){if(!options.$shared){return;} @@ -1177,26 +1186,28 @@ removeHoverStyle(element,datasetIndex,index){this._setStyle(element,index,'activ setHoverStyle(element,datasetIndex,index){this._setStyle(element,index,'active',true);} _removeDatasetHoverStyle(){const element=this._cachedMeta.dataset;if(element){this._setStyle(element,undefined,'active',false);}} _setDatasetHoverStyle(){const element=this._cachedMeta.dataset;if(element){this._setStyle(element,undefined,'active',true);}} -_resyncElements(resetNewElements){const me=this;const data=me._data;const elements=me._cachedMeta.data;for(const[method,arg1,arg2]of me._syncList){me[method](arg1,arg2);} -me._syncList=[];const numMeta=elements.length;const numData=data.length;const count=Math.min(numData,numMeta);if(count){me.parse(0,count);} -if(numData>numMeta){me._insertElements(numMeta,numData-numMeta,resetNewElements);}else if(numData{arr.length+=count;for(i=arr.length-1;i>=end;i--){arr[i]=arr[i-count];}};move(data);for(i=start;inumMeta){this._insertElements(numMeta,numData-numMeta,resetNewElements);}else if(numData{arr.length+=count;for(i=arr.length-1;i>=end;i--){arr[i]=arr[i-count];}};move(data);for(i=start;i{ret[prop]=anims[prop]&&anims[prop].active()?anims[prop]._to:me[prop];});return ret;}} +getProps(props,final){const anims=this.$animations;if(!final||!anims){return this;} +const ret={};props.forEach(prop=>{ret[prop]=anims[prop]&&anims[prop].active()?anims[prop]._to:this[prop];});return ret;}} Element.defaults={};Element.defaultRoutes=undefined;const formatters={values(value){return isArray(value)?value:''+value;},numeric(tickValue,index,ticks){if(tickValue===0){return'0';} const locale=this.chart.options.locale;let notation;let delta=tickValue;if(ticks.length>1){const maxTick=Math.max(Math.abs(ticks[0].value),Math.abs(ticks[ticks.length-1].value));if(maxTick<1e-4||maxTick>1e+15){notation='scientific';} delta=calculateDelta(tickValue,ticks);} @@ -1231,8 +1242,8 @@ gc.splice(0,gcLen);}});} function getTickMarkLength(options){return options.drawTicks?options.tickLength:0;} function getTitleHeight(options,fallback){if(!options.display){return 0;} const font=toFont(options.font,fallback);const padding=toPadding(options.padding);const lines=isArray(options.text)?options.text.length:1;return(lines*font.lineHeight)+padding.height;} -function createScaleContext(parent,scale){return Object.assign(Object.create(parent),{scale,type:'scale'});} -function createTickContext(parent,index,tick){return Object.assign(Object.create(parent),{tick,index,type:'tick'});} +function createScaleContext(parent,scale){return createContext(parent,{scale,type:'scale'});} +function createTickContext(parent,index,tick){return createContext(parent,{tick,index,type:'tick'});} function titleAlign(align,position,reverse){let ret=_toLeftRightCenter(align);if((reverse&&position!=='right')||(!reverse&&position==='right')){ret=reverseAlign(ret);} return ret;} function titleArgs(scale,offset,position,align){const{top,left,bottom,right,chart}=scale;const{chartArea,scales}=chart;let rotation=0;let maxWidth,titleX,titleY;const height=bottom-top;const width=right-left;if(scale.isHorizontal()){titleX=_alignStartEnd(align,left,right);if(isObject(position)){const positionAxisID=Object.keys(position)[0];const value=position[positionAxisID];titleY=scales[positionAxisID].getPixelForValue(value)+height-offset;}else if(position==='center'){titleY=(chartArea.bottom+chartArea.top)/2+height-offset;}else{titleY=offsetFromEdge(scale,position,offset);} @@ -1240,30 +1251,30 @@ maxWidth=right-left;}else{if(isObject(position)){const positionAxisID=Object.key titleY=_alignStartEnd(align,bottom,top);rotation=position==='left'?-HALF_PI:HALF_PI;} return{titleX,titleY,maxWidth,rotation};} class Scale extends Element{constructor(cfg){super();this.id=cfg.id;this.type=cfg.type;this.options=undefined;this.ctx=cfg.ctx;this.chart=cfg.chart;this.top=undefined;this.bottom=undefined;this.left=undefined;this.right=undefined;this.width=undefined;this.height=undefined;this._margins={left:0,right:0,top:0,bottom:0};this.maxWidth=undefined;this.maxHeight=undefined;this.paddingTop=undefined;this.paddingBottom=undefined;this.paddingLeft=undefined;this.paddingRight=undefined;this.axis=undefined;this.labelRotation=undefined;this.min=undefined;this.max=undefined;this._range=undefined;this.ticks=[];this._gridLineItems=null;this._labelItems=null;this._labelSizes=null;this._length=0;this._maxLength=0;this._longestTextCache={};this._startPixel=undefined;this._endPixel=undefined;this._reversePixels=false;this._userMax=undefined;this._userMin=undefined;this._suggestedMax=undefined;this._suggestedMin=undefined;this._ticksLength=0;this._borderValue=0;this._cache={};this._dataLimitsCached=false;this.$context=undefined;} -init(options){const me=this;me.options=options.setContext(me.getContext());me.axis=options.axis;me._userMin=me.parse(options.min);me._userMax=me.parse(options.max);me._suggestedMin=me.parse(options.suggestedMin);me._suggestedMax=me.parse(options.suggestedMax);} +init(options){this.options=options.setContext(this.getContext());this.axis=options.axis;this._userMin=this.parse(options.min);this._userMax=this.parse(options.max);this._suggestedMin=this.parse(options.suggestedMin);this._suggestedMax=this.parse(options.suggestedMax);} parse(raw,index){return raw;} getUserBounds(){let{_userMin,_userMax,_suggestedMin,_suggestedMax}=this;_userMin=finiteOrDefault(_userMin,Number.POSITIVE_INFINITY);_userMax=finiteOrDefault(_userMax,Number.NEGATIVE_INFINITY);_suggestedMin=finiteOrDefault(_suggestedMin,Number.POSITIVE_INFINITY);_suggestedMax=finiteOrDefault(_suggestedMax,Number.NEGATIVE_INFINITY);return{min:finiteOrDefault(_userMin,_suggestedMin),max:finiteOrDefault(_userMax,_suggestedMax),minDefined:isNumberFinite(_userMin),maxDefined:isNumberFinite(_userMax)};} -getMinMax(canStack){const me=this;let{min,max,minDefined,maxDefined}=me.getUserBounds();let range;if(minDefined&&maxDefined){return{min,max};} -const metas=me.getMatchingVisibleMetas();for(let i=0,ilen=metas.length;imax?max:min;max=minDefined&&min>max?min:max;return{min:finiteOrDefault(min,finiteOrDefault(max,min)),max:finiteOrDefault(max,finiteOrDefault(min,max))};} +getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0};} getTicks(){return this.ticks;} getLabels(){const data=this.chart.data;return this.options.labels||(this.isHorizontal()?data.xLabels:data.yLabels)||data.labels||[];} beforeLayout(){this._cache={};this._dataLimitsCached=false;} beforeUpdate(){callback(this.options.beforeUpdate,[this]);} -update(maxWidth,maxHeight,margins){const me=this;const tickOpts=me.options.ticks;const sampleSize=tickOpts.sampleSize;me.beforeUpdate();me.maxWidth=maxWidth;me.maxHeight=maxHeight;me._margins=margins=Object.assign({left:0,right:0,top:0,bottom:0},margins);me.ticks=null;me._labelSizes=null;me._gridLineItems=null;me._labelItems=null;me.beforeSetDimensions();me.setDimensions();me.afterSetDimensions();me._maxLength=me.isHorizontal()?me.width+margins.left+margins.right:me.height+margins.top+margins.bottom;if(!me._dataLimitsCached){me.beforeDataLimits();me.determineDataLimits();me.afterDataLimits();me._range=_addGrace(me,me.options.grace);me._dataLimitsCached=true;} -me.beforeBuildTicks();me.ticks=me.buildTicks()||[];me.afterBuildTicks();const samplingEnabled=sampleSize=maxRotation||numTicks<=1||!me.isHorizontal()){me.labelRotation=minRotation;return;} -const labelSizes=me._getLabelSizes();const maxLabelWidth=labelSizes.widest.width;const maxLabelHeight=labelSizes.highest.height;const maxWidth=_limitValue(me.chart.width-maxLabelWidth,0,me.maxWidth);tickWidth=options.offset?me.maxWidth/numTicks:maxWidth/(numTicks-1);if(maxLabelWidth+6>tickWidth){tickWidth=maxWidth/(numTicks-(options.offset?0.5:1));maxHeight=me.maxHeight-getTickMarkLength(options.grid) --tickOpts.padding-getTitleHeight(options.title,me.chart.options.font);maxLabelDiagonal=Math.sqrt(maxLabelWidth*maxLabelWidth+maxLabelHeight*maxLabelHeight);labelRotation=toDegrees(Math.min(Math.asin(_limitValue((labelSizes.highest.height+6)/tickWidth,-1,1)),Math.asin(_limitValue(maxHeight/maxLabelDiagonal,-1,1))-Math.asin(_limitValue(maxLabelHeight/maxLabelDiagonal,-1,1))));labelRotation=Math.max(minRotation,Math.min(maxRotation,labelRotation));} -me.labelRotation=labelRotation;} +calculateLabelRotation(){const options=this.options;const tickOpts=options.ticks;const numTicks=this.ticks.length;const minRotation=tickOpts.minRotation||0;const maxRotation=tickOpts.maxRotation;let labelRotation=minRotation;let tickWidth,maxHeight,maxLabelDiagonal;if(!this._isVisible()||!tickOpts.display||minRotation>=maxRotation||numTicks<=1||!this.isHorizontal()){this.labelRotation=minRotation;return;} +const labelSizes=this._getLabelSizes();const maxLabelWidth=labelSizes.widest.width;const maxLabelHeight=labelSizes.highest.height;const maxWidth=_limitValue(this.chart.width-maxLabelWidth,0,this.maxWidth);tickWidth=options.offset?this.maxWidth/numTicks:maxWidth/(numTicks-1);if(maxLabelWidth+6>tickWidth){tickWidth=maxWidth/(numTicks-(options.offset?0.5:1));maxHeight=this.maxHeight-getTickMarkLength(options.grid) +-tickOpts.padding-getTitleHeight(options.title,this.chart.options.font);maxLabelDiagonal=Math.sqrt(maxLabelWidth*maxLabelWidth+maxLabelHeight*maxLabelHeight);labelRotation=toDegrees(Math.min(Math.asin(_limitValue((labelSizes.highest.height+6)/tickWidth,-1,1)),Math.asin(_limitValue(maxHeight/maxLabelDiagonal,-1,1))-Math.asin(_limitValue(maxLabelHeight/maxLabelDiagonal,-1,1))));labelRotation=Math.max(minRotation,Math.min(maxRotation,labelRotation));} +this.labelRotation=labelRotation;} afterCalculateLabelRotation(){callback(this.options.afterCalculateLabelRotation,[this]);} beforeFit(){callback(this.options.beforeFit,[this]);} -fit(){const me=this;const minSize={width:0,height:0};const{chart,options:{ticks:tickOpts,title:titleOpts,grid:gridOpts}}=me;const display=me._isVisible();const isHorizontal=me.isHorizontal();if(display){const titleHeight=getTitleHeight(titleOpts,chart.options.font);if(isHorizontal){minSize.width=me.maxWidth;minSize.height=getTickMarkLength(gridOpts)+titleHeight;}else{minSize.height=me.maxHeight;minSize.width=getTickMarkLength(gridOpts)+titleHeight;} -if(tickOpts.display&&me.ticks.length){const{first,last,widest,highest}=me._getLabelSizes();const tickPadding=tickOpts.padding*2;const angleRadians=toRadians(me.labelRotation);const cos=Math.cos(angleRadians);const sin=Math.sin(angleRadians);if(isHorizontal){const labelHeight=tickOpts.mirror?0:sin*widest.width+cos*highest.height;minSize.height=Math.min(me.maxHeight,minSize.height+labelHeight+tickPadding);}else{const labelWidth=tickOpts.mirror?0:cos*widest.width+sin*highest.height;minSize.width=Math.min(me.maxWidth,minSize.width+labelWidth+tickPadding);} -me._calculatePadding(first,last,sin,cos);}} -me._handleMargins();if(isHorizontal){me.width=me._length=chart.width-me._margins.left-me._margins.right;me.height=minSize.height;}else{me.width=minSize.width;me.height=me._length=chart.height-me._margins.top-me._margins.bottom;}} -_calculatePadding(first,last,sin,cos){const me=this;const{ticks:{align,padding},position}=me.options;const isRotated=me.labelRotation!==0;const labelsBelowTicks=position!=='top'&&me.axis==='x';if(me.isHorizontal()){const offsetLeft=me.getPixelForTick(0)-me.left;const offsetRight=me.right-me.getPixelForTick(me.ticks.length-1);let paddingLeft=0;let paddingRight=0;if(isRotated){if(labelsBelowTicks){paddingLeft=cos*first.width;paddingRight=sin*last.height;}else{paddingLeft=sin*first.height;paddingRight=cos*last.width;}}else if(align==='start'){paddingRight=last.width;}else if(align==='end'){paddingLeft=first.width;}else{paddingLeft=first.width/2;paddingRight=last.width/2;} -me.paddingLeft=Math.max((paddingLeft-offsetLeft+padding)*me.width/(me.width-offsetLeft),0);me.paddingRight=Math.max((paddingRight-offsetRight+padding)*me.width/(me.width-offsetRight),0);}else{let paddingTop=last.height/2;let paddingBottom=first.height/2;if(align==='start'){paddingTop=0;paddingBottom=first.height;}else if(align==='end'){paddingTop=last.height;paddingBottom=0;} -me.paddingTop=paddingTop+padding;me.paddingBottom=paddingBottom+padding;}} -_handleMargins(){const me=this;if(me._margins){me._margins.left=Math.max(me.paddingLeft,me._margins.left);me._margins.top=Math.max(me.paddingTop,me._margins.top);me._margins.right=Math.max(me.paddingRight,me._margins.right);me._margins.bottom=Math.max(me.paddingBottom,me._margins.bottom);}} +fit(){const minSize={width:0,height:0};const{chart,options:{ticks:tickOpts,title:titleOpts,grid:gridOpts}}=this;const display=this._isVisible();const isHorizontal=this.isHorizontal();if(display){const titleHeight=getTitleHeight(titleOpts,chart.options.font);if(isHorizontal){minSize.width=this.maxWidth;minSize.height=getTickMarkLength(gridOpts)+titleHeight;}else{minSize.height=this.maxHeight;minSize.width=getTickMarkLength(gridOpts)+titleHeight;} +if(tickOpts.display&&this.ticks.length){const{first,last,widest,highest}=this._getLabelSizes();const tickPadding=tickOpts.padding*2;const angleRadians=toRadians(this.labelRotation);const cos=Math.cos(angleRadians);const sin=Math.sin(angleRadians);if(isHorizontal){const labelHeight=tickOpts.mirror?0:sin*widest.width+cos*highest.height;minSize.height=Math.min(this.maxHeight,minSize.height+labelHeight+tickPadding);}else{const labelWidth=tickOpts.mirror?0:cos*widest.width+sin*highest.height;minSize.width=Math.min(this.maxWidth,minSize.width+labelWidth+tickPadding);} +this._calculatePadding(first,last,sin,cos);}} +this._handleMargins();if(isHorizontal){this.width=this._length=chart.width-this._margins.left-this._margins.right;this.height=minSize.height;}else{this.width=minSize.width;this.height=this._length=chart.height-this._margins.top-this._margins.bottom;}} +_calculatePadding(first,last,sin,cos){const{ticks:{align,padding},position}=this.options;const isRotated=this.labelRotation!==0;const labelsBelowTicks=position!=='top'&&this.axis==='x';if(this.isHorizontal()){const offsetLeft=this.getPixelForTick(0)-this.left;const offsetRight=this.right-this.getPixelForTick(this.ticks.length-1);let paddingLeft=0;let paddingRight=0;if(isRotated){if(labelsBelowTicks){paddingLeft=cos*first.width;paddingRight=sin*last.height;}else{paddingLeft=sin*first.height;paddingRight=cos*last.width;}}else if(align==='start'){paddingRight=last.width;}else if(align==='end'){paddingLeft=first.width;}else{paddingLeft=first.width/2;paddingRight=last.width/2;} +this.paddingLeft=Math.max((paddingLeft-offsetLeft+padding)*this.width/(this.width-offsetLeft),0);this.paddingRight=Math.max((paddingRight-offsetRight+padding)*this.width/(this.width-offsetRight),0);}else{let paddingTop=last.height/2;let paddingBottom=first.height/2;if(align==='start'){paddingTop=0;paddingBottom=first.height;}else if(align==='end'){paddingTop=last.height;paddingBottom=0;} +this.paddingTop=paddingTop+padding;this.paddingBottom=paddingBottom+padding;}} +_handleMargins(){if(this._margins){this._margins.left=Math.max(this.paddingLeft,this._margins.left);this._margins.top=Math.max(this.paddingTop,this._margins.top);this._margins.right=Math.max(this.paddingRight,this._margins.right);this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom);}} afterFit(){callback(this.options.afterFit,[this]);} isHorizontal(){const{axis,position}=this.options;return position==='top'||position==='bottom'||axis==='x';} isFullSize(){return this.options.fullSize;} -_convertTicksToLabels(ticks){const me=this;me.beforeTickToLabelConversion();me.generateTickLabels(ticks);let i,ilen;for(i=0,ilen=ticks.length;iticks.length-1){return null;} return this.getPixelForValue(ticks[index].value);} -getPixelForDecimal(decimal){const me=this;if(me._reversePixels){decimal=1-decimal;} -const pixel=me._startPixel+decimal*me._length;return _int16Range(me._alignToPixels?_alignPixel(me.chart,pixel,0):pixel);} +getPixelForDecimal(decimal){if(this._reversePixels){decimal=1-decimal;} +const pixel=this._startPixel+decimal*this._length;return _int16Range(this._alignToPixels?_alignPixel(this.chart,pixel,0):pixel);} getDecimalForPixel(pixel){const decimal=(pixel-this._startPixel)/this._length;return this._reversePixels?1-decimal:decimal;} getBasePixel(){return this.getPixelForValue(this.getBaseValue());} getBaseValue(){const{min,max}=this;return min<0&&max<0?max:min>0&&max>0?min:0;} -getContext(index){const me=this;const ticks=me.ticks||[];if(index>=0&&indexw*sin?w/cos:h/sin:h*sin=0&&indexw*sin?w/cos:h/sin:h*sin0;} -_computeGridLineItems(chartArea){const me=this;const axis=me.axis;const chart=me.chart;const options=me.options;const{grid,position}=options;const offset=grid.offset;const isHorizontal=me.isHorizontal();const ticks=me.ticks;const ticksLength=ticks.length+(offset?1:0);const tl=getTickMarkLength(grid);const items=[];const borderOpts=grid.setContext(me.getContext());const axisWidth=borderOpts.drawBorder?borderOpts.borderWidth:0;const axisHalfWidth=axisWidth/2;const alignBorderValue=function(pixel){return _alignPixel(chart,pixel,axisWidth);};let borderValue,i,lineValue,alignedLineValue;let tx1,ty1,tx2,ty2,x1,y1,x2,y2;if(position==='top'){borderValue=alignBorderValue(me.bottom);ty1=me.bottom-tl;ty2=borderValue-axisHalfWidth;y1=alignBorderValue(chartArea.top)+axisHalfWidth;y2=chartArea.bottom;}else if(position==='bottom'){borderValue=alignBorderValue(me.top);y1=chartArea.top;y2=alignBorderValue(chartArea.bottom)-axisHalfWidth;ty1=borderValue+axisHalfWidth;ty2=me.top+tl;}else if(position==='left'){borderValue=alignBorderValue(me.right);tx1=me.right-tl;tx2=borderValue-axisHalfWidth;x1=alignBorderValue(chartArea.left)+axisHalfWidth;x2=chartArea.right;}else if(position==='right'){borderValue=alignBorderValue(me.left);x1=chartArea.left;x2=alignBorderValue(chartArea.right)-axisHalfWidth;tx1=borderValue+axisHalfWidth;tx2=me.left+tl;}else if(axis==='x'){if(position==='center'){borderValue=alignBorderValue((chartArea.top+chartArea.bottom)/2+0.5);}else if(isObject(position)){const positionAxisID=Object.keys(position)[0];const value=position[positionAxisID];borderValue=alignBorderValue(me.chart.scales[positionAxisID].getPixelForValue(value));} -y1=chartArea.top;y2=chartArea.bottom;ty1=borderValue+axisHalfWidth;ty2=ty1+tl;}else if(axis==='y'){if(position==='center'){borderValue=alignBorderValue((chartArea.left+chartArea.right)/2);}else if(isObject(position)){const positionAxisID=Object.keys(position)[0];const value=position[positionAxisID];borderValue=alignBorderValue(me.chart.scales[positionAxisID].getPixelForValue(value));} +_computeGridLineItems(chartArea){const axis=this.axis;const chart=this.chart;const options=this.options;const{grid,position}=options;const offset=grid.offset;const isHorizontal=this.isHorizontal();const ticks=this.ticks;const ticksLength=ticks.length+(offset?1:0);const tl=getTickMarkLength(grid);const items=[];const borderOpts=grid.setContext(this.getContext());const axisWidth=borderOpts.drawBorder?borderOpts.borderWidth:0;const axisHalfWidth=axisWidth/2;const alignBorderValue=function(pixel){return _alignPixel(chart,pixel,axisWidth);};let borderValue,i,lineValue,alignedLineValue;let tx1,ty1,tx2,ty2,x1,y1,x2,y2;if(position==='top'){borderValue=alignBorderValue(this.bottom);ty1=this.bottom-tl;ty2=borderValue-axisHalfWidth;y1=alignBorderValue(chartArea.top)+axisHalfWidth;y2=chartArea.bottom;}else if(position==='bottom'){borderValue=alignBorderValue(this.top);y1=chartArea.top;y2=alignBorderValue(chartArea.bottom)-axisHalfWidth;ty1=borderValue+axisHalfWidth;ty2=this.top+tl;}else if(position==='left'){borderValue=alignBorderValue(this.right);tx1=this.right-tl;tx2=borderValue-axisHalfWidth;x1=alignBorderValue(chartArea.left)+axisHalfWidth;x2=chartArea.right;}else if(position==='right'){borderValue=alignBorderValue(this.left);x1=chartArea.left;x2=alignBorderValue(chartArea.right)-axisHalfWidth;tx1=borderValue+axisHalfWidth;tx2=this.left+tl;}else if(axis==='x'){if(position==='center'){borderValue=alignBorderValue((chartArea.top+chartArea.bottom)/2+0.5);}else if(isObject(position)){const positionAxisID=Object.keys(position)[0];const value=position[positionAxisID];borderValue=alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));} +y1=chartArea.top;y2=chartArea.bottom;ty1=borderValue+axisHalfWidth;ty2=ty1+tl;}else if(axis==='y'){if(position==='center'){borderValue=alignBorderValue((chartArea.left+chartArea.right)/2);}else if(isObject(position)){const positionAxisID=Object.keys(position)[0];const value=position[positionAxisID];borderValue=alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));} tx1=borderValue-axisHalfWidth;tx2=tx1-tl;x1=chartArea.left;x2=chartArea.right;} -const limit=valueOrDefault(options.ticks.maxTicksLimit,ticksLength);const step=Math.max(1,Math.ceil(ticksLength/limit));for(i=0;it.value===value);if(index>=0){const opts=grid.setContext(me.getContext(index));return opts.lineWidth;} +getLineWidthForValue(value){const grid=this.options.grid;if(!this._isVisible()||!grid.display){return 0;} +const ticks=this.ticks;const index=ticks.findIndex(t=>t.value===value);if(index>=0){const opts=grid.setContext(this.getContext(index));return opts.lineWidth;} return 0;} -drawGrid(chartArea){const me=this;const grid=me.options.grid;const ctx=me.ctx;const items=me._gridLineItems||(me._gridLineItems=me._computeGridLineItems(chartArea));let i,ilen;const drawLine=(p1,p2,style)=>{if(!style.width||!style.color){return;} +drawGrid(chartArea){const grid=this.options.grid;const ctx=this.ctx;const items=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(chartArea));let i,ilen;const drawLine=(p1,p2,style)=>{if(!style.width||!style.color){return;} ctx.save();ctx.lineWidth=style.width;ctx.strokeStyle=style.color;ctx.setLineDash(style.borderDash||[]);ctx.lineDashOffset=style.borderDashOffset;ctx.beginPath();ctx.moveTo(p1.x,p1.y);ctx.lineTo(p2.x,p2.y);ctx.stroke();ctx.restore();};if(grid.display){for(i=0,ilen=items.length;i{this.draw(chartArea);}}];} +return[{z:gz,draw:(chartArea)=>{this.drawBackground();this.drawGrid(chartArea);this.drawTitle();}},{z:gz+1,draw:()=>{this.drawBorder();}},{z:tz,draw:(chartArea)=>{this.drawLabels(chartArea);}}];} +getMatchingVisibleMetas(type){const metas=this.chart.getSortedVisibleDatasetMetas();const axisID=this.axis+'AxisID';const result=[];let i,ilen;for(i=0,ilen=metas.length;i{const reg=typedRegistry||me._getRegistryForType(arg);if(typedRegistry||reg.isForType(arg)||(reg===me.plugins&&arg.id)){me._exec(method,reg,arg);}else{each(arg,item=>{const itemReg=typedRegistry||me._getRegistryForType(item);me._exec(method,itemReg,item);});}});} +_each(method,args,typedRegistry){[...args].forEach(arg=>{const reg=typedRegistry||this._getRegistryForType(arg);if(typedRegistry||reg.isForType(arg)||(reg===this.plugins&&arg.id)){this._exec(method,reg,arg);}else{each(arg,item=>{const itemReg=typedRegistry||this._getRegistryForType(item);this._exec(method,itemReg,item);});}});} _exec(method,registry,component){const camelMethod=_capitalize(method);callback(component['before'+camelMethod],[],component);registry[method](component);callback(component['after'+camelMethod],[],component);} _getRegistryForType(type){for(let i=0;i{const scaleConf=configScales[id];const axis=determineAxis(id,scaleConf);const defaultId=getDefaultScaleIDFromAxis(axis,chartIndexAxis);const defaultScaleOptions=chartDefaults.scales||{};firstIDs[axis]=firstIDs[axis]||id;scales[id]=mergeIf(Object.create(null),[{axis},scaleConf,defaultScaleOptions[axis],defaultScaleOptions[defaultId]]);});config.data.datasets.forEach(dataset=>{const type=dataset.type||config.type;const indexAxis=dataset.indexAxis||getIndexAxis(type,options);const datasetDefaults=overrides[type]||{};const defaultScaleOptions=datasetDefaults.scales||{};Object.keys(defaultScaleOptions).forEach(defaultID=>{const axis=getAxisFromDefaultScaleID(defaultID,indexAxis);const id=dataset[axis+'AxisID']||firstIDs[axis]||axis;scales[id]=scales[id]||Object.create(null);mergeIf(scales[id],[{axis},configScales[id],defaultScaleOptions[defaultID]]);});});Object.keys(scales).forEach(key=>{const scale=scales[key];mergeIf(scale,[defaults.scales[scale.type],defaults.scale]);});return scales;} +function mergeScaleConfig(config,options){const chartDefaults=overrides[config.type]||{scales:{}};const configScales=options.scales||{};const chartIndexAxis=getIndexAxis(config.type,options);const firstIDs=Object.create(null);const scales=Object.create(null);Object.keys(configScales).forEach(id=>{const scaleConf=configScales[id];if(!isObject(scaleConf)){return console.error(`Invalid scale configuration for scale: ${id}`);} +if(scaleConf._proxy){return console.warn(`Ignoring resolver passed as options for scale: ${id}`);} +const axis=determineAxis(id,scaleConf);const defaultId=getDefaultScaleIDFromAxis(axis,chartIndexAxis);const defaultScaleOptions=chartDefaults.scales||{};firstIDs[axis]=firstIDs[axis]||id;scales[id]=mergeIf(Object.create(null),[{axis},scaleConf,defaultScaleOptions[axis],defaultScaleOptions[defaultId]]);});config.data.datasets.forEach(dataset=>{const type=dataset.type||config.type;const indexAxis=dataset.indexAxis||getIndexAxis(type,options);const datasetDefaults=overrides[type]||{};const defaultScaleOptions=datasetDefaults.scales||{};Object.keys(defaultScaleOptions).forEach(defaultID=>{const axis=getAxisFromDefaultScaleID(defaultID,indexAxis);const id=dataset[axis+'AxisID']||firstIDs[axis]||axis;scales[id]=scales[id]||Object.create(null);mergeIf(scales[id],[{axis},configScales[id],defaultScaleOptions[defaultID]]);});});Object.keys(scales).forEach(key=>{const scale=scales[key];mergeIf(scale,[defaults.scales[scale.type],defaults.scale]);});return scales;} function initOptions(config){const options=config.options||(config.options={});options.plugins=valueOrDefault(options.plugins,{});options.scales=mergeScaleConfig(config,options);} function initData(data){data=data||{};data.datasets=data.datasets||[];data.labels=data.labels||[];return data;} function initConfig(config){config=config||{};config.data=initData(config.data);initOptions(config);return config;} @@ -1465,18 +1478,19 @@ createResolver(scopes,context,prefixes=[''],descriptorDefaults){const{resolver}= function getResolver(resolverCache,scopes,prefixes){let cache=resolverCache.get(scopes);if(!cache){cache=new Map();resolverCache.set(scopes,cache);} const cacheKey=prefixes.join();let cached=cache.get(cacheKey);if(!cached){const resolver=_createResolver(scopes,prefixes);cached={resolver,subPrefixes:prefixes.filter(p=>!p.toLowerCase().includes('hover'))};cache.set(cacheKey,cached);} return cached;} -function needContext(proxy,names){const{isScriptable,isIndexable}=_descriptors(proxy);for(const prop of names){if((isScriptable(prop)&&isFunction(proxy[prop]))||(isIndexable(prop)&&isArray(proxy[prop]))){return true;}} +const hasFunction=value=>isObject(value)&&Object.getOwnPropertyNames(value).reduce((acc,key)=>acc||isFunction(value[key]),false);function needContext(proxy,names){const{isScriptable,isIndexable}=_descriptors(proxy);for(const prop of names){const scriptable=isScriptable(prop);const indexable=isIndexable(prop);const value=(indexable||scriptable)&&proxy[prop];if((scriptable&&(isFunction(value)||hasFunction(value)))||(indexable&&isArray(value))){return true;}} return false;} -var version="3.5.1";const KNOWN_POSITIONS=['top','bottom','left','right','chartArea'];function positionIsHorizontal(position,axis){return position==='top'||position==='bottom'||(KNOWN_POSITIONS.indexOf(position)===-1&&axis==='x');} +var version="3.6.2";const KNOWN_POSITIONS=['top','bottom','left','right','chartArea'];function positionIsHorizontal(position,axis){return position==='top'||position==='bottom'||(KNOWN_POSITIONS.indexOf(position)===-1&&axis==='x');} function compare2Level(l1,l2){return function(a,b){return a[l1]===b[l1]?a[l2]-b[l2]:a[l1]-b[l1];};} function onAnimationsComplete(context){const chart=context.chart;const animationOptions=chart.options.animation;chart.notifyPlugins('afterRender');callback(animationOptions&&animationOptions.onComplete,[context],chart);} function onAnimationProgress(context){const chart=context.chart;const animationOptions=chart.options.animation;callback(animationOptions&&animationOptions.onProgress,[context],chart);} function getCanvas(item){if(_isDomSupported()&&typeof item==='string'){item=document.getElementById(item);}else if(item&&item.length){item=item[0];} if(item&&item.canvas){item=item.canvas;} return item;} -const instances={};const getChart=(key)=>{const canvas=getCanvas(key);return Object.values(instances).filter((c)=>c.canvas===canvas).pop();};class Chart{constructor(item,userConfig){const me=this;const config=this.config=new Config(userConfig);const initialCanvas=getCanvas(item);const existingChart=getChart(initialCanvas);if(existingChart){throw new Error('Canvas is already in use. Chart with ID \''+existingChart.id+'\''+' must be destroyed before the canvas can be reused.');} -const options=config.createResolver(config.chartOptionScopes(),me.getContext());this.platform=new(config.platform||_detectPlatform(initialCanvas))();const context=me.platform.acquireContext(initialCanvas,options.aspectRatio);const canvas=context&&context.canvas;const height=canvas&&canvas.height;const width=canvas&&canvas.width;this.id=uid();this.ctx=context;this.canvas=canvas;this.width=width;this.height=height;this._options=options;this._aspectRatio=this.aspectRatio;this._layers=[];this._metasets=[];this._stacks=undefined;this.boxes=[];this.currentDevicePixelRatio=undefined;this.chartArea=undefined;this._active=[];this._lastEvent=undefined;this._listeners={};this._responsiveListeners=undefined;this._sortedMetasets=[];this.scales={};this._plugins=new PluginService();this.$proxies={};this._hiddenIndices={};this.attached=false;this._animationsDisabled=undefined;this.$context=undefined;this._doResize=debounce(()=>this.update('resize'),options.resizeDelay||0);instances[me.id]=me;if(!context||!canvas){console.error("Failed to create chart: can't acquire context from the given item");return;} -animator.listen(me,'complete',onAnimationsComplete);animator.listen(me,'progress',onAnimationProgress);me._initialize();if(me.attached){me.update();}} +const instances={};const getChart=(key)=>{const canvas=getCanvas(key);return Object.values(instances).filter((c)=>c.canvas===canvas).pop();};function moveNumericKeys(obj,start,move){const keys=Object.keys(obj);for(const key of keys){const intKey=+key;if(intKey>=start){const value=obj[key];delete obj[key];if(move>0||intKey>start){obj[intKey+move]=value;}}}} +class Chart{constructor(item,userConfig){const config=this.config=new Config(userConfig);const initialCanvas=getCanvas(item);const existingChart=getChart(initialCanvas);if(existingChart){throw new Error('Canvas is already in use. Chart with ID \''+existingChart.id+'\''+' must be destroyed before the canvas can be reused.');} +const options=config.createResolver(config.chartOptionScopes(),this.getContext());this.platform=new(config.platform||_detectPlatform(initialCanvas))();this.platform.updateConfig(config);const context=this.platform.acquireContext(initialCanvas,options.aspectRatio);const canvas=context&&context.canvas;const height=canvas&&canvas.height;const width=canvas&&canvas.width;this.id=uid();this.ctx=context;this.canvas=canvas;this.width=width;this.height=height;this._options=options;this._aspectRatio=this.aspectRatio;this._layers=[];this._metasets=[];this._stacks=undefined;this.boxes=[];this.currentDevicePixelRatio=undefined;this.chartArea=undefined;this._active=[];this._lastEvent=undefined;this._listeners={};this._responsiveListeners=undefined;this._sortedMetasets=[];this.scales={};this._plugins=new PluginService();this.$proxies={};this._hiddenIndices={};this.attached=false;this._animationsDisabled=undefined;this.$context=undefined;this._doResize=debounce(mode=>this.update(mode),options.resizeDelay||0);this._dataChanges=[];instances[this.id]=this;if(!context||!canvas){console.error("Failed to create chart: can't acquire context from the given item");return;} +animator.listen(this,'complete',onAnimationsComplete);animator.listen(this,'progress',onAnimationProgress);this._initialize();if(this.attached){this.update();}} get aspectRatio(){const{options:{aspectRatio,maintainAspectRatio},width,height,_aspectRatio}=this;if(!isNullOrUndef(aspectRatio)){return aspectRatio;} if(maintainAspectRatio&&_aspectRatio){return _aspectRatio;} return height?width/height:null;} @@ -1484,100 +1498,107 @@ get data(){return this.config.data;} set data(data){this.config.data=data;} get options(){return this._options;} set options(options){this.config.options=options;} -_initialize(){const me=this;me.notifyPlugins('beforeInit');if(me.options.responsive){me.resize();}else{retinaScale(me,me.options.devicePixelRatio);} -me.bindEvents();me.notifyPlugins('afterInit');return me;} +_initialize(){this.notifyPlugins('beforeInit');if(this.options.responsive){this.resize();}else{retinaScale(this,this.options.devicePixelRatio);} +this.bindEvents();this.notifyPlugins('afterInit');return this;} clear(){clearCanvas(this.canvas,this.ctx);return this;} stop(){animator.stop(this);return this;} resize(width,height){if(!animator.running(this)){this._resize(width,height);}else{this._resizeBeforeDraw={width,height};}} -_resize(width,height){const me=this;const options=me.options;const canvas=me.canvas;const aspectRatio=options.maintainAspectRatio&&me.aspectRatio;const newSize=me.platform.getMaximumSize(canvas,width,height,aspectRatio);const newRatio=options.devicePixelRatio||me.platform.getDevicePixelRatio();me.width=newSize.width;me.height=newSize.height;me._aspectRatio=me.aspectRatio;if(!retinaScale(me,newRatio,true)){return;} -me.notifyPlugins('resize',{size:newSize});callback(options.onResize,[me,newSize],me);if(me.attached){if(me._doResize()){me.render();}}} +_resize(width,height){const options=this.options;const canvas=this.canvas;const aspectRatio=options.maintainAspectRatio&&this.aspectRatio;const newSize=this.platform.getMaximumSize(canvas,width,height,aspectRatio);const newRatio=options.devicePixelRatio||this.platform.getDevicePixelRatio();const mode=this.width?'resize':'attach';this.width=newSize.width;this.height=newSize.height;this._aspectRatio=this.aspectRatio;if(!retinaScale(this,newRatio,true)){return;} +this.notifyPlugins('resize',{size:newSize});callback(options.onResize,[this,newSize],this);if(this.attached){if(this._doResize(mode)){this.render();}}} ensureScalesHaveIDs(){const options=this.options;const scalesOptions=options.scales||{};each(scalesOptions,(axisOptions,axisID)=>{axisOptions.id=axisID;});} -buildOrUpdateScales(){const me=this;const options=me.options;const scaleOpts=options.scales;const scales=me.scales;const updated=Object.keys(scales).reduce((obj,id)=>{obj[id]=false;return obj;},{});let items=[];if(scaleOpts){items=items.concat(Object.keys(scaleOpts).map((id)=>{const scaleOptions=scaleOpts[id];const axis=determineAxis(id,scaleOptions);const isRadial=axis==='r';const isHorizontal=axis==='x';return{options:scaleOptions,dposition:isRadial?'chartArea':isHorizontal?'bottom':'left',dtype:isRadial?'radialLinear':isHorizontal?'category':'linear'};}));} +buildOrUpdateScales(){const options=this.options;const scaleOpts=options.scales;const scales=this.scales;const updated=Object.keys(scales).reduce((obj,id)=>{obj[id]=false;return obj;},{});let items=[];if(scaleOpts){items=items.concat(Object.keys(scaleOpts).map((id)=>{const scaleOptions=scaleOpts[id];const axis=determineAxis(id,scaleOptions);const isRadial=axis==='r';const isHorizontal=axis==='x';return{options:scaleOptions,dposition:isRadial?'chartArea':isHorizontal?'bottom':'left',dtype:isRadial?'radialLinear':isHorizontal?'category':'linear'};}));} each(items,(item)=>{const scaleOptions=item.options;const id=scaleOptions.id;const axis=determineAxis(id,scaleOptions);const scaleType=valueOrDefault(scaleOptions.type,item.dtype);if(scaleOptions.position===undefined||positionIsHorizontal(scaleOptions.position,axis)!==positionIsHorizontal(item.dposition)){scaleOptions.position=item.dposition;} -updated[id]=true;let scale=null;if(id in scales&&scales[id].type===scaleType){scale=scales[id];}else{const scaleClass=registry.getScale(scaleType);scale=new scaleClass({id,type:scaleType,ctx:me.ctx,chart:me});scales[scale.id]=scale;} -scale.init(scaleOptions,options);});each(updated,(hasUpdated,id)=>{if(!hasUpdated){delete scales[id];}});each(scales,(scale)=>{layouts.configure(me,scale,scale.options);layouts.addBox(me,scale);});} -_updateMetasets(){const me=this;const metasets=me._metasets;const numData=me.data.datasets.length;const numMeta=metasets.length;metasets.sort((a,b)=>a.index-b.index);if(numMeta>numData){for(let i=numData;i{if(!hasUpdated){delete scales[id];}});each(scales,(scale)=>{layouts.configure(this,scale,scale.options);layouts.addBox(this,scale);});} +_updateMetasets(){const metasets=this._metasets;const numData=this.data.datasets.length;const numMeta=metasets.length;metasets.sort((a,b)=>a.index-b.index);if(numMeta>numData){for(let i=numData;idatasets.length){delete me._stacks;} -metasets.forEach((meta,index)=>{if(datasets.filter(x=>x===meta._dataset).length===0){me._destroyDatasetMeta(index);}});} -buildOrUpdateControllers(){const me=this;const newControllers=[];const datasets=me.data.datasets;let i,ilen;me._removeUnreferencedMetasets();for(i=0,ilen=datasets.length;i{me.getDatasetMeta(datasetIndex).controller.reset();},me);} +this._sortedMetasets=metasets.slice(0).sort(compare2Level('order','index'));} +_removeUnreferencedMetasets(){const{_metasets:metasets,data:{datasets}}=this;if(metasets.length>datasets.length){delete this._stacks;} +metasets.forEach((meta,index)=>{if(datasets.filter(x=>x===meta._dataset).length===0){this._destroyDatasetMeta(index);}});} +buildOrUpdateControllers(){const newControllers=[];const datasets=this.data.datasets;let i,ilen;this._removeUnreferencedMetasets();for(i=0,ilen=datasets.length;i{this.getDatasetMeta(datasetIndex).controller.reset();},this);} reset(){this._resetElements();this.notifyPlugins('reset');} -update(mode){const me=this;const config=me.config;config.update();me._options=config.createResolver(config.chartOptionScopes(),me.getContext());each(me.scales,(scale)=>{layouts.removeBox(me,scale);});const animsDisabled=me._animationsDisabled=!me.options.animation;me.ensureScalesHaveIDs();me.buildOrUpdateScales();const existingEvents=new Set(Object.keys(me._listeners));const newEvents=new Set(me.options.events);if(!setsEqual(existingEvents,newEvents)||!!this._responsiveListeners!==me.options.responsive){me.unbindEvents();me.bindEvents();} -me._plugins.invalidate();if(me.notifyPlugins('beforeUpdate',{mode,cancelable:true})===false){return;} -const newControllers=me.buildOrUpdateControllers();me.notifyPlugins('beforeElementsUpdate');let minPadding=0;for(let i=0,ilen=me.data.datasets.length;i{controller.reset();});} -me._updateDatasets(mode);me.notifyPlugins('afterUpdate',{mode});me._layers.sort(compare2Level('z','_idx'));if(me._lastEvent){me._eventHandler(me._lastEvent,true);} -me.render();} -_updateLayout(minPadding){const me=this;if(me.notifyPlugins('beforeLayout',{cancelable:true})===false){return;} -layouts.update(me,me.width,me.height,minPadding);const area=me.chartArea;const noArea=area.width<=0||area.height<=0;me._layers=[];each(me.boxes,(box)=>{if(noArea&&box.position==='chartArea'){return;} +update(mode){const config=this.config;config.update();const options=this._options=config.createResolver(config.chartOptionScopes(),this.getContext());const animsDisabled=this._animationsDisabled=!options.animation;this._updateScales();this._checkEventBindings();this._updateHiddenIndices();this._plugins.invalidate();if(this.notifyPlugins('beforeUpdate',{mode,cancelable:true})===false){return;} +const newControllers=this.buildOrUpdateControllers();this.notifyPlugins('beforeElementsUpdate');let minPadding=0;for(let i=0,ilen=this.data.datasets.length;i{controller.reset();});} +this._updateDatasets(mode);this.notifyPlugins('afterUpdate',{mode});this._layers.sort(compare2Level('z','_idx'));if(this._lastEvent){this._eventHandler(this._lastEvent,true);} +this.render();} +_updateScales(){each(this.scales,(scale)=>{layouts.removeBox(this,scale);});this.ensureScalesHaveIDs();this.buildOrUpdateScales();} +_checkEventBindings(){const options=this.options;const existingEvents=new Set(Object.keys(this._listeners));const newEvents=new Set(options.events);if(!setsEqual(existingEvents,newEvents)||!!this._responsiveListeners!==options.responsive){this.unbindEvents();this.bindEvents();}} +_updateHiddenIndices(){const{_hiddenIndices}=this;const changes=this._getUniformDataChanges()||[];for(const{method,start,count}of changes){const move=method==='_removeElements'?-count:count;moveNumericKeys(_hiddenIndices,start,move);}} +_getUniformDataChanges(){const _dataChanges=this._dataChanges;if(!_dataChanges||!_dataChanges.length){return;} +this._dataChanges=[];const datasetCount=this.data.datasets.length;const makeSet=(idx)=>new Set(_dataChanges.filter(c=>c[0]===idx).map((c,i)=>i+','+c.splice(1).join(',')));const changeSet=makeSet(0);for(let i=1;ic.split(',')).map(a=>({method:a[1],start:+a[2],count:+a[3]}));} +_updateLayout(minPadding){if(this.notifyPlugins('beforeLayout',{cancelable:true})===false){return;} +layouts.update(this,this.width,this.height,minPadding);const area=this.chartArea;const noArea=area.width<=0||area.height<=0;this._layers=[];each(this.boxes,(box)=>{if(noArea&&box.position==='chartArea'){return;} if(box.configure){box.configure();} -me._layers.push(...box._layers());},me);me._layers.forEach((item,index)=>{item._idx=index;});me.notifyPlugins('afterLayout');} -_updateDatasets(mode){const me=this;const isFunction=typeof mode==='function';if(me.notifyPlugins('beforeDatasetsUpdate',{mode,cancelable:true})===false){return;} -for(let i=0,ilen=me.data.datasets.length;i{item._idx=index;});this.notifyPlugins('afterLayout');} +_updateDatasets(mode){if(this.notifyPlugins('beforeDatasetsUpdate',{mode,cancelable:true})===false){return;} +for(let i=0,ilen=this.data.datasets.length;i=0;--i){me._drawDataset(metasets[i]);} -me.notifyPlugins('afterDatasetsDraw');} -_drawDataset(meta){const me=this;const ctx=me.ctx;const clip=meta._clip;const useClip=!clip.disabled;const area=me.chartArea;const args={meta,index:meta.index,cancelable:true};if(me.notifyPlugins('beforeDatasetDraw',args)===false){return;} -if(useClip){clipArea(ctx,{left:clip.left===false?0:area.left-clip.left,right:clip.right===false?me.width:area.right+clip.right,top:clip.top===false?0:area.top-clip.top,bottom:clip.bottom===false?me.height:area.bottom+clip.bottom});} +_drawDatasets(){if(this.notifyPlugins('beforeDatasetsDraw',{cancelable:true})===false){return;} +const metasets=this.getSortedVisibleDatasetMetas();for(let i=metasets.length-1;i>=0;--i){this._drawDataset(metasets[i]);} +this.notifyPlugins('afterDatasetsDraw');} +_drawDataset(meta){const ctx=this.ctx;const clip=meta._clip;const useClip=!clip.disabled;const area=this.chartArea;const args={meta,index:meta.index,cancelable:true};if(this.notifyPlugins('beforeDatasetDraw',args)===false){return;} +if(useClip){clipArea(ctx,{left:clip.left===false?0:area.left-clip.left,right:clip.right===false?this.width:area.right+clip.right,top:clip.top===false?0:area.top-clip.top,bottom:clip.bottom===false?this.height:area.bottom+clip.bottom});} meta.controller.draw();if(useClip){unclipArea(ctx);} -args.cancelable=false;me.notifyPlugins('afterDatasetDraw',args);} +args.cancelable=false;this.notifyPlugins('afterDatasetDraw',args);} getElementsAtEventForMode(e,mode,options,useFinalPosition){const method=Interaction.modes[mode];if(typeof method==='function'){return method(this,e,options,useFinalPosition);} return[];} -getDatasetMeta(datasetIndex){const me=this;const dataset=me.data.datasets[datasetIndex];const metasets=me._metasets;let meta=metasets.filter(x=>x&&x._dataset===dataset).pop();if(!meta){meta={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:dataset&&dataset.order||0,index:datasetIndex,_dataset:dataset,_parsed:[],_sorted:false};metasets.push(meta);} +getDatasetMeta(datasetIndex){const dataset=this.data.datasets[datasetIndex];const metasets=this._metasets;let meta=metasets.filter(x=>x&&x._dataset===dataset).pop();if(!meta){meta={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:dataset&&dataset.order||0,index:datasetIndex,_dataset:dataset,_parsed:[],_sorted:false};metasets.push(meta);} return meta;} -getContext(){return this.$context||(this.$context={chart:this,type:'chart'});} +getContext(){return this.$context||(this.$context=createContext(null,{chart:this,type:'chart'}));} getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length;} isDatasetVisible(datasetIndex){const dataset=this.data.datasets[datasetIndex];if(!dataset){return false;} const meta=this.getDatasetMeta(datasetIndex);return typeof meta.hidden==='boolean'?!meta.hidden:!dataset.hidden;} setDatasetVisibility(datasetIndex,visible){const meta=this.getDatasetMeta(datasetIndex);meta.hidden=!visible;} toggleDataVisibility(index){this._hiddenIndices[index]=!this._hiddenIndices[index];} getDataVisibility(index){return!this._hiddenIndices[index];} -_updateVisibility(datasetIndex,dataIndex,visible){const me=this;const mode=visible?'show':'hide';const meta=me.getDatasetMeta(datasetIndex);const anims=meta.controller._resolveAnimations(undefined,mode);if(defined(dataIndex)){meta.data[dataIndex].hidden=!visible;me.update();}else{me.setDatasetVisibility(datasetIndex,visible);anims.update(meta,{visible});me.update((ctx)=>ctx.datasetIndex===datasetIndex?mode:undefined);}} +_updateVisibility(datasetIndex,dataIndex,visible){const mode=visible?'show':'hide';const meta=this.getDatasetMeta(datasetIndex);const anims=meta.controller._resolveAnimations(undefined,mode);if(defined(dataIndex)){meta.data[dataIndex].hidden=!visible;this.update();}else{this.setDatasetVisibility(datasetIndex,visible);anims.update(meta,{visible});this.update((ctx)=>ctx.datasetIndex===datasetIndex?mode:undefined);}} hide(datasetIndex,dataIndex){this._updateVisibility(datasetIndex,dataIndex,false);} show(datasetIndex,dataIndex){this._updateVisibility(datasetIndex,dataIndex,true);} -_destroyDatasetMeta(datasetIndex){const me=this;const meta=me._metasets&&me._metasets[datasetIndex];if(meta&&meta.controller){meta.controller._destroy();delete me._metasets[datasetIndex];}} -destroy(){const me=this;const{canvas,ctx}=me;let i,ilen;me.stop();animator.remove(me);for(i=0,ilen=me.data.datasets.length;i{platform.addEventListener(me,type,listener);listeners[type]=listener;};const listener=function(e,x,y){e.offsetX=x;e.offsetY=y;me._eventHandler(e);};each(me.options.events,(type)=>_add(type,listener));} -bindResponsiveEvents(){const me=this;if(!me._responsiveListeners){me._responsiveListeners={};} -const listeners=me._responsiveListeners;const platform=me.platform;const _add=(type,listener)=>{platform.addEventListener(me,type,listener);listeners[type]=listener;};const _remove=(type,listener)=>{if(listeners[type]){platform.removeEventListener(me,type,listener);delete listeners[type];}};const listener=(width,height)=>{if(me.canvas){me.resize(width,height);}};let detached;const attached=()=>{_remove('attach',attached);me.attached=true;me.resize();_add('resize',listener);_add('detach',detached);};detached=()=>{me.attached=false;_remove('resize',listener);_add('attach',attached);};if(platform.isAttached(me.canvas)){attached();}else{detached();}} -unbindEvents(){const me=this;each(me._listeners,(listener,type)=>{me.platform.removeEventListener(me,type,listener);});me._listeners={};each(me._responsiveListeners,(listener,type)=>{me.platform.removeEventListener(me,type,listener);});me._responsiveListeners=undefined;} +bindUserEvents(){const listeners=this._listeners;const platform=this.platform;const _add=(type,listener)=>{platform.addEventListener(this,type,listener);listeners[type]=listener;};const listener=(e,x,y)=>{e.offsetX=x;e.offsetY=y;this._eventHandler(e);};each(this.options.events,(type)=>_add(type,listener));} +bindResponsiveEvents(){if(!this._responsiveListeners){this._responsiveListeners={};} +const listeners=this._responsiveListeners;const platform=this.platform;const _add=(type,listener)=>{platform.addEventListener(this,type,listener);listeners[type]=listener;};const _remove=(type,listener)=>{if(listeners[type]){platform.removeEventListener(this,type,listener);delete listeners[type];}};const listener=(width,height)=>{if(this.canvas){this.resize(width,height);}};let detached;const attached=()=>{_remove('attach',attached);this.attached=true;this.resize();_add('resize',listener);_add('detach',detached);};detached=()=>{this.attached=false;_remove('resize',listener);this._stop();this._resize(0,0);_add('attach',attached);};if(platform.isAttached(this.canvas)){attached();}else{detached();}} +unbindEvents(){each(this._listeners,(listener,type)=>{this.platform.removeEventListener(this,type,listener);});this._listeners={};each(this._responsiveListeners,(listener,type)=>{this.platform.removeEventListener(this,type,listener);});this._responsiveListeners=undefined;} updateHoverStyle(items,mode,enabled){const prefix=enabled?'set':'remove';let meta,item,i,ilen;if(mode==='dataset'){meta=this.getDatasetMeta(items[0].datasetIndex);meta.controller['_'+prefix+'DatasetHoverStyle']();} for(i=0,ilen=items.length;i{const meta=me.getDatasetMeta(datasetIndex);if(!meta){throw new Error('No dataset found at index '+datasetIndex);} -return{datasetIndex,element:meta.data[index],index,};});const changed=!_elementsEqual(active,lastActive);if(changed){me._active=active;me._updateHoverStyles(active,lastActive);}} +setActiveElements(activeElements){const lastActive=this._active||[];const active=activeElements.map(({datasetIndex,index})=>{const meta=this.getDatasetMeta(datasetIndex);if(!meta){throw new Error('No dataset found at index '+datasetIndex);} +return{datasetIndex,element:meta.data[index],index,};});const changed=!_elementsEqual(active,lastActive);if(changed){this._active=active;this._updateHoverStyles(active,lastActive);}} notifyPlugins(hook,args,filter){return this._plugins.notify(this,hook,args,filter);} -_updateHoverStyles(active,lastActive,replay){const me=this;const hoverOptions=me.options.hover;const diff=(a,b)=>a.filter(x=>!b.some(y=>x.datasetIndex===y.datasetIndex&&x.index===y.index));const deactivated=diff(lastActive,active);const activated=replay?active:diff(active,lastActive);if(deactivated.length){me.updateHoverStyle(deactivated,hoverOptions.mode,false);} -if(activated.length&&hoverOptions.mode){me.updateHoverStyle(activated,hoverOptions.mode,true);}} -_eventHandler(e,replay){const me=this;const args={event:e,replay,cancelable:true};const eventFilter=(plugin)=>(plugin.options.events||this.options.events).includes(e.type);if(me.notifyPlugins('beforeEvent',args,eventFilter)===false){return;} -const changed=me._handleEvent(e,replay);args.cancelable=false;me.notifyPlugins('afterEvent',args,eventFilter);if(changed||args.changed){me.render();} -return me;} -_handleEvent(e,replay){const me=this;const{_active:lastActive=[],options}=me;const hoverOptions=options.hover;const useFinalPosition=replay;let active=[];let changed=false;let lastEvent=null;if(e.type!=='mouseout'){active=me.getElementsAtEventForMode(e,hoverOptions.mode,hoverOptions,useFinalPosition);lastEvent=e.type==='click'?me._lastEvent:e;} -me._lastEvent=null;if(_isPointInArea(e,me.chartArea,me._minPadding)){callback(options.onHover,[e,active,me],me);if(e.type==='mouseup'||e.type==='click'||e.type==='contextmenu'){callback(options.onClick,[e,active,me],me);}} -changed=!_elementsEqual(active,lastActive);if(changed||replay){me._active=active;me._updateHoverStyles(active,lastActive,replay);} -me._lastEvent=lastEvent;return changed;}} +_updateHoverStyles(active,lastActive,replay){const hoverOptions=this.options.hover;const diff=(a,b)=>a.filter(x=>!b.some(y=>x.datasetIndex===y.datasetIndex&&x.index===y.index));const deactivated=diff(lastActive,active);const activated=replay?active:diff(active,lastActive);if(deactivated.length){this.updateHoverStyle(deactivated,hoverOptions.mode,false);} +if(activated.length&&hoverOptions.mode){this.updateHoverStyle(activated,hoverOptions.mode,true);}} +_eventHandler(e,replay){const args={event:e,replay,cancelable:true};const eventFilter=(plugin)=>(plugin.options.events||this.options.events).includes(e.native.type);if(this.notifyPlugins('beforeEvent',args,eventFilter)===false){return;} +const changed=this._handleEvent(e,replay);args.cancelable=false;this.notifyPlugins('afterEvent',args,eventFilter);if(changed||args.changed){this.render();} +return this;} +_handleEvent(e,replay){const{_active:lastActive=[],options}=this;const hoverOptions=options.hover;const useFinalPosition=replay;let active=[];let changed=false;let lastEvent=null;if(e.type!=='mouseout'){active=this.getElementsAtEventForMode(e,hoverOptions.mode,hoverOptions,useFinalPosition);lastEvent=e.type==='click'?this._lastEvent:e;} +this._lastEvent=null;if(_isPointInArea(e,this.chartArea,this._minPadding)){callback(options.onHover,[e,active,this],this);if(e.type==='mouseup'||e.type==='click'||e.type==='contextmenu'){callback(options.onClick,[e,active,this],this);}} +changed=!_elementsEqual(active,lastActive);if(changed||replay){this._active=active;this._updateHoverStyles(active,lastActive,replay);} +this._lastEvent=lastEvent;return changed;}} const invalidatePlugins=()=>each(Chart.instances,(chart)=>chart._plugins.invalidate());const enumerable=true;Object.defineProperties(Chart,{defaults:{enumerable,value:defaults},instances:{enumerable,value:instances},overrides:{enumerable,value:overrides},registry:{enumerable,value:registry},version:{enumerable,value:version},getChart:{enumerable,value:getChart},register:{enumerable,value:(...items)=>{registry.add(...items);invalidatePlugins();}},unregister:{enumerable,value:(...items)=>{registry.remove(...items);invalidatePlugins();}}});function abstract(){throw new Error('This method is not implemented: Check that a complete date adapter is provided.');} class DateAdapter{constructor(options){this.options=options||{};} formats(){return abstract();} @@ -1587,10 +1608,10 @@ add(timestamp,amount,unit){return abstract();} diff(a,b,unit){return abstract();} startOf(timestamp,unit,weekday){return abstract();} endOf(timestamp,unit){return abstract();}} -DateAdapter.override=function(members){Object.assign(DateAdapter.prototype,members);};var _adapters={_date:DateAdapter};function getAllScaleValues(scale){if(!scale._cache.$bar){const metas=scale.getMatchingVisibleMetas('bar');let values=[];for(let i=0,ilen=metas.length;ia-b));} return scale._cache.$bar;} -function computeMinSampleSize(scale){const values=getAllScaleValues(scale);let min=scale._length;let i,ilen,curr,prev;const updateMinAndPrev=()=>{if(curr===32767||curr===-32768){return;} +function computeMinSampleSize(meta){const scale=meta.iScale;const values=getAllScaleValues(scale,meta.type);let min=scale._length;let i,ilen,curr,prev;const updateMinAndPrev=()=>{if(curr===32767||curr===-32768){return;} if(defined(prev)){min=Math.min(min,Math.abs(curr-prev)||min);} prev=curr;};for(i=0,ilen=values.length;i=0;--i){max=Math.max(max,data[i].size()/2,_parsed[i]._custom);} +parseArrayData(meta,data,start,count){const parsed=super.parseArrayData(meta,data,start,count);for(let i=0;i=0;--i){max=Math.max(max,data[i].size(this.resolveDataElementOptions(i))/2);} return max>0&&max;} -getLabelAndValue(index){const me=this;const meta=me._cachedMeta;const{xScale,yScale}=meta;const parsed=me.getParsed(index);const x=xScale.getLabelForValue(parsed.x);const y=yScale.getLabelForValue(parsed.y);const r=parsed._custom;return{label:meta.label,value:'('+x+', '+y+(r?', '+r:'')+')'};} -update(mode){const me=this;const points=me._cachedMeta.data;me.updateElements(points,0,points.length,mode);} -updateElements(points,start,count,mode){const me=this;const reset=mode==='reset';const{iScale,vScale}=me._cachedMeta;const firstOpts=me.resolveDataElementOptions(start,mode);const sharedOptions=me.getSharedOptions(firstOpts);const includeOptions=me.includeOptions(mode,sharedOptions);const iAxis=iScale.axis;const vAxis=vScale.axis;for(let i=start;i+data[i];if(isObject(data[start])){const{key='value'}=this._parsing;getter=(i)=>+resolveObjectKey(data[i],key);} +let i,ilen;for(i=start,ilen=start+count;i0&&!isNaN(value)){return TAU*(Math.abs(value)/total);} return 0;} -getLabelAndValue(index){const me=this;const meta=me._cachedMeta;const chart=me.chart;const labels=chart.data.labels||[];const value=formatNumber(meta._parsed[index],chart.options.locale);return{label:labels[index]||'',value,};} -getMaxBorderWidth(arcs){const me=this;let max=0;const chart=me.chart;let i,ilen,meta,controller,options;if(!arcs){for(i=0,ilen=chart.data.datasets.length;iname!=='spacing',_indexable:(name)=>name!=='spacing',};DoughnutController.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(chart){const data=chart.data;if(data.labels.length&&data.datasets.length){const{labels:{pointStyle}}=chart.legend.options;return data.labels.map((label,i)=>{const meta=chart.getDatasetMeta(0);const style=meta.controller.getStyle(i);return{text:label,fillStyle:style.backgroundColor,strokeStyle:style.borderColor,lineWidth:style.borderWidth,pointStyle:pointStyle,hidden:!chart.getDataVisibility(i),index:i};});} return[];}},onClick(e,legendItem,legend){legend.chart.toggleDataVisibility(legendItem.index);legend.chart.update();}},tooltip:{callbacks:{title(){return'';},label(tooltipItem){let dataLabel=tooltipItem.label;const value=': '+tooltipItem.formattedValue;if(isArray(dataLabel)){dataLabel=dataLabel.slice();dataLabel[0]+=value;}else{dataLabel+=value;} return dataLabel;}}}}};class LineController extends DatasetController{initialize(){this.enableOptionSharing=true;super.initialize();} -update(mode){const me=this;const meta=me._cachedMeta;const{dataset:line,data:points=[],_dataset}=meta;const animationsDisabled=me.chart._animationsDisabled;let{start,count}=getStartAndCountOfVisiblePoints(meta,points,animationsDisabled);me._drawStart=start;me._drawCount=count;if(scaleRangesChanged(meta)){start=0;count=points.length;} -line._datasetIndex=me.index;line._decimated=!!_dataset._decimated;line.points=points;const options=me.resolveDatasetElementOptions(mode);if(!me.options.showLine){options.borderWidth=0;} -options.segment=me.options.segment;me.updateElement(line,undefined,{animated:!animationsDisabled,options},mode);me.updateElements(points,start,count,mode);} -updateElements(points,start,count,mode){const me=this;const reset=mode==='reset';const{iScale,vScale,_stacked}=me._cachedMeta;const firstOpts=me.resolveDataElementOptions(start,mode);const sharedOptions=me.getSharedOptions(firstOpts);const includeOptions=me.includeOptions(mode,sharedOptions);const iAxis=iScale.axis;const vAxis=vScale.axis;const spanGaps=me.options.spanGaps;const maxGapLength=isNumber(spanGaps)?spanGaps:Number.POSITIVE_INFINITY;const directUpdate=me.chart._animationsDisabled||reset||mode==='none';let prevParsed=start>0&&me.getParsed(start-1);for(let i=start;i0&&(parsed[iAxis]-prevParsed[iAxis])>maxGapLength;properties.parsed=parsed;if(includeOptions){properties.options=sharedOptions||me.resolveDataElementOptions(i,point.active?'active':mode);} -if(!directUpdate){me.updateElement(point,i,properties,mode);} +update(mode){const meta=this._cachedMeta;const{dataset:line,data:points=[],_dataset}=meta;const animationsDisabled=this.chart._animationsDisabled;let{start,count}=getStartAndCountOfVisiblePoints(meta,points,animationsDisabled);this._drawStart=start;this._drawCount=count;if(scaleRangesChanged(meta)){start=0;count=points.length;} +line._chart=this.chart;line._datasetIndex=this.index;line._decimated=!!_dataset._decimated;line.points=points;const options=this.resolveDatasetElementOptions(mode);if(!this.options.showLine){options.borderWidth=0;} +options.segment=this.options.segment;this.updateElement(line,undefined,{animated:!animationsDisabled,options},mode);this.updateElements(points,start,count,mode);} +updateElements(points,start,count,mode){const reset=mode==='reset';const{iScale,vScale,_stacked,_dataset}=this._cachedMeta;const firstOpts=this.resolveDataElementOptions(start,mode);const sharedOptions=this.getSharedOptions(firstOpts);const includeOptions=this.includeOptions(mode,sharedOptions);const iAxis=iScale.axis;const vAxis=vScale.axis;const{spanGaps,segment}=this.options;const maxGapLength=isNumber(spanGaps)?spanGaps:Number.POSITIVE_INFINITY;const directUpdate=this.chart._animationsDisabled||reset||mode==='none';let prevParsed=start>0&&this.getParsed(start-1);for(let i=start;i0&&(parsed[iAxis]-prevParsed[iAxis])>maxGapLength;if(segment){properties.parsed=parsed;properties.raw=_dataset.data[i];} +if(includeOptions){properties.options=sharedOptions||this.resolveDataElementOptions(i,point.active?'active':mode);} +if(!directUpdate){this.updateElement(point,i,properties,mode);} prevParsed=parsed;} -me.updateSharedOptions(sharedOptions,mode,firstOpts);} -getMaxOverflow(){const me=this;const meta=me._cachedMeta;const dataset=meta.dataset;const border=dataset.options&&dataset.options.borderWidth||0;const data=meta.data||[];if(!data.length){return border;} -const firstPoint=data[0].size(me.resolveDataElementOptions(0));const lastPoint=data[data.length-1].size(me.resolveDataElementOptions(data.length-1));return Math.max(border,firstPoint,lastPoint)/2;} +this.updateSharedOptions(sharedOptions,mode,firstOpts);} +getMaxOverflow(){const meta=this._cachedMeta;const dataset=meta.dataset;const border=dataset.options&&dataset.options.borderWidth||0;const data=meta.data||[];if(!data.length){return border;} +const firstPoint=data[0].size(this.resolveDataElementOptions(0));const lastPoint=data[data.length-1].size(this.resolveDataElementOptions(data.length-1));return Math.max(border,firstPoint,lastPoint)/2;} draw(){const meta=this._cachedMeta;meta.dataset.updateControlPoints(this.chart.chartArea,meta.iScale.axis);super.draw();}} LineController.id='line';LineController.defaults={datasetElementType:'line',dataElementType:'point',showLine:true,spanGaps:false,};LineController.overrides={scales:{_index_:{type:'category',},_value_:{type:'linear',},}};function getStartAndCountOfVisiblePoints(meta,points,animationsDisabled){const pointCount=points.length;let start=0;let count=pointCount;if(meta._sorted){const{iScale,_parsed}=meta;const axis=iScale.axis;const{min,max,minDefined,maxDefined}=iScale.getUserBounds();if(minDefined){start=_limitValue(Math.min(_lookupByKey(_parsed,iScale.axis,min).lo,animationsDisabled?pointCount:_lookupByKey(points,axis,iScale.getPixelForValue(min)).lo),0,pointCount-1);} if(maxDefined){count=_limitValue(Math.max(_lookupByKey(_parsed,iScale.axis,max).hi+1,animationsDisabled?0:_lookupByKey(points,axis,iScale.getPixelForValue(max)).hi+1),start,pointCount)-start;}else{count=pointCount-start;}} @@ -1715,22 +1742,22 @@ return{start,count};} function scaleRangesChanged(meta){const{xScale,yScale,_scaleRanges}=meta;const newRanges={xmin:xScale.min,xmax:xScale.max,ymin:yScale.min,ymax:yScale.max};if(!_scaleRanges){meta._scaleRanges=newRanges;return true;} const changed=_scaleRanges.xmin!==xScale.min||_scaleRanges.xmax!==xScale.max||_scaleRanges.ymin!==yScale.min||_scaleRanges.ymax!==yScale.max;Object.assign(_scaleRanges,newRanges);return changed;} class PolarAreaController extends DatasetController{constructor(chart,datasetIndex){super(chart,datasetIndex);this.innerRadius=undefined;this.outerRadius=undefined;} -getLabelAndValue(index){const me=this;const meta=me._cachedMeta;const chart=me.chart;const labels=chart.data.labels||[];const value=formatNumber(meta._parsed[index].r,chart.options.locale);return{label:labels[index]||'',value,};} +getLabelAndValue(index){const meta=this._cachedMeta;const chart=this.chart;const labels=chart.data.labels||[];const value=formatNumber(meta._parsed[index].r,chart.options.locale);return{label:labels[index]||'',value,};} update(mode){const arcs=this._cachedMeta.data;this._updateRadius();this.updateElements(arcs,0,arcs.length,mode);} -_updateRadius(){const me=this;const chart=me.chart;const chartArea=chart.chartArea;const opts=chart.options;const minSize=Math.min(chartArea.right-chartArea.left,chartArea.bottom-chartArea.top);const outerRadius=Math.max(minSize/2,0);const innerRadius=Math.max(opts.cutoutPercentage?(outerRadius/100)*(opts.cutoutPercentage):1,0);const radiusLength=(outerRadius-innerRadius)/chart.getVisibleDatasetCount();me.outerRadius=outerRadius-(radiusLength*me.index);me.innerRadius=me.outerRadius-radiusLength;} -updateElements(arcs,start,count,mode){const me=this;const reset=mode==='reset';const chart=me.chart;const dataset=me.getDataset();const opts=chart.options;const animationOpts=opts.animation;const scale=me._cachedMeta.rScale;const centerX=scale.xCenter;const centerY=scale.yCenter;const datasetStartAngle=scale.getIndexAngle(0)-0.5*PI;let angle=datasetStartAngle;let i;const defaultAngle=360/me.countVisibleElements();for(i=0;i{if(!isNaN(dataset.data[index])&&this.chart.getDataVisibility(index)){count++;}});return count;} _computeAngle(index,mode,defaultAngle){return this.chart.getDataVisibility(index)?toRadians(this.resolveDataElementOptions(index,mode).angle||defaultAngle):0;}} PolarAreaController.id='polarArea';PolarAreaController.defaults={dataElementType:'arc',animation:{animateRotate:true,animateScale:true},animations:{numbers:{type:'number',properties:['x','y','startAngle','endAngle','innerRadius','outerRadius']},},indexAxis:'r',startAngle:0,};PolarAreaController.overrides={aspectRatio:1,plugins:{legend:{labels:{generateLabels(chart){const data=chart.data;if(data.labels.length&&data.datasets.length){const{labels:{pointStyle}}=chart.legend.options;return data.labels.map((label,i)=>{const meta=chart.getDatasetMeta(0);const style=meta.controller.getStyle(i);return{text:label,fillStyle:style.backgroundColor,strokeStyle:style.borderColor,lineWidth:style.borderWidth,pointStyle:pointStyle,hidden:!chart.getDataVisibility(i),index:i};});} return[];}},onClick(e,legendItem,legend){legend.chart.toggleDataVisibility(legendItem.index);legend.chart.update();}},tooltip:{callbacks:{title(){return'';},label(context){return context.chart.data.labels[context.dataIndex]+': '+context.formattedValue;}}}},scales:{r:{type:'radialLinear',angleLines:{display:false},beginAtZero:true,grid:{circular:true},pointLabels:{display:false},startAngle:0}}};class PieController extends DoughnutController{} -PieController.id='pie';PieController.defaults={cutout:0,rotation:0,circumference:360,radius:'100%'};class RadarController extends DatasetController{getLabelAndValue(index){const me=this;const vScale=me._cachedMeta.vScale;const parsed=me.getParsed(index);return{label:vScale.getLabels()[index],value:''+vScale.getLabelForValue(parsed[vScale.axis])};} -update(mode){const me=this;const meta=me._cachedMeta;const line=meta.dataset;const points=meta.data||[];const labels=meta.iScale.getLabels();line.points=points;if(mode!=='resize'){const options=me.resolveDatasetElementOptions(mode);if(!me.options.showLine){options.borderWidth=0;} -const properties={_loop:true,_fullLoop:labels.length===points.length,options};me.updateElement(line,undefined,properties,mode);} -me.updateElements(points,0,points.length,mode);} -updateElements(points,start,count,mode){const me=this;const dataset=me.getDataset();const scale=me._cachedMeta.rScale;const reset=mode==='reset';for(let i=start;ipixelMargin){angleMargin=pixelMargin/innerRadius;ctx.arc(x,y,innerRadius,endAngle+angleMargin,startAngle-angleMargin,true);}else{ctx.arc(x,y,pixelMargin,endAngle+HALF_PI,startAngle-HALF_PI);} ctx.closePath();ctx.clip();} @@ -1755,12 +1782,12 @@ if(element.fullCircles){drawFullCircleBorders(ctx,element,inner);} if(inner){clipArc(ctx,element,endAngle);} pathArc(ctx,element,offset,spacing,endAngle);ctx.stroke();} class ArcElement extends Element{constructor(cfg){super();this.options=undefined;this.circumference=undefined;this.startAngle=undefined;this.endAngle=undefined;this.innerRadius=undefined;this.outerRadius=undefined;this.pixelMargin=0;this.fullCircles=0;if(cfg){Object.assign(this,cfg);}} -inRange(chartX,chartY,useFinalPosition){const point=this.getProps(['x','y'],useFinalPosition);const{angle,distance}=getAngleFromPoint(point,{x:chartX,y:chartY});const{startAngle,endAngle,innerRadius,outerRadius,circumference}=this.getProps(['startAngle','endAngle','innerRadius','outerRadius','circumference'],useFinalPosition);const rAdjust=this.options.spacing/2;const betweenAngles=circumference>=TAU||_angleBetween(angle,startAngle,endAngle);const withinRadius=(distance>=innerRadius+rAdjust&&distance<=outerRadius+rAdjust);return(betweenAngles&&withinRadius);} +inRange(chartX,chartY,useFinalPosition){const point=this.getProps(['x','y'],useFinalPosition);const{angle,distance}=getAngleFromPoint(point,{x:chartX,y:chartY});const{startAngle,endAngle,innerRadius,outerRadius,circumference}=this.getProps(['startAngle','endAngle','innerRadius','outerRadius','circumference'],useFinalPosition);const rAdjust=this.options.spacing/2;const _circumference=valueOrDefault(circumference,endAngle-startAngle);const betweenAngles=_circumference>=TAU||_angleBetween(angle,startAngle,endAngle);const withinRadius=_isBetween(distance,innerRadius+rAdjust,outerRadius+rAdjust);return(betweenAngles&&withinRadius);} getCenterPoint(useFinalPosition){const{x,y,startAngle,endAngle,innerRadius,outerRadius}=this.getProps(['x','y','startAngle','endAngle','innerRadius','outerRadius','circumference',],useFinalPosition);const{offset,spacing}=this.options;const halfAngle=(startAngle+endAngle)/2;const halfRadius=(innerRadius+outerRadius+spacing+offset)/2;return{x:x+Math.cos(halfAngle)*halfRadius,y:y+Math.sin(halfAngle)*halfRadius};} tooltipPosition(useFinalPosition){return this.getCenterPoint(useFinalPosition);} -draw(ctx){const me=this;const{options,circumference}=me;const offset=(options.offset||0)/2;const spacing=(options.spacing||0)/2;me.pixelMargin=(options.borderAlign==='inner')?0.33:0;me.fullCircles=circumference>TAU?Math.floor(circumference/TAU):0;if(circumference===0||me.innerRadius<0||me.outerRadius<0){return;} -ctx.save();let radiusOffset=0;if(offset){radiusOffset=offset/2;const halfAngle=(me.startAngle+me.endAngle)/2;ctx.translate(Math.cos(halfAngle)*radiusOffset,Math.sin(halfAngle)*radiusOffset);if(me.circumference>=PI){radiusOffset=offset;}} -ctx.fillStyle=options.backgroundColor;ctx.strokeStyle=options.borderColor;const endAngle=drawArc(ctx,me,radiusOffset,spacing);drawBorder(ctx,me,radiusOffset,spacing,endAngle);ctx.restore();}} +draw(ctx){const{options,circumference}=this;const offset=(options.offset||0)/2;const spacing=(options.spacing||0)/2;this.pixelMargin=(options.borderAlign==='inner')?0.33:0;this.fullCircles=circumference>TAU?Math.floor(circumference/TAU):0;if(circumference===0||this.innerRadius<0||this.outerRadius<0){return;} +ctx.save();let radiusOffset=0;if(offset){radiusOffset=offset/2;const halfAngle=(this.startAngle+this.endAngle)/2;ctx.translate(Math.cos(halfAngle)*radiusOffset,Math.sin(halfAngle)*radiusOffset);if(this.circumference>=PI){radiusOffset=offset;}} +ctx.fillStyle=options.backgroundColor;ctx.strokeStyle=options.borderColor;const endAngle=drawArc(ctx,this,radiusOffset,spacing);drawBorder(ctx,this,radiusOffset,spacing,endAngle);ctx.restore();}} ArcElement.id='arc';ArcElement.defaults={borderAlign:'center',borderColor:'#fff',borderRadius:0,borderWidth:2,offset:0,spacing:0,angle:undefined,};ArcElement.defaultRoutes={backgroundColor:'backgroundColor'};function setStyle(ctx,options,style=options){ctx.lineCap=valueOrDefault(style.borderCapStyle,options.borderCapStyle);ctx.setLineDash(valueOrDefault(style.borderDash,options.borderDash));ctx.lineDashOffset=valueOrDefault(style.borderDashOffset,options.borderDashOffset);ctx.lineJoin=valueOrDefault(style.borderJoinStyle,options.borderJoinStyle);ctx.lineWidth=valueOrDefault(style.borderWidth,options.borderWidth);ctx.strokeStyle=valueOrDefault(style.borderColor,options.borderColor);} function lineTo(ctx,previous,target){ctx.lineTo(target.x,target.y);} function getLineMethod(options){if(options.stepped){return _steppedLineTo;} @@ -1785,23 +1812,23 @@ function strokePathWithCache(ctx,line,start,count){let path=line._path;if(!path) setStyle(ctx,line.options);ctx.stroke(path);} function strokePathDirect(ctx,line,start,count){const{segments,options}=line;const segmentMethod=_getSegmentMethod(line);for(const segment of segments){setStyle(ctx,options,segment.style);ctx.beginPath();if(segmentMethod(ctx,line,segment,{start,end:start+count-1})){ctx.closePath();} ctx.stroke();}} -const usePath2D=typeof Path2D==='function';function draw(ctx,line,start,count){if(usePath2D&&line.segments.length===1){strokePathWithCache(ctx,line,start,count);}else{strokePathDirect(ctx,line,start,count);}} -class LineElement extends Element{constructor(cfg){super();this.animated=true;this.options=undefined;this._loop=undefined;this._fullLoop=undefined;this._path=undefined;this._points=undefined;this._segments=undefined;this._decimated=false;this._pointsUpdated=false;this._datasetIndex=undefined;if(cfg){Object.assign(this,cfg);}} -updateControlPoints(chartArea,indexAxis){const me=this;const options=me.options;if((options.tension||options.cubicInterpolationMode==='monotone')&&!options.stepped&&!me._pointsUpdated){const loop=options.spanGaps?me._loop:me._fullLoop;_updateBezierControlPoints(me._points,options,chartArea,loop,indexAxis);me._pointsUpdated=true;}} -set points(points){const me=this;me._points=points;delete me._segments;delete me._path;me._pointsUpdated=false;} +const usePath2D=typeof Path2D==='function';function draw(ctx,line,start,count){if(usePath2D&&!line.options.segment){strokePathWithCache(ctx,line,start,count);}else{strokePathDirect(ctx,line,start,count);}} +class LineElement extends Element{constructor(cfg){super();this.animated=true;this.options=undefined;this._chart=undefined;this._loop=undefined;this._fullLoop=undefined;this._path=undefined;this._points=undefined;this._segments=undefined;this._decimated=false;this._pointsUpdated=false;this._datasetIndex=undefined;if(cfg){Object.assign(this,cfg);}} +updateControlPoints(chartArea,indexAxis){const options=this.options;if((options.tension||options.cubicInterpolationMode==='monotone')&&!options.stepped&&!this._pointsUpdated){const loop=options.spanGaps?this._loop:this._fullLoop;_updateBezierControlPoints(this._points,options,chartArea,loop,indexAxis);this._pointsUpdated=true;}} +set points(points){this._points=points;delete this._segments;delete this._path;this._pointsUpdated=false;} get points(){return this._points;} get segments(){return this._segments||(this._segments=_computeSegments(this,this.options.segment));} first(){const segments=this.segments;const points=this.points;return segments.length&&points[segments[0].start];} last(){const segments=this.segments;const points=this.points;const count=segments.length;return count&&points[segments[count-1].end];} -interpolate(point,property){const me=this;const options=me.options;const value=point[property];const points=me.points;const segments=_boundSegments(me,{property,start:value,end:value});if(!segments.length){return;} +interpolate(point,property){const options=this.options;const value=point[property];const points=this.points;const segments=_boundSegments(this,{property,start:value,end:value});if(!segments.length){return;} const result=[];const _interpolate=_getInterpolationMethod(options);let i,ilen;for(i=0,ilen=segments.length;iname!=='borderDash'&&name!=='fill',};function inRange$1(el,pos,axis,useFinalPosition){const options=el.options;const{[axis]:value}=el.getProps([axis],useFinalPosition);return(Math.abs(pos-value)=bounds.left&&x<=bounds.right)&&(skipY||y>=bounds.top&&y<=bounds.bottom);} +function inRange(bar,x,y,useFinalPosition){const skipX=x===null;const skipY=y===null;const skipBoth=skipX&&skipY;const bounds=bar&&!skipBoth&&getBarBounds(bar,useFinalPosition);return bounds&&(skipX||_isBetween(x,bounds.left,bounds.right))&&(skipY||_isBetween(y,bounds.top,bounds.bottom));} function hasRadius(radius){return radius.topLeft||radius.topRight||radius.bottomLeft||radius.bottomRight;} function addNormalRectPath(ctx,rect){ctx.rect(rect.x,rect.y,rect.w,rect.h);} function inflateRect(rect,amount,refRect={}){const x=rect.x!==refRect.x?-amount:0;const y=rect.y!==refRect.y?-amount:0;const w=(rect.x+rect.w!==refRect.x+refRect.w?amount:0)-x;const h=(rect.y+rect.h!==refRect.y+refRect.h?amount:0)-y;return{x:rect.x+x,y:rect.y+y,w:rect.w+w,h:rect.h+h,radius:rect.radius};} -class BarElement extends Element{constructor(cfg){super();this.options=undefined;this.horizontal=undefined;this.base=undefined;this.width=undefined;this.height=undefined;if(cfg){Object.assign(this,cfg);}} -draw(ctx){const options=this.options;const{inner,outer}=boundingRects(this);const addRectPath=hasRadius(outer.radius)?addRoundedRectPath:addNormalRectPath;const inflateAmount=0.33;ctx.save();if(outer.w!==inner.w||outer.h!==inner.h){ctx.beginPath();addRectPath(ctx,inflateRect(outer,inflateAmount,inner));ctx.clip();addRectPath(ctx,inflateRect(inner,-inflateAmount,outer));ctx.fillStyle=options.borderColor;ctx.fill('evenodd');} -ctx.beginPath();addRectPath(ctx,inflateRect(inner,inflateAmount,outer));ctx.fillStyle=options.backgroundColor;ctx.fill();ctx.restore();} +class BarElement extends Element{constructor(cfg){super();this.options=undefined;this.horizontal=undefined;this.base=undefined;this.width=undefined;this.height=undefined;this.inflateAmount=undefined;if(cfg){Object.assign(this,cfg);}} +draw(ctx){const{inflateAmount,options:{borderColor,backgroundColor}}=this;const{inner,outer}=boundingRects(this);const addRectPath=hasRadius(outer.radius)?addRoundedRectPath:addNormalRectPath;ctx.save();if(outer.w!==inner.w||outer.h!==inner.h){ctx.beginPath();addRectPath(ctx,inflateRect(outer,inflateAmount,inner));ctx.clip();addRectPath(ctx,inflateRect(inner,-inflateAmount,outer));ctx.fillStyle=borderColor;ctx.fill('evenodd');} +ctx.beginPath();addRectPath(ctx,inflateRect(inner,inflateAmount));ctx.fillStyle=backgroundColor;ctx.fill();ctx.restore();} inRange(mouseX,mouseY,useFinalPosition){return inRange(this,mouseX,mouseY,useFinalPosition);} inXRange(mouseX,useFinalPosition){return inRange(this,mouseX,null,useFinalPosition);} inYRange(mouseY,useFinalPosition){return inRange(this,null,mouseY,useFinalPosition);} getCenterPoint(useFinalPosition){const{x,y,base,horizontal}=this.getProps(['x','y','base','horizontal'],useFinalPosition);return{x:horizontal?(x+base)/2:x,y:horizontal?y:(y+base)/2};} getRange(axis){return axis==='x'?this.width/2:this.height/2;}} -BarElement.id='bar';BarElement.defaults={borderSkipped:'start',borderWidth:0,borderRadius:0,enableBorderRadius:true,pointStyle:undefined};BarElement.defaultRoutes={backgroundColor:'backgroundColor',borderColor:'borderColor'};var elements=Object.freeze({__proto__:null,ArcElement:ArcElement,LineElement:LineElement,PointElement:PointElement,BarElement:BarElement});function lttbDecimation(data,start,count,availableWidth,options){const samples=options.samples||availableWidth;if(samples>=count){return data.slice(start,start+count);} +BarElement.id='bar';BarElement.defaults={borderSkipped:'start',borderWidth:0,borderRadius:0,inflateAmount:'auto',pointStyle:undefined};BarElement.defaultRoutes={backgroundColor:'backgroundColor',borderColor:'borderColor'};var elements=Object.freeze({__proto__:null,ArcElement:ArcElement,LineElement:LineElement,PointElement:PointElement,BarElement:BarElement});function lttbDecimation(data,start,count,availableWidth,options){const samples=options.samples||availableWidth;if(samples>=count){return data.slice(start,start+count);} const decimated=[];const bucketWidth=(count-2)/(samples-2);let sampledIndex=0;const endIndex=start+count-1;let a=start;let i,maxAreaPoint,maxArea,area,nextA;decimated[sampledIndex++]=data[a];for(i=0;imaxArea){maxArea=area;maxAreaPoint=data[j];nextA=j;}} @@ -1880,16 +1907,16 @@ return computeLinearBoundary(source);} function findSegmentEnd(start,end,points){for(;end>start;end--){const point=points[end];if(!isNaN(point.x)&&!isNaN(point.y)){break;}} return end;} function pointsFromSegments(boundary,line){const{x=null,y=null}=boundary||{};const linePoints=line.points;const points=[];line.segments.forEach(({start,end})=>{end=findSegmentEnd(start,end,linePoints);const first=linePoints[start];const last=linePoints[end];if(y!==null){points.push({x:first.x,y});points.push({x:last.x,y});}else if(x!==null){points.push({x,y:first.y});points.push({x,y:last.y});}});return points;} -function buildStackLine(source){const{chart,scale,index,line}=source;const points=[];const segments=line.segments;const sourcePoints=line.points;const linesBelow=getLinesBelow(chart,index);linesBelow.push(createBoundaryLine({x:null,y:scale.bottom},line));for(let i=0;imeta.type==='line'&&!meta.hidden;function getLinesBelow(chart,index){const below=[];const metas=chart.getSortedVisibleDatasetMetas();for(let i=0;i=firstValue&&pointValue<=lastValue){first=pointValue===firstValue;last=pointValue===lastValue;break;}} +const pointValue=point[property];const segments=line.segments;const linePoints=line.points;let first=false;let last=false;for(let i=0;i=0;--i){const source=metasets[i].$filler;if(source){drawfill(chart.ctx,source,chart.chartArea);}}},beforeDatasetDraw(chart,args,options){const source=args.meta.$filler;if(!source||source.fill===false||options.drawTime!=='beforeDatasetDraw'){return;} drawfill(chart.ctx,source,chart.chartArea);},defaults:{propagate:true,drawTime:'beforeDatasetDraw'}};const getBoxSize=(labelOpts,fontSize)=>{let{boxHeight=fontSize,boxWidth=fontSize}=labelOpts;if(labelOpts.usePointStyle){boxHeight=Math.min(boxHeight,fontSize);boxWidth=Math.min(boxWidth,fontSize);} return{boxWidth,boxHeight,itemHeight:Math.max(fontSize,boxHeight)};};const itemsEqual=(a,b)=>a!==null&&b!==null&&a.datasetIndex===b.datasetIndex&&a.index===b.index;class Legend extends Element{constructor(config){super();this._added=false;this.legendHitBoxes=[];this._hoveredItem=null;this.doughnutMode=false;this.chart=config.chart;this.options=config.options;this.ctx=config.ctx;this.legendItems=undefined;this.columnSizes=undefined;this.lineWidths=undefined;this.maxHeight=undefined;this.maxWidth=undefined;this.top=undefined;this.bottom=undefined;this.left=undefined;this.right=undefined;this.height=undefined;this.width=undefined;this._margins=undefined;this.position=undefined;this.weight=undefined;this.fullSize=undefined;} -update(maxWidth,maxHeight,margins){const me=this;me.maxWidth=maxWidth;me.maxHeight=maxHeight;me._margins=margins;me.setDimensions();me.buildLabels();me.fit();} -setDimensions(){const me=this;if(me.isHorizontal()){me.width=me.maxWidth;me.left=me._margins.left;me.right=me.width;}else{me.height=me.maxHeight;me.top=me._margins.top;me.bottom=me.height;}} -buildLabels(){const me=this;const labelOpts=me.options.labels||{};let legendItems=callback(labelOpts.generateLabels,[me.chart],me)||[];if(labelOpts.filter){legendItems=legendItems.filter((item)=>labelOpts.filter(item,me.chart.data));} -if(labelOpts.sort){legendItems=legendItems.sort((a,b)=>labelOpts.sort(a,b,me.chart.data));} -if(me.options.reverse){legendItems.reverse();} -me.legendItems=legendItems;} -fit(){const me=this;const{options,ctx}=me;if(!options.display){me.width=me.height=0;return;} -const labelOpts=options.labels;const labelFont=toFont(labelOpts.font);const fontSize=labelFont.size;const titleHeight=me._computeTitleHeight();const{boxWidth,itemHeight}=getBoxSize(labelOpts,fontSize);let width,height;ctx.font=labelFont.string;if(me.isHorizontal()){width=me.maxWidth;height=me._fitRows(titleHeight,fontSize,boxWidth,itemHeight)+10;}else{height=me.maxHeight;width=me._fitCols(titleHeight,fontSize,boxWidth,itemHeight)+10;} -me.width=Math.min(width,options.maxWidth||me.maxWidth);me.height=Math.min(height,options.maxHeight||me.maxHeight);} -_fitRows(titleHeight,fontSize,boxWidth,itemHeight){const me=this;const{ctx,maxWidth,options:{labels:{padding}}}=me;const hitboxes=me.legendHitBoxes=[];const lineWidths=me.lineWidths=[0];const lineHeight=itemHeight+padding;let totalHeight=titleHeight;ctx.textAlign='left';ctx.textBaseline='middle';let row=-1;let top=-lineHeight;me.legendItems.forEach((legendItem,i)=>{const itemWidth=boxWidth+(fontSize/2)+ctx.measureText(legendItem.text).width;if(i===0||lineWidths[lineWidths.length-1]+itemWidth+2*padding>maxWidth){totalHeight+=lineHeight;lineWidths[lineWidths.length-(i>0?0:1)]=0;top+=lineHeight;row++;} +update(maxWidth,maxHeight,margins){this.maxWidth=maxWidth;this.maxHeight=maxHeight;this._margins=margins;this.setDimensions();this.buildLabels();this.fit();} +setDimensions(){if(this.isHorizontal()){this.width=this.maxWidth;this.left=this._margins.left;this.right=this.width;}else{this.height=this.maxHeight;this.top=this._margins.top;this.bottom=this.height;}} +buildLabels(){const labelOpts=this.options.labels||{};let legendItems=callback(labelOpts.generateLabels,[this.chart],this)||[];if(labelOpts.filter){legendItems=legendItems.filter((item)=>labelOpts.filter(item,this.chart.data));} +if(labelOpts.sort){legendItems=legendItems.sort((a,b)=>labelOpts.sort(a,b,this.chart.data));} +if(this.options.reverse){legendItems.reverse();} +this.legendItems=legendItems;} +fit(){const{options,ctx}=this;if(!options.display){this.width=this.height=0;return;} +const labelOpts=options.labels;const labelFont=toFont(labelOpts.font);const fontSize=labelFont.size;const titleHeight=this._computeTitleHeight();const{boxWidth,itemHeight}=getBoxSize(labelOpts,fontSize);let width,height;ctx.font=labelFont.string;if(this.isHorizontal()){width=this.maxWidth;height=this._fitRows(titleHeight,fontSize,boxWidth,itemHeight)+10;}else{height=this.maxHeight;width=this._fitCols(titleHeight,fontSize,boxWidth,itemHeight)+10;} +this.width=Math.min(width,options.maxWidth||this.maxWidth);this.height=Math.min(height,options.maxHeight||this.maxHeight);} +_fitRows(titleHeight,fontSize,boxWidth,itemHeight){const{ctx,maxWidth,options:{labels:{padding}}}=this;const hitboxes=this.legendHitBoxes=[];const lineWidths=this.lineWidths=[0];const lineHeight=itemHeight+padding;let totalHeight=titleHeight;ctx.textAlign='left';ctx.textBaseline='middle';let row=-1;let top=-lineHeight;this.legendItems.forEach((legendItem,i)=>{const itemWidth=boxWidth+(fontSize/2)+ctx.measureText(legendItem.text).width;if(i===0||lineWidths[lineWidths.length-1]+itemWidth+2*padding>maxWidth){totalHeight+=lineHeight;lineWidths[lineWidths.length-(i>0?0:1)]=0;top+=lineHeight;row++;} hitboxes[i]={left:0,top,row,width:itemWidth,height:itemHeight};lineWidths[lineWidths.length-1]+=itemWidth+padding;});return totalHeight;} -_fitCols(titleHeight,fontSize,boxWidth,itemHeight){const me=this;const{ctx,maxHeight,options:{labels:{padding}}}=me;const hitboxes=me.legendHitBoxes=[];const columnSizes=me.columnSizes=[];const heightLimit=maxHeight-titleHeight;let totalWidth=padding;let currentColWidth=0;let currentColHeight=0;let left=0;let col=0;me.legendItems.forEach((legendItem,i)=>{const itemWidth=boxWidth+(fontSize/2)+ctx.measureText(legendItem.text).width;if(i>0&¤tColHeight+itemHeight+2*padding>heightLimit){totalWidth+=currentColWidth+padding;columnSizes.push({width:currentColWidth,height:currentColHeight});left+=currentColWidth+padding;col++;currentColWidth=currentColHeight=0;} +_fitCols(titleHeight,fontSize,boxWidth,itemHeight){const{ctx,maxHeight,options:{labels:{padding}}}=this;const hitboxes=this.legendHitBoxes=[];const columnSizes=this.columnSizes=[];const heightLimit=maxHeight-titleHeight;let totalWidth=padding;let currentColWidth=0;let currentColHeight=0;let left=0;let col=0;this.legendItems.forEach((legendItem,i)=>{const itemWidth=boxWidth+(fontSize/2)+ctx.measureText(legendItem.text).width;if(i>0&¤tColHeight+itemHeight+2*padding>heightLimit){totalWidth+=currentColWidth+padding;columnSizes.push({width:currentColWidth,height:currentColHeight});left+=currentColWidth+padding;col++;currentColWidth=currentColHeight=0;} hitboxes[i]={left,top:currentColHeight,col,width:itemWidth,height:itemHeight};currentColWidth=Math.max(currentColWidth,itemWidth);currentColHeight+=itemHeight+padding;});totalWidth+=currentColWidth;columnSizes.push({width:currentColWidth,height:currentColHeight});return totalWidth;} -adjustHitBoxes(){const me=this;if(!me.options.display){return;} -const titleHeight=me._computeTitleHeight();const{legendHitBoxes:hitboxes,options:{align,labels:{padding},rtl}}=me;const rtlHelper=getRtlAdapter(rtl,me.left,me.width);if(this.isHorizontal()){let row=0;let left=_alignStartEnd(align,me.left+padding,me.right-me.lineWidths[row]);for(const hitbox of hitboxes){if(row!==hitbox.row){row=hitbox.row;left=_alignStartEnd(align,me.left+padding,me.right-me.lineWidths[row]);} -hitbox.top+=me.top+titleHeight+padding;hitbox.left=rtlHelper.leftForLtr(rtlHelper.x(left),hitbox.width);left+=hitbox.width+padding;}}else{let col=0;let top=_alignStartEnd(align,me.top+titleHeight+padding,me.bottom-me.columnSizes[col].height);for(const hitbox of hitboxes){if(hitbox.col!==col){col=hitbox.col;top=_alignStartEnd(align,me.top+titleHeight+padding,me.bottom-me.columnSizes[col].height);} -hitbox.top=top;hitbox.left+=me.left+padding;hitbox.left=rtlHelper.leftForLtr(rtlHelper.x(hitbox.left),hitbox.width);top+=hitbox.height+padding;}}} +adjustHitBoxes(){if(!this.options.display){return;} +const titleHeight=this._computeTitleHeight();const{legendHitBoxes:hitboxes,options:{align,labels:{padding},rtl}}=this;const rtlHelper=getRtlAdapter(rtl,this.left,this.width);if(this.isHorizontal()){let row=0;let left=_alignStartEnd(align,this.left+padding,this.right-this.lineWidths[row]);for(const hitbox of hitboxes){if(row!==hitbox.row){row=hitbox.row;left=_alignStartEnd(align,this.left+padding,this.right-this.lineWidths[row]);} +hitbox.top+=this.top+titleHeight+padding;hitbox.left=rtlHelper.leftForLtr(rtlHelper.x(left),hitbox.width);left+=hitbox.width+padding;}}else{let col=0;let top=_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-this.columnSizes[col].height);for(const hitbox of hitboxes){if(hitbox.col!==col){col=hitbox.col;top=_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-this.columnSizes[col].height);} +hitbox.top=top;hitbox.left+=this.left+padding;hitbox.left=rtlHelper.leftForLtr(rtlHelper.x(hitbox.left),hitbox.width);top+=hitbox.height+padding;}}} isHorizontal(){return this.options.position==='top'||this.options.position==='bottom';} -draw(){const me=this;if(me.options.display){const ctx=me.ctx;clipArea(ctx,me);me._draw();unclipArea(ctx);}} -_draw(){const me=this;const{options:opts,columnSizes,lineWidths,ctx}=me;const{align,labels:labelOpts}=opts;const defaultColor=defaults.color;const rtlHelper=getRtlAdapter(opts.rtl,me.left,me.width);const labelFont=toFont(labelOpts.font);const{color:fontColor,padding}=labelOpts;const fontSize=labelFont.size;const halfFontSize=fontSize/2;let cursor;me.drawTitle();ctx.textAlign=rtlHelper.textAlign('left');ctx.textBaseline='middle';ctx.lineWidth=0.5;ctx.font=labelFont.string;const{boxWidth,boxHeight,itemHeight}=getBoxSize(labelOpts,fontSize);const drawLegendBox=function(x,y,legendItem){if(isNaN(boxWidth)||boxWidth<=0||isNaN(boxHeight)||boxHeight<0){return;} +draw(){if(this.options.display){const ctx=this.ctx;clipArea(ctx,this);this._draw();unclipArea(ctx);}} +_draw(){const{options:opts,columnSizes,lineWidths,ctx}=this;const{align,labels:labelOpts}=opts;const defaultColor=defaults.color;const rtlHelper=getRtlAdapter(opts.rtl,this.left,this.width);const labelFont=toFont(labelOpts.font);const{color:fontColor,padding}=labelOpts;const fontSize=labelFont.size;const halfFontSize=fontSize/2;let cursor;this.drawTitle();ctx.textAlign=rtlHelper.textAlign('left');ctx.textBaseline='middle';ctx.lineWidth=0.5;ctx.font=labelFont.string;const{boxWidth,boxHeight,itemHeight}=getBoxSize(labelOpts,fontSize);const drawLegendBox=function(x,y,legendItem){if(isNaN(boxWidth)||boxWidth<=0||isNaN(boxHeight)||boxHeight<0){return;} ctx.save();const lineWidth=valueOrDefault(legendItem.lineWidth,1);ctx.fillStyle=valueOrDefault(legendItem.fillStyle,defaultColor);ctx.lineCap=valueOrDefault(legendItem.lineCap,'butt');ctx.lineDashOffset=valueOrDefault(legendItem.lineDashOffset,0);ctx.lineJoin=valueOrDefault(legendItem.lineJoin,'miter');ctx.lineWidth=lineWidth;ctx.strokeStyle=valueOrDefault(legendItem.strokeStyle,defaultColor);ctx.setLineDash(valueOrDefault(legendItem.lineDash,[]));if(labelOpts.usePointStyle){const drawOptions={radius:boxWidth*Math.SQRT2/2,pointStyle:legendItem.pointStyle,rotation:legendItem.rotation,borderWidth:lineWidth};const centerX=rtlHelper.xPlus(x,boxWidth/2);const centerY=y+halfFontSize;drawPoint(ctx,drawOptions,centerX,centerY);}else{const yBoxTop=y+Math.max((fontSize-boxHeight)/2,0);const xBoxLeft=rtlHelper.leftForLtr(x,boxWidth);const borderRadius=toTRBLCorners(legendItem.borderRadius);ctx.beginPath();if(Object.values(borderRadius).some(v=>v!==0)){addRoundedRectPath(ctx,{x:xBoxLeft,y:yBoxTop,w:boxWidth,h:boxHeight,radius:borderRadius,});}else{ctx.rect(xBoxLeft,yBoxTop,boxWidth,boxHeight);} ctx.fill();if(lineWidth!==0){ctx.stroke();}} -ctx.restore();};const fillText=function(x,y,legendItem){renderText(ctx,legendItem.text,x,y+(itemHeight/2),labelFont,{strikethrough:legendItem.hidden,textAlign:rtlHelper.textAlign(legendItem.textAlign)});};const isHorizontal=me.isHorizontal();const titleHeight=this._computeTitleHeight();if(isHorizontal){cursor={x:_alignStartEnd(align,me.left+padding,me.right-lineWidths[0]),y:me.top+padding+titleHeight,line:0};}else{cursor={x:me.left+padding,y:_alignStartEnd(align,me.top+titleHeight+padding,me.bottom-columnSizes[0].height),line:0};} -overrideTextDirection(me.ctx,opts.textDirection);const lineHeight=itemHeight+padding;me.legendItems.forEach((legendItem,i)=>{ctx.strokeStyle=legendItem.fontColor||fontColor;ctx.fillStyle=legendItem.fontColor||fontColor;const textWidth=ctx.measureText(legendItem.text).width;const textAlign=rtlHelper.textAlign(legendItem.textAlign||(legendItem.textAlign=labelOpts.textAlign));const width=boxWidth+halfFontSize+textWidth;let x=cursor.x;let y=cursor.y;rtlHelper.setWidth(me.width);if(isHorizontal){if(i>0&&x+width+padding>me.right){y=cursor.y+=lineHeight;cursor.line++;x=cursor.x=_alignStartEnd(align,me.left+padding,me.right-lineWidths[cursor.line]);}}else if(i>0&&y+lineHeight>me.bottom){x=cursor.x=x+columnSizes[cursor.line].width+padding;cursor.line++;y=cursor.y=_alignStartEnd(align,me.top+titleHeight+padding,me.bottom-columnSizes[cursor.line].height);} -const realX=rtlHelper.x(x);drawLegendBox(realX,y,legendItem);x=_textX(textAlign,x+boxWidth+halfFontSize,isHorizontal?x+width:me.right,opts.rtl);fillText(rtlHelper.x(x),y,legendItem);if(isHorizontal){cursor.x+=width+padding;}else{cursor.y+=lineHeight;}});restoreTextDirection(me.ctx,opts.textDirection);} -drawTitle(){const me=this;const opts=me.options;const titleOpts=opts.title;const titleFont=toFont(titleOpts.font);const titlePadding=toPadding(titleOpts.padding);if(!titleOpts.display){return;} -const rtlHelper=getRtlAdapter(opts.rtl,me.left,me.width);const ctx=me.ctx;const position=titleOpts.position;const halfFontSize=titleFont.size/2;const topPaddingPlusHalfFontSize=titlePadding.top+halfFontSize;let y;let left=me.left;let maxWidth=me.width;if(this.isHorizontal()){maxWidth=Math.max(...me.lineWidths);y=me.top+topPaddingPlusHalfFontSize;left=_alignStartEnd(opts.align,left,me.right-maxWidth);}else{const maxHeight=me.columnSizes.reduce((acc,size)=>Math.max(acc,size.height),0);y=topPaddingPlusHalfFontSize+_alignStartEnd(opts.align,me.top,me.bottom-maxHeight-opts.labels.padding-me._computeTitleHeight());} +ctx.restore();};const fillText=function(x,y,legendItem){renderText(ctx,legendItem.text,x,y+(itemHeight/2),labelFont,{strikethrough:legendItem.hidden,textAlign:rtlHelper.textAlign(legendItem.textAlign)});};const isHorizontal=this.isHorizontal();const titleHeight=this._computeTitleHeight();if(isHorizontal){cursor={x:_alignStartEnd(align,this.left+padding,this.right-lineWidths[0]),y:this.top+padding+titleHeight,line:0};}else{cursor={x:this.left+padding,y:_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-columnSizes[0].height),line:0};} +overrideTextDirection(this.ctx,opts.textDirection);const lineHeight=itemHeight+padding;this.legendItems.forEach((legendItem,i)=>{ctx.strokeStyle=legendItem.fontColor||fontColor;ctx.fillStyle=legendItem.fontColor||fontColor;const textWidth=ctx.measureText(legendItem.text).width;const textAlign=rtlHelper.textAlign(legendItem.textAlign||(legendItem.textAlign=labelOpts.textAlign));const width=boxWidth+halfFontSize+textWidth;let x=cursor.x;let y=cursor.y;rtlHelper.setWidth(this.width);if(isHorizontal){if(i>0&&x+width+padding>this.right){y=cursor.y+=lineHeight;cursor.line++;x=cursor.x=_alignStartEnd(align,this.left+padding,this.right-lineWidths[cursor.line]);}}else if(i>0&&y+lineHeight>this.bottom){x=cursor.x=x+columnSizes[cursor.line].width+padding;cursor.line++;y=cursor.y=_alignStartEnd(align,this.top+titleHeight+padding,this.bottom-columnSizes[cursor.line].height);} +const realX=rtlHelper.x(x);drawLegendBox(realX,y,legendItem);x=_textX(textAlign,x+boxWidth+halfFontSize,isHorizontal?x+width:this.right,opts.rtl);fillText(rtlHelper.x(x),y,legendItem);if(isHorizontal){cursor.x+=width+padding;}else{cursor.y+=lineHeight;}});restoreTextDirection(this.ctx,opts.textDirection);} +drawTitle(){const opts=this.options;const titleOpts=opts.title;const titleFont=toFont(titleOpts.font);const titlePadding=toPadding(titleOpts.padding);if(!titleOpts.display){return;} +const rtlHelper=getRtlAdapter(opts.rtl,this.left,this.width);const ctx=this.ctx;const position=titleOpts.position;const halfFontSize=titleFont.size/2;const topPaddingPlusHalfFontSize=titlePadding.top+halfFontSize;let y;let left=this.left;let maxWidth=this.width;if(this.isHorizontal()){maxWidth=Math.max(...this.lineWidths);y=this.top+topPaddingPlusHalfFontSize;left=_alignStartEnd(opts.align,left,this.right-maxWidth);}else{const maxHeight=this.columnSizes.reduce((acc,size)=>Math.max(acc,size.height),0);y=topPaddingPlusHalfFontSize+_alignStartEnd(opts.align,this.top,this.bottom-maxHeight-opts.labels.padding-this._computeTitleHeight());} const x=_alignStartEnd(position,left,left+maxWidth);ctx.textAlign=rtlHelper.textAlign(_toLeftRightCenter(position));ctx.textBaseline='middle';ctx.strokeStyle=titleOpts.color;ctx.fillStyle=titleOpts.color;ctx.font=titleFont.string;renderText(ctx,titleOpts.text,x,y,titleFont);} _computeTitleHeight(){const titleOpts=this.options.title;const titleFont=toFont(titleOpts.font);const titlePadding=toPadding(titleOpts.padding);return titleOpts.display?titleFont.lineHeight+titlePadding.height:0;} -_getLegendItemAt(x,y){const me=this;let i,hitBox,lh;if(x>=me.left&&x<=me.right&&y>=me.top&&y<=me.bottom){lh=me.legendHitBoxes;for(i=0;i=hitBox.left&&x<=hitBox.left+hitBox.width&&y>=hitBox.top&&y<=hitBox.top+hitBox.height){return me.legendItems[i];}}} +_getLegendItemAt(x,y){let i,hitBox,lh;if(_isBetween(x,this.left,this.right)&&_isBetween(y,this.top,this.bottom)){lh=this.legendHitBoxes;for(i=0;ictx.chart.options.color,boxWidth:40,padding:10,generateLabels(chart){const datasets=chart.data.datasets;const{labels:{usePointStyle,pointStyle,textAlign,color}}=chart.legend.options;return chart._getSortedDatasetMetas().map((meta)=>{const style=meta.controller.getStyle(usePointStyle?0:undefined);const borderWidth=toPadding(style.borderWidth);return{text:datasets[meta.index].label,fillStyle:style.backgroundColor,fontColor:color,hidden:!meta.visible,lineCap:style.borderCapStyle,lineDash:style.borderDash,lineDashOffset:style.borderDashOffset,lineJoin:style.borderJoinStyle,lineWidth:(borderWidth.width+borderWidth.height)/4,strokeStyle:style.borderColor,pointStyle:pointStyle||style.pointStyle,rotation:style.rotation,textAlign:textAlign||style.textAlign,borderRadius:0,datasetIndex:meta.index};},this);}},title:{color:(ctx)=>ctx.chart.options.color,display:false,position:'center',text:'',}},descriptors:{_scriptable:(name)=>!name.startsWith('on'),labels:{_scriptable:(name)=>!['generateLabels','filter','sort'].includes(name),}},};class Title extends Element{constructor(config){super();this.chart=config.chart;this.options=config.options;this.ctx=config.ctx;this._padding=undefined;this.top=undefined;this.bottom=undefined;this.left=undefined;this.right=undefined;this.width=undefined;this.height=undefined;this.position=undefined;this.weight=undefined;this.fullSize=undefined;} -update(maxWidth,maxHeight){const me=this;const opts=me.options;me.left=0;me.top=0;if(!opts.display){me.width=me.height=me.right=me.bottom=0;return;} -me.width=me.right=maxWidth;me.height=me.bottom=maxHeight;const lineCount=isArray(opts.text)?opts.text.length:1;me._padding=toPadding(opts.padding);const textSize=lineCount*toFont(opts.font).lineHeight+me._padding.height;if(me.isHorizontal()){me.height=textSize;}else{me.width=textSize;}} +update(maxWidth,maxHeight){const opts=this.options;this.left=0;this.top=0;if(!opts.display){this.width=this.height=this.right=this.bottom=0;return;} +this.width=this.right=maxWidth;this.height=this.bottom=maxHeight;const lineCount=isArray(opts.text)?opts.text.length:1;this._padding=toPadding(opts.padding);const textSize=lineCount*toFont(opts.font).lineHeight+this._padding.height;if(this.isHorizontal()){this.height=textSize;}else{this.width=textSize;}} isHorizontal(){const pos=this.options.position;return pos==='top'||pos==='bottom';} _drawArgs(offset){const{top,left,bottom,right,options}=this;const align=options.align;let rotation=0;let maxWidth,titleX,titleY;if(this.isHorizontal()){titleX=_alignStartEnd(align,left,right);titleY=top+offset;maxWidth=right-left;}else{if(options.position==='left'){titleX=left+offset;titleY=_alignStartEnd(align,bottom,top);rotation=PI*-0.5;}else{titleX=right-offset;titleY=_alignStartEnd(align,top,bottom);rotation=PI*0.5;} maxWidth=bottom-top;} return{titleX,titleY,maxWidth,rotation};} -draw(){const me=this;const ctx=me.ctx;const opts=me.options;if(!opts.display){return;} -const fontOpts=toFont(opts.font);const lineHeight=fontOpts.lineHeight;const offset=lineHeight/2+me._padding.top;const{titleX,titleY,maxWidth,rotation}=me._drawArgs(offset);renderText(ctx,opts.text,0,0,fontOpts,{color:opts.color,maxWidth,rotation,textAlign:_toLeftRightCenter(opts.align),textBaseline:'middle',translation:[titleX,titleY],});}} +draw(){const ctx=this.ctx;const opts=this.options;if(!opts.display){return;} +const fontOpts=toFont(opts.font);const lineHeight=fontOpts.lineHeight;const offset=lineHeight/2+this._padding.top;const{titleX,titleY,maxWidth,rotation}=this._drawArgs(offset);renderText(ctx,opts.text,0,0,fontOpts,{color:opts.color,maxWidth,rotation,textAlign:_toLeftRightCenter(opts.align),textBaseline:'middle',translation:[titleX,titleY],});}} function createTitle(chart,titleOpts){const title=new Title({ctx:chart.ctx,options:titleOpts,chart});layouts.configure(chart,title,titleOpts);layouts.addBox(chart,title);chart.titleBlock=title;} var plugin_title={id:'title',_element:Title,start(chart,_args,options){createTitle(chart,options);},stop(chart){const titleBlock=chart.titleBlock;layouts.removeBox(chart,titleBlock);delete chart.titleBlock;},beforeUpdate(chart,_args,options){const title=chart.titleBlock;layouts.configure(chart,title,options);title.options=options;},defaults:{align:'center',display:false,font:{weight:'bold',},fullSize:true,padding:10,position:'top',text:'',weight:2000},defaultRoutes:{color:'color'},descriptors:{_scriptable:true,_indexable:false,},};const map=new WeakMap();var plugin_subtitle={id:'subtitle',start(chart,_args,options){const title=new Title({ctx:chart.ctx,options,chart});layouts.configure(chart,title,options);layouts.addBox(chart,title);map.set(chart,title);},stop(chart){layouts.removeBox(chart,map.get(chart));map.delete(chart);},beforeUpdate(chart,_args,options){const title=map.get(chart);layouts.configure(chart,title,options);title.options=options;},defaults:{align:'center',display:false,font:{weight:'normal',},fullSize:true,padding:0,position:'top',text:'',weight:1500},defaultRoutes:{color:'color'},descriptors:{_scriptable:true,_indexable:false,},};const positioners={average(items){if(!items.length){return false;} let i,len;let x=0;let y=0;let count=0;for(i=0,len=items.length;i{each(bodyItem.before,maxLineWidth);each(bodyItem.lines,maxLineWidth);each(bodyItem.after,maxLineWidth);});widthPadding=0;ctx.font=footerFont.string;each(tooltip.footer,maxLineWidth);ctx.restore();width+=padding.width;return{width,height};} +let widthPadding=0;const maxLineWidth=function(line){width=Math.max(width,ctx.measureText(line).width+widthPadding);};ctx.save();ctx.font=titleFont.string;each(tooltip.title,maxLineWidth);ctx.font=bodyFont.string;each(tooltip.beforeBody.concat(tooltip.afterBody),maxLineWidth);widthPadding=options.displayColors?(boxWidth+2+options.boxPadding):0;each(body,(bodyItem)=>{each(bodyItem.before,maxLineWidth);each(bodyItem.lines,maxLineWidth);each(bodyItem.after,maxLineWidth);});widthPadding=0;ctx.font=footerFont.string;each(tooltip.footer,maxLineWidth);ctx.restore();width+=padding.width;return{width,height};} function determineYAlign(chart,size){const{y,height}=size;if(y(chart.height-height/2)){return'bottom';} return'center';} function doesNotFitWithAlign(xAlign,chart,options,size){const{x,width}=size;const caret=options.caretSize+options.caretPadding;if(xAlign==='left'&&x+width+caret>chart.width){return true;} @@ -2008,84 +2035,88 @@ function alignX(size,xAlign){let{x,width}=size;if(xAlign==='right'){x-=width;}el return x;} function alignY(size,yAlign,paddingAndSize){let{y,height}=size;if(yAlign==='top'){y+=paddingAndSize;}else if(yAlign==='bottom'){y-=height+paddingAndSize;}else{y-=(height/2);} return y;} -function getBackgroundPoint(options,size,alignment,chart){const{caretSize,caretPadding,cornerRadius}=options;const{xAlign,yAlign}=alignment;const paddingAndSize=caretSize+caretPadding;const radiusAndPadding=cornerRadius+caretPadding;let x=alignX(size,xAlign);const y=alignY(size,yAlign,paddingAndSize);if(yAlign==='center'){if(xAlign==='left'){x+=paddingAndSize;}else if(xAlign==='right'){x-=paddingAndSize;}}else if(xAlign==='left'){x-=radiusAndPadding;}else if(xAlign==='right'){x+=radiusAndPadding;} +function getBackgroundPoint(options,size,alignment,chart){const{caretSize,caretPadding,cornerRadius}=options;const{xAlign,yAlign}=alignment;const paddingAndSize=caretSize+caretPadding;const{topLeft,topRight,bottomLeft,bottomRight}=toTRBLCorners(cornerRadius);let x=alignX(size,xAlign);const y=alignY(size,yAlign,paddingAndSize);if(yAlign==='center'){if(xAlign==='left'){x+=paddingAndSize;}else if(xAlign==='right'){x-=paddingAndSize;}}else if(xAlign==='left'){x-=Math.max(topLeft,bottomLeft)+caretSize;}else if(xAlign==='right'){x+=Math.max(topRight,bottomRight)+caretSize;} return{x:_limitValue(x,0,chart.width-size.width),y:_limitValue(y,0,chart.height-size.height)};} function getAlignedX(tooltip,align,options){const padding=toPadding(options.padding);return align==='center'?tooltip.x+tooltip.width/2:align==='right'?tooltip.x+tooltip.width-padding.right:tooltip.x+padding.left;} function getBeforeAfterBodyLines(callback){return pushOrConcat([],splitNewlines(callback));} -function createTooltipContext(parent,tooltip,tooltipItems){return Object.assign(Object.create(parent),{tooltip,tooltipItems,type:'tooltip'});} +function createTooltipContext(parent,tooltip,tooltipItems){return createContext(parent,{tooltip,tooltipItems,type:'tooltip'});} function overrideCallbacks(callbacks,context){const override=context&&context.dataset&&context.dataset.tooltip&&context.dataset.tooltip.callbacks;return override?callbacks.override(override):callbacks;} class Tooltip extends Element{constructor(config){super();this.opacity=0;this._active=[];this._chart=config._chart;this._eventPosition=undefined;this._size=undefined;this._cachedAnimations=undefined;this._tooltipItems=[];this.$animations=undefined;this.$context=undefined;this.options=config.options;this.dataPoints=undefined;this.title=undefined;this.beforeBody=undefined;this.body=undefined;this.afterBody=undefined;this.footer=undefined;this.xAlign=undefined;this.yAlign=undefined;this.x=undefined;this.y=undefined;this.height=undefined;this.width=undefined;this.caretX=undefined;this.caretY=undefined;this.labelColors=undefined;this.labelPointStyles=undefined;this.labelTextColors=undefined;} initialize(options){this.options=options;this._cachedAnimations=undefined;this.$context=undefined;} -_resolveAnimations(){const me=this;const cached=me._cachedAnimations;if(cached){return cached;} -const chart=me._chart;const options=me.options.setContext(me.getContext());const opts=options.enabled&&chart.options.animation&&options.animations;const animations=new Animations(me._chart,opts);if(opts._cacheable){me._cachedAnimations=Object.freeze(animations);} +_resolveAnimations(){const cached=this._cachedAnimations;if(cached){return cached;} +const chart=this._chart;const options=this.options.setContext(this.getContext());const opts=options.enabled&&chart.options.animation&&options.animations;const animations=new Animations(this._chart,opts);if(opts._cacheable){this._cachedAnimations=Object.freeze(animations);} return animations;} -getContext(){const me=this;return me.$context||(me.$context=createTooltipContext(me._chart.getContext(),me,me._tooltipItems));} -getTitle(context,options){const me=this;const{callbacks}=options;const beforeTitle=callbacks.beforeTitle.apply(me,[context]);const title=callbacks.title.apply(me,[context]);const afterTitle=callbacks.afterTitle.apply(me,[context]);let lines=[];lines=pushOrConcat(lines,splitNewlines(beforeTitle));lines=pushOrConcat(lines,splitNewlines(title));lines=pushOrConcat(lines,splitNewlines(afterTitle));return lines;} +getContext(){return this.$context||(this.$context=createTooltipContext(this._chart.getContext(),this,this._tooltipItems));} +getTitle(context,options){const{callbacks}=options;const beforeTitle=callbacks.beforeTitle.apply(this,[context]);const title=callbacks.title.apply(this,[context]);const afterTitle=callbacks.afterTitle.apply(this,[context]);let lines=[];lines=pushOrConcat(lines,splitNewlines(beforeTitle));lines=pushOrConcat(lines,splitNewlines(title));lines=pushOrConcat(lines,splitNewlines(afterTitle));return lines;} getBeforeBody(tooltipItems,options){return getBeforeAfterBodyLines(options.callbacks.beforeBody.apply(this,[tooltipItems]));} -getBody(tooltipItems,options){const me=this;const{callbacks}=options;const bodyItems=[];each(tooltipItems,(context)=>{const bodyItem={before:[],lines:[],after:[]};const scoped=overrideCallbacks(callbacks,context);pushOrConcat(bodyItem.before,splitNewlines(scoped.beforeLabel.call(me,context)));pushOrConcat(bodyItem.lines,scoped.label.call(me,context));pushOrConcat(bodyItem.after,splitNewlines(scoped.afterLabel.call(me,context)));bodyItems.push(bodyItem);});return bodyItems;} +getBody(tooltipItems,options){const{callbacks}=options;const bodyItems=[];each(tooltipItems,(context)=>{const bodyItem={before:[],lines:[],after:[]};const scoped=overrideCallbacks(callbacks,context);pushOrConcat(bodyItem.before,splitNewlines(scoped.beforeLabel.call(this,context)));pushOrConcat(bodyItem.lines,scoped.label.call(this,context));pushOrConcat(bodyItem.after,splitNewlines(scoped.afterLabel.call(this,context)));bodyItems.push(bodyItem);});return bodyItems;} getAfterBody(tooltipItems,options){return getBeforeAfterBodyLines(options.callbacks.afterBody.apply(this,[tooltipItems]));} -getFooter(tooltipItems,options){const me=this;const{callbacks}=options;const beforeFooter=callbacks.beforeFooter.apply(me,[tooltipItems]);const footer=callbacks.footer.apply(me,[tooltipItems]);const afterFooter=callbacks.afterFooter.apply(me,[tooltipItems]);let lines=[];lines=pushOrConcat(lines,splitNewlines(beforeFooter));lines=pushOrConcat(lines,splitNewlines(footer));lines=pushOrConcat(lines,splitNewlines(afterFooter));return lines;} -_createItems(options){const me=this;const active=me._active;const data=me._chart.data;const labelColors=[];const labelPointStyles=[];const labelTextColors=[];let tooltipItems=[];let i,len;for(i=0,len=active.length;ioptions.filter(element,index,array,data));} if(options.itemSort){tooltipItems=tooltipItems.sort((a,b)=>options.itemSort(a,b,data));} -each(tooltipItems,(context)=>{const scoped=overrideCallbacks(options.callbacks,context);labelColors.push(scoped.labelColor.call(me,context));labelPointStyles.push(scoped.labelPointStyle.call(me,context));labelTextColors.push(scoped.labelTextColor.call(me,context));});me.labelColors=labelColors;me.labelPointStyles=labelPointStyles;me.labelTextColors=labelTextColors;me.dataPoints=tooltipItems;return tooltipItems;} -update(changed,replay){const me=this;const options=me.options.setContext(me.getContext());const active=me._active;let properties;let tooltipItems=[];if(!active.length){if(me.opacity!==0){properties={opacity:0};}}else{const position=positioners[options.position].call(me,active,me._eventPosition);tooltipItems=me._createItems(options);me.title=me.getTitle(tooltipItems,options);me.beforeBody=me.getBeforeBody(tooltipItems,options);me.body=me.getBody(tooltipItems,options);me.afterBody=me.getAfterBody(tooltipItems,options);me.footer=me.getFooter(tooltipItems,options);const size=me._size=getTooltipSize(me,options);const positionAndSize=Object.assign({},position,size);const alignment=determineAlignment(me._chart,options,positionAndSize);const backgroundPoint=getBackgroundPoint(options,positionAndSize,alignment,me._chart);me.xAlign=alignment.xAlign;me.yAlign=alignment.yAlign;properties={opacity:1,x:backgroundPoint.x,y:backgroundPoint.y,width:size.width,height:size.height,caretX:position.x,caretY:position.y};} -me._tooltipItems=tooltipItems;me.$context=undefined;if(properties){me._resolveAnimations().update(me,properties);} -if(changed&&options.external){options.external.call(me,{chart:me._chart,tooltip:me,replay});}} +each(tooltipItems,(context)=>{const scoped=overrideCallbacks(options.callbacks,context);labelColors.push(scoped.labelColor.call(this,context));labelPointStyles.push(scoped.labelPointStyle.call(this,context));labelTextColors.push(scoped.labelTextColor.call(this,context));});this.labelColors=labelColors;this.labelPointStyles=labelPointStyles;this.labelTextColors=labelTextColors;this.dataPoints=tooltipItems;return tooltipItems;} +update(changed,replay){const options=this.options.setContext(this.getContext());const active=this._active;let properties;let tooltipItems=[];if(!active.length){if(this.opacity!==0){properties={opacity:0};}}else{const position=positioners[options.position].call(this,active,this._eventPosition);tooltipItems=this._createItems(options);this.title=this.getTitle(tooltipItems,options);this.beforeBody=this.getBeforeBody(tooltipItems,options);this.body=this.getBody(tooltipItems,options);this.afterBody=this.getAfterBody(tooltipItems,options);this.footer=this.getFooter(tooltipItems,options);const size=this._size=getTooltipSize(this,options);const positionAndSize=Object.assign({},position,size);const alignment=determineAlignment(this._chart,options,positionAndSize);const backgroundPoint=getBackgroundPoint(options,positionAndSize,alignment,this._chart);this.xAlign=alignment.xAlign;this.yAlign=alignment.yAlign;properties={opacity:1,x:backgroundPoint.x,y:backgroundPoint.y,width:size.width,height:size.height,caretX:position.x,caretY:position.y};} +this._tooltipItems=tooltipItems;this.$context=undefined;if(properties){this._resolveAnimations().update(this,properties);} +if(changed&&options.external){options.external.call(this,{chart:this._chart,tooltip:this,replay});}} drawCaret(tooltipPoint,ctx,size,options){const caretPosition=this.getCaretPosition(tooltipPoint,size,options);ctx.lineTo(caretPosition.x1,caretPosition.y1);ctx.lineTo(caretPosition.x2,caretPosition.y2);ctx.lineTo(caretPosition.x3,caretPosition.y3);} -getCaretPosition(tooltipPoint,size,options){const{xAlign,yAlign}=this;const{cornerRadius,caretSize}=options;const{x:ptX,y:ptY}=tooltipPoint;const{width,height}=size;let x1,x2,x3,y1,y2,y3;if(yAlign==='center'){y2=ptY+(height/2);if(xAlign==='left'){x1=ptX;x2=x1-caretSize;y1=y2+caretSize;y3=y2-caretSize;}else{x1=ptX+width;x2=x1+caretSize;y1=y2-caretSize;y3=y2+caretSize;} -x3=x1;}else{if(xAlign==='left'){x2=ptX+cornerRadius+(caretSize);}else if(xAlign==='right'){x2=ptX+width-cornerRadius-caretSize;}else{x2=this.caretX;} +getCaretPosition(tooltipPoint,size,options){const{xAlign,yAlign}=this;const{caretSize,cornerRadius}=options;const{topLeft,topRight,bottomLeft,bottomRight}=toTRBLCorners(cornerRadius);const{x:ptX,y:ptY}=tooltipPoint;const{width,height}=size;let x1,x2,x3,y1,y2,y3;if(yAlign==='center'){y2=ptY+(height/2);if(xAlign==='left'){x1=ptX;x2=x1-caretSize;y1=y2+caretSize;y3=y2-caretSize;}else{x1=ptX+width;x2=x1+caretSize;y1=y2-caretSize;y3=y2+caretSize;} +x3=x1;}else{if(xAlign==='left'){x2=ptX+Math.max(topLeft,bottomLeft)+(caretSize);}else if(xAlign==='right'){x2=ptX+width-Math.max(topRight,bottomRight)-caretSize;}else{x2=this.caretX;} if(yAlign==='top'){y1=ptY;y2=y1-caretSize;x1=x2-caretSize;x3=x2+caretSize;}else{y1=ptY+height;y2=y1+caretSize;x1=x2+caretSize;x3=x2-caretSize;} y3=y1;} return{x1,x2,x3,y1,y2,y3};} -drawTitle(pt,ctx,options){const me=this;const title=me.title;const length=title.length;let titleFont,titleSpacing,i;if(length){const rtlHelper=getRtlAdapter(options.rtl,me.x,me.width);pt.x=getAlignedX(me,options.titleAlign,options);ctx.textAlign=rtlHelper.textAlign(options.titleAlign);ctx.textBaseline='middle';titleFont=toFont(options.titleFont);titleSpacing=options.titleSpacing;ctx.fillStyle=options.titleColor;ctx.font=titleFont.string;for(i=0;iv!==0)){ctx.beginPath();ctx.fillStyle=options.multiKeyBackground;addRoundedRectPath(ctx,{x:outerX,y:colorY,w:boxWidth,h:boxHeight,radius:borderRadius,});ctx.fill();ctx.stroke();ctx.fillStyle=labelColors.backgroundColor;ctx.beginPath();addRoundedRectPath(ctx,{x:innerX,y:colorY+1,w:boxWidth-2,h:boxHeight-2,radius:borderRadius,});ctx.fill();}else{ctx.fillStyle=options.multiKeyBackground;ctx.fillRect(outerX,colorY,boxWidth,boxHeight);ctx.strokeRect(outerX,colorY,boxWidth,boxHeight);ctx.fillStyle=labelColors.backgroundColor;ctx.fillRect(innerX,colorY+1,boxWidth-2,boxHeight-2);}} -ctx.fillStyle=me.labelTextColors[i];} -drawBody(pt,ctx,options){const me=this;const{body}=me;const{bodySpacing,bodyAlign,displayColors,boxHeight,boxWidth}=options;const bodyFont=toFont(options.bodyFont);let bodyLineHeight=bodyFont.lineHeight;let xLinePadding=0;const rtlHelper=getRtlAdapter(options.rtl,me.x,me.width);const fillLineOfText=function(line){ctx.fillText(line,rtlHelper.x(pt.x+xLinePadding),pt.y+bodyLineHeight/2);pt.y+=bodyLineHeight+bodySpacing;};const bodyAlignForCalculation=rtlHelper.textAlign(bodyAlign);let bodyItem,textColor,lines,i,j,ilen,jlen;ctx.textAlign=bodyAlign;ctx.textBaseline='middle';ctx.font=bodyFont.string;pt.x=getAlignedX(me,bodyAlignForCalculation,options);ctx.fillStyle=options.bodyColor;each(me.beforeBody,fillLineOfText);xLinePadding=displayColors&&bodyAlignForCalculation!=='right'?bodyAlign==='center'?(boxWidth/2+1):(boxWidth+2):0;for(i=0,ilen=body.length;iv!==0)){ctx.beginPath();ctx.fillStyle=options.multiKeyBackground;addRoundedRectPath(ctx,{x:outerX,y:colorY,w:boxWidth,h:boxHeight,radius:borderRadius,});ctx.fill();ctx.stroke();ctx.fillStyle=labelColors.backgroundColor;ctx.beginPath();addRoundedRectPath(ctx,{x:innerX,y:colorY+1,w:boxWidth-2,h:boxHeight-2,radius:borderRadius,});ctx.fill();}else{ctx.fillStyle=options.multiKeyBackground;ctx.fillRect(outerX,colorY,boxWidth,boxHeight);ctx.strokeRect(outerX,colorY,boxWidth,boxHeight);ctx.fillStyle=labelColors.backgroundColor;ctx.fillRect(innerX,colorY+1,boxWidth-2,boxHeight-2);}} +ctx.fillStyle=this.labelTextColors[i];} +drawBody(pt,ctx,options){const{body}=this;const{bodySpacing,bodyAlign,displayColors,boxHeight,boxWidth,boxPadding}=options;const bodyFont=toFont(options.bodyFont);let bodyLineHeight=bodyFont.lineHeight;let xLinePadding=0;const rtlHelper=getRtlAdapter(options.rtl,this.x,this.width);const fillLineOfText=function(line){ctx.fillText(line,rtlHelper.x(pt.x+xLinePadding),pt.y+bodyLineHeight/2);pt.y+=bodyLineHeight+bodySpacing;};const bodyAlignForCalculation=rtlHelper.textAlign(bodyAlign);let bodyItem,textColor,lines,i,j,ilen,jlen;ctx.textAlign=bodyAlign;ctx.textBaseline='middle';ctx.font=bodyFont.string;pt.x=getAlignedX(this,bodyAlignForCalculation,options);ctx.fillStyle=options.bodyColor;each(this.beforeBody,fillLineOfText);xLinePadding=displayColors&&bodyAlignForCalculation!=='right'?bodyAlign==='center'?(boxWidth/2+boxPadding):(boxWidth+2+boxPadding):0;for(i=0,ilen=body.length;i0){ctx.stroke();}} -_updateAnimationTarget(options){const me=this;const chart=me._chart;const anims=me.$animations;const animX=anims&&anims.x;const animY=anims&&anims.y;if(animX||animY){const position=positioners[options.position].call(me,me._active,me._eventPosition);if(!position){return;} -const size=me._size=getTooltipSize(me,options);const positionAndSize=Object.assign({},position,me._size);const alignment=determineAlignment(chart,options,positionAndSize);const point=getBackgroundPoint(options,positionAndSize,alignment,chart);if(animX._to!==point.x||animY._to!==point.y){me.xAlign=alignment.xAlign;me.yAlign=alignment.yAlign;me.width=size.width;me.height=size.height;me.caretX=position.x;me.caretY=position.y;me._resolveAnimations().update(me,point);}}} -draw(ctx){const me=this;const options=me.options.setContext(me.getContext());let opacity=me.opacity;if(!opacity){return;} -me._updateAnimationTarget(options);const tooltipSize={width:me.width,height:me.height};const pt={x:me.x,y:me.y};opacity=Math.abs(opacity)<1e-3?0:opacity;const padding=toPadding(options.padding);const hasTooltipContent=me.title.length||me.beforeBody.length||me.body.length||me.afterBody.length||me.footer.length;if(options.enabled&&hasTooltipContent){ctx.save();ctx.globalAlpha=opacity;me.drawBackground(pt,ctx,tooltipSize,options);overrideTextDirection(ctx,options.textDirection);pt.y+=padding.top;me.drawTitle(pt,ctx,options);me.drawBody(pt,ctx,options);me.drawFooter(pt,ctx,options);restoreTextDirection(ctx,options.textDirection);ctx.restore();}} +xLinePadding=0;bodyLineHeight=bodyFont.lineHeight;each(this.afterBody,fillLineOfText);pt.y-=bodySpacing;} +drawFooter(pt,ctx,options){const footer=this.footer;const length=footer.length;let footerFont,i;if(length){const rtlHelper=getRtlAdapter(options.rtl,this.x,this.width);pt.x=getAlignedX(this,options.footerAlign,options);pt.y+=options.footerMarginTop;ctx.textAlign=rtlHelper.textAlign(options.footerAlign);ctx.textBaseline='middle';footerFont=toFont(options.footerFont);ctx.fillStyle=options.footerColor;ctx.font=footerFont.string;for(i=0;i0){ctx.stroke();}} +_updateAnimationTarget(options){const chart=this._chart;const anims=this.$animations;const animX=anims&&anims.x;const animY=anims&&anims.y;if(animX||animY){const position=positioners[options.position].call(this,this._active,this._eventPosition);if(!position){return;} +const size=this._size=getTooltipSize(this,options);const positionAndSize=Object.assign({},position,this._size);const alignment=determineAlignment(chart,options,positionAndSize);const point=getBackgroundPoint(options,positionAndSize,alignment,chart);if(animX._to!==point.x||animY._to!==point.y){this.xAlign=alignment.xAlign;this.yAlign=alignment.yAlign;this.width=size.width;this.height=size.height;this.caretX=position.x;this.caretY=position.y;this._resolveAnimations().update(this,point);}}} +draw(ctx){const options=this.options.setContext(this.getContext());let opacity=this.opacity;if(!opacity){return;} +this._updateAnimationTarget(options);const tooltipSize={width:this.width,height:this.height};const pt={x:this.x,y:this.y};opacity=Math.abs(opacity)<1e-3?0:opacity;const padding=toPadding(options.padding);const hasTooltipContent=this.title.length||this.beforeBody.length||this.body.length||this.afterBody.length||this.footer.length;if(options.enabled&&hasTooltipContent){ctx.save();ctx.globalAlpha=opacity;this.drawBackground(pt,ctx,tooltipSize,options);overrideTextDirection(ctx,options.textDirection);pt.y+=padding.top;this.drawTitle(pt,ctx,options);this.drawBody(pt,ctx,options);this.drawFooter(pt,ctx,options);restoreTextDirection(ctx,options.textDirection);ctx.restore();}} getActiveElements(){return this._active||[];} -setActiveElements(activeElements,eventPosition){const me=this;const lastActive=me._active;const active=activeElements.map(({datasetIndex,index})=>{const meta=me._chart.getDatasetMeta(datasetIndex);if(!meta){throw new Error('Cannot find a dataset at index '+datasetIndex);} -return{datasetIndex,element:meta.data[index],index,};});const changed=!_elementsEqual(lastActive,active);const positionChanged=me._positionChanged(active,eventPosition);if(changed||positionChanged){me._active=active;me._eventPosition=eventPosition;me.update(true);}} -handleEvent(e,replay){const me=this;const options=me.options;const lastActive=me._active||[];let changed=false;let active=[];if(e.type!=='mouseout'){active=me._chart.getElementsAtEventForMode(e,options.mode,options,replay);if(options.reverse){active.reverse();}} -const positionChanged=me._positionChanged(active,e);changed=replay||!_elementsEqual(active,lastActive)||positionChanged;if(changed){me._active=active;if(options.enabled||options.external){me._eventPosition={x:e.x,y:e.y};me.update(true,replay);}} +setActiveElements(activeElements,eventPosition){const lastActive=this._active;const active=activeElements.map(({datasetIndex,index})=>{const meta=this._chart.getDatasetMeta(datasetIndex);if(!meta){throw new Error('Cannot find a dataset at index '+datasetIndex);} +return{datasetIndex,element:meta.data[index],index,};});const changed=!_elementsEqual(lastActive,active);const positionChanged=this._positionChanged(active,eventPosition);if(changed||positionChanged){this._active=active;this._eventPosition=eventPosition;this.update(true);}} +handleEvent(e,replay){const options=this.options;const lastActive=this._active||[];let changed=false;let active=[];if(e.type!=='mouseout'){active=this._chart.getElementsAtEventForMode(e,options.mode,options,replay);if(options.reverse){active.reverse();}} +const positionChanged=this._positionChanged(active,e);changed=replay||!_elementsEqual(active,lastActive)||positionChanged;if(changed){this._active=active;if(options.enabled||options.external){this._eventPosition={x:e.x,y:e.y};this.update(true,replay);}} return changed;} _positionChanged(active,e){const{caretX,caretY,options}=this;const position=positioners[options.position].call(this,active,e);return position!==false&&(caretX!==position.x||caretY!==position.y);}} Tooltip.positioners=positioners;var plugin_tooltip={id:'tooltip',_element:Tooltip,positioners,afterInit(chart,_args,options){if(options){chart.tooltip=new Tooltip({_chart:chart,options});}},beforeUpdate(chart,_args,options){if(chart.tooltip){chart.tooltip.initialize(options);}},reset(chart,_args,options){if(chart.tooltip){chart.tooltip.initialize(options);}},afterDraw(chart){const tooltip=chart.tooltip;const args={tooltip};if(chart.notifyPlugins('beforeTooltipDraw',args)===false){return;} if(tooltip){tooltip.draw(chart.ctx);} -chart.notifyPlugins('afterTooltipDraw',args);},afterEvent(chart,args){if(chart.tooltip){const useFinalPosition=args.replay;if(chart.tooltip.handleEvent(args.event,useFinalPosition)){args.changed=true;}}},defaults:{enabled:true,external:null,position:'average',backgroundColor:'rgba(0,0,0,0.8)',titleColor:'#fff',titleFont:{weight:'bold',},titleSpacing:2,titleMarginBottom:6,titleAlign:'left',bodyColor:'#fff',bodySpacing:2,bodyFont:{},bodyAlign:'left',footerColor:'#fff',footerSpacing:2,footerMarginTop:6,footerFont:{weight:'bold',},footerAlign:'left',padding:6,caretPadding:2,caretSize:5,cornerRadius:6,boxHeight:(ctx,opts)=>opts.bodyFont.size,boxWidth:(ctx,opts)=>opts.bodyFont.size,multiKeyBackground:'#fff',displayColors:true,borderColor:'rgba(0,0,0,0)',borderWidth:0,animation:{duration:400,easing:'easeOutQuart',},animations:{numbers:{type:'number',properties:['x','y','width','height','caretX','caretY'],},opacity:{easing:'linear',duration:200}},callbacks:{beforeTitle:noop,title(tooltipItems){if(tooltipItems.length>0){const item=tooltipItems[0];const labels=item.chart.data.labels;const labelCount=labels?labels.length:0;if(this&&this.options&&this.options.mode==='dataset'){return item.dataset.label||'';}else if(item.label){return item.label;}else if(labelCount>0&&item.dataIndexopts.bodyFont.size,boxWidth:(ctx,opts)=>opts.bodyFont.size,multiKeyBackground:'#fff',displayColors:true,boxPadding:0,borderColor:'rgba(0,0,0,0)',borderWidth:0,animation:{duration:400,easing:'easeOutQuart',},animations:{numbers:{type:'number',properties:['x','y','width','height','caretX','caretY'],},opacity:{easing:'linear',duration:200}},callbacks:{beforeTitle:noop,title(tooltipItems){if(tooltipItems.length>0){const item=tooltipItems[0];const labels=item.chart.data.labels;const labelCount=labels?labels.length:0;if(this&&this.options&&this.options.mode==='dataset'){return item.dataset.label||'';}else if(item.label){return item.label;}else if(labelCount>0&&item.dataIndexname!=='filter'&&name!=='itemSort'&&name!=='external',_indexable:false,callbacks:{_scriptable:false,_indexable:false,},animation:{_fallback:false},animations:{_fallback:'animation'}},additionalOptionScopes:['interaction']};var plugins=Object.freeze({__proto__:null,Decimation:plugin_decimation,Filler:plugin_filler,Legend:plugin_legend,SubTitle:plugin_subtitle,Title:plugin_title,Tooltip:plugin_tooltip});const addIfString=(labels,raw,index)=>typeof raw==='string'?labels.push(raw)-1:isNaN(raw)?null:index;function findOrAddLabel(labels,raw,index){const first=labels.indexOf(raw);if(first===-1){return addIfString(labels,raw,index);} +return label;},labelColor(tooltipItem){const meta=tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);const options=meta.controller.getStyle(tooltipItem.dataIndex);return{borderColor:options.borderColor,backgroundColor:options.backgroundColor,borderWidth:options.borderWidth,borderDash:options.borderDash,borderDashOffset:options.borderDashOffset,borderRadius:0,};},labelTextColor(){return this.options.bodyColor;},labelPointStyle(tooltipItem){const meta=tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);const options=meta.controller.getStyle(tooltipItem.dataIndex);return{pointStyle:options.pointStyle,rotation:options.rotation,};},afterLabel:noop,afterBody:noop,beforeFooter:noop,footer:noop,afterFooter:noop}},defaultRoutes:{bodyFont:'font',footerFont:'font',titleFont:'font'},descriptors:{_scriptable:(name)=>name!=='filter'&&name!=='itemSort'&&name!=='external',_indexable:false,callbacks:{_scriptable:false,_indexable:false,},animation:{_fallback:false},animations:{_fallback:'animation'}},additionalOptionScopes:['interaction']};var plugins=Object.freeze({__proto__:null,Decimation:plugin_decimation,Filler:plugin_filler,Legend:plugin_legend,SubTitle:plugin_subtitle,Title:plugin_title,Tooltip:plugin_tooltip});const addIfString=(labels,raw,index,addedLabels)=>{if(typeof raw==='string'){index=labels.push(raw)-1;addedLabels.unshift({index,label:raw});}else if(isNaN(raw)){index=null;} +return index;};function findOrAddLabel(labels,raw,index,addedLabels){const first=labels.indexOf(raw);if(first===-1){return addIfString(labels,raw,index,addedLabels);} const last=labels.lastIndexOf(raw);return first!==last?index:first;} -const validIndex=(index,max)=>index===null?null:_limitValue(Math.round(index),0,max);class CategoryScale extends Scale{constructor(cfg){super(cfg);this._startValue=undefined;this._valueRange=0;} +const validIndex=(index,max)=>index===null?null:_limitValue(Math.round(index),0,max);class CategoryScale extends Scale{constructor(cfg){super(cfg);this._startValue=undefined;this._valueRange=0;this._addedLabels=[];} +init(scaleOptions){const added=this._addedLabels;if(added.length){const labels=this.getLabels();for(const{index,label}of added){if(labels[index]===label){labels.splice(index,1);}} +this._addedLabels=[];} +super.init(scaleOptions);} parse(raw,index){if(isNullOrUndef(raw)){return null;} -const labels=this.getLabels();index=isFinite(index)&&labels[index]===raw?index:findOrAddLabel(labels,raw,valueOrDefault(index,raw));return validIndex(index,labels.length-1);} -determineDataLimits(){const me=this;const{minDefined,maxDefined}=me.getUserBounds();let{min,max}=me.getMinMax(true);if(me.options.bounds==='ticks'){if(!minDefined){min=0;} -if(!maxDefined){max=me.getLabels().length-1;}} -me.min=min;me.max=max;} -buildTicks(){const me=this;const min=me.min;const max=me.max;const offset=me.options.offset;const ticks=[];let labels=me.getLabels();labels=(min===0&&max===labels.length-1)?labels:labels.slice(min,max+1);me._valueRange=Math.max(labels.length-(offset?0:1),1);me._startValue=me.min-(offset?0.5:0);for(let value=min;value<=max;value++){ticks.push({value});} +const labels=this.getLabels();index=isFinite(index)&&labels[index]===raw?index:findOrAddLabel(labels,raw,valueOrDefault(index,raw),this._addedLabels);return validIndex(index,labels.length-1);} +determineDataLimits(){const{minDefined,maxDefined}=this.getUserBounds();let{min,max}=this.getMinMax(true);if(this.options.bounds==='ticks'){if(!minDefined){min=0;} +if(!maxDefined){max=this.getLabels().length-1;}} +this.min=min;this.max=max;} +buildTicks(){const min=this.min;const max=this.max;const offset=this.options.offset;const ticks=[];let labels=this.getLabels();labels=(min===0&&max===labels.length-1)?labels:labels.slice(min,max+1);this._valueRange=Math.max(labels.length-(offset?0:1),1);this._startValue=this.min-(offset?0.5:0);for(let value=min;value<=max;value++){ticks.push({value});} return ticks;} -getLabelForValue(value){const me=this;const labels=me.getLabels();if(value>=0&&value=0&&valueticks.length-1){return null;} -return me.getPixelForValue(ticks[index].value);} -getValueForPixel(pixel){const me=this;return Math.round(me._startValue+me.getDecimalForPixel(pixel)*me._valueRange);} +configure(){super.configure();if(!this.isHorizontal()){this._reversePixels=!this._reversePixels;}} +getPixelForValue(value){if(typeof value!=='number'){value=this.parse(value);} +return value===null?NaN:this.getPixelForDecimal((value-this._startValue)/this._valueRange);} +getPixelForTick(index){const ticks=this.ticks;if(index<0||index>ticks.length-1){return null;} +return this.getPixelForValue(ticks[index].value);} +getValueForPixel(pixel){return Math.round(this._startValue+this.getDecimalForPixel(pixel)*this._valueRange);} getBasePixel(){return this.bottom;}} CategoryScale.id='category';CategoryScale.defaults={ticks:{callback:CategoryScale.prototype.getLabelForValue}};function generateTicks$1(generationOptions,dataRange){const ticks=[];const MIN_SPACING=1e-14;const{bounds,step,min,max,precision,count,maxTicks,maxDigits,includeBounds}=generationOptions;const unit=step||1;const maxSpaces=maxTicks-1;const{min:rmin,max:rmax}=dataRange;const minDefined=!isNullOrUndef(min);const maxDefined=!isNullOrUndef(max);const countDefined=!isNullOrUndef(count);const minSpacing=(rmax-rmin)/(maxDigits+1);let spacing=niceNum((rmax-rmin)/maxSpaces/unit)*unit;let factor,niceMin,niceMax,numSpaces;if(spacingmaxSpaces){spacing=niceNum(numSpaces*spacing/maxSpaces/unit)*unit;} @@ -2095,29 +2126,29 @@ if(minDefined&&maxDefined&&step&&almostWhole((max-min)/step,spacing/1000)){numSp const decimalPlaces=Math.max(_decimalPlaces(spacing),_decimalPlaces(niceMin));factor=Math.pow(10,isNullOrUndef(precision)?decimalPlaces:precision);niceMin=Math.round(niceMin*factor)/factor;niceMax=Math.round(niceMax*factor)/factor;let j=0;if(minDefined){if(includeBounds&&niceMin!==min){ticks.push({value:min});if(niceMin(min=minDefined?min:v);const setMax=v=>(max=maxDefined?max:v);if(beginAtZero){const minSign=sign(min);const maxSign=sign(max);if(minSign<0&&maxSign<0){setMax(0);}else if(minSign>0&&maxSign>0){setMin(0);}} +handleTickRangeOptions(){const{beginAtZero}=this.options;const{minDefined,maxDefined}=this.getUserBounds();let{min,max}=this;const setMin=v=>(min=minDefined?min:v);const setMax=v=>(max=maxDefined?max:v);if(beginAtZero){const minSign=sign(min);const maxSign=sign(max);if(minSign<0&&maxSign<0){setMax(0);}else if(minSign>0&&maxSign>0){setMin(0);}} if(min===max){let offset=1;if(max>=Number.MAX_SAFE_INTEGER||min<=Number.MIN_SAFE_INTEGER){offset=Math.abs(max*0.05);} setMax(max+offset);if(!beginAtZero){setMin(min-offset);}} -me.min=min;me.max=max;} -getTickLimit(){const me=this;const tickOpts=me.options.ticks;let{maxTicksLimit,stepSize}=tickOpts;let maxTicks;if(stepSize){maxTicks=Math.ceil(me.max/stepSize)-Math.floor(me.min/stepSize)+1;}else{maxTicks=me.computeTickLimit();maxTicksLimit=maxTicksLimit||11;} +this.min=min;this.max=max;} +getTickLimit(){const tickOpts=this.options.ticks;let{maxTicksLimit,stepSize}=tickOpts;let maxTicks;if(stepSize){maxTicks=Math.ceil(this.max/stepSize)-Math.floor(this.min/stepSize)+1;if(maxTicks>1000){console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`);maxTicks=1000;}}else{maxTicks=this.computeTickLimit();maxTicksLimit=maxTicksLimit||11;} if(maxTicksLimit){maxTicks=Math.min(maxTicksLimit,maxTicks);} return maxTicks;} computeTickLimit(){return Number.POSITIVE_INFINITY;} -buildTicks(){const me=this;const opts=me.options;const tickOpts=opts.ticks;let maxTicks=me.getTickLimit();maxTicks=Math.max(2,maxTicks);const numericGeneratorOptions={maxTicks,bounds:opts.bounds,min:opts.min,max:opts.max,precision:tickOpts.precision,step:tickOpts.stepSize,count:tickOpts.count,maxDigits:me._maxDigits(),horizontal:me.isHorizontal(),minRotation:tickOpts.minRotation||0,includeBounds:tickOpts.includeBounds!==false};const dataRange=me._range||me;const ticks=generateTicks$1(numericGeneratorOptions,dataRange);if(opts.bounds==='ticks'){_setMinAndMaxByKey(ticks,me,'value');} -if(opts.reverse){ticks.reverse();me.start=me.max;me.end=me.min;}else{me.start=me.min;me.end=me.max;} +buildTicks(){const opts=this.options;const tickOpts=opts.ticks;let maxTicks=this.getTickLimit();maxTicks=Math.max(2,maxTicks);const numericGeneratorOptions={maxTicks,bounds:opts.bounds,min:opts.min,max:opts.max,precision:tickOpts.precision,step:tickOpts.stepSize,count:tickOpts.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:tickOpts.minRotation||0,includeBounds:tickOpts.includeBounds!==false};const dataRange=this._range||this;const ticks=generateTicks$1(numericGeneratorOptions,dataRange);if(opts.bounds==='ticks'){_setMinAndMaxByKey(ticks,this,'value');} +if(opts.reverse){ticks.reverse();this.start=this.max;this.end=this.min;}else{this.start=this.min;this.end=this.max;} return ticks;} -configure(){const me=this;const ticks=me.ticks;let start=me.min;let end=me.max;super.configure();if(me.options.offset&&ticks.length){const offset=(end-start)/Math.max(ticks.length-1,1)/2;start-=offset;end+=offset;} -me._startValue=start;me._endValue=end;me._valueRange=end-start;} -getLabelForValue(value){return formatNumber(value,this.chart.options.locale);}} -class LinearScale extends LinearScaleBase{determineDataLimits(){const me=this;const{min,max}=me.getMinMax(true);me.min=isNumberFinite(min)?min:0;me.max=isNumberFinite(max)?max:1;me.handleTickRangeOptions();} -computeTickLimit(){const me=this;const horizontal=me.isHorizontal();const length=horizontal?me.width:me.height;const minRotation=toRadians(me.options.ticks.minRotation);const ratio=(horizontal?Math.sin(minRotation):Math.cos(minRotation))||0.001;const tickFont=me._resolveTickFontOptions(0);return Math.ceil(length/Math.min(40,tickFont.lineHeight/ratio));} +configure(){const ticks=this.ticks;let start=this.min;let end=this.max;super.configure();if(this.options.offset&&ticks.length){const offset=(end-start)/Math.max(ticks.length-1,1)/2;start-=offset;end+=offset;} +this._startValue=start;this._endValue=end;this._valueRange=end-start;} +getLabelForValue(value){return formatNumber(value,this.chart.options.locale,this.options.ticks.format);}} +class LinearScale extends LinearScaleBase{determineDataLimits(){const{min,max}=this.getMinMax(true);this.min=isNumberFinite(min)?min:0;this.max=isNumberFinite(max)?max:1;this.handleTickRangeOptions();} +computeTickLimit(){const horizontal=this.isHorizontal();const length=horizontal?this.width:this.height;const minRotation=toRadians(this.options.ticks.minRotation);const ratio=(horizontal?Math.sin(minRotation):Math.cos(minRotation))||0.001;const tickFont=this._resolveTickFontOptions(0);return Math.ceil(length/Math.min(40,tickFont.lineHeight/ratio));} getPixelForValue(value){return value===null?NaN:this.getPixelForDecimal((value-this._startValue)/this._valueRange);} getValueForPixel(pixel){return this._startValue+this.getDecimalForPixel(pixel)*this._valueRange;}} LinearScale.id='linear';LinearScale.defaults={ticks:{callback:Ticks.formatters.numeric}};function isMajor(tickVal){const remain=tickVal/(Math.pow(10,Math.floor(log10(tickVal))));return remain===1;} @@ -2126,22 +2157,22 @@ tickVal=Math.round(significand*Math.pow(10,exp)*precision)/precision;}while(exp< class LogarithmicScale extends Scale{constructor(cfg){super(cfg);this.start=undefined;this.end=undefined;this._startValue=undefined;this._valueRange=0;} parse(raw,index){const value=LinearScaleBase.prototype.parse.apply(this,[raw,index]);if(value===0){this._zero=true;return undefined;} return isNumberFinite(value)&&value>0?value:null;} -determineDataLimits(){const me=this;const{min,max}=me.getMinMax(true);me.min=isNumberFinite(min)?Math.max(0,min):null;me.max=isNumberFinite(max)?Math.max(0,max):null;if(me.options.beginAtZero){me._zero=true;} -me.handleTickRangeOptions();} -handleTickRangeOptions(){const me=this;const{minDefined,maxDefined}=me.getUserBounds();let min=me.min;let max=me.max;const setMin=v=>(min=minDefined?min:v);const setMax=v=>(max=maxDefined?max:v);const exp=(v,m)=>Math.pow(10,Math.floor(log10(v))+m);if(min===max){if(min<=0){setMin(1);setMax(10);}else{setMin(exp(min,-1));setMax(exp(max,+1));}} +determineDataLimits(){const{min,max}=this.getMinMax(true);this.min=isNumberFinite(min)?Math.max(0,min):null;this.max=isNumberFinite(max)?Math.max(0,max):null;if(this.options.beginAtZero){this._zero=true;} +this.handleTickRangeOptions();} +handleTickRangeOptions(){const{minDefined,maxDefined}=this.getUserBounds();let min=this.min;let max=this.max;const setMin=v=>(min=minDefined?min:v);const setMax=v=>(max=maxDefined?max:v);const exp=(v,m)=>Math.pow(10,Math.floor(log10(v))+m);if(min===max){if(min<=0){setMin(1);setMax(10);}else{setMin(exp(min,-1));setMax(exp(max,+1));}} if(min<=0){setMin(exp(max,-1));} if(max<=0){setMax(exp(min,+1));} -if(me._zero&&me.min!==me._suggestedMin&&min===exp(me.min,0)){setMin(exp(min,-1));} -me.min=min;me.max=max;} -buildTicks(){const me=this;const opts=me.options;const generationOptions={min:me._userMin,max:me._userMax};const ticks=generateTicks(generationOptions,me);if(opts.bounds==='ticks'){_setMinAndMaxByKey(ticks,me,'value');} -if(opts.reverse){ticks.reverse();me.start=me.max;me.end=me.min;}else{me.start=me.min;me.end=me.max;} +if(this._zero&&this.min!==this._suggestedMin&&min===exp(this.min,0)){setMin(exp(min,-1));} +this.min=min;this.max=max;} +buildTicks(){const opts=this.options;const generationOptions={min:this._userMin,max:this._userMax};const ticks=generateTicks(generationOptions,this);if(opts.bounds==='ticks'){_setMinAndMaxByKey(ticks,this,'value');} +if(opts.reverse){ticks.reverse();this.start=this.max;this.end=this.min;}else{this.start=this.min;this.end=this.max;} return ticks;} -getLabelForValue(value){return value===undefined?'0':formatNumber(value,this.chart.options.locale);} -configure(){const me=this;const start=me.min;super.configure();me._startValue=log10(start);me._valueRange=log10(me.max)-log10(start);} -getPixelForValue(value){const me=this;if(value===undefined||value===0){value=me.min;} +getLabelForValue(value){return value===undefined?'0':formatNumber(value,this.chart.options.locale,this.options.ticks.format);} +configure(){const start=this.min;super.configure();this._startValue=log10(start);this._valueRange=log10(this.max)-log10(start);} +getPixelForValue(value){if(value===undefined||value===0){value=this.min;} if(value===null||isNaN(value)){return NaN;} -return me.getPixelForDecimal(value===me.min?0:(log10(value)-me._startValue)/me._valueRange);} -getValueForPixel(pixel){const me=this;const decimal=me.getDecimalForPixel(pixel);return Math.pow(10,me._startValue+decimal*me._valueRange);}} +return this.getPixelForDecimal(value===this.min?0:(log10(value)-this._startValue)/this._valueRange);} +getValueForPixel(pixel){const decimal=this.getDecimalForPixel(pixel);return Math.pow(10,this._startValue+decimal*this._valueRange);}} LogarithmicScale.id='logarithmic';LogarithmicScale.defaults={ticks:{callback:Ticks.formatters.logarithmic,major:{enabled:true}}};function getTickBackdropHeight(opts){const tickOpts=opts.ticks;if(tickOpts.display&&opts.display){const padding=toPadding(tickOpts.backdropPadding);return valueOrDefault(tickOpts.font&&tickOpts.font.size,defaults.font.size)+padding.height;} return 0;} function measureLabelSize(ctx,font,label){label=isArray(label)?label:[label];return{w:_longestText(ctx,font.string,label),h:label.length*font.lineHeight};} @@ -2166,36 +2197,36 @@ function pathRadiusLine(scale,radius,circular,labelCount){const{ctx}=scale;if(ci function drawRadiusLine(scale,gridLineOpts,radius,labelCount){const ctx=scale.ctx;const circular=gridLineOpts.circular;const{color,lineWidth}=gridLineOpts;if((!circular&&!labelCount)||!color||!lineWidth||radius<0){return;} ctx.save();ctx.strokeStyle=color;ctx.lineWidth=lineWidth;ctx.setLineDash(gridLineOpts.borderDash);ctx.lineDashOffset=gridLineOpts.borderDashOffset;ctx.beginPath();pathRadiusLine(scale,radius,circular,labelCount);ctx.closePath();ctx.stroke();ctx.restore();} function numberOrZero(param){return isNumber(param)?param:0;} -function createPointLabelContext(parent,index,label){return Object.assign(Object.create(parent),{label,index,type:'pointLabel'});} +function createPointLabelContext(parent,index,label){return createContext(parent,{label,index,type:'pointLabel'});} class RadialLinearScale extends LinearScaleBase{constructor(cfg){super(cfg);this.xCenter=undefined;this.yCenter=undefined;this.drawingArea=undefined;this._pointLabels=[];this._pointLabelItems=[];} -setDimensions(){const me=this;me.width=me.maxWidth;me.height=me.maxHeight;me.paddingTop=getTickBackdropHeight(me.options)/2;me.xCenter=Math.floor(me.width/2);me.yCenter=Math.floor((me.height-me.paddingTop)/2);me.drawingArea=Math.min(me.height-me.paddingTop,me.width)/2;} -determineDataLimits(){const me=this;const{min,max}=me.getMinMax(false);me.min=isNumberFinite(min)&&!isNaN(min)?min:0;me.max=isNumberFinite(max)&&!isNaN(max)?max:0;me.handleTickRangeOptions();} +setDimensions(){this.width=this.maxWidth;this.height=this.maxHeight;this.paddingTop=getTickBackdropHeight(this.options)/2;this.xCenter=Math.floor(this.width/2);this.yCenter=Math.floor((this.height-this.paddingTop)/2);this.drawingArea=Math.min(this.height-this.paddingTop,this.width)/2;} +determineDataLimits(){const{min,max}=this.getMinMax(false);this.min=isNumberFinite(min)&&!isNaN(min)?min:0;this.max=isNumberFinite(max)&&!isNaN(max)?max:0;this.handleTickRangeOptions();} computeTickLimit(){return Math.ceil(this.drawingArea/getTickBackdropHeight(this.options));} -generateTickLabels(ticks){const me=this;LinearScaleBase.prototype.generateTickLabels.call(me,ticks);me._pointLabels=me.getLabels().map((value,index)=>{const label=callback(me.options.pointLabels.callback,[value,index],me);return label||label===0?label:'';});} -fit(){const me=this;const opts=me.options;if(opts.display&&opts.pointLabels.display){fitWithPointLabels(me);}else{me.setCenterPoint(0,0,0,0);}} -_setReductions(largestPossibleRadius,furthestLimits,furthestAngles){const me=this;let radiusReductionLeft=furthestLimits.l/Math.sin(furthestAngles.l);let radiusReductionRight=Math.max(furthestLimits.r-me.width,0)/Math.sin(furthestAngles.r);let radiusReductionTop=-furthestLimits.t/Math.cos(furthestAngles.t);let radiusReductionBottom=-Math.max(furthestLimits.b-(me.height-me.paddingTop),0)/Math.cos(furthestAngles.b);radiusReductionLeft=numberOrZero(radiusReductionLeft);radiusReductionRight=numberOrZero(radiusReductionRight);radiusReductionTop=numberOrZero(radiusReductionTop);radiusReductionBottom=numberOrZero(radiusReductionBottom);me.drawingArea=Math.max(largestPossibleRadius/2,Math.min(Math.floor(largestPossibleRadius-(radiusReductionLeft+radiusReductionRight)/2),Math.floor(largestPossibleRadius-(radiusReductionTop+radiusReductionBottom)/2)));me.setCenterPoint(radiusReductionLeft,radiusReductionRight,radiusReductionTop,radiusReductionBottom);} -setCenterPoint(leftMovement,rightMovement,topMovement,bottomMovement){const me=this;const maxRight=me.width-rightMovement-me.drawingArea;const maxLeft=leftMovement+me.drawingArea;const maxTop=topMovement+me.drawingArea;const maxBottom=(me.height-me.paddingTop)-bottomMovement-me.drawingArea;me.xCenter=Math.floor(((maxLeft+maxRight)/2)+me.left);me.yCenter=Math.floor(((maxTop+maxBottom)/2)+me.top+me.paddingTop);} +generateTickLabels(ticks){LinearScaleBase.prototype.generateTickLabels.call(this,ticks);this._pointLabels=this.getLabels().map((value,index)=>{const label=callback(this.options.pointLabels.callback,[value,index],this);return label||label===0?label:'';});} +fit(){const opts=this.options;if(opts.display&&opts.pointLabels.display){fitWithPointLabels(this);}else{this.setCenterPoint(0,0,0,0);}} +_setReductions(largestPossibleRadius,furthestLimits,furthestAngles){let radiusReductionLeft=furthestLimits.l/Math.sin(furthestAngles.l);let radiusReductionRight=Math.max(furthestLimits.r-this.width,0)/Math.sin(furthestAngles.r);let radiusReductionTop=-furthestLimits.t/Math.cos(furthestAngles.t);let radiusReductionBottom=-Math.max(furthestLimits.b-(this.height-this.paddingTop),0)/Math.cos(furthestAngles.b);radiusReductionLeft=numberOrZero(radiusReductionLeft);radiusReductionRight=numberOrZero(radiusReductionRight);radiusReductionTop=numberOrZero(radiusReductionTop);radiusReductionBottom=numberOrZero(radiusReductionBottom);this.drawingArea=Math.max(largestPossibleRadius/2,Math.min(Math.floor(largestPossibleRadius-(radiusReductionLeft+radiusReductionRight)/2),Math.floor(largestPossibleRadius-(radiusReductionTop+radiusReductionBottom)/2)));this.setCenterPoint(radiusReductionLeft,radiusReductionRight,radiusReductionTop,radiusReductionBottom);} +setCenterPoint(leftMovement,rightMovement,topMovement,bottomMovement){const maxRight=this.width-rightMovement-this.drawingArea;const maxLeft=leftMovement+this.drawingArea;const maxTop=topMovement+this.drawingArea;const maxBottom=(this.height-this.paddingTop)-bottomMovement-this.drawingArea;this.xCenter=Math.floor(((maxLeft+maxRight)/2)+this.left);this.yCenter=Math.floor(((maxTop+maxBottom)/2)+this.top+this.paddingTop);} getIndexAngle(index){const angleMultiplier=TAU/this.getLabels().length;const startAngle=this.options.startAngle||0;return _normalizeAngle(index*angleMultiplier+toRadians(startAngle));} -getDistanceFromCenterForValue(value){const me=this;if(isNullOrUndef(value)){return NaN;} -const scalingFactor=me.drawingArea/(me.max-me.min);if(me.options.reverse){return(me.max-value)*scalingFactor;} -return(value-me.min)*scalingFactor;} +getDistanceFromCenterForValue(value){if(isNullOrUndef(value)){return NaN;} +const scalingFactor=this.drawingArea/(this.max-this.min);if(this.options.reverse){return(this.max-value)*scalingFactor;} +return(value-this.min)*scalingFactor;} getValueForDistanceFromCenter(distance){if(isNullOrUndef(distance)){return NaN;} -const me=this;const scaledDistance=distance/(me.drawingArea/(me.max-me.min));return me.options.reverse?me.max-scaledDistance:me.min+scaledDistance;} -getPointLabelContext(index){const me=this;const pointLabels=me._pointLabels||[];if(index>=0&&index=0&&index{if(index!==0){offset=me.getDistanceFromCenterForValue(tick.value);const optsAtIndex=grid.setContext(me.getContext(index-1));drawRadiusLine(me,optsAtIndex,offset,labelCount);}});} -if(angleLines.display){ctx.save();for(i=me.getLabels().length-1;i>=0;i--){const optsAtIndex=angleLines.setContext(me.getPointLabelContext(i));const{color,lineWidth}=optsAtIndex;if(!lineWidth||!color){continue;} -ctx.lineWidth=lineWidth;ctx.strokeStyle=color;ctx.setLineDash(optsAtIndex.borderDash);ctx.lineDashOffset=optsAtIndex.borderDashOffset;offset=me.getDistanceFromCenterForValue(opts.ticks.reverse?me.min:me.max);position=me.getPointPosition(i,offset);ctx.beginPath();ctx.moveTo(me.xCenter,me.yCenter);ctx.lineTo(position.x,position.y);ctx.stroke();} +drawBackground(){const{backgroundColor,grid:{circular}}=this.options;if(backgroundColor){const ctx=this.ctx;ctx.save();ctx.beginPath();pathRadiusLine(this,this.getDistanceFromCenterForValue(this._endValue),circular,this.getLabels().length);ctx.closePath();ctx.fillStyle=backgroundColor;ctx.fill();ctx.restore();}} +drawGrid(){const ctx=this.ctx;const opts=this.options;const{angleLines,grid}=opts;const labelCount=this.getLabels().length;let i,offset,position;if(opts.pointLabels.display){drawPointLabels(this,labelCount);} +if(grid.display){this.ticks.forEach((tick,index)=>{if(index!==0){offset=this.getDistanceFromCenterForValue(tick.value);const optsAtIndex=grid.setContext(this.getContext(index-1));drawRadiusLine(this,optsAtIndex,offset,labelCount);}});} +if(angleLines.display){ctx.save();for(i=this.getLabels().length-1;i>=0;i--){const optsAtIndex=angleLines.setContext(this.getPointLabelContext(i));const{color,lineWidth}=optsAtIndex;if(!lineWidth||!color){continue;} +ctx.lineWidth=lineWidth;ctx.strokeStyle=color;ctx.setLineDash(optsAtIndex.borderDash);ctx.lineDashOffset=optsAtIndex.borderDashOffset;offset=this.getDistanceFromCenterForValue(opts.ticks.reverse?this.min:this.max);position=this.getPointPosition(i,offset);ctx.beginPath();ctx.moveTo(this.xCenter,this.yCenter);ctx.lineTo(position.x,position.y);ctx.stroke();} ctx.restore();}} drawBorder(){} -drawLabels(){const me=this;const ctx=me.ctx;const opts=me.options;const tickOpts=opts.ticks;if(!tickOpts.display){return;} -const startAngle=me.getIndexAngle(0);let offset,width;ctx.save();ctx.translate(me.xCenter,me.yCenter);ctx.rotate(startAngle);ctx.textAlign='center';ctx.textBaseline='middle';me.ticks.forEach((tick,index)=>{if(index===0&&!opts.reverse){return;} -const optsAtIndex=tickOpts.setContext(me.getContext(index));const tickFont=toFont(optsAtIndex.font);offset=me.getDistanceFromCenterForValue(me.ticks[index].value);if(optsAtIndex.showLabelBackdrop){ctx.font=tickFont.string;width=ctx.measureText(tick.label).width;ctx.fillStyle=optsAtIndex.backdropColor;const padding=toPadding(optsAtIndex.backdropPadding);ctx.fillRect(-width/2-padding.left,-offset-tickFont.size/2-padding.top,width+padding.width,tickFont.size+padding.height);} +drawLabels(){const ctx=this.ctx;const opts=this.options;const tickOpts=opts.ticks;if(!tickOpts.display){return;} +const startAngle=this.getIndexAngle(0);let offset,width;ctx.save();ctx.translate(this.xCenter,this.yCenter);ctx.rotate(startAngle);ctx.textAlign='center';ctx.textBaseline='middle';this.ticks.forEach((tick,index)=>{if(index===0&&!opts.reverse){return;} +const optsAtIndex=tickOpts.setContext(this.getContext(index));const tickFont=toFont(optsAtIndex.font);offset=this.getDistanceFromCenterForValue(this.ticks[index].value);if(optsAtIndex.showLabelBackdrop){ctx.font=tickFont.string;width=ctx.measureText(tick.label).width;ctx.fillStyle=optsAtIndex.backdropColor;const padding=toPadding(optsAtIndex.backdropPadding);ctx.fillRect(-width/2-padding.left,-offset-tickFont.size/2-padding.top,width+padding.width,tickFont.size+padding.height);} renderText(ctx,tick.label,0,-offset,tickFont,{color:optsAtIndex.color,});});ctx.restore();} drawTitle(){}} RadialLinearScale.id='radialLinear';RadialLinearScale.defaults={display:true,animate:true,position:'chartArea',angleLines:{display:true,lineWidth:1,borderDash:[],borderDashOffset:0.0},grid:{circular:false},startAngle:0,ticks:{showLabelBackdrop:true,callback:Ticks.formatters.numeric},pointLabels:{backdropColor:undefined,backdropPadding:2,display:true,font:{size:10},callback(label){return label;},padding:5}};RadialLinearScale.defaultRoutes={'angleLines.color':'borderColor','pointLabels.color':'color','ticks.color':'color'};RadialLinearScale.descriptors={angleLines:{_fallback:'grid'}};const INTERVALS={millisecond:{common:true,size:1,steps:1000},second:{common:true,size:1000,steps:60},minute:{common:true,size:60000,steps:60},hour:{common:true,size:3600000,steps:24},day:{common:true,size:86400000,steps:30},week:{common:false,size:604800000,steps:4},month:{common:true,size:2.628e9,steps:12},quarter:{common:false,size:7.884e9,steps:4},year:{common:true,size:3.154e10}};const UNITS=(Object.keys(INTERVALS));function sorter(a,b){return a-b;} @@ -2220,55 +2251,55 @@ init(scaleOpts,opts){const time=scaleOpts.time||(scaleOpts.time={});const adapte parse(raw,index){if(raw===undefined){return null;} return parse(this,raw);} beforeLayout(){super.beforeLayout();this._cache={data:[],labels:[],all:[]};} -determineDataLimits(){const me=this;const options=me.options;const adapter=me._adapter;const unit=options.time.unit||'day';let{min,max,minDefined,maxDefined}=me.getUserBounds();function _applyBounds(bounds){if(!minDefined&&!isNaN(bounds.min)){min=Math.min(min,bounds.min);} +determineDataLimits(){const options=this.options;const adapter=this._adapter;const unit=options.time.unit||'day';let{min,max,minDefined,maxDefined}=this.getUserBounds();function _applyBounds(bounds){if(!minDefined&&!isNaN(bounds.min)){min=Math.min(min,bounds.min);} if(!maxDefined&&!isNaN(bounds.max)){max=Math.max(max,bounds.max);}} -if(!minDefined||!maxDefined){_applyBounds(me._getLabelBounds());if(options.bounds!=='ticks'||options.ticks.source!=='labels'){_applyBounds(me.getMinMax(false));}} -min=isNumberFinite(min)&&!isNaN(min)?min:+adapter.startOf(Date.now(),unit);max=isNumberFinite(max)&&!isNaN(max)?max:+adapter.endOf(Date.now(),unit)+1;me.min=Math.min(min,max-1);me.max=Math.max(min+1,max);} +if(!minDefined||!maxDefined){_applyBounds(this._getLabelBounds());if(options.bounds!=='ticks'||options.ticks.source!=='labels'){_applyBounds(this.getMinMax(false));}} +min=isNumberFinite(min)&&!isNaN(min)?min:+adapter.startOf(Date.now(),unit);max=isNumberFinite(max)&&!isNaN(max)?max:+adapter.endOf(Date.now(),unit)+1;this.min=Math.min(min,max-1);this.max=Math.max(min+1,max);} _getLabelBounds(){const arr=this.getLabelTimestamps();let min=Number.POSITIVE_INFINITY;let max=Number.NEGATIVE_INFINITY;if(arr.length){min=arr[0];max=arr[arr.length-1];} return{min,max};} -buildTicks(){const me=this;const options=me.options;const timeOpts=options.time;const tickOpts=options.ticks;const timestamps=tickOpts.source==='labels'?me.getLabelTimestamps():me._generate();if(options.bounds==='ticks'&×tamps.length){me.min=me._userMin||timestamps[0];me.max=me._userMax||timestamps[timestamps.length-1];} -const min=me.min;const max=me.max;const ticks=_filterBetween(timestamps,min,max);me._unit=timeOpts.unit||(tickOpts.autoSkip?determineUnitForAutoTicks(timeOpts.minUnit,me.min,me.max,me._getLabelCapacity(min)):determineUnitForFormatting(me,ticks.length,timeOpts.minUnit,me.min,me.max));me._majorUnit=!tickOpts.major.enabled||me._unit==='year'?undefined:determineMajorUnit(me._unit);me.initOffsets(timestamps);if(options.reverse){ticks.reverse();} -return ticksFromTimestamps(me,ticks,me._majorUnit);} -initOffsets(timestamps){const me=this;let start=0;let end=0;let first,last;if(me.options.offset&×tamps.length){first=me.getDecimalForValue(timestamps[0]);if(timestamps.length===1){start=1-first;}else{start=(me.getDecimalForValue(timestamps[1])-first)/2;} -last=me.getDecimalForValue(timestamps[timestamps.length-1]);if(timestamps.length===1){end=last;}else{end=(last-me.getDecimalForValue(timestamps[timestamps.length-2]))/2;}} -const limit=timestamps.length<3?0.5:0.25;start=_limitValue(start,0,limit);end=_limitValue(end,0,limit);me._offsets={start,end,factor:1/(start+1+end)};} -_generate(){const me=this;const adapter=me._adapter;const min=me.min;const max=me.max;const options=me.options;const timeOpts=options.time;const minor=timeOpts.unit||determineUnitForAutoTicks(timeOpts.minUnit,min,max,me._getLabelCapacity(min));const stepSize=valueOrDefault(timeOpts.stepSize,1);const weekday=minor==='week'?timeOpts.isoWeekday:false;const hasWeekday=isNumber(weekday)||weekday===true;const ticks={};let first=min;let time,count;if(hasWeekday){first=+adapter.startOf(first,'isoWeek',weekday);} +buildTicks(){const options=this.options;const timeOpts=options.time;const tickOpts=options.ticks;const timestamps=tickOpts.source==='labels'?this.getLabelTimestamps():this._generate();if(options.bounds==='ticks'&×tamps.length){this.min=this._userMin||timestamps[0];this.max=this._userMax||timestamps[timestamps.length-1];} +const min=this.min;const max=this.max;const ticks=_filterBetween(timestamps,min,max);this._unit=timeOpts.unit||(tickOpts.autoSkip?determineUnitForAutoTicks(timeOpts.minUnit,this.min,this.max,this._getLabelCapacity(min)):determineUnitForFormatting(this,ticks.length,timeOpts.minUnit,this.min,this.max));this._majorUnit=!tickOpts.major.enabled||this._unit==='year'?undefined:determineMajorUnit(this._unit);this.initOffsets(timestamps);if(options.reverse){ticks.reverse();} +return ticksFromTimestamps(this,ticks,this._majorUnit);} +initOffsets(timestamps){let start=0;let end=0;let first,last;if(this.options.offset&×tamps.length){first=this.getDecimalForValue(timestamps[0]);if(timestamps.length===1){start=1-first;}else{start=(this.getDecimalForValue(timestamps[1])-first)/2;} +last=this.getDecimalForValue(timestamps[timestamps.length-1]);if(timestamps.length===1){end=last;}else{end=(last-this.getDecimalForValue(timestamps[timestamps.length-2]))/2;}} +const limit=timestamps.length<3?0.5:0.25;start=_limitValue(start,0,limit);end=_limitValue(end,0,limit);this._offsets={start,end,factor:1/(start+1+end)};} +_generate(){const adapter=this._adapter;const min=this.min;const max=this.max;const options=this.options;const timeOpts=options.time;const minor=timeOpts.unit||determineUnitForAutoTicks(timeOpts.minUnit,min,max,this._getLabelCapacity(min));const stepSize=valueOrDefault(timeOpts.stepSize,1);const weekday=minor==='week'?timeOpts.isoWeekday:false;const hasWeekday=isNumber(weekday)||weekday===true;const ticks={};let first=min;let time,count;if(hasWeekday){first=+adapter.startOf(first,'isoWeek',weekday);} first=+adapter.startOf(first,hasWeekday?'day':minor);if(adapter.diff(max,min,minor)>100000*stepSize){throw new Error(min+' and '+max+' are too far apart with stepSize of '+stepSize+' '+minor);} -const timestamps=options.ticks.source==='data'&&me.getDataTimestamps();for(time=first,count=0;timea-b).map(x=>+x);} -getLabelForValue(value){const me=this;const adapter=me._adapter;const timeOpts=me.options.time;if(timeOpts.tooltipFormat){return adapter.format(value,timeOpts.tooltipFormat);} +getLabelForValue(value){const adapter=this._adapter;const timeOpts=this.options.time;if(timeOpts.tooltipFormat){return adapter.format(value,timeOpts.tooltipFormat);} return adapter.format(value,timeOpts.displayFormats.datetime);} -_tickFormatFunction(time,index,ticks,format){const me=this;const options=me.options;const formats=options.time.displayFormats;const unit=me._unit;const majorUnit=me._majorUnit;const minorFormat=unit&&formats[unit];const majorFormat=majorUnit&&formats[majorUnit];const tick=ticks[index];const major=majorUnit&&majorFormat&&tick&&tick.major;const label=me._adapter.format(time,format||(major?majorFormat:minorFormat));const formatter=options.ticks.callback;return formatter?callback(formatter,[label,index,ticks],me):label;} +_tickFormatFunction(time,index,ticks,format){const options=this.options;const formats=options.time.displayFormats;const unit=this._unit;const majorUnit=this._majorUnit;const minorFormat=unit&&formats[unit];const majorFormat=majorUnit&&formats[majorUnit];const tick=ticks[index];const major=majorUnit&&majorFormat&&tick&&tick.major;const label=this._adapter.format(time,format||(major?majorFormat:minorFormat));const formatter=options.ticks.callback;return formatter?callback(formatter,[label,index,ticks],this):label;} generateTickLabels(ticks){let i,ilen,tick;for(i=0,ilen=ticks.length;i0?capacity:1;} -getDataTimestamps(){const me=this;let timestamps=me._cache.data||[];let i,ilen;if(timestamps.length){return timestamps;} -const metas=me.getMatchingVisibleMetas();if(me._normalized&&metas.length){return(me._cache.data=metas[0].controller.getAllParsedValues(me));} -for(i=0,ilen=metas.length;i0?capacity:1;} +getDataTimestamps(){let timestamps=this._cache.data||[];let i,ilen;if(timestamps.length){return timestamps;} +const metas=this.getMatchingVisibleMetas();if(this._normalized&&metas.length){return(this._cache.data=metas[0].controller.getAllParsedValues(this));} +for(i=0,ilen=metas.length;i=table[lo].pos&&val<=table[hi].pos){({lo,hi}=_lookupByKey(table,'pos',val));} ({pos:prevSource,time:prevTarget}=table[lo]);({pos:nextSource,time:nextTarget}=table[hi]);}else{if(val>=table[lo].time&&val<=table[hi].time){({lo,hi}=_lookupByKey(table,'time',val));} ({time:prevSource,pos:prevTarget}=table[lo]);({time:nextSource,pos:nextTarget}=table[hi]);} const span=nextSource-prevSource;return span?prevTarget+(nextTarget-prevTarget)*(val-prevSource)/span:prevTarget;} class TimeSeriesScale extends TimeScale{constructor(props){super(props);this._table=[];this._minPos=undefined;this._tableRange=undefined;} -initOffsets(){const me=this;const timestamps=me._getTimestampsForTable();const table=me._table=me.buildLookupTable(timestamps);me._minPos=interpolate(table,me.min);me._tableRange=interpolate(table,me.max)-me._minPos;super.initOffsets(timestamps);} +initOffsets(){const timestamps=this._getTimestampsForTable();const table=this._table=this.buildLookupTable(timestamps);this._minPos=interpolate(table,this.min);this._tableRange=interpolate(table,this.max)-this._minPos;super.initOffsets(timestamps);} buildLookupTable(timestamps){const{min,max}=this;const items=[];const table=[];let i,ilen,prev,curr,next;for(i=0,ilen=timestamps.length;i=min&&curr<=max){items.push(curr);}} if(items.length<2){return[{time:min,pos:0},{time:max,pos:1}];} for(i=0,ilen=items.length;i=0;i--){startDelim=delimiters[i];if(startDelim.marker!==95&&startDelim.marker!==42){continue;} if(startDelim.end===-1){continue;} -endDelim=delimiters[startDelim.end];isStrong=i>0&&delimiters[i-1].end===startDelim.end+1&&delimiters[i-1].token===startDelim.token-1&&delimiters[startDelim.end+1].token===endDelim.token+1&&delimiters[i-1].marker===startDelim.marker;ch=String.fromCharCode(startDelim.marker);token=state.tokens[startDelim.token];token.type=isStrong?"strong_open":"em_open";token.tag=isStrong?"strong":"em";token.nesting=1;token.markup=isStrong?ch+ch:ch;token.content="";token=state.tokens[endDelim.token];token.type=isStrong?"strong_close":"em_close";token.tag=isStrong?"strong":"em";token.nesting=-1;token.markup=isStrong?ch+ch:ch;token.content="";if(isStrong){state.tokens[delimiters[i-1].token].content="";state.tokens[delimiters[startDelim.end+1].token].content="";i--;}}} +endDelim=delimiters[startDelim.end];isStrong=i>0&&delimiters[i-1].end===startDelim.end+1&&delimiters[i-1].marker===startDelim.marker&&delimiters[i-1].token===startDelim.token-1&&delimiters[startDelim.end+1].token===endDelim.token+1;ch=String.fromCharCode(startDelim.marker);token=state.tokens[startDelim.token];token.type=isStrong?"strong_open":"em_open";token.tag=isStrong?"strong":"em";token.nesting=1;token.markup=isStrong?ch+ch:ch;token.content="";token=state.tokens[endDelim.token];token.type=isStrong?"strong_close":"em_close";token.tag=isStrong?"strong":"em";token.nesting=-1;token.markup=isStrong?ch+ch:ch;token.content="";if(isStrong){state.tokens[delimiters[i-1].token].content="";state.tokens[delimiters[startDelim.end+1].token].content="";i--;}}} var postProcess_1=function emphasis(state){var curr,tokens_meta=state.tokens_meta,max=state.tokens_meta.length;postProcess(state,state.delimiters);for(curr=0;currminOpenerIdx;openerIdx-=opener.jump+1){opener=delimiters[openerIdx];if(opener.marker!==closer.marker)continue;if(opener.open&&opener.end<0){isOddMatch=false;if(opener.close||closer.open){if((opener.length+closer.length)%3===0){if(opener.length%3!==0||closer.length%3!==0){isOddMatch=true;}}} -if(!isOddMatch){lastJump=openerIdx>0&&!delimiters[openerIdx-1].open?delimiters[openerIdx-1].jump+1:0;closer.jump=closerIdx-openerIdx+lastJump;closer.open=false;opener.end=closerIdx;opener.jump=lastJump;opener.close=false;newMinOpenerIdx=-1;break;}}} +state.pos++;return true;};function processDelimiters(state,delimiters){var closerIdx,openerIdx,closer,opener,minOpenerIdx,newMinOpenerIdx,isOddMatch,lastJump,openersBottom={},max=delimiters.length;if(!max)return;var headerIdx=0;var lastTokenIdx=-2;var jumps=[];for(closerIdx=0;closerIdxminOpenerIdx;openerIdx-=jumps[openerIdx]+1){opener=delimiters[openerIdx];if(opener.marker!==closer.marker)continue;if(opener.open&&opener.end<0){isOddMatch=false;if(opener.close||closer.open){if((opener.length+closer.length)%3===0){if(opener.length%3!==0||closer.length%3!==0){isOddMatch=true;}}} +if(!isOddMatch){lastJump=openerIdx>0&&!delimiters[openerIdx-1].open?jumps[openerIdx-1]+1:0;jumps[closerIdx]=closerIdx-openerIdx+lastJump;jumps[openerIdx]=lastJump;closer.open=false;opener.end=closerIdx;opener.close=false;newMinOpenerIdx=-1;lastTokenIdx=-2;break;}}} if(newMinOpenerIdx!==-1){openersBottom[closer.marker][(closer.open?3:0)+(closer.length||0)%3]=newMinOpenerIdx;}}} var balance_pairs=function link_pairs(state){var curr,tokens_meta=state.tokens_meta,max=state.tokens_meta.length;processDelimiters(state,state.delimiters);for(curr=0;curr0)level++;if(tokens[curr].type==="text"&&curr+1=end){break;} continue;} state.pending+=state.src[state.pos++];} -if(state.pending){state.pushPending();}};ParserInline.prototype.parse=function(str,md,env,outTokens){var i,rules,len;var state=new this.State(str,md,env,outTokens);this.tokenize(state);rules=this.ruler2.getRules("");len=rules.length;for(i=0;i|$))";re.tpl_email_fuzzy="(^|"+text_separators+'|"|\\(|'+re.src_ZCc+")"+"("+re.src_email_name+"@"+re.tpl_host_fuzzy_strict+")";re.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+re.src_ZPCc+"))"+"((?![$+<=>^`|\uff5c])"+re.tpl_host_port_fuzzy_strict+re.src_path+")";re.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+re.src_ZPCc+"))"+"((?![$+<=>^`|\uff5c])"+re.tpl_host_port_no_ip_fuzzy_strict+re.src_path+")";return re;};function assign(obj){var sources=Array.prototype.slice.call(arguments,1);sources.forEach((function(source){if(!source){return;} +if(state.pending){state.pushPending();}};ParserInline.prototype.parse=function(str,md,env,outTokens){var i,rules,len;var state=new this.State(str,md,env,outTokens);this.tokenize(state);rules=this.ruler2.getRules("");len=rules.length;for(i=0;i|$))";re.tpl_email_fuzzy="(^|"+text_separators+'|"|\\(|'+re.src_ZCc+")"+"("+re.src_email_name+"@"+re.tpl_host_fuzzy_strict+")";re.tpl_link_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+re.src_ZPCc+"))"+"((?![$+<=>^`|\uff5c])"+re.tpl_host_port_fuzzy_strict+re.src_path+")";re.tpl_link_no_ip_fuzzy="(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|"+re.src_ZPCc+"))"+"((?![$+<=>^`|\uff5c])"+re.tpl_host_port_no_ip_fuzzy_strict+re.src_path+")";return re;};function assign(obj){var sources=Array.prototype.slice.call(arguments,1);sources.forEach((function(source){if(!source){return;} Object.keys(source).forEach((function(key){obj[key]=source[key];}));}));return obj;} function _class(obj){return Object.prototype.toString.call(obj);} function isString(obj){return _class(obj)==="[object String]";} diff --git a/public/dist/scripts/app.js b/public/dist/scripts/app.js index 3dc087237d..4be8c6e87b 100644 --- a/public/dist/scripts/app.js +++ b/public/dist/scripts/app.js @@ -782,6 +782,10 @@ console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i function sync(){if(!element.value){return;} let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');} teamID.value=json.teamID||'';keyID.value=json.keyID||'';p8.value=json.p8||'';} +sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-microsoft",controller:function(element){let clientSecret=document.getElementById("oauth2MicrosoftClientSecret");let tenantId=document.getElementById("oauth2MicrosoftTenantId");element.addEventListener('change',sync);clientSecret.addEventListener('change',update);tenantId.addEventListener('change',update);function update(){let json={};json.clientSecret=clientSecret.value;json.tenantId=tenantId.value;element.value=JSON.stringify(json);} +function sync(){if(!element.value){return;} +let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');} +clientSecret.value=json.clientSecret||'';tenantId.value=json.tenantId||'';} sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown,rtl){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){alignText();element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:''},{name:"underline",icon:''},{name:"italic",icon:''},{name:"olist",icon:''},{name:"ulist",icon:''},{name:"link",icon:''}]});var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;console.log(clipboardData.getData("Text"));window.pell.exec("insertText",clipboardData.getData("Text"));return true;};var alignText=function(){let paragraphs=editor.content.querySelectorAll('p,li');let last='';for(let paragraph of paragraphs){var content=paragraph.textContent;if(content.trim()===''){content=last.textContent;} diff --git a/public/scripts/views/forms/oauth-microsoft.js b/public/scripts/views/forms/oauth-microsoft.js new file mode 100644 index 0000000000..c0729ef347 --- /dev/null +++ b/public/scripts/views/forms/oauth-microsoft.js @@ -0,0 +1,50 @@ +(function (window) { + "use strict"; + + //TODO: Make this generic + + window.ls.container.get("view").add({ + selector: "data-forms-oauth-microsoft", + controller: function (element) { + // element contains the final secret + + // Get all custom input fields by their ID + let clientSecret = document.getElementById("oauth2MicrosoftClientSecret"); + let tenantId = document.getElementById("oauth2MicrosoftTenantId"); + + // Add Change Listeners for element and all custom input fields + + element.addEventListener('change', sync); + clientSecret.addEventListener('change', update); + tenantId.addEventListener('change', update); + + // Build the JSON based on input in custom input fields + function update() { + let json = {}; + + json.clientSecret = clientSecret.value; + json.tenantId = tenantId.value; + + element.value = JSON.stringify(json); + } + + function sync() { + if (!element.value) { + return; + } + + let json = {}; + + try { + json = JSON.parse(element.value); + } catch (error) { + console.error('Failed to parse secret key'); + } + + clientSecret.value = json.clientSecret || ''; + tenantId.value = json.tenantId || ''; + } + sync(); + } + }); +})(window); diff --git a/src/Appwrite/Auth/OAuth2/Microsoft.php b/src/Appwrite/Auth/OAuth2/Microsoft.php index 902719ef8d..40d0a7b269 100644 --- a/src/Appwrite/Auth/OAuth2/Microsoft.php +++ b/src/Appwrite/Auth/OAuth2/Microsoft.php @@ -36,7 +36,7 @@ class Microsoft extends OAuth2 */ public function getLoginURL(): string { - return 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize?'.\http_build_query([ + return 'https://login.microsoftonline.com/'.$this->getTenantId().'/oauth2/v2.0/authorize?'.\http_build_query([ 'client_id' => $this->appID, 'redirect_uri' => $this->callback, 'state'=> \json_encode($this->state), @@ -57,12 +57,12 @@ class Microsoft extends OAuth2 $accessToken = $this->request( 'POST', - 'https://login.microsoftonline.com/common/oauth2/v2.0/token', + 'https://login.microsoftonline.com/'.$this->getTenantId().'/oauth2/v2.0/token', $headers, \http_build_query([ 'code' => $code, 'client_id' => $this->appID, - 'client_secret' => $this->appSecret, + 'client_secret' => $this->getClientSecret(), 'redirect_uri' => $this->callback, 'scope' => \implode(' ', $this->getScopes()), 'grant_type' => 'authorization_code' @@ -141,4 +141,39 @@ class Microsoft extends OAuth2 return $this->user; } + + /** + * Extracts the Client Secret from the JSON stored in appSecret + * @return string + */ + protected function getClientSecret():string + { + $secret = $this->decodeJson(); + + return (isset($secret['clientSecret'])) ? $secret['clientSecret'] : ''; + } + + /** + * Decode the JSON stored in appSecret + * @return array + */ + protected function decodeJson():array{ + + try { + $secret = \json_decode($this->appSecret, true); + } catch (\Throwable $th) { + throw new Exception('Invalid secret'); + } + return $secret; + } + + /** + * Extracts the Tenant Id from the JSON stored in appSecret. Defaults to 'common' as a fallback + * @return string + */ + protected function getTenantId():string + { + $secret = $this->decodeJson(); + return (isset($secret['tenantId'])) ? $secret['tenantId'] : 'common'; + } } From ec97e845a221c3c64ac01d20eaaa5fed6db7a22c Mon Sep 17 00:00:00 2001 From: Simon Trockel Date: Mon, 17 Jan 2022 14:03:08 +0100 Subject: [PATCH 04/21] Update app/views/console/users/index.phtml Co-authored-by: Eldad A. Fux --- app/views/console/users/index.phtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/console/users/index.phtml b/app/views/console/users/index.phtml index 2b58cb0dbc..bb539f732e 100644 --- a/app/views/console/users/index.phtml +++ b/app/views/console/users/index.phtml @@ -480,7 +480,7 @@ $smtpEnabled = $this->getParam('smtpEnabled', false); escape($form)); echo $form_view - ->setParam("provider",$provider) + ->setParam("provider", $provider) ->render(); ?> From ad78e22a9dbd898ad93ea577de05aa252627e381 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Fri, 21 Jan 2022 15:35:52 -0500 Subject: [PATCH 05/21] Default to collection-level permissions from console --- app/views/console/database/collection.phtml | 18 +++++++++--------- app/views/console/database/index.phtml | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/console/database/collection.phtml b/app/views/console/database/collection.phtml index 93db293ad1..46d377430b 100644 --- a/app/views/console/database/collection.phtml +++ b/app/views/console/database/collection.phtml @@ -527,15 +527,6 @@ $logs = $this->getParam('logs', null);
-
-
-
- Document Level -

With Document Level permissions, you have granular access control over every document. Users will only be able to access documents for which they have explicit permissions.

-

In this permission level, document permissions take precedence and collection permissions are ignored.

-
-
-
@@ -554,6 +545,15 @@ $logs = $this->getParam('logs', null);
+
+
+
+ Document Level +

With Document Level permissions, you have granular access control over every document. Users will only be able to access documents for which they have explicit permissions.

+

In this permission level, document permissions take precedence and collection permissions are ignored.

+
+
+
diff --git a/app/views/console/database/index.phtml b/app/views/console/database/index.phtml index 951f24c3d2..6b485a2051 100644 --- a/app/views/console/database/index.phtml +++ b/app/views/console/database/index.phtml @@ -108,7 +108,7 @@ - + From 8233881c4414dd3bc03bef8e1739c4e7b69e03b8 Mon Sep 17 00:00:00 2001 From: Simon Trockel Date: Sat, 22 Jan 2022 19:31:40 +0100 Subject: [PATCH 06/21] feat: Generic custom Oauth provider form --- app/controllers/api/account.php | 2 +- app/views/console/users/index.phtml | 4 +- app/views/console/users/oauth/microsoft.phtml | 2 +- gulpfile.js | 2 +- public/dist/scripts/app-all.js | 8 ++- public/dist/scripts/app.js | 8 ++- public/scripts/views/forms/oauth-custom.js | 68 +++++++++++++++++++ public/scripts/views/forms/oauth-microsoft.js | 50 -------------- 8 files changed, 85 insertions(+), 59 deletions(-) create mode 100644 public/scripts/views/forms/oauth-custom.js delete mode 100644 public/scripts/views/forms/oauth-microsoft.js diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 2ff087b725..497cc4e839 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -315,7 +315,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId') ->label('docs', false) ->param('projectId', '', new Text(1024), 'Project ID.') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(1024), 'OAuth2 code.') + ->param('code', '', new Text(2048), 'OAuth2 code.') ->param('state', '', new Text(2048), 'Login state params.', true) ->inject('request') ->inject('response') diff --git a/app/views/console/users/index.phtml b/app/views/console/users/index.phtml index bb539f732e..f93172903b 100644 --- a/app/views/console/users/index.phtml +++ b/app/views/console/users/index.phtml @@ -478,8 +478,8 @@ $smtpEnabled = $this->getParam('smtpEnabled', false); escape($form)); - echo $form_view + $form = new View(__DIR__.'/oauth/'.$this->escape($form)); + echo $form ->setParam("provider", $provider) ->render(); ?> diff --git a/app/views/console/users/oauth/microsoft.phtml b/app/views/console/users/oauth/microsoft.phtml index 201953c91a..e366f80ca1 100644 --- a/app/views/console/users/oauth/microsoft.phtml +++ b/app/views/console/users/oauth/microsoft.phtml @@ -9,4 +9,4 @@ $provider = $this->getParam('provider', ''); - \ No newline at end of file + \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index bd91cbe633..5cd7d145c9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -58,7 +58,7 @@ const configApp = { 'public/scripts/views/forms/move-up.js', 'public/scripts/views/forms/nav.js', 'public/scripts/views/forms/oauth-apple.js', - 'public/scripts/views/forms/oauth-microsoft.js', + 'public/scripts/views/forms/oauth-custom.js', 'public/scripts/views/forms/password-meter.js', 'public/scripts/views/forms/pell.js', 'public/scripts/views/forms/required.js', diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index f9d8722eea..3fab6fb049 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -3761,10 +3761,14 @@ console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i function sync(){if(!element.value){return;} let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');} teamID.value=json.teamID||'';keyID.value=json.keyID||'';p8.value=json.p8||'';} -sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-microsoft",controller:function(element){let clientSecret=document.getElementById("oauth2MicrosoftClientSecret");let tenantId=document.getElementById("oauth2MicrosoftTenantId");element.addEventListener('change',sync);clientSecret.addEventListener('change',update);tenantId.addEventListener('change',update);function update(){let json={};json.clientSecret=clientSecret.value;json.tenantId=tenantId.value;element.value=JSON.stringify(json);} +sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantId":"oauth2MicrosoftTenantId"}} +let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unkown")} +let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}} +function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}} +element.value=JSON.stringify(json);} function sync(){if(!element.value){return;} let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');} -clientSecret.value=json.clientSecret||'';tenantId.value=json.tenantId||'';} +for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){elements[key].value=json[key]||'';}}} sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown,rtl){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){alignText();element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:''},{name:"underline",icon:''},{name:"italic",icon:''},{name:"olist",icon:''},{name:"ulist",icon:''},{name:"link",icon:''}]});var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;console.log(clipboardData.getData("Text"));window.pell.exec("insertText",clipboardData.getData("Text"));return true;};var alignText=function(){let paragraphs=editor.content.querySelectorAll('p,li');let last='';for(let paragraph of paragraphs){var content=paragraph.textContent;if(content.trim()===''){content=last.textContent;} diff --git a/public/dist/scripts/app.js b/public/dist/scripts/app.js index 4be8c6e87b..71acb36bca 100644 --- a/public/dist/scripts/app.js +++ b/public/dist/scripts/app.js @@ -782,10 +782,14 @@ console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i function sync(){if(!element.value){return;} let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');} teamID.value=json.teamID||'';keyID.value=json.keyID||'';p8.value=json.p8||'';} -sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-microsoft",controller:function(element){let clientSecret=document.getElementById("oauth2MicrosoftClientSecret");let tenantId=document.getElementById("oauth2MicrosoftTenantId");element.addEventListener('change',sync);clientSecret.addEventListener('change',update);tenantId.addEventListener('change',update);function update(){let json={};json.clientSecret=clientSecret.value;json.tenantId=tenantId.value;element.value=JSON.stringify(json);} +sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantId":"oauth2MicrosoftTenantId"}} +let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unkown")} +let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}} +function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}} +element.value=JSON.stringify(json);} function sync(){if(!element.value){return;} let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');} -clientSecret.value=json.clientSecret||'';tenantId.value=json.tenantId||'';} +for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){elements[key].value=json[key]||'';}}} sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-password-meter",controller:function(element,window){var calc=function(password){var score=0;if(!password)return score;var letters=new window.Object();for(var i=0;i60)return(meter.className="password-meter strong");if(score>30)return(meter.className="password-meter medium");if(score>=0)return(meter.className="password-meter weak");};var meter=window.document.createElement("div");meter.className="password-meter";element.parentNode.insertBefore(meter,element.nextSibling);element.addEventListener("change",callback);element.addEventListener("keypress",callback);element.addEventListener("keyup",callback);element.addEventListener("keydown",callback);}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-pell",controller:function(element,window,document,markdown,rtl){var div=document.createElement("div");element.className="pell hide";div.className="input pell";element.parentNode.insertBefore(div,element);element.tabIndex=-1;var turndownService=new TurndownService();turndownService.addRule("underline",{filter:["u"],replacement:function(content){return"__"+content+"__";}});var editor=window.pell.init({element:div,onChange:function onChange(html){alignText();element.value=turndownService.turndown(html);},defaultParagraphSeparator:"p",actions:[{name:"bold",icon:''},{name:"underline",icon:''},{name:"italic",icon:''},{name:"olist",icon:''},{name:"ulist",icon:''},{name:"link",icon:''}]});var clean=function(e){e.stopPropagation();e.preventDefault();var clipboardData=e.clipboardData||window.clipboardData;console.log(clipboardData.getData("Text"));window.pell.exec("insertText",clipboardData.getData("Text"));return true;};var alignText=function(){let paragraphs=editor.content.querySelectorAll('p,li');let last='';for(let paragraph of paragraphs){var content=paragraph.textContent;if(content.trim()===''){content=last.textContent;} diff --git a/public/scripts/views/forms/oauth-custom.js b/public/scripts/views/forms/oauth-custom.js new file mode 100644 index 0000000000..7daa80a2d2 --- /dev/null +++ b/public/scripts/views/forms/oauth-custom.js @@ -0,0 +1,68 @@ +(function (window) { + "use strict"; + + //TODO: Make this generic + + window.ls.container.get("view").add({ + selector: "data-forms-oauth-custom", + controller: function (element) { + // provider configuration for custom forms. Keys will be property names in JSON, values the elementIDs for the according inputs + let providers = { + "Microsoft": { + "clientSecret": "oauth2MicrosoftClientSecret", + "tenantId": "oauth2MicrosoftTenantId" + } + } + let provider = element.getAttribute("data-forms-oauth-custom"); + if (!provider || !providers.hasOwnProperty(provider)) { console.error("Provider for custom form not set or unkown") } + let config = providers[provider]; + + // Add Change Listeners for element + element.addEventListener('change', sync); + + // Get all inputs by id and register change event listener + let elements = {}; + for (const key in config) { + if (Object.hasOwnProperty.call(config, key)) { + elements[key] = document.getElementById(config[key]); + elements[key].addEventListener('change', update); + } + } + + + // Build the JSON based on input in custom input fields + function update() { + let json = {}; + for (const key in elements) { + if (Object.hasOwnProperty.call(elements, key)) { + json[key] = elements[key].value + } + } + + element.value = JSON.stringify(json); + } + + // When the JSON changes (on load) change values in custom input fields + function sync() { + if (!element.value) { + return; + } + + let json = {}; + + try { + json = JSON.parse(element.value); + } catch (error) { + console.error('Failed to parse secret key'); + } + + for (const key in elements) { + if (Object.hasOwnProperty.call(elements, key)) { + elements[key].value = json[key] || ''; + } + } + } + sync(); + } + }); +})(window); diff --git a/public/scripts/views/forms/oauth-microsoft.js b/public/scripts/views/forms/oauth-microsoft.js deleted file mode 100644 index c0729ef347..0000000000 --- a/public/scripts/views/forms/oauth-microsoft.js +++ /dev/null @@ -1,50 +0,0 @@ -(function (window) { - "use strict"; - - //TODO: Make this generic - - window.ls.container.get("view").add({ - selector: "data-forms-oauth-microsoft", - controller: function (element) { - // element contains the final secret - - // Get all custom input fields by their ID - let clientSecret = document.getElementById("oauth2MicrosoftClientSecret"); - let tenantId = document.getElementById("oauth2MicrosoftTenantId"); - - // Add Change Listeners for element and all custom input fields - - element.addEventListener('change', sync); - clientSecret.addEventListener('change', update); - tenantId.addEventListener('change', update); - - // Build the JSON based on input in custom input fields - function update() { - let json = {}; - - json.clientSecret = clientSecret.value; - json.tenantId = tenantId.value; - - element.value = JSON.stringify(json); - } - - function sync() { - if (!element.value) { - return; - } - - let json = {}; - - try { - json = JSON.parse(element.value); - } catch (error) { - console.error('Failed to parse secret key'); - } - - clientSecret.value = json.clientSecret || ''; - tenantId.value = json.tenantId || ''; - } - sync(); - } - }); -})(window); From e29ac692d578fa3fac5da15b8dc7e04ce3523e75 Mon Sep 17 00:00:00 2001 From: Simon Trockel Date: Sat, 22 Jan 2022 21:02:51 +0100 Subject: [PATCH 07/21] Adjust all code param lengths --- app/controllers/api/account.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index c6560ab22a..b25e61db1a 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -342,7 +342,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId') ->label('docs', false) ->param('projectId', '', new Text(1024), 'Project ID.') ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(1024), 'OAuth2 code.') + ->param('code', '', new Text(2048), 'OAuth2 code.') ->param('state', '', new Text(2048), 'Login state params.', true) ->inject('request') ->inject('response') @@ -370,7 +370,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') ->label('abuse-key', 'ip:{ip}') ->label('docs', false) ->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 provider.') - ->param('code', '', new Text(1024), 'OAuth2 code.') + ->param('code', '', new Text(2048), 'OAuth2 code.') ->param('state', '', new Text(2048), 'OAuth2 state params.', true) ->inject('request') ->inject('response') From 45635cdbcc85e47883d36c68cb6ecd1ca9c22a51 Mon Sep 17 00:00:00 2001 From: Simon Trockel Date: Sat, 22 Jan 2022 23:13:16 +0100 Subject: [PATCH 08/21] Rework Apple Custom Oauth Form --- app/views/console/users/oauth/apple.phtml | 8 +- gulpfile.js | 1 - public/dist/scripts/app-all.js | 6 +- public/dist/scripts/app.js | 6 +- public/scripts/views/forms/oauth-apple.js | 93 ---------------------- public/scripts/views/forms/oauth-custom.js | 5 ++ 6 files changed, 14 insertions(+), 105 deletions(-) delete mode 100644 public/scripts/views/forms/oauth-apple.js diff --git a/app/views/console/users/oauth/apple.phtml b/app/views/console/users/oauth/apple.phtml index d18f851ff8..7fb56b88ea 100644 --- a/app/views/console/users/oauth/apple.phtml +++ b/app/views/console/users/oauth/apple.phtml @@ -4,4 +4,10 @@ $provider = $this->getParam('provider', ''); - \ No newline at end of file + +
+
+
+
+
+
\ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 5cd7d145c9..0b0d1904de 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -57,7 +57,6 @@ const configApp = { 'public/scripts/views/forms/move-down.js', 'public/scripts/views/forms/move-up.js', 'public/scripts/views/forms/nav.js', - 'public/scripts/views/forms/oauth-apple.js', 'public/scripts/views/forms/oauth-custom.js', 'public/scripts/views/forms/password-meter.js', 'public/scripts/views/forms/pell.js', diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index 3fab6fb049..ffdba956d0 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -3757,11 +3757,7 @@ list["filters-"+filter.key]=params[key][i];}}}} return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');} -console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-apple",controller:function(element){let container=document.createElement("div");let row=document.createElement("div");let col1=document.createElement("div");let col2=document.createElement("div");let keyID=document.createElement("input");let keyLabel=document.createElement("label");let teamID=document.createElement("input");let teamLabel=document.createElement("label");let p8=document.createElement("textarea");let p8Label=document.createElement("label");keyLabel.textContent='Key ID';teamLabel.textContent='Team ID';p8Label.textContent='P8 File';row.classList.add('row');row.classList.add('thin');container.appendChild(row);container.appendChild(p8Label);container.appendChild(p8);row.appendChild(col1);row.appendChild(col2);col1.classList.add('col');col1.classList.add('span-6');col1.appendChild(keyLabel);col1.appendChild(keyID);col2.classList.add('col');col2.classList.add('span-6');col2.appendChild(teamLabel);col2.appendChild(teamID);keyID.type='text';keyID.placeholder='SHAB13ROFN';teamID.type='text';teamID.placeholder='ELA2CD3AED';p8.accept='.p8';p8.classList.add('margin-bottom-no');element.parentNode.insertBefore(container,element.nextSibling);element.addEventListener('change',sync);keyID.addEventListener('change',update);teamID.addEventListener('change',update);p8.addEventListener('change',update);function update(){let json={};json.keyID=keyID.value;json.teamID=teamID.value;json.p8=p8.value;element.value=JSON.stringify(json);} -function sync(){if(!element.value){return;} -let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');} -teamID.value=json.teamID||'';keyID.value=json.keyID||'';p8.value=json.p8||'';} -sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantId":"oauth2MicrosoftTenantId"}} +console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantId":"oauth2MicrosoftTenantId"},"Apple":{"keyId":"oauth2AppleKeyId","teamId":"oauth2AppleTeamId","p8":"oauth2AppleP8"}} let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unkown")} let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}} function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}} diff --git a/public/dist/scripts/app.js b/public/dist/scripts/app.js index 71acb36bca..42ab30e400 100644 --- a/public/dist/scripts/app.js +++ b/public/dist/scripts/app.js @@ -778,11 +778,7 @@ list["filters-"+filter.key]=params[key][i];}}}} return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');} -console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-apple",controller:function(element){let container=document.createElement("div");let row=document.createElement("div");let col1=document.createElement("div");let col2=document.createElement("div");let keyID=document.createElement("input");let keyLabel=document.createElement("label");let teamID=document.createElement("input");let teamLabel=document.createElement("label");let p8=document.createElement("textarea");let p8Label=document.createElement("label");keyLabel.textContent='Key ID';teamLabel.textContent='Team ID';p8Label.textContent='P8 File';row.classList.add('row');row.classList.add('thin');container.appendChild(row);container.appendChild(p8Label);container.appendChild(p8);row.appendChild(col1);row.appendChild(col2);col1.classList.add('col');col1.classList.add('span-6');col1.appendChild(keyLabel);col1.appendChild(keyID);col2.classList.add('col');col2.classList.add('span-6');col2.appendChild(teamLabel);col2.appendChild(teamID);keyID.type='text';keyID.placeholder='SHAB13ROFN';teamID.type='text';teamID.placeholder='ELA2CD3AED';p8.accept='.p8';p8.classList.add('margin-bottom-no');element.parentNode.insertBefore(container,element.nextSibling);element.addEventListener('change',sync);keyID.addEventListener('change',update);teamID.addEventListener('change',update);p8.addEventListener('change',update);function update(){let json={};json.keyID=keyID.value;json.teamID=teamID.value;json.p8=p8.value;element.value=JSON.stringify(json);} -function sync(){if(!element.value){return;} -let json={};try{json=JSON.parse(element.value);}catch(error){console.error('Failed to parse secret key');} -teamID.value=json.teamID||'';keyID.value=json.keyID||'';p8.value=json.p8||'';} -sync();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantId":"oauth2MicrosoftTenantId"}} +console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantId":"oauth2MicrosoftTenantId"},"Apple":{"keyId":"oauth2AppleKeyId","teamId":"oauth2AppleTeamId","p8":"oauth2AppleP8"}} let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unkown")} let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}} function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}} diff --git a/public/scripts/views/forms/oauth-apple.js b/public/scripts/views/forms/oauth-apple.js deleted file mode 100644 index cadc8ecb16..0000000000 --- a/public/scripts/views/forms/oauth-apple.js +++ /dev/null @@ -1,93 +0,0 @@ -(function(window) { - "use strict"; - - window.ls.container.get("view").add({ - selector: "data-forms-oauth-apple", - controller: function(element) { - let container = document.createElement("div"); - let row = document.createElement("div"); - let col1 = document.createElement("div"); - let col2 = document.createElement("div"); - let keyID = document.createElement("input"); - let keyLabel = document.createElement("label"); - let teamID = document.createElement("input"); - let teamLabel = document.createElement("label"); - let p8 = document.createElement("textarea"); - let p8Label = document.createElement("label"); - - keyLabel.textContent = 'Key ID'; - teamLabel.textContent = 'Team ID'; - p8Label.textContent = 'P8 File'; - - row.classList.add('row'); - row.classList.add('thin'); - container.appendChild(row); - container.appendChild(p8Label); - container.appendChild(p8); - - row.appendChild(col1); - row.appendChild(col2); - - col1.classList.add('col'); - col1.classList.add('span-6'); - col1.appendChild(keyLabel); - col1.appendChild(keyID); - - col2.classList.add('col'); - col2.classList.add('span-6'); - col2.appendChild(teamLabel); - col2.appendChild(teamID); - - keyID.type = 'text'; - keyID.placeholder = 'SHAB13ROFN'; - teamID.type = 'text'; - teamID.placeholder = 'ELA2CD3AED'; - p8.accept = '.p8'; - p8.classList.add('margin-bottom-no'); - - element.parentNode.insertBefore(container, element.nextSibling); - - element.addEventListener('change', sync); - keyID.addEventListener('change', update); - teamID.addEventListener('change', update); - p8.addEventListener('change', update); - - function update() { - let json = {}; - - json.keyID = keyID.value; - json.teamID = teamID.value; - json.p8 = p8.value; - - element.value = JSON.stringify(json); - } - - function sync() { - if(!element.value) { - return; - } - - let json = {}; - - try { - json = JSON.parse(element.value); - } catch (error) { - console.error('Failed to parse secret key'); - } - - teamID.value = json.teamID || ''; - keyID.value = json.keyID || ''; - p8.value = json.p8 || ''; - } - - // function syncB() { - // picker.value = element.value; - // } - - // element.parentNode.insertBefore(preview, element); - - // update(); - sync(); - } - }); -})(window); diff --git a/public/scripts/views/forms/oauth-custom.js b/public/scripts/views/forms/oauth-custom.js index 7daa80a2d2..323c874411 100644 --- a/public/scripts/views/forms/oauth-custom.js +++ b/public/scripts/views/forms/oauth-custom.js @@ -11,6 +11,11 @@ "Microsoft": { "clientSecret": "oauth2MicrosoftClientSecret", "tenantId": "oauth2MicrosoftTenantId" + }, + "Apple": { + "keyId": "oauth2AppleKeyId", + "teamId": "oauth2AppleTeamId", + "p8": "oauth2AppleP8" } } let provider = element.getAttribute("data-forms-oauth-custom"); From f6dd0e926ea6c46bdfa33bfe964f18e17b5d5e1b Mon Sep 17 00:00:00 2001 From: Simon Trockel Date: Sun, 23 Jan 2022 00:16:07 +0100 Subject: [PATCH 09/21] Add documentation for Custom Oauth Provider forms --- docs/tutorials/add-oauth2-provider.md | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/tutorials/add-oauth2-provider.md b/docs/tutorials/add-oauth2-provider.md index bbd3b3011e..405394da1a 100644 --- a/docs/tutorials/add-oauth2-provider.md +++ b/docs/tutorials/add-oauth2-provider.md @@ -175,4 +175,34 @@ If everything goes well, raise a pull request and be ready to respond to any fee First of all, commit the changes with the message `Added XXX OAuth2 Provider` and push it. This will publish a new branch to your forked version of Appwrite. If you visit it at `github.com/YOUR_USERNAME/appwrite`, you will see a new alert saying you are ready to submit a pull request. Follow the steps GitHub provides, and at the end, you will have your pull request submitted. ## 🤕 Stuck ? + If you need any help with the contribution, feel free to head over to [our discord channel](https://appwrite.io/discord) and we'll be happy to help you out. + +## 😉 Need more freedom + +If your OAuth provider requires special configuration apart from `clientId` and `clientSecret` you can create a custom form. Currently this is being realized through putting all custom fields as JSON into the `clientSecret` field to keep the project API stable. You can implement your custom form following these steps: + +1. Add your custom form in `app/views/console/users/oauth/[PROVIDER].phtml`. Below is a template you can use. Add the filename to `app/config/providers.php`. + +```php +getParam('provider', ''); +?> + + + + + +``` + +2. Add the config for creating the JSON in `public/scripts/views/forms/oauth-custom.js` using this template +```js +{ + "[Provider]":{ + "[JSON property name 1]":"[html element Id 1]", + "[JSON property name 2]":"[html element Id 2]" + } +} +``` + +3. In your provider class `src/Appwrite/Auth/OAuth2/[Provider].php` add logic to decode the JSON using the same property names. From d49ef8355dfecd3ea7b4a57ca6b000c05fbfed69 Mon Sep 17 00:00:00 2001 From: Torsten Dittmann Date: Mon, 24 Jan 2022 11:27:46 +0100 Subject: [PATCH 10/21] fix: ui boolean default values --- app/views/console/database/collection.phtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/console/database/collection.phtml b/app/views/console/database/collection.phtml index 93db293ad1..2e432cc825 100644 --- a/app/views/console/database/collection.phtml +++ b/app/views/console/database/collection.phtml @@ -880,10 +880,10 @@ $logs = $this->getParam('logs', null);
  Default Value
From c15d57f573894d3ab15b04f6d54327624fff7a89 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Tue, 25 Jan 2022 15:51:04 +0100 Subject: [PATCH 11/21] Fixed update document permission check and implement test for it --- app/controllers/api/database.php | 16 ++- .../Database/DatabaseCustomClientTest.php | 105 ++++++++++++++++++ 2 files changed, 115 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 9d5ac716b3..ec30f1d2bb 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -2009,14 +2009,18 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId') $roles = Authorization::getRoles(); if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { - foreach ($data['$read'] as $read) { - if (!Authorization::isRole($read)) { - throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400); + if(!is_null($read)) { + foreach ($data['$read'] as $read) { + if (!Authorization::isRole($read)) { + throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400); + } } } - foreach ($data['$write'] as $write) { - if (!Authorization::isRole($write)) { - throw new Exception('Write permissions must be one of: ('.\implode(', ', $roles).')', 400); + if(!is_null($write)) { + foreach ($data['$write'] as $write) { + if (!Authorization::isRole($write)) { + throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400); + } } } } diff --git a/tests/e2e/Services/Database/DatabaseCustomClientTest.php b/tests/e2e/Services/Database/DatabaseCustomClientTest.php index 5bb035ac1b..9d5aae758b 100644 --- a/tests/e2e/Services/Database/DatabaseCustomClientTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomClientTest.php @@ -2,13 +2,118 @@ namespace Tests\E2E\Services\Database; +use Tests\E2E\Client; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\SideClient; +use function array_merge; +use function sleep; class DatabaseCustomClientTest extends Scope { use DatabaseBase; use ProjectCustom; use SideClient; + + public function testUpdateWithoutPermission(): array + { + // If document has been created by server and client tried to update it without adjusting permissions, permission validation should be skipped + + // As a part of preparation, we get ID of currently logged-in user + $response = $this->client->call(Client::METHOD_GET, '/account', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(200, $response['headers']['status-code']); + + $userId = $response['body']['$id']; + + // Create collection + $response = $this->client->call(Client::METHOD_POST, '/database/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => 'permissionCheck', + 'name' => 'permissionCheck', + 'read' => [], + 'write' => [], + 'permission' => 'document' + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + // Add attribute to collection + $response = $this->client->call(Client::METHOD_POST, '/database/collections/permissionCheck/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'name', + 'size' => 255, + 'required' => true, + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + // Wait for database worker to finish creating attributes + sleep(2); + + // Creating document by server, give read permission to our user + some other user + $response = $this->client->call(Client::METHOD_POST, '/database/collections/permissionCheck/documents', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'documentId' => 'permissionCheckDocument', + 'data' => [ + 'name' => 'AppwriteBeginner', + ], + 'read' => ['user:' . $userId, 'user:user2'], + 'write' => ['user:' . $userId], + ]); + $this->assertEquals(201, $response['headers']['status-code']); + + // Update document + // This is the point of this test. We should be allowed to do this action, and it should not fail on permission check + $response = $this->client->call(Client::METHOD_PATCH, '/database/collections/permissionCheck/documents/permissionCheckDocument', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'data' => [ + 'name' => 'AppwriteExpert', + ] + ]); + $this->assertEquals(200, $response['headers']['status-code']); + + // Get name of the document, should be the new one + $response = $this->client->call(Client::METHOD_GET, '/database/collections/permissionCheck/documents/permissionCheckDocument', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders())); + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertEquals("AppwriteExpert", $response['body']['name']); + + // Cleanup to prevent collision with other tests + // Delete collection + $response = $this->client->call(Client::METHOD_DELETE, '/database/collections/permissionCheck', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + + $this->assertEquals(204, $response['headers']['status-code']); + + + // Wait for database worker to finish deleting collection + sleep(2); + + // Make sure collection has been deleted + $response = $this->client->call(Client::METHOD_GET, '/database/collections/permissionCheck', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ])); + $this->assertEquals(404, $response['headers']['status-code']); + + return []; + } } \ No newline at end of file From 160a451feda822ae52ec060093ca967eaa3f7647 Mon Sep 17 00:00:00 2001 From: kodumbeats Date: Tue, 25 Jan 2022 16:48:18 -0500 Subject: [PATCH 12/21] Lengths and orders not necessary for fulltext indexes --- app/config/collections.php | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/app/config/collections.php b/app/config/collections.php index abcdec9c1d..daccd9258d 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -99,8 +99,8 @@ $collections = [ '$id' => '_fulltext_search', 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], - 'lengths' => [1024], - 'orders' => [Database::ORDER_ASC], + 'lengths' => [], + 'orders' => [], ], ], ], @@ -574,8 +574,8 @@ $collections = [ '$id' => '_key_search', 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], - 'lengths' => [2048], - 'orders' => [Database::ORDER_ASC], + 'lengths' => [], + 'orders' => [], ], ], ], @@ -1106,8 +1106,8 @@ $collections = [ '$id' => '_key_search', 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], - 'lengths' => [2048], - 'orders' => [Database::ORDER_ASC], + 'lengths' => [], + 'orders' => [], ], [ '$id' => '_key_deleted_email', @@ -1422,8 +1422,8 @@ $collections = [ '$id' => '_key_search', 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], - 'lengths' => [2048], - 'orders' => [Database::ORDER_ASC], + 'lengths' => [], + 'orders' => [], ], ], ], @@ -1720,8 +1720,8 @@ $collections = [ '$id' => '_key_search', 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], - 'lengths' => [2048], - 'orders' => [Database::ORDER_ASC], + 'lengths' => [], + 'orders' => [], ], ], ], @@ -1892,8 +1892,8 @@ $collections = [ '$id' => '_key_search', 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], - 'lengths' => [2048], - 'orders' => [Database::ORDER_ASC], + 'lengths' => [], + 'orders' => [], ], ], ], @@ -1983,8 +1983,8 @@ $collections = [ '$id' => '_key_search', 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], - 'lengths' => [2048], - 'orders' => [Database::ORDER_ASC], + 'lengths' => [], + 'orders' => [], ], ], ], @@ -2118,8 +2118,8 @@ $collections = [ '$id' => '_fulltext_search', 'type' => Database::INDEX_FULLTEXT, 'attributes' => ['search'], - 'lengths' => [16384], - 'orders' => [Database::ORDER_ASC], + 'lengths' => [], + 'orders' => [], ], ], ], From 32a066fb482d109c1859bc32ea5babfadcf87a79 Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Wed, 26 Jan 2022 10:17:55 +0100 Subject: [PATCH 13/21] Cleanup --- tests/e2e/Services/Database/DatabaseCustomClientTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/e2e/Services/Database/DatabaseCustomClientTest.php b/tests/e2e/Services/Database/DatabaseCustomClientTest.php index 9d5aae758b..f0f373323b 100644 --- a/tests/e2e/Services/Database/DatabaseCustomClientTest.php +++ b/tests/e2e/Services/Database/DatabaseCustomClientTest.php @@ -6,8 +6,6 @@ use Tests\E2E\Client; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\SideClient; -use function array_merge; -use function sleep; class DatabaseCustomClientTest extends Scope { From e2ee905a8e7b80aba9cb14f83b2fb73d88b44545 Mon Sep 17 00:00:00 2001 From: Simon Trockel Date: Wed, 26 Jan 2022 11:08:10 +0100 Subject: [PATCH 14/21] Fix formatting src/Appwrite/Auth/OAuth2/Microsoft.php Co-authored-by: Christy Jacob --- src/Appwrite/Auth/OAuth2/Microsoft.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Appwrite/Auth/OAuth2/Microsoft.php b/src/Appwrite/Auth/OAuth2/Microsoft.php index 40d0a7b269..7f3b54567d 100644 --- a/src/Appwrite/Auth/OAuth2/Microsoft.php +++ b/src/Appwrite/Auth/OAuth2/Microsoft.php @@ -157,8 +157,8 @@ class Microsoft extends OAuth2 * Decode the JSON stored in appSecret * @return array */ - protected function decodeJson():array{ - + protected function decodeJson(): array + { try { $secret = \json_decode($this->appSecret, true); } catch (\Throwable $th) { From 7bf5da97594ee302e2fe50e8688df8414517037a Mon Sep 17 00:00:00 2001 From: Simon Trockel Date: Wed, 26 Jan 2022 11:09:51 +0100 Subject: [PATCH 15/21] Fix formatting src/Appwrite/Auth/OAuth2/Microsoft.php Co-authored-by: Christy Jacob --- src/Appwrite/Auth/OAuth2/Microsoft.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Microsoft.php b/src/Appwrite/Auth/OAuth2/Microsoft.php index 7f3b54567d..630c4aa224 100644 --- a/src/Appwrite/Auth/OAuth2/Microsoft.php +++ b/src/Appwrite/Auth/OAuth2/Microsoft.php @@ -171,7 +171,7 @@ class Microsoft extends OAuth2 * Extracts the Tenant Id from the JSON stored in appSecret. Defaults to 'common' as a fallback * @return string */ - protected function getTenantId():string + protected function getTenantId(): string { $secret = $this->decodeJson(); return (isset($secret['tenantId'])) ? $secret['tenantId'] : 'common'; From 8b6914e194c9ec6f41d03e5dc4601433549fb658 Mon Sep 17 00:00:00 2001 From: Simon Trockel Date: Wed, 26 Jan 2022 11:10:00 +0100 Subject: [PATCH 16/21] Fix formatting src/Appwrite/Auth/OAuth2/Microsoft.php Co-authored-by: Christy Jacob --- src/Appwrite/Auth/OAuth2/Microsoft.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Appwrite/Auth/OAuth2/Microsoft.php b/src/Appwrite/Auth/OAuth2/Microsoft.php index 630c4aa224..c926165e2a 100644 --- a/src/Appwrite/Auth/OAuth2/Microsoft.php +++ b/src/Appwrite/Auth/OAuth2/Microsoft.php @@ -146,7 +146,7 @@ class Microsoft extends OAuth2 * Extracts the Client Secret from the JSON stored in appSecret * @return string */ - protected function getClientSecret():string + protected function getClientSecret(): string { $secret = $this->decodeJson(); From f6568b3cf59b5e4fe1821b55270b08f2bb1618fd Mon Sep 17 00:00:00 2001 From: Matej Baco Date: Wed, 26 Jan 2022 16:04:50 +0100 Subject: [PATCH 17/21] Imroved UI of required and optional enum --- app/views/console/database/document.phtml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/views/console/database/document.phtml b/app/views/console/database/document.phtml index 308629c609..8c8de7429b 100644 --- a/app/views/console/database/document.phtml +++ b/app/views/console/database/document.phtml @@ -169,8 +169,11 @@ $logs = $this->getParam('logs', null);