Переглянути джерело

stepVal is set as optional.

Ievgen Naida 2 роки тому
батько
коміт
56bccf115f

+ 2 - 1
.eslintignore

@@ -9,4 +9,5 @@ tests/src
 ./tests/tests/
 ./tests/tests/
 tests/tests/
 tests/tests/
 tests/*.js
 tests/*.js
-tests/*.js.map
+tests/*.js.map
+karma.conf.js

+ 15 - 16
.eslintrc.js

@@ -1,16 +1,15 @@
-module.exports = {
-    parser: "@typescript-eslint/parser", // Specifies the ESLint parser
-    parserOptions: {
-        ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
-        sourceType: "module" // Allows for the use of imports
-    },
-    extends: [
-        "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin
-        "plugin:prettier/recommended" 
-
-    ],
-    rules: {
-        // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
-        // e.g. "@typescript-eslint/explicit-function-return-type": "off",
-    }
-};
+module.exports = {
+  parser: '@typescript-eslint/parser', // Specifies the ESLint parser
+  parserOptions: {
+    ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
+    sourceType: 'module', // Allows for the use of imports
+  },
+  extends: [
+    'plugin:@typescript-eslint/recommended', // Uses the recommended rules from the @typescript-eslint/eslint-plugin
+    'plugin:prettier/recommended',
+  ],
+  rules: {
+    // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
+    // e.g. "@typescript-eslint/explicit-function-return-type": "off",
+  },
+};

+ 6 - 6
.prettierrc.js

@@ -1,8 +1,8 @@
 module.exports = {
 module.exports = {
-	semi: true,
-	trailingComma: 'all',
-	singleQuote: true,
-	printWidth: 200,
-	tabWidth: 2,
-	useTabs: false,
+  semi: true,
+  trailingComma: 'all',
+  singleQuote: true,
+  printWidth: 200,
+  tabWidth: 2,
+  useTabs: false,
 };
 };

+ 16 - 9
README.md

@@ -131,7 +131,9 @@ See the [live demo](https://ievgennaida.github.io/animation-timeline-control/)
 ## Model
 ## Model
 
 
 Keyframes model is used to pass keyframes and rows to be visualized.
 Keyframes model is used to pass keyframes and rows to be visualized.
-Component is not changing passed model and it's used only for the visual purpose. It also means that any attached metadata can be passed and it will be preserved (Use case: you can attach additional data for each keyframe).
+Component is using passed model for the visualization purpose and has no method to manage tracks or keyframes.
+It also means that any attached metadata can be passed and it will be preserved
+(Use case: you can attach additional data for each keyframe).
 
 
 Read only and defined by the interfaces:
 Read only and defined by the interfaces:
 
 
@@ -209,13 +211,11 @@ Styles can be applied on a few levels:
 
 
 Styles are applied by a global settings and can be overridden by a row or keyframe style.
 Styles are applied by a global settings and can be overridden by a row or keyframe style.
 
 
-## Debug component
+## Changes
 
 
-VsCode configuration is included to the configuration,
-so you can navigate to the debug window and click 'Launch Debug File'.
-Put breakpoint in any typescript file and trigger function from the browser.
+## 2.2.1
 
 
-## Changes
+ TypeScript fixes, updated build packages.
 
 
 ## > 2.0
 ## > 2.0
 
 
@@ -244,7 +244,16 @@ Run next command to pack JavaScript as a bundle:
 
 
 ### Debug
 ### Debug
 
 
-VSCode is used as IDE.
+VSCode is used as IDE and configuration is included to the project sources.
+
+To debug project you should run command once files are changed:
+
+```cmd
+npm run build
+```
+
+Then navigate to the debug window and click 'Launch Debug File'.
+Put breakpoint in any typescript file and trigger function from the browser.
 
 
 Recommended extensions:
 Recommended extensions:
 
 
@@ -252,8 +261,6 @@ Recommended extensions:
 - ESLint
 - ESLint
 - esbenp.prettier-vscode
 - esbenp.prettier-vscode
 
 
-Click 'debug start' and ensure that unminified version of the file is used.
-
 ### Dev Dependencies
 ### Dev Dependencies
 
 
 Component has no production dependencies when built.
 Component has no production dependencies when built.

+ 1 - 1
cspell.json

@@ -1,5 +1,5 @@
 {
 {
-  "version": "0.1",
+  "version": "0.2",
   "words": [
   "words": [
     "NESW",
     "NESW",
     "NWSE",
     "NWSE",

+ 25 - 25
karma.conf.js

@@ -1,25 +1,25 @@
-// Karma is a tool to start server and test component in the real or headless browser.
-const {createDefaultConfig} = require('@open-wc/testing-karma');
-const merge = require('deepmerge');
-
-module.exports = config => {
-    config.set(
-        merge(createDefaultConfig(config), {
-            frameworks: ['mocha', 'chai'],
-            client: {
-                mocha: {ui: 'bdd'}
-            },
-            files: [
-                {
-                    pattern: config.grep ? config.grep : 'test/**/*.test.js',
-                    type: 'module'
-                },
-            ],
-            esm: {
-                // if you are using 'bare module imports' you will need this option
-                nodeResolve: true,
-            },
-        }),
-    );
-    return config;
-}
+// Karma is a tool to start server and test component in the real or headless browser.
+const { createDefaultConfig } = require('@open-wc/testing-karma');
+const merge = require('deepmerge');
+
+module.exports = (config) => {
+  config.set(
+    merge(createDefaultConfig(config), {
+      frameworks: ['mocha', 'chai'],
+      client: {
+        mocha: { ui: 'bdd' },
+      },
+      files: [
+        {
+          pattern: config.grep ? config.grep : 'test/**/*.test.js',
+          type: 'module',
+        },
+      ],
+      esm: {
+        // if you are using 'bare module imports' you will need this option
+        nodeResolve: true,
+      },
+    }),
+  );
+  return config;
+};

+ 83 - 7
lib/animation-timeline.js

