@tsparticles/engine 4.0.0-alpha.22 → 4.0.0-alpha.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/515.min.js +1 -1
  2. package/browser/Core/Canvas.js +17 -1
  3. package/browser/Core/Container.js +1 -4
  4. package/browser/Core/Engine.js +1 -1
  5. package/browser/Core/Particle.js +1 -2
  6. package/browser/Core/Utils/Constants.js +1 -1
  7. package/browser/Core/Utils/QuadTree.js +54 -33
  8. package/browser/Core/Utils/Ranges.js +1 -1
  9. package/browser/Utils/CanvasUtils.js +12 -10
  10. package/browser/Utils/ColorUtils.js +9 -1
  11. package/browser/Utils/MathUtils.js +4 -6
  12. package/cjs/Core/Canvas.js +17 -1
  13. package/cjs/Core/Container.js +1 -4
  14. package/cjs/Core/Engine.js +1 -1
  15. package/cjs/Core/Particle.js +1 -2
  16. package/cjs/Core/Utils/Constants.js +1 -1
  17. package/cjs/Core/Utils/QuadTree.js +54 -33
  18. package/cjs/Core/Utils/Ranges.js +1 -1
  19. package/cjs/Utils/CanvasUtils.js +12 -10
  20. package/cjs/Utils/ColorUtils.js +9 -1
  21. package/cjs/Utils/MathUtils.js +4 -6
  22. package/dist_browser_Core_Container_js.js +5 -5
  23. package/esm/Core/Canvas.js +17 -1
  24. package/esm/Core/Container.js +1 -4
  25. package/esm/Core/Engine.js +1 -1
  26. package/esm/Core/Particle.js +1 -2
  27. package/esm/Core/Utils/Constants.js +1 -1
  28. package/esm/Core/Utils/QuadTree.js +54 -33
  29. package/esm/Core/Utils/Ranges.js +1 -1
  30. package/esm/Utils/CanvasUtils.js +12 -10
  31. package/esm/Utils/ColorUtils.js +9 -1
  32. package/esm/Utils/MathUtils.js +4 -6
  33. package/package.json +1 -1
  34. package/report.html +1 -1
  35. package/tsparticles.engine.js +9 -9
  36. package/tsparticles.engine.min.js +2 -2
  37. package/types/Core/Canvas.d.ts +5 -0
  38. package/types/Core/Interfaces/IContainerPlugin.d.ts +2 -0
  39. package/types/Core/Interfaces/IShapeDrawData.d.ts +8 -0
  40. package/types/Core/Utils/Constants.d.ts +1 -1
  41. package/types/Core/Utils/QuadTree.d.ts +3 -4
  42. package/types/Utils/CanvasUtils.d.ts +0 -3
  43. package/types/Utils/ColorUtils.d.ts +2 -0
  44. package/umd/Core/Canvas.js +16 -0
  45. package/umd/Core/Container.js +1 -4
  46. package/umd/Core/Engine.js +1 -1
  47. package/umd/Core/Particle.js +3 -4
  48. package/umd/Core/Utils/Constants.js +2 -2
  49. package/umd/Core/Utils/QuadTree.js +53 -32
  50. package/umd/Core/Utils/Ranges.js +1 -1
  51. package/umd/Utils/CanvasUtils.js +12 -11
  52. package/umd/Utils/ColorUtils.js +10 -1
  53. package/umd/Utils/MathUtils.js +4 -6
@@ -1,6 +1,6 @@
1
1
  import { clear, drawParticle, drawParticlePlugin, paintBase, paintImage } from "../Utils/CanvasUtils.js";
2
2
  import { cloneStyle, getFullScreenStyle, safeMatchMedia, safeMutationObserver } from "../Utils/Utils.js";
3
- import { defaultTransformValue, generatedAttribute, minimumSize, zIndexFactorOffset } from "./Utils/Constants.js";
3
+ import { defaultTransformValue, defaultZoom, generatedAttribute, half, minimumSize, zIndexFactorOffset, } from "./Utils/Constants.js";
4
4
  import { getStyleFromHsl, getStyleFromRgb, rangeColorToHsl, rangeColorToRgb } from "../Utils/ColorUtils.js";
5
5
  const fColorIndex = 0, sColorIndex = 1;
