Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19fcc4d7de | ||
|
|
ce5d489b2d | ||
|
|
5f5e336370 | ||
|
|
ac542b1694 | ||
|
|
4e27d66d36 | ||
|
|
638f77a047 | ||
|
|
45e1957fd7 | ||
|
|
d5a5c6553e | ||
|
|
ab9f7b8ab0 |
No files matched your search
@@ -4,6 +4,27 @@ The WEB GUI client of SEA.
|
||||
|
||||
This repository contains the code of the server for the control and graphical parts, plus the client code (HTML, CSS, JS).
|
||||
|
||||
**IMPORTANT**
|
||||
|
||||
This branch is an attempt to migrate from ChartJS 2.9.4 to 4.4.4.
|
||||
TESTED ON SAFARI : with this new version, the application takes much less RAM, and does not crash at some point. The user can still experience some latencies, but it might be due to the presence of too many time axis labels + the fact that each graphs has its own (for the moment).
|
||||
|
||||
Here is a list of what has been done :
|
||||
- Uprgaded the ChartJS zoom plugin library, and changed the corresponding options in the chart configuration. The previous version was not working with the version 4.4.4 of ChartJS
|
||||
- Installing the date library Luxon and its adpater for ChartJS. This is needed because since version 3.x, time axes need these libraries.
|
||||
- Renamed or moved all needed parameters in the ChartJS configuration.
|
||||
- Changed all `xAxes` and `yAxes` references to `x` and `y`.
|
||||
- Adapted `afterBuildTicks` callbacks with the new signature (only `axis` is given)
|
||||
- Changed all references to `ticks.max|min` : these two properties are one step higher, at the level of the axis, not the ticks
|
||||
|
||||
Here is a list of what needs to be done :
|
||||
- Change the implementation of the callback located in `options.scales.x.ticks` at chart creation in the Chart class, so it considers that the label is a timestamp. Reference : https://www.chartjs.org/docs/latest/axes/labelling.html#creating-custom-tick-formats
|
||||
- Labels of the x axis are not displayed in the desired format, and do not rescale properly on zooming/dezooming. There can be too much labels, that make them rotate and invisible.
|
||||
- The cursor now also displays when the click ends, which is not the same behavior as before.
|
||||
- Make the zoom type toggle work again.
|
||||
- Make the zoom via touchpad less sensitive. The recent tests have shown that the zoom via gesture is very sensitive. Two things can be looked for : 1. see if there is the possibility to adapt the sensitivity of the zoom for the touchpad only or 2. update the library Hammer.js which is used by ChartJS to handle this type of gesture (even if the current version is 0.0.1 version later than the last one, this might be an explanation).
|
||||
- Display only one time axis.
|
||||
|
||||
**Summary**
|
||||
|
||||
- [Documentation](#documentation)
|
||||
|
||||
@@ -41,9 +41,14 @@
|
||||
<script src="externalFiles/eventsource.js"></script>
|
||||
<!-- <script src="externalFiles/d3.min.js"></script> -->
|
||||
<script src="externalFiles/swiper-bundle.min.js"></script>
|
||||
<script src="externalFiles/Chart.bundle.min.js"></script>
|
||||
<!-- <script src="externalFiles/Chart.bundle.min.js"></script> -->
|
||||
<script src="externalFiles/chart.umd.min.js"></script>
|
||||
<script src="externalFiles/luxon.min.js"></script>
|
||||
<script src="externalFiles/chartjs-adapter-luxon.umd.min.js"></script>
|
||||
|
||||
<script src="externalFiles/hammer.js"></script>
|
||||
<script src="externalFiles/chartjs-zoom.js"></script>
|
||||
<script src="externalFiles/chartjs-plugin-zoom.min.js"></script>
|
||||
<!-- <script src="externalFiles/chartjs-zoom.js"></script> -->
|
||||
<script src="jsFiles/SEAWebClientLocalStorage.js"></script>
|
||||
<script src="jsFiles/SEAWebClientResponsivity.js"></script>
|
||||
<script src="jsFiles/SEAWebClientSwiper.js"></script>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"chart.umd.js","sources":["../src/helpers/helpers.core.ts","../src/helpers/helpers.math.ts","../src/helpers/helpers.collection.ts","../src/helpers/helpers.extras.ts","../src/core/core.animator.js","../node_modules/.pnpm/@kurkle+color@0.3.2/node_modules/@kurkle/color/dist/color.esm.js","../src/helpers/helpers.color.ts","../src/core/core.animations.defaults.js","../src/helpers/helpers.intl.ts","../src/core/core.ticks.js","../src/core/core.defaults.js","../src/core/core.layouts.defaults.js","../src/core/core.scale.defaults.js","../src/helpers/helpers.dom.ts","../src/helpers/helpers.canvas.ts","../src/helpers/helpers.config.ts","../src/helpers/helpers.curve.ts","../src/helpers/helpers.easing.ts","../src/helpers/helpers.interpolation.ts","../src/helpers/helpers.options.ts","../src/helpers/helpers.rtl.ts","../src/helpers/helpers.segment.js","../src/core/core.interaction.js","../src/core/core.layouts.js","../src/platform/platform.base.js","../src/platform/platform.basic.js","../src/platform/platform.dom.js","../src/platform/index.js","../src/core/core.animation.js","../src/core/core.animations.js","../src/core/core.datasetController.js","../src/core/core.element.ts","../src/core/core.scale.autoskip.js","../src/core/core.scale.js","../src/core/core.typedRegistry.js","../src/core/core.registry.js","../src/core/core.plugins.js","../src/core/core.config.js","../src/core/core.controller.js","../src/core/core.adapters.ts","../src/controllers/controller.bar.js","../src/controllers/controller.doughnut.js","../src/controllers/controller.polarArea.js","../src/controllers/controller.bubble.js","../src/controllers/controller.line.js","../src/controllers/controller.pie.js","../src/controllers/controller.radar.js","../src/controllers/controller.scatter.js","../src/elements/element.arc.ts","../src/elements/element.line.js","../src/elements/element.point.ts","../src/elements/element.bar.js","../src/scales/scale.category.js","../src/scales/scale.linearbase.js","../src/scales/scale.linear.js","../src/scales/scale.logarithmic.js","../src/scales/scale.radialLinear.js","../src/scales/scale.time.js","../src/scales/scale.timeseries.js","../src/plugins/plugin.colors.ts","../src/plugins/plugin.decimation.js","../src/plugins/plugin.filler/filler.segment.js","../src/plugins/plugin.filler/filler.helper.js","../src/plugins/plugin.filler/filler.options.js","../src/plugins/plugin.filler/filler.target.stack.js","../src/plugins/plugin.filler/simpleArc.js","../src/plugins/plugin.filler/filler.target.js","../src/plugins/plugin.filler/filler.drawing.js","../src/plugins/plugin.filler/index.js","../src/plugins/plugin.legend.js","../src/plugins/plugin.title.js","../src/plugins/plugin.subtitle.js","../src/plugins/plugin.tooltip.js","../src/index.umd.ts"],"sourcesContent":["/**\n * @namespace Chart.helpers\n */\n\nimport type {AnyObject} from '../types/basic.js';\nimport type {ActiveDataPoint, ChartEvent} from '../types/index.js';\n\n/**\n * An empty function that can be used, for example, for optional callback.\n */\nexport function noop() {\n /* noop */\n}\n\n/**\n * Returns a unique id, sequentially generated from a global variable.\n */\nexport const uid = (() => {\n let id = 0;\n return () => id++;\n})();\n\n/**\n * Returns true if `value` is neither null nor undefined, else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */\nexport function isNullOrUndef(value: unknown): value is null | undefined {\n return value === null || typeof value === 'undefined';\n}\n\n/**\n * Returns true if `value` is an array (including typed arrays), else returns false.\n * @param value - The value to test.\n * @function\n */\nexport function isArray<T = unknown>(value: unknown): value is T[] {\n if (Array.isArray && Array.isArray(value)) {\n return true;\n }\n const type = Object.prototype.toString.call(value);\n if (type.slice(0, 7) === '[object' && type.slice(-6) === 'Array]') {\n return true;\n }\n return false;\n}\n\n/**\n * Returns true if `value` is an object (excluding null), else returns false.\n * @param value - The value to test.\n * @since 2.7.0\n */\nexport function isObject(value: unknown): value is AnyObject {\n return value !== null && Object.prototype.toString.call(value) === '[object Object]';\n}\n\n/**\n * Returns true if `value` is a finite number, else returns false\n * @param value - The value to test.\n */\nfunction isNumberFinite(value: unknown): value is number {\n return (typeof value === 'number' || value instanceof Number) && isFinite(+value);\n}\nexport {\n isNumberFinite as isFinite,\n};\n\n/**\n * Returns `value` if finite, else returns `defaultValue`.\n * @param value - The value to return if defined.\n * @param defaultValue - The value to return if `value` is not finite.\n */\nexport function finiteOrDefault(value: unknown, defaultValue: number) {\n return isNumberFinite(value) ? value : defaultValue;\n}\n\n/**\n * Returns `value` if defined, else returns `defaultValue`.\nLine truncated
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Skipped minification because the original files appears to be already minified.
|
||||
* Original file: /npm/chart.js@4.4.4/dist/chart.umd.js
|
||||
*
|
||||
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
||||
*/
|
||||
/*!
|
||||
* Chart.js v4.4.4
|
||||
* https://www.chartjs.org
|
||||
* (c) 2024 Chart.js Contributors
|
||||
* Released under the MIT License
|
||||
*/
|
||||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Chart=e()}(this,(function(){"use strict";var t=Object.freeze({__proto__:null,get Colors(){return Go},get Decimation(){return Qo},get Filler(){return ma},get Legend(){return ya},get SubTitle(){return ka},get Title(){return Ma},get Tooltip(){return Ba}});function e(){}const i=(()=>{let t=0;return()=>t++})();function s(t){return null==t}function n(t){if(Array.isArray&&Array.isArray(t))return!0;const e=Object.prototype.toString.call(t);return"[object"===e.slice(0,7)&&"Array]"===e.slice(-6)}function o(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)}function a(t){return("number"==typeof t||t instanceof Number)&&isFinite(+t)}function r(t,e){return a(t)?t:e}function l(t,e){return void 0===t?e:t}const h=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100:+t/e,c=(t,e)=>"string"==typeof t&&t.endsWith("%")?parseFloat(t)/100*e:+t;function d(t,e,i){if(t&&"function"==typeof t.call)return t.apply(i,e)}function u(t,e,i,s){let a,r,l;if(n(t))if(r=t.length,s)for(a=r-1;a>=0;a--)e.call(i,t[a],a);else for(a=0;a<r;a++)e.call(i,t[a],a);else if(o(t))for(l=Object.keys(t),r=l.length,a=0;a<r;a++)e.call(i,t[l[a]],l[a])}function f(t,e){let i,s,n,o;if(!t||!e||t.length!==e.length)return!1;for(i=0,s=t.length;i<s;++i)if(n=t[i],o=e[i],n.datasetIndex!==o.datasetIndex||n.index!==o.index)return!1;return!0}function g(t){if(n(t))return t.map(g);if(o(t)){const e=Object.create(null),i=Object.keys(t),s=i.length;let n=0;for(;n<s;++n)e[i[n]]=g(t[i[n]]);return e}return t}function p(t){return-1===["__proto__","prototype","constructor"].indexOf(t)}function m(t,e,i,s){if(!p(t))return;const n=e[t],a=i[t];o(n)&&o(a)?x(n,a,s):e[t]=g(a)}function x(t,e,i){const s=n(e)?e:[e],a=s.length;if(!o(t))return t;const r=(i=i||{}).merger||m;let l;for(let e=0;e<a;++e){if(l=s[e],!o(l))continue;const n=Object.keys(l);for(let e=0,s=n.length;e<s;++e)r(n[e],t,l,i)}return t}function b(t,e){return x(t,e,{merger:_})}function _(t,e,i){if(!p(t))return;const s=e[t],n=i[t];o(s)&&o(n)?b(s,n):Object.prototype.hasOwnProperty.call(e,t)||(e[t]=g(n))}const y={"":t=>t,x:t=>t.x,y:t=>t.y};function v(t){const e=t.split("."),i=[];let s="";for(const t of e)s+=t,s.endsWith("\\")?s=s.slice(0,-1)+".":(i.push(s),s="");return i}function M(t,e){const i=y[e]||(y[e]=function(t){const e=v(t);return t=>{for(const i of e){if(""===i)break;t=t&&t[i]}return t}}(e));return i(t)}function w(t){return t.charAt(0).toUpperCase()+t.slice(1)}const k=t=>void 0!==t,S=t=>"function"==typeof t,P=(t,e)=>{if(t.size!==e.size)return!1;for(const i of t)if(!e.has(i))return!1;return!0};function D(t){return"mouseup"===t.type||"click"===t.type||"contextmenu"===t.type}const C=Math.PI,O=2*C,A=O+C,T=Number.POSITIVE_INFINITY,L=C/180,E=C/2,R=C/4,I=2*C/3,z=Math.log10,F=Math.sign;function V(t,e,i){return Math.abs(t-e)<i}function B(t){const e=Math.round(t);t=V(t,e,t/1e3)?e:t;const i=Math.pow(10,Math.floor(z(t))),s=t/i;return(s<=1?1:s<=2?2:s<=5?5:10)*i}function W(t){const e=[],i=Math.sqrt(t);let s;for(s=1;s<i;s++)t%s==0&&(e.push(s),e.push(t/s));return i===(0|i)&&e.push(i),e.sort(((t,e)=>t-e)).pop(),e}function N(t){return!isNaN(parseFloat(t))&&isFinite(t)}function H(t,e){const i=Math.round(t);return i-e<=t&&i+e>=t}function j(t,e,i){let s,n,o;for(s=0,n=t.length;s<n;s++)o=t[s][i],isNaN(o)||(e.min=Math.min(e.min,o),e.max=Math.max(e.max,o))}function $(t){return t*(C/180)}function Y(t){return t*(180/C)}function U(t){if(!a(t))return;let e=1,i=0;for(;Math.round(t*e)/e!==t;)e*=10,i++;return i}function X(t,e){const i=e.x-t.x,s=e.y-t.y,n=Math.sqrt(i*i+s*s);let o=Math.atan2(s,i);return o<-.5*C&&(o+=O),{angle:o,distance:n}}function q(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))}function K(t,e){return(t-e+A)%O-C}function G(t){return(t%O+O)%O}function Z(t,e,i,s){const n=G(t),o=G(e),a=G(i),r=G(o-n),l=G(a-n),h=G(n-o),c=G(n-a);return n===o||n===a||s&&o===a||r>l&&h<c}function J(t,e,i){return Math.max(e,Math.min(i,t))}function Q(t){return J(t,-32768,32767)}function tt(t,e,i,s=1e-6){return t>=Math.min(e,i)-s&&t<=Math.max(e,i)+s}function et(t,e,i){i=i||(i=>t[i]<e);let s,n=t.length-1,o=0;for(;n-o>1;)s=o+n>>1,i(s)?o=s:n=s;return{lo:o,hi:n}}const it=(t,e,i,s)=>et(t,i,s?s=>{const n=t[s][e];return n<i||n===i&&t[s+1][e]===i}:s=>t[s][e]<i),st=(t,e,i)=>et(t,i,(s=>t[s][e]>=i));function nt(t,e,i){let s=0,n=t.length;for(;s<n&&t[s]<e;)s++;for(;n>s&&t[n-1]>i;)n--;return s>0||n<t.length?t.slice(s,n):t}const ot=["push","pop","shift","splice","unshift"];function at(t,e){t._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),ot.forEach((e=>{const i="_onData"+w(e),s=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value(...e){const n=s.apply(this,e);return t._chartjs.listeners.forEach((t=>{"function"==typeof t[i]&&t[i](...e)})),n}})})))}function rt(t,e){Line truncated
|
||||
/*!
|
||||
* @kurkle/color v0.3.2
|
||||
* https://github.com/kurkle/color#readme
|
||||
* (c) 2023 Jukka Kurkela
|
||||
* Released under the MIT License
|
||||
*/function _t(t){return t+.5|0}const yt=(t,e,i)=>Math.max(Math.min(t,i),e);function vt(t){return yt(_t(2.55*t),0,255)}function Mt(t){return yt(_t(255*t),0,255)}function wt(t){return yt(_t(t/2.55)/100,0,1)}function kt(t){return yt(_t(100*t),0,100)}const St={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Pt=[..."0123456789ABCDEF"],Dt=t=>Pt[15&t],Ct=t=>Pt[(240&t)>>4]+Pt[15&t],Ot=t=>(240&t)>>4==(15&t);function At(t){var e=(t=>Ot(t.r)&&Ot(t.g)&&Ot(t.b)&&Ot(t.a))(t)?Dt:Ct;return t?"#"+e(t.r)+e(t.g)+e(t.b)+((t,e)=>t<255?e(t):"")(t.a,e):void 0}const Tt=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function Lt(t,e,i){const s=e*Math.min(i,1-i),n=(e,n=(e+t/30)%12)=>i-s*Math.max(Math.min(n-3,9-n,1),-1);return[n(0),n(8),n(4)]}function Et(t,e,i){const s=(s,n=(s+t/60)%6)=>i-i*e*Math.max(Math.min(n,4-n,1),0);return[s(5),s(3),s(1)]}function Rt(t,e,i){const s=Lt(t,1,.5);let n;for(e+i>1&&(n=1/(e+i),e*=n,i*=n),n=0;n<3;n++)s[n]*=1-e-i,s[n]+=e;return s}function It(t){const e=t.r/255,i=t.g/255,s=t.b/255,n=Math.max(e,i,s),o=Math.min(e,i,s),a=(n+o)/2;let r,l,h;return n!==o&&(h=n-o,l=a>.5?h/(2-n-o):h/(n+o),r=function(t,e,i,s,n){return t===n?(e-i)/s+(e<i?6:0):e===n?(i-t)/s+2:(t-e)/s+4}(e,i,s,h,n),r=60*r+.5),[0|r,l||0,a]}function zt(t,e,i,s){return(Array.isArray(e)?t(e[0],e[1],e[2]):t(e,i,s)).map(Mt)}function Ft(t,e,i){return zt(Lt,t,e,i)}function Vt(t){return(t%360+360)%360}function Bt(t){const e=Tt.exec(t);let i,s=255;if(!e)return;e[5]!==i&&(s=e[6]?vt(+e[5]):Mt(+e[5]));const n=Vt(+e[2]),o=+e[3]/100,a=+e[4]/100;return i="hwb"===e[1]?function(t,e,i){return zt(Rt,t,e,i)}(n,o,a):"hsv"===e[1]?function(t,e,i){return zt(Et,t,e,i)}(n,o,a):Ft(n,o,a),{r:i[0],g:i[1],b:i[2],a:s}}const Wt={x:"dark",Z:"light",Y:"re",X:"blu",W:"gr",V:"medium",U:"slate",A:"ee",T:"ol",S:"or",B:"ra",C:"lateg",D:"ights",R:"in",Q:"turquois",E:"hi",P:"ro",O:"al",N:"le",M:"de",L:"yello",F:"en",K:"ch",G:"arks",H:"ea",I:"ightg",J:"wh"},Nt={OiceXe:"f0f8ff",antiquewEte:"faebd7",aqua:"ffff",aquamarRe:"7fffd4",azuY:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"0",blanKedOmond:"ffebcd",Xe:"ff",XeviTet:"8a2be2",bPwn:"a52a2a",burlywood:"deb887",caMtXe:"5f9ea0",KartYuse:"7fff00",KocTate:"d2691e",cSO:"ff7f50",cSnflowerXe:"6495ed",cSnsilk:"fff8dc",crimson:"dc143c",cyan:"ffff",xXe:"8b",xcyan:"8b8b",xgTMnPd:"b8860b",xWay:"a9a9a9",xgYF:"6400",xgYy:"a9a9a9",xkhaki:"bdb76b",xmagFta:"8b008b",xTivegYF:"556b2f",xSange:"ff8c00",xScEd:"9932cc",xYd:"8b0000",xsOmon:"e9967a",xsHgYF:"8fbc8f",xUXe:"483d8b",xUWay:"2f4f4f",xUgYy:"2f4f4f",xQe:"ced1",xviTet:"9400d3",dAppRk:"ff1493",dApskyXe:"bfff",dimWay:"696969",dimgYy:"696969",dodgerXe:"1e90ff",fiYbrick:"b22222",flSOwEte:"fffaf0",foYstWAn:"228b22",fuKsia:"ff00ff",gaRsbSo:"dcdcdc",ghostwEte:"f8f8ff",gTd:"ffd700",gTMnPd:"daa520",Way:"808080",gYF:"8000",gYFLw:"adff2f",gYy:"808080",honeyMw:"f0fff0",hotpRk:"ff69b4",RdianYd:"cd5c5c",Rdigo:"4b0082",ivSy:"fffff0",khaki:"f0e68c",lavFMr:"e6e6fa",lavFMrXsh:"fff0f5",lawngYF:"7cfc00",NmoncEffon:"fffacd",ZXe:"add8e6",ZcSO:"f08080",Zcyan:"e0ffff",ZgTMnPdLw:"fafad2",ZWay:"d3d3d3",ZgYF:"90ee90",ZgYy:"d3d3d3",ZpRk:"ffb6c1",ZsOmon:"ffa07a",ZsHgYF:"20b2aa",ZskyXe:"87cefa",ZUWay:"778899",ZUgYy:"778899",ZstAlXe:"b0c4de",ZLw:"ffffe0",lime:"ff00",limegYF:"32cd32",lRF:"faf0e6",magFta:"ff00ff",maPon:"800000",VaquamarRe:"66cdaa",VXe:"cd",VScEd:"ba55d3",VpurpN:"9370db",VsHgYF:"3cb371",VUXe:"7b68ee",VsprRggYF:"fa9a",VQe:"48d1cc",VviTetYd:"c71585",midnightXe:"191970",mRtcYam:"f5fffa",mistyPse:"ffe4e1",moccasR:"ffe4b5",navajowEte:"ffdead",navy:"80",Tdlace:"fdf5e6",Tive:"808000",TivedBb:"6b8e23",Sange:"ffa500",SangeYd:"ff4500",ScEd:"da70d6",pOegTMnPd:"eee8aa",pOegYF:"98fb98",pOeQe:"afeeee",pOeviTetYd:"db7093",papayawEp:"ffefd5",pHKpuff:"ffdab9",peru:"cd853f",pRk:"ffc0cb",plum:"dda0dd",powMrXe:"b0e0e6",purpN:"800080",YbeccapurpN:"663399",Yd:"ff0000",Psybrown:"bc8f8f",PyOXe:"4169e1",saddNbPwn:"8b4513",sOmon:"fa8072",sandybPwn:"f4a460",sHgYF:"2e8b57",sHshell:"fff5ee",siFna:"a0522d",silver:"c0c0c0",skyXe:"87ceeb",UXe:"6a5acd",UWay:"708090",UgYy:"708090",snow:"fffafa",sprRggYF:"ff7f",stAlXe:"4682b4",tan:"d2b48c",teO:"8080",tEstN:"d8bfd8",tomato:"ff6347",Qe:"40e0d0",viTet:"ee82ee",JHt:"f5deb3",wEte:"ffffff",wEtesmoke:"f5f5f5",Lw:"ffff00",LwgYF:"9acd32"};let Ht;function jt(t){Ht||(Ht=function(){const t={},e=Object.keys(Nt),i=Object.keys(Wt);let s,n,o,a,r;for(s=0;s<e.length;s++){for(a=r=e[s],n=0;n<i.length;n++)o=i[n],r=r.replace(o,Wt[o]);o=parseInt(Nt[a],16),t[r]=[o>>16&255,o>>8&255,255&o]}return t}(),Ht.transparent=[0,0,0,0]);const e=Ht[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:4===e.length?e[3]:255}}const $t=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;const Yt=t=>t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055,Ut=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4);function Xt(t,e,i){if(t){let s=It(t);s[e]=Math.max(0,Math.min(s[e]+s[e]*i,0===e?360:1)),Line truncated
|
||||
//# sourceMappingURL=chart.umd.js.map
|
||||
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* chartjs-adapter-luxon v1.3.1
|
||||
* https://www.chartjs.org
|
||||
* (c) 2023 chartjs-adapter-luxon Contributors
|
||||
* Released under the MIT license
|
||||
*/
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(require("chart.js"),require("luxon")):"function"==typeof define&&define.amd?define(["chart.js","luxon"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Chart,e.luxon)}(this,(function(e,t){"use strict";const n={datetime:t.DateTime.DATETIME_MED_WITH_SECONDS,millisecond:"h:mm:ss.SSS a",second:t.DateTime.TIME_WITH_SECONDS,minute:t.DateTime.TIME_SIMPLE,hour:{hour:"numeric"},day:{day:"numeric",month:"short"},week:"DD",month:{month:"short",year:"numeric"},quarter:"'Q'q - yyyy",year:{year:"numeric"}};e._adapters._date.override({_id:"luxon",_create:function(e){return t.DateTime.fromMillis(e,this.options)},init(e){this.options.locale||(this.options.locale=e.locale)},formats:function(){return n},parse:function(e,n){const i=this.options,r=typeof e;return null===e||"undefined"===r?null:("number"===r?e=this._create(e):"string"===r?e="string"==typeof n?t.DateTime.fromFormat(e,n,i):t.DateTime.fromISO(e,i):e instanceof Date?e=t.DateTime.fromJSDate(e,i):"object"!==r||e instanceof t.DateTime||(e=t.DateTime.fromObject(e,i)),e.isValid?e.valueOf():null)},format:function(e,t){const n=this._create(e);return"string"==typeof t?n.toFormat(t):n.toLocaleString(t)},add:function(e,t,n){const i={};return i[n]=t,this._create(e).plus(i).valueOf()},diff:function(e,t,n){return this._create(e).diff(this._create(t)).as(n).valueOf()},startOf:function(e,t,n){if("isoWeek"===t){n=Math.trunc(Math.min(Math.max(0,n),6));const t=this._create(e);return t.minus({days:(t.weekday-n+7)%7}).startOf("day").valueOf()}return t?this._create(e).startOf(t).valueOf():e},endOf:function(e,t){return this._create(e).endOf(t).valueOf()}})}));
|
||||
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* chartjs-plugin-zoom v2.0.1
|
||||
* undefined
|
||||
* (c) 2016-2023 chartjs-plugin-zoom Contributors
|
||||
* Released under the MIT License
|
||||
*/
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("chart.js"),require("hammerjs"),require("chart.js/helpers")):"function"==typeof define&&define.amd?define(["chart.js","hammerjs","chart.js/helpers"],t):(e="undefined"!=typeof globalThis?globalThis:e||self).ChartZoom=t(e.Chart,e.Hammer,e.Chart.helpers)}(this,(function(e,t,n){"use strict";function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var a=o(t);const i=e=>e&&e.enabled&&e.modifierKey,c=(e,t)=>e&&t[e+"Key"],r=(e,t)=>e&&!t[e+"Key"];function s(e,t,n){return void 0===e||("string"==typeof e?-1!==e.indexOf(t):"function"==typeof e&&-1!==e({chart:n}).indexOf(t))}function l(e,t){return"function"==typeof e&&(e=e({chart:t})),"string"==typeof e?{x:-1!==e.indexOf("x"),y:-1!==e.indexOf("y")}:{x:!1,y:!1}}function u(e,t,o){const{mode:a="xy",scaleMode:i,overScaleMode:c}=e||{},r=function({x:e,y:t},n){const o=n.scales,a=Object.keys(o);for(let n=0;n<a.length;n++){const i=o[a[n]];if(t>=i.top&&t<=i.bottom&&e>=i.left&&e<=i.right)return i}return null}(t,o),s=l(a,o),u=l(i,o);if(c){const e=l(c,o);for(const t of["x","y"])e[t]&&(u[t]=s[t],s[t]=!1)}if(r&&u[r.axis])return[r];const m=[];return n.each(o.scales,(function(e){s[e.axis]&&m.push(e)})),m}const m=new WeakMap;function d(e){let t=m.get(e);return t||(t={originalScaleLimits:{},updatedScaleLimits:{},handlers:{},panDelta:{}},m.set(e,t)),t}function f(e,t,n){const o=e.max-e.min,a=o*(t-1),i=e.isHorizontal()?n.x:n.y,c=Math.max(0,Math.min(1,(e.getValueForPixel(i)-e.min)/o||0));return{min:a*c,max:a*(1-c)}}function p(e,t,o,a,i){let c=o[a];if("original"===c){const o=e.originalScaleLimits[t.id][a];c=n.valueOrDefault(o.options,o.scale)}return n.valueOrDefault(c,i)}function h(e,{min:t,max:n},o,a=!1){const i=d(e.chart),{id:c,axis:r,options:s}=e,l=o&&(o[c]||o[r])||{},{minRange:u=0}=l,m=p(i,e,l,"min",-1/0),f=p(i,e,l,"max",1/0),h=a?Math.max(n-t,u):e.max-e.min,x=(h-n+t)/2;return n+=x,(t-=x)<m?(t=m,n=Math.min(m+h,f)):n>f&&(n=f,t=Math.max(f-h,m)),s.min=t,s.max=n,i.updatedScaleLimits[e.id]={min:t,max:n},e.parse(t)!==e.min||e.parse(n)!==e.max}const x=e=>0===e||isNaN(e)?0:e<0?Math.min(Math.round(e),-1):Math.max(Math.round(e),1);const g={second:500,minute:3e4,hour:18e5,day:432e5,week:3024e5,month:1296e6,quarter:5184e6,year:157248e5};function y(e,t,n,o=!1){const{min:a,max:i,options:c}=e,r=c.time&&c.time.round,s=g[r]||0,l=e.getValueForPixel(e.getPixelForValue(a+s)-t),u=e.getValueForPixel(e.getPixelForValue(i+s)-t),{min:m=-1/0,max:d=1/0}=o&&n&&n[e.axis]||{};return!!(isNaN(l)||isNaN(u)||l<m||u>d)||h(e,{min:l,max:u},n,o)}function b(e,t,n){return y(e,t,n,!0)}const v={category:function(e,t,n,o){const a=f(e,t,n);return e.min===e.max&&t<1&&function(e){const t=e.getLabels().length-1;e.min>0&&(e.min-=1),e.max<t&&(e.max+=1)}(e),h(e,{min:e.min+x(a.min),max:e.max-x(a.max)},o,!0)},default:function(e,t,n,o){const a=f(e,t,n);return h(e,{min:e.min+a.min,max:e.max-a.max},o,!0)}},w={default:function(e,t,n,o){h(e,function(e,t,n){const o=e.getValueForPixel(t),a=e.getValueForPixel(n);return{min:Math.min(o,a),max:Math.max(o,a)}}(e,t,n),o,!0)}},z={category:function(e,t,n){const o=e.getLabels().length-1;let{min:a,max:i}=e;const c=Math.max(i-a,1),r=Math.round(function(e){return e.isHorizontal()?e.width:e.height}(e)/Math.max(c,10)),s=Math.round(Math.abs(t/r));let l;return t<-r?(i=Math.min(i+s,o),a=1===c?i:i-c,l=i===o):t>r&&(a=Math.max(0,a-s),i=1===c?a:a+c,l=0===a),h(e,{min:a,max:i},n)||l},default:y,logarithmic:b,timeseries:b};function M(e,t){n.each(e,((n,o)=>{t[o]||delete e[o]}))}function k(e,t){const{scales:o}=e,{originalScaleLimits:a,updatedScaleLimits:i}=t;return n.each(o,(function(e){(function(e,t,n){const{id:o,options:{min:a,max:i}}=e;if(!t[o]||!n[o])return!0;const c=n[o];return c.min!==a||c.max!==i})(e,a,i)&&(a[e.id]={min:{scale:e.min,options:e.options.min},max:{scale:e.max,options:e.options.max}})})),M(a,o),M(i,o),a}function S(e,t,o,a){const i=v[e.type]||v.default;n.callback(i,[e,t,o,a])}function P(e,t,o,a,i){const c=w[e.type]||w.default;n.callback(c,[e,t,o,a,i])}function D(e){const t=e.chartArea;return{x:(t.left+t.right)/2,y:(t.top+t.bottom)/2}}function j(e,t,o="none"){const{x:a=1,y:i=1,focalPoint:c=D(e)}="number"==typeof t?{x:t,y:t}:t,r=d(e),{options:{limits:s,zoom:l}}=r;k(e,r);const m=1!==a,f=1!==i,p=u(l,c,e);n.each(p||e.scales,(function(e){e.isHorizontal()&&m?S(e,a,c,s):!e.isHorizontal()&&f&&S(e,i,c,s)})),e.update(o),n.callback(l.onZoom,[{chart:e}])}function O(e,t,o,a="none"){const i=d(e),{options:{limits:c,zoom:r}}=i,{mode:l="xy"}=r;k(e,i);const u=s(l,"x",e),m=s(l,"y",e);n.each(e.scales,(function(e){e.isHorizontal()&&u?P(e,t.x,o.x,c):!e.isHorizontal()&&m&&P(e,t.y,o.y,c)})),e.update(a),n.callback(r.onZoom,[{chart:e}])}function C(e){const t=d(e);let o=1,a=1;return n.each(e.scales,(function(e){const i=function(e,t){const o=e.originalScaleLimits[t];if(!o)return;const{min:a,max:i}=o;return n.valueOrDefault(i.options,i.scale)-n.valueOrDefault(a.options,a.scale)}(t,e.id);if(i){const t=Math.round(i/(e.max-e.min)*10Line truncated
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
var luxon=function(e){"use strict";function L(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,function(e){e=function(e,t){if("object"!=typeof e||null===e)return e;var n=e[Symbol.toPrimitive];if(void 0===n)return("string"===t?String:Number)(e);n=n.call(e,t||"default");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}(e,"string");return"symbol"==typeof e?e:String(e)}(r.key),r)}}function i(e,t,n){t&&L(e.prototype,t),n&&L(e,n),Object.defineProperty(e,"prototype",{writable:!1})}function l(){return(l=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n,r=arguments[t];for(n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e}).apply(this,arguments)}function o(e,t){e.prototype=Object.create(t.prototype),z(e.prototype.constructor=e,t)}function j(e){return(j=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function z(e,t){return(z=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(e,t){return e.__proto__=t,e})(e,t)}function A(e,t,n){return(A=function(){if("undefined"!=typeof Reflect&&Reflect.construct&&!Reflect.construct.sham){if("function"==typeof Proxy)return 1;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),1}catch(e){}}}()?Reflect.construct.bind():function(e,t,n){var r=[null];r.push.apply(r,t);t=new(Function.bind.apply(e,r));return n&&z(t,n.prototype),t}).apply(null,arguments)}function q(e){var n="function"==typeof Map?new Map:void 0;return function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw new TypeError("Super expression must either be null or a function");if(void 0!==n){if(n.has(e))return n.get(e);n.set(e,t)}function t(){return A(e,arguments,j(this).constructor)}return t.prototype=Object.create(e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),z(t,e)}(e)}function _(e,t){if(null==e)return{};for(var n,r={},i=Object.keys(e),o=0;o<i.length;o++)n=i[o],0<=t.indexOf(n)||(r[n]=e[n]);return r}function U(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n<t;n++)r[n]=e[n];return r}function R(e,t){var n,r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(r)return(r=r.call(e)).next.bind(r);if(Array.isArray(e)||(r=function(e,t){var n;if(e)return"string"==typeof e?U(e,t):"Map"===(n="Object"===(n=Object.prototype.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:n)||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?U(e,t):void 0}(e))||t&&e&&"number"==typeof e.length)return r&&(e=r),n=0,function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var t=function(e){function t(){return e.apply(this,arguments)||this}return o(t,e),t}(q(Error)),P=function(t){function e(e){return t.call(this,"Invalid DateTime: "+e.toMessage())||this}return o(e,t),e}(t),Y=function(t){function e(e){return t.call(this,"Invalid Interval: "+e.toMessage())||this}return o(e,t),e}(t),H=function(t){function e(e){return t.call(this,"Invalid Duration: "+e.toMessage())||this}return o(e,t),e}(t),w=function(e){function t(){return e.apply(this,arguments)||this}return o(t,e),t}(t),J=function(t){function e(e){return t.call(this,"Invalid unit "+e)||this}return o(e,t),e}(t),u=function(e){function t(){return e.apply(this,arguments)||this}return o(t,e),t}(t),n=function(e){function t(){return e.call(this,"Zone is an abstract class")||this}return o(t,e),t}(t),t="numeric",r="short",a="long",G={year:t,month:t,day:t},$={year:t,month:r,day:t},B={year:t,month:r,day:t,weekday:r},Q={year:t,month:a,day:t},K={year:t,month:a,day:t,weekday:a},X={hour:t,minute:t},ee={hour:t,minute:t,second:t},te={hour:t,minute:t,second:t,timeZoneName:r},ne={hour:t,minute:t,second:t,timeZoneName:a},re={hour:t,minute:t,hourCycle:"h23"},ie={hour:t,minute:t,second:t,hourCycle:"h23"},oe={hour:t,minute:t,second:t,hourCycle:"h23",timeZoneName:r},ae={hour:t,minute:t,second:t,hourCycle:"h23",timeZoneName:a},se={year:t,month:t,day:t,hour:t,minute:t},ue={year:t,month:t,day:t,hour:t,minute:t,second:t},le={year:t,month:r,day:t,hour:t,minute:t},ce={year:t,month:r,day:t,hour:t,minute:t,second:t},fe={year:t,month:r,day:t,weekday:r,hour:t,minute:t},de={year:t,month:a,day:t,hour:t,minute:t,timeZoneName:r},he={year:t,month:a,day:t,hour:t,minute:t,second:t,timeZoneName:r},me={year:t,month:a,day:t,weekday:a,hour:t,minute:t,timeZoneName:a},ye={year:t,month:a,day:t,weekday:a,hour:t,minute:t,second:t,timeZoneName:a},s=function(){function e(){}var t=e.prototype;return t.offsetName=function(e,t){throw new n},t.formatOffset=function(e,t){throw new n},t.offset=function(e){throw neLine truncated
|
||||
@@ -395,9 +395,11 @@ function loadCurvesSettingsPopup(){
|
||||
let graphs = (function (){
|
||||
let dataset_to_graph_map = {}; // a dictionnary mapping a variable name to a two values array, containing its graph index and its position inside the graph
|
||||
let blocks, liveMode=true, top_vars=[], bottom_vars=[];
|
||||
let legendFlag = false, currentZoomMode = isTouchDevice ? 'xy' : 'x';
|
||||
let legendFlag = false;
|
||||
let currentZoomMode = isTouchDevice ? 'xy' : 'x';
|
||||
let prevTime = null, prevMin = null, prevMax = null, prevGraph = null; // zoom speed limitation
|
||||
let cursorLinePos = null; // the position of the cursor line (given by its x value)
|
||||
let clickMode = 0; // 1: mouse is down, 2: pan is active, 0: after mouse down
|
||||
|
||||
let type = 'linear'; // type of graphs axis to display
|
||||
|
||||
@@ -601,8 +603,8 @@ let graphs = (function (){
|
||||
* @returns If the minimun y-value of all the curves of the charts is greater than the maximum y-value (same)
|
||||
*/
|
||||
function autoScale(chart) {
|
||||
axis = chart.options.scales.yAxes[0];
|
||||
tax = chart.options.scales.xAxes[0].ticks;
|
||||
ay = chart.options.scales.y;
|
||||
ax = chart.options.scales.x;
|
||||
datasets = chart.data.datasets;
|
||||
let max = -1e99;
|
||||
let min = 1e99;
|
||||
@@ -613,8 +615,8 @@ let graphs = (function (){
|
||||
for (let i = 0; i < datasets.length; i++){
|
||||
ds = datasets[i];
|
||||
if (ds.borderWidth == 1) continue;
|
||||
let lmax = maxAr(ds.data, tax.min, tax.max);
|
||||
let lmin = minAr(ds.data, tax.min, tax.max);
|
||||
let lmax = maxAr(ds.data, ax.min, ax.max);
|
||||
let lmin = minAr(ds.data, ax.min, ax.max);
|
||||
if(lmax > max)
|
||||
max = lmax;
|
||||
if(lmin < min)
|
||||
@@ -648,8 +650,8 @@ let graphs = (function (){
|
||||
}
|
||||
extraMin = Math.min(min - ystep * 0.5, extraMin);
|
||||
extraMax = Math.max(max + ystep * 0.5, extraMax);
|
||||
if (min >= axis.ticks.min && axis.ticks.min >= extraMin &&
|
||||
max <= axis.ticks.max && axis.ticks.max <= extraMax) {
|
||||
if (min >= ay.min && ay.min >= extraMin &&
|
||||
max <= ay.max && ay.max <= extraMax) {
|
||||
//console.log('NOCHANGE', max, axis.ticks.max, extraMax)
|
||||
return; // do not yet change
|
||||
}
|
||||
@@ -658,8 +660,8 @@ let graphs = (function (){
|
||||
min = extraMin;
|
||||
max = extraMax;
|
||||
}
|
||||
axis.min = axis.ticks.min = min;
|
||||
axis.max = axis.ticks.max = max;
|
||||
ay.min = min;
|
||||
ay.max = max;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -701,9 +703,13 @@ let graphs = (function (){
|
||||
legendFlag = true;
|
||||
let trect = evt.target.getBoundingClientRect();
|
||||
let X = evt.clientX - trect.x, Y = evt.clientY - trect.y;
|
||||
menuGraphicsPopup.hide();
|
||||
showLegends(true, false);
|
||||
cursorLine(X);
|
||||
if (X == cursorLinePos) {
|
||||
cursorLine(null);
|
||||
} else {
|
||||
menuGraphicsPopup.hide();
|
||||
showLegends(true, false);
|
||||
cursorLine(X);
|
||||
}
|
||||
setLiveMode();
|
||||
update();
|
||||
for (let gr of graph_array.slice(0, ngraphs)) {
|
||||
@@ -714,7 +720,18 @@ let graphs = (function (){
|
||||
}
|
||||
}
|
||||
}
|
||||
container.addEventListener('click', clickHandler)
|
||||
function mouseDown(evt) {
|
||||
clickMode = 1;
|
||||
}
|
||||
function mouseUp(evt) {
|
||||
if (clickMode == 1) { // mouse was down, but no pan happend
|
||||
clickHandler(evt);
|
||||
}
|
||||
clickMode = 0;
|
||||
}
|
||||
// container.addEventListener('click', clickHandler)
|
||||
container.addEventListener('mousedown', mouseDown);
|
||||
container.addEventListener('mouseup', mouseUp);
|
||||
|
||||
/**
|
||||
* Sets (overwrite) the data (curve) of the given variable
|
||||
@@ -788,8 +805,8 @@ let graphs = (function (){
|
||||
* @returns When data is received (no need to autoScale and update as it is done in reloadData)
|
||||
*/
|
||||
function checkReload(graph){
|
||||
let tk = graph.chart.options.scales.xAxes[0].ticks;
|
||||
let xmin = tk.min, xmax = tk.max;
|
||||
let ax = graph.chart.options.scales.x;
|
||||
let xmin = ax.min, xmax = ax.max;
|
||||
/*
|
||||
if (xmax < now()-100000) { // was 100000 = 100sec
|
||||
if (liveMode) console.log('UPDATES OFF?')
|
||||
@@ -828,14 +845,14 @@ let graphs = (function (){
|
||||
* @param {*} graph - The graph Object on which the zoom callback has to be called
|
||||
*/
|
||||
function zoomCallback(graph){
|
||||
let tk, min, max;
|
||||
let a, min, max;
|
||||
if (currentZoomMode == 'y') {
|
||||
tk = graph.chart.options.scales.yAxes[0].ticks;
|
||||
a = graph.chart.options.scales.y;
|
||||
} else {
|
||||
tk = graph.chart.options.scales.xAxes[0].ticks;
|
||||
a = graph.chart.options.scales.x;
|
||||
}
|
||||
min = tk.min;
|
||||
max = tk.max;
|
||||
min = a.min;
|
||||
max = a.max;
|
||||
if (!isTouchDevice) {
|
||||
/*
|
||||
if (prevGraph != graph) {
|
||||
@@ -859,8 +876,8 @@ let graphs = (function (){
|
||||
*/
|
||||
}
|
||||
if (currentZoomMode == 'y') {
|
||||
tk.min = min;
|
||||
tk.max = max;
|
||||
a.min = min;
|
||||
a.max = max;
|
||||
graph.setAutoScale(false);
|
||||
} else {
|
||||
if (liveMode && max < lastTime) setLiveMode(false);
|
||||
@@ -924,8 +941,9 @@ let graphs = (function (){
|
||||
* @param {*} graph - The graph for which the function has to be called
|
||||
*/
|
||||
function panCallback(graph){
|
||||
let tk = graph.chart.options.scales.xAxes[0].ticks;
|
||||
let xmin = tk.min, xmax = tk.max;
|
||||
let ax = graph.chart.options.scales.x;
|
||||
let xmin = ax.min, xmax = ax.max;
|
||||
clickMode = 2; // mouse pan mode
|
||||
if (liveMode && xmax < lastTime) setLiveMode(false);
|
||||
setMinMax(xmin,xmax);
|
||||
update();
|
||||
@@ -1363,119 +1381,138 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
parent.appendChild(canvas);
|
||||
let ctx = canvas.getContext("2d");
|
||||
let self = this;
|
||||
chart = new Chart(ctx, {
|
||||
type: 'scatter',
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation:{duration:0},
|
||||
scales: {
|
||||
yAxes: [{ticks: {
|
||||
beginAtZero: false,
|
||||
mirror: true,
|
||||
padding: -10,
|
||||
//workaround for proper number format
|
||||
callback: function(label, index, labels) {
|
||||
if(index == 0 || index == labels.length-1)
|
||||
return "";
|
||||
return strFormat(label);
|
||||
}
|
||||
},
|
||||
gridLines:{drawTicks:false},
|
||||
scaleLabel: false, // {display: true, labelString: y_label},
|
||||
type: scaleType,
|
||||
position: 'right',
|
||||
afterBuildTicks: function(axis, ticks) {
|
||||
if (scaleType == "logarithmic" && ticks.length <= 4) {
|
||||
y1 = ticks[0];
|
||||
y0 = ticks.slice(-1)[0];
|
||||
span = y1 - y0;
|
||||
step = Math.abs(span * 0.3).toExponential(0);
|
||||
if (step[0] > '5') {
|
||||
step = '5' + step.substr(1);
|
||||
} else if (step[0] > '2') {
|
||||
step = '2' + step.substr(1);
|
||||
}
|
||||
step = Number.parseFloat(step);
|
||||
ticks = [y1];
|
||||
for (let yt = Math.ceil(y1 / step) * step; yt > y0; yt -= step) {
|
||||
ticks.push(yt);
|
||||
}
|
||||
ticks.push(y0);
|
||||
}
|
||||
return ticks
|
||||
},
|
||||
}],
|
||||
xAxes: [{
|
||||
scaleLabel: false,//{display: true, labelString: x_label},
|
||||
type: 'time',
|
||||
time: {
|
||||
displayFormats: {'millisecond': 'HH:mm:ss.SSS', 'second': 'HH:mm:ss', 'minute': 'HH:mm','hour': 'dd HH:mm', 'day': 'dd MMM DD', 'week': 'MMM DD', 'month': 'MMM DD'},
|
||||
},
|
||||
ticks: { padding: -20,
|
||||
callback: function(label, index, labels) {
|
||||
let l = labels.length - 1;
|
||||
if (index == 0 || index == l) return "";
|
||||
if (index == 1 || index == l - 1) {
|
||||
// skip first and / or last label, if too close to the end
|
||||
let minstep = 0.05 * (labels[l].value - labels[0].value);
|
||||
if (index == 1) {
|
||||
if (labels[1].value - labels[0].value < minstep) return "";
|
||||
} else {
|
||||
if (labels[l].value - labels[l-1].value < minstep) return "";
|
||||
}
|
||||
}
|
||||
hourofday = /\S+ (\d+:00)/.exec(label);
|
||||
if (hourofday && hourofday[1] != '00:00') {
|
||||
return hourofday[1];
|
||||
}
|
||||
return label;
|
||||
|
||||
let chart_options = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation:{duration:0},
|
||||
scales: {
|
||||
y:{
|
||||
beginAtZero: false,
|
||||
ticks:{
|
||||
mirror: true,
|
||||
padding: -10,
|
||||
callback: function(label, index, labels) {
|
||||
if(index == 0 || index == labels.length-1)
|
||||
return "";
|
||||
return strFormat(label);
|
||||
}
|
||||
},
|
||||
grid:{drawTicks:false},
|
||||
title: false, //Former scaleLabel
|
||||
type: scaleType,
|
||||
position: 'right',
|
||||
afterBuildTicks: function(axis) {
|
||||
let ticks = axis.ticks
|
||||
if (scaleType == "logarithmic" && ticks.length <= 4) {
|
||||
y1 = ticks[0];
|
||||
y0 = ticks.slice(-1)[0];
|
||||
span = y1 - y0;
|
||||
step = Math.abs(span * 0.3).toExponential(0);
|
||||
if (step[0] > '5') {
|
||||
step = '5' + step.substr(1);
|
||||
} else if (step[0] > '2') {
|
||||
step = '2' + step.substr(1);
|
||||
}
|
||||
},
|
||||
afterBuildTicks: function(axis, ticks) {
|
||||
if (!ticks || ticks.length <= 2) return ticks;
|
||||
first = ticks[0].value;
|
||||
step = ticks[1].value - first;
|
||||
offset = (first - axis._adapter.startOf(first, 'day')) % step;
|
||||
let start = 0;
|
||||
if (ticks[0].value - offset < axis.min) start = 1;
|
||||
let v = axis.min;
|
||||
result = [{value: v, major: false}];
|
||||
for (tick of ticks.slice(start)) {
|
||||
v = tick.value - offset;
|
||||
result.push({value: v, major: false});
|
||||
}
|
||||
v += step;
|
||||
if (v < axis.max) result.push({value:v, major: false});
|
||||
result.push({value: axis.max, major: false});
|
||||
return result;
|
||||
},
|
||||
gridLines:{drawTicks:false},
|
||||
}],
|
||||
step = Number.parseFloat(step);
|
||||
ticks = [y1];
|
||||
for (let yt = Math.ceil(y1 / step) * step; yt > y0; yt -= step) {
|
||||
ticks.push(yt);
|
||||
}
|
||||
ticks.push(y0);
|
||||
}
|
||||
return ticks
|
||||
},
|
||||
},
|
||||
tooltips: false,
|
||||
legend: false,
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'xy',
|
||||
speed: 10,
|
||||
threshold: 10,
|
||||
onPan: function({chart}) { graphs.panCallback(chart.graph);},
|
||||
//onPanComplete: function({chart}){graphs.checkReload(chart.graph);redraw()},
|
||||
onPanComplete: function({chart}){graphs.updateAuto();},
|
||||
},
|
||||
zoom: {
|
||||
enabled: true,
|
||||
drag: false,
|
||||
mode: isTouchDevice ? 'xy': 'x',
|
||||
speed: 0.1,
|
||||
sensitivity: 1,
|
||||
onZoom: function({chart}) { graphs.zoomCallback(chart.graph);},
|
||||
//onZoomComplete: function({chart}){graphs.checkReload(chart.graph);redraw()},
|
||||
onZoomComplete: function({chart}){graphs.onZoomCompleteCallback()},
|
||||
x:{
|
||||
title: false, // former scaleLabel
|
||||
type: 'time',
|
||||
time: {
|
||||
displayFormats: {'millisecond': 'HH:mm:ss.SSS', 'second': 'HH:mm:ss', 'minute': 'HH:mm','hour': 'EEE d. HH:mm', 'day': 'EE d.', 'week': 'd. MMM yy', 'month': 'MMM yy'},
|
||||
},
|
||||
ticks: {
|
||||
padding: -20,
|
||||
// stepSize: 180000,
|
||||
autoSkip: true,
|
||||
maxRotation: 0,
|
||||
// callback not used, this is better done in afterBuildTicks
|
||||
},
|
||||
afterBuildTicks: function(axis) {
|
||||
let ticks = axis.ticks
|
||||
|
||||
if (!ticks || ticks.length <= 2) return ticks;
|
||||
first = ticks[0].value;
|
||||
step = ticks[1].value - first;
|
||||
offset = (first - axis._adapter.startOf(first, 'day')) % step;
|
||||
let result = [];
|
||||
let v = axis.min;
|
||||
for (tick of ticks) {
|
||||
v = tick.value - offset;
|
||||
if (v > axis.min + step / 2) {
|
||||
result.push({value: v, major: false});
|
||||
}
|
||||
}
|
||||
v += step;
|
||||
if (v < axis.max) result.push({value:v, major: false});
|
||||
axis.ticks = result;
|
||||
// return result;
|
||||
},
|
||||
beforeFit: function(axis) { // called after ticks are autoskipped
|
||||
prevday = '';
|
||||
for (tick of axis.ticks) {
|
||||
s = tick.label.split(' ');
|
||||
if (s.length == 3) { // format with day
|
||||
// show date only on first label of a day
|
||||
day = s.slice(0, 2).join(' ');
|
||||
if (day != prevday) {
|
||||
prevday = day;
|
||||
} else {
|
||||
tick.label = s[2]; // time
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
grid:{drawTicks:false},
|
||||
}
|
||||
},
|
||||
plugins:{
|
||||
tooltip: false,
|
||||
legend: false,
|
||||
zoom:{
|
||||
pan: {
|
||||
enabled: true,
|
||||
mode: 'xy',
|
||||
speed: 10,
|
||||
threshold: 10,
|
||||
onPan: function({chart}) { graphs.panCallback(chart.graph);},
|
||||
onPanComplete: function({chart}){graphs.updateAuto();},
|
||||
},
|
||||
zoom: {
|
||||
wheel:{
|
||||
enabled: true
|
||||
},
|
||||
pinch:{
|
||||
enabled: true
|
||||
},
|
||||
mode: isTouchDevice ? 'xy': 'x',
|
||||
speed: 0.1,
|
||||
sensitivity: 1,
|
||||
onZoom: function({chart}) { graphs.zoomCallback(chart.graph);},
|
||||
onZoomComplete: function({chart}){graphs.onZoomCompleteCallback()},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (gindex != 0) {
|
||||
// show time labels only on first chart
|
||||
chart_options.scales.x.ticks.callback = function () { return ' '; }
|
||||
}
|
||||
|
||||
chart = new Chart(ctx, {type: 'scatter', options: chart_options})
|
||||
|
||||
//console.log('create legend')
|
||||
let legend = document.createElement('div');
|
||||
@@ -1802,8 +1839,8 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
* @param {number} max - The maximum timestamp in milliseconds of the viewing window
|
||||
*/
|
||||
function setMinMax(min, max){
|
||||
let ax = chart.options.scales.xAxes[0];
|
||||
let ay = chart.options.scales.yAxes[0];
|
||||
let ax = chart.options.scales.x;
|
||||
let ay = chart.options.scales.y;
|
||||
// clamp X-span
|
||||
let span = max - min;
|
||||
let half = 0;
|
||||
@@ -1818,13 +1855,13 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
mid = (chart.lastXmin + chart.lastXmax) * 0.5;
|
||||
min = mid - half;
|
||||
max = mid + half;
|
||||
ay.ticks.min = chart.lastYmin;
|
||||
ay.ticks.max = chart.lastYmax;
|
||||
ay.min = chart.lastYmin;
|
||||
ay.max = chart.lastYmax;
|
||||
} else {
|
||||
chart.lastXmin = min;
|
||||
chart.lastXmax = max;
|
||||
chart.lastYmin = ay.ticks.min;
|
||||
chart.lastYmax = ay.ticks.max;
|
||||
chart.lastYmin = ay.min;
|
||||
chart.lastYmax = ay.max;
|
||||
}
|
||||
// custom algorithm for tick step
|
||||
mainstep = 1000;
|
||||
@@ -1844,13 +1881,13 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
}
|
||||
mainstep *= info[1];
|
||||
}
|
||||
ax.time.unit = info[0];
|
||||
ax.time.stepSize = Math.round(step / mainstep);
|
||||
//ax.ticks.unit = ax.time.unit;
|
||||
//ax.ticks.stepSize =ax.time.stepSize;
|
||||
//console.log('INFO', step, mainstep, info, ax, ax.time);
|
||||
ax.ticks.max = max;
|
||||
ax.ticks.min = min;
|
||||
unit = info[0];
|
||||
rstep = Math.round(step / mainstep);
|
||||
ax.time.unit = unit;
|
||||
ax.ticks.stepSize = rstep;
|
||||
|
||||
ax.max = max;
|
||||
ax.min = min;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1873,7 +1910,7 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
* Called when log button in the legend is clicked
|
||||
*/
|
||||
function toggleAxesType(){
|
||||
setAxesType((chart.options.scales.yAxes[0].type=== 'linear') ? 'logarithmic' : 'linear');
|
||||
setAxesType((chart.options.scales.y.type=== 'linear') ? 'logarithmic' : 'linear');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1887,7 +1924,7 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
}else{
|
||||
linlog.innerHTML = "<strong>☒</strong> log";
|
||||
}
|
||||
chart.options.scales.yAxes[0].type = type;
|
||||
chart.options.scales.y.type = type;
|
||||
chart.options.animation.duration = 800;
|
||||
if (autoScaleFlag) graphs.autoScale(chart);
|
||||
update();
|
||||
@@ -1905,8 +1942,8 @@ function Graph(gindex, container, x_label, y_label, tag, scaleType = "linear"){
|
||||
let y = null;
|
||||
for(let j = 0; j < chart.getDatasetMeta(i).data.length; j++){
|
||||
let dp = chart.getDatasetMeta(i).data[j];
|
||||
if (dp._model.x >= x) break;
|
||||
y = chart.data.datasets[i].data[dp._index].y;
|
||||
if (dp.x >= x) break; //_model does not exist anymore, properties are defined directly on elements
|
||||
y = chart.data.datasets[i].data[dp.$context.index].y; // $context not mentionned in ChartJS doc, seen with console.log
|
||||
}
|
||||
valueElm = legendvalues[chart.data.datasets[i].key];
|
||||
if (labelMinWidth == 0) {
|
||||
@@ -1996,6 +2033,7 @@ function updateCharts2(graph){
|
||||
console.log('graphs.doUpdates skipped');
|
||||
return;
|
||||
}
|
||||
//console.log('G', graph);
|
||||
for(let key in graph){
|
||||
if (graph[key][0] != null) {
|
||||
// there is at least ONE valid datapoint
|
||||
|
||||
@@ -76,7 +76,7 @@ new Settings()
|
||||
.treat("showConsole", "sc", to_bool, true)
|
||||
.treat("showOverview", "so", to_bool, true)
|
||||
.treat("showGraphics", "sg", to_bool, true) // false)
|
||||
.treat("hideRightPart", "hr", to_bool, false) //used to completely disable the right part
|
||||
.treat("hideRightPart", "hr", to_bool, true) //used to completely disable the right part
|
||||
.treat("wideGraphs", "wg", to_bool, false) //used to toggle the size of the graphs part
|
||||
.treat("showAsync", "sa", to_bool, false)
|
||||
|
||||
|
||||
+11
-17
@@ -190,7 +190,7 @@ class InfluxGraph:
|
||||
"""
|
||||
Polls the last known values for all the available variables, and returns only those whose polled values are more recent than the most recent displayed one.
|
||||
Every plain minute, all the variables are returned with a point having their last known value at the current timestamp to synchronize all the curves on the GUI.
|
||||
|
||||
|
||||
Returns :
|
||||
{"type":"graph-update", "time":(int), "graph":{(str):[[(int),(float)]]}} | None : a dictionnary with its "graph-update" type
|
||||
(so it can be processed by the client), and a "graph" dictionnary with the variable names as key, and an array of points, which are an array containing the timestamp
|
||||
@@ -201,21 +201,15 @@ class InfluxGraph:
|
||||
now, = self.get_abs_time([0])
|
||||
|
||||
result = self.influx_data_getter.poll_last_values(list(self.variables.keys()), self.lastvalues, now)
|
||||
for variable in self.lastvalues.keys():
|
||||
if variable in result.keys():
|
||||
|
||||
# removes points older than the last known point (queries are in seconds and might return points already displayed)
|
||||
while len(result[variable]) > 0:
|
||||
if result[variable][0][0] <= self.lastvalues[variable][0]:
|
||||
result[variable].pop(0)
|
||||
else:
|
||||
break
|
||||
|
||||
if len(result[variable]) > 0 and result[variable][-1][0] > self.lastvalues[variable][0]:
|
||||
self.lastvalues[variable] = (result[variable][-1][0], result[variable][-1][1])
|
||||
else:
|
||||
del result[variable]
|
||||
|
||||
for variable, values in list(result.items()):
|
||||
tlast = self.lastvalues.get(variable, (0,))[0]
|
||||
# removes points older than the last known point (queries are in seconds and might return points already displayed)
|
||||
while values and values[0][0] <= tlast:
|
||||
values.pop(0)
|
||||
if values and values[-1][0] > tlast:
|
||||
self.lastvalues[variable] = values[-1]
|
||||
else:
|
||||
del result[variable]
|
||||
if int(now / 60) != int(self.end_query / 60):
|
||||
# Update unchanged values every plain minute
|
||||
for var, (_, lastx) in self.lastvalues.items():
|
||||
@@ -224,4 +218,4 @@ class InfluxGraph:
|
||||
self.end_query = now
|
||||
if len(result) > 0:
|
||||
return dict(type='graph-update', time=now, graph=result)
|
||||
return None
|
||||
return None
|
||||
Reference in new issue
Block a user