@@ -49,7 +49,28 @@ __webpack_require__.r(__webpack_exports__);
 
 
 // EXPORTS
 // EXPORTS
 __webpack_require__.d(__webpack_exports__, {
 __webpack_require__.d(__webpack_exports__, {
-  "Timeline": () => (/* binding */ Timeline)
+  "Timeline": () => (/* reexport */ Timeline),
+  "TimelineCapShape": () => (/* reexport */ TimelineCapShape),
+  "TimelineClickEvent": () => (/* reexport */ TimelineClickEvent),
+  "TimelineCursorType": () => (/* reexport */ TimelineCursorType),
+  "TimelineDragEvent": () => (/* reexport */ TimelineDragEvent),
+  "TimelineElementType": () => (/* reexport */ TimelineElementType),
+  "TimelineEventSource": () => (/* reexport */ TimelineEventSource),
+  "TimelineEvents": () => (/* reexport */ TimelineEvents),
+  "TimelineEventsEmitter": () => (/* reexport */ TimelineEventsEmitter),
+  "TimelineInteractionMode": () => (/* reexport */ TimelineInteractionMode),
+  "TimelineKeyframeChangedEvent": () => (/* reexport */ TimelineKeyframeChangedEvent),
+  "TimelineKeyframeShape": () => (/* reexport */ TimelineKeyframeShape),
+  "TimelineSelectedEvent": () => (/* reexport */ TimelineSelectedEvent),
+  "TimelineSelectionMode": () => (/* reexport */ TimelineSelectionMode),
+  "TimelineStyleUtils": () => (/* reexport */ TimelineStyleUtils),
+  "TimelineTimeChangedEvent": () => (/* reexport */ TimelineTimeChangedEvent),
+  "TimelineUtils": () => (/* reexport */ TimelineUtils),
+  "defaultTimelineConsts": () => (/* reexport */ defaultTimelineConsts),
+  "defaultTimelineKeyframeStyle": () => (/* reexport */ defaultTimelineKeyframeStyle),
+  "defaultTimelineOptions": () => (/* reexport */ defaultTimelineOptions),
+  "defaultTimelineRowStyle": () => (/* reexport */ defaultTimelineRowStyle),
+  "defaultTimelineStyle": () => (/* reexport */ defaultTimelineStyle)
 });
 });
 
 
 ;// CONCATENATED MODULE: ./src/timelineEventsEmitter.ts
 ;// CONCATENATED MODULE: ./src/timelineEventsEmitter.ts
@@ -994,7 +1015,7 @@ var Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
    */
    */
 
 
   /**
   /**
-   * Dynamically generated event.
+   * Dynamically generated canvas inside of the container.
    */
    */
 
 
   /**
   /**
@@ -2156,7 +2177,7 @@ var Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
         min = 0;
         min = 0;
       }
       }
       min *= this._currentZoom || 1;
       min *= this._currentZoom || 1;
-      var steps = this._options.stepVal * this._currentZoom || 1;
+      var steps = (this._options.stepVal || 0) * this._currentZoom || 1;
       var val = min + px / this._options.stepPx * steps;
       var val = min + px / this._options.stepPx * steps;
       return val;
       return val;
     }
     }
@@ -2191,7 +2212,7 @@ var Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
         min = 0;
         min = 0;
       }
       }
       min *= this._currentZoom || 1;
       min *= this._currentZoom || 1;
-      var steps = this._options.stepVal * this._currentZoom || 1;
+      var steps = (this._options.stepVal || 0) * this._currentZoom || 1;
       return (-min + val) * (this._options.stepPx / steps);
       return (-min + val) * (this._options.stepPx / steps);
     }
     }
 
 
@@ -2945,7 +2966,7 @@ var Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
     value: function _setOptions(toSet) {
     value: function _setOptions(toSet) {
       this._options = this._mergeOptions(toSet);
       this._options = this._mergeOptions(toSet);
       // Normalize and validate spans per value.
       // Normalize and validate spans per value.
-      this._options.snapStep = TimelineUtils.keepInBounds(this._options.snapStep, 0, this._options.stepVal);
+      this._options.snapStep = TimelineUtils.keepInBounds(this._options.snapStep, 0, this._options.stepVal || 0);
       this._currentZoom = this._setZoom(this._options.zoom, this._options.zoomMin, this._options.zoomMax);
       this._currentZoom = this._setZoom(this._options.zoom, this._options.zoomMin, this._options.zoomMax);
       this._options.min = TimelineUtils.isNumber(this._options.min) ? this._options.min : 0;
       this._options.min = TimelineUtils.isNumber(this._options.min) ? this._options.min : 0;
       this._options.max = TimelineUtils.isNumber(this._options.max) ? this._options.max : Number.MAX_VALUE;
       this._options.max = TimelineUtils.isNumber(this._options.max) ? this._options.max : Number.MAX_VALUE;
@@ -3018,8 +3039,8 @@ var Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
   }, {
   }, {
     key: "_updateCanvasScale",
     key: "_updateCanvasScale",
     value: function _updateCanvasScale() {
     value: function _updateCanvasScale() {
-      if (!this._scrollContainer || !this._ctx) {
-        console.log('control should be initialized first');
+      if (!this._scrollContainer || !this._container || !this._ctx) {
+        console.log('Component should be initialized first');
         return;
         return;
       }
       }
       var changed = false;
       var changed = false;
@@ -3438,6 +3459,61 @@ var Timeline = /*#__PURE__*/function (_TimelineEventsEmitte) {
   }]);
   }]);
   return Timeline;
   return Timeline;
 }(TimelineEventsEmitter);
 }(TimelineEventsEmitter);
+;// CONCATENATED MODULE: ./src/animation-timeline.ts
+// bundle entry point
+
+
+
+
+
+
+
+
+
+// @public styles
+
+
+
+
+
+
+
+
+// @private helper containers.
+
+
+
+
+
+
+
+
+// @private virtual model
+
+
+
+
+
+
+// @public events
+
+
+
+
+
+
+
+// @public enums
+
+
+
+
+
+
+
+
+// @private defaults are exposed:
+
 /******/ 	return __webpack_exports__;
 /******/ 	return __webpack_exports__;
 /******/ })()
 /******/ })()
 ;
 ;

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
lib/animation-timeline.js.map


Різницю між файлами не показано, бо вона завелика
+ 0 - 0
lib/animation-timeline.min.js


+ 1 - 1
lib/settings/timelineOptions.d.ts