6
6
  function setTransformValue(factor, newFactor, key) {
@@ -42,6 +42,7 @@ export class Canvas {
42
42
  container;
43
43
  element;
44
44
  size;
45
+ zoom = defaultZoom;
45
46
  _canvasClearPlugins;
46
47
  _canvasPaintPlugins;
47
48
  _canvasSettings;
@@ -66,6 +67,7 @@ export class Canvas {
66
67
  _reusablePluginColors = [undefined, undefined];
67
68
  _reusableTransform = {};
68
69
  _standardSize;
70
+ _zoomCenter;
69
71
  constructor(container, engine) {
70
72
  this.container = container;
71
73
  this._engine = engine;
@@ -220,6 +222,16 @@ export class Canvas {
220
222
  }
221
223
  });
222
224
  }
225
+ getZoomCenter() {
226
+ const pxRatio = this.container.retina.pixelRatio, { width, height } = this.size;
227
+ if (this._zoomCenter) {
228
+ return this._zoomCenter;
229
+ }
230
+ return {
231
+ x: (width * half) / pxRatio,
232
+ y: (height * half) / pxRatio,
233
+ };
234
+ }
223
235
  init() {
224
236
  this._safeMutationObserver(obs => {
225
237
  obs.disconnect();
@@ -419,6 +431,10 @@ export class Canvas {
419
431
  this._pointerEvents = type;
420
432
  this._repairStyle();
421
433
  }
434
+ setZoom(zoomLevel, center) {
435
+ this.zoom = zoomLevel;
436
+ this._zoomCenter = center;
437
+ }
422
438
  stop() {
423
439
  this._safeMutationObserver(obs => {
424
440
  obs.disconnect();
@@ -122,15 +122,12 @@ export class Container {
122
122
  return;
123
123
  }
124
124
  let refreshTime = force;
125
- const frame = (timestamp) => {
125
+ this._drawAnimationFrame = animate((timestamp) => {
126
126
  if (refreshTime) {
127
127
  this._lastFrameTime = undefined;
128
128
  refreshTime = false;
129
129
  }
130
130
  this._nextFrame(timestamp);
131
- };
132
- this._drawAnimationFrame = animate(timestamp => {
133
- frame(timestamp);
134
131
  });
135
132
  }
136
133
  async export(type, options = {}) {
@@ -87,7 +87,7 @@ export class Engine {
87
87
  return this._domArray;
88
88
  }
89
89
  get version() {
90
- return "4.0.0-alpha.22";
90
+ return "4.0.0-alpha.24";
91
91
  }
92
92
  addColorManager(name, manager) {
93
93
  this.colorManagers.set(name, manager);
@@ -1,4 +1,5 @@
1
1
  import { Vector, Vector3d } from "./Utils/Vectors.js";
2
+ import { alterHsl, getHslFromAnimation } from "../Utils/ColorUtils.js";
2
3
  import { calcExactPositionOrRandomFromSize, clamp, degToRad, getParticleBaseVelocity, getParticleDirectionAngle, getRandom, getRangeValue, randomInRangeValue, setRangeValue, } from "../Utils/MathUtils.js";
3
4
  import { decayOffset, defaultAngle, defaultOpacity, defaultRetryCount, defaultTransform, double, doublePI, half, identity, millisecondsToSeconds, minZ, randomColorValue, squareExp, triple, tryCountIncrement, zIndexFactorOffset, } from "./Utils/Constants.js";
4
5
  import { deepExtend, getPosition, initParticleNumericAnimationValue, isInArray, itemFromSingleOrMultiple, } from "../Utils/Utils.js";
@@ -6,8 +7,6 @@ import { EventType } from "../Enums/Types/EventType.js";
6
7
  import { MoveDirection } from "../Enums/Directions/MoveDirection.js";
7
8
  import { OutMode } from "../Enums/Modes/OutMode.js";
8
9
  import { ParticleOutType } from "../Enums/Types/ParticleOutType.js";
9
- import { alterHsl } from "../Utils/CanvasUtils.js";
10
- import { getHslFromAnimation } from "../Utils/ColorUtils.js";
11
10
  import { loadParticlesOptions } from "../Utils/OptionsUtils.js";
12
11
  function loadEffectData(effect, effectOptions, id, reduceDuplicates) {
13
12
  const effectData = effectOptions.options[effect];
@@ -7,4 +7,4 @@ export const generatedAttribute = "generated", resizeEvent = "resize", visibilit
7
7
  b: 0,
8
8
  c: 0,
9
9
  d: 1,
10
- }, randomColorValue = "random", midColorValue = "mid", double = 2, doublePI = Math.PI * double, defaultFps = 60, defaultAlpha = 1, generatedTrue = "true", generatedFalse = "false", canvasTag = "canvas", defaultRetryCount = 0, squareExp = 2, qTreeCapacity = 4, defaultRemoveQuantity = 1, defaultRatio = 1, defaultReduceFactor = 1, subdivideCount = 4, inverseFactorNumerator = 1.0, rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, empty = 0, quarter = 0.25, threeQuarter = half + quarter, minVelocity = 0, defaultTransformValue = 1, minimumSize = 0, minimumLength = 0, zIndexFactorOffset = 1, defaultOpacity = 1, clickRadius = 1, touchEndLengthOffset = 1, minCoordinate = 0, removeDeleteCount = 1, removeMinIndex = 0, defaultFpsLimit = 120, minFpsLimit = 0, canvasFirstIndex = 0, loadRandomFactor = 10000, loadMinIndex = 0, one = 1, none = 0, decayOffset = 1, tryCountIncrement = 1, minRetries = 0, minZ = 0, defaultRadius = 0, posOffset = -quarter, sizeFactor = 1.5, minLimit = 0, countOffset = 1, minCount = 0, minIndex = 0, lengthOffset = 1, defaultDensityFactor = 1, deleteCount = 1, touchDelay = 500, manualDefaultPosition = 50, defaultAngle = 0, identity = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, triple = 3, sextuple = 6, sNormalizedOffset = 1, phaseNumerator = 1, defaultRgbMin = 0, defaultVelocity = 0, defaultLoops = 0, defaultTime = 0;
10
+ }, randomColorValue = "random", midColorValue = "mid", double = 2, doublePI = Math.PI * double, defaultFps = 60, defaultAlpha = 1, generatedTrue = "true", generatedFalse = "false", canvasTag = "canvas", defaultRetryCount = 0, squareExp = 2, qTreeCapacity = 4, defaultRemoveQuantity = 1, defaultRatio = 1, defaultReduceFactor = 1, subdivideCount = 4, inverseFactorNumerator = 1, rgbMax = 255, hMax = 360, sMax = 100, lMax = 100, hMin = 0, sMin = 0, hPhase = 60, empty = 0, quarter = 0.25, threeQuarter = half + quarter, minVelocity = 0, defaultTransformValue = 1, minimumSize = 0, minimumLength = 0, zIndexFactorOffset = 1, defaultOpacity = 1, clickRadius = 1, touchEndLengthOffset = 1, minCoordinate = 0, removeDeleteCount = 1, removeMinIndex = 0, defaultFpsLimit = 120, minFpsLimit = 0, canvasFirstIndex = 0, loadRandomFactor = 10000, loadMinIndex = 0, one = 1, none = 0, decayOffset = 1, tryCountIncrement = 1, minRetries = 0, minZ = 0, defaultRadius = 0, posOffset = -quarter, sizeFactor = 1.5, minLimit = 0, countOffset = 1, minCount = 0, minIndex = 0, lengthOffset = 1, defaultDensityFactor = 1, deleteCount = 1, touchDelay = 500, manualDefaultPosition = 50, defaultAngle = 0, identity = 1, minStrokeWidth = 0, lFactor = 1, lMin = 0, triple = 3, sextuple = 6, sNormalizedOffset = 1, phaseNumerator = 1, defaultRgbMin = 0, defaultVelocity = 0, defaultLoops = 0, defaultTime = 0, defaultZoom = 1;
@@ -1,51 +1,71 @@
1
1
  import { Circle, Rectangle } from "./Ranges.js";
2
- import { double, half, subdivideCount } from "./Constants.js";
2
+ import { half, subdivideCount } from "./Constants.js";
3
3
  import { getDistance } from "../../Utils/MathUtils.js";
4
4
  export class QuadTree {
5
5
  rectangle;
6
6
  capacity;
7
- _divided;
8
- _points;
9
- _subs;
7
+ _points = [];
8
+ _subs = null;
10
9
  constructor(rectangle, capacity) {
11
10
  this.rectangle = rectangle;
12
11
  this.capacity = capacity;
13
- this._points = [];
14
- this._divided = false;
15
- this._subs = [];
16
12
  }
17
13
  insert(point) {
18
14
  if (!this.rectangle.contains(point.position)) {
19
15
  return false;
20
16
  }
21
- if (this._points.length < this.capacity) {
22
- this._points.push(point);
23
- return true;
24
- }
25
- if (!this._divided) {
17
+ const subs = this._subs;
18
+ if (!subs) {
19
+ if (this._points.length < this.capacity) {
20
+ this._points.push(point);
21
+ return true;
22
+ }
26
23
  this._subdivide();
24
+ const newSubs = this._subs;
25
+ for (const p of this._points) {
26
+ for (let s = 0; s < subdivideCount; s++) {
27
+ if (newSubs?.[s]?.insert(p)) {
28
+ break;
29
+ }
30
+ }
31
+ }
32
+ this._points.length = 0;
33
+ for (let s = 0; s < subdivideCount; s++) {
34
+ if (newSubs?.[s]?.insert(point)) {
35
+ return true;
36
+ }
37
+ }
38
+ return false;
27
39
  }
28
- return this._subs.some(sub => sub.insert(point));
40
+ for (let s = 0; s < subdivideCount; s++) {
41
+ if (subs[s]?.insert(point)) {
42
+ return true;
43
+ }
44
+ }
45
+ return false;
29
46
  }
30
- query(range, check) {
31
- const res = [];
47
+ query(range, check, out = []) {
32
48
  if (!range.intersects(this.rectangle)) {
33
- return [];
49
+ return out;
34
50
  }
35
- for (const p of this._points) {
36
- if (!range.contains(p.position) &&
37
- getDistance(range.position, p.position) > p.particle.getRadius() &&
38
- (!check || check(p.particle))) {
51
+ const points = this._points;
52
+ for (const p of points) {
53
+ const particle = p.particle;
54
+ if (!range.contains(p.position) && getDistance(range.position, p.position) > particle.getRadius()) {
55
+ continue;
56
+ }
57
+ if (check && !check(particle)) {
39
58
  continue;
40
59
  }
41
- res.push(p.particle);
60
+ out.push(particle);
42
61
  }
43
- if (this._divided) {
44
- for (const sub of this._subs) {
45
- res.push(...sub.query(range, check));
62
+ const subs = this._subs;
63
+ if (subs) {
64
+ for (const s of subs) {
65
+ s.query(range, check, out);
46
66
  }
47
67
  }
48
- return res;
68
+ return out;
49
69
  }
50
70
  queryCircle(position, radius, check) {
51
71
  return this.query(new Circle(position.x, position.y, radius), check);
@@ -53,12 +73,13 @@ export class QuadTree {
53
73
  queryRectangle(position, size, check) {
54
74
  return this.query(new Rectangle(position.x, position.y, size.width, size.height), check);
55
75
  }
56
- _subdivide = () => {
57
- const { x, y } = this.rectangle.position, { width, height } = this.rectangle.size, { capacity } = this;
58
- for (let i = 0; i < subdivideCount; i++) {
59
- const fixedIndex = i % double;
60
- this._subs.push(new QuadTree(new Rectangle(x + width * half * fixedIndex, y + height * half * (Math.round(i * half) - fixedIndex), width * half, height * half), capacity));
61
- }
62
- this._divided = true;
63
- };
76
+ _subdivide() {
77
+ const rect = this.rectangle, { x, y } = rect.position, { width, height } = rect.size, halfWidth = width * half, halfHeight = height * half, capacity = this.capacity;
78
+ this._subs = [
79
+ new QuadTree(new Rectangle(x, y, halfWidth, halfHeight), capacity),
80
+ new QuadTree(new Rectangle(x + halfWidth, y, halfWidth, halfHeight), capacity),
81
+ new QuadTree(new Rectangle(x, y + halfHeight, halfWidth, halfHeight), capacity),
82
+ new QuadTree(new Rectangle(x + halfWidth, y + halfHeight, halfWidth, halfHeight), capacity),
83
+ ];
84
+ }
64
85
  }
@@ -24,7 +24,7 @@ export class Circle extends BaseRange {
24
24
  intersects(range) {
25
25
  const pos1 = this.position, pos2 = range.position, distPos = { x: Math.abs(pos2.x - pos1.x), y: Math.abs(pos2.y - pos1.y) }, r = this.radius;
26
26
  if (range instanceof Circle || range.type === RangeType.circle) {
27
- const circleRange = range, rSum = r + circleRange.radius, dist = Math.sqrt(distPos.x ** squareExp + distPos.y ** squareExp);
27
+ const circleRange = range, rSum = r + circleRange.radius, dist = Math.hypot(distPos.x, distPos.y);
28
28
  return rSum > dist;
29
29
  }
30
30
  else if (range instanceof Rectangle || range.type === RangeType.rectangle) {
@@ -1,5 +1,4 @@
1
- import { lFactor, minStrokeWidth, originPoint } from "../Core/Utils/Constants.js";
2
- import { AlterType } from "../Enums/Types/AlterType.js";
1
+ import { defaultZoom, minStrokeWidth, originPoint } from "../Core/Utils/Constants.js";
3
2
  export function paintBase(context, dimension, baseColor) {
4
3
  context.fillStyle = baseColor ?? "rgba(0,0,0,0)";
5
4
  context.fillRect(originPoint.x, originPoint.y, dimension.width, dimension.height);
@@ -16,7 +15,10 @@ export function clear(context, dimension) {
16
15
  context.clearRect(originPoint.x, originPoint.y, dimension.width, dimension.height);
17
16
  }
18
17
  export function drawParticle(data) {
19
- const { container, context, particle, delta, colorStyles, radius, opacity, transform } = data, pos = particle.getPosition(), transformData = particle.getTransformData(transform);
18
+ const { container, context, particle, delta, colorStyles, radius, opacity, transform } = data, pos = particle.getPosition(), transformData = particle.getTransformData(transform), drawScale = defaultZoom, drawPosition = {
19
+ x: pos.x,
20
+ y: pos.y,
21
+ };
20
22
  context.setTransform(transformData.a, transformData.b, transformData.c, transformData.d, pos.x, pos.y);
21
23
  if (colorStyles.fill) {
22
24
  context.fillStyle = colorStyles.fill;
@@ -30,13 +32,20 @@ export function drawParticle(data) {
30
32
  context,
31
33
  particle,
32
34
  radius,
35
+ drawRadius: radius * drawScale,
33
36
  opacity,
34
37
  delta,
35
38
  pixelRatio: container.retina.pixelRatio,
36
39
  fill: particle.shapeFill,
37
40
  stroke: strokeWidth > minStrokeWidth || !particle.shapeFill,
38
41
  transformData,
42
+ position: { ...pos },
43
+ drawPosition,
44
+ drawScale,
39
45
  };
46
+ for (const plugin of container.plugins) {
47
+ plugin.drawParticleTransform?.(drawData);
48
+ }
40
49
  drawBeforeEffect(container, drawData);
41
50
  drawShapeBeforeDraw(container, drawData);
42
51
  drawShape(container, drawData);
@@ -115,10 +124,3 @@ export function drawParticlePlugin(context, plugin, particle, delta) {
115
124
  }
116
125
  plugin.drawParticle(context, particle, delta);
117
126
  }
118
- export function alterHsl(color, type, value) {
119
- return {
120
- h: color.h,
121
- s: color.s,
122
- l: color.l + (type === AlterType.darken ? -lFactor : lFactor) * value,
123
- };
124
- }
@@ -1,6 +1,7 @@
1
1
  import { clamp, getRandom, getRandomInRange, getRangeMax, getRangeMin, getRangeValue, mix, randomInRangeValue, setRangeValue, } from "./MathUtils.js";
2
- import { decayOffset, defaultLoops, defaultOpacity, defaultRgbMin, defaultTime, defaultVelocity, double, hMax, hMin, hPhase, half, identity, lMax, lMin, midColorValue, millisecondsToSeconds, percentDenominator, phaseNumerator, randomColorValue, rgbMax, sMax, sMin, sNormalizedOffset, sextuple, triple, } from "../Core/Utils/Constants.js";
2
+ import { decayOffset, defaultLoops, defaultOpacity, defaultRgbMin, defaultTime, defaultVelocity, double, hMax, hMin, hPhase, half, identity, lFactor, lMax, lMin, midColorValue, millisecondsToSeconds, percentDenominator, phaseNumerator, randomColorValue, rgbMax, sMax, sMin, sNormalizedOffset, sextuple, triple, } from "../Core/Utils/Constants.js";
3
3
  import { isArray, isString } from "./TypeUtils.js";
4
+ import { AlterType } from "../Enums/Types/AlterType.js";
4
5
  import { AnimationStatus } from "../Enums/AnimationStatus.js";
5
6
  import { itemFromArray } from "./Utils.js";
6
7
  const styleCache = new Map(), maxCacheSize = 1000, firstIndex = 0, rgbFixedPrecision = 2, hslFixedPrecision = 2;
@@ -350,3 +351,10 @@ export function updateColor(color, delta) {
350
351
  updateColorValue(s, ranges.s, true, delta);
351
352
  updateColorValue(l, ranges.l, true, delta);
352
353
  }
354
+ export function alterHsl(color, type, value) {
355
+ return {
356
+ h: color.h,
357
+ s: color.s,
358
+ l: color.l + (type === AlterType.darken ? -lFactor : lFactor) * value,
359
+ };
360
+ }
@@ -21,10 +21,8 @@ export function getRandomInRange(min, max) {
21
21
  return getRandom() * (max - min) + min;
22
22
  }
23
23
  export function setAnimationFunctions(nextFrame, cancel) {
24
- _animationLoop.nextFrame = (callback) => nextFrame(callback);
25
- _animationLoop.cancel = (handle) => {
26
- cancel(handle);
27
- };
24
+ _animationLoop.nextFrame = nextFrame;
25
+ _animationLoop.cancel = cancel;
28
26
  }
29
27
  export function animate(fn) {
30
28
  return _animationLoop.nextFrame(fn);
@@ -68,8 +66,8 @@ export function setRangeValue(source, value) {
68
66
  : setRangeValue(min, max);
69
67
  }
70
68
  export function getDistances(pointA, pointB) {
71
- const dx = pointA.x - pointB.x, dy = pointA.y - pointB.y, squareExp = 2;
72
- return { dx: dx, dy: dy, distance: Math.sqrt(dx ** squareExp + dy ** squareExp) };
69
+ const dx = pointA.x - pointB.x, dy = pointA.y - pointB.y;
70
+ return { dx: dx, dy: dy, distance: Math.hypot(dx, dy) };
73
71
  }
74
72
  export function getDistance(pointA, pointB) {
75
73
  return getDistances(pointA, pointB).distance;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsparticles/engine",
3
- "version": "4.0.0-alpha.22",
3
+ "version": "4.0.0-alpha.24",
4
4
  "description": "Easily create highly customizable particle, confetti and fireworks animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.",
5
5
  "homepage": "https://particles.js.org",
6
6
  "scripts": {
package/report.html CHANGED
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="UTF-8"/>
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
6
- <title>@tsparticles/engine [5 Feb 2026 at 19:32]</title>
6
+ <title>@tsparticles/engine [12 Feb 2026 at 18:48]</title>
7
7
  <link rel="shortcut icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABrVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+O1foceMD///+J0/qK1Pr7/v8Xdr/9///W8P4UdL7L7P0Scr2r4Pyj3vwad8D5/f/2/f+55f3E6f34+/2H0/ojfMKpzOd0rNgQcb3F3O/j9f7c8v6g3Pz0/P/w+v/q+P7n9v6T1/uQ1vuE0vqLut/y+v+Z2fvt+f+15Pzv9fuc2/vR7v2V2Pvd6/bg9P7I6/285/2y4/yp3/zp8vk8i8kqgMT7/P31+fyv4vxGkcz6/P6/6P3j7vfS5PNnpNUxhcbO7f7F6v3O4vHK3/DA2u631Ouy0eqXweKJud5wqthfoNMMbLvY8f73+v2dxeR8sNtTmdDx9/zX6PSjyeaCtd1YnNGX2PuQveCGt95Nls42h8dLlM3F4vBtAAAAM3RSTlMAAyOx0/sKBvik8opWGBMOAe3l1snDm2E9LSb06eHcu5JpHbarfHZCN9CBb08zzkdNS0kYaptYAAAFV0lEQVRYw92X51/aYBDHHS2O2qqttVbrqNq9m+TJIAYIShBkWwqIiCgoWvfeq7Z2/s29hyQNyUcR7LveGwVyXy6XH8/9rqxglLfUPLxVduUor3h0rfp2TYvpivk37929TkG037hffoX0+peVtZQc1589rigVUdXS/ABSAyEmGIO/1XfvldSK8vs3OqB6u3m0nxmIrvgB0dj7rr7Y9IbuF68hnfFaiHA/sxqm0wciIG43P60qKv9WXWc1RXGh/mFESFABTSBi0sNAKzqet17eCtOb3kZIDwxEEU0oAIJGYxNBDhBND29e0rtXXbcpuPmED9IhEAAQ/AXEaF8EPmnrrKsv0LvWR3fg5sWDNAFZOgAgaKvZDogHNU9MFwnnYROkc56RD5CjAbQX9Ow4g7upCsvYu55aSI/Nj0H1akgKQEUM94dwK65hYRmFU9MIcH/fqJYOZYcnuJSU/waKDgTOEVaVKhwrTRP5XzgSpAITYzom7UvkhFX5VutmxeNnWDjjswTKTyfgluNDGbUpWissXhF3s7mlSml+czWkg3D0l1nNjGNjz3myOQOa1KM/jOS6ebdbAVTCi4gljHSFrviza7tOgRWcS0MOUX9zdNgag5w7rRqA44Lzw0hr1WqES36dFliSJFlh2rXIae3FFcDDgKdxrUIDePr8jGcSClV1u7A9xeN0ModY/pHMxmR1EzRh8TJiwqsHmKW0l4FCEZI+jHio+JdPPE9qwQtTRxku2D8sIeRL2LnxWSllANCQGOIiqVHAz2ye2JR0DcH+HoxDkaADLjgxjKQ+AwCX/g0+DNgdG0ukYCONAe+dbc2IAc6fwt1ARoDSezNHxV2Cmzwv3O6lDMV55edBGwGK9n1+x2F8EDfAGCxug8MhpsMEcTEAWf3rx2vZhe/LAmtIn/6apE6PN0ULKgywD9mmdxbmFl3OvD5AS5fW5zLbv/YHmcsBTjf/afDz3MaZTVCfAP9z6/Bw6ycv8EUBWJIn9zYcoAWWlW9+OzO3vkTy8H+RANLmdrpOuYWdZYEXpo+TlCJrW5EARb7fF+bWdqf3hhyZI1nWJQHgznErZhbjoEsWqi8dQNoE294aldzFurwSABL2XXMf9+H1VQGke9exw5P/AnA5Pv5ngMul7LOvO922iwACu8WkCwLCafvM4CeWPxfA8lNHcWZSoi8EwMAIciKX2Z4SWCMAa3snCZ/G4EA8D6CMLNFsGQhkkz/gQNEBbPCbWsxGUpYVu3z8IyNAknwJkfPMEhLyrdi5RTyUVACkw4GSFRNWJNEW+fgPGwHD8/JxnRuLabN4CGNRkAE23na2+VmEAUmrYymSGjMAYqH84YUIyzgzs3XC7gNgH36Vcc4zKY9o9fgPBXUAiHHwVboBHGLiX6Zcjp1f2wu4tvzZKo0ecPnDtQYDQvJXaBeNzce45Fp28ZQLrEZVuFqgBwOalArKXnW1UzlnSusQKJqKYNuz4tOnI6sZG4zanpemv+7ySU2jbA9h6uhcgpfy6G2PahirDZ6zvq6zDduMVFTKvzw8wgyEdelwY9in3XkEPs3osJuwRQ4qTkfzifndg9Gfc4pdsu82+tTnHZTBa2EAMrqr2t43pguc8tNm7JQVQ2S0ukj2d22dhXYP0/veWtwKrCkNoNimAN5+Xr/oLrxswKbVJjteWrX7eR63o4j9q0GxnaBdWgGA5VStpanIjQmEhV0/nVt5VOFUvix6awJhPcAaTEShgrG+iGyvb5a0Ndb1YGHFPEwoqAinoaykaID1o1pdPNu7XsnCKQ3R+hwWIIhGvORcJUBYXe3Xa3vq/mF/N9V13ugufMkfXn+KHsRD0B8AAAAASUVORK5CYII=" type="image/x-icon" />
8
8
 
9
9
  <script>