From 2a313f28fcb8675223708b0657de7517a3281095 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Wed, 17 Apr 2024 12:27:21 -0400
Subject: restoring eslint - updates not complete yet

---
 src/client/views/ContextMenu.tsx | 30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

(limited to 'src/client/views/ContextMenu.tsx')

diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index ca877b93e..18b433a77 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -1,16 +1,16 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { StrCast } from '../../fields/Types';
-import { SettingsManager } from '../util/SettingsManager';
+import { DivHeight, DivWidth } from '../../ClientUtils';
+import { SnappingManager } from '../util/SnappingManager';
 import './ContextMenu.scss';
 import { ContextMenuItem, ContextMenuProps, OriginalMenuProps } from './ContextMenuItem';
 import { ObservableReactComponent } from './ObservableReactComponent';
-import { DivHeight, DivWidth } from '../../Utils';
 
 @observer
 export class ContextMenu extends ObservableReactComponent<{}> {
+    // eslint-disable-next-line no-use-before-define
     static Instance: ContextMenu;
 
     private _ignoreUp = false;
@@ -124,8 +124,8 @@ export class ContextMenu extends ObservableReactComponent<{}> {
 
     @action
     displayMenu = (x: number, y: number, initSearch = '', showSearch = false, onDisplay?: () => void) => {
-        //maxX and maxY will change if the UI/font size changes, but will work for any amount
-        //of items added to the menu
+        // maxX and maxY will change if the UI/font size changes, but will work for any amount
+        // of items added to the menu
 
         this._showSearch = showSearch;
         this._pageX = x;
@@ -192,7 +192,7 @@ export class ContextMenu extends ObservableReactComponent<{}> {
                     key={index + value.join(' -> ')}
                     className="contextMenu-group"
                     style={{
-                        background: StrCast(SettingsManager.userVariantColor),
+                        background: SnappingManager.userVariantColor,
                     }}>
                     <div className="contextMenu-description">{value.join(' -> ')}</div>
                 </div>
@@ -224,11 +224,11 @@ export class ContextMenu extends ObservableReactComponent<{}> {
                     display: this._display ? '' : 'none',
                     left: this.pageX,
                     ...(this._yRelativeToTop ? { top: Math.max(0, this.pageY) } : { bottom: this.pageY }),
-                    background: SettingsManager.userBackgroundColor,
-                    color: SettingsManager.userColor,
+                    background: SnappingManager.userBackgroundColor,
+                    color: SnappingManager.userColor,
                 }}>
                 {!this.itemsNeedSearch ? null : (
-                    <span className={'search-icon'}>
+                    <span className="search-icon">
                         <span className="icon-background">
                             <FontAwesomeIcon icon="search" size="lg" />
                         </span>
@@ -267,7 +267,7 @@ export class ContextMenu extends ObservableReactComponent<{}> {
             if (item) {
                 item.event({ x: this.pageX, y: this.pageY });
             } else {
-                //if (this._searchString.startsWith(this._defaultPrefix)) {
+                // if (this._searchString.startsWith(this._defaultPrefix)) {
                 this._defaultItem?.(this._searchString.substring(this._defaultPrefix.length));
             }
             this.closeMenu();
@@ -281,12 +281,10 @@ export class ContextMenu extends ObservableReactComponent<{}> {
         this._searchString = e.target.value;
         if (!this._searchString) {
             this._selectedIndex = -1;
+        } else if (this._selectedIndex === -1) {
+            this._selectedIndex = 0;
         } else {
-            if (this._selectedIndex === -1) {
-                this._selectedIndex = 0;
-            } else {
-                this._selectedIndex = Math.min(this.flatItems.length - 1, this._selectedIndex);
-            }
+            this._selectedIndex = Math.min(this.flatItems.length - 1, this._selectedIndex);
         }
     };
 }
-- 
cgit v1.2.3-70-g09d2


From 9d69ab27de83ead3e499edc9028ba85749407a1e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 23 Apr 2024 18:35:59 -0400
Subject: more lint cleanup

---
 src/client/goldenLayout.d.ts                       |   3 +-
 src/client/views/AntimodeMenu.tsx                  |  18 +-
 src/client/views/ComponentDecorations.tsx          |   3 +-
 src/client/views/ContextMenu.tsx                   |  19 +-
 src/client/views/DocComponent.tsx                  |  28 +--
 src/client/views/DocumentButtonBar.tsx             |   2 +-
 src/client/views/DocumentDecorations.tsx           |  24 +--
 src/client/views/FilterPanel.tsx                   |   2 +-
 src/client/views/InkControlPtHandles.tsx           |  33 +--
 src/client/views/InkStrokeProperties.ts            |  77 ++++---
 src/client/views/InkTangentHandles.tsx             |  13 +-
 src/client/views/InkingStroke.tsx                  |   6 +-
 src/client/views/KeyphraseQueryView.tsx            |  26 +--
 src/client/views/LightboxView.tsx                  |  82 ++++---
 src/client/views/Main.tsx                          |   4 +-
 src/client/views/MainView.tsx                      |   5 +-
 src/client/views/MainViewModal.tsx                 |   5 +-
 src/client/views/MarqueeAnnotator.tsx              |  32 +--
 src/client/views/PreviewCursor.tsx                 |  16 +-
 .../views/PropertiesDocBacklinksSelector.tsx       |   1 +
 src/client/views/PropertiesDocContextSelector.tsx  |  11 +-
 src/client/views/PropertiesSection.tsx             |  59 +++--
 src/client/views/ScriptBox.tsx                     |  39 +++-
 src/debug/Repl.tsx                                 |  18 +-
 src/debug/Test.tsx                                 |   3 +-
 src/debug/Viewer.tsx                               |  40 ++--
 src/extensions/ArrayExtensions.ts                  |  37 ----
 src/extensions/Extensions.ts                       |   9 +
 src/extensions/ExtensionsTypings.ts                |   9 +
 src/extensions/Extensions_Array.ts                 |  37 ++++
 src/extensions/Extensions_String.ts                |  16 ++
 src/extensions/General/Extensions.ts               |   9 -
 src/extensions/General/ExtensionsTypings.ts        |   8 -
 src/extensions/StringExtensions.ts                 |  17 --
 src/fields/DateField.ts                            |   4 +-
 src/fields/HtmlField.ts                            |   4 +-
 src/fields/IconField.ts                            |   2 +-
 src/fields/List.ts                                 |   4 +-
 src/fields/Schema.ts                               |  17 +-
 src/fields/URLField.ts                             |  18 +-
 src/mobile/MobileMain.tsx                          |   2 +-
 src/pen-gestures/ndollar.ts                        | 238 +++++++++++----------
 42 files changed, 547 insertions(+), 453 deletions(-)
 delete mode 100644 src/extensions/ArrayExtensions.ts
 create mode 100644 src/extensions/Extensions.ts
 create mode 100644 src/extensions/ExtensionsTypings.ts
 create mode 100644 src/extensions/Extensions_Array.ts
 create mode 100644 src/extensions/Extensions_String.ts
 delete mode 100644 src/extensions/General/Extensions.ts
 delete mode 100644 src/extensions/General/ExtensionsTypings.ts
 delete mode 100644 src/extensions/StringExtensions.ts

(limited to 'src/client/views/ContextMenu.tsx')

diff --git a/src/client/goldenLayout.d.ts b/src/client/goldenLayout.d.ts
index b50240563..26e8ba356 100644
--- a/src/client/goldenLayout.d.ts
+++ b/src/client/goldenLayout.d.ts
@@ -1,3 +1,2 @@
-
 declare const GoldenLayout: any;
-export = GoldenLayout;
\ No newline at end of file
+export = GoldenLayout;
diff --git a/src/client/views/AntimodeMenu.tsx b/src/client/views/AntimodeMenu.tsx
index db7e64deb..b1eb730fa 100644
--- a/src/client/views/AntimodeMenu.tsx
+++ b/src/client/views/AntimodeMenu.tsx
@@ -3,6 +3,7 @@ import * as React from 'react';
 import { SettingsManager } from '../util/SettingsManager';
 import './AntimodeMenu.scss';
 import { ObservableReactComponent } from './ObservableReactComponent';
+
 export interface AntimodeMenuProps {}
 
 /**
@@ -76,7 +77,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab
     };
 
     @action
-    protected pointerLeave = (e: React.PointerEvent) => {
+    protected pointerLeave = () => {
         if (!this.Pinned && this._canFade) {
             this._transitionProperty = 'opacity';
             this._transitionDuration = '0.5s';
@@ -87,7 +88,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab
     };
 
     @action
-    protected pointerEntered = (e: React.PointerEvent) => {
+    protected pointerEntered = () => {
         this._transitionProperty = 'opacity';
         this._transitionDuration = '0.1s';
         this._transitionDelay = '';
@@ -95,8 +96,10 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab
     };
 
     @action
-    protected togglePin = (e: React.MouseEvent) => {
-        runInAction(() => (this.Pinned = !this.Pinned));
+    protected togglePin = () => {
+        runInAction(() => {
+            this.Pinned = !this.Pinned;
+        });
     };
 
     protected dragStart = (e: React.PointerEvent) => {
@@ -114,8 +117,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab
 
     @action
     protected dragging = (e: PointerEvent) => {
-        const width = this._mainCont.current!.getBoundingClientRect().width;
-        const height = this._mainCont.current!.getBoundingClientRect().height;
+        const { width, height } = this._mainCont.current!.getBoundingClientRect();
 
         const left = e.pageX - this._offsetX;
         const top = e.pageY - this._offsetY;
@@ -139,9 +141,7 @@ export abstract class AntimodeMenu<T extends AntimodeMenuProps> extends Observab
         e.preventDefault();
     };
 
-    protected getDragger = () => {
-        return <div className="antimodeMenu-dragger" key="dragger" onPointerDown={this.dragStart} style={{ width: '20px' }} />;
-    };
+    protected getDragger = () => <div className="antimodeMenu-dragger" key="dragger" onPointerDown={this.dragStart} style={{ width: '20px' }} />;
 
     protected getElement(buttons: JSX.Element, expanded: boolean = false) {
         const containerClass = expanded ? 'antimodeMenu-cont expanded' : 'antimodeMenu-cont';
diff --git a/src/client/views/ComponentDecorations.tsx b/src/client/views/ComponentDecorations.tsx
index ca4e5f2bc..64b8a8446 100644
--- a/src/client/views/ComponentDecorations.tsx
+++ b/src/client/views/ComponentDecorations.tsx
@@ -1,10 +1,11 @@
 import { observer } from 'mobx-react';
+import * as React from 'react';
 import { SelectionManager } from '../util/SelectionManager';
 import './ComponentDecorations.scss';
-import * as React from 'react';
 
 @observer
 export class ComponentDecorations extends React.Component<{ boundsTop: number; boundsLeft: number }, { value: string }> {
+    // eslint-disable-next-line no-use-before-define
     static Instance: ComponentDecorations;
 
     render() {
diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx
index 18b433a77..d784a14b8 100644
--- a/src/client/views/ContextMenu.tsx
+++ b/src/client/views/ContextMenu.tsx
@@ -1,3 +1,6 @@
+/* eslint-disable react/no-array-index-key */
+/* eslint-disable react/jsx-props-no-spreading */
+/* eslint-disable default-param-last */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { action, computed, IReactionDisposer, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
@@ -147,15 +150,13 @@ export class ContextMenu extends ObservableReactComponent<{}> {
 
     @computed get filteredItems(): (OriginalMenuProps | string[])[] {
         const searchString = this._searchString.toLowerCase().split(' ');
-        const matches = (descriptions: string[]): boolean => {
-            return searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s)));
-        };
+        const matches = (descriptions: string[]) => searchString.every(s => descriptions.some(desc => desc.toLowerCase().includes(s)));
         const flattenItems = (items: ContextMenuProps[], groupFunc: (groupName: any) => string[]) => {
             let eles: (OriginalMenuProps | string[])[] = [];
 
             const leaves: OriginalMenuProps[] = [];
-            for (const item of items) {
-                const description = item.description;
+            items.forEach(item => {
+                const { description } = item;
                 const path = groupFunc(description);
                 if ('subitems' in item) {
                     const children = flattenItems(item.subitems, name => [...groupFunc(description), name]);
@@ -163,13 +164,10 @@ export class ContextMenu extends ObservableReactComponent<{}> {
                         eles.push(path);
                         eles = eles.concat(children);
                     }
-                } else {
-                    if (!matches(path)) {
-                        continue;
-                    }
+                } else if (matches(path)) {
                     leaves.push(item);
                 }
-            }
+            });
 
             eles = [...leaves, ...eles];
 
@@ -241,6 +239,7 @@ export class ContextMenu extends ObservableReactComponent<{}> {
                             value={this._searchString}
                             onKeyDown={this.onKeyDown}
                             onChange={this.onChange}
+                            // eslint-disable-next-line jsx-a11y/no-autofocus
                             autoFocus
                         />
                     </span>
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index fae3610ca..fd29b1ca4 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -249,7 +249,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
         removeDocument(doc: Doc | Doc[], annotationKey?: string, leavePushpin?: boolean, dontAddToRemoved?: boolean): boolean {
             const effectiveAcl = GetEffectiveAcl(this.dataDoc);
             const indocs = doc instanceof Doc ? [doc] : doc;
-            const docs = indocs.filter(doc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(doc) === AclAdmin);
+            const docs = indocs.filter(fdoc => [AclEdit, AclAdmin].includes(effectiveAcl) || GetEffectiveAcl(fdoc) === AclAdmin);
 
             // docs.forEach(doc => doc.annotationOn === this.Document && Doc.SetInPlace(doc, 'annotationOn', undefined, true));
             const targetDataDoc = this.Document[DocData]; // this.dataDoc;  // we want to write to the template, not the actual data doc
@@ -258,13 +258,13 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
 
             if (toRemove.length !== 0) {
                 const recentlyClosed = this.Document !== Doc.MyRecentlyClosed ? Doc.MyRecentlyClosed : undefined;
-                toRemove.forEach(doc => {
+                toRemove.forEach(rdoc => {
                     // leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey);
-                    Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc, true);
-                    doc.embedContainer = undefined;
-                    if (recentlyClosed && !dontAddToRemoved && doc.type !== DocumentType.LOADING) {
-                        Doc.AddDocToList(recentlyClosed, 'data', doc, undefined, true, true);
-                        Doc.RemoveEmbedding(doc, doc);
+                    Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, rdoc, true);
+                    rdoc.embedContainer = undefined;
+                    if (recentlyClosed && !dontAddToRemoved && rdoc.type !== DocumentType.LOADING) {
+                        Doc.AddDocToList(recentlyClosed, 'data', rdoc, undefined, true, true);
+                        Doc.RemoveEmbedding(rdoc, rdoc);
                     }
                 });
                 if (targetDataDoc.isGroup && DocListCast(targetDataDoc[annotationKey ?? this.annotationKey]).length < 2) {
@@ -295,7 +295,7 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
         @action.bound
         addDocument = (doc: Doc | Doc[], annotationKey?: string): boolean => {
             const docs = doc instanceof Doc ? [doc] : doc;
-            if (this._props.filterAddDocument?.(docs) === false || docs.find(doc => Doc.AreProtosEqual(doc, this.Document) && Doc.LayoutField(doc) === Doc.LayoutField(this.Document))) {
+            if (this._props.filterAddDocument?.(docs) === false || docs.find(fdoc => Doc.AreProtosEqual(fdoc, this.Document) && Doc.LayoutField(fdoc) === Doc.LayoutField(this.Document))) {
                 return false;
             }
             const targetDataDoc = this.Document[DocData]; // this.dataDoc;  // we want to write to the template, not the actual data doc
@@ -307,12 +307,12 @@ export function ViewBoxAnnotatableComponent<P extends FieldViewProps>() {
             const added = docs;
             if (added.length) {
                 if ([AclAugment, AclEdit, AclAdmin].includes(effectiveAcl)) {
-                    added.forEach(doc => {
-                        doc._dragOnlyWithinContainer = undefined;
-                        if (annotationKey ?? this._annotationKeySuffix()) doc[DocData].annotationOn = this.Document;
-                        else doc[DocData].annotationOn = undefined;
-                        Doc.SetContainer(doc, this.Document);
-                        inheritParentAcls(targetDataDoc, doc, true);
+                    added.forEach(adoc => {
+                        adoc._dragOnlyWithinContainer = undefined;
+                        if (annotationKey ?? this._annotationKeySuffix()) adoc[DocData].annotationOn = this.Document;
+                        else adoc[DocData].annotationOn = undefined;
+                        Doc.SetContainer(adoc, this.Document);
+                        inheritParentAcls(targetDataDoc, adoc, true);
                     });
 
                     const annoDocs = Doc.Get(targetDataDoc, annotationKey ?? this.annotationKey, true) as List<Doc>; // get the dataDoc directly ... when using templates there may be some default items already there, but we can't change them, so we copy them below (should really be some kind of inheritance since the template contents could change)
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 63b485a43..c007af6fa 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -260,7 +260,7 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
         const targetDoc = this.view0?.Document;
         return !targetDoc ? null : (
             <Tooltip title={<div className="dash-tooltip">Open Context Menu</div>}>
-                <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'pointer' }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => this.openContextMenu(e))}>
+                <div className="documentButtonBar-icon" style={{ color: 'white', cursor: 'pointer' }} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => this.openContextMenu(clickEv))}>
                     <FontAwesomeIcon className="documentdecorations-icon" icon="bars" />
                 </div>
             </Tooltip>
diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx
index 009097b3b..3083b9be0 100644
--- a/src/client/views/DocumentDecorations.tsx
+++ b/src/client/views/DocumentDecorations.tsx
@@ -154,7 +154,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
     onContainerDown = (e: React.PointerEvent) => {
         const effectiveLayoutAcl = GetEffectiveAcl(SelectionManager.Views[0].Document);
         if (effectiveLayoutAcl === AclAdmin || effectiveLayoutAcl === AclEdit || effectiveLayoutAcl === AclAugment) {
-            setupMoveUpEvents(this, e, e => this.onBackgroundMove(true, e), emptyFunction, emptyFunction);
+            setupMoveUpEvents(this, e, moveEv => this.onBackgroundMove(true, moveEv), emptyFunction, emptyFunction);
             e.stopPropagation();
         }
     };
@@ -165,7 +165,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             setupMoveUpEvents(
                 this,
                 e,
-                e => this.onBackgroundMove(true, e),
+                moveEv => this.onBackgroundMove(true, moveEv),
                 emptyFunction,
                 action(() => {
                     const selected = SelectionManager.Views.length === 1 ? SelectionManager.Docs[0] : undefined;
@@ -179,7 +179,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
     };
 
     onBackgroundDown = (e: React.PointerEvent) => {
-        setupMoveUpEvents(this, e, e => this.onBackgroundMove(false, e), emptyFunction, emptyFunction);
+        setupMoveUpEvents(this, e, moveEv => this.onBackgroundMove(false, moveEv), emptyFunction, emptyFunction);
         e.stopPropagation();
     };
     @action
@@ -308,10 +308,10 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         setupMoveUpEvents(
             this,
             e,
-            e => {
+            moveEv => {
                 const [x, y] = [this.Bounds.x + 3, this.Bounds.y + 3];
                 const maxDist = Math.min((this.Bounds.r - this.Bounds.x) / 2, (this.Bounds.b - this.Bounds.y) / 2);
-                const dist = e.clientX < x && e.clientY < y ? 0 : Math.sqrt((e.clientX - x) * (e.clientX - x) + (e.clientY - y) * (e.clientY - y));
+                const dist = moveEv.clientX < x && moveEv.clientY < y ? 0 : Math.sqrt((moveEv.clientX - x) * (moveEv.clientX - x) + (moveEv.clientY - y) * (moveEv.clientY - y));
                 SelectionManager.Docs.forEach(doc => {
                     const docMax = Math.min(NumCast(doc.width) / 2, NumCast(doc.height) / 2);
                     const radius = Math.min(1, dist / maxDist) * docMax; // set radius based on ratio of drag distance to half diagonal distance of bounding box
@@ -358,7 +358,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         setupMoveUpEvents(
             this,
             e,
-            (e: PointerEvent, down: number[], delta: number[]) => // return false to keep getting events
+            (moveEv: PointerEvent, down: number[], delta: number[]) => // return false to keep getting events
                 this.setRotateCenter(seldocview, [this.rotCenter[0] + delta[0], this.rotCenter[1] + delta[1]]) as any as boolean, 
             action(() => { this._isRotating = false; }), // upEvent
             action(() => { seldocview.Document._rotation_centerX = seldocview.Document._rotation_centerY = 0; }),
@@ -399,9 +399,9 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
         setupMoveUpEvents(
             this,
             e,
-            (e: PointerEvent, down: number[], delta: number[]) => {
-                const previousPoint = { X: e.clientX, Y: e.clientY };
-                const movedPoint = { X: e.clientX - delta[0], Y: e.clientY - delta[1] };
+            (moveEv: PointerEvent, down: number[], delta: number[]) => {
+                const previousPoint = { X: moveEv.clientX, Y: moveEv.clientY };
+                const movedPoint = { X: moveEv.clientX - delta[0], Y: moveEv.clientY - delta[1] };
                 const deltaAng = InkStrokeProperties.angleChange(movedPoint, previousPoint, rcScreen);
                 if (selectedInk.length) {
                     deltaAng && InkStrokeProperties.Instance.rotateInk(selectedInk, deltaAng, rcScreen);
@@ -483,8 +483,8 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             this._interactionLock = true;
             this._snapPt = thisPt;
             e.ctrlKey && (SelectionManager.Views.forEach(docView => !Doc.NativeHeight(docView.Document) && docView.toggleNativeDimensions()));
-            const fixedAspect = SelectionManager.Docs.some(this.hasFixedAspect);
-            const scaleAspect = {x:scale.x === 1 && fixedAspect ? scale.y : scale.x, y: scale.x !== 1 && fixedAspect ? scale.x : scale.y};
+            const hasFixedAspect = SelectionManager.Docs.some(this.hasFixedAspect);
+            const scaleAspect = {x:scale.x === 1 && hasFixedAspect ? scale.y : scale.x, y: scale.x !== 1 && hasFixedAspect ? scale.x : scale.y};
             SelectionManager.Views.forEach(docView => 
                 this.resizeView(docView, refPt, scaleAspect, { dragHdl, ctrlKey:e.ctrlKey })); // prettier-ignore
             await new Promise<any>(res => { setTimeout(() => { res(this._interactionLock = undefined)})});
@@ -692,7 +692,7 @@ export class DocumentDecorations extends ObservableReactComponent<DocumentDecora
             });
         const topBtn = (key: string, icon: string, pointerDown: undefined | ((e: React.PointerEvent) => void), click: undefined | ((e: any) => void), title: string) => (
             <Tooltip key={key} title={<div className="dash-tooltip">{title}</div>} placement="top">
-                <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, e => click!(e)))}>
+                <div className={`documentDecorations-${key}Button`} onContextMenu={e => e.preventDefault()} onPointerDown={pointerDown ?? (e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => click!(clickEv)))}>
                     <FontAwesomeIcon icon={icon as any} />
                 </div>
             </Tooltip>
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index be307600c..994107c01 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -301,7 +301,7 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
                         }
                         style={{ color: SettingsManager.userColor, background: SettingsManager.userBackgroundColor }}
                         onBlur={undoable(e => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')}
-                        onKeyDown={e => e.key === 'Enter' && undoable(e => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')(e)}
+                        onKeyDown={e => e.key === 'Enter' && undoable(() => Doc.setDocFilter(this.Document, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')()}
                     />
                 );
             case 'checkbox':
diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index 0358344b9..b4fe44733 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
 import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import { Doc } from '../../fields/Doc';
-import { ControlPoint, InkData, PointData } from '../../fields/InkField';
+import { ControlPoint, InkData } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { listSpec } from '../../fields/Schema';
 import { Cast } from '../../fields/Types';
@@ -14,6 +14,7 @@ import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
 import { SnappingManager } from '../util/SnappingManager';
 import { ObservableReactComponent } from './ObservableReactComponent';
+import { PointData } from '../../pen-gestures/GestureTypes';
 
 export interface InkControlProps {
     inkDoc: Doc;
@@ -48,7 +49,7 @@ export class InkControlPtHandles extends ObservableReactComponent<InkControlProp
      */
     @action
     onControlDown = (e: React.PointerEvent, controlIndex: number): void => {
-        const ptFromScreen = this._props.inkView.ptFromScreen;
+        const { ptFromScreen } = this._props.inkView;
         if (ptFromScreen) {
             const order = controlIndex % 4;
             const handleIndexA = ((order === 3 ? controlIndex - 1 : controlIndex - 2) + this._props.inkCtrlPoints.length) % this._props.inkCtrlPoints.length;
@@ -60,7 +61,7 @@ export class InkControlPtHandles extends ObservableReactComponent<InkControlProp
             setupMoveUpEvents(
                 this,
                 e,
-                action((e: PointerEvent, down: number[], delta: number[]) => {
+                action((moveEv: PointerEvent, down: number[], delta: number[]) => {
                     if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('drag ink ctrl pt');
                     const inkMoveEnd = ptFromScreen({ X: delta[0], Y: delta[1] });
                     const inkMoveStart = ptFromScreen({ X: 0, Y: 0 });
@@ -75,9 +76,9 @@ export class InkControlPtHandles extends ObservableReactComponent<InkControlProp
                     this._props.inkView.controlUndo = undefined;
                     UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
                 }),
-                action((e: PointerEvent, doubleTap: boolean | undefined) => {
+                action((moveEv: PointerEvent, doubleTap: boolean | undefined) => {
                     const equivIndex = controlIndex === 0 ? this._props.inkCtrlPoints.length - 1 : controlIndex === this._props.inkCtrlPoints.length - 1 ? 0 : controlIndex;
-                    if (doubleTap || e.button === 2) {
+                    if (doubleTap || moveEv.button === 2) {
                         if (!brokenIndices?.includes(equivIndex) && !brokenIndices?.includes(controlIndex)) {
                             if (brokenIndices) brokenIndices.push(controlIndex);
                             else this._props.inkDoc.brokenInkIndices = new List<number>([controlIndex]);
@@ -128,7 +129,9 @@ export class InkControlPtHandles extends ObservableReactComponent<InkControlProp
      * Changes the current selected control point.
      */
     @action
-    changeCurrPoint = (i: number) => (InkStrokeProperties.Instance._currentPoint = i);
+    changeCurrPoint = (i: number) => {
+        InkStrokeProperties.Instance._currentPoint = i;
+    };
 
     render() {
         // Accessing the current ink's data and extracting all control points.
@@ -176,7 +179,7 @@ export class InkControlPtHandles extends ObservableReactComponent<InkControlProp
         };
         return (
             <svg>
-                {!nearestScreenPt ? null : <circle key={'npt'} cx={nearestScreenPt.X} cy={nearestScreenPt.Y} r={this._props.screenSpaceLineWidth * 2} fill={'#00007777'} stroke={'#00007777'} strokeWidth={0} pointerEvents="none" />}
+                {!nearestScreenPt ? null : <circle key="npt" cx={nearestScreenPt.X} cy={nearestScreenPt.Y} r={this._props.screenSpaceLineWidth * 2} fill="#00007777" stroke="#00007777" strokeWidth={0} pointerEvents="none" />}
                 {sreenCtrlPoints.map(control => hdl(control, this._overControl !== control.I ? 1 : 3 / 2, Colors.WHITE))}
             </svg>
         );
@@ -207,21 +210,21 @@ export class InkEndPtHandles extends ObservableReactComponent<InkEndProps> {
         setupMoveUpEvents(
             this,
             e,
-            action(e => {
+            action(moveEv => {
                 if (this._throttle++ % 2 !== 0) return false;
                 if (!this._props.inkView.controlUndo) this._props.inkView.controlUndo = UndoManager.StartBatch('stretch ink');
                 // compute stretch factor by finding scaling along axis between start and end points
                 const p1 = pt1();
                 const p2 = pt2();
                 const v1 = { X: p1.X - p2.X, Y: p1.Y - p2.Y };
-                const v2 = { X: e.clientX - p2.X, Y: e.clientY - p2.Y };
+                const v2 = { X: moveEv.clientX - p2.X, Y: moveEv.clientY - p2.Y };
                 const v1len = Math.sqrt(v1.X * v1.X + v1.Y * v1.Y);
                 const v2len = Math.sqrt(v2.X * v2.X + v2.Y * v2.Y);
                 const scaling = v2len / v1len;
                 const v1n = { X: v1.X / v1len, Y: v1.Y / v1len };
                 const v2n = { X: v2.X / v2len, Y: v2.Y / v2len };
                 const angle = Math.acos(v1n.X * v2n.X + v1n.Y * v2n.Y) * Math.sign(v1.X * v2.Y - v2.X * v1.Y);
-                InkStrokeProperties.Instance.stretchInk(SelectionManager.Views, scaling, p2, v1n, e.shiftKey);
+                InkStrokeProperties.Instance.stretchInk(SelectionManager.Views, scaling, p2, v1n, moveEv.shiftKey);
                 InkStrokeProperties.Instance.rotateInk(SelectionManager.Views, angle, pt2()); // bcz: call pt2() func here because pt2 will have changed from previous stretchInk call
                 return false;
             }),
@@ -243,10 +246,14 @@ export class InkEndPtHandles extends ObservableReactComponent<InkEndProps> {
                 cy={pt?.Y}
                 r={this._props.screenSpaceLineWidth * 2}
                 fill={this._overStart ? '#aaaaaa' : '#99999977'}
-                stroke={'#00007777'}
+                stroke="#00007777"
                 strokeWidth={0}
-                onPointerLeave={action(() => (this._overStart = false))}
-                onPointerEnter={action(() => (this._overStart = true))}
+                onPointerLeave={action(() => {
+                    this._overStart = false;
+                })}
+                onPointerEnter={action(() => {
+                    this._overStart = true;
+                })}
                 onPointerDown={dragFunc}
                 pointerEvents="all"
             />
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 52ea89cde..b3f9f9ea7 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -1,20 +1,22 @@
 import { Bezier } from 'bezier-js';
+import * as _ from 'lodash';
 import { action, makeObservable, observable, reaction, runInAction } from 'mobx';
 import { Doc, NumListCast, Opt } from '../../fields/Doc';
-import { InkData, InkField, InkTool, PointData } from '../../fields/InkField';
+import { InkData, InkField, InkTool } from '../../fields/InkField';
 import { List } from '../../fields/List';
 import { listSpec } from '../../fields/Schema';
 import { Cast, NumCast } from '../../fields/Types';
+import { PointData } from '../../pen-gestures/GestureTypes';
 import { Point } from '../../pen-gestures/ndollar';
 import { DocumentType } from '../documents/DocumentTypes';
-import { FitOneCurve } from '../util/bezierFit';
 import { DocumentManager } from '../util/DocumentManager';
 import { undoBatch } from '../util/UndoManager';
+import { FitOneCurve } from '../util/bezierFit';
 import { InkingStroke } from './InkingStroke';
 import { DocumentView } from './nodes/DocumentView';
-import * as _ from 'lodash';
 
 export class InkStrokeProperties {
+    // eslint-disable-next-line no-use-before-define
     static _Instance: InkStrokeProperties | undefined;
     public static get Instance() {
         return this._Instance || new InkStrokeProperties();
@@ -28,11 +30,15 @@ export class InkStrokeProperties {
         makeObservable(this);
         reaction(
             () => this._controlButton,
-            button => button && (Doc.ActiveTool = InkTool.None)
+            button => {
+                button && (Doc.ActiveTool = InkTool.None);
+            }
         );
         reaction(
             () => Doc.ActiveTool,
-            tool => tool !== InkTool.None && (this._controlButton = false)
+            tool => {
+                tool !== InkTool.None && (this._controlButton = false);
+            }
         );
     }
 
@@ -46,7 +52,7 @@ export class InkStrokeProperties {
         func: (view: DocumentView, ink: InkData, ptsXscale: number, ptsYscale: number, inkStrokeWidth: number) => { X: number; Y: number }[] | undefined,
         requireCurrPoint: boolean = false
     ) => {
-        var appliedFunc = false;
+        let appliedFunc = false;
         (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach(
             action(inkView => {
                 if (!requireCurrPoint || this._currentPoint !== -1) {
@@ -85,7 +91,7 @@ export class InkStrokeProperties {
      */
     @undoBatch
     addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number; Y: number }[]) => {
-        this.applyFunction(inkView, (view: DocumentView, ink: InkData) => {
+        this.applyFunction(inkView, (view: DocumentView /* , ink: InkData */) => {
             const doc = view.Document;
             const array = [controls[i], controls[i + 1], controls[i + 2], controls[i + 3]];
             const newsegs = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).split(t);
@@ -94,7 +100,9 @@ export class InkStrokeProperties {
 
             // Updating the indices of the control points whose handle tangency has been broken.
             doc.brokenInkIndices = new List(Cast(doc.brokenInkIndices, listSpec('number'), []).map(control => (control > i ? control + 4 : control)));
-            runInAction(() => (this._currentPoint = -1));
+            runInAction(() => {
+                this._currentPoint = -1;
+            });
 
             return controls;
         });
@@ -126,8 +134,8 @@ export class InkStrokeProperties {
      */
     getNewHandlePoints = (C: PointData[], D: PointData[], newControl: PointData) => {
         const [m, n] = [C.length, D.length];
-        let handleSizeA = Math.sqrt(Math.pow(newControl.X - C[0].X, 2) + Math.pow(newControl.Y - C[0].Y, 2));
-        let handleSizeB = Math.sqrt(Math.pow(D[n - 1].X - newControl.X, 2) + Math.pow(D[n - 1].Y - newControl.Y, 2));
+        let handleSizeA = Math.sqrt((newControl.X - C[0].X) ** 2 + (newControl.Y - C[0].Y) ** 2);
+        let handleSizeB = Math.sqrt((D[n - 1].X - newControl.X) ** 2 + (D[n - 1].Y - newControl.Y) ** 2);
         // Scaling adjustments to improve the ratio between the magnitudes of the two handle lines.
         // (Ensures that the new point added doesn't augment the inital shape of the curve much).
         if (handleSizeA < 75 && handleSizeB < 75) {
@@ -167,13 +175,13 @@ export class InkStrokeProperties {
                     const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4;
                     const splicedPoints = ink.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8));
                     const samples: Point[] = [];
-                    var startDir = { x: 0, y: 0 };
-                    var endDir = { x: 0, y: 0 };
-                    for (var i = 0; i < splicedPoints.length / 4; i++) {
+                    let startDir = { x: 0, y: 0 };
+                    let endDir = { x: 0, y: 0 };
+                    for (let i = 0; i < splicedPoints.length / 4; i++) {
                         const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
                         if (i === 0) startDir = bez.derivative(0);
                         if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1);
-                        for (var t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) {
+                        for (let t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) {
                             const pt = bez.compute(t);
                             samples.push(new Point(pt.x, pt.y));
                         }
@@ -186,7 +194,9 @@ export class InkStrokeProperties {
                     }
                 }
                 doc.brokenInkIndices = new List(brokenIndices.map(control => (control >= this._currentPoint ? control - 4 : control)));
-                runInAction(() => (this._currentPoint = -1));
+                runInAction(() => {
+                    this._currentPoint = -1;
+                });
                 return newPoints.length < 4 ? undefined : newPoints;
             },
             true
@@ -200,7 +210,7 @@ export class InkStrokeProperties {
      */
     @undoBatch
     rotateInk = (inkStrokes: DocumentView[], angle: number, scrpt: PointData) => {
-        this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number, inkStrokeWidth: number) => {
+        this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number /* , inkStrokeWidth: number */) => {
             const inkCenterPt = view.ComponentView?.ptFromScreen?.(scrpt);
             return !inkCenterPt
                 ? ink
@@ -247,8 +257,8 @@ export class InkStrokeProperties {
             const closed = InkingStroke.IsClosed(ink);
             const brokenIndices = Cast(inkView.Document.brokenInkIndices, listSpec('number'), []);
             if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1 && brokenIndices.findIndex(value => value === controlIndex) === -1) {
-                const cpt_before = ink[controlIndex];
-                const cpt = { X: cpt_before.X + deltaX, Y: cpt_before.Y + deltaY };
+                const cptBefore = ink[controlIndex];
+                const cpt = { X: cptBefore.X + deltaX, Y: cptBefore.Y + deltaY };
                 const newink = origInk.slice();
                 const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4;
                 const splicedPoints = origInk.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8));
@@ -256,28 +266,28 @@ export class InkStrokeProperties {
                 if ((nearestSeg === 0 && nearestT < 1e-1) || (nearestSeg === 4 && 1 - nearestT < 1e-1)) return ink.slice();
                 const samplesLeft: Point[] = [];
                 const samplesRight: Point[] = [];
-                var startDir = { x: 0, y: 0 };
-                var endDir = { x: 0, y: 0 };
-                for (var i = 0; i < nearestSeg / 4 + 1; i++) {
+                let startDir = { x: 0, y: 0 };
+                let endDir = { x: 0, y: 0 };
+                for (let i = 0; i < nearestSeg / 4 + 1; i++) {
                     const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
                     if (i === 0) startDir = bez.derivative(_.isEqual(bez.derivative(0), { x: 0, y: 0, t: 0 }) ? 1e-8 : 0);
                     if (i === nearestSeg / 4) endDir = bez.derivative(nearestT);
-                    for (var t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) {
+                    for (let t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) {
                         const pt = bez.compute(i !== nearestSeg / 4 ? t : Math.min(nearestT, t));
                         samplesLeft.push(new Point(pt.x, pt.y));
                     }
                 }
-                var { finalCtrls } = FitOneCurve(samplesLeft, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
-                for (var i = nearestSeg / 4; i < splicedPoints.length / 4; i++) {
+                let { finalCtrls } = FitOneCurve(samplesLeft, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
+                for (let i = nearestSeg / 4; i < splicedPoints.length / 4; i++) {
                     const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
                     if (i === nearestSeg / 4) startDir = bez.derivative(nearestT);
                     if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(_.isEqual(bez.derivative(1), { x: 0, y: 0, t: 1 }) ? 1 - 1e-8 : 1);
-                    for (var t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) {
+                    for (let t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) {
                         const pt = bez.compute(Math.min(1, t));
                         samplesRight.push(new Point(pt.x, pt.y));
                     }
                 }
-                const { finalCtrls: rightCtrls, error: errorRight } = FitOneCurve(samplesRight, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
+                const { finalCtrls: rightCtrls /* , error: errorRight */ } = FitOneCurve(samplesRight, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y });
                 finalCtrls = finalCtrls.concat(rightCtrls);
                 newink.splice(this._currentPoint - 4, 8, ...finalCtrls);
                 return newink;
@@ -307,11 +317,12 @@ export class InkStrokeProperties {
         });
 
     public static nearestPtToStroke(ctrlPoints: { X: number; Y: number }[], refInkSpacePt: { X: number; Y: number }, excludeSegs?: number[]) {
-        var distance = Number.MAX_SAFE_INTEGER;
-        var nearestT = -1;
-        var nearestSeg = -1;
-        var nearestPt = { X: 0, Y: 0 };
-        for (var i = 0; i < ctrlPoints.length - 3; i += 4) {
+        let distance = Number.MAX_SAFE_INTEGER;
+        let nearestT = -1;
+        let nearestSeg = -1;
+        let nearestPt = { X: 0, Y: 0 };
+        for (let i = 0; i < ctrlPoints.length - 3; i += 4) {
+            // eslint-disable-next-line no-continue
             if (excludeSegs?.includes(i)) continue;
             const array = [ctrlPoints[i], ctrlPoints[i + 1], ctrlPoints[i + 2], ctrlPoints[i + 3]];
             const point = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).project({ x: refInkSpacePt.X, y: refInkSpacePt.Y });
@@ -380,6 +391,7 @@ export class InkStrokeProperties {
                     const snappedInkPt = doc === inkView.Document ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space
 
                     if (snappedInkPt) {
+                        // eslint-disable-next-line no-param-reassign
                         snapData = { nearestPt: snappedInkPt, distance: snapped.distance };
                     }
                 }
@@ -406,6 +418,7 @@ export class InkStrokeProperties {
                 inkCopy[handleIndexB] = this.rotatePoint(handleB, controlPoint, angleDifference);
                 return inkCopy;
             }
+            return undefined;
         });
     };
 
@@ -430,7 +443,9 @@ export class InkStrokeProperties {
         const magnitudeB = Math.sqrt(vectorB.X * vectorB.X + vectorB.Y * vectorB.Y);
         if (magnitudeA === 0 || magnitudeB === 0) return 0;
         // Normalizing the vectors.
+        // eslint-disable-next-line no-param-reassign
         vectorA = { X: vectorA.X / magnitudeA, Y: vectorA.Y / magnitudeA };
+        // eslint-disable-next-line no-param-reassign
         vectorB = { X: vectorB.X / magnitudeB, Y: vectorB.Y / magnitudeB };
         return Math.acos(vectorB.X * vectorA.X + vectorB.Y * vectorA.Y);
     }
diff --git a/src/client/views/InkTangentHandles.tsx b/src/client/views/InkTangentHandles.tsx
index f3a757f19..577acc4d1 100644
--- a/src/client/views/InkTangentHandles.tsx
+++ b/src/client/views/InkTangentHandles.tsx
@@ -8,17 +8,16 @@ import { listSpec } from '../../fields/Schema';
 import { Cast } from '../../fields/Types';
 import { emptyFunction } from '../../Utils';
 import { setupMoveUpEvents } from '../../ClientUtils';
-import { Transform } from '../util/Transform';
 import { UndoManager } from '../util/UndoManager';
 import { Colors } from './global/globalEnums';
 import { InkingStroke } from './InkingStroke';
 import { InkStrokeProperties } from './InkStrokeProperties';
+
 export interface InkHandlesProps {
     inkDoc: Doc;
     inkView: InkingStroke;
     screenCtrlPoints: InkData;
     screenSpaceLineWidth: number;
-    ScreenToLocalTransform: () => Transform;
 }
 
 @observer
@@ -38,9 +37,9 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> {
         setupMoveUpEvents(
             this,
             e,
-            action((e: PointerEvent, down: number[], delta: number[]) => {
+            action((moveEv: PointerEvent, down: number[], delta: number[]) => {
                 if (!this.props.inkView.controlUndo) this.props.inkView.controlUndo = UndoManager.StartBatch('DocDecs move tangent');
-                if (e.altKey) this.onBreakTangent(controlIndex);
+                if (moveEv.altKey) this.onBreakTangent(controlIndex);
                 const inkMoveEnd = this.props.inkView.ptFromScreen({ X: delta[0], Y: delta[1] });
                 const inkMoveStart = this.props.inkView.ptFromScreen({ X: 0, Y: 0 });
                 this.docView && InkStrokeProperties.Instance.moveTangentHandle(this.docView, -(inkMoveEnd.X - inkMoveStart.X), -(inkMoveEnd.Y - inkMoveStart.Y), handleIndex, oppositeHandleIndex, controlIndex);
@@ -101,11 +100,12 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> {
                 tangentLines.push({ X1: data[i].X, Y1: data[i].Y, X2: data[i + 1].X, Y2: data[i + 1].Y, X3: data[i + 3].X, Y3: data[i + 3].Y, dot1: i + 1, dot2: i + 2 });
             }
         }
-        const screenSpaceLineWidth = this.props.screenSpaceLineWidth;
+        const { screenSpaceLineWidth } = this.props;
 
         return (
             <>
                 {tangentHandles.map((pts, i) => (
+                    // eslint-disable-next-line react/no-array-index-key
                     <svg height="10" width="10" key={`hdl${i}`}>
                         <circle
                             cx={pts.X}
@@ -129,12 +129,13 @@ export class InkTangentHandles extends React.Component<InkHandlesProps> {
                             x2={x2}
                             y2={y2}
                             stroke={Colors.MEDIUM_BLUE}
-                            strokeDasharray={'1 1'}
+                            strokeDasharray="1 1"
                             strokeWidth={1}
                             display={pts.dot1 === InkStrokeProperties.Instance._currentPoint || pts.dot2 === InkStrokeProperties.Instance._currentPoint ? 'inherit' : 'none'}
                         />
                     );
                     return (
+                        // eslint-disable-next-line react/no-array-index-key
                         <svg height="100" width="100" key={`line${i}`}>
                             {tangentLine(pts.X1, pts.Y1, pts.X2, pts.Y2)}
                             {tangentLine(pts.X2, pts.Y2, pts.X3, pts.Y3)}
diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index e04daec3b..35067047b 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -144,7 +144,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>()
             e,
             !isEditing
                 ? returnFalse
-                : action((e: PointerEvent, down: number[], delta: number[]) => {
+                : action((moveEv: PointerEvent, down: number[], delta: number[]) => {
                       if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch('drag ink ctrl pt');
                       const inkMoveEnd = this.ptFromScreen({ X: delta[0], Y: delta[1] });
                       const inkMoveStart = this.ptFromScreen({ X: 0, Y: 0 });
@@ -159,7 +159,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>()
                       this.controlUndo = undefined;
                       UndoManager.FilterBatches(['data', 'x', 'y', 'width', 'height']);
                   }),
-            action((e: PointerEvent, doubleTap: boolean | undefined) => {
+            action((moveEv: PointerEvent, doubleTap: boolean | undefined) => {
                 if (doubleTap) {
                     InkStrokeProperties.Instance._controlButton = true;
                     InkStrokeProperties.Instance._currentPoint = -1;
@@ -331,7 +331,7 @@ export class InkingStroke extends ViewBoxAnnotatableComponent<FieldViewProps>()
                     false
                 )}
                 <InkControlPtHandles inkView={this} inkDoc={inkDoc} inkCtrlPoints={inkData} screenCtrlPoints={this.screenCtrlPts} nearestScreenPt={this.nearestScreenPt} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} />
-                <InkTangentHandles inkView={this} inkDoc={inkDoc} screenCtrlPoints={this.screenCtrlPts} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} ScreenToLocalTransform={this.ScreenToLocalBoxXf} />
+                <InkTangentHandles inkView={this} inkDoc={inkDoc} screenCtrlPoints={this.screenCtrlPts} screenSpaceLineWidth={screenSpaceCenterlineStrokeWidth} />
             </div>
         );
     };
diff --git a/src/client/views/KeyphraseQueryView.tsx b/src/client/views/KeyphraseQueryView.tsx
index e996fc946..81f004010 100644
--- a/src/client/views/KeyphraseQueryView.tsx
+++ b/src/client/views/KeyphraseQueryView.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable jsx-a11y/label-has-associated-control */
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import './KeyphraseQueryView.scss';
@@ -9,28 +10,21 @@ export interface KP_Props {
 
 @observer
 export class KeyphraseQueryView extends React.Component<KP_Props> {
-    constructor(props: KP_Props) {
-        super(props);
-    }
-
     render() {
-        const kps = this.props.keyphrases.toString();
         const keyterms = this.props.keyphrases.split(',');
         return (
             <div>
                 <h5>Select queries to send:</h5>
                 <form>
-                    {keyterms.map((kp: string) => {
-                        //return (<p>{"-" + kp}</p>);
-                        return (
-                            <p>
-                                <label>
-                                    <input name="query" type="radio" />
-                                    <span>{kp}</span>
-                                </label>
-                            </p>
-                        );
-                    })}
+                    {keyterms.map((kp: string) => (
+                        //  return (<p>{"-" + kp}</p>);
+                        <p>
+                            <label>
+                                <input name="query" type="radio" />
+                                <span>{kp}</span>
+                            </label>
+                        </p>
+                    ))}
                 </form>
             </div>
         );
diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx
index c34554b56..24433cd01 100644
--- a/src/client/views/LightboxView.tsx
+++ b/src/client/views/LightboxView.tsx
@@ -1,3 +1,6 @@
+/* eslint-disable no-use-before-define */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
 import { IconProp } from '@fortawesome/fontawesome-svg-core';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Toggle, ToggleType, Type } from 'browndash-components';
@@ -43,6 +46,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
      */
     public static Contains(view?:DocumentView) { return view && LightboxView.Instance?._docView && (view.containerViewPath?.() ?? []).concat(view).includes(LightboxView.Instance?._docView); } // prettier-ignore
     public static get LightboxDoc() { return LightboxView.Instance?._doc; } // prettier-ignore
+    // eslint-disable-next-line no-use-before-define
     static Instance: LightboxView;
     private _path: {
         doc: Opt<Doc>; //
@@ -72,11 +76,18 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
     @action
     public SetLightboxDoc(doc: Opt<Doc>, target?: Doc, future?: Doc[], layoutTemplate?: Doc | string) {
         const lightDoc = this._doc;
-        lightDoc && lightDoc !== doc && savedKeys.forEach(key => (lightDoc[key] = this._savedState[key]));
+        lightDoc &&
+            lightDoc !== doc &&
+            savedKeys.forEach(key => {
+                lightDoc[key] = this._savedState[key];
+            });
         this._savedState = {};
 
         if (doc) {
-            lightDoc !== doc && savedKeys.map(key => (this._savedState[key] = Doc.Get(doc, key, true)));
+            lightDoc !== doc &&
+                savedKeys.forEach(key => {
+                    this._savedState[key] = Doc.Get(doc, key, true);
+                });
             const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement();
             l && (Cast(l.link_anchor_2, Doc, null).backgroundColor = 'lightgreen');
             CollectionStackedTimeline.CurrentlyPlaying?.forEach(dv => dv.ComponentView?.Pause?.());
@@ -107,8 +118,9 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
         return true;
     }
 
-    public AddDocTab = (doc: Doc, location: OpenWhere, layoutTemplate?: Doc | string) =>
-        this.SetLightboxDoc(
+    public AddDocTab = (docs: Doc | Doc[], location: OpenWhere, layoutTemplate?: Doc | string) => {
+        const doc = (docs instanceof Doc ? [docs] : docs).lastElement();
+        return this.SetLightboxDoc(
             doc,
             undefined,
             [...DocListCast(doc[Doc.LayoutFieldKey(doc)]), ...DocListCast(doc[Doc.LayoutFieldKey(doc) + '_annotations']).filter(anno => anno.annotationOn !== doc), ...this._future].sort(
@@ -116,6 +128,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
             ),
             layoutTemplate
         );
+    };
     @action
     next = () => {
         const lightDoc = this._doc;
@@ -180,8 +193,12 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
             this.SetLightboxDoc(undefined);
         }
     };
-    toggleFitWidth = () => this._doc && (this._doc._layout_fitWidth = !this._doc._layout_fitWidth);
-    togglePen = () => (Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen);
+    toggleFitWidth = () => {
+        this._doc && (this._doc._layout_fitWidth = !this._doc._layout_fitWidth);
+    };
+    togglePen = () => {
+        Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen;
+    };
     toggleExplore = () => SnappingManager.SetExploreMode(!SnappingManager.ExploreMode);
 
     lightboxDoc = () => this._doc;
@@ -191,33 +208,31 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
     lightboxDocTemplate = () => this._layoutTemplate;
     future = () => this._future;
 
-    renderNavBtn = (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: IconProp, display: any, click: () => void, color?: string) => {
-        return (
+    renderNavBtn = (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: IconProp, display: any, click: () => void, color?: string) => (
+        <div
+            className="lightboxView-navBtn-frame"
+            style={{
+                display: display ? '' : 'none',
+                left,
+                width: bottom !== undefined ? undefined : Math.min(this._props.PanelWidth / 4, this._props.maxBorder[0]),
+                bottom,
+            }}>
             <div
-                className="lightboxView-navBtn-frame"
-                style={{
-                    display: display ? '' : 'none',
-                    left,
-                    width: bottom !== undefined ? undefined : Math.min(this._props.PanelWidth / 4, this._props.maxBorder[0]),
-                    bottom,
+                className="lightboxView-navBtn"
+                title={color}
+                style={{ top, color: SettingsManager.userColor, background: undefined }}
+                onClick={e => {
+                    e.stopPropagation();
+                    click();
                 }}>
-                <div
-                    className="lightboxView-navBtn"
-                    title={color}
-                    style={{ top, color: SettingsManager.userColor, background: undefined }}
-                    onClick={e => {
-                        e.stopPropagation();
-                        click();
-                    }}>
-                    <div style={{ height: 10 }}>{color}</div>
-                    <FontAwesomeIcon icon={icon} size="3x" />
-                </div>
+                <div style={{ height: 10 }}>{color}</div>
+                <FontAwesomeIcon icon={icon} size="3x" />
             </div>
-        );
-    };
+        </div>
+    );
     render() {
-        let downx = 0,
-            downy = 0;
+        let downx = 0;
+        let downy = 0;
         const toggleBtn = (classname: string, tooltip: string, toggleBackground: any, icon: IconProp, icon2: IconProp | string, onClick: () => void) => (
             <div className={classname}>
                 <Toggle
@@ -253,10 +268,12 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
                         clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`,
                         background: SettingsManager.userBackgroundColor,
                     }}>
-                    <GestureOverlay isActive={true}>
+                    <GestureOverlay isActive>
                         <DocumentView
                             key={this._doc.title + this._doc[Id]} // this makes a new DocumentView when the document changes which makes link following work, otherwise no DocView is registered for the new Doc
-                            ref={action((r: DocumentView | null) => (this._docView = r !== null ? r : undefined))}
+                            ref={action((r: DocumentView | null) => {
+                                this._docView = r !== null ? r : undefined;
+                            })}
                             Document={this._doc}
                             PanelWidth={this.lightboxWidth}
                             PanelHeight={this.lightboxHeight}
@@ -266,7 +283,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
                             styleProvider={DefaultStyleProvider}
                             ScreenToLocalTransform={this.lightboxScreenToLocal}
                             renderDepth={0}
-                            suppressSetHeight={this._doc._layout_fitWidth ? true : false}
+                            suppressSetHeight={!!this._doc._layout_fitWidth}
                             containerViewPath={returnEmptyDoclist}
                             childFilters={returnEmptyFilter}
                             childFiltersByRanges={returnEmptyFilter}
@@ -303,6 +320,7 @@ export class LightboxView extends ObservableReactComponent<LightboxViewProps> {
 }
 interface LightboxTourBtnProps {
     navBtn: (left: Opt<string | number>, bottom: Opt<number>, top: number, icon: IconProp, display: any, click: () => void, color?: string) => JSX.Element;
+    // eslint-disable-next-line react/no-unused-prop-types
     future: () => Opt<Doc[]>;
     stepInto: () => void;
     lightboxDoc: () => Opt<Doc>;
diff --git a/src/client/views/Main.tsx b/src/client/views/Main.tsx
index 17c21326d..01f3b032e 100644
--- a/src/client/views/Main.tsx
+++ b/src/client/views/Main.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable no-new */
 // if ((module as any).hot) {
 //     (module as any).hot.accept();
 // }
@@ -5,7 +6,7 @@
 import * as dotenv from 'dotenv'; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
 import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
-import { AssignAllExtensions } from '../../extensions/General/Extensions';
+import { AssignAllExtensions } from '../../extensions/Extensions';
 import { FieldLoader } from '../../fields/FieldLoader';
 import { CurrentUserUtils } from '../util/CurrentUserUtils';
 import { PingManager } from '../util/PingManager';
@@ -15,6 +16,7 @@ import { CollectionView } from './collections/CollectionView';
 import './global/globalScripts';
 import { MainView } from './MainView';
 import { BranchingTrailManager } from '../util/BranchingTrailManager';
+
 dotenv.config();
 
 AssignAllExtensions();
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index a13fe1cb7..10d423c05 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -747,7 +747,8 @@ export class MainView extends ObservableReactComponent<{}> {
 
     sidebarScreenToLocal = () => new Transform(0, -this.topOfSidebarDoc, 1);
     mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0);
-    static addDocTabFunc_impl = (doc: Doc, location: OpenWhere): boolean => {
+    static addDocTabFunc_impl = (docs: Doc | Doc[], location: OpenWhere): boolean => {
+        const doc = (docs instanceof Doc ? [docs] : docs).lastElement();
         const whereFields = location.split(':');
         const keyValue = whereFields.includes(OpenWhereMod.keyvalue);
         const whereMods = whereFields.length > 1 ? (whereFields[1] as OpenWhereMod) : OpenWhereMod.none;
@@ -957,7 +958,7 @@ export class MainView extends ObservableReactComponent<{}> {
                     childFiltersByRanges={returnEmptyFilter}
                     searchFilterDocs={returnEmptyDoclist}
                 />
-                {['watching', 'recording'].includes(StrCast(this.userDoc?.presentationMode)) ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{StrCast(this.userDoc?.presentationMode)}</div> : <></>}
+                {['watching', 'recording'].includes(StrCast(this.userDoc?.presentationMode)) ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{StrCast(this.userDoc?.presentationMode)}</div> : null}
             </div>
         );
     }
diff --git a/src/client/views/MainViewModal.tsx b/src/client/views/MainViewModal.tsx
index e52562625..a6dc5c62b 100644
--- a/src/client/views/MainViewModal.tsx
+++ b/src/client/views/MainViewModal.tsx
@@ -1,3 +1,6 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable react/require-default-props */
 import { isDark } from 'browndash-components';
 import { observer } from 'mobx-react';
 import * as React from 'react';
@@ -11,7 +14,6 @@ export interface MainViewOverlayProps {
     dialogueBoxStyle?: React.CSSProperties;
     overlayStyle?: React.CSSProperties;
     dialogueBoxDisplayedOpacity?: number;
-    overlayDisplayedOpacity?: number;
     closeOnExternalClick?: () => void; // the close method of a MainViewModal, triggered if there is a click on the overlay (closing the modal)
 }
 
@@ -20,7 +22,6 @@ export class MainViewModal extends React.Component<MainViewOverlayProps> {
     render() {
         const p = this.props;
         const dialogueOpacity = p.dialogueBoxDisplayedOpacity || 1;
-        const overlayOpacity = p.overlayDisplayedOpacity || 0.4;
         return !p.isDisplayed ? null : (
             <div
                 className="mainViewModal-cont"
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index f92bd68a3..0301632ec 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -16,7 +16,6 @@ import { DocumentView } from './nodes/DocumentView';
 import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 import { ObservableReactComponent } from './ObservableReactComponent';
 import { AnchorMenu } from './pdf/AnchorMenu';
-const _global = (window /* browser */ || global) /* node */ as any;
 
 export interface MarqueeAnnotatorProps {
     Document: Doc;
@@ -101,8 +100,8 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
         let minY = Number.MAX_VALUE;
         let maxY = -Number.MIN_VALUE;
         const annoDocs: Doc[] = [];
-        savedAnnoMap.forEach((value: HTMLDivElement[], key: number) =>
-            value.map(anno => {
+        savedAnnoMap.forEach((value: HTMLDivElement[]) =>
+            value.forEach(anno => {
                 const textRegion = new Doc();
                 textRegion.x = parseInt(anno.style.left ?? '0');
                 textRegion.y = parseInt(anno.style.top ?? '0');
@@ -131,7 +130,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
         return textRegionAnno;
     };
     @action
-    highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean, summarize?: boolean) => {
+    highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
         // creates annotation documents for current highlights
         const effectiveAcl = GetEffectiveAcl(this.props.Document[DocData]);
         const annotationDoc = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeAnnotationDocument(color, isLinkButton, savedAnnotations);
@@ -157,7 +156,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
     //    3) localize the unrotated vector by scaling into the marquee container's coordinates
     //    4) reattach the vector to the center of the bounding box
     getTransformedScreenPt = (down: number[]) => {
-        const marqueeContainer = this.props.marqueeContainer;
+        const { marqueeContainer } = this.props;
         const containerXf = this.props.isNativeScaled ? this.props.docView().screenToContentsTransform() : this.props.docView().screenToViewTransform();
         const boundingRect = marqueeContainer.getBoundingClientRect();
         const center = { x: boundingRect.x + boundingRect.width / 2, y: boundingRect.y + boundingRect.height / 2 };
@@ -177,15 +176,15 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
         document.addEventListener('pointermove', this.onSelectMove);
         document.addEventListener('pointerup', this.onSelectEnd);
 
-        AnchorMenu.Instance.OnCrop = (e: PointerEvent) => {
+        AnchorMenu.Instance.OnCrop = () => {
             if (this.props.anchorMenuCrop) {
                 UndoManager.RunInBatch(() => this.props.anchorMenuCrop?.(this.highlight('', true, undefined, false), true), 'cropping');
             }
         };
-        AnchorMenu.Instance.OnClick = undoable((e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation');
+        AnchorMenu.Instance.OnClick = undoable(() => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation');
         AnchorMenu.Instance.OnAudio = unimplementedFunction;
         AnchorMenu.Instance.Highlight = (color: string) => this.highlight(color, false, undefined, true);
-        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true);
+        AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]> /* , addAsAnnotation?: boolean */) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true);
         AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true);
 
         /**
@@ -220,13 +219,14 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
             : action((e: PointerEvent, ele: HTMLElement) => {
                   e.preventDefault();
                   e.stopPropagation();
-                  var cropRegion: Doc | undefined;
+                  let cropRegion: Doc | undefined;
+                  // eslint-disable-next-line no-return-assign
                   const sourceAnchorCreator = () => (cropRegion = this.highlight('', true, undefined, true)); // hyperlink color
-                  const targetCreator = (annotationOn: Doc | undefined) => this.props.anchorMenuCrop!(cropRegion, false)!;
+                  const targetCreator = (/* annotationOn: Doc | undefined */) => this.props.anchorMenuCrop!(cropRegion, false)!;
                   DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(this.props.docView(), sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
-                      dragComplete: e => {
-                          if (!e.aborted && e.linkDocument) {
-                              const linkDocData = e.linkDocument[DocData];
+                      dragComplete: dragEx => {
+                          if (!dragEx.aborted && dragEx.linkDocument) {
+                              const linkDocData = dragEx.linkDocument[DocData];
                               linkDocData.link_relationship = 'cropped image';
                               linkDocData.title = 'crop: ' + this.props.Document.title;
                               linkDocData.link_displayLine = false;
@@ -252,7 +252,7 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
         const movLoc = this.getTransformedScreenPt([e.clientX, e.clientY]);
         this._width = movLoc.x - this._start.x;
         this._height = movLoc.y - this._start.y;
-        //e.stopPropagation(); // overlay documents are all 'active', yet they can be dragged. if we stop propagation, then they can be marqueed but not dragged.  if we don't stop, then they will be marqueed and dragged, but the marquee will be zero width since the doc will move along with the cursor.
+        // e.stopPropagation(); // overlay documents are all 'active', yet they can be dragged. if we stop propagation, then they can be marqueed but not dragged.  if we don't stop, then they will be marqueed and dragged, but the marquee will be zero width since the doc will move along with the cursor.
     };
 
     onSelectEnd = (e: PointerEvent) => {
@@ -268,7 +268,9 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
             // copy the temporary marquee to allow for multiple selections (not currently available though).
             const copy = document.createElement('div');
             const scale = (this.props.scaling?.() || 1) * NumCast(this.props.Document._freeform_scale, 1);
-            ['border', 'opacity', 'top', 'left', 'width', 'height'].forEach(prop => (copy.style[prop as any] = marqueeStyle[prop as any]));
+            ['border', 'opacity', 'top', 'left', 'width', 'height'].forEach(prop => {
+                copy.style[prop as any] = marqueeStyle[prop as any];
+            });
             copy.className = 'marqueeAnnotator-annotationBox';
             copy.style.top = parseInt(marqueeStyle.top.toString().replace('px', '')) / scale + this.props.scrollTop + 'px';
             copy.style.left = parseInt(marqueeStyle.left.toString().replace('px', '')) / scale + 'px';
diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx
index 6d03034ec..fddb40624 100644
--- a/src/client/views/PreviewCursor.tsx
+++ b/src/client/views/PreviewCursor.tsx
@@ -13,6 +13,7 @@ import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox';
 
 @observer
 export class PreviewCursor extends ObservableReactComponent<{}> {
+    // eslint-disable-next-line no-use-before-define
     static _instance: PreviewCursor;
     public static get Instance() {
         return PreviewCursor._instance;
@@ -39,7 +40,9 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
     paste = async (e: ClipboardEvent) => {
         if (this.Visible && e.clipboardData) {
             const newPoint = this._getTransform?.().transformPoint(this._clickPoint[0], this._clickPoint[1]);
-            runInAction(() => (this.Visible = false));
+            runInAction(() => {
+                this.Visible = false;
+            });
 
             // tests for URL and makes web document
             const re: any = /^https?:\/\//g;
@@ -88,10 +91,10 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
                     UndoManager.RunInBatch(() => this._addLiveTextDoc?.(DocUtils.GetNewTextDoc('', newPoint[0], newPoint[1], 500, undefined, undefined)), 'paste');
                 }
             }
-            //pasting in images
+            // pasting in images
             else if (e.clipboardData.getData('text/html') !== '' && e.clipboardData.getData('text/html').includes('<img src=')) {
-                const re: any = /<img src="(.*?)"/g;
-                const arr: any[] = re.exec(e.clipboardData.getData('text/html'));
+                const regEx: any = /<img src="(.*?)"/g;
+                const arr: any[] = regEx.exec(e.clipboardData.getData('text/html'));
 
                 if (newPoint) {
                     undoBatch(() => {
@@ -122,7 +125,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
     @action
     onKeyDown = (e: KeyboardEvent) => {
         // Mixing events between React and Native is finicky.
-        //if not these keys, make a textbox if preview cursor is active!
+        // if not these keys, make a textbox if preview cursor is active!
         if (
             e.key !== 'Escape' &&
             e.key !== 'Backspace' &&
@@ -162,7 +165,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
         }
     };
 
-    //when focus is lost, this will remove the preview cursor
+    // when focus is lost, this will remove the preview cursor
     @action onBlur = (): void => {
         this.Visible = false;
     };
@@ -192,6 +195,7 @@ export class PreviewCursor extends ObservableReactComponent<{}> {
     }
     render() {
         return !this._clickPoint || !this.Visible ? null : (
+            // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
             <div className="previewCursor" onBlur={this.onBlur} tabIndex={0} ref={e => e?.focus()} style={{ color: lightOrDark(this.Doc?.backgroundColor ?? 'white'), transform: `translate(${this._clickPoint[0]}px, ${this._clickPoint[1]}px)` }}>
                 I
             </div>
diff --git a/src/client/views/PropertiesDocBacklinksSelector.tsx b/src/client/views/PropertiesDocBacklinksSelector.tsx
index cf5105efc..be5b7ec1f 100644
--- a/src/client/views/PropertiesDocBacklinksSelector.tsx
+++ b/src/client/views/PropertiesDocBacklinksSelector.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react/no-unused-prop-types */
 import { action } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
diff --git a/src/client/views/PropertiesDocContextSelector.tsx b/src/client/views/PropertiesDocContextSelector.tsx
index b8bbde9de..2d04f2fe3 100644
--- a/src/client/views/PropertiesDocContextSelector.tsx
+++ b/src/client/views/PropertiesDocContextSelector.tsx
@@ -1,7 +1,10 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable jsx-a11y/anchor-is-valid */
 import { computed, makeObservable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import { Doc, DocListCast } from '../../fields/Doc';
+import { Doc } from '../../fields/Doc';
 import { Id } from '../../fields/FieldSymbols';
 import { Cast, StrCast } from '../../fields/Types';
 import { DocFocusOrOpen } from '../util/DocumentManager';
@@ -52,9 +55,9 @@ export class PropertiesDocContextSelector extends ObservableReactComponent<Prope
             .map(doc => ({ col: doc, target }));
     }
 
-    getOnClick = (col: Doc, target: Doc) => {
+    getOnClick = (clickCol: Doc) => {
         if (!this._props.DocView) return;
-        col = Doc.IsDataProto(col) ? Doc.MakeDelegate(col) : col;
+        const col = Doc.IsDataProto(clickCol) ? Doc.MakeDelegate(clickCol) : clickCol;
         DocFocusOrOpen(Doc.GetProto(this._props.DocView.Document), undefined, col);
     };
 
@@ -65,7 +68,7 @@ export class PropertiesDocContextSelector extends ObservableReactComponent<Prope
                 {this._props.hideTitle ? null : <p key="contexts">Contexts:</p>}
                 {this._docs.map(doc => (
                     <p key={doc.col[Id] + doc.target[Id]}>
-                        <a onClick={() => this.getOnClick(doc.col, doc.target)}>{StrCast(doc.col.title)}</a>
+                        <a onClick={() => this.getOnClick(doc.col)}>{StrCast(doc.col.title)}</a>
                     </p>
                 ))}
             </div>
diff --git a/src/client/views/PropertiesSection.tsx b/src/client/views/PropertiesSection.tsx
index 3c9fa1123..f083f03b9 100644
--- a/src/client/views/PropertiesSection.tsx
+++ b/src/client/views/PropertiesSection.tsx
@@ -1,5 +1,8 @@
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable react/require-default-props */
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
+import { action, computed } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
 import { SettingsManager } from '../util/SettingsManager';
@@ -10,7 +13,6 @@ export interface PropertiesSectionProps {
     children?: JSX.Element | string | null;
     isOpen: boolean;
     setIsOpen: (bool: boolean) => any;
-    inSection?: boolean;
     setInSection?: (bool: boolean) => any;
     onDoubleClick?: () => void;
 }
@@ -21,44 +23,35 @@ export class PropertiesSection extends React.Component<PropertiesSectionProps> {
         return SettingsManager.userColor;
     }
 
-    @computed get backgroundColor() {
-        return SettingsManager.userBackgroundColor;
-    }
-
     @computed get variantColor() {
         return SettingsManager.userVariantColor;
     }
 
-    @observable isDouble: boolean = false;
-
     render() {
         if (this.props.children === undefined || this.props.children === null) return null;
-        else
-            return (
-                <div className="propertiesView-section" onPointerEnter={action(() => this.props.setInSection && this.props.setInSection(true))} onPointerLeave={action(() => this.props.setInSection && this.props.setInSection(false))}>
-                    <div
-                        className="propertiesView-sectionTitle"
-                        onDoubleClick={action(e => {
-                            this.isDouble = true;
-                            this.props.onDoubleClick && this.props.onDoubleClick();
-                            this.props.setIsOpen(true);
-                            setTimeout(() => (this.isDouble = false), 300);
-                        })}
-                        onClick={action(e => {
-                            this.props.setIsOpen(!this.props.isOpen);
-                        })}
-                        style={{
-                            background: this.variantColor,
-                            // this.props.isOpen ? this.variantColor : this.backgroundColor,
-                            color: this.color,
-                        }}>
-                        {this.props.title}
-                        <div className="propertiesView-sectionTitle-icon">
-                            <FontAwesomeIcon icon={this.props.isOpen ? 'caret-down' : 'caret-right'} size="lg" />
-                        </div>
+        return (
+            <div className="propertiesView-section" onPointerEnter={action(() => this.props.setInSection && this.props.setInSection(true))} onPointerLeave={action(() => this.props.setInSection && this.props.setInSection(false))}>
+                <div
+                    className="propertiesView-sectionTitle"
+                    onDoubleClick={action(() => {
+                        this.props.onDoubleClick && this.props.onDoubleClick();
+                        this.props.setIsOpen(true);
+                    })}
+                    onClick={action(() => {
+                        this.props.setIsOpen(!this.props.isOpen);
+                    })}
+                    style={{
+                        background: this.variantColor,
+                        // this.props.isOpen ? this.variantColor : this.backgroundColor,
+                        color: this.color,
+                    }}>
+                    {this.props.title}
+                    <div className="propertiesView-sectionTitle-icon">
+                        <FontAwesomeIcon icon={this.props.isOpen ? 'caret-down' : 'caret-right'} size="lg" />
                     </div>
-                    {!this.props.isOpen ? null : <div className="propertiesView-content">{this.props.children}</div>}
                 </div>
-            );
+                {!this.props.isOpen ? null : <div className="propertiesView-content">{this.props.children}</div>}
+            </div>
+        );
     }
 }
diff --git a/src/client/views/ScriptBox.tsx b/src/client/views/ScriptBox.tsx
index 365980f33..8de359e07 100644
--- a/src/client/views/ScriptBox.tsx
+++ b/src/client/views/ScriptBox.tsx
@@ -1,17 +1,18 @@
+/* eslint-disable react/require-default-props */
 import { action, makeObservable, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
+import { emptyFunction } from '../../Utils';
 import { Doc, Opt } from '../../fields/Doc';
+import { DocData } from '../../fields/DocSymbols';
 import { ScriptField } from '../../fields/ScriptField';
 import { ScriptCast } from '../../fields/Types';
-import { emptyFunction } from '../../ClientUtils';
 import { DragManager } from '../util/DragManager';
 import { CompileScript } from '../util/Scripting';
 import { EditableView } from './EditableView';
-import { DocumentIconContainer } from './nodes/DocumentIcon';
 import { OverlayView } from './OverlayView';
 import './ScriptBox.scss';
-import { DocData } from '../../fields/DocSymbols';
+import { DocumentIconContainer } from './nodes/DocumentIcon';
 
 export interface ScriptBoxProps {
     onSave: (text: string, onError: (error: string) => void) => void;
@@ -25,6 +26,7 @@ export interface ScriptBoxProps {
 export class ScriptBox extends React.Component<ScriptBoxProps> {
     @observable
     private _scriptText: string;
+    overlayDisposer?: () => void;
 
     constructor(props: ScriptBoxProps) {
         super(props);
@@ -42,7 +44,6 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
         console.log('ScriptBox: ' + error);
     };
 
-    overlayDisposer?: () => void;
     onFocus = () => {
         this.overlayDisposer?.();
         this.overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
@@ -53,21 +54,35 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
     };
 
     render() {
-        let onFocus: Opt<() => void> = undefined,
-            onBlur: Opt<() => void> = undefined;
+        let onFocus: Opt<() => void>;
+        let onBlur: Opt<() => void>;
         if (this.props.showDocumentIcons) {
             onFocus = this.onFocus;
             onBlur = this.onBlur;
         }
-        const params = <EditableView contents={''} display={'block'} maxHeight={72} height={35} fontSize={28} GetValue={() => ''} SetValue={(value: string) => (this.props.setParams?.(value.split(' ').filter(s => s !== ' ')) ? true : true)} />;
+        const params = (
+            <EditableView
+                contents=""
+                display="block"
+                maxHeight={72}
+                height={35}
+                fontSize={28}
+                GetValue={() => ''}
+                SetValue={(value: string) => {
+                    this.props.setParams?.(value.split(' ').filter(s => s !== ' '));
+                    return true;
+                }}
+            />
+        );
         return (
             <div className="scriptBox-outerDiv">
                 <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
-                    <textarea className="scriptBox-textarea" onChange={this.onChange} value={this._scriptText} onFocus={onFocus} onBlur={onBlur}></textarea>
+                    <textarea className="scriptBox-textarea" onChange={this.onChange} value={this._scriptText} onFocus={onFocus} onBlur={onBlur} />
                     <div style={{ background: 'beige' }}>{params}</div>
                 </div>
                 <div className="scriptBox-toolbar">
                     <button
+                        type="button"
                         onClick={e => {
                             this.props.onSave(this._scriptText, this.onError);
                             e.stopPropagation();
@@ -75,6 +90,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
                         Save
                     </button>
                     <button
+                        type="button"
                         onClick={e => {
                             this.props.onCancel && this.props.onCancel();
                             e.stopPropagation();
@@ -85,11 +101,12 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
             </div>
         );
     }
-    //let l = docList(this.source[0].data).length; if (l) { let ind = this.target[0].index !== undefined ? (this.target[0].index+1) % l : 0;  this.target[0].index = ind;  this.target[0].proto = getProto(docList(this.source[0].data)[ind]);}
+    // let l = docList(this.source[0].data).length; if (l) { let ind = this.target[0].index !== undefined ? (this.target[0].index+1) % l : 0;  this.target[0].index = ind;  this.target[0].proto = getProto(docList(this.source[0].data)[ind]);}
+    // eslint-disable-next-line react/sort-comp
     public static EditButtonScript(title: string, doc: Doc, fieldKey: string, clientX: number, clientY: number, contextParams?: { [name: string]: string }, defaultScript?: ScriptField) {
         let overlayDisposer: () => void = emptyFunction;
         const script = ScriptCast(doc[fieldKey]) || defaultScript;
-        let originalText: string | undefined = undefined;
+        let originalText: string | undefined;
         if (script) {
             originalText = script.script.originalScript;
         }
@@ -124,7 +141,7 @@ export class ScriptBox extends React.Component<ScriptBoxProps> {
                         div.style.display = 'inline-block';
                         div.style.transform = `translate(${clientX}px, ${clientY}px)`;
                         div.innerHTML = 'button';
-                        params.length && DragManager.StartButtonDrag([div], text, doc.title + '-instance', {}, params, (button: Doc) => {}, clientX, clientY);
+                        params.length && DragManager.StartButtonDrag([div], text, doc.title + '-instance', {}, params, () => {}, clientX, clientY);
 
                         doc[DocData][fieldKey] = new ScriptField(script);
                         overlayDisposer();
diff --git a/src/debug/Repl.tsx b/src/debug/Repl.tsx
index a9f7c085f..8ac502348 100644
--- a/src/debug/Repl.tsx
+++ b/src/debug/Repl.tsx
@@ -30,7 +30,7 @@ class Repl extends React.Component {
             if (!script.compiled) {
                 this.executedCommands.push({ command: this.text, result: 'Compile Error' });
             } else {
-                const result = script.run({ makeInterface }, e => this.executedCommands.push({ command: this.text, result: e.message || e }));
+                const result = script.run({ makeInterface }, err => this.executedCommands.push({ command: this.text, result: err.message || err }));
                 result.success && this.executedCommands.push({ command: this.text, result: result.result });
             }
             this.text = '';
@@ -39,15 +39,13 @@ class Repl extends React.Component {
 
     @computed
     get commands() {
-        return this.executedCommands.map(command => {
-            return (
-                <div style={{ marginTop: '5px' }}>
-                    <p>{command.command}</p>
-                    {/* <pre>{JSON.stringify(command.result, null, 2)}</pre> */}
-                    <pre>{command.result instanceof RefField || command.result instanceof ObjectField ? 'object' : String(command.result)}</pre>
-                </div>
-            );
-        });
+        return this.executedCommands.map(command => (
+            <div style={{ marginTop: '5px' }}>
+                <p>{command.command}</p>
+                {/* <pre>{JSON.stringify(command.result, null, 2)}</pre> */}
+                <pre>{command.result instanceof RefField || command.result instanceof ObjectField ? 'object' : String(command.result)}</pre>
+            </div>
+        ));
     }
 
     render() {
diff --git a/src/debug/Test.tsx b/src/debug/Test.tsx
index c906dcc03..856cf6bd4 100644
--- a/src/debug/Test.tsx
+++ b/src/debug/Test.tsx
@@ -1,11 +1,12 @@
 import * as React from 'react';
 import * as ReactDOM from 'react-dom/client';
-console.log('ENTERED');
+
 class Test extends React.Component {
     render() {
         return <div> HELLO WORLD </div>;
     }
 }
+
 const root = ReactDOM.createRoot(document.getElementById('root')!);
 
 root.render(<Test />);
diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx
index fc62463e6..f3295a6c6 100644
--- a/src/debug/Viewer.tsx
+++ b/src/debug/Viewer.tsx
@@ -1,7 +1,12 @@
+/* eslint-disable react/no-unescaped-entities */
+/* eslint-disable react/button-has-type */
+/* eslint-disable no-use-before-define */
+/* eslint-disable react/no-array-index-key */
+/* eslint-disable no-redeclare */
 import { action, configure, observable } from 'mobx';
 import { observer } from 'mobx-react';
 import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
 import { DocServer } from '../client/DocServer';
 import { resolvedPorts } from '../client/util/CurrentUserUtils';
 import { CompileScript } from '../client/util/Scripting';
@@ -86,14 +91,12 @@ class DocumentViewer extends React.Component<{ field: Doc }> {
         let content;
         if (this.expanded) {
             const keys = Object.keys(this.props.field);
-            const fields = keys.map(key => {
-                return (
-                    <div key={key}>
-                        <b>({key}): </b>
-                        <DebugViewer field={this.props.field[key]} setValue={value => applyToDoc(this.props.field, key, value)}></DebugViewer>
-                    </div>
-                );
-            });
+            const fields = keys.map(key => (
+                <div key={key}>
+                    <b>({key}): </b>
+                    <DebugViewer field={this.props.field[key]} setValue={value => applyToDoc(this.props.field, key, value)} />
+                </div>
+            ));
             content = (
                 <div>
                     Document ({this.props.field[Id]})<div style={{ paddingLeft: '25px' }}>{fields}</div>
@@ -115,7 +118,7 @@ class DocumentViewer extends React.Component<{ field: Doc }> {
 class DebugViewer extends React.Component<{ field: FieldResult; setValue(value: string): boolean }> {
     render() {
         let content;
-        const field = this.props.field;
+        const { field } = this.props;
         if (field instanceof List) {
             content = <ListViewer field={field} />;
         } else if (field instanceof Doc) {
@@ -171,7 +174,7 @@ class Viewer extends React.Component {
                 <input value={this.idToAdd} onChange={this.inputOnChange} onKeyDown={this.onKeyDown} />
                 <div>
                     {this.fields.map((field, index) => (
-                        <DebugViewer field={field} key={index} setValue={() => false}></DebugViewer>
+                        <DebugViewer field={field} key={index} setValue={() => false} />
                     ))}
                 </div>
             </>
@@ -181,10 +184,13 @@ class Viewer extends React.Component {
 
 (async function () {
     await DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, 'viewer');
-    ReactDOM.render(
-        <div style={{ position: 'absolute', width: '100%', height: '100%' }}>
-            <Viewer />
-        </div>,
-        document.getElementById('root')
-    );
+    const root = document.getElementById('root');
+    if (root) {
+        const reactDom = ReactDOM.createRoot(root);
+        reactDom.render(
+            <div style={{ position: 'absolute', width: '100%', height: '100%' }}>
+                <Viewer />
+            </div>
+        );
+    }
 })();
diff --git a/src/extensions/ArrayExtensions.ts b/src/extensions/ArrayExtensions.ts
deleted file mode 100644
index 8e125766d..000000000
--- a/src/extensions/ArrayExtensions.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-export default class ArrayExtension {
-    private readonly property: string;
-    private readonly body: <T>(this: Array<T>) => any;
-
-    constructor(property: string, body: <T>(this: Array<T>) => any) {
-        this.property = property;
-        this.body = body;
-    }
-
-    assign() {
-        Object.defineProperty(Array.prototype, this.property, {
-            value: this.body,
-            enumerable: false
-        });
-    }
-
-}
-
-/**
- * IMPORTANT: Any extension you add here *must* have a corresponding type definition
- * in the Array<T> interface in ./General/ExtensionsTypings.ts. Otherwise,
- * Typescript will not recognize your new function.
- */
-const extensions = [
-    new ArrayExtension("lastElement", function () {
-        if (!this.length) {
-            return undefined;
-        }
-        return this[this.length - 1];
-    })
-];
-
-function Assign() {
-    extensions.forEach(extension => extension.assign());
-}
-
-export { Assign };
\ No newline at end of file
diff --git a/src/extensions/Extensions.ts b/src/extensions/Extensions.ts
new file mode 100644
index 000000000..5815527d1
--- /dev/null
+++ b/src/extensions/Extensions.ts
@@ -0,0 +1,9 @@
+import { Assign as ArrayAssign } from './Extensions_Array';
+import { Assign as StringAssign } from './Extensions_String';
+
+function AssignAllExtensions() {
+    ArrayAssign();
+    StringAssign();
+}
+
+export { AssignAllExtensions };
diff --git a/src/extensions/ExtensionsTypings.ts b/src/extensions/ExtensionsTypings.ts
new file mode 100644
index 000000000..d6ffd3be3
--- /dev/null
+++ b/src/extensions/ExtensionsTypings.ts
@@ -0,0 +1,9 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+interface Array<T> {
+    lastElement(): T;
+}
+
+interface String {
+    removeTrailingNewlines(): string;
+    hasNewline(): boolean;
+}
diff --git a/src/extensions/Extensions_Array.ts b/src/extensions/Extensions_Array.ts
new file mode 100644
index 000000000..a50fb330f
--- /dev/null
+++ b/src/extensions/Extensions_Array.ts
@@ -0,0 +1,37 @@
+export default class ArrayExtension {
+    private readonly property: string;
+    private readonly body: <T>(this: Array<T>) => any;
+
+    constructor(property: string, body: <T>(this: Array<T>) => any) {
+        this.property = property;
+        this.body = body;
+    }
+
+    assign() {
+        // eslint-disable-next-line no-extend-native
+        Object.defineProperty(Array.prototype, this.property, {
+            value: this.body,
+            enumerable: false,
+        });
+    }
+}
+
+/**
+ * IMPORTANT: Any extension you add here *must* have a corresponding type definition
+ * in the Array<T> interface in ./General/ExtensionsTypings.ts. Otherwise,
+ * Typescript will not recognize your new function.
+ */
+const extensions = [
+    new ArrayExtension('lastElement', function () {
+        if (!this.length) {
+            return undefined;
+        }
+        return this[this.length - 1];
+    }),
+];
+
+function Assign() {
+    extensions.forEach(extension => extension.assign());
+}
+
+export { Assign };
diff --git a/src/extensions/Extensions_String.ts b/src/extensions/Extensions_String.ts
new file mode 100644
index 000000000..c95095f8e
--- /dev/null
+++ b/src/extensions/Extensions_String.ts
@@ -0,0 +1,16 @@
+/* eslint-disable no-extend-native */
+function Assign() {
+    String.prototype.removeTrailingNewlines = function () {
+        let sliced = this;
+        while (sliced.endsWith('\n')) {
+            sliced = sliced.substring(0, this.length - 1);
+        }
+        return sliced as string;
+    };
+
+    String.prototype.hasNewline = function () {
+        return this.endsWith('\n');
+    };
+}
+
+export { Assign };
diff --git a/src/extensions/General/Extensions.ts b/src/extensions/General/Extensions.ts
deleted file mode 100644
index 4b6d05d5f..000000000
--- a/src/extensions/General/Extensions.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Assign as ArrayAssign } from "../ArrayExtensions";
-import { Assign as StringAssign } from "../StringExtensions";
-
-function AssignAllExtensions() {
-    ArrayAssign();
-    StringAssign();
-}
-
-export { AssignAllExtensions };
\ No newline at end of file
diff --git a/src/extensions/General/ExtensionsTypings.ts b/src/extensions/General/ExtensionsTypings.ts
deleted file mode 100644
index 370157ed0..000000000
--- a/src/extensions/General/ExtensionsTypings.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-interface Array<T> {
-    lastElement(): T;
-}
-
-interface String {
-    removeTrailingNewlines(): string;
-    hasNewline(): boolean;
-}
\ No newline at end of file
diff --git a/src/extensions/StringExtensions.ts b/src/extensions/StringExtensions.ts
deleted file mode 100644
index 2c76e56c8..000000000
--- a/src/extensions/StringExtensions.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-function Assign() {
-
-    String.prototype.removeTrailingNewlines = function () {
-        let sliced = this;
-        while (sliced.endsWith("\n")) {
-            sliced = sliced.substring(0, this.length - 1);
-        }
-        return sliced as string;
-    };
-
-    String.prototype.hasNewline = function () {
-        return this.endsWith("\n");
-    };
-
-}
-
-export { Assign };
\ No newline at end of file
diff --git a/src/fields/DateField.ts b/src/fields/DateField.ts
index 56a3177f8..f0a851ce6 100644
--- a/src/fields/DateField.ts
+++ b/src/fields/DateField.ts
@@ -1,4 +1,4 @@
-import { serializable, date } from 'serializr';
+import { serializable, date as serializrDate } from 'serializr';
 import { Deserializable } from '../client/util/SerializationHelper';
 import { ObjectField } from './ObjectField';
 import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
@@ -7,7 +7,7 @@ import { scriptingGlobal, ScriptingGlobals } from '../client/util/ScriptingGloba
 @scriptingGlobal
 @Deserializable('date')
 export class DateField extends ObjectField {
-    @serializable(date())
+    @serializable(serializrDate())
     readonly date: Date;
 
     constructor(date: Date = new Date()) {
diff --git a/src/fields/HtmlField.ts b/src/fields/HtmlField.ts
index b67f0f7e9..536f5ce4c 100644
--- a/src/fields/HtmlField.ts
+++ b/src/fields/HtmlField.ts
@@ -1,7 +1,7 @@
+import { primitive, serializable } from 'serializr';
 import { Deserializable } from '../client/util/SerializationHelper';
-import { serializable, primitive } from 'serializr';
-import { ObjectField } from './ObjectField';
 import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
+import { ObjectField } from './ObjectField';
 
 @Deserializable('html')
 export class HtmlField extends ObjectField {
diff --git a/src/fields/IconField.ts b/src/fields/IconField.ts
index 4d2badb68..33e5be7af 100644
--- a/src/fields/IconField.ts
+++ b/src/fields/IconField.ts
@@ -1,5 +1,5 @@
-import { Deserializable } from '../client/util/SerializationHelper';
 import { serializable, primitive } from 'serializr';
+import { Deserializable } from '../client/util/SerializationHelper';
 import { ObjectField } from './ObjectField';
 import { Copy, ToJavascriptString, ToScriptString, ToString } from './FieldSymbols';
 
diff --git a/src/fields/List.ts b/src/fields/List.ts
index f97f208fe..852b05bc6 100644
--- a/src/fields/List.ts
+++ b/src/fields/List.ts
@@ -1,5 +1,5 @@
 import { action, computed, makeObservable, observable } from 'mobx';
-import { alias, list, serializable } from 'serializr';
+import { alias, list as serializrList, serializable } from 'serializr';
 import { ScriptingGlobals } from '../client/util/ScriptingGlobals';
 import { Deserializable, afterDocDeserialize, autoObject } from '../client/util/SerializationHelper';
 import { Field, FieldType, StrListCast } from './Doc';
@@ -279,7 +279,7 @@ class ListImpl<T extends FieldType> extends ObjectField {
         return this[FieldTuples].map(toRealField);
     }
 
-    @serializable(alias(ListFieldName, list(autoObject(), { afterDeserialize: afterDocDeserialize })))
+    @serializable(alias(ListFieldName, serializrList(autoObject(), { afterDeserialize: afterDocDeserialize })))
     private get __fieldTuples() {
         return this[FieldTuples];
     }
diff --git a/src/fields/Schema.ts b/src/fields/Schema.ts
index ed603e5de..7217bec37 100644
--- a/src/fields/Schema.ts
+++ b/src/fields/Schema.ts
@@ -1,3 +1,7 @@
+/* eslint-disable guard-for-in */
+/* eslint-disable no-restricted-syntax */
+/* eslint-disable no-redeclare */
+/* eslint-disable no-use-before-define */
 import { Interface, ToInterface, Cast, ToConstructor, HasTail, Head, Tail, ListSpec, ToType, DefaultFieldConstructor } from './Types';
 import { Doc, FieldType } from './Doc';
 import { ObjectField } from './ObjectField';
@@ -12,6 +16,7 @@ type AllToInterface<T extends Interface[]> = {
 
 export const emptySchema = createSchema({});
 export const Document = makeInterface(emptySchema);
+// eslint-disable-next-line no-redeclare
 export type Document = makeInterface<[typeof emptySchema]>;
 
 export interface InterfaceFunc<T extends Interface[]> {
@@ -39,6 +44,7 @@ export function makeInterface<T extends Interface[]>(...schemas: T): InterfaceFu
                         // defaultSpec
                         return Cast(field, desc.type, desc.defaultVal);
                     }
+                    // eslint-disable-next-line no-prototype-builtins
                     if (typeof desc === 'function' && !ObjectField.isPrototypeOf(desc) && !RefField.isPrototypeOf(desc)) {
                         const doc = Cast(field, Doc);
                         if (doc === undefined) {
@@ -73,8 +79,8 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
             get() {
                 return Cast(this.__doc[key], type as any);
             },
-            set(value) {
-                value = Cast(value, type as any);
+            set(setValue) {
+                const value = Cast(setValue, type as any);
                 if (value !== undefined) {
                     this.__doc[key] = value;
                     return;
@@ -93,14 +99,15 @@ export function makeStrictInterface<T extends Interface>(schema: T): (doc: Doc)
     };
 }
 
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
 export function createSchema<T extends Interface>(schema: T): T & { proto: ToConstructor<Doc> } {
     return undefined as any;
-    (schema as any).proto = Doc;
-    return schema as any;
+    // (schema as any).proto = Doc;
+    // return schema as any;
 }
 
 export function listSpec<U extends ToConstructor<FieldType>>(type: U): ListSpec<ToType<U>> {
-    return { List: type as any }; //TODO Types
+    return { List: type as any }; // TODO Types
 }
 
 export function defaultSpec<T extends ToConstructor<FieldType>>(type: T, defaultVal: ToType<T>): DefaultFieldConstructor<ToType<T>> {
diff --git a/src/fields/URLField.ts b/src/fields/URLField.ts
index c6c51957d..3a83e7ca0 100644
--- a/src/fields/URLField.ts
+++ b/src/fields/URLField.ts
@@ -16,16 +16,18 @@ export abstract class URLField extends ObjectField {
     @serializable(url())
     readonly url: URL;
 
-    constructor(url: string);
-    constructor(url: URL);
-    constructor(url: URL | string) {
+    constructor(urlVal: string);
+    // eslint-disable-next-line @typescript-eslint/no-shadow
+    constructor(urlVal: URL);
+    // eslint-disable-next-line @typescript-eslint/no-shadow
+    constructor(urlVal: URL | string) {
         super();
         this.url =
-            typeof url !== 'string'
-                ? url // it's an URL
-                : url.startsWith('http')
-                  ? new URL(url)
-                  : new URL(url, window.location.origin);
+            typeof urlVal !== 'string'
+                ? urlVal // it's an URL
+                : urlVal.startsWith('http')
+                  ? new URL(urlVal)
+                  : new URL(urlVal, window.location.origin);
     }
 
     [ToScriptString]() {
diff --git a/src/mobile/MobileMain.tsx b/src/mobile/MobileMain.tsx
index dc3a73def..07839b6f6 100644
--- a/src/mobile/MobileMain.tsx
+++ b/src/mobile/MobileMain.tsx
@@ -3,7 +3,7 @@ import * as ReactDOM from 'react-dom';
 import { DocServer } from '../client/DocServer';
 import { Docs } from '../client/documents/Documents';
 import { CurrentUserUtils } from '../client/util/CurrentUserUtils';
-import { AssignAllExtensions } from '../extensions/General/Extensions';
+import { AssignAllExtensions } from '../extensions/Extensions';
 import { MobileInterface } from './MobileInterface';
 
 AssignAllExtensions();
diff --git a/src/pen-gestures/ndollar.ts b/src/pen-gestures/ndollar.ts
index d28d303b8..5f1ea00dc 100644
--- a/src/pen-gestures/ndollar.ts
+++ b/src/pen-gestures/ndollar.ts
@@ -1,3 +1,4 @@
+/* eslint-disable no-use-before-define */
 import { Gestures } from './GestureTypes';
 
 /**
@@ -69,20 +70,34 @@ import { Gestures } from './GestureTypes';
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- **/
+ * */
 
 //
 // Point class
 //
 export class Point {
-    constructor(public X: number, public Y: number) {}
+    // eslint-disable-next-line no-useless-constructor
+    constructor(
+        public X: number,
+        public Y: number
+    ) {
+        /* empty */
+    }
 }
 
 //
 // Rectangle class
 //
 export class Rectangle {
-    constructor(public X: number, public Y: number, public Width: number, public Height: number) {}
+    // eslint-disable-next-line no-useless-constructor
+    constructor(
+        public X: number,
+        public Y: number,
+        public Width: number,
+        public Height: number
+    ) {
+        /* empty */
+    }
 }
 
 //
@@ -93,7 +108,11 @@ export class Unistroke {
     public StartUnitVector: Point;
     public Vector: number[];
 
-    constructor(public Name: string, useBoundedRotationInvariance: boolean, points: Point[]) {
+    constructor(
+        public Name: string,
+        useBoundedRotationInvariance: boolean,
+        points: Point[]
+    ) {
         this.Points = Resample(points, NumPoints);
         const radians = IndicativeAngle(this.Points);
         this.Points = RotateBy(this.Points, -radians);
@@ -121,15 +140,15 @@ export class Multistroke {
         this.NumStrokes = strokes.length; // number of individual strokes
 
         const order = new Array(strokes.length); // array of integer indices
-        for (var i = 0; i < strokes.length; i++) {
+        for (let i = 0; i < strokes.length; i++) {
             order[i] = i; // initialize
         }
-        const orders = new Array(); // array of integer arrays
-        HeapPermute(strokes.length, order, /*out*/ orders);
+        const orders = [] as any[]; // array of integer arrays
+        HeapPermute(strokes.length, order, /* out */ orders);
 
         const unistrokes = MakeUnistrokes(strokes, orders); // returns array of point arrays
         this.Unistrokes = new Array(unistrokes.length); // unistrokes for this multistroke
-        for (var j = 0; j < unistrokes.length; j++) {
+        for (let j = 0; j < unistrokes.length; j++) {
             this.Unistrokes[j] = new Unistroke(this.Name, useBoundedRotationInvariance, unistrokes[j]);
         }
     }
@@ -139,7 +158,14 @@ export class Multistroke {
 // Result class
 //
 export class Result {
-    constructor(public Name: string, public Score: any, public Time: any) {}
+    // eslint-disable-next-line no-useless-constructor
+    constructor(
+        public Name: string,
+        public Score: any,
+        public Time: any
+    ) {
+        /* empty */
+    }
 }
 
 //
@@ -174,44 +200,40 @@ export class NDollarRecognizer {
             new Multistroke(
                 Gestures.Rectangle,
                 useBoundedRotationInvariance,
-                new Array(
-                    new Array(
-                        new Point(30, 146), //new Point(29, 160), new Point(30, 180), new Point(31, 200),
-                        new Point(30, 222), //new Point(50, 219), new Point(70, 225), new Point(90, 230),
-                        new Point(106, 225), //new Point(100, 200), new Point(106, 180), new Point(110, 160),
-                        new Point(106, 146), //new Point(80, 150), new Point(50, 146),
-                        new Point(30, 143)
-                    )
-                )
+                new Array([
+                    new Point(30, 146), // new Point(29, 160), new Point(30, 180), new Point(31, 200),
+                    new Point(30, 222), // new Point(50, 219), new Point(70, 225), new Point(90, 230),
+                    new Point(106, 225), // new Point(100, 200), new Point(106, 180), new Point(110, 160),
+                    new Point(106, 146), // new Point(80, 150), new Point(50, 146),
+                    new Point(30, 143),
+                ])
             )
         );
-        this.Multistrokes.push(new Multistroke(Gestures.Line, useBoundedRotationInvariance, new Array(new Array(new Point(12, 347), new Point(119, 347)))));
+        this.Multistrokes.push(new Multistroke(Gestures.Line, useBoundedRotationInvariance, [[new Point(12, 347), new Point(119, 347)]]));
         this.Multistrokes.push(
             new Multistroke(
                 Gestures.Triangle, // equilateral
                 useBoundedRotationInvariance,
-                new Array(new Array(new Point(40, 100), new Point(100, 200), new Point(140, 102), new Point(42, 100)))
+                new Array([new Point(40, 100), new Point(100, 200), new Point(140, 102), new Point(42, 100)])
             )
         );
         this.Multistrokes.push(
             new Multistroke(
                 Gestures.Circle,
                 useBoundedRotationInvariance,
-                new Array(
-                    new Array(
-                        new Point(200, 250),
-                        new Point(240, 230),
-                        new Point(248, 210),
-                        new Point(248, 190),
-                        new Point(240, 170),
-                        new Point(200, 150),
-                        new Point(160, 170),
-                        new Point(151, 190),
-                        new Point(151, 210),
-                        new Point(160, 230),
-                        new Point(201, 250)
-                    )
-                )
+                new Array([
+                    new Point(200, 250),
+                    new Point(240, 230),
+                    new Point(248, 210),
+                    new Point(248, 190),
+                    new Point(240, 170),
+                    new Point(200, 150),
+                    new Point(160, 170),
+                    new Point(151, 190),
+                    new Point(151, 210),
+                    new Point(160, 230),
+                    new Point(201, 250),
+                ])
             )
         );
         NumMultistrokes = this.Multistrokes.length; // NumMultistrokes flags the end of the non user-defined gstures strokes
@@ -292,20 +314,21 @@ export class NDollarRecognizer {
         const points = CombineStrokes(strokes); // make one connected unistroke from the given strokes
         const candidate = new Unistroke('', useBoundedRotationInvariance, points);
 
-        var u = -1;
-        var b = +Infinity;
+        let u = -1;
+        let b = +Infinity;
         for (
-            var i = 0;
+            let i = 0;
             i < this.Multistrokes.length;
             i++ // for each multistroke template
         ) {
             if (!requireSameNoOfStrokes || strokes.length === this.Multistrokes[i].NumStrokes) {
                 // optional -- only attempt match when same # of component strokes
-                for (const unistroke of this.Multistrokes[i].Unistrokes) {
+                // eslint-disable-next-line no-loop-func
+                this.Multistrokes[i].Unistrokes.forEach(unistroke => {
                     // for each unistroke within this multistroke
                     if (AngleBetweenUnitVectors(candidate.StartUnitVector, unistroke.StartUnitVector) <= AngleSimilarityThreshold) {
                         // strokes start in the same direction
-                        var d;
+                        let d;
                         if (useProtractor) {
                             d = OptimalCosineDistance(unistroke.Vector, candidate.Vector); // Protractor
                         } else {
@@ -316,7 +339,7 @@ export class NDollarRecognizer {
                             u = i; // multistroke owner of unistroke
                         }
                     }
-                }
+                });
             }
         }
         const t1 = Date.now();
@@ -325,12 +348,12 @@ export class NDollarRecognizer {
 
     AddGesture = (name: string, useBoundedRotationInvariance: boolean, strokes: any[]) => {
         this.Multistrokes[this.Multistrokes.length] = new Multistroke(name, useBoundedRotationInvariance, strokes);
-        var num = 0;
-        for (const multistroke of this.Multistrokes) {
+        let num = 0;
+        this.Multistrokes.forEach(multistroke => {
             if (multistroke.Name === name) {
                 num++;
             }
-        }
+        });
         return num;
     };
 
@@ -343,11 +366,11 @@ export class NDollarRecognizer {
 //
 // Private helper functions from here on down
 //
-function HeapPermute(n: number, order: any[], /*out*/ orders: any[]) {
+function HeapPermute(n: number, order: any[], /* out */ orders: any[]) {
     if (n === 1) {
         orders[orders.length] = order.slice(); // append copy
     } else {
-        for (var i = 0; i < n; i++) {
+        for (let i = 0; i < n; i++) {
             HeapPermute(n - 1, order, orders);
             if (n % 2 === 1) {
                 // swap 0, n-1
@@ -364,47 +387,44 @@ function HeapPermute(n: number, order: any[], /*out*/ orders: any[]) {
     }
 }
 
-function MakeUnistrokes(strokes: any, orders: any) {
-    const unistrokes = new Array(); // array of point arrays
-    for (const order of orders) {
+function MakeUnistrokes(strokes: any, orders: any[]) {
+    const unistrokes = [] as any[]; // array of point arrays
+    orders.forEach(order => {
         for (
-            var b = 0;
-            b < Math.pow(2, order.length);
+            let b = 0;
+            b < order.length ** 2;
             b++ // use b's bits for directions
         ) {
-            const unistroke = new Array(); // array of points
-            for (var i = 0; i < order.length; i++) {
-                var pts;
+            const unistroke = [] as any[]; // array of points
+            for (let i = 0; i < order.length; i++) {
+                let pts;
+                // eslint-disable-next-line no-bitwise
                 if (((b >> i) & 1) === 1) {
                     // is b's bit at index i on?
                     pts = strokes[order[i]].slice().reverse(); // copy and reverse
                 } else {
                     pts = strokes[order[i]].slice(); // copy
                 }
-                for (const point of pts) {
+                pts.forEach((point: any) => {
                     unistroke[unistroke.length] = point; // append points
-                }
+                });
             }
             unistrokes[unistrokes.length] = unistroke; // add one unistroke to set
         }
-    }
+    });
     return unistrokes;
 }
 
-function CombineStrokes(strokes: any) {
-    const points = new Array();
-    for (const stroke of strokes) {
-        for (const { X, Y } of stroke) {
-            points[points.length] = new Point(X, Y);
-        }
-    }
+function CombineStrokes(strokes: any[]) {
+    const points: Point[] = [];
+    strokes.forEach(stroke => stroke.forEach((X: any, Y: any) => points.push(new Point(X, Y))));
     return points;
 }
 function Resample(points: any, n: any) {
     const I = PathLength(points) / (n - 1); // interval length
-    var D = 0.0;
+    let D = 0.0;
     const newpoints = new Array(points[0]);
-    for (var i = 1; i < points.length; i++) {
+    for (let i = 1; i < points.length; i++) {
         const d = Distance(points[i - 1], points[i]);
         if (D + d >= I) {
             const qx = points[i - 1].X + ((I - D) / d) * (points[i].X - points[i - 1].X);
@@ -425,55 +445,55 @@ function IndicativeAngle(points: any) {
     const c = Centroid(points);
     return Math.atan2(c.Y - points[0].Y, c.X - points[0].X);
 }
-function RotateBy(points: any, radians: any) {
+function RotateBy(points: Point[], radians: any) {
     // rotates points around centroid
     const c = Centroid(points);
     const cos = Math.cos(radians);
     const sin = Math.sin(radians);
-    const newpoints = new Array();
-    for (const point of points) {
+    const newpoints: Point[] = [];
+    points.forEach(point => {
         const qx = (point.X - c.X) * cos - (point.Y - c.Y) * sin + c.X;
         const qy = (point.X - c.X) * sin + (point.Y - c.Y) * cos + c.Y;
-        newpoints[newpoints.length] = new Point(qx, qy);
-    }
+        newpoints.push(new Point(qx, qy));
+    });
     return newpoints;
 }
 function ScaleDimTo(points: any, size: any, ratio1D: any) {
     // scales bbox uniformly for 1D, non-uniformly for 2D
     const B = BoundingBox(points);
     const uniformly = Math.min(B.Width / B.Height, B.Height / B.Width) <= ratio1D; // 1D or 2D gesture test
-    const newpoints = new Array();
-    for (const { X, Y } of points) {
+    const newpoints: Point[] = [];
+    points.forEach((X: any, Y: any) => {
         const qx = uniformly ? X * (size / Math.max(B.Width, B.Height)) : X * (size / B.Width);
         const qy = uniformly ? Y * (size / Math.max(B.Width, B.Height)) : Y * (size / B.Height);
         newpoints[newpoints.length] = new Point(qx, qy);
-    }
+    });
     return newpoints;
 }
 function TranslateTo(points: any, pt: any) {
     // translates points' centroid
     const c = Centroid(points);
-    const newpoints = new Array();
-    for (const { X, Y } of points) {
+    const newpoints: Point[] = [];
+    points.forEach((X: any, Y: any) => {
         const qx = X + pt.X - c.X;
         const qy = Y + pt.Y - c.Y;
         newpoints[newpoints.length] = new Point(qx, qy);
-    }
+    });
     return newpoints;
 }
 function Vectorize(points: any, useBoundedRotationInvariance: any) {
     // for Protractor
-    var cos = 1.0;
-    var sin = 0.0;
+    let cos = 1.0;
+    let sin = 0.0;
     if (useBoundedRotationInvariance) {
         const iAngle = Math.atan2(points[0].Y, points[0].X);
         const baseOrientation = (Math.PI / 4.0) * Math.floor((iAngle + Math.PI / 8.0) / (Math.PI / 4.0));
         cos = Math.cos(baseOrientation - iAngle);
         sin = Math.sin(baseOrientation - iAngle);
     }
-    var sum = 0.0;
-    const vector = new Array<number>();
-    for (var i = 0; i < points.length; i++) {
+    let sum = 0.0;
+    const vector: number[] = [];
+    for (let i = 0; i < points.length; i++) {
         const newX = points[i].X * cos - points[i].Y * sin;
         const newY = points[i].Y * cos + points[i].X * sin;
         vector[vector.length] = newX;
@@ -481,16 +501,16 @@ function Vectorize(points: any, useBoundedRotationInvariance: any) {
         sum += newX * newX + newY * newY;
     }
     const magnitude = Math.sqrt(sum);
-    for (var i = 0; i < vector.length; i++) {
+    for (let i = 0; i < vector.length; i++) {
         vector[i] /= magnitude;
     }
     return vector;
 }
 function OptimalCosineDistance(v1: any, v2: any) {
     // for Protractor
-    var a = 0.0;
-    var b = 0.0;
-    for (var i = 0; i < v1.length; i += 2) {
+    let a = 0.0;
+    let b = 0.0;
+    for (let i = 0; i < v1.length; i += 2) {
         a += v1[i] * v2[i] + v1[i + 1] * v2[i + 1];
         b += v1[i] * v2[i + 1] - v1[i + 1] * v2[i];
     }
@@ -498,18 +518,20 @@ function OptimalCosineDistance(v1: any, v2: any) {
     return Math.acos(a * Math.cos(angle) + b * Math.sin(angle));
 }
 function DistanceAtBestAngle(points: any, T: any, a: any, b: any, threshold: any) {
-    var x1 = Phi * a + (1.0 - Phi) * b;
-    var f1 = DistanceAtAngle(points, T, x1);
-    var x2 = (1.0 - Phi) * a + Phi * b;
-    var f2 = DistanceAtAngle(points, T, x2);
+    let x1 = Phi * a + (1.0 - Phi) * b;
+    let f1 = DistanceAtAngle(points, T, x1);
+    let x2 = (1.0 - Phi) * a + Phi * b;
+    let f2 = DistanceAtAngle(points, T, x2);
     while (Math.abs(b - a) > threshold) {
         if (f1 < f2) {
+            // eslint-disable-next-line no-param-reassign
             b = x2;
             x2 = x1;
             f2 = f1;
             x1 = Phi * a + (1.0 - Phi) * b;
             f1 = DistanceAtAngle(points, T, x1);
         } else {
+            // eslint-disable-next-line no-param-reassign
             a = x1;
             x1 = x2;
             f1 = f2;
@@ -523,34 +545,34 @@ function DistanceAtAngle(points: any, T: any, radians: any) {
     const newpoints = RotateBy(points, radians);
     return PathDistance(newpoints, T.Points);
 }
-function Centroid(points: any) {
-    var x = 0.0,
-        y = 0.0;
-    for (const point of points) {
+function Centroid(points: Point[]) {
+    let x = 0.0;
+    let y = 0.0;
+    points.forEach(point => {
         x += point.X;
         y += point.Y;
-    }
+    });
     x /= points.length;
     y /= points.length;
     return new Point(x, y);
 }
-function BoundingBox(points: any) {
-    var minX = +Infinity,
-        maxX = -Infinity,
-        minY = +Infinity,
-        maxY = -Infinity;
-    for (const { X, Y } of points) {
+function BoundingBox(points: Point[]) {
+    let minX = +Infinity;
+    let maxX = -Infinity;
+    let minY = +Infinity;
+    let maxY = -Infinity;
+    points.forEach((X: any, Y: any) => {
         minX = Math.min(minX, X);
         minY = Math.min(minY, Y);
         maxX = Math.max(maxX, X);
         maxY = Math.max(maxY, Y);
-    }
+    });
     return new Rectangle(minX, minY, maxX - minX, maxY - minY);
 }
 function PathDistance(pts1: any, pts2: any) {
     // average distance between corresponding points in two paths
-    var d = 0.0;
-    for (var i = 0; i < pts1.length; i++) {
+    let d = 0.0;
+    for (let i = 0; i < pts1.length; i++) {
         // assumes pts1.length == pts2.length
         d += Distance(pts1[i], pts2[i]);
     }
@@ -558,8 +580,8 @@ function PathDistance(pts1: any, pts2: any) {
 }
 function PathLength(points: any) {
     // length traversed by a point path
-    var d = 0.0;
-    for (var i = 1; i < points.length; i++) {
+    let d = 0.0;
+    for (let i = 1; i < points.length; i++) {
         d += Distance(points[i - 1], points[i]);
     }
     return d;
@@ -570,9 +592,9 @@ function Distance(p1: any, p2: any) {
     const dy = p2.Y - p1.Y;
     return Math.sqrt(dx * dx + dy * dy);
 }
-function CalcStartUnitVector(points: any, index: any) {
+function CalcStartUnitVector(points: Point[], index: any) {
     // start angle from points[0] to points[index] normalized as a unit vector
-    const v = new Point(points[index]?.X - points[0]?.X, points[index]?.Y - points[0]?.Y);
+    const v = new Point(points[index].X - points[0].X, points[index].Y - points[0].Y);
     const len = Math.sqrt(v.X * v.X + v.Y * v.Y);
     return new Point(v.X / len, v.Y / len);
 }
-- 
cgit v1.2.3-70-g09d2