@@ -21,7 +21,7 @@ export interface TimelineOptions extends TimelineRanged {
     /**
     /**
      * Number of points that should fit into the one stepPx.
      * Number of points that should fit into the one stepPx.
      */
      */
-    stepVal: number;
+    stepVal?: number;
     stepSmallPx?: number;
     stepSmallPx?: number;
     /**
     /**
      * Snap step in units. from 0 to stepVal
      * Snap step in units. from 0 to stepVal

+ 2 - 2
lib/timeline.d.ts

@@ -8,7 +8,7 @@ import { TimelineElement } from './utils/timelineElement';
 import { TimelineCutBoundsRectResults } from './utils/timelineCutBoundsRectResults';
 import { TimelineCutBoundsRectResults } from './utils/timelineCutBoundsRectResults';
 import { TimelineSelectionResults } from './utils/timelineSelectionResults';
 import { TimelineSelectionResults } from './utils/timelineSelectionResults';
 import { TimelineMouseData } from './utils/timelineMouseData';
 import { TimelineMouseData } from './utils/timelineMouseData';
-import { TimelineElementDragState } from './utils/TimelineElementDragState';
+import { TimelineElementDragState } from './utils/timelineElementDragState';
 import { TimelineDraggableData } from './utils/timelineDraggableData';
 import { TimelineDraggableData } from './utils/timelineDraggableData';
 import { TimelineModelCalcResults } from './utils/timelineModelCalcResults';
 import { TimelineModelCalcResults } from './utils/timelineModelCalcResults';
 import { TimelineCalculatedRow } from './utils/timelineCalculatedRow';
 import { TimelineCalculatedRow } from './utils/timelineCalculatedRow';
@@ -28,7 +28,7 @@ export declare class Timeline extends TimelineEventsEmitter {
      */
      */
     _container: HTMLElement | null;
     _container: HTMLElement | null;
     /**
     /**
-     * Dynamically generated event.
+     * Dynamically generated canvas inside of the container.
      */
      */
     _canvas: HTMLCanvasElement | null;
     _canvas: HTMLCanvasElement | null;
     /**
     /**

+ 0 - 3
lib/utils/TimelineElementDragState.js

@@ -1,3 +0,0 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-//# sourceMappingURL=TimelineElementDragState.js.map

+ 0 - 1
lib/utils/TimelineElementDragState.js.map

@@ -1 +0,0 @@
-{"version":3,"file":"TimelineElementDragState.js","sourceRoot":"","sources":["../../src/utils/TimelineElementDragState.ts"],"names":[],"mappings":""}

+ 0 - 3
lib/utils/timelineCalculated.js

@@ -1,3 +0,0 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-//# sourceMappingURL=timelineCalculated.js.map

+ 0 - 1
lib/utils/timelineCalculated.js.map

@@ -1 +0,0 @@
-{"version":3,"file":"timelineCalculated.js","sourceRoot":"","sources":["../../src/utils/timelineCalculated.ts"],"names":[],"mappings":""}

+ 0 - 3
lib/utils/timelineCalculatedGroup.js

@@ -1,3 +0,0 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-//# sourceMappingURL=timelineCalculatedGroup.js.map

+ 0 - 1
lib/utils/timelineCalculatedGroup.js.map

@@ -1 +0,0 @@
-{"version":3,"file":"timelineCalculatedGroup.js","sourceRoot":"","sources":["../../src/utils/timelineCalculatedGroup.ts"],"names":[],"mappings":""}

+ 0 - 3
lib/utils/timelineCalculatedKeyframe.js

@@ -1,3 +0,0 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-//# sourceMappingURL=timelineCalculatedKeyframe.js.map

+ 0 - 1
lib/utils/timelineCalculatedKeyframe.js.map

@@ -1 +0,0 @@
-{"version":3,"file":"timelineCalculatedKeyframe.js","sourceRoot":"","sources":["../../src/utils/timelineCalculatedKeyframe.ts"],"names":[],"mappings":""}

+ 0 - 3
lib/utils/timelineCalculatedRow.js

@@ -1,3 +0,0 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-//# sourceMappingURL=timelineCalculatedRow.js.map

+ 0 - 1
lib/utils/timelineCalculatedRow.js.map

@@ -1 +0,0 @@
-{"version":3,"file":"timelineCalculatedRow.js","sourceRoot":"","sources":["../../src/utils/timelineCalculatedRow.ts"],"names":[],"mappings":""}

+ 6 - 13
package-lock.json

@@ -1525,12 +1525,6 @@
         "eslint-visitor-keys": "^3.3.0"
         "eslint-visitor-keys": "^3.3.0"
       }
       }
     },
     },
-    "@ungap/promise-all-settled": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
-      "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
-      "dev": true
-    },
     "@webassemblyjs/ast": {
     "@webassemblyjs/ast": {
       "version": "1.11.1",
       "version": "1.11.1",
       "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
       "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
@@ -1955,9 +1949,9 @@
       "dev": true
       "dev": true
     },
     },
     "caniuse-lite": {
     "caniuse-lite": {
-      "version": "1.0.30001419",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001419.tgz",
-      "integrity": "sha512-aFO1r+g6R7TW+PNQxKzjITwLOyDhVRLjW0LcwS/HCZGUUKTGNp9+IwLC4xyDSZBygVL/mxaFR3HIV6wEKQuSzw==",
+      "version": "1.0.30001420",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001420.tgz",
+      "integrity": "sha512-OnyeJ9ascFA9roEj72ok2Ikp7PHJTKubtEJIQ/VK3fdsS50q4KWy+Z5X0A1/GswEItKX0ctAp8n4SYDE7wTu6A==",
       "dev": true
       "dev": true
     },
     },
     "chai": {
     "chai": {
@@ -3192,12 +3186,11 @@
       }
       }
     },
     },
     "mocha": {
     "mocha": {
-      "version": "10.0.0",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz",
-      "integrity": "sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==",
+      "version": "10.1.0",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz",
+      "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==",
       "dev": true,
       "dev": true,
       "requires": {
       "requires": {
-        "@ungap/promise-all-settled": "1.1.2",
         "ansi-colors": "4.1.1",
         "ansi-colors": "4.1.1",
         "browser-stdout": "1.3.1",
         "browser-stdout": "1.3.1",
         "chokidar": "3.5.3",
         "chokidar": "3.5.3",

+ 3 - 2
package.json

@@ -19,7 +19,7 @@
     "eslint": "8.25.0",
     "eslint": "8.25.0",
     "eslint-config-prettier": "8.5.0",
     "eslint-config-prettier": "8.5.0",
     "eslint-plugin-prettier": "4.2.1",
     "eslint-plugin-prettier": "4.2.1",
-    "mocha": "10.0.0",
+    "mocha": "10.1.0",
     "prettier": "2.7.1",
     "prettier": "2.7.1",
     "ts-loader": "9.4.1",
     "ts-loader": "9.4.1",
     "typescript": "4.8.4",
     "typescript": "4.8.4",
@@ -27,7 +27,8 @@
     "webpack-cli": "4.10.0"
     "webpack-cli": "4.10.0"
   },
   },
   "scripts": {
   "scripts": {
-    "test": "echo \"Run tests/unittest.html explicitly to execute tests\" && exit 1",
+    "start": "echo \"Run index.html in your browser. Build after files are changed.\" && exit 1",
+    "test": "echo \"Run tests/unittest.html explicitly to execute tests. Build after files are changed.\" && exit 1",
     "build-ts-def": "tsc -emitDeclarationOnly",
     "build-ts-def": "tsc -emitDeclarationOnly",
     "build": "webpack && npm run build-tests && npm run build-ts-def",
     "build": "webpack && npm run build-tests && npm run build-ts-def",
     "lint": "eslint --ext .ts,.html src --ignore-path .gitignore && prettier \"src/*.ts\" --check --ignore-path .gitignore",
     "lint": "eslint --ext .ts,.html src --ignore-path .gitignore && prettier \"src/*.ts\" --check --ignore-path .gitignore",

+ 1 - 1
src/settings/timelineOptions.ts

@@ -22,7 +22,7 @@ export interface TimelineOptions extends TimelineRanged {
   /**
   /**
    * Number of points that should fit into the one stepPx.
    * Number of points that should fit into the one stepPx.
    */
    */
-  stepVal: number;
+  stepVal?: number;
   stepSmallPx?: number;
   stepSmallPx?: number;
   /**
   /**
    * Snap step in units. from 0 to stepVal
    * Snap step in units. from 0 to stepVal

+ 7 - 7
src/timeline.ts

@@ -19,7 +19,7 @@ import { TimelineElement } from './utils/timelineElement';
 import { TimelineCutBoundsRectResults } from './utils/timelineCutBoundsRectResults';
 import { TimelineCutBoundsRectResults } from './utils/timelineCutBoundsRectResults';
 import { TimelineSelectionResults } from './utils/timelineSelectionResults';
 import { TimelineSelectionResults } from './utils/timelineSelectionResults';
 import { TimelineMouseData } from './utils/timelineMouseData';
 import { TimelineMouseData } from './utils/timelineMouseData';
-import { TimelineElementDragState } from './utils/TimelineElementDragState';
+import { TimelineElementDragState } from './utils/timelineElementDragState';
 import { TimelineDraggableData } from './utils/timelineDraggableData';
 import { TimelineDraggableData } from './utils/timelineDraggableData';
 
 
 // @private virtual model
 // @private virtual model
@@ -54,7 +54,7 @@ export class Timeline extends TimelineEventsEmitter {
    */
    */
   _container: HTMLElement | null = null;
   _container: HTMLElement | null = null;
   /**
   /**
-   * Dynamically generated event.
+   * Dynamically generated canvas inside of the container.
    */
    */
   _canvas: HTMLCanvasElement | null = null;
   _canvas: HTMLCanvasElement | null = null;
   /**
   /**
@@ -1176,7 +1176,7 @@ export class Timeline extends TimelineEventsEmitter {
       min = 0;
       min = 0;
     }
     }
     min *= this._currentZoom || 1;
     min *= this._currentZoom || 1;
-    const steps = this._options.stepVal * this._currentZoom || 1;
+    const steps = (this._options.stepVal || 0) * this._currentZoom || 1;
     const val = min + (px / this._options.stepPx) * steps;
     const val = min + (px / this._options.stepPx) * steps;
     return val;
     return val;
   }
   }
@@ -1205,7 +1205,7 @@ export class Timeline extends TimelineEventsEmitter {
       min = 0;
       min = 0;
     }
     }
     min *= this._currentZoom || 1;
     min *= this._currentZoom || 1;
-    const steps = this._options.stepVal * this._currentZoom || 1;
+    const steps = (this._options.stepVal || 0) * this._currentZoom || 1;
     return (-min + val) * (this._options.stepPx / steps);
     return (-min + val) * (this._options.stepPx / steps);
   }
   }
 
 
@@ -1981,7 +1981,7 @@ export class Timeline extends TimelineEventsEmitter {
   _setOptions(toSet: TimelineOptions): TimelineOptions {
   _setOptions(toSet: TimelineOptions): TimelineOptions {
     this._options = this._mergeOptions(toSet);
     this._options = this._mergeOptions(toSet);
     // Normalize and validate spans per value.
     // Normalize and validate spans per value.
-    this._options.snapStep = TimelineUtils.keepInBounds(this._options.snapStep, 0, this._options.stepVal);
+    this._options.snapStep = TimelineUtils.keepInBounds(this._options.snapStep, 0, this._options.stepVal || 0);
     this._currentZoom = this._setZoom(this._options.zoom, this._options.zoomMin, this._options.zoomMax);
     this._currentZoom = this._setZoom(this._options.zoom, this._options.zoomMin, this._options.zoomMax);
     this._options.min = TimelineUtils.isNumber(this._options.min) ? this._options.min : 0;
     this._options.min = TimelineUtils.isNumber(this._options.min) ? this._options.min : 0;
     this._options.max = TimelineUtils.isNumber(this._options.max) ? this._options.max : Number.MAX_VALUE;
     this._options.max = TimelineUtils.isNumber(this._options.max) ? this._options.max : Number.MAX_VALUE;
@@ -2046,8 +2046,8 @@ export class Timeline extends TimelineEventsEmitter {
    * Apply container div size to the container on changes detected.
    * Apply container div size to the container on changes detected.
    */
    */
   _updateCanvasScale(): boolean {
   _updateCanvasScale(): boolean {
-    if (!this._scrollContainer || !this._ctx) {
-      console.log('control should be initialized first');
+    if (!this._scrollContainer || !this._container || !this._ctx) {
+      console.log('Component should be initialized first');
       return;
       return;
     }
     }
     let changed = false;
     let changed = false;

+ 24 - 0
tests/README.md

@@ -0,0 +1,24 @@
+# Run Tests
+
+Build
+
+```CMD
+npm run build
+```
+
+open unittests.html in any browser.
+
+Note: js folder is autogenerated. To add a new test *.ts files should be used.
+
+## Debug
+
+VSCode is used as IDE and configuration is included to the project sources.
+
+To debug project you should run command once files are changed:
+
+```cmd
+npm run build
+```
+
+Then navigate to the debug window and click 'Launch Test File'.
+Put breakpoint in any typescript tests file to and refresh page or test to run debugger.

Різницю між файлами не показано, бо вона завелика
+ 7 - 5
tests/js/settingsTests.test.js


Різницю між файлами не показано, бо вона завелика
+ 4 - 2
tests/js/styleTests.test.js


Різницю між файлами не показано, бо вона завелика
+ 210 - 97
tests/js/timelineTests.test.js


+ 5 - 5
tests/settingsTests.test.ts

@@ -23,7 +23,7 @@ describe('_mergeOptions', function () {
     const merged = timeline._mergeOptions(options);
     const merged = timeline._mergeOptions(options);
     chai.expect(merged.id).equal(options.id);
     chai.expect(merged.id).equal(options.id);
     chai.expect(!!merged.rowsStyle).equal(true, 'Row style cannot be null');
     chai.expect(!!merged.rowsStyle).equal(true, 'Row style cannot be null');
-    chai.expect(!!merged.rowsStyle.keyframesStyle).equal(true, 'Keyframes style cannot be null');
+    chai.expect(!!merged.rowsStyle?.keyframesStyle).equal(true, 'Keyframes style cannot be null');
   });
   });
 
 
   it('Deep styles are merged', function () {
   it('Deep styles are merged', function () {
@@ -44,11 +44,11 @@ describe('_mergeOptions', function () {
     const merged = timeline._mergeOptions(options);
     const merged = timeline._mergeOptions(options);
     chai.expect(merged.id).equal('new id');
     chai.expect(merged.id).equal('new id');
     chai.expect(merged.headerHeight).equal(44);
     chai.expect(merged.headerHeight).equal(44);
-    chai.expect(merged.rowsStyle.height).equal(100);
-    chai.expect(merged.rowsStyle.keyframesStyle.hidden).equal(true);
-    chai.expect(merged.rowsStyle.keyframesStyle.draggable).equal(false);
+    chai.expect(merged.rowsStyle?.height).equal(100);
+    chai.expect(merged.rowsStyle?.keyframesStyle?.hidden).equal(true);
+    chai.expect(merged.rowsStyle?.keyframesStyle?.draggable).equal(false);
     const defOptions = defaultTimelineOptions as TimelineOptions;
     const defOptions = defaultTimelineOptions as TimelineOptions;
-    chai.expect(merged.rowsStyle.keyframesStyle.shape, defOptions.rowsStyle.keyframesStyle.shape);
+    chai.expect(merged.rowsStyle?.keyframesStyle?.shape, defOptions.rowsStyle?.keyframesStyle?.shape);
   });
   });
   it('Original options are not affected', function () {
   it('Original options are not affected', function () {
     const timeline = new Timeline();
     const timeline = new Timeline();

+ 2 - 2
tests/styleTests.test.ts

@@ -141,7 +141,7 @@ describe('TimelineStyleUtils', function () {
       } as TimelineOptions;
       } as TimelineOptions;
 
 
       const rowsStyle = {} as TimelineRowStyle;
       const rowsStyle = {} as TimelineRowStyle;
-      chai.expect(TimelineStyleUtils.getRowHeight(rowsStyle, globalStyle)).equal(globalStyle.rowsStyle.height);
+      chai.expect(TimelineStyleUtils.getRowHeight(rowsStyle, globalStyle)).equal(globalStyle.rowsStyle?.height);
     });
     });
     it('Margin bottom is taken from global settings', function () {
     it('Margin bottom is taken from global settings', function () {
       const globalStyle = {
       const globalStyle = {
@@ -153,7 +153,7 @@ describe('TimelineStyleUtils', function () {
       } as TimelineOptions;
       } as TimelineOptions;
 
 
       const rowsStyle = {} as TimelineRowStyle;
       const rowsStyle = {} as TimelineRowStyle;
-      chai.expect(TimelineStyleUtils.getRowMarginBottom(rowsStyle, globalStyle)).equal(globalStyle.rowsStyle.marginBottom);
+      chai.expect(TimelineStyleUtils.getRowMarginBottom(rowsStyle, globalStyle)).equal(globalStyle.rowsStyle?.marginBottom);
     });
     });
     it('Margin bottom is taken from row settings', function () {
     it('Margin bottom is taken from row settings', function () {
       const globalStyle = {
       const globalStyle = {

+ 188 - 96
tests/timelineTests.test.ts

@@ -219,10 +219,14 @@ describe('Timeline', function () {
       chai.expect(element.selectionChanged).equal(true);
       chai.expect(element.selectionChanged).equal(true);
       let changed = 0;
       let changed = 0;
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
-        row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-          chai.expect(keyframe.selected).equal(true);
-          changed++;
-        });
+        const len = row?.keyframes?.length || 0;
+        chai.expect(len > 0).equal(true);
+        if (row && row.keyframes) {
+          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+            chai.expect(keyframe.selected).equal(true);
+            changed++;
+          });
+        }
       });
       });
 
 
       chai.expect(element.selected.length).equal(changed);
       chai.expect(element.selected.length).equal(changed);
@@ -235,14 +239,18 @@ describe('Timeline', function () {
       let changed = 0;
       let changed = 0;
       let selectable = 0;
       let selectable = 0;
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
-        row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-          keyframe.selected = false;
-          keyframe.selectable = changed % 2 === 0;
-          if (keyframe.selectable) {
-            selectable++;
-          }
-          changed++;
-        });
+        const len = row?.keyframes?.length || 0;
+        chai.expect(len > 0).equal(true);
+        if (row.keyframes) {
+          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+            keyframe.selected = false;
+            keyframe.selectable = changed % 2 === 0;
+            if (keyframe.selectable) {
+              selectable++;
+            }
+            changed++;
+          });
+        }
       });
       });
       const selectionResults = timeline.select(element);
       const selectionResults = timeline.select(element);
       chai.expect(selectionResults.changed.length).equal(selectable);
       chai.expect(selectionResults.changed.length).equal(selectable);
