From d563a87ebe5353c80515ae5c85829f02a7e48064 Mon Sep 17 00:00:00 2001
From: Zyie <24736175+Zyie@users.noreply.github.com>
Date: Mon, 12 Feb 2024 15:29:08 +0000
Subject: [PATCH] tidy
---
.github/workflows/main.yml | 126 +-
README.md | 23 +-
global.d.ts | 9 +
package-lock.json | 16 +
package.json | 4 +-
src/chrome/src/background/index.ts | 1 -
src/chrome/src/content/index.ts | 2 +-
src/chrome/src/devtools/index.html | 2 +-
src/chrome/src/devtools/panel/panel.html | 2 +-
src/chrome/src/inject/index.ts | 2 +-
src/chrome/src/popup/index.html | 2 +-
src/example/index.html | 2 +-
src/example/interactivity.html | 2 +-
src/example/src/main.ts | 6 +-
src/example/text.html | 2 +-
src/lib/src/components/Container.tsx | 9 +-
.../components/collapsible/Collapsible.css | 61 -
.../components/collapsible/Collapsible.tsx | 36 +-
src/lib/src/components/collapsible/styles.tsx | 51 +
.../components/properties/BaseProperty.tsx | 9 +-
.../src/components/properties/Properties.css | 4 -
.../src/components/properties/Properties.tsx | 54 +-
.../components/properties/number/Number.tsx | 6 +-
.../components/properties/number/Vector2.tsx | 8 +-
.../components/properties/slider/Slider.tsx | 4 +-
.../components/properties/switch/Switch.tsx | 15 +-
.../src/components/properties/text/Text.tsx | 12 +-
.../src/components/scene/SceneComponent.css | 0
.../src/components/scene/SceneComponent.tsx | 158 +-
src/lib/src/components/scene/inspector.tsx | 22 +
src/lib/src/components/scene/stats.tsx | 155 ++
.../src/components/smooth-charts/smoothie.ts | 1948 +++++++++--------
.../src/components/tree/TreeViewComponent.css | 67 -
.../src/components/tree/TreeViewComponent.tsx | 56 +-
src/lib/src/components/tree/styles.tsx | 69 +
src/lib/src/detection/devtool.ts | 33 +-
.../properties/AnimatedSpritePropsPlugin.ts | 25 +
.../properties/ContainerPropsPlugin.ts | 102 +-
.../properties/GraphicsPropsPlugin.ts | 17 +
.../detection/properties/MeshPropsPlugin.ts | 18 +
.../properties/NineSlicePropsPlugin.ts | 44 +
.../detection/properties/PropertyPlugins.ts | 77 +
.../detection/properties/SpritePropsPlugin.ts | 10 +-
.../detection/properties/TextPropsPlugin.ts | 20 +
.../properties/TilingSpritePropsPlugin.ts | 31 +
.../src/detection/properties/properties.ts | 35 +-
src/lib/src/detection/updateSceneGraph.ts | 3 +-
src/lib/src/detection/utils/getPixiType.ts | 3 +-
src/lib/src/detection/utils/loop.ts | 30 +-
src/lib/src/detection/utils/poll.ts | 4 +-
src/lib/src/detection/utils/utils.ts | 13 -
src/lib/src/pages/index.css | 78 +-
src/lib/src/pages/index.tsx | 9 +-
src/lib/src/pages/styles.tsx | 41 +
src/lib/src/utils/Diff.ts | 2 +-
src/lib/src/utils/DiffChar.ts | 4 +-
src/lib/src/utils/checkDiff.ts | 1 -
tsconfig.json | 51 +-
vite.inject.config.ts | 10 +-
59 files changed, 1979 insertions(+), 1627 deletions(-)
delete mode 100644 src/lib/src/components/collapsible/Collapsible.css
create mode 100644 src/lib/src/components/collapsible/styles.tsx
delete mode 100644 src/lib/src/components/properties/Properties.css
delete mode 100644 src/lib/src/components/scene/SceneComponent.css
create mode 100644 src/lib/src/components/scene/inspector.tsx
create mode 100644 src/lib/src/components/scene/stats.tsx
delete mode 100644 src/lib/src/components/tree/TreeViewComponent.css
create mode 100644 src/lib/src/components/tree/styles.tsx
create mode 100644 src/lib/src/detection/properties/AnimatedSpritePropsPlugin.ts
create mode 100644 src/lib/src/detection/properties/GraphicsPropsPlugin.ts
create mode 100644 src/lib/src/detection/properties/MeshPropsPlugin.ts
create mode 100644 src/lib/src/detection/properties/NineSlicePropsPlugin.ts
create mode 100644 src/lib/src/detection/properties/PropertyPlugins.ts
create mode 100644 src/lib/src/detection/properties/TextPropsPlugin.ts
create mode 100644 src/lib/src/detection/properties/TilingSpritePropsPlugin.ts
delete mode 100644 src/lib/src/detection/utils/utils.ts
create mode 100644 src/lib/src/pages/styles.tsx
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 7f3a63a..b1dcb64 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -1,11 +1,11 @@
name: Automation
on:
push:
- branches: [ '**' ]
+ branches: ['**']
release:
- types: [ published ]
+ types: [published]
pull_request:
- branches: [ '**' ]
+ branches: ['**']
jobs:
build:
name: Build
@@ -18,70 +18,70 @@ jobs:
SOURCE_DIR: 'deploy'
runs-on: ubuntu-20.04
steps:
- - uses: actions/checkout@v3
- - name: Install xvfb
- run: sudo apt-get install xvfb
- - name: Use Node.js 18.x
- uses: actions/setup-node@v3
- with:
- node-version: 18
- - name: Install Dependencies
- run: npm ci
+ - uses: actions/checkout@v3
+ - name: Install xvfb
+ run: sudo apt-get install xvfb
+ - name: Use Node.js 18.x
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ - name: Install Dependencies
+ run: npm ci
- - name: Build for Distribution
- run: xvfb-run --auto-servernum npm run dist
+ - name: Build for Distribution
+ run: xvfb-run --auto-servernum npm run dist
- # All the below are deploy-related steps
- - name: Extract Branch Name
- id: branch_name
- if: github.event_name == 'push' && !contains(github.ref, 'refs/tags')
- run: echo BRANCH_NAME=${GITHUB_REF/refs\/heads\//} >> $GITHUB_OUTPUT
+ # All the below are deploy-related steps
+ - name: Extract Branch Name
+ id: branch_name
+ if: github.event_name == 'push' && !contains(github.ref, 'refs/tags')
+ run: echo BRANCH_NAME=${GITHUB_REF/refs\/heads\//} >> $GITHUB_OUTPUT
- # Examples:
- # 1) PR feature/acme merged into dev
- # 2) branch A merged into branch B
- # 3) branch A pushed directly to git
- - name: Deploy Non-Tag Branches
- uses: jakejarvis/s3-sync-action@master
- if: github.event_name == 'push' && env.AWS_ACCESS_KEY_ID != ''
- with:
- args: --acl public-read --follow-symlinks --delete --cache-control "max-age=60"
- env:
- DEST_DIR: ${{ steps.branch_name.outputs.BRANCH_NAME }}
+ # Examples:
+ # 1) PR feature/acme merged into dev
+ # 2) branch A merged into branch B
+ # 3) branch A pushed directly to git
+ - name: Deploy Non-Tag Branches
+ uses: jakejarvis/s3-sync-action@master
+ if: github.event_name == 'push' && env.AWS_ACCESS_KEY_ID != ''
+ with:
+ args: --acl public-read --follow-symlinks --delete --cache-control "max-age=60"
+ env:
+ DEST_DIR: ${{ steps.branch_name.outputs.BRANCH_NAME }}
- # Release is published and deployed into s3://bucket-name/v5.22/
- - name: Deploy Released Branches
- uses: jakejarvis/s3-sync-action@master
- if: github.event_name == 'release' && env.AWS_ACCESS_KEY_ID != ''
- with:
- args: --acl public-read --follow-symlinks --delete --cache-control "max-age=2592000"
- env:
- DEST_DIR: ${{ github.event.release.tag_name }}
+ # Release is published and deployed into s3://bucket-name/v5.22/
+ - name: Deploy Released Branches
+ uses: jakejarvis/s3-sync-action@master
+ if: github.event_name == 'release' && env.AWS_ACCESS_KEY_ID != ''
+ with:
+ args: --acl public-read --follow-symlinks --delete --cache-control "max-age=2592000"
+ env:
+ DEST_DIR: ${{ github.event.release.tag_name }}
- # Same release from previous deployed into s3://bucket-name/release/
- - name: Deploy Latest Release
- uses: jakejarvis/s3-sync-action@master
- if: github.event_name == 'release' && github.event.release.prerelease == false && env.AWS_ACCESS_KEY_ID != ''
- with:
- args: --acl public-read --follow-symlinks --delete --cache-control "max-age=1209600"
- env:
- DEST_DIR: 'latest'
+ # Same release from previous deployed into s3://bucket-name/release/
+ - name: Deploy Latest Release
+ uses: jakejarvis/s3-sync-action@master
+ if: github.event_name == 'release' && github.event.release.prerelease == false && env.AWS_ACCESS_KEY_ID != ''
+ with:
+ args: --acl public-read --follow-symlinks --delete --cache-control "max-age=1209600"
+ env:
+ DEST_DIR: 'latest'
- # Publish to NPM
- - name: Publish Latest Release
- if: github.event_name == 'release' && github.event.release.prerelease == false && env.NODE_AUTH_TOKEN != ''
- run: npm run publish-ci
+ # Publish to NPM
+ - name: Publish Latest Release
+ if: github.event_name == 'release' && github.event.release.prerelease == false && env.NODE_AUTH_TOKEN != ''
+ run: npm run publish-ci
- # Publish to NPM with prerelease dist-tag
- - name: Publish Latest Prerelease
- if: github.event_name == 'release' && github.event.release.prerelease && env.NODE_AUTH_TOKEN != ''
- run: npm run publish-ci
- env:
- XS_PUBLISH_TAG: prerelease
-
- # Automatically attach browser files to release
- - name: Upload to Release
- if: github.event_name == 'release'
- uses: softprops/action-gh-release@v1
- with:
- files: dist/*
\ No newline at end of file
+ # Publish to NPM with prerelease dist-tag
+ - name: Publish Latest Prerelease
+ if: github.event_name == 'release' && github.event.release.prerelease && env.NODE_AUTH_TOKEN != ''
+ run: npm run publish-ci
+ env:
+ XS_PUBLISH_TAG: prerelease
+
+ # Automatically attach browser files to release
+ - name: Upload to Release
+ if: github.event_name == 'release'
+ uses: softprops/action-gh-release@v1
+ with:
+ files: dist/*
diff --git a/README.md b/README.md
index 3999d58..5f5aaef 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
Depending on your version of PixiJS, you'll need to figure out which major version of PixiJS Filters to use.
| PixiJS | PixiJS Filters |
-|-------------|----------------|
+| ----------- | -------------- |
| v5.x | v4.x |
| v6.x - v7.x | v5.x |
| v8.x | v6.x |
@@ -34,8 +34,8 @@ If all else failes, you can manually download the bundled file from the [release
## Filters
-| Filter | Preview |
-|----------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
+| Filter | Preview |
+| -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --- |
| **AdjustmentFilter**
_pixi-filters/adjustment_
[View demo][Adjustment_demo] | ![adjustment](https://filters.pixijs.download/main/screenshots/adjustment.png?v=3) |
| **AdvancedBloomFilter**
_pixi-filters/advanced-bloom_
[View demo][AdvancedBloom_demo] | ![advanced-bloom](https://filters.pixijs.download/main/screenshots/advanced-bloom.png?v=3) |
| **AsciiFilter**
_pixi-filters/ascii_
[View demo][Ascii_demo] | ![ascii](https://filters.pixijs.download/main/screenshots/ascii.png?v=3) |
@@ -65,8 +65,8 @@ If all else failes, you can manually download the bundled file from the [release
| **PixelateFilter**
_pixi-filters/pixelate_
[View demo][Pixelate_demo] | ![pixelate](https://filters.pixijs.download/main/screenshots/pixelate.png?v=3) |
| **RadialBlurFilter**
_pixi-filters/radial-blur_
[View demo][RadialBlur_demo] | ![radial-blur](https://filters.pixijs.download/main/screenshots/radial-blur.png?v=3) |
| **ReflectionFilter**
_pixi-filters/reflection_
[View demo][Reflection_demo] | ![reflection](https://filters.pixijs.download/main/screenshots/reflection.png?v=3) |
-| **RGBSplitFilter**
_pixi-filters/rgb-split_
[View demo][RGBSplit_demo] | ![rgb split](https://filters.pixijs.download/main/screenshots/rgb.png?v=3) | |
-| **ShockwaveFilter**
_pixi-filters/shockwave_
[View demo][Shockwave_demo] | ![shockwave](https://filters.pixijs.download/main/screenshots/shockwave.gif?v=3) |
+| **RGBSplitFilter**
_pixi-filters/rgb-split_
[View demo][RGBSplit_demo] | ![rgb split](https://filters.pixijs.download/main/screenshots/rgb.png?v=3) | |
+| **ShockwaveFilter**
_pixi-filters/shockwave_
[View demo][Shockwave_demo] | ![shockwave](https://filters.pixijs.download/main/screenshots/shockwave.gif?v=3) |
| **SimpleLightmapFilter**
_pixi-filters/simple-lightmap_
[View demo][SimpleLightmap_demo] | ![simple-lightmap](https://filters.pixijs.download/main/screenshots/simple-lightmap.png?v=3) |
| **TiltShiftFilter**
_pixi-filters/tilt-shift_
[View demo][TiltShift_demo] | ![tilt-shift](https://filters.pixijs.download/main/screenshots/tilt-shift.png?v=3) |
| **TwistFilter**
_pixi-filters/twist_
[View demo][Twist_demo] | ![twist](https://filters.pixijs.download/main/screenshots/twist.png?v=3) |
@@ -76,10 +76,10 @@ If all else failes, you can manually download the bundled file from the [release
PixiJS has a handful of core filters that are built-in to the PixiJS library.
-| Filter | Preview |
-|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|
-| **AlphaFilter**
[View demo][Alpha_demo] | ![alpha](https://filters.pixijs.download/main/screenshots/alpha.png?v=3) |
-| **BlurFilter**
[View demo][Blur_demo] | ![blur](https://filters.pixijs.download/main/screenshots/blur.png?v=3) |
+| Filter | Preview |
+| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
+| **AlphaFilter**
[View demo][Alpha_demo] | ![alpha](https://filters.pixijs.download/main/screenshots/alpha.png?v=3) |
+| **BlurFilter**
[View demo][Blur_demo] | ![blur](https://filters.pixijs.download/main/screenshots/blur.png?v=3) |
| **ColorMatrixFilter** (contrast)
[View demo][ColorMatrix_demo] | ![color-matrix-contrast](https://filters.pixijs.download/main/screenshots/color-matrix-contrast.png?v=3) |
| **ColorMatrixFilter** (desaturate)
[View demo][ColorMatrix_demo] | ![color-matrix-desaturate](https://filters.pixijs.download/main/screenshots/color-matrix-desaturate.png?v=3) |
| **ColorMatrixFilter** (kodachrome)
[View demo][ColorMatrix_demo] | ![color-matrix-kodachrome](https://filters.pixijs.download/main/screenshots/color-matrix-kodachrome.png?v=3) |
@@ -89,8 +89,8 @@ PixiJS has a handful of core filters that are built-in to the PixiJS library.
| **ColorMatrixFilter** (predator)
[View demo][ColorMatrix_demo] | ![color-matrix-predator](https://filters.pixijs.download/main/screenshots/color-matrix-predator.png?v=3) |
| **ColorMatrixFilter** (saturate)
[View demo][ColorMatrix_demo] | ![color-matrix-saturate](https://filters.pixijs.download/main/screenshots/color-matrix-saturate.png?v=3) |
| **ColorMatrixFilter** (sepia)
[View demo][ColorMatrix_demo] | ![color-matrix-sepia](https://filters.pixijs.download/main/screenshots/color-matrix-sepia.png?v=3) |
-| **DisplacementFilter**
[View demo][Displacement_demo] | ![displacement](https://filters.pixijs.download/main/screenshots/displacement.png?v=3) |
-| **NoiseFilter**
[View demo][Noise_demo] | ![noise](https://filters.pixijs.download/main/screenshots/noise.png?v=3) |
+| **DisplacementFilter**
[View demo][Displacement_demo] | ![displacement](https://filters.pixijs.download/main/screenshots/displacement.png?v=3) |
+| **NoiseFilter**
[View demo][Noise_demo] | ![noise](https://filters.pixijs.download/main/screenshots/noise.png?v=3) |
## Building
@@ -117,6 +117,7 @@ npm run watch
API documention can be found [here](http://pixijs.io/filters/docs/).
+
[Adjustment_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=AdjustmentFilter
[AdvancedBloom_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=AdvancedBloomFilter
[Ascii_demo]: https://filters.pixijs.download/main/examples/index.html?enabled=AsciiFilter
diff --git a/global.d.ts b/global.d.ts
index 7b07860..390564c 100644
--- a/global.d.ts
+++ b/global.d.ts
@@ -5,6 +5,15 @@ declare global {
__PIXI_RENDERER__: import('pixi.js').Renderer | undefined;
__PIXI__: import('pixi.js');
__PIXI_DEVTOOL_WRAPPER__: any;
+ __PIXI__DEVTOOLS__: {
+ pixi: typeof import('pixi.js');
+ app: import('pixi.js').Application | undefined;
+ stage?: import('pixi.js').Container | undefined;
+ renderer?: import('pixi.js').Renderer | undefined;
+ scenePanel?: {
+ propertyPlugins?: any[];
+ };
+ };
}
}
diff --git a/package-lock.json b/package-lock.json
index 2a2648c..7e2219c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,6 +58,7 @@
"patch-package": "^8.0.0",
"pixi.js": "^8.0.0-rc.8",
"pre-commit": "^1.2.2",
+ "prettier": "^3.2.5",
"rimraf": "^4.4.1",
"ts-node": "^10.9.1",
"typescript": "^5.3.3",
@@ -15954,6 +15955,21 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
+ "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/pretty-format": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
diff --git a/package.json b/package.json
index 5a22dfa..95a4763 100644
--- a/package.json
+++ b/package.json
@@ -25,7 +25,8 @@
"copy:assets": "copyfiles \"{dist,docs,examples}/**\" deploy",
"deploy": "xs clean,build,docs && xs upload",
"release": "xs bump,test && run-s deploy && xs git-push",
- "publish-ci": "xs publish"
+ "publish-ci": "xs publish",
+ "prettier": "prettier . --write"
},
"pre-commit": [
"lint",
@@ -84,6 +85,7 @@
"patch-package": "^8.0.0",
"pixi.js": "^8.0.0-rc.8",
"pre-commit": "^1.2.2",
+ "prettier": "^3.2.5",
"rimraf": "^4.4.1",
"ts-node": "^10.9.1",
"typescript": "^5.3.3",
diff --git a/src/chrome/src/background/index.ts b/src/chrome/src/background/index.ts
index 6c65659..f2a9f8e 100644
--- a/src/chrome/src/background/index.ts
+++ b/src/chrome/src/background/index.ts
@@ -52,7 +52,6 @@ chrome.runtime.onMessage.addListener((request: Message, sender: chrome.runtime.M
}
});
-
chrome.runtime.onConnect.addListener(function (port) {
if (port.name !== 'devtools-connection') return;
diff --git a/src/chrome/src/content/index.ts b/src/chrome/src/content/index.ts
index d00cf21..c535ae9 100644
--- a/src/chrome/src/content/index.ts
+++ b/src/chrome/src/content/index.ts
@@ -29,5 +29,5 @@ window.addEventListener(
chrome.runtime.sendMessage(message);
}
},
- false
+ false,
);
diff --git a/src/chrome/src/devtools/index.html b/src/chrome/src/devtools/index.html
index 687359c..7fc23cd 100644
--- a/src/chrome/src/devtools/index.html
+++ b/src/chrome/src/devtools/index.html
@@ -1,4 +1,4 @@
-
+
diff --git a/src/chrome/src/devtools/panel/panel.html b/src/chrome/src/devtools/panel/panel.html
index 375262e..4334723 100644
--- a/src/chrome/src/devtools/panel/panel.html
+++ b/src/chrome/src/devtools/panel/panel.html
@@ -1,4 +1,4 @@
-
+
TimeSeries
with optional data options.
- *
- * Options are of the form (defaults shown):
- *
- * - * { - * resetBounds: true, // enables/disables automatic scaling of the y-axis - * resetBoundsInterval: 3000 // the period between scaling calculations, in millis - * } - *- * - * Presentation options for TimeSeries are specified as an argument to
SmoothieChart.addTimeSeries
.
- *
- * @constructor
- */
- function TimeSeries(options) {
- this.options = Util.extend({}, TimeSeries.defaultOptions, options);
- this.disabled = false;
- this.clear();
- }
-
- TimeSeries.defaultOptions = {
- resetBoundsInterval: 3000,
- resetBounds: true
- };
-
- /**
- * Clears all data and state from this TimeSeries object.
- */
- TimeSeries.prototype.clear = function() {
- this.data = [];
- this.maxValue = Number.NaN; // The maximum value ever seen in this TimeSeries.
- this.minValue = Number.NaN; // The minimum value ever seen in this TimeSeries.
- };
-
- /**
- * Recalculate the min/max values for this TimeSeries
object.
- *
- * This causes the graph to scale itself in the y-axis.
- */
- TimeSeries.prototype.resetBounds = function() {
- if (this.data.length) {
- // Walk through all data points, finding the min/max value
- this.maxValue = this.data[0][1];
- this.minValue = this.data[0][1];
- for (var i = 1; i < this.data.length; i++) {
- var value = this.data[i][1];
- if (value > this.maxValue) {
- this.maxValue = value;
- }
- if (value < this.minValue) {
- this.minValue = value;
- }
- }
- } else {
- // No data exists, so set min/max to NaN
- this.maxValue = Number.NaN;
- this.minValue = Number.NaN;
}
- };
-
- /**
- * Adds a new data point to the TimeSeries
, preserving chronological order.
- *
- * @param timestamp the position, in time, of this data point
- * @param value the value of this data point
- * @param sumRepeatedTimeStampValues if timestamp
has an exact match in the series, this flag controls
- * whether it is replaced, or the values summed (defaults to false.)
- */
- TimeSeries.prototype.append = function(timestamp, value, sumRepeatedTimeStampValues) {
- // Rewind until we hit an older timestamp
- var i = this.data.length - 1;
- while (i >= 0 && this.data[i][0] > timestamp) {
- i--;
+ return arguments[0];
+ },
+ binarySearch: function (data, value) {
+ var low = 0,
+ high = data.length;
+ while (low < high) {
+ var mid = (low + high) >> 1;
+ if (value < data[mid][0]) high = mid;
+ else low = mid + 1;
}
-
- if (i === -1) {
- // This new item is the oldest data
- this.data.splice(0, 0, [timestamp, value]);
- } else if (this.data.length > 0 && this.data[i][0] === timestamp) {
- // Update existing values in the array
- if (sumRepeatedTimeStampValues) {
- // Sum this value into the existing 'bucket'
- this.data[i][1] += value;
- value = this.data[i][1];
- } else {
- // Replace the previous value
- this.data[i][1] = value;
+ return low;
+ },
+ };
+
+ /**
+ * Initialises a new TimeSeries
with optional data options.
+ *
+ * Options are of the form (defaults shown):
+ *
+ * + * { + * resetBounds: true, // enables/disables automatic scaling of the y-axis + * resetBoundsInterval: 3000 // the period between scaling calculations, in millis + * } + *+ * + * Presentation options for TimeSeries are specified as an argument to
SmoothieChart.addTimeSeries
.
+ *
+ * @constructor
+ */
+ function TimeSeries(options) {
+ this.options = Util.extend({}, TimeSeries.defaultOptions, options);
+ this.disabled = false;
+ this.clear();
+ }
+
+ TimeSeries.defaultOptions = {
+ resetBoundsInterval: 3000,
+ resetBounds: true,
+ };
+
+ /**
+ * Clears all data and state from this TimeSeries object.
+ */
+ TimeSeries.prototype.clear = function () {
+ this.data = [];
+ this.maxValue = Number.NaN; // The maximum value ever seen in this TimeSeries.
+ this.minValue = Number.NaN; // The minimum value ever seen in this TimeSeries.
+ };
+
+ /**
+ * Recalculate the min/max values for this TimeSeries
object.
+ *
+ * This causes the graph to scale itself in the y-axis.
+ */
+ TimeSeries.prototype.resetBounds = function () {
+ if (this.data.length) {
+ // Walk through all data points, finding the min/max value
+ this.maxValue = this.data[0][1];
+ this.minValue = this.data[0][1];
+ for (var i = 1; i < this.data.length; i++) {
+ var value = this.data[i][1];
+ if (value > this.maxValue) {
+ this.maxValue = value;
+ }
+ if (value < this.minValue) {
+ this.minValue = value;
}
- } else if (i < this.data.length - 1) {
- // Splice into the correct position to keep timestamps in order
- this.data.splice(i + 1, 0, [timestamp, value]);
- } else {
- // Add to the end of the array
- this.data.push([timestamp, value]);
- }
-
- this.maxValue = isNaN(this.maxValue) ? value : Math.max(this.maxValue, value);
- this.minValue = isNaN(this.minValue) ? value : Math.min(this.minValue, value);
- };
-
- TimeSeries.prototype.dropOldData = function(oldestValidTime, maxDataSetLength) {
- // We must always keep one expired data point as we need this to draw the
- // line that comes into the chart from the left, but any points prior to that can be removed.
- var removeCount = 0;
- while (this.data.length - removeCount >= maxDataSetLength && this.data[removeCount + 1][0] < oldestValidTime) {
- removeCount++;
}
- if (removeCount !== 0) {
- this.data.splice(0, removeCount);
+ } else {
+ // No data exists, so set min/max to NaN
+ this.maxValue = Number.NaN;
+ this.minValue = Number.NaN;
+ }
+ };
+
+ /**
+ * Adds a new data point to the TimeSeries
, preserving chronological order.
+ *
+ * @param timestamp the position, in time, of this data point
+ * @param value the value of this data point
+ * @param sumRepeatedTimeStampValues if timestamp
has an exact match in the series, this flag controls
+ * whether it is replaced, or the values summed (defaults to false.)
+ */
+ TimeSeries.prototype.append = function (timestamp, value, sumRepeatedTimeStampValues) {
+ // Rewind until we hit an older timestamp
+ var i = this.data.length - 1;
+ while (i >= 0 && this.data[i][0] > timestamp) {
+ i--;
+ }
+
+ if (i === -1) {
+ // This new item is the oldest data
+ this.data.splice(0, 0, [timestamp, value]);
+ } else if (this.data.length > 0 && this.data[i][0] === timestamp) {
+ // Update existing values in the array
+ if (sumRepeatedTimeStampValues) {
+ // Sum this value into the existing 'bucket'
+ this.data[i][1] += value;
+ value = this.data[i][1];
+ } else {
+ // Replace the previous value
+ this.data[i][1] = value;
}
- };
-
- /**
- * Initialises a new SmoothieChart
.
- *
- * Options are optional, and should be of the form below. Just specify the values you
- * need and the rest will be given sensible defaults as shown:
- *
- * - * { - * minValue: undefined, // specify to clamp the lower y-axis to a given value - * maxValue: undefined, // specify to clamp the upper y-axis to a given value - * maxValueScale: 1, // allows proportional padding to be added above the chart. for 10% padding, specify 1.1. - * minValueScale: 1, // allows proportional padding to be added below the chart. for 10% padding, specify 1.1. - * yRangeFunction: undefined, // function({min: , max: }) { return {min: , max: }; } - * scaleSmoothing: 0.125, // controls the rate at which y-value zoom animation occurs - * millisPerPixel: 20, // sets the speed at which the chart pans by - * enableDpiScaling: true, // support rendering at different DPI depending on the device - * yMinFormatter: function(min, precision) { // callback function that formats the min y value label - * return parseFloat(min).toFixed(precision); - * }, - * yMaxFormatter: function(max, precision) { // callback function that formats the max y value label - * return parseFloat(max).toFixed(precision); - * }, - * yIntermediateFormatter: function(intermediate, precision) { // callback function that formats the intermediate y value labels - * return parseFloat(intermediate).toFixed(precision); - * }, - * maxDataSetLength: 2, - * interpolation: 'bezier' // one of 'bezier', 'linear', or 'step' - * timestampFormatter: null, // optional function to format time stamps for bottom of chart - * // you may use SmoothieChart.timeFormatter, or your own: function(date) { return ''; } - * scrollBackwards: false, // reverse the scroll direction of the chart - * horizontalLines: [], // [ { value: 0, color: '#ffffff', lineWidth: 1 } ] - * grid: - * { - * fillStyle: '#000000', // the background colour of the chart - * lineWidth: 1, // the pixel width of grid lines - * strokeStyle: '#777777', // colour of grid lines - * millisPerLine: 1000, // distance between vertical grid lines - * sharpLines: false, // controls whether grid lines are 1px sharp, or softened - * verticalSections: 2, // number of vertical sections marked out by horizontal grid lines - * borderVisible: true // whether the grid lines trace the border of the chart or not - * }, - * labels - * { - * disabled: false, // enables/disables labels showing the min/max values - * fillStyle: '#ffffff', // colour for text of labels, - * fontSize: 15, - * fontFamily: 'sans-serif', - * precision: 2, - * showIntermediateLabels: false, // shows intermediate labels between min and max values along y axis - * intermediateLabelSameAxis: true, - * }, - * title - * { - * text: '', // the text to display on the left side of the chart - * fillStyle: '#ffffff', // colour for text - * fontSize: 15, - * fontFamily: 'sans-serif', - * verticalAlign: 'middle' // one of 'top', 'middle', or 'bottom' - * }, - * tooltip: false // show tooltip when mouse is over the chart - * tooltipLine: { // properties for a vertical line at the cursor position - * lineWidth: 1, - * strokeStyle: '#BBBBBB' - * }, - * tooltipFormatter: SmoothieChart.tooltipFormatter, // formatter function for tooltip text - * nonRealtimeData: false, // use time of latest data as current time - * displayDataFromPercentile: 1, // display not latest data, but data from the given percentile - * // useful when trying to see old data saved by setting a high value for maxDataSetLength - * // should be a value between 0 and 1 - * responsive: false, // whether the chart should adapt to the size of the canvas - * limitFPS: 0 // maximum frame rate the chart will render at, in FPS (zero means no limit) - * } - *- * - * @constructor - */ - function SmoothieChart(options) { - this.options = Util.extend({}, SmoothieChart.defaultChartOptions, options); - this.seriesSet = []; - this.currentValueRange = 1; - this.currentVisMinValue = 0; - this.lastRenderTimeMillis = 0; - this.lastChartTimestamp = 0; - - this.mousemove = this.mousemove.bind(this); - this.mouseout = this.mouseout.bind(this); + } else if (i < this.data.length - 1) { + // Splice into the correct position to keep timestamps in order + this.data.splice(i + 1, 0, [timestamp, value]); + } else { + // Add to the end of the array + this.data.push([timestamp, value]); } - - /** Formats the HTML string content of the tooltip. */ - SmoothieChart.tooltipFormatter = function (timestamp, data) { - var timestampFormatter = this.options.timestampFormatter || SmoothieChart.timeFormatter, - lines = [timestampFormatter(new Date(timestamp))], - label; - - for (var i = 0; i < data.length; ++i) { - label = data[i].series.options.tooltipLabel || '' - if (label !== ''){ - label = label + ' '; - } - lines.push('' + + + this.maxValue = isNaN(this.maxValue) ? value : Math.max(this.maxValue, value); + this.minValue = isNaN(this.minValue) ? value : Math.min(this.minValue, value); + }; + + TimeSeries.prototype.dropOldData = function (oldestValidTime, maxDataSetLength) { + // We must always keep one expired data point as we need this to draw the + // line that comes into the chart from the left, but any points prior to that can be removed. + var removeCount = 0; + while (this.data.length - removeCount >= maxDataSetLength && this.data[removeCount + 1][0] < oldestValidTime) { + removeCount++; + } + if (removeCount !== 0) { + this.data.splice(0, removeCount); + } + }; + + /** + * Initialises a new
SmoothieChart
.
+ *
+ * Options are optional, and should be of the form below. Just specify the values you
+ * need and the rest will be given sensible defaults as shown:
+ *
+ * + * { + * minValue: undefined, // specify to clamp the lower y-axis to a given value + * maxValue: undefined, // specify to clamp the upper y-axis to a given value + * maxValueScale: 1, // allows proportional padding to be added above the chart. for 10% padding, specify 1.1. + * minValueScale: 1, // allows proportional padding to be added below the chart. for 10% padding, specify 1.1. + * yRangeFunction: undefined, // function({min: , max: }) { return {min: , max: }; } + * scaleSmoothing: 0.125, // controls the rate at which y-value zoom animation occurs + * millisPerPixel: 20, // sets the speed at which the chart pans by + * enableDpiScaling: true, // support rendering at different DPI depending on the device + * yMinFormatter: function(min, precision) { // callback function that formats the min y value label + * return parseFloat(min).toFixed(precision); + * }, + * yMaxFormatter: function(max, precision) { // callback function that formats the max y value label + * return parseFloat(max).toFixed(precision); + * }, + * yIntermediateFormatter: function(intermediate, precision) { // callback function that formats the intermediate y value labels + * return parseFloat(intermediate).toFixed(precision); + * }, + * maxDataSetLength: 2, + * interpolation: 'bezier' // one of 'bezier', 'linear', or 'step' + * timestampFormatter: null, // optional function to format time stamps for bottom of chart + * // you may use SmoothieChart.timeFormatter, or your own: function(date) { return ''; } + * scrollBackwards: false, // reverse the scroll direction of the chart + * horizontalLines: [], // [ { value: 0, color: '#ffffff', lineWidth: 1 } ] + * grid: + * { + * fillStyle: '#000000', // the background colour of the chart + * lineWidth: 1, // the pixel width of grid lines + * strokeStyle: '#777777', // colour of grid lines + * millisPerLine: 1000, // distance between vertical grid lines + * sharpLines: false, // controls whether grid lines are 1px sharp, or softened + * verticalSections: 2, // number of vertical sections marked out by horizontal grid lines + * borderVisible: true // whether the grid lines trace the border of the chart or not + * }, + * labels + * { + * disabled: false, // enables/disables labels showing the min/max values + * fillStyle: '#ffffff', // colour for text of labels, + * fontSize: 15, + * fontFamily: 'sans-serif', + * precision: 2, + * showIntermediateLabels: false, // shows intermediate labels between min and max values along y axis + * intermediateLabelSameAxis: true, + * }, + * title + * { + * text: '', // the text to display on the left side of the chart + * fillStyle: '#ffffff', // colour for text + * fontSize: 15, + * fontFamily: 'sans-serif', + * verticalAlign: 'middle' // one of 'top', 'middle', or 'bottom' + * }, + * tooltip: false // show tooltip when mouse is over the chart + * tooltipLine: { // properties for a vertical line at the cursor position + * lineWidth: 1, + * strokeStyle: '#BBBBBB' + * }, + * tooltipFormatter: SmoothieChart.tooltipFormatter, // formatter function for tooltip text + * nonRealtimeData: false, // use time of latest data as current time + * displayDataFromPercentile: 1, // display not latest data, but data from the given percentile + * // useful when trying to see old data saved by setting a high value for maxDataSetLength + * // should be a value between 0 and 1 + * responsive: false, // whether the chart should adapt to the size of the canvas + * limitFPS: 0 // maximum frame rate the chart will render at, in FPS (zero means no limit) + * } + *+ * + * @constructor + */ + function SmoothieChart(options) { + this.options = Util.extend({}, SmoothieChart.defaultChartOptions, options); + this.seriesSet = []; + this.currentValueRange = 1; + this.currentVisMinValue = 0; + this.lastRenderTimeMillis = 0; + this.lastChartTimestamp = 0; + + this.mousemove = this.mousemove.bind(this); + this.mouseout = this.mouseout.bind(this); + } + + /** Formats the HTML string content of the tooltip. */ + SmoothieChart.tooltipFormatter = function (timestamp, data) { + var timestampFormatter = this.options.timestampFormatter || SmoothieChart.timeFormatter, + lines = [timestampFormatter(new Date(timestamp))], + label; + + for (var i = 0; i < data.length; ++i) { + label = data[i].series.options.tooltipLabel || ''; + if (label !== '') { + label = label + ' '; + } + lines.push( + '' + label + - this.options.yMaxFormatter(data[i].value, this.options.labels.precision) + ''); - } - - return lines.join('
TimeSeries
to this chart, with optional presentation options.
- *
- * Presentation options should be of the form (defaults shown):
- *
- * - * { - * lineWidth: 1, - * strokeStyle: '#ffffff', - * fillStyle: undefined, - * tooltipLabel: undefined - * } - *- */ - SmoothieChart.prototype.addTimeSeries = function(timeSeries, options) { - this.seriesSet.push({timeSeries: timeSeries, options: Util.extend({}, SmoothieChart.defaultSeriesPresentationOptions, options)}); - if (timeSeries.options.resetBounds && timeSeries.options.resetBoundsInterval > 0) { - timeSeries.resetBoundsTimerId = setInterval( - function() { - timeSeries.resetBounds(); - }, - timeSeries.options.resetBoundsInterval - ); - } - }; - - /** - * Removes the specified
TimeSeries
from the chart.
- */
- SmoothieChart.prototype.removeTimeSeries = function(timeSeries) {
- // Find the correct timeseries to remove, and remove it
- var numSeries = this.seriesSet.length;
- for (var i = 0; i < numSeries; i++) {
- if (this.seriesSet[i].timeSeries === timeSeries) {
- this.seriesSet.splice(i, 1);
- break;
- }
- }
- // If a timer was operating for that timeseries, remove it
- if (timeSeries.resetBoundsTimerId) {
- // Stop resetting the bounds, if we were
- clearInterval(timeSeries.resetBoundsTimerId);
- }
- };
-
- /**
- * Gets render options for the specified TimeSeries
.
- *
- * As you may use a single TimeSeries
in multiple charts with different formatting in each usage,
- * these settings are stored in the chart.
- */
- SmoothieChart.prototype.getTimeSeriesOptions = function(timeSeries) {
- // Find the correct timeseries to remove, and remove it
- var numSeries = this.seriesSet.length;
- for (var i = 0; i < numSeries; i++) {
- if (this.seriesSet[i].timeSeries === timeSeries) {
- return this.seriesSet[i].options;
- }
- }
+
+ return {
+ requestAnimationFrame: requestAnimationFrame,
+ cancelAnimationFrame: cancelAnimationFrame,
};
-
- /**
- * Brings the specified TimeSeries
to the top of the chart. It will be rendered last.
- */
- SmoothieChart.prototype.bringToFront = function(timeSeries) {
- // Find the correct timeseries to remove, and remove it
- var numSeries = this.seriesSet.length;
- for (var i = 0; i < numSeries; i++) {
- if (this.seriesSet[i].timeSeries === timeSeries) {
- var set = this.seriesSet.splice(i, 1);
- this.seriesSet.push(set[0]);
- break;
- }
+ })();
+
+ SmoothieChart.defaultSeriesPresentationOptions = {
+ lineWidth: 1,
+ strokeStyle: '#ffffff',
+ };
+
+ /**
+ * Adds a TimeSeries
to this chart, with optional presentation options.
+ *
+ * Presentation options should be of the form (defaults shown):
+ *
+ * + * { + * lineWidth: 1, + * strokeStyle: '#ffffff', + * fillStyle: undefined, + * tooltipLabel: undefined + * } + *+ */ + SmoothieChart.prototype.addTimeSeries = function (timeSeries, options) { + this.seriesSet.push({ + timeSeries: timeSeries, + options: Util.extend({}, SmoothieChart.defaultSeriesPresentationOptions, options), + }); + if (timeSeries.options.resetBounds && timeSeries.options.resetBoundsInterval > 0) { + timeSeries.resetBoundsTimerId = setInterval(function () { + timeSeries.resetBounds(); + }, timeSeries.options.resetBoundsInterval); + } + }; + + /** + * Removes the specified
TimeSeries
from the chart.
+ */
+ SmoothieChart.prototype.removeTimeSeries = function (timeSeries) {
+ // Find the correct timeseries to remove, and remove it
+ var numSeries = this.seriesSet.length;
+ for (var i = 0; i < numSeries; i++) {
+ if (this.seriesSet[i].timeSeries === timeSeries) {
+ this.seriesSet.splice(i, 1);
+ break;
}
- };
-
- /**
- * Instructs the SmoothieChart
to start rendering to the provided canvas, with specified delay.
- *
- * @param canvas the target canvas element
- * @param delayMillis an amount of time to wait before a data point is shown. This can prevent the end of the series
- * from appearing on screen, with new values flashing into view, at the expense of some latency.
- */
- SmoothieChart.prototype.streamTo = function(canvas, delayMillis) {
- this.canvas = canvas;
- this.delay = delayMillis;
- this.start();
- };
-
- SmoothieChart.prototype.getTooltipEl = function () {
- // Create the tool tip element lazily
- if (!this.tooltipEl) {
- this.tooltipEl = document.createElement('div');
- this.tooltipEl.className = 'smoothie-chart-tooltip';
- this.tooltipEl.style.position = 'absolute';
- this.tooltipEl.style.display = 'none';
- document.body.appendChild(this.tooltipEl);
+ }
+ // If a timer was operating for that timeseries, remove it
+ if (timeSeries.resetBoundsTimerId) {
+ // Stop resetting the bounds, if we were
+ clearInterval(timeSeries.resetBoundsTimerId);
+ }
+ };
+
+ /**
+ * Gets render options for the specified TimeSeries
.
+ *
+ * As you may use a single TimeSeries
in multiple charts with different formatting in each usage,
+ * these settings are stored in the chart.
+ */
+ SmoothieChart.prototype.getTimeSeriesOptions = function (timeSeries) {
+ // Find the correct timeseries to remove, and remove it
+ var numSeries = this.seriesSet.length;
+ for (var i = 0; i < numSeries; i++) {
+ if (this.seriesSet[i].timeSeries === timeSeries) {
+ return this.seriesSet[i].options;
}
- return this.tooltipEl;
- };
-
- SmoothieChart.prototype.updateTooltip = function () {
- if(!this.options.tooltip){
- return;
+ }
+ };
+
+ /**
+ * Brings the specified TimeSeries
to the top of the chart. It will be rendered last.
+ */
+ SmoothieChart.prototype.bringToFront = function (timeSeries) {
+ // Find the correct timeseries to remove, and remove it
+ var numSeries = this.seriesSet.length;
+ for (var i = 0; i < numSeries; i++) {
+ if (this.seriesSet[i].timeSeries === timeSeries) {
+ var set = this.seriesSet.splice(i, 1);
+ this.seriesSet.push(set[0]);
+ break;
}
- var el = this.getTooltipEl();
-
- if (!this.mouseover || !this.options.tooltip) {
- el.style.display = 'none';
- return;
+ }
+ };
+
+ /**
+ * Instructs the SmoothieChart
to start rendering to the provided canvas, with specified delay.
+ *
+ * @param canvas the target canvas element
+ * @param delayMillis an amount of time to wait before a data point is shown. This can prevent the end of the series
+ * from appearing on screen, with new values flashing into view, at the expense of some latency.
+ */
+ SmoothieChart.prototype.streamTo = function (canvas, delayMillis) {
+ this.canvas = canvas;
+ this.delay = delayMillis;
+ this.start();
+ };
+
+ SmoothieChart.prototype.getTooltipEl = function () {
+ // Create the tool tip element lazily
+ if (!this.tooltipEl) {
+ this.tooltipEl = document.createElement('div');
+ this.tooltipEl.className = 'smoothie-chart-tooltip';
+ this.tooltipEl.style.position = 'absolute';
+ this.tooltipEl.style.display = 'none';
+ document.body.appendChild(this.tooltipEl);
+ }
+ return this.tooltipEl;
+ };
+
+ SmoothieChart.prototype.updateTooltip = function () {
+ if (!this.options.tooltip) {
+ return;
+ }
+ var el = this.getTooltipEl();
+
+ if (!this.mouseover || !this.options.tooltip) {
+ el.style.display = 'none';
+ return;
+ }
+
+ var time = this.lastChartTimestamp;
+
+ // x pixel to time
+ var t = this.options.scrollBackwards
+ ? time - this.mouseX * this.options.millisPerPixel
+ : time - (this.canvas.offsetWidth - this.mouseX) * this.options.millisPerPixel;
+
+ var data = [];
+
+ // For each data set...
+ for (var d = 0; d < this.seriesSet.length; d++) {
+ var timeSeries = this.seriesSet[d].timeSeries;
+ if (timeSeries.disabled) {
+ continue;
}
-
- var time = this.lastChartTimestamp;
-
- // x pixel to time
- var t = this.options.scrollBackwards
- ? time - this.mouseX * this.options.millisPerPixel
- : time - (this.canvas.offsetWidth - this.mouseX) * this.options.millisPerPixel;
-
- var data = [];
-
- // For each data set...
- for (var d = 0; d < this.seriesSet.length; d++) {
- var timeSeries = this.seriesSet[d].timeSeries;
- if (timeSeries.disabled) {
- continue;
- }
-
- // find datapoint closest to time 't'
- var closeIdx = Util.binarySearch(timeSeries.data, t);
- if (closeIdx > 0 && closeIdx < timeSeries.data.length) {
- data.push({ series: this.seriesSet[d], index: closeIdx, value: timeSeries.data[closeIdx][1] });
- }
+
+ // find datapoint closest to time 't'
+ var closeIdx = Util.binarySearch(timeSeries.data, t);
+ if (closeIdx > 0 && closeIdx < timeSeries.data.length) {
+ data.push({ series: this.seriesSet[d], index: closeIdx, value: timeSeries.data[closeIdx][1] });
}
-
- if (data.length) {
- el.innerHTML = this.options.tooltipFormatter.call(this, t, data);
- el.style.display = 'block';
- } else {
- el.style.display = 'none';
+ }
+
+ if (data.length) {
+ el.innerHTML = this.options.tooltipFormatter.call(this, t, data);
+ el.style.display = 'block';
+ } else {
+ el.style.display = 'none';
+ }
+ };
+
+ SmoothieChart.prototype.mousemove = function (evt) {
+ this.mouseover = true;
+ this.mouseX = evt.offsetX;
+ this.mouseY = evt.offsetY;
+ this.mousePageX = evt.pageX;
+ this.mousePageY = evt.pageY;
+ if (!this.options.tooltip) {
+ return;
+ }
+ var el = this.getTooltipEl();
+ el.style.top = Math.round(this.mousePageY) + 'px';
+ el.style.left = Math.round(this.mousePageX) + 'px';
+ this.updateTooltip();
+ };
+
+ SmoothieChart.prototype.mouseout = function () {
+ this.mouseover = false;
+ this.mouseX = this.mouseY = -1;
+ if (this.tooltipEl) this.tooltipEl.style.display = 'none';
+ };
+
+ /**
+ * Make sure the canvas has the optimal resolution for the device's pixel ratio.
+ */
+ SmoothieChart.prototype.resize = function () {
+ var dpr = !this.options.enableDpiScaling || !window ? 1 : window.devicePixelRatio,
+ width,
+ height;
+ if (this.options.responsive) {
+ // Newer behaviour: Use the canvas's size in the layout, and set the internal
+ // resolution according to that size and the device pixel ratio (eg: high DPI)
+ width = this.canvas.offsetWidth;
+ height = this.canvas.offsetHeight;
+
+ if (width !== this.lastWidth) {
+ this.lastWidth = width;
+ this.canvas.setAttribute('width', Math.floor(width * dpr).toString());
+ this.canvas.getContext('2d').scale(dpr, dpr);
}
- };
-
- SmoothieChart.prototype.mousemove = function (evt) {
- this.mouseover = true;
- this.mouseX = evt.offsetX;
- this.mouseY = evt.offsetY;
- this.mousePageX = evt.pageX;
- this.mousePageY = evt.pageY;
- if(!this.options.tooltip){
- return;
+ if (height !== this.lastHeight) {
+ this.lastHeight = height;
+ this.canvas.setAttribute('height', Math.floor(height * dpr).toString());
+ this.canvas.getContext('2d').scale(dpr, dpr);
}
- var el = this.getTooltipEl();
- el.style.top = Math.round(this.mousePageY) + 'px';
- el.style.left = Math.round(this.mousePageX) + 'px';
- this.updateTooltip();
- };
-
- SmoothieChart.prototype.mouseout = function () {
- this.mouseover = false;
- this.mouseX = this.mouseY = -1;
- if (this.tooltipEl)
- this.tooltipEl.style.display = 'none';
- };
-
- /**
- * Make sure the canvas has the optimal resolution for the device's pixel ratio.
- */
- SmoothieChart.prototype.resize = function () {
- var dpr = !this.options.enableDpiScaling || !window ? 1 : window.devicePixelRatio,
- width, height;
- if (this.options.responsive) {
- // Newer behaviour: Use the canvas's size in the layout, and set the internal
- // resolution according to that size and the device pixel ratio (eg: high DPI)
- width = this.canvas.offsetWidth;
- height = this.canvas.offsetHeight;
-
- if (width !== this.lastWidth) {
-
- this.lastWidth = width;
- this.canvas.setAttribute('width', (Math.floor(width * dpr)).toString());
- this.canvas.getContext('2d').scale(dpr, dpr);
- }
- if (height !== this.lastHeight) {
- this.lastHeight = height;
- this.canvas.setAttribute('height', (Math.floor(height * dpr)).toString());
- this.canvas.getContext('2d').scale(dpr, dpr);
- }
- } else if (dpr !== 1) {
- // Older behaviour: use the canvas's inner dimensions and scale the element's size
- // according to that size and the device pixel ratio (eg: high DPI)
- width = parseInt(this.canvas.getAttribute('width'));
- height = parseInt(this.canvas.getAttribute('height'));
-
- if (!this.originalWidth || (Math.floor(this.originalWidth * dpr) !== width)) {
- this.originalWidth = width;
- this.canvas.setAttribute('width', (Math.floor(width * dpr)).toString());
- this.canvas.style.width = width + 'px';
- this.canvas.getContext('2d').scale(dpr, dpr);
- }
-
- if (!this.originalHeight || (Math.floor(this.originalHeight * dpr) !== height)) {
- this.originalHeight = height;
- this.canvas.setAttribute('height', (Math.floor(height * dpr)).toString());
- this.canvas.style.height = height + 'px';
- this.canvas.getContext('2d').scale(dpr, dpr);
- }
+ } else if (dpr !== 1) {
+ // Older behaviour: use the canvas's inner dimensions and scale the element's size
+ // according to that size and the device pixel ratio (eg: high DPI)
+ width = parseInt(this.canvas.getAttribute('width'));
+ height = parseInt(this.canvas.getAttribute('height'));
+
+ if (!this.originalWidth || Math.floor(this.originalWidth * dpr) !== width) {
+ this.originalWidth = width;
+ this.canvas.setAttribute('width', Math.floor(width * dpr).toString());
+ this.canvas.style.width = width + 'px';
+ this.canvas.getContext('2d').scale(dpr, dpr);
}
- };
-
- /**
- * Starts the animation of this chart.
- */
- SmoothieChart.prototype.start = function() {
- if (this.frame) {
- // We're already running, so just return
- return;
+
+ if (!this.originalHeight || Math.floor(this.originalHeight * dpr) !== height) {
+ this.originalHeight = height;
+ this.canvas.setAttribute('height', Math.floor(height * dpr).toString());
+ this.canvas.style.height = height + 'px';
+ this.canvas.getContext('2d').scale(dpr, dpr);
}
-
- this.canvas.addEventListener('mousemove', this.mousemove);
- this.canvas.addEventListener('mouseout', this.mouseout);
-
- // Renders a frame, and queues the next frame for later rendering
- var animate = function() {
- this.frame = SmoothieChart.AnimateCompatibility.requestAnimationFrame(function() {
- if(this.options.nonRealtimeData){
- var dateZero = new Date(0);
- // find the data point with the latest timestamp
- var maxTimeStamp = this.seriesSet.reduce(function(max, series){
- var dataSet = series.timeSeries.data;
- var indexToCheck = Math.round(this.options.displayDataFromPercentile * dataSet.length) - 1;
- indexToCheck = indexToCheck >= 0 ? indexToCheck : 0;
- indexToCheck = indexToCheck <= dataSet.length -1 ? indexToCheck : dataSet.length -1;
- if(dataSet && dataSet.length > 0)
- {
- // timestamp corresponds to element 0 of the data point
- var lastDataTimeStamp = dataSet[indexToCheck][0];
- max = max > lastDataTimeStamp ? max : lastDataTimeStamp;
- }
- return max;
- }.bind(this), dateZero);
+ }
+ };
+
+ /**
+ * Starts the animation of this chart.
+ */
+ SmoothieChart.prototype.start = function () {
+ if (this.frame) {
+ // We're already running, so just return
+ return;
+ }
+
+ this.canvas.addEventListener('mousemove', this.mousemove);
+ this.canvas.addEventListener('mouseout', this.mouseout);
+
+ // Renders a frame, and queues the next frame for later rendering
+ var animate = function () {
+ this.frame = SmoothieChart.AnimateCompatibility.requestAnimationFrame(
+ function () {
+ if (this.options.nonRealtimeData) {
+ var dateZero = new Date(0);
+ // find the data point with the latest timestamp
+ var maxTimeStamp = this.seriesSet.reduce(
+ function (max, series) {
+ var dataSet = series.timeSeries.data;
+ var indexToCheck = Math.round(this.options.displayDataFromPercentile * dataSet.length) - 1;
+ indexToCheck = indexToCheck >= 0 ? indexToCheck : 0;
+ indexToCheck = indexToCheck <= dataSet.length - 1 ? indexToCheck : dataSet.length - 1;
+ if (dataSet && dataSet.length > 0) {
+ // timestamp corresponds to element 0 of the data point
+ var lastDataTimeStamp = dataSet[indexToCheck][0];
+ max = max > lastDataTimeStamp ? max : lastDataTimeStamp;
+ }
+ return max;
+ }.bind(this),
+ dateZero,
+ );
// use the max timestamp as current time
this.render(this.canvas, maxTimeStamp > dateZero ? maxTimeStamp : null);
} else {
this.render();
}
animate();
- }.bind(this));
- }.bind(this);
-
- animate();
- };
-
- /**
- * Stops the animation of this chart.
- */
- SmoothieChart.prototype.stop = function() {
- if (this.frame) {
- SmoothieChart.AnimateCompatibility.cancelAnimationFrame(this.frame);
- delete this.frame;
- this.canvas.removeEventListener('mousemove', this.mousemove);
- this.canvas.removeEventListener('mouseout', this.mouseout);
- }
- };
-
- SmoothieChart.prototype.updateValueRange = function() {
- // Calculate the current scale of the chart, from all time series.
- var chartOptions = this.options,
- chartMaxValue = Number.NaN,
- chartMinValue = Number.NaN;
-
- for (var d = 0; d < this.seriesSet.length; d++) {
- // TODO(ndunn): We could calculate / track these values as they stream in.
- var timeSeries = this.seriesSet[d].timeSeries;
- if (timeSeries.disabled) {
- continue;
- }
-
- if (!isNaN(timeSeries.maxValue)) {
- chartMaxValue = !isNaN(chartMaxValue) ? Math.max(chartMaxValue, timeSeries.maxValue) : timeSeries.maxValue;
- }
-
- if (!isNaN(timeSeries.minValue)) {
- chartMinValue = !isNaN(chartMinValue) ? Math.min(chartMinValue, timeSeries.minValue) : timeSeries.minValue;
- }
+ }.bind(this),
+ );
+ }.bind(this);
+
+ animate();
+ };
+
+ /**
+ * Stops the animation of this chart.
+ */
+ SmoothieChart.prototype.stop = function () {
+ if (this.frame) {
+ SmoothieChart.AnimateCompatibility.cancelAnimationFrame(this.frame);
+ delete this.frame;
+ this.canvas.removeEventListener('mousemove', this.mousemove);
+ this.canvas.removeEventListener('mouseout', this.mouseout);
+ }
+ };
+
+ SmoothieChart.prototype.updateValueRange = function () {
+ // Calculate the current scale of the chart, from all time series.
+ var chartOptions = this.options,
+ chartMaxValue = Number.NaN,
+ chartMinValue = Number.NaN;
+
+ for (var d = 0; d < this.seriesSet.length; d++) {
+ // TODO(ndunn): We could calculate / track these values as they stream in.
+ var timeSeries = this.seriesSet[d].timeSeries;
+ if (timeSeries.disabled) {
+ continue;
}
-
- // Scale the chartMaxValue to add padding at the top if required
- if (chartOptions.maxValue != null) {
- chartMaxValue = chartOptions.maxValue;
- } else {
- chartMaxValue *= chartOptions.maxValueScale;
+
+ if (!isNaN(timeSeries.maxValue)) {
+ chartMaxValue = !isNaN(chartMaxValue) ? Math.max(chartMaxValue, timeSeries.maxValue) : timeSeries.maxValue;
}
-
- // Set the minimum if we've specified one
- if (chartOptions.minValue != null) {
- chartMinValue = chartOptions.minValue;
- } else {
- chartMinValue -= Math.abs(chartMinValue * chartOptions.minValueScale - chartMinValue);
+
+ if (!isNaN(timeSeries.minValue)) {
+ chartMinValue = !isNaN(chartMinValue) ? Math.min(chartMinValue, timeSeries.minValue) : timeSeries.minValue;
}
-
- // If a custom range function is set, call it
- if (this.options.yRangeFunction) {
- var range = this.options.yRangeFunction({min: chartMinValue, max: chartMaxValue});
- chartMinValue = range.min;
- chartMaxValue = range.max;
+ }
+
+ // Scale the chartMaxValue to add padding at the top if required
+ if (chartOptions.maxValue != null) {
+ chartMaxValue = chartOptions.maxValue;
+ } else {
+ chartMaxValue *= chartOptions.maxValueScale;
+ }
+
+ // Set the minimum if we've specified one
+ if (chartOptions.minValue != null) {
+ chartMinValue = chartOptions.minValue;
+ } else {
+ chartMinValue -= Math.abs(chartMinValue * chartOptions.minValueScale - chartMinValue);
+ }
+
+ // If a custom range function is set, call it
+ if (this.options.yRangeFunction) {
+ var range = this.options.yRangeFunction({ min: chartMinValue, max: chartMaxValue });
+ chartMinValue = range.min;
+ chartMaxValue = range.max;
+ }
+
+ if (!isNaN(chartMaxValue) && !isNaN(chartMinValue)) {
+ if (chartMinValue === chartMaxValue) {
+ chartMinValue -= 1;
+ //chartMaxValue += 1;
}
-
- if (!isNaN(chartMaxValue) && !isNaN(chartMinValue)) {
- if (chartMinValue === chartMaxValue) {
- chartMinValue -= 1;
- //chartMaxValue += 1;
- }
-
- var targetValueRange = chartMaxValue - chartMinValue;
- var valueRangeDiff = (targetValueRange - this.currentValueRange);
- var minValueDiff = (chartMinValue - this.currentVisMinValue);
- this.isAnimatingScale = Math.abs(valueRangeDiff) > 0.1 || Math.abs(minValueDiff) > 0.1;
- const t = 0.000001;
- if (Math.abs(valueRangeDiff) < t) {
- valueRangeDiff = t;
- }
- this.currentValueRange += chartOptions.scaleSmoothing * valueRangeDiff;
- this.currentVisMinValue += chartOptions.scaleSmoothing * minValueDiff;
+
+ var targetValueRange = chartMaxValue - chartMinValue;
+ var valueRangeDiff = targetValueRange - this.currentValueRange;
+ var minValueDiff = chartMinValue - this.currentVisMinValue;
+ this.isAnimatingScale = Math.abs(valueRangeDiff) > 0.1 || Math.abs(minValueDiff) > 0.1;
+ const t = 0.000001;
+ if (Math.abs(valueRangeDiff) < t) {
+ valueRangeDiff = t;
}
-
- this.valueRange = { min: chartMinValue, max: chartMaxValue };
- };
-
- SmoothieChart.prototype.render = function(canvas, time) {
- var nowMillis = Date.now();
-
- // Respect any frame rate limit.
- if (this.options.limitFPS > 0 && nowMillis - this.lastRenderTimeMillis < (1000/this.options.limitFPS))
+ this.currentValueRange += chartOptions.scaleSmoothing * valueRangeDiff;
+ this.currentVisMinValue += chartOptions.scaleSmoothing * minValueDiff;
+ }
+
+ this.valueRange = { min: chartMinValue, max: chartMaxValue };
+ };
+
+ SmoothieChart.prototype.render = function (canvas, time) {
+ var nowMillis = Date.now();
+
+ // Respect any frame rate limit.
+ if (this.options.limitFPS > 0 && nowMillis - this.lastRenderTimeMillis < 1000 / this.options.limitFPS) return;
+
+ if (!this.isAnimatingScale) {
+ // We're not animating. We can use the last render time and the scroll speed to work out whether
+ // we actually need to paint anything yet. If not, we can return immediately.
+
+ // Render at least every 1/6th of a second. The canvas may be resized, which there is
+ // no reliable way to detect.
+ var maxIdleMillis = Math.min(1000 / 6, this.options.millisPerPixel);
+
+ if (nowMillis - this.lastRenderTimeMillis < maxIdleMillis) {
return;
-
- if (!this.isAnimatingScale) {
- // We're not animating. We can use the last render time and the scroll speed to work out whether
- // we actually need to paint anything yet. If not, we can return immediately.
-
- // Render at least every 1/6th of a second. The canvas may be resized, which there is
- // no reliable way to detect.
- var maxIdleMillis = Math.min(1000/6, this.options.millisPerPixel);
-
- if (nowMillis - this.lastRenderTimeMillis < maxIdleMillis) {
- return;
- }
}
-
- this.resize();
- this.updateTooltip();
-
- this.lastRenderTimeMillis = nowMillis;
-
- canvas = canvas || this.canvas;
- time = time || nowMillis - (this.delay || 0);
-
- // Round time down to pixel granularity, so motion appears smoother.
- time -= time % this.options.millisPerPixel;
-
- this.lastChartTimestamp = time;
-
- var context = canvas.getContext('2d'),
- chartOptions = this.options,
- dimensions = { top: 0, left: 0, width: canvas.clientWidth, height: canvas.clientHeight },
- // Calculate the threshold time for the oldest data points.
- oldestValidTime = time - (dimensions.width * chartOptions.millisPerPixel),
- valueToYPixel = function(value) {
- var offset = value - this.currentVisMinValue;
- return this.currentValueRange === 0
- ? dimensions.height
- : dimensions.height - (Math.round((offset / this.currentValueRange) * dimensions.height));
- }.bind(this),
- timeToXPixel = function(t) {
- if(chartOptions.scrollBackwards) {
- return Math.round((time - t) / chartOptions.millisPerPixel);
- }
- return Math.round(dimensions.width - ((time - t) / chartOptions.millisPerPixel));
- };
-
- this.updateValueRange();
-
- context.font = chartOptions.labels.fontSize + 'px ' + chartOptions.labels.fontFamily;
-
- // Save the state of the canvas context, any transformations applied in this method
- // will get removed from the stack at the end of this method when .restore() is called.
- context.save();
-
- // Move the origin.
- context.translate(dimensions.left, dimensions.top);
-
- // Create a clipped rectangle - anything we draw will be constrained to this rectangle.
- // This prevents the occasional pixels from curves near the edges overrunning and creating
- // screen cheese (that phrase should need no explanation).
- context.beginPath();
- context.rect(0, 0, dimensions.width, dimensions.height);
- context.clip();
-
- // Clear the working area.
- context.save();
- context.fillStyle = chartOptions.grid.fillStyle;
- context.clearRect(0, 0, dimensions.width, dimensions.height);
- context.fillRect(0, 0, dimensions.width, dimensions.height);
- context.restore();
-
- // Grid lines...
- context.save();
- context.lineWidth = chartOptions.grid.lineWidth;
- context.strokeStyle = chartOptions.grid.strokeStyle;
- // Vertical (time) dividers.
- if (chartOptions.grid.millisPerLine > 0) {
- context.beginPath();
- for (var t = time - (time % chartOptions.grid.millisPerLine);
- t >= oldestValidTime;
- t -= chartOptions.grid.millisPerLine) {
- var gx = timeToXPixel(t);
- if (chartOptions.grid.sharpLines) {
- gx -= 0.5;
- }
- context.moveTo(gx, 0);
- context.lineTo(gx, dimensions.height);
+ }
+
+ this.resize();
+ this.updateTooltip();
+
+ this.lastRenderTimeMillis = nowMillis;
+
+ canvas = canvas || this.canvas;
+ time = time || nowMillis - (this.delay || 0);
+
+ // Round time down to pixel granularity, so motion appears smoother.
+ time -= time % this.options.millisPerPixel;
+
+ this.lastChartTimestamp = time;
+
+ var context = canvas.getContext('2d'),
+ chartOptions = this.options,
+ dimensions = { top: 0, left: 0, width: canvas.clientWidth, height: canvas.clientHeight },
+ // Calculate the threshold time for the oldest data points.
+ oldestValidTime = time - dimensions.width * chartOptions.millisPerPixel,
+ valueToYPixel = function (value) {
+ var offset = value - this.currentVisMinValue;
+ return this.currentValueRange === 0
+ ? dimensions.height
+ : dimensions.height - Math.round((offset / this.currentValueRange) * dimensions.height);
+ }.bind(this),
+ timeToXPixel = function (t) {
+ if (chartOptions.scrollBackwards) {
+ return Math.round((time - t) / chartOptions.millisPerPixel);
}
- context.stroke();
- context.closePath();
- }
-
- // Horizontal (value) dividers.
- for (var v = 1; v < chartOptions.grid.verticalSections; v++) {
- var gy = Math.round(v * dimensions.height / chartOptions.grid.verticalSections);
+ return Math.round(dimensions.width - (time - t) / chartOptions.millisPerPixel);
+ };
+
+ this.updateValueRange();
+
+ context.font = chartOptions.labels.fontSize + 'px ' + chartOptions.labels.fontFamily;
+
+ // Save the state of the canvas context, any transformations applied in this method
+ // will get removed from the stack at the end of this method when .restore() is called.
+ context.save();
+
+ // Move the origin.
+ context.translate(dimensions.left, dimensions.top);
+
+ // Create a clipped rectangle - anything we draw will be constrained to this rectangle.
+ // This prevents the occasional pixels from curves near the edges overrunning and creating
+ // screen cheese (that phrase should need no explanation).
+ context.beginPath();
+ context.rect(0, 0, dimensions.width, dimensions.height);
+ context.clip();
+
+ // Clear the working area.
+ context.save();
+ context.fillStyle = chartOptions.grid.fillStyle;
+ context.clearRect(0, 0, dimensions.width, dimensions.height);
+ context.fillRect(0, 0, dimensions.width, dimensions.height);
+ context.restore();
+
+ // Grid lines...
+ context.save();
+ context.lineWidth = chartOptions.grid.lineWidth;
+ context.strokeStyle = chartOptions.grid.strokeStyle;
+ // Vertical (time) dividers.
+ if (chartOptions.grid.millisPerLine > 0) {
+ context.beginPath();
+ for (
+ var t = time - (time % chartOptions.grid.millisPerLine);
+ t >= oldestValidTime;
+ t -= chartOptions.grid.millisPerLine
+ ) {
+ var gx = timeToXPixel(t);
if (chartOptions.grid.sharpLines) {
- gy -= 0.5;
+ gx -= 0.5;
}
+ context.moveTo(gx, 0);
+ context.lineTo(gx, dimensions.height);
+ }
+ context.stroke();
+ context.closePath();
+ }
+
+ // Horizontal (value) dividers.
+ for (var v = 1; v < chartOptions.grid.verticalSections; v++) {
+ var gy = Math.round((v * dimensions.height) / chartOptions.grid.verticalSections);
+ if (chartOptions.grid.sharpLines) {
+ gy -= 0.5;
+ }
+ context.beginPath();
+ context.moveTo(0, gy);
+ context.lineTo(dimensions.width, gy);
+ context.stroke();
+ context.closePath();
+ }
+ // Bounding rectangle.
+ if (chartOptions.grid.borderVisible) {
+ context.beginPath();
+ context.strokeRect(0, 0, dimensions.width, dimensions.height);
+ context.closePath();
+ }
+ context.restore();
+
+ // Draw any horizontal lines...
+ if (chartOptions.horizontalLines && chartOptions.horizontalLines.length) {
+ for (var hl = 0; hl < chartOptions.horizontalLines.length; hl++) {
+ var line = chartOptions.horizontalLines[hl],
+ hly = Math.round(valueToYPixel(line.value)) - 0.5;
+ context.strokeStyle = line.color || '#ffffff';
+ context.lineWidth = line.lineWidth || 1;
context.beginPath();
- context.moveTo(0, gy);
- context.lineTo(dimensions.width, gy);
+ context.moveTo(0, hly);
+ context.lineTo(dimensions.width, hly);
context.stroke();
context.closePath();
}
- // Bounding rectangle.
- if (chartOptions.grid.borderVisible) {
- context.beginPath();
- context.strokeRect(0, 0, dimensions.width, dimensions.height);
- context.closePath();
+ }
+
+ // For each data set...
+ for (var d = 0; d < this.seriesSet.length; d++) {
+ context.save();
+ var timeSeries = this.seriesSet[d].timeSeries;
+ if (timeSeries.disabled) {
+ continue;
}
- context.restore();
-
- // Draw any horizontal lines...
- if (chartOptions.horizontalLines && chartOptions.horizontalLines.length) {
- for (var hl = 0; hl < chartOptions.horizontalLines.length; hl++) {
- var line = chartOptions.horizontalLines[hl],
- hly = Math.round(valueToYPixel(line.value)) - 0.5;
- context.strokeStyle = line.color || '#ffffff';
- context.lineWidth = line.lineWidth || 1;
- context.beginPath();
- context.moveTo(0, hly);
- context.lineTo(dimensions.width, hly);
- context.stroke();
- context.closePath();
+
+ var dataSet = timeSeries.data,
+ seriesOptions = this.seriesSet[d].options;
+
+ // Delete old data that's moved off the left of the chart.
+ timeSeries.dropOldData(oldestValidTime, chartOptions.maxDataSetLength);
+
+ // Set style for this dataSet.
+ context.lineWidth = seriesOptions.lineWidth;
+ context.strokeStyle = seriesOptions.strokeStyle;
+ // Draw the line...
+ context.beginPath();
+ // Retain lastX, lastY for calculating the control points of bezier curves.
+ var firstX = 0,
+ firstY = 0,
+ lastX = 0,
+ lastY = 0;
+ for (var i = 0; i < dataSet.length && dataSet.length !== 1; i++) {
+ var x = timeToXPixel(dataSet[i][0]),
+ y = valueToYPixel(dataSet[i][1]);
+
+ if (i === 0) {
+ firstX = x;
+ firstY = y;
+ context.moveTo(x, y);
+ } else {
+ switch (chartOptions.interpolation) {
+ case 'linear':
+ case 'line': {
+ context.lineTo(x, y);
+ break;
+ }
+ case 'bezier':
+ default: {
+ // Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves
+ //
+ // Assuming A was the last point in the line plotted and B is the new point,
+ // we draw a curve with control points P and Q as below.
+ //
+ // A---P
+ // |
+ // |
+ // |
+ // Q---B
+ //
+ // Importantly, A and P are at the same y coordinate, as are B and Q. This is
+ // so adjacent curves appear to flow as one.
+ //
+ context.bezierCurveTo(
+ // startPoint (A) is implicit from last iteration of loop
+ Math.round((lastX + x) / 2),
+ lastY, // controlPoint1 (P)
+ Math.round(lastX + x) / 2,
+ y, // controlPoint2 (Q)
+ x,
+ y,
+ ); // endPoint (B)
+ break;
+ }
+ case 'step': {
+ context.lineTo(x, lastY);
+ context.lineTo(x, y);
+ break;
+ }
+ }
}
+
+ lastX = x;
+ lastY = y;
}
-
- // For each data set...
- for (var d = 0; d < this.seriesSet.length; d++) {
- context.save();
- var timeSeries = this.seriesSet[d].timeSeries;
- if (timeSeries.disabled) {
- continue;
- }
-
- var dataSet = timeSeries.data,
- seriesOptions = this.seriesSet[d].options;
-
- // Delete old data that's moved off the left of the chart.
- timeSeries.dropOldData(oldestValidTime, chartOptions.maxDataSetLength);
-
- // Set style for this dataSet.
- context.lineWidth = seriesOptions.lineWidth;
- context.strokeStyle = seriesOptions.strokeStyle;
- // Draw the line...
- context.beginPath();
- // Retain lastX, lastY for calculating the control points of bezier curves.
- var firstX = 0, firstY = 0, lastX = 0, lastY = 0;
- for (var i = 0; i < dataSet.length && dataSet.length !== 1; i++) {
- var x = timeToXPixel(dataSet[i][0]),
- y = valueToYPixel(dataSet[i][1]);
-
- if (i === 0) {
- firstX = x;
- firstY = y;
- context.moveTo(x, y);
+
+ if (dataSet.length > 1) {
+ if (seriesOptions.fillStyle) {
+ // Close up the fill region.
+ if (chartOptions.scrollBackwards) {
+ context.lineTo(lastX, dimensions.height + seriesOptions.lineWidth);
+ context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth);
+ context.lineTo(firstX, firstY);
} else {
- switch (chartOptions.interpolation) {
- case "linear":
- case "line": {
- context.lineTo(x,y);
- break;
- }
- case "bezier":
- default: {
- // Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves
- //
- // Assuming A was the last point in the line plotted and B is the new point,
- // we draw a curve with control points P and Q as below.
- //
- // A---P
- // |
- // |
- // |
- // Q---B
- //
- // Importantly, A and P are at the same y coordinate, as are B and Q. This is
- // so adjacent curves appear to flow as one.
- //
- context.bezierCurveTo( // startPoint (A) is implicit from last iteration of loop
- Math.round((lastX + x) / 2), lastY, // controlPoint1 (P)
- Math.round((lastX + x)) / 2, y, // controlPoint2 (Q)
- x, y); // endPoint (B)
- break;
- }
- case "step": {
- context.lineTo(x,lastY);
- context.lineTo(x,y);
- break;
- }
- }
+ context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, lastY);
+ context.lineTo(
+ dimensions.width + seriesOptions.lineWidth + 1,
+ dimensions.height + seriesOptions.lineWidth + 1,
+ );
+ context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth);
}
-
- lastX = x; lastY = y;
+ context.fillStyle = seriesOptions.fillStyle;
+ context.fill();
}
-
- if (dataSet.length > 1) {
- if (seriesOptions.fillStyle) {
- // Close up the fill region.
- if (chartOptions.scrollBackwards) {
- context.lineTo(lastX, dimensions.height + seriesOptions.lineWidth);
- context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth);
- context.lineTo(firstX, firstY);
- } else {
- context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, lastY);
- context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, dimensions.height + seriesOptions.lineWidth + 1);
- context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth);
- }
- context.fillStyle = seriesOptions.fillStyle;
- context.fill();
- }
-
- if (seriesOptions.strokeStyle && seriesOptions.strokeStyle !== 'none') {
- context.stroke();
- }
- context.closePath();
+
+ if (seriesOptions.strokeStyle && seriesOptions.strokeStyle !== 'none') {
+ context.stroke();
}
- context.restore();
- }
-
- if (chartOptions.tooltip && this.mouseX >= 0) {
- // Draw vertical bar to show tooltip position
- context.lineWidth = chartOptions.tooltipLine.lineWidth;
- context.strokeStyle = chartOptions.tooltipLine.strokeStyle;
- context.beginPath();
- context.moveTo(this.mouseX, 0);
- context.lineTo(this.mouseX, dimensions.height);
context.closePath();
- context.stroke();
- this.updateTooltip();
- }
-
- // Draw the axis values on the chart.
- if (!chartOptions.labels.disabled && !isNaN(this.valueRange.min) && !isNaN(this.valueRange.max)) {
- var maxValueString = chartOptions.yMaxFormatter(this.valueRange.max, chartOptions.labels.precision),
- minValueString = chartOptions.yMinFormatter(this.valueRange.min, chartOptions.labels.precision),
- maxLabelPos = chartOptions.scrollBackwards ? 0 : dimensions.width - context.measureText(maxValueString).width - 2,
- minLabelPos = chartOptions.scrollBackwards ? 0 : dimensions.width - context.measureText(minValueString).width - 2;
- context.fillStyle = chartOptions.labels.fillStyle;
- context.fillText(maxValueString, maxLabelPos, chartOptions.labels.fontSize);
- context.fillText(minValueString, minLabelPos, dimensions.height - 2);
}
-
- // Display intermediate y axis labels along y-axis to the left of the chart
- if ( chartOptions.labels.showIntermediateLabels
- && !isNaN(this.valueRange.min) && !isNaN(this.valueRange.max)
- && chartOptions.grid.verticalSections > 0) {
- // show a label above every vertical section divider
- var step = (this.valueRange.max - this.valueRange.min) / chartOptions.grid.verticalSections;
- var stepPixels = dimensions.height / chartOptions.grid.verticalSections;
- for (var v = 1; v < chartOptions.grid.verticalSections; v++) {
- var gy = dimensions.height - Math.round(v * stepPixels);
- if (chartOptions.grid.sharpLines) {
- gy -= 0.5;
- }
- var yValue = chartOptions.yIntermediateFormatter(this.valueRange.min + (v * step), chartOptions.labels.precision);
- //left of right axis?
- let intermediateLabelPos =
- chartOptions.labels.intermediateLabelSameAxis
- ? (chartOptions.scrollBackwards ? 0 : dimensions.width - context.measureText(yValue).width - 2)
- : (chartOptions.scrollBackwards ? dimensions.width - context.measureText(yValue).width - 2 : 0);
-
- context.fillText(yValue, intermediateLabelPos, gy - chartOptions.grid.lineWidth);
+ context.restore();
+ }
+
+ if (chartOptions.tooltip && this.mouseX >= 0) {
+ // Draw vertical bar to show tooltip position
+ context.lineWidth = chartOptions.tooltipLine.lineWidth;
+ context.strokeStyle = chartOptions.tooltipLine.strokeStyle;
+ context.beginPath();
+ context.moveTo(this.mouseX, 0);
+ context.lineTo(this.mouseX, dimensions.height);
+ context.closePath();
+ context.stroke();
+ this.updateTooltip();
+ }
+
+ // Draw the axis values on the chart.
+ if (!chartOptions.labels.disabled && !isNaN(this.valueRange.min) && !isNaN(this.valueRange.max)) {
+ var maxValueString = chartOptions.yMaxFormatter(this.valueRange.max, chartOptions.labels.precision),
+ minValueString = chartOptions.yMinFormatter(this.valueRange.min, chartOptions.labels.precision),
+ maxLabelPos = chartOptions.scrollBackwards
+ ? 0
+ : dimensions.width - context.measureText(maxValueString).width - 2,
+ minLabelPos = chartOptions.scrollBackwards
+ ? 0
+ : dimensions.width - context.measureText(minValueString).width - 2;
+ context.fillStyle = chartOptions.labels.fillStyle;
+ context.fillText(maxValueString, maxLabelPos, chartOptions.labels.fontSize);
+ context.fillText(minValueString, minLabelPos, dimensions.height - 2);
+ }
+
+ // Display intermediate y axis labels along y-axis to the left of the chart
+ if (
+ chartOptions.labels.showIntermediateLabels &&
+ !isNaN(this.valueRange.min) &&
+ !isNaN(this.valueRange.max) &&
+ chartOptions.grid.verticalSections > 0
+ ) {
+ // show a label above every vertical section divider
+ var step = (this.valueRange.max - this.valueRange.min) / chartOptions.grid.verticalSections;
+ var stepPixels = dimensions.height / chartOptions.grid.verticalSections;
+ for (var v = 1; v < chartOptions.grid.verticalSections; v++) {
+ var gy = dimensions.height - Math.round(v * stepPixels);
+ if (chartOptions.grid.sharpLines) {
+ gy -= 0.5;
}
+ var yValue = chartOptions.yIntermediateFormatter(this.valueRange.min + v * step, chartOptions.labels.precision);
+ //left of right axis?
+ let intermediateLabelPos = chartOptions.labels.intermediateLabelSameAxis
+ ? chartOptions.scrollBackwards
+ ? 0
+ : dimensions.width - context.measureText(yValue).width - 2
+ : chartOptions.scrollBackwards
+ ? dimensions.width - context.measureText(yValue).width - 2
+ : 0;
+
+ context.fillText(yValue, intermediateLabelPos, gy - chartOptions.grid.lineWidth);
}
-
- // Display timestamps along x-axis at the bottom of the chart.
- if (chartOptions.timestampFormatter && chartOptions.grid.millisPerLine > 0) {
- var textUntilX = chartOptions.scrollBackwards
- ? context.measureText(minValueString).width
- : dimensions.width - context.measureText(minValueString).width + 4;
- for (var t = time - (time % chartOptions.grid.millisPerLine);
- t >= oldestValidTime;
- t -= chartOptions.grid.millisPerLine) {
- var gx = timeToXPixel(t);
- // Only draw the timestamp if it won't overlap with the previously drawn one.
- if ((!chartOptions.scrollBackwards && gx < textUntilX) || (chartOptions.scrollBackwards && gx > textUntilX)) {
- // Formats the timestamp based on user specified formatting function
- // SmoothieChart.timeFormatter function above is one such formatting option
- var tx = new Date(t),
- ts = chartOptions.timestampFormatter(tx),
- tsWidth = context.measureText(ts).width;
-
- textUntilX = chartOptions.scrollBackwards
- ? gx + tsWidth + 2
- : gx - tsWidth - 2;
-
- context.fillStyle = chartOptions.labels.fillStyle;
- if(chartOptions.scrollBackwards) {
- context.fillText(ts, gx, dimensions.height - 2);
- } else {
- context.fillText(ts, gx - tsWidth, dimensions.height - 2);
- }
+ }
+
+ // Display timestamps along x-axis at the bottom of the chart.
+ if (chartOptions.timestampFormatter && chartOptions.grid.millisPerLine > 0) {
+ var textUntilX = chartOptions.scrollBackwards
+ ? context.measureText(minValueString).width
+ : dimensions.width - context.measureText(minValueString).width + 4;
+ for (
+ var t = time - (time % chartOptions.grid.millisPerLine);
+ t >= oldestValidTime;
+ t -= chartOptions.grid.millisPerLine
+ ) {
+ var gx = timeToXPixel(t);
+ // Only draw the timestamp if it won't overlap with the previously drawn one.
+ if ((!chartOptions.scrollBackwards && gx < textUntilX) || (chartOptions.scrollBackwards && gx > textUntilX)) {
+ // Formats the timestamp based on user specified formatting function
+ // SmoothieChart.timeFormatter function above is one such formatting option
+ var tx = new Date(t),
+ ts = chartOptions.timestampFormatter(tx),
+ tsWidth = context.measureText(ts).width;
+
+ textUntilX = chartOptions.scrollBackwards ? gx + tsWidth + 2 : gx - tsWidth - 2;
+
+ context.fillStyle = chartOptions.labels.fillStyle;
+ if (chartOptions.scrollBackwards) {
+ context.fillText(ts, gx, dimensions.height - 2);
+ } else {
+ context.fillText(ts, gx - tsWidth, dimensions.height - 2);
}
}
}
-
- // Display title.
- if (chartOptions.title.text !== '') {
- context.font = chartOptions.title.fontSize + 'px ' + chartOptions.title.fontFamily;
- var titleXPos = chartOptions.scrollBackwards ? dimensions.width - context.measureText(chartOptions.title.text).width - 2 : 2;
- if (chartOptions.title.verticalAlign == 'bottom') {
- context.textBaseline = 'bottom';
- var titleYPos = dimensions.height;
- } else if (chartOptions.title.verticalAlign == 'middle') {
- context.textBaseline = 'middle';
- var titleYPos = dimensions.height / 2;
- } else {
- context.textBaseline = 'top';
- var titleYPos = 0;
- }
- context.fillStyle = chartOptions.title.fillStyle;
- context.fillText(chartOptions.title.text, titleXPos, titleYPos);
+ }
+
+ // Display title.
+ if (chartOptions.title.text !== '') {
+ context.font = chartOptions.title.fontSize + 'px ' + chartOptions.title.fontFamily;
+ var titleXPos = chartOptions.scrollBackwards
+ ? dimensions.width - context.measureText(chartOptions.title.text).width - 2
+ : 2;
+ if (chartOptions.title.verticalAlign == 'bottom') {
+ context.textBaseline = 'bottom';
+ var titleYPos = dimensions.height;
+ } else if (chartOptions.title.verticalAlign == 'middle') {
+ context.textBaseline = 'middle';
+ var titleYPos = dimensions.height / 2;
+ } else {
+ context.textBaseline = 'top';
+ var titleYPos = 0;
}
-
- context.restore(); // See .save() above.
- };
-
- // Sample timestamp formatting function
- SmoothieChart.timeFormatter = function(date) {
- function pad2(number) { return (number < 10 ? '0' : '') + number }
- return pad2(date.getHours()) + ':' + pad2(date.getMinutes()) + ':' + pad2(date.getSeconds());
- };
-
- exports.TimeSeries = TimeSeries;
- exports.SmoothieChart = SmoothieChart;
-
- })(typeof exports === 'undefined' ? this : exports);
-
-
\ No newline at end of file
+ context.fillStyle = chartOptions.title.fillStyle;
+ context.fillText(chartOptions.title.text, titleXPos, titleYPos);
+ }
+
+ context.restore(); // See .save() above.
+ };
+
+ // Sample timestamp formatting function
+ SmoothieChart.timeFormatter = function (date) {
+ function pad2(number) {
+ return (number < 10 ? '0' : '') + number;
+ }
+ return pad2(date.getHours()) + ':' + pad2(date.getMinutes()) + ':' + pad2(date.getSeconds());
+ };
+
+ exports.TimeSeries = TimeSeries;
+ exports.SmoothieChart = SmoothieChart;
+})(typeof exports === 'undefined' ? this : exports);
diff --git a/src/lib/src/components/tree/TreeViewComponent.css b/src/lib/src/components/tree/TreeViewComponent.css
deleted file mode 100644
index 0c3e276..0000000
--- a/src/lib/src/components/tree/TreeViewComponent.css
+++ /dev/null
@@ -1,67 +0,0 @@
-@import url('../../pages/index.css');
-
-.tree {
- width: 100%;
- height: 100%;
-}
-
-.directory {
- font-size: 16px;
- color: white;
- user-select: none;
- padding: 20px;
-}
-
-.directory .tree,
-.directory .tree-node,
-.directory .tree-node-group {
- list-style: none;
- margin: 0;
- padding: 0;
-}
-
-.directory .tree-branch-wrapper,
-.directory .tree-node__leaf {
- outline: none;
-}
-
-.directory .tree-node {
- cursor: pointer;
-}
-
-.directory .tree-node:hover {
- background: rgb(255 255 255 / 10%);
-}
-
-.directory .tree .tree-node--focused {
- background: rgb(255 255 255 / 20%);
-}
-
-.directory .tree .tree-node--selected {
- background: rgb(231 34 100 / 20%)
-}
-
-.directory .tree-node__branch {
- display: block;
-}
-
-.directory .icon {
- vertical-align: middle;
- padding-right: 5px;
-}
-
-.directory .container.open {
- color: var(--active-bg-color);
- font-weight: bold;
-
- /* or use text-shadow for a bolder effect */
- text-shadow: 0 0 1px black;
-}
-
-.directory .container.closed {
- color: var(--active-color);
- font-weight: bold;
-
- /* or use text-shadow for a bolder effect */
- text-shadow: 0 0 1px black;
-}
\ No newline at end of file
diff --git a/src/lib/src/components/tree/TreeViewComponent.tsx b/src/lib/src/components/tree/TreeViewComponent.tsx
index 4c7ae64..6a23b43 100644
--- a/src/lib/src/components/tree/TreeViewComponent.tsx
+++ b/src/lib/src/components/tree/TreeViewComponent.tsx
@@ -1,14 +1,32 @@
-import React from 'react';
+import React, { useState } from 'react';
import TreeView, { flattenTree } from 'react-accessible-treeview';
import {
FaLayerGroup as ContainerIcon,
FaRegObjectGroup as SceneNodeIcon,
FaFileImage as SpriteIcon,
} from 'react-icons/fa6';
-import { SectionContainer, SectionHeader, Title, TitleGroup } from '../Container';
+import { SectionHeader, Title, TitleGroup } from '../Container';
import { usePixiStore } from '@lib/src/pages';
-import './TreeViewComponent.css';
+import { TreeDirectory, TreeWrapper } from './styles';
+
+type FlattenTreeData = ReturnTypePlease install the PixiJS DevTools extension from the Chrome Web Store to use this panel.
++ This page doesn’t appear to be using PixiJS. If this seems wrong, follow the project setup guide{'https://github.com/pixijs/dev-tools#readme'} +