@@ -252,19 +260,27 @@ describe('Timeline', function () {
       const timeline = new Timeline();
       const timeline = new Timeline();
       timeline._model = model;
       timeline._model = model;
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
-        row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-          keyframe.selectable = true;
-          keyframe.selected = true;
-        });
+        const len = row?.keyframes?.length || 0;
+        chai.expect(len > 0).equal(true);
+        if (row.keyframes) {
+          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+            keyframe.selectable = true;
+            keyframe.selected = true;
+          });
+        }
       });
       });
       // deselect all
       // deselect all
       const element = timeline.deselectAll();
       const element = timeline.deselectAll();
       chai.expect(element.selectionChanged).equal(true);
       chai.expect(element.selectionChanged).equal(true);
       chai.expect(element.selected.length).equal(0);
       chai.expect(element.selected.length).equal(0);
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
-        row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-          chai.expect(keyframe.selected).equal(false);
-        });
+        const len = row?.keyframes?.length || 0;
+        chai.expect(len > 0).equal(true);
+        if (row.keyframes) {
+          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+            chai.expect(keyframe.selected).equal(false);
+          });
+        }
       });
       });
     });
     });
     it('Select one and deselect other all', function () {
     it('Select one and deselect other all', function () {
@@ -273,29 +289,42 @@ describe('Timeline', function () {
       let expectedChanged = 0;
       let expectedChanged = 0;
       // Select all
       // Select all
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
-        row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-          keyframe.selectable = true;
-          keyframe.selected = true;
-          expectedChanged++;
-        });
+        const len = row?.keyframes?.length || 0;
+        chai.expect(len > 0).equal(true);
+        if (row.keyframes) {
+          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+            keyframe.selectable = true;
+            keyframe.selected = true;
+            expectedChanged++;
+          });
+        }
       });
       });
 
 
       // select one will deselect other
       // select one will deselect other
-      const toSelect = model.rows[1].keyframes[0];
-
+      const rowToSelect = model.rows[1];
+      chai.expect(!!rowToSelect.keyframes).equal(true);
+      if (!rowToSelect.keyframes) {
+        return;
+      }
+      // toggle selection
+      const toSelect = rowToSelect.keyframes[0];
       const element = timeline.select(toSelect);
       const element = timeline.select(toSelect);
       chai.expect(element.selectionChanged).equal(true);
       chai.expect(element.selectionChanged).equal(true);
       chai.expect(element.selected.length).equal(1);
       chai.expect(element.selected.length).equal(1);
       chai.expect(element.changed.length).equal(expectedChanged - 1);
       chai.expect(element.changed.length).equal(expectedChanged - 1);
 
 
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
-        row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-          if (toSelect == keyframe) {
-            chai.expect(keyframe.selected).equal(true);
-          } else {
-            chai.expect(keyframe.selected).equal(false);
-          }
-        });
+        const len = row?.keyframes?.length || 0;
+        chai.expect(len > 0).equal(true);
+        if (row.keyframes) {
+          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+            if (toSelect == keyframe) {
+              chai.expect(keyframe.selected).equal(true);
+            } else {
+              chai.expect(keyframe.selected).equal(false);
+            }
+          });
+        }
       });
       });
     });
     });
     it('Revert selection (Toggle)', function () {
     it('Revert selection (Toggle)', function () {
@@ -304,15 +333,25 @@ describe('Timeline', function () {
       let totalKeyframes = 0;
       let totalKeyframes = 0;
       // Select all
       // Select all
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
-        row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-          keyframe.selectable = true;
-          keyframe.selected = true;
-          totalKeyframes++;
-        });
+        const len = row?.keyframes?.length || 0;
+        chai.expect(len > 0).equal(true);
+        if (row.keyframes) {
+          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+            keyframe.selectable = true;
+            keyframe.selected = true;
+            totalKeyframes++;
+          });
+        }
       });
       });
 
 
+      const rowToSelect = model.rows[1];
+      chai.expect(!!rowToSelect.keyframes).equal(true);
+      if (!rowToSelect.keyframes) {
+        return;
+      }
       // toggle selection
       // toggle selection
-      const toSelect = model.rows[1].keyframes[0];
+      const toSelect = rowToSelect.keyframes[0];
+
       // item is selected, should be reverted
       // item is selected, should be reverted
       const element = timeline.select(toSelect, TimelineSelectionMode.Revert);
       const element = timeline.select(toSelect, TimelineSelectionMode.Revert);
       chai.expect(element.selectionChanged).equal(true);
       chai.expect(element.selectionChanged).equal(true);
@@ -320,13 +359,16 @@ describe('Timeline', function () {
       chai.expect(element.changed.length).equal(1);
       chai.expect(element.changed.length).equal(1);
 
 
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
-        row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-          if (toSelect == keyframe) {
-            chai.expect(keyframe.selected).equal(false);
-          } else {
-            chai.expect(keyframe.selected).equal(true);
-          }
-        });
+        chai.expect((row.keyframes?.length || 0) > 0).equal(true);
+        if (row.keyframes) {
+          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+            if (toSelect == keyframe) {
+              chai.expect(keyframe.selected).equal(false);
+            } else {
+              chai.expect(keyframe.selected).equal(true);
+            }
+          });
+        }
       });
       });
     });
     });
     it('Select full row', function () {
     it('Select full row', function () {
@@ -334,14 +376,22 @@ describe('Timeline', function () {
       timeline._model = model;
       timeline._model = model;
       // Deselect all
       // Deselect all
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
-        row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-          keyframe.selectable = true;
-          keyframe.selected = false;
-        });
+        const len = row?.keyframes?.length || 0;
+        chai.expect(len > 0).equal(true);
+        if (row.keyframes) {
+          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+            keyframe.selectable = true;
+            keyframe.selected = false;
+          });
+        }
       });
       });
 
 
       // select one will deselect other
       // select one will deselect other
       const rowToSelect = model.rows[1];
       const rowToSelect = model.rows[1];
+      chai.expect(!!rowToSelect.keyframes).equal(true);
+      if (!rowToSelect.keyframes) {
+        return;
+      }
       const element = timeline.select(rowToSelect.keyframes);
       const element = timeline.select(rowToSelect.keyframes);
       chai.expect(element.selectionChanged).equal(true);
       chai.expect(element.selectionChanged).equal(true);
       chai.expect(element.selected.length).equal(rowToSelect.keyframes.length);
       chai.expect(element.selected.length).equal(rowToSelect.keyframes.length);
@@ -349,13 +399,21 @@ describe('Timeline', function () {
 
 
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
         if (rowToSelect === row) {
         if (rowToSelect === row) {
-          rowToSelect.keyframes.forEach((keyframe: TimelineKeyframe) => {
-            chai.expect(keyframe.selected).equal(true);
-          });
+          const len = row?.keyframes?.length || 0;
+          chai.expect(len > 0).equal(true);
+          if (rowToSelect.keyframes) {
+            rowToSelect.keyframes.forEach((keyframe: TimelineKeyframe) => {
+              chai.expect(keyframe.selected).equal(true);
+            });
+          }
         } else {
         } else {
-          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-            chai.expect(keyframe.selected).equal(false);
-          });
+          const len = row?.keyframes?.length || 0;
+          chai.expect(len > 0).equal(true);
+          if (row.keyframes) {
+            row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+              chai.expect(keyframe.selected).equal(false);
+            });
+          }
         }
         }
       });
       });
     });
     });
@@ -364,34 +422,48 @@ describe('Timeline', function () {
       timeline._model = model;
       timeline._model = model;
       // Deselect all
       // Deselect all
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
-        row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-          keyframe.selected = false;
-        });
+        const len = row?.keyframes?.length || 0;
+        chai.expect(len > 0).equal(true);
+        if (row.keyframes) {
+          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+            keyframe.selected = false;
+          });
+        }
       });
       });
 
 
       // select one row (array of the keyframes)
       // select one row (array of the keyframes)
       const rowToSelect = model.rows[1];
       const rowToSelect = model.rows[1];
+      chai.expect(!!rowToSelect.keyframes).equal(true);
+      if (!rowToSelect.keyframes) {
+        return;
+      }
       let results = timeline.select(rowToSelect.keyframes);
       let results = timeline.select(rowToSelect.keyframes);
       chai.expect(results.selectionChanged).equal(true);
       chai.expect(results.selectionChanged).equal(true);
-      chai.expect(results.selected.length).equal(rowToSelect.keyframes.length);
-      chai.expect(results.changed.length).equal(rowToSelect.keyframes.length);
+      chai.expect(results.selected.length).equal(rowToSelect.keyframes?.length);
+      chai.expect(results.changed.length).equal(rowToSelect.keyframes?.length);
 
 
       // (array of the keyframes)
       // (array of the keyframes)
       const rowToSelect2 = model.rows[2];
       const rowToSelect2 = model.rows[2];
-      results = timeline.select(rowToSelect2.keyframes, TimelineSelectionMode.Append);
+      results = timeline.select(rowToSelect2?.keyframes || [], TimelineSelectionMode.Append);
       chai.expect(results.selectionChanged).equal(true);
       chai.expect(results.selectionChanged).equal(true);
-      chai.expect(results.selected.length).equal(rowToSelect.keyframes.length + rowToSelect2.keyframes.length);
-      chai.expect(results.changed.length).equal(rowToSelect2.keyframes.length);
+      chai.expect(results.selected.length).equal((rowToSelect?.keyframes?.length || 0) + (rowToSelect2?.keyframes?.length || 0));
+      chai.expect(results.changed.length).equal(rowToSelect2?.keyframes?.length);
 
 
       model.rows.forEach((row: TimelineRow) => {
       model.rows.forEach((row: TimelineRow) => {
         if (rowToSelect === row || rowToSelect2 === row) {
         if (rowToSelect === row || rowToSelect2 === row) {
-          rowToSelect.keyframes.forEach((keyframe: TimelineKeyframe) => {
-            chai.expect(keyframe.selected).equal(true);
-          });
+          chai.expect((rowToSelect.keyframes?.length || 0) > 0).equal(true);
+          if (rowToSelect.keyframes) {
+            rowToSelect.keyframes.forEach((keyframe: TimelineKeyframe) => {
+              chai.expect(keyframe.selected).equal(true);
+            });
+          }
         } else {
         } else {
-          row.keyframes.forEach((keyframe: TimelineKeyframe) => {
-            chai.expect(keyframe.selected).equal(false);
-          });
+          chai.expect((row.keyframes?.length || 0) > 0).equal(true);
+          if (row.keyframes) {
+            row.keyframes.forEach((keyframe: TimelineKeyframe) => {
+              chai.expect(keyframe.selected).equal(false);
+            });
+          }
         }
         }
       });
       });
     });
     });
@@ -559,20 +631,30 @@ describe('Timeline', function () {
       } as TimelineModel;
       } as TimelineModel;
       timeline._model = model;
       timeline._model = model;
       const move = -50;
       const move = -50;
+      const row = model.rows[0];
+      chai.expect(!!row).equal(true);
+      if (!row) {
+        return;
+      }
+      const keyframes = row.keyframes;
+      chai.expect(!!keyframes).equal(true);
+      if (!keyframes) {
+        return;
+      }
       const movedOffset = timeline._moveElements(move, [
       const movedOffset = timeline._moveElements(move, [
         {
         {
-          keyframe: model.rows[0].keyframes[1],
-          row: model.rows[0],
+          keyframe: keyframes[1],
+          row: row,
         } as TimelineElementDragState,
         } as TimelineElementDragState,
         {
         {
-          keyframe: model.rows[0].keyframes[0],
-          row: model.rows[0],
+          keyframe: keyframes[0],
+          row: row,
         } as TimelineElementDragState,
         } as TimelineElementDragState,
       ]);
       ]);
 
 
       chai.expect(movedOffset).equal(move);
       chai.expect(movedOffset).equal(move);
-      chai.expect(model.rows[0].keyframes[0].val).equal(item1 + move);
-      chai.expect(model.rows[0].keyframes[1].val).equal(item2 + move);
+      chai.expect(keyframes[0]?.val).equal(item1 + move);
+      chai.expect(keyframes[1]?.val).equal(item2 + move);
     });
     });
     it('move right', function () {
     it('move right', function () {
       const timeline = new Timeline();
       const timeline = new Timeline();
@@ -583,20 +665,30 @@ describe('Timeline', function () {
       } as TimelineModel;
       } as TimelineModel;
       timeline._model = model;
       timeline._model = model;
       const move = 100;
       const move = 100;
+      const row = model.rows[0];
+      chai.expect(!!row).equal(true);
+      if (!row) {
+        return;
+      }
+      const keyframes = row.keyframes;
+      chai.expect(!!keyframes).equal(true);
+      if (!keyframes) {
+        return;
+      }
       const movedOffset = timeline._moveElements(move, [
       const movedOffset = timeline._moveElements(move, [
         {
         {
-          keyframe: model.rows[0].keyframes[1],
-          row: model.rows[0],
+          keyframe: keyframes[1],
+          row: row,
         } as TimelineElementDragState,
         } as TimelineElementDragState,
         {
         {
-          keyframe: model.rows[0].keyframes[0],
-          row: model.rows[0],
+          keyframe: keyframes[0],
+          row: row,
         } as TimelineElementDragState,
         } as TimelineElementDragState,
       ]);
       ]);
 
 
       chai.expect(movedOffset).equal(move);
       chai.expect(movedOffset).equal(move);
-      chai.expect(model.rows[0].keyframes[0].val).equal(item1 + move);
-      chai.expect(model.rows[0].keyframes[1].val).equal(item2 + move);
+      chai.expect(keyframes[0]?.val).equal(item1 + move);
+      chai.expect(keyframes[1]?.val).equal(item2 + move);
     });
     });
     it('move left limited by min', function () {
     it('move left limited by min', function () {
       const timeline = new Timeline();
       const timeline = new Timeline();
@@ -615,8 +707,8 @@ describe('Timeline', function () {
       const movedOffset = timeline._moveElements(move, elementsToMove);
       const movedOffset = timeline._moveElements(move, elementsToMove);
 
 
       chai.expect(movedOffset).equal(move / 2);
       chai.expect(movedOffset).equal(move / 2);
-      chai.expect(elementsToMove[0].keyframe.val).equal(0);
-      chai.expect(elementsToMove[1].keyframe.val).equal(25);
+      chai.expect(elementsToMove[0].keyframe?.val).equal(0);
+      chai.expect(elementsToMove[1].keyframe?.val).equal(25);
     });
     });
 
 
     it('move right limited by max', function () {
     it('move right limited by max', function () {
@@ -636,8 +728,8 @@ describe('Timeline', function () {
       const movedOffset = timeline._moveElements(move, elementsToMove);
       const movedOffset = timeline._moveElements(move, elementsToMove);
 
 
       chai.expect(movedOffset).equal(move / 2);
       chai.expect(movedOffset).equal(move / 2);
-      chai.expect(elementsToMove[0].keyframe.val).equal(item1 + 50);
-      chai.expect(elementsToMove[1].keyframe.val).equal(item2 + 50);
+      chai.expect(elementsToMove[0].keyframe?.val).equal(item1 + 50);
+      chai.expect(elementsToMove[1].keyframe?.val).equal(item2 + 50);
     });
     });
     it('move right limited by max negative', function () {
     it('move right limited by max negative', function () {
       const timeline = new Timeline();
       const timeline = new Timeline();
@@ -656,8 +748,8 @@ describe('Timeline', function () {
       const movedOffset = timeline._moveElements(move, elementsToMove);
       const movedOffset = timeline._moveElements(move, elementsToMove);
 
 
       chai.expect(movedOffset).equal(25);
       chai.expect(movedOffset).equal(25);
-      chai.expect(elementsToMove[0].keyframe.val).equal(item1 + 25);
-      chai.expect(elementsToMove[1].keyframe.val).equal(item2 + 25);
+      chai.expect(elementsToMove[0].keyframe?.val).equal(item1 + 25);
+      chai.expect(elementsToMove[1].keyframe?.val).equal(item2 + 25);
     });
     });
     it('move right limited by max negative when other row out of the bounds', function () {
     it('move right limited by max negative when other row out of the bounds', function () {
       const timeline = new Timeline();
       const timeline = new Timeline();
@@ -686,10 +778,10 @@ describe('Timeline', function () {
       const movedOffset = timeline._moveElements(move, elementsToMove);
       const movedOffset = timeline._moveElements(move, elementsToMove);
       const moved = move / 2;
       const moved = move / 2;
       chai.expect(movedOffset).equal(moved);
       chai.expect(movedOffset).equal(moved);
-      chai.expect(elementsToMove[0].keyframe.val).equal(100 + moved);
-      chai.expect(elementsToMove[1].keyframe.val).equal(400 + moved);
-      chai.expect(elementsToMove[2].keyframe.val).equal(200 + moved);
-      chai.expect(elementsToMove[3].keyframe.val).equal(300 + moved);
+      chai.expect(elementsToMove[0].keyframe?.val).equal(100 + moved);
+      chai.expect(elementsToMove[1].keyframe?.val).equal(400 + moved);
+      chai.expect(elementsToMove[2].keyframe?.val).equal(200 + moved);
+      chai.expect(elementsToMove[3].keyframe?.val).equal(300 + moved);
     });
     });
     it('move left limited by min negative', function () {
     it('move left limited by min negative', function () {
       const timeline = new Timeline();
       const timeline = new Timeline();
@@ -708,8 +800,8 @@ describe('Timeline', function () {
       const movedOffset = timeline._moveElements(move, elementsToMove);
       const movedOffset = timeline._moveElements(move, elementsToMove);
 
 
       chai.expect(movedOffset).equal(move / 2);
       chai.expect(movedOffset).equal(move / 2);
-      chai.expect(elementsToMove[0].keyframe.val).equal(item1 - 50);
-      chai.expect(elementsToMove[1].keyframe.val).equal(item2 - 50);
+      chai.expect(elementsToMove[0].keyframe?.val).equal(item1 - 50);
+      chai.expect(elementsToMove[1].keyframe?.val).equal(item2 - 50);
     });
     });
     it('move left only one keyframe is limited', function () {
     it('move left only one keyframe is limited', function () {
       const timeline = new Timeline();
       const timeline = new Timeline();
@@ -728,8 +820,8 @@ describe('Timeline', function () {
       const movedOffset = timeline._moveElements(move, elementsToMove);
       const movedOffset = timeline._moveElements(move, elementsToMove);
 
 
       chai.expect(movedOffset).equal(move / 2);
       chai.expect(movedOffset).equal(move / 2);
-      chai.expect(elementsToMove[0].keyframe.val).equal(25 + 50);
-      chai.expect(elementsToMove[1].keyframe.val).equal(50 + 50);
+      chai.expect(elementsToMove[0].keyframe?.val).equal(25 + 50);
+      chai.expect(elementsToMove[1].keyframe?.val).equal(50 + 50);
     });
     });
   });
   });
 });
 });

+ 1 - 2
tests/unittests.html

@@ -12,7 +12,7 @@
     <div>
     <div>
         <a href="../index.html">Back</a>
         <a href="../index.html">Back</a>
         <p>
         <p>
-            Build tests first and then tests can be executed here.
+            Build tests using the npm command first and then tests can be executed here.
             Results:
             Results:
         </p>
         </p>
     </div>
     </div>
@@ -25,7 +25,6 @@
             window.exports = {};
             window.exports = {};
         }
         }
     </script>
     </script>
-    <script src="./js/asserts.js"></script>
     <script>if (!window.require) {
     <script>if (!window.require) {
             window.require = function (moduleName) {
             window.require = function (moduleName) {
                 if (moduleName.indexOf('assert') > 0) {
                 if (moduleName.indexOf('assert') > 0) {

+ 2 - 2
webpack.config.js

@@ -12,7 +12,7 @@ module.exports = [
     mode: 'production',
     mode: 'production',
     name: 'minimized-prod',
     name: 'minimized-prod',
     entry: {
     entry: {
-      'animation-timeline': './src/timeline.ts',
+      'animation-timeline': './src/animation-timeline.ts',
     },
     },
     module: {
     module: {
       rules: [
       rules: [
@@ -39,7 +39,7 @@ module.exports = [
     mode: 'production',
     mode: 'production',
     name: 'unminimized',
     name: 'unminimized',
     entry: {
     entry: {
-      'animation-timeline': './src/timeline.ts',
+      'animation-timeline': './src/animation-timeline.ts',
     },
     },
     module: {
     module: {
       rules: [
       rules: [

Деякі файли не було показано, через те що забагато файлів було змінено